diff options
Diffstat (limited to 'libweston')
85 files changed, 0 insertions, 54484 deletions
diff --git a/libweston/animation.c b/libweston/animation.c deleted file mode 100644 index a81a8c19..00000000 --- a/libweston/animation.c +++ /dev/null @@ -1,532 +0,0 @@ -/* - * Copyright © 2011 Intel Corporation - * - * 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 <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <stdio.h> -#include <math.h> -#include <inttypes.h> - -#include <unistd.h> -#include <fcntl.h> - -#include <libweston/libweston.h> -#include "shared/helpers.h" -#include "shared/timespec-util.h" - -WL_EXPORT void -weston_spring_init(struct weston_spring *spring, - double k, double current, double target) -{ - spring->k = k; - spring->friction = 400.0; - spring->current = current; - spring->previous = current; - spring->target = target; - spring->clip = WESTON_SPRING_OVERSHOOT; - spring->min = 0.0; - spring->max = 1.0; -} - -WL_EXPORT void -weston_spring_update(struct weston_spring *spring, const struct timespec *time) -{ - double force, v, current, step; - - /* Limit the number of executions of the loop below by ensuring that - * the timestamp for last update of the spring is no more than 1s ago. - * This handles the case where time moves backwards or forwards in - * large jumps. - */ - if (timespec_sub_to_msec(time, &spring->timestamp) > 1000) { - weston_log("unexpectedly large timestamp jump " - "(from %" PRId64 " to %" PRId64 ")\n", - timespec_to_msec(&spring->timestamp), - timespec_to_msec(time)); - timespec_add_msec(&spring->timestamp, time, -1000); - } - - step = 0.01; - while (4 < timespec_sub_to_msec(time, &spring->timestamp)) { - current = spring->current; - v = current - spring->previous; - force = spring->k * (spring->target - current) / 10.0 + - (spring->previous - current) - v * spring->friction; - - spring->current = - current + (current - spring->previous) + - force * step * step; - spring->previous = current; - - switch (spring->clip) { - case WESTON_SPRING_OVERSHOOT: - break; - - case WESTON_SPRING_CLAMP: - if (spring->current > spring->max) { - spring->current = spring->max; - spring->previous = spring->max; - } else if (spring->current < 0.0) { - spring->current = spring->min; - spring->previous = spring->min; - } - break; - - case WESTON_SPRING_BOUNCE: - if (spring->current > spring->max) { - spring->current = - 2 * spring->max - spring->current; - spring->previous = - 2 * spring->max - spring->previous; - } else if (spring->current < spring->min) { - spring->current = - 2 * spring->min - spring->current; - spring->previous = - 2 * spring->min - spring->previous; - } - break; - } - - timespec_add_msec(&spring->timestamp, &spring->timestamp, 4); - } -} - -WL_EXPORT int -weston_spring_done(struct weston_spring *spring) -{ - return fabs(spring->previous - spring->target) < 0.002 && - fabs(spring->current - spring->target) < 0.002; -} - -typedef void (*weston_view_animation_frame_func_t)(struct weston_view_animation *animation); - -struct weston_view_animation { - struct weston_view *view; - struct weston_animation animation; - struct weston_spring spring; - struct weston_transform transform; - struct wl_listener listener; - float start, stop; - weston_view_animation_frame_func_t frame; - weston_view_animation_frame_func_t reset; - weston_view_animation_done_func_t done; - void *data; - void *private; -}; - -WL_EXPORT void -weston_view_animation_destroy(struct weston_view_animation *animation) -{ - wl_list_remove(&animation->animation.link); - wl_list_remove(&animation->listener.link); - wl_list_remove(&animation->transform.link); - if (animation->reset) - animation->reset(animation); - weston_view_geometry_dirty(animation->view); - if (animation->done) - animation->done(animation, animation->data); - free(animation); -} - -static void -handle_animation_view_destroy(struct wl_listener *listener, void *data) -{ - struct weston_view_animation *animation = - container_of(listener, - struct weston_view_animation, listener); - - weston_view_animation_destroy(animation); -} - -static void -weston_view_animation_frame(struct weston_animation *base, - struct weston_output *output, - const struct timespec *time) -{ - struct weston_view_animation *animation = - container_of(base, - struct weston_view_animation, animation); - struct weston_compositor *compositor = - animation->view->surface->compositor; - - if (base->frame_counter <= 1) - animation->spring.timestamp = *time; - - weston_spring_update(&animation->spring, time); - - if (weston_spring_done(&animation->spring)) { - weston_view_schedule_repaint(animation->view); - weston_view_animation_destroy(animation); - return; - } - - if (animation->frame) - animation->frame(animation); - - weston_view_geometry_dirty(animation->view); - weston_view_schedule_repaint(animation->view); - - /* The view's output_mask will be zero if its position is - * offscreen. Animations should always run but as they are also - * run off the repaint cycle, if there's nothing to repaint - * the animation stops running. Therefore if we catch this situation - * and schedule a repaint on all outputs it will be avoided. - */ - if (animation->view->output_mask == 0) - weston_compositor_schedule_repaint(compositor); -} - -static void -idle_animation_destroy(void *data) -{ - struct weston_view_animation *animation = data; - - weston_view_animation_destroy(animation); -} - -static struct weston_view_animation * -weston_view_animation_create(struct weston_view *view, - float start, float stop, - weston_view_animation_frame_func_t frame, - weston_view_animation_frame_func_t reset, - weston_view_animation_done_func_t done, - void *data, - void *private) -{ - struct weston_view_animation *animation; - struct weston_compositor *ec = view->surface->compositor; - struct wl_event_loop *loop; - - animation = malloc(sizeof *animation); - if (!animation) - return NULL; - - animation->view = view; - animation->frame = frame; - animation->reset = reset; - animation->done = done; - animation->data = data; - animation->start = start; - animation->stop = stop; - animation->private = private; - - weston_matrix_init(&animation->transform.matrix); - wl_list_insert(&view->geometry.transformation_list, - &animation->transform.link); - - animation->animation.frame = weston_view_animation_frame; - - animation->listener.notify = handle_animation_view_destroy; - wl_signal_add(&view->destroy_signal, &animation->listener); - - if (view->output) { - wl_list_insert(&view->output->animation_list, - &animation->animation.link); - } else { - wl_list_init(&animation->animation.link); - loop = wl_display_get_event_loop(ec->wl_display); - wl_event_loop_add_idle(loop, idle_animation_destroy, animation); - } - - return animation; -} - -static void -weston_view_animation_run(struct weston_view_animation *animation) -{ - struct timespec zero_time = { 0 }; - - animation->animation.frame_counter = 0; - weston_view_animation_frame(&animation->animation, NULL, &zero_time); -} - -static void -reset_alpha(struct weston_view_animation *animation) -{ - struct weston_view *view = animation->view; - - view->alpha = animation->stop; -} - -static void -zoom_frame(struct weston_view_animation *animation) -{ - struct weston_view *es = animation->view; - float scale; - - scale = animation->start + - (animation->stop - animation->start) * - animation->spring.current; - weston_matrix_init(&animation->transform.matrix); - weston_matrix_translate(&animation->transform.matrix, - -0.5f * es->surface->width, - -0.5f * es->surface->height, 0); - weston_matrix_scale(&animation->transform.matrix, scale, scale, scale); - weston_matrix_translate(&animation->transform.matrix, - 0.5f * es->surface->width, - 0.5f * es->surface->height, 0); - - es->alpha = animation->spring.current; - if (es->alpha > 1.0) - es->alpha = 1.0; -} - -WL_EXPORT struct weston_view_animation * -weston_zoom_run(struct weston_view *view, float start, float stop, - weston_view_animation_done_func_t done, void *data) -{ - struct weston_view_animation *zoom; - - zoom = weston_view_animation_create(view, start, stop, - zoom_frame, reset_alpha, - done, data, NULL); - - if (zoom == NULL) - return NULL; - - weston_spring_init(&zoom->spring, 300.0, start, stop); - zoom->spring.friction = 1400; - zoom->spring.previous = start - (stop - start) * 0.03; - - weston_view_animation_run(zoom); - - return zoom; -} - -static void -fade_frame(struct weston_view_animation *animation) -{ - if (animation->spring.current > 0.999) - animation->view->alpha = 1; - else if (animation->spring.current < 0.001 ) - animation->view->alpha = 0; - else - animation->view->alpha = animation->spring.current; -} - -WL_EXPORT struct weston_view_animation * -weston_fade_run(struct weston_view *view, - float start, float end, float k, - weston_view_animation_done_func_t done, void *data) -{ - struct weston_view_animation *fade; - - fade = weston_view_animation_create(view, start, end, - fade_frame, reset_alpha, - done, data, NULL); - - if (fade == NULL) - return NULL; - - weston_spring_init(&fade->spring, 1000.0, start, end); - fade->spring.friction = 4000; - fade->spring.previous = start - (end - start) * 0.1; - - view->alpha = start; - - weston_view_animation_run(fade); - - return fade; -} - -WL_EXPORT void -weston_fade_update(struct weston_view_animation *fade, float target) -{ - fade->spring.target = target; - fade->stop = target; -} - -static void -stable_fade_frame(struct weston_view_animation *animation) -{ - struct weston_view *back_view; - - if (animation->spring.current > 0.999) - animation->view->alpha = 1; - else if (animation->spring.current < 0.001 ) - animation->view->alpha = 0; - else - animation->view->alpha = animation->spring.current; - - back_view = (struct weston_view *) animation->private; - back_view->alpha = - (animation->spring.target - animation->view->alpha) / - (1.0 - animation->view->alpha); - weston_view_geometry_dirty(back_view); -} - -WL_EXPORT struct weston_view_animation * -weston_stable_fade_run(struct weston_view *front_view, float start, - struct weston_view *back_view, float end, - weston_view_animation_done_func_t done, void *data) -{ - struct weston_view_animation *fade; - - fade = weston_view_animation_create(front_view, 0, 0, - stable_fade_frame, NULL, - done, data, back_view); - - if (fade == NULL) - return NULL; - - weston_spring_init(&fade->spring, 400, start, end); - fade->spring.friction = 1150; - - front_view->alpha = start; - back_view->alpha = end; - - weston_view_animation_run(fade); - - return fade; -} - -static void -slide_frame(struct weston_view_animation *animation) -{ - float scale; - - scale = animation->start + - (animation->stop - animation->start) * - animation->spring.current; - weston_matrix_init(&animation->transform.matrix); - weston_matrix_translate(&animation->transform.matrix, 0, scale, 0); -} - -WL_EXPORT struct weston_view_animation * -weston_slide_run(struct weston_view *view, float start, float stop, - weston_view_animation_done_func_t done, void *data) -{ - struct weston_view_animation *animation; - - animation = weston_view_animation_create(view, start, stop, - slide_frame, NULL, done, - data, NULL); - if (!animation) - return NULL; - - weston_spring_init(&animation->spring, 400.0, 0.0, 1.0); - animation->spring.friction = 600; - animation->spring.clip = WESTON_SPRING_BOUNCE; - - weston_view_animation_run(animation); - - return animation; -} - -struct weston_move_animation { - int dx; - int dy; - bool reverse; - bool scale; - weston_view_animation_done_func_t done; -}; - -static void -move_frame(struct weston_view_animation *animation) -{ - struct weston_move_animation *move = animation->private; - float scale; - float progress = animation->spring.current; - - if (move->reverse) - progress = 1.0 - progress; - - scale = animation->start + - (animation->stop - animation->start) * - progress; - weston_matrix_init(&animation->transform.matrix); - if (move->scale) - weston_matrix_scale(&animation->transform.matrix, scale, scale, - 1.0f); - weston_matrix_translate(&animation->transform.matrix, - move->dx * progress, move->dy * progress, - 0); -} - -static void -move_done(struct weston_view_animation *animation, void *data) -{ - struct weston_move_animation *move = animation->private; - - if (move->done) - move->done(animation, data); - - free(move); -} - -static struct weston_view_animation * -weston_move_scale_run_internal(struct weston_view *view, int dx, int dy, - float start, float end, bool reverse, bool scale, - weston_view_animation_done_func_t done, - void *data) -{ - struct weston_move_animation *move; - struct weston_view_animation *animation; - - move = malloc(sizeof(*move)); - if (!move) - return NULL; - move->dx = dx; - move->dy = dy; - move->reverse = reverse; - move->scale = scale; - move->done = done; - - animation = weston_view_animation_create(view, start, end, move_frame, - NULL, move_done, data, move); - - if (animation == NULL){ - free(move); - return NULL; - } - - weston_spring_init(&animation->spring, 400.0, 0.0, 1.0); - animation->spring.friction = 1150; - - weston_view_animation_run(animation); - - return animation; -} - -WL_EXPORT struct weston_view_animation * -weston_move_scale_run(struct weston_view *view, int dx, int dy, - float start, float end, bool reverse, - weston_view_animation_done_func_t done, void *data) -{ - return weston_move_scale_run_internal(view, dx, dy, start, end, reverse, - true, done, data); -} - -WL_EXPORT struct weston_view_animation * -weston_move_run(struct weston_view *view, int dx, int dy, - float start, float end, bool reverse, - weston_view_animation_done_func_t done, void *data) -{ - return weston_move_scale_run_internal(view, dx, dy, start, end, reverse, - false, done, data); -} diff --git a/libweston/backend-drm/drm-gbm.c b/libweston/backend-drm/drm-gbm.c deleted file mode 100644 index 324c2a83..00000000 --- a/libweston/backend-drm/drm-gbm.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2017, 2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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 <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <ctype.h> -#include <string.h> -#include <dlfcn.h> - -#include "drm-internal.h" -#include "pixman-renderer.h" -#include "pixel-formats.h" -#include "renderer-gl/gl-renderer.h" -#include "shared/weston-egl-ext.h" -#include "linux-dmabuf.h" -#include "linux-explicit-synchronization.h" - -struct gl_renderer_interface *gl_renderer; - -static struct gbm_device * -create_gbm_device(int fd) -{ - struct gbm_device *gbm; - - gl_renderer = weston_load_module("gl-renderer.so", - "gl_renderer_interface"); - if (!gl_renderer) - return NULL; - - /* GBM will load a dri driver, but even though they need symbols from - * libglapi, in some version of Mesa they are not linked to it. Since - * only the gl-renderer module links to it, the call above won't make - * these symbols globally available, and loading the DRI driver fails. - * Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL. */ - dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL); - - gbm = gbm_create_device(fd); - - return gbm; -} - -/* When initializing EGL, if the preferred buffer format isn't available - * we may be able to substitute an ARGB format for an XRGB one. - * - * This returns 0 if substitution isn't possible, but 0 might be a - * legitimate format for other EGL platforms, so the caller is - * responsible for checking for 0 before calling gl_renderer->create(). - * - * This works around https://bugs.freedesktop.org/show_bug.cgi?id=89689 - * but it's entirely possible we'll see this again on other implementations. - */ -static uint32_t -fallback_format_for(uint32_t format) -{ - const struct pixel_format_info *pf; - - pf = pixel_format_get_info_by_opaque_substitute(format); - if (!pf) - return 0; - - return pf->format; -} - -static int -drm_backend_create_gl_renderer(struct drm_backend *b) -{ - uint32_t format[3] = { - b->gbm_format, - fallback_format_for(b->gbm_format), - 0, - }; - unsigned n_formats = 2; - - if (format[1]) - n_formats = 3; - if (gl_renderer->display_create(b->compositor, - EGL_PLATFORM_GBM_KHR, - (void *)b->gbm, - EGL_WINDOW_BIT, - format, - n_formats) < 0) { - return -1; - } - - return 0; -} - -int -init_egl(struct drm_backend *b) -{ - b->gbm = create_gbm_device(b->drm.fd); - - if (!b->gbm) - return -1; - - if (drm_backend_create_gl_renderer(b) < 0) { - gbm_device_destroy(b->gbm); - return -1; - } - - return 0; -} - -static void drm_output_fini_cursor_egl(struct drm_output *output) -{ - unsigned int i; - - for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) { - drm_fb_unref(output->gbm_cursor_fb[i]); - output->gbm_cursor_fb[i] = NULL; - } -} - -static int -drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b) -{ - unsigned int i; - - /* No point creating cursors if we don't have a plane for them. */ - if (!output->cursor_plane) - return 0; - - for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) { - struct gbm_bo *bo; - - bo = gbm_bo_create(b->gbm, b->cursor_width, b->cursor_height, - GBM_FORMAT_ARGB8888, - GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE); - if (!bo) - goto err; - - output->gbm_cursor_fb[i] = - drm_fb_get_from_bo(bo, b, false, BUFFER_CURSOR); - if (!output->gbm_cursor_fb[i]) { - gbm_bo_destroy(bo); - goto err; - } - output->gbm_cursor_handle[i] = gbm_bo_get_handle(bo).s32; - } - - return 0; - -err: - weston_log("cursor buffers unavailable, using gl cursors\n"); - b->cursors_are_broken = true; - drm_output_fini_cursor_egl(output); - return -1; -} - -/* Init output state that depends on gl or gbm */ -int -drm_output_init_egl(struct drm_output *output, struct drm_backend *b) -{ - uint32_t format[2] = { - output->gbm_format, - fallback_format_for(output->gbm_format), - }; - unsigned n_formats = 1; - struct weston_mode *mode = output->base.current_mode; - struct drm_plane *plane = output->scanout_plane; - unsigned int i; - - assert(output->gbm_surface == NULL); - - for (i = 0; i < plane->count_formats; i++) { - if (plane->formats[i].format == output->gbm_format) - break; - } - - if (i == plane->count_formats) { - weston_log("format 0x%x not supported by output %s\n", - output->gbm_format, output->base.name); - return -1; - } - -#ifdef HAVE_GBM_MODIFIERS - if (plane->formats[i].count_modifiers > 0) { - output->gbm_surface = - gbm_surface_create_with_modifiers(b->gbm, - mode->width, - mode->height, - output->gbm_format, - plane->formats[i].modifiers, - plane->formats[i].count_modifiers); - } - - /* If allocating with modifiers fails, try again without. This can - * happen when the KMS display device supports modifiers but the - * GBM driver does not, e.g. the old i915 Mesa driver. */ - if (!output->gbm_surface) -#endif - { - output->gbm_surface = - gbm_surface_create(b->gbm, mode->width, mode->height, - output->gbm_format, - output->gbm_bo_flags); - } - - if (!output->gbm_surface) { - weston_log("failed to create gbm surface\n"); - return -1; - } - - if (format[1]) - n_formats = 2; - if (gl_renderer->output_window_create(&output->base, - (EGLNativeWindowType)output->gbm_surface, - output->gbm_surface, - format, - n_formats) < 0) { - weston_log("failed to create gl renderer output state\n"); - gbm_surface_destroy(output->gbm_surface); - output->gbm_surface = NULL; - return -1; - } - - drm_output_init_cursor_egl(output, b); - - return 0; -} - -void -drm_output_fini_egl(struct drm_output *output) -{ - struct drm_backend *b = to_drm_backend(output->base.compositor); - - /* Destroying the GBM surface will destroy all our GBM buffers, - * regardless of refcount. Ensure we destroy them here. */ - if (!b->shutting_down && - output->scanout_plane->state_cur->fb && - output->scanout_plane->state_cur->fb->type == BUFFER_GBM_SURFACE) { - drm_plane_state_free(output->scanout_plane->state_cur, true); - output->scanout_plane->state_cur = - drm_plane_state_alloc(NULL, output->scanout_plane); - output->scanout_plane->state_cur->complete = true; - } - - gl_renderer->output_destroy(&output->base); - gbm_surface_destroy(output->gbm_surface); - output->gbm_surface = NULL; - drm_output_fini_cursor_egl(output); -} - -struct drm_fb * -drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) -{ - struct drm_output *output = state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct gbm_bo *bo; - struct drm_fb *ret; - - output->base.compositor->renderer->repaint_output(&output->base, - damage); - - bo = gbm_surface_lock_front_buffer(output->gbm_surface); - if (!bo) { - weston_log("failed to lock front buffer: %s\n", - strerror(errno)); - return NULL; - } - - /* The renderer always produces an opaque image. */ - ret = drm_fb_get_from_bo(bo, b, true, BUFFER_GBM_SURFACE); - if (!ret) { - weston_log("failed to get drm_fb for bo\n"); - gbm_surface_release_buffer(output->gbm_surface, bo); - return NULL; - } - ret->gbm_surface = output->gbm_surface; - - return ret; -} - -static void -switch_to_gl_renderer(struct drm_backend *b) -{ - struct drm_output *output; - bool dmabuf_support_inited; - bool linux_explicit_sync_inited; - - if (!b->use_pixman) - return; - - dmabuf_support_inited = !!b->compositor->renderer->import_dmabuf; - linux_explicit_sync_inited = - b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC; - - weston_log("Switching to GL renderer\n"); - - b->gbm = create_gbm_device(b->drm.fd); - if (!b->gbm) { - weston_log("Failed to create gbm device. " - "Aborting renderer switch\n"); - return; - } - - wl_list_for_each(output, &b->compositor->output_list, base.link) - pixman_renderer_output_destroy(&output->base); - - b->compositor->renderer->destroy(b->compositor); - - if (drm_backend_create_gl_renderer(b) < 0) { - gbm_device_destroy(b->gbm); - weston_log("Failed to create GL renderer. Quitting.\n"); - /* FIXME: we need a function to shutdown cleanly */ - assert(0); - } - - wl_list_for_each(output, &b->compositor->output_list, base.link) - drm_output_init_egl(output, b); - - b->use_pixman = 0; - - if (!dmabuf_support_inited && b->compositor->renderer->import_dmabuf) { - if (linux_dmabuf_setup(b->compositor) < 0) - weston_log("Error: initializing dmabuf " - "support failed.\n"); - } - - if (!linux_explicit_sync_inited && - (b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC)) { - if (linux_explicit_synchronization_setup(b->compositor) < 0) - weston_log("Error: initializing explicit " - " synchronization support failed.\n"); - } -} - -void -renderer_switch_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, void *data) -{ - struct drm_backend *b = - to_drm_backend(keyboard->seat->compositor); - - switch_to_gl_renderer(b); -} - diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h deleted file mode 100644 index 2384a9ac..00000000 --- a/libweston/backend-drm/drm-internal.h +++ /dev/null @@ -1,825 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2017, 2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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 <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <ctype.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <linux/input.h> -#include <linux/vt.h> -#include <assert.h> -#include <sys/mman.h> -#include <time.h> - - -#include <xf86drm.h> -#include <xf86drmMode.h> -#include <drm_fourcc.h> - -#ifdef BUILD_DRM_GBM -#include <gbm.h> -#endif -#include <libudev.h> - -#include <libweston/libweston.h> -#include <libweston/backend-drm.h> -#include <libweston/weston-log.h> -#include "shared/helpers.h" -#include "libinput-seat.h" -#include "backend.h" -#include "libweston-internal.h" - -#ifndef DRM_CLIENT_CAP_ASPECT_RATIO -#define DRM_CLIENT_CAP_ASPECT_RATIO 4 -#endif - -#ifndef GBM_BO_USE_CURSOR -#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64 -#endif - -#ifndef GBM_BO_USE_LINEAR -#define GBM_BO_USE_LINEAR (1 << 4) -#endif - -#ifndef DRM_PLANE_ZPOS_INVALID_PLANE -#define DRM_PLANE_ZPOS_INVALID_PLANE 0xffffffffffffffffULL -#endif - -/** - * A small wrapper to print information into the 'drm-backend' debug scope. - * - * The following conventions are used to print variables: - * - * - fixed uint32_t values, including Weston object IDs such as weston_output - * IDs, DRM object IDs such as CRTCs or properties, and GBM/DRM formats: - * "%lu (0x%lx)" (unsigned long) value, (unsigned long) value - * - * - fixed uint64_t values, such as DRM property values (including object IDs - * when used as a value): - * "%llu (0x%llx)" (unsigned long long) value, (unsigned long long) value - * - * - non-fixed-width signed int: - * "%d" value - * - * - non-fixed-width unsigned int: - * "%u (0x%x)" value, value - * - * - non-fixed-width unsigned long: - * "%lu (0x%lx)" value, value - * - * Either the integer or hexadecimal forms may be omitted if it is known that - * one representation is not useful (e.g. width/height in hex are rarely what - * you want). - * - * This is to avoid implicit widening or narrowing when we use fixed-size - * types: uint32_t can be resolved by either unsigned int or unsigned long - * on a 32-bit system but only unsigned int on a 64-bit system, with uint64_t - * being unsigned long long on a 32-bit system and unsigned long on a 64-bit - * system. To avoid confusing side effects, we explicitly cast to the widest - * possible type and use a matching format specifier. - */ -#define drm_debug(b, ...) \ - weston_log_scope_printf((b)->debug, __VA_ARGS__) - -#define MAX_CLONED_CONNECTORS 4 - -#ifndef DRM_MODE_PICTURE_ASPECT_64_27 -#define DRM_MODE_PICTURE_ASPECT_64_27 3 -#define DRM_MODE_FLAG_PIC_AR_64_27 \ - (DRM_MODE_PICTURE_ASPECT_64_27<<19) -#endif -#ifndef DRM_MODE_PICTURE_ASPECT_256_135 -#define DRM_MODE_PICTURE_ASPECT_256_135 4 -#define DRM_MODE_FLAG_PIC_AR_256_135 \ - (DRM_MODE_PICTURE_ASPECT_256_135<<19) -#endif - - -/** - * Represents the values of an enum-type KMS property - */ -struct drm_property_enum_info { - const char *name; /**< name as string (static, not freed) */ - bool valid; /**< true if value is supported; ignore if false */ - uint64_t value; /**< raw value */ -}; - -/** - * Holds information on a DRM property, including its ID and the enum - * values it holds. - * - * DRM properties are allocated dynamically, and maintained as DRM objects - * within the normal object ID space; they thus do not have a stable ID - * to refer to. This includes enum values, which must be referred to by - * integer values, but these are not stable. - * - * drm_property_info allows a cache to be maintained where Weston can use - * enum values internally to refer to properties, with the mapping to DRM - * ID values being maintained internally. - */ -struct drm_property_info { - const char *name; /**< name as string (static, not freed) */ - uint32_t prop_id; /**< KMS property object ID */ - uint32_t flags; - unsigned int num_enum_values; /**< number of enum values */ - struct drm_property_enum_info *enum_values; /**< array of enum values */ - unsigned int num_range_values; - uint64_t range_values[2]; -}; - -/** - * List of properties attached to DRM planes - */ -enum wdrm_plane_property { - WDRM_PLANE_TYPE = 0, - WDRM_PLANE_SRC_X, - WDRM_PLANE_SRC_Y, - WDRM_PLANE_SRC_W, - WDRM_PLANE_SRC_H, - WDRM_PLANE_CRTC_X, - WDRM_PLANE_CRTC_Y, - WDRM_PLANE_CRTC_W, - WDRM_PLANE_CRTC_H, - WDRM_PLANE_FB_ID, - WDRM_PLANE_CRTC_ID, - WDRM_PLANE_IN_FORMATS, - WDRM_PLANE_IN_FENCE_FD, - WDRM_PLANE_FB_DAMAGE_CLIPS, - WDRM_PLANE_ZPOS, - WDRM_PLANE__COUNT -}; - -/** - * Possible values for the WDRM_PLANE_TYPE property. - */ -enum wdrm_plane_type { - WDRM_PLANE_TYPE_PRIMARY = 0, - WDRM_PLANE_TYPE_CURSOR, - WDRM_PLANE_TYPE_OVERLAY, - WDRM_PLANE_TYPE__COUNT -}; - -/** - * List of properties attached to a DRM connector - */ -enum wdrm_connector_property { - WDRM_CONNECTOR_EDID = 0, - WDRM_CONNECTOR_DPMS, - WDRM_CONNECTOR_CRTC_ID, - WDRM_CONNECTOR_NON_DESKTOP, - WDRM_CONNECTOR_CONTENT_PROTECTION, - WDRM_CONNECTOR_HDCP_CONTENT_TYPE, - WDRM_CONNECTOR__COUNT -}; - -enum wdrm_content_protection_state { - WDRM_CONTENT_PROTECTION_UNDESIRED = 0, - WDRM_CONTENT_PROTECTION_DESIRED, - WDRM_CONTENT_PROTECTION_ENABLED, - WDRM_CONTENT_PROTECTION__COUNT -}; - -enum wdrm_hdcp_content_type { - WDRM_HDCP_CONTENT_TYPE0 = 0, - WDRM_HDCP_CONTENT_TYPE1, - WDRM_HDCP_CONTENT_TYPE__COUNT -}; - -enum wdrm_dpms_state { - WDRM_DPMS_STATE_OFF = 0, - WDRM_DPMS_STATE_ON, - WDRM_DPMS_STATE_STANDBY, /* unused */ - WDRM_DPMS_STATE_SUSPEND, /* unused */ - WDRM_DPMS_STATE__COUNT -}; - -/** - * List of properties attached to DRM CRTCs - */ -enum wdrm_crtc_property { - WDRM_CRTC_MODE_ID = 0, - WDRM_CRTC_ACTIVE, - WDRM_CRTC__COUNT -}; - -struct drm_backend { - struct weston_backend base; - struct weston_compositor *compositor; - - struct udev *udev; - struct wl_event_source *drm_source; - - struct udev_monitor *udev_monitor; - struct wl_event_source *udev_drm_source; - - struct { - int id; - int fd; - char *filename; - dev_t devnum; - } drm; - struct gbm_device *gbm; - struct wl_listener session_listener; - uint32_t gbm_format; - - /* we need these parameters in order to not fail drmModeAddFB2() - * due to out of bounds dimensions, and then mistakenly set - * sprites_are_broken: - */ - int min_width, max_width; - int min_height, max_height; - - struct wl_list plane_list; - - void *repaint_data; - - bool state_invalid; - - /* CRTC IDs not used by any enabled output. */ - struct wl_array unused_crtcs; - - bool sprites_are_broken; - bool cursors_are_broken; - - bool universal_planes; - bool atomic_modeset; - - bool use_pixman; - bool use_pixman_shadow; - - struct udev_input input; - - int32_t cursor_width; - int32_t cursor_height; - - uint32_t pageflip_timeout; - - bool shutting_down; - - bool aspect_ratio_supported; - - bool fb_modifiers; - - struct weston_log_scope *debug; -}; - -struct drm_mode { - struct weston_mode base; - drmModeModeInfo mode_info; - uint32_t blob_id; -}; - -enum drm_fb_type { - BUFFER_INVALID = 0, /**< never used */ - BUFFER_CLIENT, /**< directly sourced from client */ - BUFFER_DMABUF, /**< imported from linux_dmabuf client */ - BUFFER_PIXMAN_DUMB, /**< internal Pixman rendering */ - BUFFER_GBM_SURFACE, /**< internal EGL rendering */ - BUFFER_CURSOR, /**< internal cursor buffer */ -}; - -struct drm_fb { - enum drm_fb_type type; - - int refcnt; - - uint32_t fb_id, size; - uint32_t handles[4]; - uint32_t strides[4]; - uint32_t offsets[4]; - int num_planes; - const struct pixel_format_info *format; - uint64_t modifier; - int width, height; - int fd; - struct weston_buffer_reference buffer_ref; - struct weston_buffer_release_reference buffer_release_ref; - - /* Used by gbm fbs */ - struct gbm_bo *bo; - struct gbm_surface *gbm_surface; - - /* Used by dumb fbs */ - void *map; -}; - -struct drm_edid { - char eisa_id[13]; - char monitor_name[13]; - char pnp_id[5]; - char serial_number[13]; -}; - -/** - * Pending state holds one or more drm_output_state structures, collected from - * performing repaint. This pending state is transient, and only lives between - * beginning a repaint group and flushing the results: after flush, each - * output state will complete and be retired separately. - */ -struct drm_pending_state { - struct drm_backend *backend; - struct wl_list output_list; -}; - -/* - * Output state holds the dynamic state for one Weston output, i.e. a KMS CRTC, - * plus >= 1 each of encoder/connector/plane. Since everything but the planes - * is currently statically assigned per-output, we mainly use this to track - * plane state. - * - * pending_state is set when the output state is owned by a pending_state, - * i.e. when it is being constructed and has not yet been applied. When the - * output state has been applied, the owning pending_state is freed. - */ -struct drm_output_state { - struct drm_pending_state *pending_state; - struct drm_output *output; - struct wl_list link; - enum dpms_enum dpms; - enum weston_hdcp_protection protection; - struct wl_list plane_list; -}; - -/** - * An instance of this class is created each time we believe we have a plane - * suitable to be used by a view as a direct scan-out. The list is initalized - * and populated locally. - */ -struct drm_plane_zpos { - struct drm_plane *plane; - struct wl_list link; /**< :candidate_plane_zpos_list */ -}; - -/** - * Plane state holds the dynamic state for a plane: where it is positioned, - * and which buffer it is currently displaying. - * - * The plane state is owned by an output state, except when setting an initial - * state. See drm_output_state for notes on state object lifetime. - */ -struct drm_plane_state { - struct drm_plane *plane; - struct drm_output *output; - struct drm_output_state *output_state; - - struct drm_fb *fb; - - struct weston_view *ev; /**< maintained for drm_assign_planes only */ - - int32_t src_x, src_y; - uint32_t src_w, src_h; - int32_t dest_x, dest_y; - uint32_t dest_w, dest_h; - - uint64_t zpos; - - bool complete; - - /* We don't own the fd, so we shouldn't close it */ - int in_fence_fd; - - pixman_region32_t damage; /* damage to kernel */ - - struct wl_list link; /* drm_output_state::plane_list */ -}; - -/** - * A plane represents one buffer, positioned within a CRTC, and stacked - * relative to other planes on the same CRTC. - * - * Each CRTC has a 'primary plane', which use used to display the classic - * framebuffer contents, as accessed through the legacy drmModeSetCrtc - * call (which combines setting the CRTC's actual physical mode, and the - * properties of the primary plane). - * - * The cursor plane also has its own alternate legacy API. - * - * Other planes are used opportunistically to display content we do not - * wish to blit into the primary plane. These non-primary/cursor planes - * are referred to as 'sprites'. - */ -struct drm_plane { - struct weston_plane base; - - struct drm_backend *backend; - - enum wdrm_plane_type type; - - uint32_t possible_crtcs; - uint32_t plane_id; - uint32_t count_formats; - - struct drm_property_info props[WDRM_PLANE__COUNT]; - - /* The last state submitted to the kernel for this plane. */ - struct drm_plane_state *state_cur; - - uint64_t zpos_min; - uint64_t zpos_max; - - struct wl_list link; - - struct { - uint32_t format; - uint32_t count_modifiers; - uint64_t *modifiers; - } formats[]; -}; - -struct drm_head { - struct weston_head base; - struct drm_backend *backend; - - drmModeConnector *connector; - uint32_t connector_id; - struct drm_edid edid; - - /* Holds the properties for the connector */ - struct drm_property_info props_conn[WDRM_CONNECTOR__COUNT]; - - struct backlight *backlight; - - drmModeModeInfo inherited_mode; /**< Original mode on the connector */ - uint32_t inherited_crtc_id; /**< Original CRTC assignment */ -}; - -struct drm_output { - struct weston_output base; - struct drm_backend *backend; - - uint32_t crtc_id; /* object ID to pass to DRM functions */ - int pipe; /* index of CRTC in resource array / bitmasks */ - - /* Holds the properties for the CRTC */ - struct drm_property_info props_crtc[WDRM_CRTC__COUNT]; - - bool page_flip_pending; - bool atomic_complete_pending; - bool destroy_pending; - bool disable_pending; - bool dpms_off_pending; - - uint32_t gbm_cursor_handle[2]; - struct drm_fb *gbm_cursor_fb[2]; - struct drm_plane *cursor_plane; - struct weston_view *cursor_view; - int current_cursor; - - struct gbm_surface *gbm_surface; - uint32_t gbm_format; - uint32_t gbm_bo_flags; - - /* Plane being displayed directly on the CRTC */ - struct drm_plane *scanout_plane; - - /* The last state submitted to the kernel for this CRTC. */ - struct drm_output_state *state_cur; - /* The previously-submitted state, where the hardware has not - * yet acknowledged completion of state_cur. */ - struct drm_output_state *state_last; - - struct drm_fb *dumb[2]; - pixman_image_t *image[2]; - int current_image; - pixman_region32_t previous_damage; - - struct vaapi_recorder *recorder; - struct wl_listener recorder_frame_listener; - - struct wl_event_source *pageflip_timer; - - bool virtual; - - submit_frame_cb virtual_submit_frame; -}; - -static inline struct drm_head * -to_drm_head(struct weston_head *base) -{ - return container_of(base, struct drm_head, base); -} - -static inline struct drm_output * -to_drm_output(struct weston_output *base) -{ - return container_of(base, struct drm_output, base); -} - -static inline struct drm_backend * -to_drm_backend(struct weston_compositor *base) -{ - return container_of(base->backend, struct drm_backend, base); -} - -static inline struct drm_mode * -to_drm_mode(struct weston_mode *base) -{ - return container_of(base, struct drm_mode, base); -} - -static inline const char * -drm_output_get_plane_type_name(struct drm_plane *p) -{ - switch (p->type) { - case WDRM_PLANE_TYPE_PRIMARY: - return "primary"; - case WDRM_PLANE_TYPE_CURSOR: - return "cursor"; - case WDRM_PLANE_TYPE_OVERLAY: - return "overlay"; - default: - assert(0); - break; - } -} - -struct drm_output * -drm_output_find_by_crtc(struct drm_backend *b, uint32_t crtc_id); - -struct drm_head * -drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id); - -static inline bool -drm_view_transform_supported(struct weston_view *ev, struct weston_output *output) -{ - struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport; - - /* This will incorrectly disallow cases where the combination of - * buffer and view transformations match the output transform. - * Fixing this requires a full analysis of the transformation - * chain. */ - if (ev->transform.enabled && - ev->transform.matrix.type >= WESTON_MATRIX_TRANSFORM_ROTATE) - return false; - - if (viewport->buffer.transform != output->transform) - return false; - - return true; -} - -int -drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode); - -struct drm_mode * -drm_output_choose_mode(struct drm_output *output, - struct weston_mode *target_mode); -void -update_head_from_connector(struct drm_head *head, - drmModeObjectProperties *props); - -void -drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list); - -void -drm_output_print_modes(struct drm_output *output); - -int -drm_output_set_mode(struct weston_output *base, - enum weston_drm_backend_output_mode mode, - const char *modeline); - -void -drm_property_info_populate(struct drm_backend *b, - const struct drm_property_info *src, - struct drm_property_info *info, - unsigned int num_infos, - drmModeObjectProperties *props); -uint64_t -drm_property_get_value(struct drm_property_info *info, - const drmModeObjectProperties *props, - uint64_t def); -uint64_t * -drm_property_get_range_values(struct drm_property_info *info, - const drmModeObjectProperties *props); -int -drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane, - const drmModeObjectProperties *props); -void -drm_property_info_free(struct drm_property_info *info, int num_props); - -extern struct drm_property_enum_info plane_type_enums[]; -extern const struct drm_property_info plane_props[]; -extern struct drm_property_enum_info dpms_state_enums[]; -extern struct drm_property_enum_info content_protection_enums[]; -extern struct drm_property_enum_info hdcp_content_type_enums[]; -extern const struct drm_property_info connector_props[]; -extern const struct drm_property_info crtc_props[]; - -int -init_kms_caps(struct drm_backend *b); - -int -drm_pending_state_test(struct drm_pending_state *pending_state); -int -drm_pending_state_apply(struct drm_pending_state *pending_state); -int -drm_pending_state_apply_sync(struct drm_pending_state *pending_state); - -void -drm_output_set_gamma(struct weston_output *output_base, - uint16_t size, uint16_t *r, uint16_t *g, uint16_t *b); - -void -drm_output_update_msc(struct drm_output *output, unsigned int seq); -void -drm_output_update_complete(struct drm_output *output, uint32_t flags, - unsigned int sec, unsigned int usec); -int -on_drm_input(int fd, uint32_t mask, void *data); - -struct drm_plane_state * -drm_output_state_get_existing_plane(struct drm_output_state *state_output, - struct drm_plane *plane); -void -drm_plane_state_free(struct drm_plane_state *state, bool force); -void -drm_output_state_free(struct drm_output_state *state); -void -drm_pending_state_free(struct drm_pending_state *pending_state); - -struct drm_fb * -drm_fb_ref(struct drm_fb *fb); -void -drm_fb_unref(struct drm_fb *fb); - -struct drm_fb * -drm_fb_create_dumb(struct drm_backend *b, int width, int height, - uint32_t format); -struct drm_fb * -drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend, - bool is_opaque, enum drm_fb_type type); - -#ifdef BUILD_DRM_GBM -extern struct drm_fb * -drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev); -extern bool -drm_can_scanout_dmabuf(struct weston_compositor *ec, - struct linux_dmabuf_buffer *dmabuf); -#else -static inline struct drm_fb * -drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev) -{ - return NULL; -} -static inline bool -drm_can_scanout_dmabuf(struct weston_compositor *ec, - struct linux_dmabuf_buffer *dmabuf) -{ - return false; -} -#endif - -struct drm_pending_state * -drm_pending_state_alloc(struct drm_backend *backend); -void -drm_pending_state_free(struct drm_pending_state *pending_state); -struct drm_output_state * -drm_pending_state_get_output(struct drm_pending_state *pending_state, - struct drm_output *output); - - -/** - * Mode for drm_output_state_duplicate. - */ -enum drm_output_state_duplicate_mode { - DRM_OUTPUT_STATE_CLEAR_PLANES, /**< reset all planes to off */ - DRM_OUTPUT_STATE_PRESERVE_PLANES, /**< preserve plane state */ -}; - -struct drm_output_state * -drm_output_state_alloc(struct drm_output *output, - struct drm_pending_state *pending_state); -struct drm_output_state * -drm_output_state_duplicate(struct drm_output_state *src, - struct drm_pending_state *pending_state, - enum drm_output_state_duplicate_mode plane_mode); -void -drm_output_state_free(struct drm_output_state *state); -struct drm_plane_state * -drm_output_state_get_plane(struct drm_output_state *state_output, - struct drm_plane *plane); -struct drm_plane_state * -drm_output_state_get_existing_plane(struct drm_output_state *state_output, - struct drm_plane *plane); - - - -struct drm_plane_state * -drm_plane_state_alloc(struct drm_output_state *state_output, - struct drm_plane *plane); -struct drm_plane_state * -drm_plane_state_duplicate(struct drm_output_state *state_output, - struct drm_plane_state *src); -void -drm_plane_state_free(struct drm_plane_state *state, bool force); -void -drm_plane_state_put_back(struct drm_plane_state *state); -bool -drm_plane_state_coords_for_view(struct drm_plane_state *state, - struct weston_view *ev, uint64_t zpos); - -void -drm_assign_planes(struct weston_output *output_base, void *repaint_data); - -bool -drm_plane_is_available(struct drm_plane *plane, struct drm_output *output); - -void -drm_output_render(struct drm_output_state *state, pixman_region32_t *damage); - -int -parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format); - -extern struct gl_renderer_interface *gl_renderer; - -#ifdef BUILD_DRM_VIRTUAL -extern int -drm_backend_init_virtual_output_api(struct weston_compositor *compositor); -#else -inline static int -drm_backend_init_virtual_output_api(struct weston_compositor *compositor) -{ - return 0; -} -#endif - -#ifdef BUILD_DRM_GBM -int -init_egl(struct drm_backend *b); - -int -drm_output_init_egl(struct drm_output *output, struct drm_backend *b); - -void -drm_output_fini_egl(struct drm_output *output); - -struct drm_fb * -drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage); - -void -renderer_switch_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, void *data); -#else -inline static int -init_egl(struct drm_backend *b) -{ - weston_log("Compiled without GBM/EGL support\n"); - return -1; -} - -inline static int -drm_output_init_egl(struct drm_output *output, struct drm_backend *b) -{ - return -1; -} - -inline static void -drm_output_fini_egl(struct drm_output *output) -{ -} - -inline static struct drm_fb * -drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) -{ - return NULL; -} - -inline static void -renderer_switch_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, void *data) -{ - weston_log("Compiled without GBM/EGL support\n"); -} -#endif diff --git a/libweston/backend-drm/drm-virtual.c b/libweston/backend-drm/drm-virtual.c deleted file mode 100644 index ebebbbd2..00000000 --- a/libweston/backend-drm/drm-virtual.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2017, 2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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 <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <ctype.h> -#include <string.h> - -#include "drm-internal.h" -#include "renderer-gl/gl-renderer.h" - -/** - * Create a drm_plane for virtual output - * - * Call drm_virtual_plane_destroy to clean up the plane. - * - * @param b DRM compositor backend - * @param output Output to create internal plane for - */ -static struct drm_plane * -drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output) -{ - struct drm_plane *plane; - - /* num of formats is one */ - plane = zalloc(sizeof(*plane) + sizeof(plane->formats[0])); - if (!plane) { - weston_log("%s: out of memory\n", __func__); - return NULL; - } - - plane->type = WDRM_PLANE_TYPE_PRIMARY; - plane->backend = b; - plane->state_cur = drm_plane_state_alloc(NULL, plane); - plane->state_cur->complete = true; - plane->formats[0].format = output->gbm_format; - plane->count_formats = 1; - if ((output->gbm_bo_flags & GBM_BO_USE_LINEAR) && b->fb_modifiers) { - uint64_t *modifiers = zalloc(sizeof *modifiers); - if (modifiers) { - *modifiers = DRM_FORMAT_MOD_LINEAR; - plane->formats[0].modifiers = modifiers; - plane->formats[0].count_modifiers = 1; - } - } - - weston_plane_init(&plane->base, b->compositor, 0, 0); - wl_list_insert(&b->plane_list, &plane->link); - - return plane; -} - -/** - * Destroy one DRM plane - * - * @param plane Plane to deallocate (will be freed) - */ -static void -drm_virtual_plane_destroy(struct drm_plane *plane) -{ - drm_plane_state_free(plane->state_cur, true); - weston_plane_release(&plane->base); - wl_list_remove(&plane->link); - if (plane->formats[0].modifiers) - free(plane->formats[0].modifiers); - free(plane); -} - -static int -drm_virtual_output_start_repaint_loop(struct weston_output *output_base) -{ - weston_output_finish_frame(output_base, NULL, - WP_PRESENTATION_FEEDBACK_INVALID); - - return 0; -} - -static int -drm_virtual_output_submit_frame(struct drm_output *output, - struct drm_fb *fb) -{ - struct drm_backend *b = to_drm_backend(output->base.compositor); - int fd, ret; - - assert(fb->num_planes == 1); - ret = drmPrimeHandleToFD(b->drm.fd, fb->handles[0], DRM_CLOEXEC, &fd); - if (ret) { - weston_log("drmPrimeHandleFD failed, errno=%d\n", errno); - return -1; - } - - drm_fb_ref(fb); - ret = output->virtual_submit_frame(&output->base, fd, fb->strides[0], - fb); - if (ret < 0) { - drm_fb_unref(fb); - close(fd); - } - return ret; -} - -static int -drm_virtual_output_repaint(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) -{ - struct drm_pending_state *pending_state = repaint_data; - struct drm_output_state *state = NULL; - struct drm_output *output = to_drm_output(output_base); - struct drm_plane *scanout_plane = output->scanout_plane; - struct drm_plane_state *scanout_state; - - assert(output->virtual); - - if (output->disable_pending || output->destroy_pending) - goto err; - - /* Drop frame if there isn't free buffers */ - if (!gbm_surface_has_free_buffers(output->gbm_surface)) { - weston_log("%s: Drop frame!!\n", __func__); - return -1; - } - - assert(!output->state_last); - - /* If planes have been disabled in the core, we might not have - * hit assign_planes at all, so might not have valid output state - * here. */ - state = drm_pending_state_get_output(pending_state, output); - if (!state) - state = drm_output_state_duplicate(output->state_cur, - pending_state, - DRM_OUTPUT_STATE_CLEAR_PLANES); - - drm_output_render(state, damage); - scanout_state = drm_output_state_get_plane(state, scanout_plane); - if (!scanout_state || !scanout_state->fb) - goto err; - - if (drm_virtual_output_submit_frame(output, scanout_state->fb) < 0) - goto err; - - return 0; - -err: - drm_output_state_free(state); - return -1; -} - -static void -drm_virtual_output_deinit(struct weston_output *base) -{ - struct drm_output *output = to_drm_output(base); - - drm_output_fini_egl(output); - - drm_virtual_plane_destroy(output->scanout_plane); -} - -static void -drm_virtual_output_destroy(struct weston_output *base) -{ - struct drm_output *output = to_drm_output(base); - - assert(output->virtual); - - if (output->base.enabled) - drm_virtual_output_deinit(&output->base); - - weston_output_release(&output->base); - - drm_output_state_free(output->state_cur); - - free(output); -} - -static int -drm_virtual_output_enable(struct weston_output *output_base) -{ - struct drm_output *output = to_drm_output(output_base); - struct drm_backend *b = to_drm_backend(output_base->compositor); - - assert(output->virtual); - - if (b->use_pixman) { - weston_log("Not support pixman renderer on Virtual output\n"); - goto err; - } - - if (!output->virtual_submit_frame) { - weston_log("The virtual_submit_frame hook is not set\n"); - goto err; - } - - output->scanout_plane = drm_virtual_plane_create(b, output); - if (!output->scanout_plane) { - weston_log("Failed to find primary plane for output %s\n", - output->base.name); - return -1; - } - - if (drm_output_init_egl(output, b) < 0) { - weston_log("Failed to init output gl state\n"); - goto err; - } - - output->base.start_repaint_loop = drm_virtual_output_start_repaint_loop; - output->base.repaint = drm_virtual_output_repaint; - output->base.assign_planes = drm_assign_planes; - output->base.set_dpms = NULL; - output->base.switch_mode = NULL; - output->base.gamma_size = 0; - output->base.set_gamma = NULL; - - weston_compositor_stack_plane(b->compositor, - &output->scanout_plane->base, - &b->compositor->primary_plane); - - return 0; -err: - return -1; -} - -static int -drm_virtual_output_disable(struct weston_output *base) -{ - struct drm_output *output = to_drm_output(base); - - assert(output->virtual); - - if (output->base.enabled) - drm_virtual_output_deinit(&output->base); - - return 0; -} - -static struct weston_output * -drm_virtual_output_create(struct weston_compositor *c, char *name) -{ - struct drm_output *output; - - output = zalloc(sizeof *output); - if (!output) - return NULL; - - output->virtual = true; - output->gbm_bo_flags = GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING; - - weston_output_init(&output->base, c, name); - - output->base.enable = drm_virtual_output_enable; - output->base.destroy = drm_virtual_output_destroy; - output->base.disable = drm_virtual_output_disable; - output->base.attach_head = NULL; - - output->state_cur = drm_output_state_alloc(output, NULL); - - weston_compositor_add_pending_output(&output->base, c); - - return &output->base; -} - -static uint32_t -drm_virtual_output_set_gbm_format(struct weston_output *base, - const char *gbm_format) -{ - struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); - - if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1) - output->gbm_format = b->gbm_format; - - return output->gbm_format; -} - -static void -drm_virtual_output_set_submit_frame_cb(struct weston_output *output_base, - submit_frame_cb cb) -{ - struct drm_output *output = to_drm_output(output_base); - - output->virtual_submit_frame = cb; -} - -static int -drm_virtual_output_get_fence_fd(struct weston_output *output_base) -{ - return gl_renderer->create_fence_fd(output_base); -} - -static void -drm_virtual_output_buffer_released(struct drm_fb *fb) -{ - drm_fb_unref(fb); -} - -static void -drm_virtual_output_finish_frame(struct weston_output *output_base, - struct timespec *stamp, - uint32_t presented_flags) -{ - struct drm_output *output = to_drm_output(output_base); - struct drm_plane_state *ps; - - wl_list_for_each(ps, &output->state_cur->plane_list, link) - ps->complete = true; - - drm_output_state_free(output->state_last); - output->state_last = NULL; - - weston_output_finish_frame(&output->base, stamp, presented_flags); - - /* We can't call this from frame_notify, because the output's - * repaint needed flag is cleared just after that */ - if (output->recorder) - weston_output_schedule_repaint(&output->base); -} - -static const struct weston_drm_virtual_output_api virt_api = { - drm_virtual_output_create, - drm_virtual_output_set_gbm_format, - drm_virtual_output_set_submit_frame_cb, - drm_virtual_output_get_fence_fd, - drm_virtual_output_buffer_released, - drm_virtual_output_finish_frame -}; - -int drm_backend_init_virtual_output_api(struct weston_compositor *compositor) -{ - return weston_plugin_api_register(compositor, - WESTON_DRM_VIRTUAL_OUTPUT_API_NAME, - &virt_api, sizeof(virt_api)); -} diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c deleted file mode 100644 index e0b1cbd7..00000000 --- a/libweston/backend-drm/drm.c +++ /dev/null @@ -1,3040 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2017, 2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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 <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <ctype.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <linux/input.h> -#include <linux/vt.h> -#include <assert.h> -#include <sys/mman.h> -#include <time.h> - -#include <xf86drm.h> -#include <xf86drmMode.h> -#include <drm_fourcc.h> - -#include <libudev.h> - -#include <libweston/libweston.h> -#include <libweston/backend-drm.h> -#include <libweston/weston-log.h> -#include "drm-internal.h" -#include "shared/helpers.h" -#include "shared/timespec-util.h" -#include "shared/string-helpers.h" -#include "pixman-renderer.h" -#include "pixel-formats.h" -#include "libbacklight.h" -#include "libinput-seat.h" -#include "launcher-util.h" -#include "vaapi-recorder.h" -#include "presentation-time-server-protocol.h" -#include "linux-dmabuf.h" -#include "linux-dmabuf-unstable-v1-server-protocol.h" -#include "linux-explicit-synchronization.h" - -static const char default_seat[] = "seat0"; - -static void -drm_backend_create_faked_zpos(struct drm_backend *b) -{ - struct drm_plane *plane; - uint64_t zpos = 0ULL; - uint64_t zpos_min_primary; - uint64_t zpos_min_overlay; - uint64_t zpos_min_cursor; - - zpos_min_primary = zpos; - wl_list_for_each(plane, &b->plane_list, link) { - /* if the property is there, bail out sooner */ - if (plane->props[WDRM_PLANE_ZPOS].prop_id != 0) - return; - - if (plane->type != WDRM_PLANE_TYPE_PRIMARY) - continue; - zpos++; - } - - zpos_min_overlay = zpos; - wl_list_for_each(plane, &b->plane_list, link) { - if (plane->type != WDRM_PLANE_TYPE_OVERLAY) - continue; - zpos++; - } - - zpos_min_cursor = zpos; - wl_list_for_each(plane, &b->plane_list, link) { - if (plane->type != WDRM_PLANE_TYPE_CURSOR) - continue; - zpos++; - } - - drm_debug(b, "[drm-backend] zpos property not found. " - "Using invented immutable zpos values:\n"); - /* assume that invented zpos values are immutable */ - wl_list_for_each(plane, &b->plane_list, link) { - if (plane->type == WDRM_PLANE_TYPE_PRIMARY) { - plane->zpos_min = zpos_min_primary; - plane->zpos_max = zpos_min_primary; - } else if (plane->type == WDRM_PLANE_TYPE_OVERLAY) { - plane->zpos_min = zpos_min_overlay; - plane->zpos_max = zpos_min_overlay; - } else if (plane->type == WDRM_PLANE_TYPE_CURSOR) { - plane->zpos_min = zpos_min_cursor; - plane->zpos_max = zpos_min_cursor; - } - drm_debug(b, "\t[plane] %s plane %d, zpos_min %"PRIu64", " - "zpos_max %"PRIu64"\n", - drm_output_get_plane_type_name(plane), - plane->plane_id, plane->zpos_min, plane->zpos_max); - } -} - -static void -wl_array_remove_uint32(struct wl_array *array, uint32_t elm) -{ - uint32_t *pos, *end; - - end = (uint32_t *) ((char *) array->data + array->size); - - wl_array_for_each(pos, array) { - if (*pos != elm) - continue; - - array->size -= sizeof(*pos); - if (pos + 1 == end) - break; - - memmove(pos, pos + 1, (char *) end - (char *) (pos + 1)); - break; - } -} - -static int -pageflip_timeout(void *data) { - /* - * Our timer just went off, that means we're not receiving drm - * page flip events anymore for that output. Let's gracefully exit - * weston with a return value so devs can debug what's going on. - */ - struct drm_output *output = data; - struct weston_compositor *compositor = output->base.compositor; - - weston_log("Pageflip timeout reached on output %s, your " - "driver is probably buggy! Exiting.\n", - output->base.name); - weston_compositor_exit_with_code(compositor, EXIT_FAILURE); - - return 0; -} - -/* Creates the pageflip timer. Note that it isn't armed by default */ -static int -drm_output_pageflip_timer_create(struct drm_output *output) -{ - struct wl_event_loop *loop = NULL; - struct weston_compositor *ec = output->base.compositor; - - loop = wl_display_get_event_loop(ec->wl_display); - assert(loop); - output->pageflip_timer = wl_event_loop_add_timer(loop, - pageflip_timeout, - output); - - if (output->pageflip_timer == NULL) { - weston_log("creating drm pageflip timer failed: %s\n", - strerror(errno)); - return -1; - } - - return 0; -} - -static void -drm_output_destroy(struct weston_output *output_base); - -/** - * Returns true if the plane can be used on the given output for its current - * repaint cycle. - */ -bool -drm_plane_is_available(struct drm_plane *plane, struct drm_output *output) -{ - assert(plane->state_cur); - - if (output->virtual) - return false; - - /* The plane still has a request not yet completed by the kernel. */ - if (!plane->state_cur->complete) - return false; - - /* The plane is still active on another output. */ - if (plane->state_cur->output && plane->state_cur->output != output) - return false; - - /* Check whether the plane can be used with this CRTC; possible_crtcs - * is a bitmask of CRTC indices (pipe), rather than CRTC object ID. */ - return !!(plane->possible_crtcs & (1 << output->pipe)); -} - -struct drm_output * -drm_output_find_by_crtc(struct drm_backend *b, uint32_t crtc_id) -{ - struct drm_output *output; - - wl_list_for_each(output, &b->compositor->output_list, base.link) { - if (output->crtc_id == crtc_id) - return output; - } - - return NULL; -} - -struct drm_head * -drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id) -{ - struct weston_head *base; - struct drm_head *head; - - wl_list_for_each(base, - &backend->compositor->head_list, compositor_link) { - head = to_drm_head(base); - if (head->connector_id == connector_id) - return head; - } - - return NULL; -} - -/** - * Get output state to disable output - * - * Returns a pointer to an output_state object which can be used to disable - * an output (e.g. DPMS off). - * - * @param pending_state The pending state object owning this update - * @param output The output to disable - * @returns A drm_output_state to disable the output - */ -static struct drm_output_state * -drm_output_get_disable_state(struct drm_pending_state *pending_state, - struct drm_output *output) -{ - struct drm_output_state *output_state; - - output_state = drm_output_state_duplicate(output->state_cur, - pending_state, - DRM_OUTPUT_STATE_CLEAR_PLANES); - output_state->dpms = WESTON_DPMS_OFF; - - output_state->protection = WESTON_HDCP_DISABLE; - - return output_state; -} - - -/** - * Mark a drm_output_state (the output's last state) as complete. This handles - * any post-completion actions such as updating the repaint timer, disabling the - * output, and finally freeing the state. - */ -void -drm_output_update_complete(struct drm_output *output, uint32_t flags, - unsigned int sec, unsigned int usec) -{ - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_plane_state *ps; - struct timespec ts; - - /* Stop the pageflip timer instead of rearming it here */ - if (output->pageflip_timer) - wl_event_source_timer_update(output->pageflip_timer, 0); - - wl_list_for_each(ps, &output->state_cur->plane_list, link) - ps->complete = true; - - drm_output_state_free(output->state_last); - output->state_last = NULL; - - if (output->destroy_pending) { - output->destroy_pending = false; - output->disable_pending = false; - output->dpms_off_pending = false; - drm_output_destroy(&output->base); - return; - } else if (output->disable_pending) { - output->disable_pending = false; - output->dpms_off_pending = false; - weston_output_disable(&output->base); - return; - } else if (output->dpms_off_pending) { - struct drm_pending_state *pending = drm_pending_state_alloc(b); - output->dpms_off_pending = false; - drm_output_get_disable_state(pending, output); - drm_pending_state_apply_sync(pending); - } else if (output->state_cur->dpms == WESTON_DPMS_OFF && - output->base.repaint_status != REPAINT_AWAITING_COMPLETION) { - /* DPMS can happen to us either in the middle of a repaint - * cycle (when we have painted fresh content, only to throw it - * away for DPMS off), or at any other random point. If the - * latter is true, then we cannot go through finish_frame, - * because the repaint machinery does not expect this. */ - return; - } - - ts.tv_sec = sec; - ts.tv_nsec = usec * 1000; - weston_output_finish_frame(&output->base, &ts, flags); - - /* We can't call this from frame_notify, because the output's - * repaint needed flag is cleared just after that */ - if (output->recorder) - weston_output_schedule_repaint(&output->base); -} - -static struct drm_fb * -drm_output_render_pixman(struct drm_output_state *state, - pixman_region32_t *damage) -{ - struct drm_output *output = state->output; - struct weston_compositor *ec = output->base.compositor; - - output->current_image ^= 1; - - pixman_renderer_output_set_buffer(&output->base, - output->image[output->current_image]); - pixman_renderer_output_set_hw_extra_damage(&output->base, - &output->previous_damage); - - ec->renderer->repaint_output(&output->base, damage); - - pixman_region32_copy(&output->previous_damage, damage); - - return drm_fb_ref(output->dumb[output->current_image]); -} - -void -drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) -{ - struct drm_output *output = state->output; - struct weston_compositor *c = output->base.compositor; - struct drm_plane_state *scanout_state; - struct drm_plane *scanout_plane = output->scanout_plane; - struct drm_backend *b = to_drm_backend(c); - struct drm_fb *fb; - - /* If we already have a client buffer promoted to scanout, then we don't - * want to render. */ - scanout_state = drm_output_state_get_plane(state, - output->scanout_plane); - if (scanout_state->fb) - return; - - if (!pixman_region32_not_empty(damage) && - scanout_plane->state_cur->fb && - (scanout_plane->state_cur->fb->type == BUFFER_GBM_SURFACE || - scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB) && - scanout_plane->state_cur->fb->width == - output->base.current_mode->width && - scanout_plane->state_cur->fb->height == - output->base.current_mode->height) { - fb = drm_fb_ref(scanout_plane->state_cur->fb); - } else if (b->use_pixman) { - fb = drm_output_render_pixman(state, damage); - } else { - fb = drm_output_render_gl(state, damage); - } - - if (!fb) { - drm_plane_state_put_back(scanout_state); - return; - } - - scanout_state->fb = fb; - scanout_state->output = output; - - scanout_state->src_x = 0; - scanout_state->src_y = 0; - scanout_state->src_w = output->base.current_mode->width << 16; - scanout_state->src_h = output->base.current_mode->height << 16; - - scanout_state->dest_x = 0; - scanout_state->dest_y = 0; - scanout_state->dest_w = scanout_state->src_w >> 16; - scanout_state->dest_h = scanout_state->src_h >> 16; - - pixman_region32_copy(&scanout_state->damage, damage); - if (output->base.zoom.active) { - weston_matrix_transform_region(&scanout_state->damage, - &output->base.matrix, - &scanout_state->damage); - } else { - pixman_region32_translate(&scanout_state->damage, - -output->base.x, -output->base.y); - weston_transformed_region(output->base.width, - output->base.height, - output->base.transform, - output->base.current_scale, - &scanout_state->damage, - &scanout_state->damage); - } - - pixman_region32_subtract(&c->primary_plane.damage, - &c->primary_plane.damage, damage); -} - -static int -drm_output_repaint(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) -{ - struct drm_pending_state *pending_state = repaint_data; - struct drm_output *output = to_drm_output(output_base); - struct drm_output_state *state = NULL; - struct drm_plane_state *scanout_state; - - assert(!output->virtual); - - if (output->disable_pending || output->destroy_pending) - goto err; - - assert(!output->state_last); - - /* If planes have been disabled in the core, we might not have - * hit assign_planes at all, so might not have valid output state - * here. */ - state = drm_pending_state_get_output(pending_state, output); - if (!state) - state = drm_output_state_duplicate(output->state_cur, - pending_state, - DRM_OUTPUT_STATE_CLEAR_PLANES); - state->dpms = WESTON_DPMS_ON; - - if (output_base->allow_protection) - state->protection = output_base->desired_protection; - else - state->protection = WESTON_HDCP_DISABLE; - - drm_output_render(state, damage); - scanout_state = drm_output_state_get_plane(state, - output->scanout_plane); - if (!scanout_state || !scanout_state->fb) - goto err; - - return 0; - -err: - drm_output_state_free(state); - return -1; -} - -/* Determine the type of vblank synchronization to use for the output. - * - * The pipe parameter indicates which CRTC is in use. Knowing this, we - * can determine which vblank sequence type to use for it. Traditional - * cards had only two CRTCs, with CRTC 0 using no special flags, and - * CRTC 1 using DRM_VBLANK_SECONDARY. The first bit of the pipe - * parameter indicates this. - * - * Bits 1-5 of the pipe parameter are 5 bit wide pipe number between - * 0-31. If this is non-zero it indicates we're dealing with a - * multi-gpu situation and we need to calculate the vblank sync - * using DRM_BLANK_HIGH_CRTC_MASK. - */ -static unsigned int -drm_waitvblank_pipe(struct drm_output *output) -{ - if (output->pipe > 1) - return (output->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT) & - DRM_VBLANK_HIGH_CRTC_MASK; - else if (output->pipe > 0) - return DRM_VBLANK_SECONDARY; - else - return 0; -} - -static int -drm_output_start_repaint_loop(struct weston_output *output_base) -{ - struct drm_output *output = to_drm_output(output_base); - struct drm_pending_state *pending_state; - struct drm_plane *scanout_plane = output->scanout_plane; - struct drm_backend *backend = - to_drm_backend(output_base->compositor); - struct timespec ts, tnow; - struct timespec vbl2now; - int64_t refresh_nsec; - int ret; - drmVBlank vbl = { - .request.type = DRM_VBLANK_RELATIVE, - .request.sequence = 0, - .request.signal = 0, - }; - - if (output->disable_pending || output->destroy_pending) - return 0; - - if (!output->scanout_plane->state_cur->fb) { - /* We can't page flip if there's no mode set */ - goto finish_frame; - } - - /* Need to smash all state in from scratch; current timings might not - * be what we want, page flip might not work, etc. - */ - if (backend->state_invalid) - goto finish_frame; - - assert(scanout_plane->state_cur->output == output); - - /* Try to get current msc and timestamp via instant query */ - vbl.request.type |= drm_waitvblank_pipe(output); - ret = drmWaitVBlank(backend->drm.fd, &vbl); - - /* Error ret or zero timestamp means failure to get valid timestamp */ - if ((ret == 0) && (vbl.reply.tval_sec > 0 || vbl.reply.tval_usec > 0)) { - ts.tv_sec = vbl.reply.tval_sec; - ts.tv_nsec = vbl.reply.tval_usec * 1000; - - /* Valid timestamp for most recent vblank - not stale? - * Stale ts could happen on Linux 3.17+, so make sure it - * is not older than 1 refresh duration since now. - */ - weston_compositor_read_presentation_clock(backend->compositor, - &tnow); - timespec_sub(&vbl2now, &tnow, &ts); - refresh_nsec = - millihz_to_nsec(output->base.current_mode->refresh); - if (timespec_to_nsec(&vbl2now) < refresh_nsec) { - drm_output_update_msc(output, vbl.reply.sequence); - weston_output_finish_frame(output_base, &ts, - WP_PRESENTATION_FEEDBACK_INVALID); - return 0; - } - } - - /* Immediate query didn't provide valid timestamp. - * Use pageflip fallback. - */ - - assert(!output->page_flip_pending); - assert(!output->state_last); - - pending_state = drm_pending_state_alloc(backend); - drm_output_state_duplicate(output->state_cur, pending_state, - DRM_OUTPUT_STATE_PRESERVE_PLANES); - - ret = drm_pending_state_apply(pending_state); - if (ret != 0) { - weston_log("applying repaint-start state failed: %s\n", - strerror(errno)); - if (ret == -EACCES) - return -1; - goto finish_frame; - } - - return 0; - -finish_frame: - /* if we cannot page-flip, immediately finish frame */ - weston_output_finish_frame(output_base, NULL, - WP_PRESENTATION_FEEDBACK_INVALID); - return 0; -} - -/** - * Begin a new repaint cycle - * - * Called by the core compositor at the beginning of a repaint cycle. Creates - * a new pending_state structure to own any output state created by individual - * output repaint functions until the repaint is flushed or cancelled. - */ -static void * -drm_repaint_begin(struct weston_compositor *compositor) -{ - struct drm_backend *b = to_drm_backend(compositor); - struct drm_pending_state *ret; - - ret = drm_pending_state_alloc(b); - b->repaint_data = ret; - - if (weston_log_scope_is_enabled(b->debug)) { - char *dbg = weston_compositor_print_scene_graph(compositor); - drm_debug(b, "[repaint] Beginning repaint; pending_state %p\n", - ret); - drm_debug(b, "%s", dbg); - free(dbg); - } - - return ret; -} - -/** - * Flush a repaint set - * - * Called by the core compositor when a repaint cycle has been completed - * and should be flushed. Frees the pending state, transitioning ownership - * of the output state from the pending state, to the update itself. When - * the update completes (see drm_output_update_complete), the output - * state will be freed. - */ -static int -drm_repaint_flush(struct weston_compositor *compositor, void *repaint_data) -{ - struct drm_backend *b = to_drm_backend(compositor); - struct drm_pending_state *pending_state = repaint_data; - int ret; - - ret = drm_pending_state_apply(pending_state); - if (ret != 0) - weston_log("repaint-flush failed: %s\n", strerror(errno)); - - drm_debug(b, "[repaint] flushed pending_state %p\n", pending_state); - b->repaint_data = NULL; - - return (ret == -EACCES) ? -1 : 0; -} - -/** - * Cancel a repaint set - * - * Called by the core compositor when a repaint has finished, so the data - * held across the repaint cycle should be discarded. - */ -static void -drm_repaint_cancel(struct weston_compositor *compositor, void *repaint_data) -{ - struct drm_backend *b = to_drm_backend(compositor); - struct drm_pending_state *pending_state = repaint_data; - - drm_pending_state_free(pending_state); - drm_debug(b, "[repaint] cancel pending_state %p\n", pending_state); - b->repaint_data = NULL; -} - -static int -drm_output_init_pixman(struct drm_output *output, struct drm_backend *b); -static void -drm_output_fini_pixman(struct drm_output *output); - -static int -drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode) -{ - struct drm_output *output = to_drm_output(output_base); - struct drm_backend *b = to_drm_backend(output_base->compositor); - struct drm_mode *drm_mode = drm_output_choose_mode(output, mode); - - if (!drm_mode) { - weston_log("%s: invalid resolution %dx%d\n", - output_base->name, mode->width, mode->height); - return -1; - } - - if (&drm_mode->base == output->base.current_mode) - return 0; - - output->base.current_mode->flags = 0; - - output->base.current_mode = &drm_mode->base; - output->base.current_mode->flags = - WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; - - /* XXX: This drops our current buffer too early, before we've started - * displaying it. Ideally this should be much more atomic and - * integrated with a full repaint cycle, rather than doing a - * sledgehammer modeswitch first, and only later showing new - * content. - */ - b->state_invalid = true; - - if (b->use_pixman) { - drm_output_fini_pixman(output); - if (drm_output_init_pixman(output, b) < 0) { - weston_log("failed to init output pixman state with " - "new mode\n"); - return -1; - } - } else { - drm_output_fini_egl(output); - if (drm_output_init_egl(output, b) < 0) { - weston_log("failed to init output egl state with " - "new mode"); - return -1; - } - } - - return 0; -} - -static int -init_pixman(struct drm_backend *b) -{ - return pixman_renderer_init(b->compositor); -} - -/** - * Create a drm_plane for a hardware plane - * - * Creates one drm_plane structure for a hardware plane, and initialises its - * properties and formats. - * - * In the absence of universal plane support, where KMS does not explicitly - * expose the primary and cursor planes to userspace, this may also create - * an 'internal' plane for internal management. - * - * This function does not add the plane to the list of usable planes in Weston - * itself; the caller is responsible for this. - * - * Call drm_plane_destroy to clean up the plane. - * - * @sa drm_output_find_special_plane - * @param b DRM compositor backend - * @param kplane DRM plane to create, or NULL if creating internal plane - * @param output Output to create internal plane for, or NULL - * @param type Type to use when creating internal plane, or invalid - * @param format Format to use for internal planes, or 0 - */ -static struct drm_plane * -drm_plane_create(struct drm_backend *b, const drmModePlane *kplane, - struct drm_output *output, enum wdrm_plane_type type, - uint32_t format) -{ - struct drm_plane *plane; - drmModeObjectProperties *props; - uint64_t *zpos_range_values; - uint32_t num_formats = (kplane) ? kplane->count_formats : 1; - - plane = zalloc(sizeof(*plane) + - (sizeof(plane->formats[0]) * num_formats)); - if (!plane) { - weston_log("%s: out of memory\n", __func__); - return NULL; - } - - plane->backend = b; - plane->count_formats = num_formats; - plane->state_cur = drm_plane_state_alloc(NULL, plane); - plane->state_cur->complete = true; - - if (kplane) { - plane->possible_crtcs = kplane->possible_crtcs; - plane->plane_id = kplane->plane_id; - - props = drmModeObjectGetProperties(b->drm.fd, kplane->plane_id, - DRM_MODE_OBJECT_PLANE); - if (!props) { - weston_log("couldn't get plane properties\n"); - goto err; - } - drm_property_info_populate(b, plane_props, plane->props, - WDRM_PLANE__COUNT, props); - plane->type = - drm_property_get_value(&plane->props[WDRM_PLANE_TYPE], - props, - WDRM_PLANE_TYPE__COUNT); - - zpos_range_values = - drm_property_get_range_values(&plane->props[WDRM_PLANE_ZPOS], - props); - - if (zpos_range_values) { - plane->zpos_min = zpos_range_values[0]; - plane->zpos_max = zpos_range_values[1]; - } else { - plane->zpos_min = DRM_PLANE_ZPOS_INVALID_PLANE; - plane->zpos_max = DRM_PLANE_ZPOS_INVALID_PLANE; - } - - if (drm_plane_populate_formats(plane, kplane, props) < 0) { - drmModeFreeObjectProperties(props); - goto err; - } - - drmModeFreeObjectProperties(props); - } - else { - plane->possible_crtcs = (1 << output->pipe); - plane->plane_id = 0; - plane->count_formats = 1; - plane->formats[0].format = format; - plane->type = type; - plane->zpos_max = DRM_PLANE_ZPOS_INVALID_PLANE; - plane->zpos_min = DRM_PLANE_ZPOS_INVALID_PLANE; - } - - if (plane->type == WDRM_PLANE_TYPE__COUNT) - goto err_props; - - /* With universal planes, everything is a DRM plane; without - * universal planes, the only DRM planes are overlay planes. - * Everything else is a fake plane. */ - if (b->universal_planes) { - assert(kplane); - } else { - if (kplane) - assert(plane->type == WDRM_PLANE_TYPE_OVERLAY); - else - assert(plane->type != WDRM_PLANE_TYPE_OVERLAY && - output); - } - - weston_plane_init(&plane->base, b->compositor, 0, 0); - wl_list_insert(&b->plane_list, &plane->link); - - return plane; - -err_props: - drm_property_info_free(plane->props, WDRM_PLANE__COUNT); -err: - drm_plane_state_free(plane->state_cur, true); - free(plane); - return NULL; -} - -/** - * Find, or create, a special-purpose plane - * - * Primary and cursor planes are a special case, in that before universal - * planes, they are driven by non-plane API calls. Without universal plane - * support, the only way to configure a primary plane is via drmModeSetCrtc, - * and the only way to configure a cursor plane is drmModeSetCursor2. - * - * Although they may actually be regular planes in the hardware, without - * universal plane support, these planes are not actually exposed to - * userspace in the regular plane list. - * - * However, for ease of internal tracking, we want to manage all planes - * through the same drm_plane structures. Therefore, when we are running - * without universal plane support, we create fake drm_plane structures - * to track these planes. - * - * @param b DRM backend - * @param output Output to use for plane - * @param type Type of plane - */ -static struct drm_plane * -drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output, - enum wdrm_plane_type type) -{ - struct drm_plane *plane; - - if (!b->universal_planes) { - uint32_t format; - - switch (type) { - case WDRM_PLANE_TYPE_CURSOR: - format = DRM_FORMAT_ARGB8888; - break; - case WDRM_PLANE_TYPE_PRIMARY: - /* We don't know what formats the primary plane supports - * before universal planes, so we just assume that the - * GBM format works; however, this isn't set until after - * the output is created. */ - format = 0; - break; - default: - assert(!"invalid type in drm_output_find_special_plane"); - break; - } - - return drm_plane_create(b, NULL, output, type, format); - } - - wl_list_for_each(plane, &b->plane_list, link) { - struct drm_output *tmp; - bool found_elsewhere = false; - - if (plane->type != type) - continue; - if (!drm_plane_is_available(plane, output)) - continue; - - /* On some platforms, primary/cursor planes can roam - * between different CRTCs, so make sure we don't claim the - * same plane for two outputs. */ - wl_list_for_each(tmp, &b->compositor->output_list, - base.link) { - if (tmp->cursor_plane == plane || - tmp->scanout_plane == plane) { - found_elsewhere = true; - break; - } - } - - if (found_elsewhere) - continue; - - plane->possible_crtcs = (1 << output->pipe); - return plane; - } - - return NULL; -} - -/** - * Destroy one DRM plane - * - * Destroy a DRM plane, removing it from screen and releasing its retained - * buffers in the process. The counterpart to drm_plane_create. - * - * @param plane Plane to deallocate (will be freed) - */ -static void -drm_plane_destroy(struct drm_plane *plane) -{ - if (plane->type == WDRM_PLANE_TYPE_OVERLAY) - drmModeSetPlane(plane->backend->drm.fd, plane->plane_id, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - drm_plane_state_free(plane->state_cur, true); - drm_property_info_free(plane->props, WDRM_PLANE__COUNT); - weston_plane_release(&plane->base); - wl_list_remove(&plane->link); - free(plane); -} - -/** - * Initialise sprites (overlay planes) - * - * Walk the list of provided DRM planes, and add overlay planes. - * - * Call destroy_sprites to free these planes. - * - * @param b DRM compositor backend - */ -static void -create_sprites(struct drm_backend *b) -{ - drmModePlaneRes *kplane_res; - drmModePlane *kplane; - struct drm_plane *drm_plane; - uint32_t i; - kplane_res = drmModeGetPlaneResources(b->drm.fd); - if (!kplane_res) { - weston_log("failed to get plane resources: %s\n", - strerror(errno)); - return; - } - - for (i = 0; i < kplane_res->count_planes; i++) { - kplane = drmModeGetPlane(b->drm.fd, kplane_res->planes[i]); - if (!kplane) - continue; - - drm_plane = drm_plane_create(b, kplane, NULL, - WDRM_PLANE_TYPE__COUNT, 0); - drmModeFreePlane(kplane); - if (!drm_plane) - continue; - - if (drm_plane->type == WDRM_PLANE_TYPE_OVERLAY) - weston_compositor_stack_plane(b->compositor, - &drm_plane->base, - &b->compositor->primary_plane); - } - - drmModeFreePlaneResources(kplane_res); -} - -/** - * Clean up sprites (overlay planes) - * - * The counterpart to create_sprites. - * - * @param b DRM compositor backend - */ -static void -destroy_sprites(struct drm_backend *b) -{ - struct drm_plane *plane, *next; - - wl_list_for_each_safe(plane, next, &b->plane_list, link) - drm_plane_destroy(plane); -} - -/* returns a value between 0-255 range, where higher is brighter */ -static uint32_t -drm_get_backlight(struct drm_head *head) -{ - long brightness, max_brightness, norm; - - brightness = backlight_get_brightness(head->backlight); - max_brightness = backlight_get_max_brightness(head->backlight); - - /* convert it on a scale of 0 to 255 */ - norm = (brightness * 255)/(max_brightness); - - return (uint32_t) norm; -} - -/* values accepted are between 0-255 range */ -static void -drm_set_backlight(struct weston_output *output_base, uint32_t value) -{ - struct drm_output *output = to_drm_output(output_base); - struct drm_head *head; - long max_brightness, new_brightness; - - if (value > 255) - return; - - wl_list_for_each(head, &output->base.head_list, base.output_link) { - if (!head->backlight) - return; - - max_brightness = backlight_get_max_brightness(head->backlight); - - /* get denormalized value */ - new_brightness = (value * max_brightness) / 255; - - backlight_set_brightness(head->backlight, new_brightness); - } -} - -static void -drm_output_init_backlight(struct drm_output *output) -{ - struct weston_head *base; - struct drm_head *head; - - output->base.set_backlight = NULL; - - wl_list_for_each(base, &output->base.head_list, output_link) { - head = to_drm_head(base); - - if (head->backlight) { - weston_log("Initialized backlight for head '%s', device %s\n", - head->base.name, head->backlight->path); - - if (!output->base.set_backlight) { - output->base.set_backlight = drm_set_backlight; - output->base.backlight_current = - drm_get_backlight(head); - } - } - } -} - -/** - * Power output on or off - * - * The DPMS/power level of an output is used to switch it on or off. This - * is DRM's hook for doing so, which can called either as part of repaint, - * or independently of the repaint loop. - * - * If we are called as part of repaint, we simply set the relevant bit in - * state and return. - * - * This function is never called on a virtual output. - */ -static void -drm_set_dpms(struct weston_output *output_base, enum dpms_enum level) -{ - struct drm_output *output = to_drm_output(output_base); - struct drm_backend *b = to_drm_backend(output_base->compositor); - struct drm_pending_state *pending_state = b->repaint_data; - struct drm_output_state *state; - int ret; - - assert(!output->virtual); - - if (output->state_cur->dpms == level) - return; - - /* If we're being called during the repaint loop, then this is - * simple: discard any previously-generated state, and create a new - * state where we disable everything. When we come to flush, this - * will be applied. - * - * However, we need to be careful: we can be called whilst another - * output is in its repaint cycle (pending_state exists), but our - * output still has an incomplete state application outstanding. - * In that case, we need to wait until that completes. */ - if (pending_state && !output->state_last) { - /* The repaint loop already sets DPMS on; we don't need to - * explicitly set it on here, as it will already happen - * whilst applying the repaint state. */ - if (level == WESTON_DPMS_ON) - return; - - state = drm_pending_state_get_output(pending_state, output); - if (state) - drm_output_state_free(state); - state = drm_output_get_disable_state(pending_state, output); - return; - } - - /* As we throw everything away when disabling, just send us back through - * a repaint cycle. */ - if (level == WESTON_DPMS_ON) { - if (output->dpms_off_pending) - output->dpms_off_pending = false; - weston_output_schedule_repaint(output_base); - return; - } - - /* If we've already got a request in the pipeline, then we need to - * park our DPMS request until that request has quiesced. */ - if (output->state_last) { - output->dpms_off_pending = true; - return; - } - - pending_state = drm_pending_state_alloc(b); - drm_output_get_disable_state(pending_state, output); - ret = drm_pending_state_apply_sync(pending_state); - if (ret != 0) - weston_log("drm_set_dpms: couldn't disable output?\n"); -} - -static const char * const connector_type_names[] = { - [DRM_MODE_CONNECTOR_Unknown] = "Unknown", - [DRM_MODE_CONNECTOR_VGA] = "VGA", - [DRM_MODE_CONNECTOR_DVII] = "DVI-I", - [DRM_MODE_CONNECTOR_DVID] = "DVI-D", - [DRM_MODE_CONNECTOR_DVIA] = "DVI-A", - [DRM_MODE_CONNECTOR_Composite] = "Composite", - [DRM_MODE_CONNECTOR_SVIDEO] = "SVIDEO", - [DRM_MODE_CONNECTOR_LVDS] = "LVDS", - [DRM_MODE_CONNECTOR_Component] = "Component", - [DRM_MODE_CONNECTOR_9PinDIN] = "DIN", - [DRM_MODE_CONNECTOR_DisplayPort] = "DP", - [DRM_MODE_CONNECTOR_HDMIA] = "HDMI-A", - [DRM_MODE_CONNECTOR_HDMIB] = "HDMI-B", - [DRM_MODE_CONNECTOR_TV] = "TV", - [DRM_MODE_CONNECTOR_eDP] = "eDP", - [DRM_MODE_CONNECTOR_VIRTUAL] = "Virtual", - [DRM_MODE_CONNECTOR_DSI] = "DSI", - [DRM_MODE_CONNECTOR_DPI] = "DPI", -}; - -/** Create a name given a DRM connector - * - * \param con The DRM connector whose type and id form the name. - * \return A newly allocate string, or NULL on error. Must be free()'d - * after use. - * - * The name does not identify the DRM display device. - */ -static char * -make_connector_name(const drmModeConnector *con) -{ - char *name; - const char *type_name = NULL; - int ret; - - if (con->connector_type < ARRAY_LENGTH(connector_type_names)) - type_name = connector_type_names[con->connector_type]; - - if (!type_name) - type_name = "UNNAMED"; - - ret = asprintf(&name, "%s-%d", type_name, con->connector_type_id); - if (ret < 0) - return NULL; - - return name; -} - -static int -drm_output_init_pixman(struct drm_output *output, struct drm_backend *b) -{ - int w = output->base.current_mode->width; - int h = output->base.current_mode->height; - uint32_t format = output->gbm_format; - uint32_t pixman_format; - unsigned int i; - uint32_t flags = 0; - - switch (format) { - case DRM_FORMAT_XRGB8888: - pixman_format = PIXMAN_x8r8g8b8; - break; - case DRM_FORMAT_RGB565: - pixman_format = PIXMAN_r5g6b5; - break; - default: - weston_log("Unsupported pixman format 0x%x\n", format); - return -1; - } - - /* FIXME error checking */ - for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) { - output->dumb[i] = drm_fb_create_dumb(b, w, h, format); - if (!output->dumb[i]) - goto err; - - output->image[i] = - pixman_image_create_bits(pixman_format, w, h, - output->dumb[i]->map, - output->dumb[i]->strides[0]); - if (!output->image[i]) - goto err; - } - - if (b->use_pixman_shadow) - flags |= PIXMAN_RENDERER_OUTPUT_USE_SHADOW; - - if (pixman_renderer_output_create(&output->base, flags) < 0) - goto err; - - weston_log("DRM: output %s %s shadow framebuffer.\n", output->base.name, - b->use_pixman_shadow ? "uses" : "does not use"); - - pixman_region32_init_rect(&output->previous_damage, - output->base.x, output->base.y, output->base.width, output->base.height); - - return 0; - -err: - for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) { - if (output->dumb[i]) - drm_fb_unref(output->dumb[i]); - if (output->image[i]) - pixman_image_unref(output->image[i]); - - output->dumb[i] = NULL; - output->image[i] = NULL; - } - - return -1; -} - -static void -drm_output_fini_pixman(struct drm_output *output) -{ - struct drm_backend *b = to_drm_backend(output->base.compositor); - unsigned int i; - - /* Destroying the Pixman surface will destroy all our buffers, - * regardless of refcount. Ensure we destroy them here. */ - if (!b->shutting_down && - output->scanout_plane->state_cur->fb && - output->scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB) { - drm_plane_state_free(output->scanout_plane->state_cur, true); - output->scanout_plane->state_cur = - drm_plane_state_alloc(NULL, output->scanout_plane); - output->scanout_plane->state_cur->complete = true; - } - - pixman_renderer_output_destroy(&output->base); - pixman_region32_fini(&output->previous_damage); - - for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) { - pixman_image_unref(output->image[i]); - drm_fb_unref(output->dumb[i]); - output->dumb[i] = NULL; - output->image[i] = NULL; - } -} - -static void -setup_output_seat_constraint(struct drm_backend *b, - struct weston_output *output, - const char *s) -{ - if (strcmp(s, "") != 0) { - struct weston_pointer *pointer; - struct udev_seat *seat; - - seat = udev_seat_get_named(&b->input, s); - if (!seat) - return; - - seat->base.output = output; - - pointer = weston_seat_get_pointer(&seat->base); - if (pointer) - weston_pointer_clamp(pointer, - &pointer->x, - &pointer->y); - } -} - -static int -drm_output_attach_head(struct weston_output *output_base, - struct weston_head *head_base) -{ - struct drm_backend *b = to_drm_backend(output_base->compositor); - - if (wl_list_length(&output_base->head_list) >= MAX_CLONED_CONNECTORS) - return -1; - - if (!output_base->enabled) - return 0; - - /* XXX: ensure the configuration will work. - * This is actually impossible without major infrastructure - * work. */ - - /* Need to go through modeset to add connectors. */ - /* XXX: Ideally we'd do this per-output, not globally. */ - /* XXX: Doing it globally, what guarantees another output's update - * will not clear the flag before this output is updated? - */ - b->state_invalid = true; - - weston_output_schedule_repaint(output_base); - - return 0; -} - -static void -drm_output_detach_head(struct weston_output *output_base, - struct weston_head *head_base) -{ - struct drm_backend *b = to_drm_backend(output_base->compositor); - - if (!output_base->enabled) - return; - - /* Need to go through modeset to drop connectors that should no longer - * be driven. */ - /* XXX: Ideally we'd do this per-output, not globally. */ - b->state_invalid = true; - - weston_output_schedule_repaint(output_base); -} - -int -parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format) -{ - const struct pixel_format_info *pinfo; - - if (s == NULL) { - *gbm_format = default_value; - - return 0; - } - - pinfo = pixel_format_get_info_by_drm_name(s); - if (!pinfo) { - weston_log("fatal: unrecognized pixel format: %s\n", s); - - return -1; - } - - /* GBM formats and DRM formats are identical. */ - *gbm_format = pinfo->format; - - return 0; -} - -static int -drm_head_read_current_setup(struct drm_head *head, struct drm_backend *backend) -{ - int drm_fd = backend->drm.fd; - drmModeEncoder *encoder; - drmModeCrtc *crtc; - - /* Get the current mode on the crtc that's currently driving - * this connector. */ - encoder = drmModeGetEncoder(drm_fd, head->connector->encoder_id); - if (encoder != NULL) { - head->inherited_crtc_id = encoder->crtc_id; - - crtc = drmModeGetCrtc(drm_fd, encoder->crtc_id); - drmModeFreeEncoder(encoder); - - if (crtc == NULL) - return -1; - if (crtc->mode_valid) - head->inherited_mode = crtc->mode; - drmModeFreeCrtc(crtc); - } - - return 0; -} - -static void -drm_output_set_gbm_format(struct weston_output *base, - const char *gbm_format) -{ - struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); - - if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1) - output->gbm_format = b->gbm_format; - - /* Without universal planes, we can't discover which formats are - * supported by the primary plane; we just hope that the GBM format - * works. */ - if (!b->universal_planes) - output->scanout_plane->formats[0].format = output->gbm_format; -} - -static void -drm_output_set_seat(struct weston_output *base, - const char *seat) -{ - struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); - - setup_output_seat_constraint(b, &output->base, - seat ? seat : ""); -} - -static int -drm_output_init_gamma_size(struct drm_output *output) -{ - struct drm_backend *backend = to_drm_backend(output->base.compositor); - drmModeCrtc *crtc; - - assert(output->base.compositor); - assert(output->crtc_id != 0); - crtc = drmModeGetCrtc(backend->drm.fd, output->crtc_id); - if (!crtc) - return -1; - - output->base.gamma_size = crtc->gamma_size; - - drmModeFreeCrtc(crtc); - - return 0; -} - -static uint32_t -drm_head_get_possible_crtcs_mask(struct drm_head *head) -{ - uint32_t possible_crtcs = 0; - drmModeEncoder *encoder; - int i; - - for (i = 0; i < head->connector->count_encoders; i++) { - encoder = drmModeGetEncoder(head->backend->drm.fd, - head->connector->encoders[i]); - if (!encoder) - continue; - - possible_crtcs |= encoder->possible_crtcs; - drmModeFreeEncoder(encoder); - } - - return possible_crtcs; -} - -static int -drm_crtc_get_index(drmModeRes *resources, uint32_t crtc_id) -{ - int i; - - for (i = 0; i < resources->count_crtcs; i++) { - if (resources->crtcs[i] == crtc_id) - return i; - } - - assert(0 && "unknown crtc id"); - return -1; -} - -/** Pick a CRTC that might be able to drive all attached connectors - * - * @param output The output whose attached heads to include. - * @param resources The DRM KMS resources. - * @return CRTC index, or -1 on failure or not found. - */ -static int -drm_output_pick_crtc(struct drm_output *output, drmModeRes *resources) -{ - struct drm_backend *backend; - struct weston_head *base; - struct drm_head *head; - uint32_t possible_crtcs = 0xffffffff; - int existing_crtc[32]; - unsigned j, n = 0; - uint32_t crtc_id; - int best_crtc_index = -1; - int fallback_crtc_index = -1; - int i; - bool match; - - backend = to_drm_backend(output->base.compositor); - - /* This algorithm ignores drmModeEncoder::possible_clones restriction, - * because it is more often set wrong than not in the kernel. */ - - /* Accumulate a mask of possible crtcs and find existing routings. */ - wl_list_for_each(base, &output->base.head_list, output_link) { - head = to_drm_head(base); - - possible_crtcs &= drm_head_get_possible_crtcs_mask(head); - - crtc_id = head->inherited_crtc_id; - if (crtc_id > 0 && n < ARRAY_LENGTH(existing_crtc)) - existing_crtc[n++] = drm_crtc_get_index(resources, - crtc_id); - } - - /* Find a crtc that could drive each connector individually at least, - * and prefer existing routings. */ - for (i = 0; i < resources->count_crtcs; i++) { - crtc_id = resources->crtcs[i]; - - /* Could the crtc not drive each connector? */ - if (!(possible_crtcs & (1 << i))) - continue; - - /* Is the crtc already in use? */ - if (drm_output_find_by_crtc(backend, crtc_id)) - continue; - - /* Try to preserve the existing CRTC -> connector routing; - * it makes initialisation faster, and also since we have a - * very dumb picking algorithm, may preserve a better - * choice. */ - for (j = 0; j < n; j++) { - if (existing_crtc[j] == i) - return i; - } - - /* Check if any other head had existing routing to this CRTC. - * If they did, this is not the best CRTC as it might be needed - * for another output we haven't enabled yet. */ - match = false; - wl_list_for_each(base, &backend->compositor->head_list, - compositor_link) { - head = to_drm_head(base); - - if (head->base.output == &output->base) - continue; - - if (weston_head_is_enabled(&head->base)) - continue; - - if (head->inherited_crtc_id == crtc_id) { - match = true; - break; - } - } - if (!match) - best_crtc_index = i; - - fallback_crtc_index = i; - } - - if (best_crtc_index != -1) - return best_crtc_index; - - if (fallback_crtc_index != -1) - return fallback_crtc_index; - - /* Likely possible_crtcs was empty due to asking for clones, - * but since the DRM documentation says the kernel lies, let's - * pick one crtc anyway. Trial and error is the only way to - * be sure if something doesn't work. */ - - /* First pick any existing assignment. */ - for (j = 0; j < n; j++) { - crtc_id = resources->crtcs[existing_crtc[j]]; - if (!drm_output_find_by_crtc(backend, crtc_id)) - return existing_crtc[j]; - } - - /* Otherwise pick any available crtc. */ - for (i = 0; i < resources->count_crtcs; i++) { - crtc_id = resources->crtcs[i]; - - if (!drm_output_find_by_crtc(backend, crtc_id)) - return i; - } - - return -1; -} - -/** Allocate a CRTC for the output - * - * @param output The output with no allocated CRTC. - * @param resources DRM KMS resources. - * @return 0 on success, -1 on failure. - * - * Finds a free CRTC that might drive the attached connectors, reserves the CRTC - * for the output, and loads the CRTC properties. - * - * Populates the cursor and scanout planes. - * - * On failure, the output remains without a CRTC. - */ -static int -drm_output_init_crtc(struct drm_output *output, drmModeRes *resources) -{ - struct drm_backend *b = to_drm_backend(output->base.compositor); - drmModeObjectPropertiesPtr props; - int i; - - assert(output->crtc_id == 0); - - i = drm_output_pick_crtc(output, resources); - if (i < 0) { - weston_log("Output '%s': No available CRTCs.\n", - output->base.name); - return -1; - } - - output->crtc_id = resources->crtcs[i]; - output->pipe = i; - - props = drmModeObjectGetProperties(b->drm.fd, output->crtc_id, - DRM_MODE_OBJECT_CRTC); - if (!props) { - weston_log("failed to get CRTC properties\n"); - goto err_crtc; - } - drm_property_info_populate(b, crtc_props, output->props_crtc, - WDRM_CRTC__COUNT, props); - drmModeFreeObjectProperties(props); - - output->scanout_plane = - drm_output_find_special_plane(b, output, - WDRM_PLANE_TYPE_PRIMARY); - if (!output->scanout_plane) { - weston_log("Failed to find primary plane for output %s\n", - output->base.name); - goto err_crtc; - } - - /* Failing to find a cursor plane is not fatal, as we'll fall back - * to software cursor. */ - output->cursor_plane = - drm_output_find_special_plane(b, output, - WDRM_PLANE_TYPE_CURSOR); - - wl_array_remove_uint32(&b->unused_crtcs, output->crtc_id); - - return 0; - -err_crtc: - output->crtc_id = 0; - output->pipe = 0; - - return -1; -} - -/** Free the CRTC from the output - * - * @param output The output whose CRTC to deallocate. - * - * The CRTC reserved for the given output becomes free to use again. - */ -static void -drm_output_fini_crtc(struct drm_output *output) -{ - struct drm_backend *b = to_drm_backend(output->base.compositor); - uint32_t *unused; - - if (!b->universal_planes && !b->shutting_down) { - /* With universal planes, the 'special' planes are allocated at - * startup, freed at shutdown, and live on the plane list in - * between. We want the planes to continue to exist and be freed - * up for other outputs. - * - * Without universal planes, our special planes are - * pseudo-planes allocated at output creation, freed at output - * destruction, and not usable by other outputs. - * - * On the other hand, if the compositor is already shutting down, - * the plane has already been destroyed. - */ - if (output->cursor_plane) - drm_plane_destroy(output->cursor_plane); - if (output->scanout_plane) - drm_plane_destroy(output->scanout_plane); - } - - drm_property_info_free(output->props_crtc, WDRM_CRTC__COUNT); - - assert(output->crtc_id != 0); - - unused = wl_array_add(&b->unused_crtcs, sizeof(*unused)); - *unused = output->crtc_id; - - /* Force resetting unused CRTCs */ - b->state_invalid = true; - - output->crtc_id = 0; - output->cursor_plane = NULL; - output->scanout_plane = NULL; -} - -static int -drm_output_enable(struct weston_output *base) -{ - struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); - drmModeRes *resources; - int ret; - - assert(!output->virtual); - - resources = drmModeGetResources(b->drm.fd); - if (!resources) { - weston_log("drmModeGetResources failed\n"); - return -1; - } - ret = drm_output_init_crtc(output, resources); - drmModeFreeResources(resources); - if (ret < 0) - return -1; - - if (drm_output_init_gamma_size(output) < 0) - goto err; - - if (b->pageflip_timeout) - drm_output_pageflip_timer_create(output); - - if (b->use_pixman) { - if (drm_output_init_pixman(output, b) < 0) { - weston_log("Failed to init output pixman state\n"); - goto err; - } - } else if (drm_output_init_egl(output, b) < 0) { - weston_log("Failed to init output gl state\n"); - goto err; - } - - drm_output_init_backlight(output); - - output->base.start_repaint_loop = drm_output_start_repaint_loop; - output->base.repaint = drm_output_repaint; - output->base.assign_planes = drm_assign_planes; - output->base.set_dpms = drm_set_dpms; - output->base.switch_mode = drm_output_switch_mode; - output->base.set_gamma = drm_output_set_gamma; - - if (output->cursor_plane) - weston_compositor_stack_plane(b->compositor, - &output->cursor_plane->base, - NULL); - else - b->cursors_are_broken = true; - - weston_compositor_stack_plane(b->compositor, - &output->scanout_plane->base, - &b->compositor->primary_plane); - - weston_log("Output %s (crtc %d) video modes:\n", - output->base.name, output->crtc_id); - drm_output_print_modes(output); - - return 0; - -err: - drm_output_fini_crtc(output); - - return -1; -} - -static void -drm_output_deinit(struct weston_output *base) -{ - struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); - - if (b->use_pixman) - drm_output_fini_pixman(output); - else - drm_output_fini_egl(output); - - /* Since our planes are no longer in use anywhere, remove their base - * weston_plane's link from the plane stacking list, unless we're - * shutting down, in which case the plane has already been - * destroyed. */ - if (!b->shutting_down) { - wl_list_remove(&output->scanout_plane->base.link); - wl_list_init(&output->scanout_plane->base.link); - - if (output->cursor_plane) { - wl_list_remove(&output->cursor_plane->base.link); - wl_list_init(&output->cursor_plane->base.link); - /* Turn off hardware cursor */ - drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); - } - } - - drm_output_fini_crtc(output); -} - -static void -drm_head_destroy(struct drm_head *head); - -static void -drm_output_destroy(struct weston_output *base) -{ - struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); - - assert(!output->virtual); - - if (output->page_flip_pending || output->atomic_complete_pending) { - output->destroy_pending = true; - weston_log("destroy output while page flip pending\n"); - return; - } - - if (output->base.enabled) - drm_output_deinit(&output->base); - - drm_mode_list_destroy(b, &output->base.mode_list); - - if (output->pageflip_timer) - wl_event_source_remove(output->pageflip_timer); - - weston_output_release(&output->base); - - assert(!output->state_last); - drm_output_state_free(output->state_cur); - - free(output); -} - -static int -drm_output_disable(struct weston_output *base) -{ - struct drm_output *output = to_drm_output(base); - - assert(!output->virtual); - - if (output->page_flip_pending || output->atomic_complete_pending) { - output->disable_pending = true; - return -1; - } - - weston_log("Disabling output %s\n", output->base.name); - - if (output->base.enabled) - drm_output_deinit(&output->base); - - output->disable_pending = false; - - return 0; -} - -/** - * Update the list of unused connectors and CRTCs - * - * This keeps the unused_crtc arrays up to date. - * - * @param b Weston backend structure - * @param resources DRM resources for this device - */ -static void -drm_backend_update_unused_outputs(struct drm_backend *b, drmModeRes *resources) -{ - int i; - - wl_array_release(&b->unused_crtcs); - wl_array_init(&b->unused_crtcs); - - for (i = 0; i < resources->count_crtcs; i++) { - struct drm_output *output; - uint32_t *crtc_id; - - output = drm_output_find_by_crtc(b, resources->crtcs[i]); - if (output && output->base.enabled) - continue; - - crtc_id = wl_array_add(&b->unused_crtcs, sizeof(*crtc_id)); - *crtc_id = resources->crtcs[i]; - } -} - -/* - * This function converts the protection status from drm values to - * weston_hdcp_protection status. The drm values as read from the connector - * properties "Content Protection" and "HDCP Content Type" need to be converted - * to appropriate weston values, that can be sent to a client application. - */ -static int -get_weston_protection_from_drm(enum wdrm_content_protection_state protection, - enum wdrm_hdcp_content_type type, - enum weston_hdcp_protection *weston_protection) - -{ - if (protection >= WDRM_CONTENT_PROTECTION__COUNT) - return -1; - if (protection == WDRM_CONTENT_PROTECTION_DESIRED || - protection == WDRM_CONTENT_PROTECTION_UNDESIRED) { - *weston_protection = WESTON_HDCP_DISABLE; - return 0; - } - if (type >= WDRM_HDCP_CONTENT_TYPE__COUNT) - return -1; - if (type == WDRM_HDCP_CONTENT_TYPE0) { - *weston_protection = WESTON_HDCP_ENABLE_TYPE_0; - return 0; - } - if (type == WDRM_HDCP_CONTENT_TYPE1) { - *weston_protection = WESTON_HDCP_ENABLE_TYPE_1; - return 0; - } - return -1; -} - -/** - * Get current content-protection status for a given head. - * - * @param head drm_head, whose protection is to be retrieved - * @param props drm property object of the connector, related to the head - * @return protection status in case of success, -1 otherwise - */ -static enum weston_hdcp_protection -drm_head_get_current_protection(struct drm_head *head, - drmModeObjectProperties *props) -{ - struct drm_property_info *info; - enum wdrm_content_protection_state protection; - enum wdrm_hdcp_content_type type; - enum weston_hdcp_protection weston_hdcp = WESTON_HDCP_DISABLE; - - info = &head->props_conn[WDRM_CONNECTOR_CONTENT_PROTECTION]; - protection = drm_property_get_value(info, props, - WDRM_CONTENT_PROTECTION__COUNT); - - if (protection == WDRM_CONTENT_PROTECTION__COUNT) - return WESTON_HDCP_DISABLE; - - info = &head->props_conn[WDRM_CONNECTOR_HDCP_CONTENT_TYPE]; - type = drm_property_get_value(info, props, - WDRM_HDCP_CONTENT_TYPE__COUNT); - - /* - * In case of platforms supporting HDCP1.4, only property - * 'Content Protection' is exposed and not the 'HDCP Content Type' - * for such cases HDCP Type 0 should be considered as the content-type. - */ - - if (type == WDRM_HDCP_CONTENT_TYPE__COUNT) - type = WDRM_HDCP_CONTENT_TYPE0; - - if (get_weston_protection_from_drm(protection, type, - &weston_hdcp) == -1) { - weston_log("Invalid drm protection:%d type:%d, for head:%s connector-id:%d\n", - protection, type, head->base.name, - head->connector_id); - return WESTON_HDCP_DISABLE; - } - - return weston_hdcp; -} - -/** Replace connector data and monitor information - * - * @param head The head to update. - * @param connector The connector data to be owned by the head, must match - * the head's connector ID. - * @return 0 on success, -1 on failure. - * - * Takes ownership of @c connector on success, not on failure. - * - * May schedule a heads changed call. - */ -static int -drm_head_assign_connector_info(struct drm_head *head, - drmModeConnector *connector) -{ - drmModeObjectProperties *props; - - assert(connector); - assert(head->connector_id == connector->connector_id); - - props = drmModeObjectGetProperties(head->backend->drm.fd, - head->connector_id, - DRM_MODE_OBJECT_CONNECTOR); - if (!props) { - weston_log("Error: failed to get connector '%s' properties\n", - head->base.name); - return -1; - } - - if (head->connector) - drmModeFreeConnector(head->connector); - head->connector = connector; - - drm_property_info_populate(head->backend, connector_props, - head->props_conn, - WDRM_CONNECTOR__COUNT, props); - update_head_from_connector(head, props); - - weston_head_set_content_protection_status(&head->base, - drm_head_get_current_protection(head, props)); - drmModeFreeObjectProperties(props); - - return 0; -} - -static void -drm_head_log_info(struct drm_head *head, const char *msg) -{ - if (head->base.connected) { - weston_log("DRM: head '%s' %s, connector %d is connected, " - "EDID make '%s', model '%s', serial '%s'\n", - head->base.name, msg, head->connector_id, - head->base.make, head->base.model, - head->base.serial_number ?: ""); - } else { - weston_log("DRM: head '%s' %s, connector %d is disconnected.\n", - head->base.name, msg, head->connector_id); - } -} - -/** Update connector and monitor information - * - * @param head The head to update. - * - * Re-reads the DRM property lists for the connector and updates monitor - * information and connection status. This may schedule a heads changed call - * to the user. - */ -static void -drm_head_update_info(struct drm_head *head) -{ - drmModeConnector *connector; - - connector = drmModeGetConnector(head->backend->drm.fd, - head->connector_id); - if (!connector) { - weston_log("DRM: getting connector info for '%s' failed.\n", - head->base.name); - return; - } - - if (drm_head_assign_connector_info(head, connector) < 0) - drmModeFreeConnector(connector); - - if (head->base.device_changed) - drm_head_log_info(head, "updated"); -} - -/** - * Create a Weston head for a connector - * - * Given a DRM connector, create a matching drm_head structure and add it - * to Weston's head list. - * - * @param backend Weston backend structure - * @param connector_id DRM connector ID for the head - * @param drm_device udev device pointer - * @returns The new head, or NULL on failure. - */ -static struct drm_head * -drm_head_create(struct drm_backend *backend, uint32_t connector_id, - struct udev_device *drm_device) -{ - struct drm_head *head; - drmModeConnector *connector; - char *name; - - head = zalloc(sizeof *head); - if (!head) - return NULL; - - connector = drmModeGetConnector(backend->drm.fd, connector_id); - if (!connector) - goto err_alloc; - - name = make_connector_name(connector); - if (!name) - goto err_alloc; - - weston_head_init(&head->base, name); - free(name); - - head->connector_id = connector_id; - head->backend = backend; - - head->backlight = backlight_init(drm_device, connector->connector_type); - - if (drm_head_assign_connector_info(head, connector) < 0) - goto err_init; - - if (head->connector->connector_type == DRM_MODE_CONNECTOR_LVDS || - head->connector->connector_type == DRM_MODE_CONNECTOR_eDP) - weston_head_set_internal(&head->base); - - if (drm_head_read_current_setup(head, backend) < 0) { - weston_log("Failed to retrieve current mode from connector %d.\n", - head->connector_id); - /* Not fatal. */ - } - - weston_compositor_add_head(backend->compositor, &head->base); - drm_head_log_info(head, "found"); - - return head; - -err_init: - weston_head_release(&head->base); - -err_alloc: - if (connector) - drmModeFreeConnector(connector); - - free(head); - - return NULL; -} - -static void -drm_head_destroy(struct drm_head *head) -{ - weston_head_release(&head->base); - - drm_property_info_free(head->props_conn, WDRM_CONNECTOR__COUNT); - drmModeFreeConnector(head->connector); - - if (head->backlight) - backlight_destroy(head->backlight); - - free(head); -} - -/** - * Create a Weston output structure - * - * Create an "empty" drm_output. This is the implementation of - * weston_backend::create_output. - * - * Creating an output is usually followed by drm_output_attach_head() - * and drm_output_enable() to make use of it. - * - * @param compositor The compositor instance. - * @param name Name for the new output. - * @returns The output, or NULL on failure. - */ -static struct weston_output * -drm_output_create(struct weston_compositor *compositor, const char *name) -{ - struct drm_backend *b = to_drm_backend(compositor); - struct drm_output *output; - - output = zalloc(sizeof *output); - if (output == NULL) - return NULL; - - output->backend = b; -#ifdef BUILD_DRM_GBM - output->gbm_bo_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; -#endif - - weston_output_init(&output->base, compositor, name); - - output->base.enable = drm_output_enable; - output->base.destroy = drm_output_destroy; - output->base.disable = drm_output_disable; - output->base.attach_head = drm_output_attach_head; - output->base.detach_head = drm_output_detach_head; - - output->destroy_pending = false; - output->disable_pending = false; - - output->state_cur = drm_output_state_alloc(output, NULL); - - weston_compositor_add_pending_output(&output->base, b->compositor); - - return &output->base; -} - -static int -drm_backend_create_heads(struct drm_backend *b, struct udev_device *drm_device) -{ - struct drm_head *head; - drmModeRes *resources; - int i; - - resources = drmModeGetResources(b->drm.fd); - if (!resources) { - weston_log("drmModeGetResources failed\n"); - return -1; - } - - b->min_width = resources->min_width; - b->max_width = resources->max_width; - b->min_height = resources->min_height; - b->max_height = resources->max_height; - - for (i = 0; i < resources->count_connectors; i++) { - uint32_t connector_id = resources->connectors[i]; - - head = drm_head_create(b, connector_id, drm_device); - if (!head) { - weston_log("DRM: failed to create head for connector %d.\n", - connector_id); - } - } - - drm_backend_update_unused_outputs(b, resources); - - drmModeFreeResources(resources); - - return 0; -} - -static void -drm_backend_update_heads(struct drm_backend *b, struct udev_device *drm_device) -{ - drmModeRes *resources; - struct weston_head *base, *next; - struct drm_head *head; - int i; - - resources = drmModeGetResources(b->drm.fd); - if (!resources) { - weston_log("drmModeGetResources failed\n"); - return; - } - - /* collect new connectors that have appeared, e.g. MST */ - for (i = 0; i < resources->count_connectors; i++) { - uint32_t connector_id = resources->connectors[i]; - - head = drm_head_find_by_connector(b, connector_id); - if (head) { - drm_head_update_info(head); - } else { - head = drm_head_create(b, connector_id, drm_device); - if (!head) - weston_log("DRM: failed to create head for hot-added connector %d.\n", - connector_id); - } - } - - /* Remove connectors that have disappeared. */ - wl_list_for_each_safe(base, next, - &b->compositor->head_list, compositor_link) { - bool removed = true; - - head = to_drm_head(base); - - for (i = 0; i < resources->count_connectors; i++) { - if (resources->connectors[i] == head->connector_id) { - removed = false; - break; - } - } - - if (!removed) - continue; - - weston_log("DRM: head '%s' (connector %d) disappeared.\n", - head->base.name, head->connector_id); - drm_head_destroy(head); - } - - drm_backend_update_unused_outputs(b, resources); - - drmModeFreeResources(resources); -} - -static enum wdrm_connector_property -drm_head_find_property_by_id(struct drm_head *head, uint32_t property_id) -{ - int i; - enum wdrm_connector_property prop = WDRM_CONNECTOR__COUNT; - - if (!head || !property_id) - return WDRM_CONNECTOR__COUNT; - - for (i = 0; i < WDRM_CONNECTOR__COUNT; i++) - if (head->props_conn[i].prop_id == property_id) { - prop = (enum wdrm_connector_property) i; - break; - } - return prop; -} - -static void -drm_backend_update_conn_props(struct drm_backend *b, - uint32_t connector_id, - uint32_t property_id) -{ - struct drm_head *head; - enum wdrm_connector_property conn_prop; - drmModeObjectProperties *props; - - head = drm_head_find_by_connector(b, connector_id); - if (!head) { - weston_log("DRM: failed to find head for connector id: %d.\n", - connector_id); - return; - } - - conn_prop = drm_head_find_property_by_id(head, property_id); - if (conn_prop >= WDRM_CONNECTOR__COUNT) - return; - - props = drmModeObjectGetProperties(b->drm.fd, - connector_id, - DRM_MODE_OBJECT_CONNECTOR); - if (!props) { - weston_log("Error: failed to get connector '%s' properties\n", - head->base.name); - return; - } - if (conn_prop == WDRM_CONNECTOR_CONTENT_PROTECTION) { - weston_head_set_content_protection_status(&head->base, - drm_head_get_current_protection(head, props)); - } - drmModeFreeObjectProperties(props); -} - -static int -udev_event_is_hotplug(struct drm_backend *b, struct udev_device *device) -{ - const char *sysnum; - const char *val; - - sysnum = udev_device_get_sysnum(device); - if (!sysnum || atoi(sysnum) != b->drm.id) - return 0; - - val = udev_device_get_property_value(device, "HOTPLUG"); - if (!val) - return 0; - - return strcmp(val, "1") == 0; -} - -static int -udev_event_is_conn_prop_change(struct drm_backend *b, - struct udev_device *device, - uint32_t *connector_id, - uint32_t *property_id) - -{ - const char *val; - int id; - - val = udev_device_get_property_value(device, "CONNECTOR"); - if (!val || !safe_strtoint(val, &id)) - return 0; - else - *connector_id = id; - - val = udev_device_get_property_value(device, "PROPERTY"); - if (!val || !safe_strtoint(val, &id)) - return 0; - else - *property_id = id; - - return 1; -} - -static int -udev_drm_event(int fd, uint32_t mask, void *data) -{ - struct drm_backend *b = data; - struct udev_device *event; - uint32_t conn_id, prop_id; - - event = udev_monitor_receive_device(b->udev_monitor); - - if (udev_event_is_hotplug(b, event)) { - if (udev_event_is_conn_prop_change(b, event, &conn_id, &prop_id)) - drm_backend_update_conn_props(b, conn_id, prop_id); - else - drm_backend_update_heads(b, event); - } - - udev_device_unref(event); - - return 1; -} - -static void -drm_destroy(struct weston_compositor *ec) -{ - struct drm_backend *b = to_drm_backend(ec); - struct weston_head *base, *next; - - udev_input_destroy(&b->input); - - wl_event_source_remove(b->udev_drm_source); - wl_event_source_remove(b->drm_source); - - b->shutting_down = true; - - destroy_sprites(b); - - weston_compositor_log_scope_destroy(b->debug); - b->debug = NULL; - weston_compositor_shutdown(ec); - - wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) - drm_head_destroy(to_drm_head(base)); - -#ifdef BUILD_DRM_GBM - if (b->gbm) - gbm_device_destroy(b->gbm); -#endif - - udev_monitor_unref(b->udev_monitor); - udev_unref(b->udev); - - weston_launcher_destroy(ec->launcher); - - wl_array_release(&b->unused_crtcs); - - close(b->drm.fd); - free(b->drm.filename); - free(b); -} - -static void -session_notify(struct wl_listener *listener, void *data) -{ - struct weston_compositor *compositor = data; - struct drm_backend *b = to_drm_backend(compositor); - struct drm_plane *plane; - struct drm_output *output; - - if (compositor->session_active) { - weston_log("activating session\n"); - weston_compositor_wake(compositor); - weston_compositor_damage_all(compositor); - b->state_invalid = true; - udev_input_enable(&b->input); - } else { - weston_log("deactivating session\n"); - udev_input_disable(&b->input); - - weston_compositor_offscreen(compositor); - - /* If we have a repaint scheduled (either from a - * pending pageflip or the idle handler), make sure we - * cancel that so we don't try to pageflip when we're - * vt switched away. The OFFSCREEN state will prevent - * further attempts at repainting. When we switch - * back, we schedule a repaint, which will process - * pending frame callbacks. */ - - wl_list_for_each(output, &compositor->output_list, base.link) { - output->base.repaint_needed = false; - if (output->cursor_plane) - drmModeSetCursor(b->drm.fd, output->crtc_id, - 0, 0, 0); - } - - output = container_of(compositor->output_list.next, - struct drm_output, base.link); - - wl_list_for_each(plane, &b->plane_list, link) { - if (plane->type != WDRM_PLANE_TYPE_OVERLAY) - continue; - - drmModeSetPlane(b->drm.fd, - plane->plane_id, - output->crtc_id, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0); - } - } -} - - -/** - * Handle KMS GPU being added/removed - * - * If the device being added/removed is the KMS device, we activate/deactivate - * the compositor session. - * - * @param compositor The compositor instance. - * @param device The device being added/removed. - * @param added Whether the device is being added (or removed) - */ -static void -drm_device_changed(struct weston_compositor *compositor, - dev_t device, bool added) -{ - struct drm_backend *b = to_drm_backend(compositor); - - if (b->drm.fd < 0 || b->drm.devnum != device || - compositor->session_active == added) - return; - - compositor->session_active = added; - wl_signal_emit(&compositor->session_signal, compositor); -} - -/** - * Determines whether or not a device is capable of modesetting. If successful, - * sets b->drm.fd and b->drm.filename to the opened device. - */ -static bool -drm_device_is_kms(struct drm_backend *b, struct udev_device *device) -{ - const char *filename = udev_device_get_devnode(device); - const char *sysnum = udev_device_get_sysnum(device); - dev_t devnum = udev_device_get_devnum(device); - drmModeRes *res; - int id = -1, fd; - - if (!filename) - return false; - - fd = weston_launcher_open(b->compositor->launcher, filename, O_RDWR); - if (fd < 0) - return false; - - res = drmModeGetResources(fd); - if (!res) - goto out_fd; - - if (res->count_crtcs <= 0 || res->count_connectors <= 0 || - res->count_encoders <= 0) - goto out_res; - - if (sysnum) - id = atoi(sysnum); - if (!sysnum || id < 0) { - weston_log("couldn't get sysnum for device %s\n", filename); - goto out_res; - } - - /* We can be called successfully on multiple devices; if we have, - * clean up old entries. */ - if (b->drm.fd >= 0) - weston_launcher_close(b->compositor->launcher, b->drm.fd); - free(b->drm.filename); - - b->drm.fd = fd; - b->drm.id = id; - b->drm.filename = strdup(filename); - b->drm.devnum = devnum; - - drmModeFreeResources(res); - - return true; - -out_res: - drmModeFreeResources(res); -out_fd: - weston_launcher_close(b->compositor->launcher, fd); - return false; -} - -/* - * Find primary GPU - * Some systems may have multiple DRM devices attached to a single seat. This - * function loops over all devices and tries to find a PCI device with the - * boot_vga sysfs attribute set to 1. - * If no such device is found, the first DRM device reported by udev is used. - * Devices are also vetted to make sure they are are capable of modesetting, - * rather than pure render nodes (GPU with no display), or pure - * memory-allocation devices (VGEM). - */ -static struct udev_device* -find_primary_gpu(struct drm_backend *b, const char *seat) -{ - struct udev_enumerate *e; - struct udev_list_entry *entry; - const char *path, *device_seat, *id; - struct udev_device *device, *drm_device, *pci; - - e = udev_enumerate_new(b->udev); - udev_enumerate_add_match_subsystem(e, "drm"); - udev_enumerate_add_match_sysname(e, "card[0-9]*"); - - udev_enumerate_scan_devices(e); - drm_device = NULL; - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { - bool is_boot_vga = false; - - path = udev_list_entry_get_name(entry); - device = udev_device_new_from_syspath(b->udev, path); - if (!device) - continue; - device_seat = udev_device_get_property_value(device, "ID_SEAT"); - if (!device_seat) - device_seat = default_seat; - if (strcmp(device_seat, seat)) { - udev_device_unref(device); - continue; - } - - pci = udev_device_get_parent_with_subsystem_devtype(device, - "pci", NULL); - if (pci) { - id = udev_device_get_sysattr_value(pci, "boot_vga"); - if (id && !strcmp(id, "1")) - is_boot_vga = true; - } - - /* If we already have a modesetting-capable device, and this - * device isn't our boot-VGA device, we aren't going to use - * it. */ - if (!is_boot_vga && drm_device) { - udev_device_unref(device); - continue; - } - - /* Make sure this device is actually capable of modesetting; - * if this call succeeds, b->drm.{fd,filename} will be set, - * and any old values freed. */ - if (!drm_device_is_kms(b, device)) { - udev_device_unref(device); - continue; - } - - /* There can only be one boot_vga device, and we try to use it - * at all costs. */ - if (is_boot_vga) { - if (drm_device) - udev_device_unref(drm_device); - drm_device = device; - break; - } - - /* Per the (!is_boot_vga && drm_device) test above, we only - * trump existing saved devices with boot-VGA devices, so if - * we end up here, this must be the first device we've seen. */ - assert(!drm_device); - drm_device = device; - } - - /* If we're returning a device to use, we must have an open FD for - * it. */ - assert(!!drm_device == (b->drm.fd >= 0)); - - udev_enumerate_unref(e); - return drm_device; -} - -static struct udev_device * -open_specific_drm_device(struct drm_backend *b, const char *name) -{ - struct udev_device *device; - - device = udev_device_new_from_subsystem_sysname(b->udev, "drm", name); - if (!device) { - weston_log("ERROR: could not open DRM device '%s'\n", name); - return NULL; - } - - if (!drm_device_is_kms(b, device)) { - udev_device_unref(device); - weston_log("ERROR: DRM device '%s' is not a KMS device.\n", name); - return NULL; - } - - /* If we're returning a device to use, we must have an open FD for - * it. */ - assert(b->drm.fd >= 0); - - return device; -} - -static void -planes_binding(struct weston_keyboard *keyboard, const struct timespec *time, - uint32_t key, void *data) -{ - struct drm_backend *b = data; - - switch (key) { - case KEY_C: - b->cursors_are_broken ^= true; - break; - case KEY_V: - /* We don't support overlay-plane usage with legacy KMS. */ - if (b->atomic_modeset) - b->sprites_are_broken ^= true; - break; - default: - break; - } -} - -#ifdef BUILD_VAAPI_RECORDER -static void -recorder_destroy(struct drm_output *output) -{ - vaapi_recorder_destroy(output->recorder); - output->recorder = NULL; - - weston_output_disable_planes_decr(&output->base); - - wl_list_remove(&output->recorder_frame_listener.link); - weston_log("[libva recorder] done\n"); -} - -static void -recorder_frame_notify(struct wl_listener *listener, void *data) -{ - struct drm_output *output; - struct drm_backend *b; - int fd, ret; - - output = container_of(listener, struct drm_output, - recorder_frame_listener); - b = to_drm_backend(output->base.compositor); - - if (!output->recorder) - return; - - ret = drmPrimeHandleToFD(b->drm.fd, - output->scanout_plane->state_cur->fb->handles[0], - DRM_CLOEXEC, &fd); - if (ret) { - weston_log("[libva recorder] " - "failed to create prime fd for front buffer\n"); - return; - } - - ret = vaapi_recorder_frame(output->recorder, fd, - output->scanout_plane->state_cur->fb->strides[0]); - if (ret < 0) { - weston_log("[libva recorder] aborted: %s\n", strerror(errno)); - recorder_destroy(output); - } -} - -static void * -create_recorder(struct drm_backend *b, int width, int height, - const char *filename) -{ - int fd; - drm_magic_t magic; - - fd = open(b->drm.filename, O_RDWR | O_CLOEXEC); - if (fd < 0) - return NULL; - - drmGetMagic(fd, &magic); - drmAuthMagic(b->drm.fd, magic); - - return vaapi_recorder_create(fd, width, height, filename); -} - -static void -recorder_binding(struct weston_keyboard *keyboard, const struct timespec *time, - uint32_t key, void *data) -{ - struct drm_backend *b = data; - struct drm_output *output; - int width, height; - - output = container_of(b->compositor->output_list.next, - struct drm_output, base.link); - - if (!output->recorder) { - if (output->gbm_format != DRM_FORMAT_XRGB8888) { - weston_log("failed to start vaapi recorder: " - "output format not supported\n"); - return; - } - - width = output->base.current_mode->width; - height = output->base.current_mode->height; - - output->recorder = - create_recorder(b, width, height, "capture.h264"); - if (!output->recorder) { - weston_log("failed to create vaapi recorder\n"); - return; - } - - weston_output_disable_planes_incr(&output->base); - - output->recorder_frame_listener.notify = recorder_frame_notify; - wl_signal_add(&output->base.frame_signal, - &output->recorder_frame_listener); - - weston_output_schedule_repaint(&output->base); - - weston_log("[libva recorder] initialized\n"); - } else { - recorder_destroy(output); - } -} -#else -static void -recorder_binding(struct weston_keyboard *keyboard, const struct timespec *time, - uint32_t key, void *data) -{ - weston_log("Compiled without libva support\n"); -} -#endif - - -static const struct weston_drm_output_api api = { - drm_output_set_mode, - drm_output_set_gbm_format, - drm_output_set_seat, -}; - -static struct drm_backend * -drm_backend_create(struct weston_compositor *compositor, - struct weston_drm_backend_config *config) -{ - struct drm_backend *b; - struct udev_device *drm_device; - struct wl_event_loop *loop; - const char *seat_id = default_seat; - const char *session_seat; - int ret; - - session_seat = getenv("XDG_SEAT"); - if (session_seat) - seat_id = session_seat; - - if (config->seat_id) - seat_id = config->seat_id; - - weston_log("initializing drm backend\n"); - - b = zalloc(sizeof *b); - if (b == NULL) - return NULL; - - b->state_invalid = true; - b->drm.fd = -1; - wl_array_init(&b->unused_crtcs); - - b->compositor = compositor; - b->use_pixman = config->use_pixman; - b->pageflip_timeout = config->pageflip_timeout; - b->use_pixman_shadow = config->use_pixman_shadow; - - b->debug = weston_compositor_add_log_scope(compositor->weston_log_ctx, - "drm-backend", - "Debug messages from DRM/KMS backend\n", - NULL, NULL, NULL); - - compositor->backend = &b->base; - - if (parse_gbm_format(config->gbm_format, DRM_FORMAT_XRGB8888, &b->gbm_format) < 0) - goto err_compositor; - - /* Check if we run drm-backend using weston-launch */ - compositor->launcher = weston_launcher_connect(compositor, config->tty, - seat_id, true); - if (compositor->launcher == NULL) { - weston_log("fatal: drm backend should be run using " - "weston-launch binary, or your system should " - "provide the logind D-Bus API.\n"); - goto err_compositor; - } - - b->udev = udev_new(); - if (b->udev == NULL) { - weston_log("failed to initialize udev context\n"); - goto err_launcher; - } - - b->session_listener.notify = session_notify; - wl_signal_add(&compositor->session_signal, &b->session_listener); - - if (config->specific_device) - drm_device = open_specific_drm_device(b, config->specific_device); - else - drm_device = find_primary_gpu(b, seat_id); - if (drm_device == NULL) { - weston_log("no drm device found\n"); - goto err_udev; - } - - if (init_kms_caps(b) < 0) { - weston_log("failed to initialize kms\n"); - goto err_udev_dev; - } - - if (b->use_pixman) { - if (init_pixman(b) < 0) { - weston_log("failed to initialize pixman renderer\n"); - goto err_udev_dev; - } - } else { - if (init_egl(b) < 0) { - weston_log("failed to initialize egl\n"); - goto err_udev_dev; - } - } - - b->base.destroy = drm_destroy; - b->base.repaint_begin = drm_repaint_begin; - b->base.repaint_flush = drm_repaint_flush; - b->base.repaint_cancel = drm_repaint_cancel; - b->base.create_output = drm_output_create; - b->base.device_changed = drm_device_changed; - b->base.can_scanout_dmabuf = drm_can_scanout_dmabuf; - - weston_setup_vt_switch_bindings(compositor); - - wl_list_init(&b->plane_list); - create_sprites(b); - - if (udev_input_init(&b->input, - compositor, b->udev, seat_id, - config->configure_device) < 0) { - weston_log("failed to create input devices\n"); - goto err_sprite; - } - - if (drm_backend_create_heads(b, drm_device) < 0) { - weston_log("Failed to create heads for %s\n", b->drm.filename); - goto err_udev_input; - } - - /* 'compute' faked zpos values in case HW doesn't expose any */ - drm_backend_create_faked_zpos(b); - - /* A this point we have some idea of whether or not we have a working - * cursor plane. */ - if (!b->cursors_are_broken) - compositor->capabilities |= WESTON_CAP_CURSOR_PLANE; - - loop = wl_display_get_event_loop(compositor->wl_display); - b->drm_source = - wl_event_loop_add_fd(loop, b->drm.fd, - WL_EVENT_READABLE, on_drm_input, b); - - b->udev_monitor = udev_monitor_new_from_netlink(b->udev, "udev"); - if (b->udev_monitor == NULL) { - weston_log("failed to initialize udev monitor\n"); - goto err_drm_source; - } - udev_monitor_filter_add_match_subsystem_devtype(b->udev_monitor, - "drm", NULL); - b->udev_drm_source = - wl_event_loop_add_fd(loop, - udev_monitor_get_fd(b->udev_monitor), - WL_EVENT_READABLE, udev_drm_event, b); - - if (udev_monitor_enable_receiving(b->udev_monitor) < 0) { - weston_log("failed to enable udev-monitor receiving\n"); - goto err_udev_monitor; - } - - udev_device_unref(drm_device); - - weston_compositor_add_debug_binding(compositor, KEY_O, - planes_binding, b); - weston_compositor_add_debug_binding(compositor, KEY_C, - planes_binding, b); - weston_compositor_add_debug_binding(compositor, KEY_V, - planes_binding, b); - weston_compositor_add_debug_binding(compositor, KEY_Q, - recorder_binding, b); - weston_compositor_add_debug_binding(compositor, KEY_W, - renderer_switch_binding, b); - - if (compositor->renderer->import_dmabuf) { - if (linux_dmabuf_setup(compositor) < 0) - weston_log("Error: initializing dmabuf " - "support failed.\n"); - if (weston_direct_display_setup(compositor) < 0) - weston_log("Error: initializing direct-display " - "support failed.\n"); - } - - if (compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC) { - if (linux_explicit_synchronization_setup(compositor) < 0) - weston_log("Error: initializing explicit " - " synchronization support failed.\n"); - } - - if (b->atomic_modeset) - if (weston_compositor_enable_content_protection(compositor) < 0) - weston_log("Error: initializing content-protection " - "support failed.\n"); - - ret = weston_plugin_api_register(compositor, WESTON_DRM_OUTPUT_API_NAME, - &api, sizeof(api)); - - if (ret < 0) { - weston_log("Failed to register output API.\n"); - goto err_udev_monitor; - } - - ret = drm_backend_init_virtual_output_api(compositor); - if (ret < 0) { - weston_log("Failed to register virtual output API.\n"); - goto err_udev_monitor; - } - - return b; - -err_udev_monitor: - wl_event_source_remove(b->udev_drm_source); - udev_monitor_unref(b->udev_monitor); -err_drm_source: - wl_event_source_remove(b->drm_source); -err_udev_input: - udev_input_destroy(&b->input); -err_sprite: -#ifdef BUILD_DRM_GBM - if (b->gbm) - gbm_device_destroy(b->gbm); -#endif - destroy_sprites(b); -err_udev_dev: - udev_device_unref(drm_device); -err_launcher: - weston_launcher_destroy(compositor->launcher); -err_udev: - udev_unref(b->udev); -err_compositor: - weston_compositor_shutdown(compositor); - free(b); - return NULL; -} - -static void -config_init_to_defaults(struct weston_drm_backend_config *config) -{ - config->use_pixman_shadow = true; -} - -WL_EXPORT int -weston_backend_init(struct weston_compositor *compositor, - struct weston_backend_config *config_base) -{ - struct drm_backend *b; - struct weston_drm_backend_config config = {{ 0, }}; - - if (config_base == NULL || - config_base->struct_version != WESTON_DRM_BACKEND_CONFIG_VERSION || - config_base->struct_size > sizeof(struct weston_drm_backend_config)) { - weston_log("drm backend config structure is invalid\n"); - return -1; - } - - config_init_to_defaults(&config); - memcpy(&config, config_base, config_base->struct_size); - - b = drm_backend_create(compositor, &config); - if (b == NULL) - return -1; - - return 0; -} diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c deleted file mode 100644 index e7349c4b..00000000 --- a/libweston/backend-drm/fb.c +++ /dev/null @@ -1,582 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2017, 2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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 <stdint.h> - -#include <xf86drm.h> -#include <xf86drmMode.h> -#include <drm_fourcc.h> - -#include <libweston/libweston.h> -#include <libweston/backend-drm.h> -#include <libweston/pixel-formats.h> -#include <libweston/linux-dmabuf.h> -#include "shared/helpers.h" -#include "drm-internal.h" -#include "linux-dmabuf.h" - -static void -drm_fb_destroy(struct drm_fb *fb) -{ - if (fb->fb_id != 0) - drmModeRmFB(fb->fd, fb->fb_id); - weston_buffer_reference(&fb->buffer_ref, NULL); - weston_buffer_release_reference(&fb->buffer_release_ref, NULL); - free(fb); -} - -static void -drm_fb_destroy_dumb(struct drm_fb *fb) -{ - struct drm_mode_destroy_dumb destroy_arg; - - assert(fb->type == BUFFER_PIXMAN_DUMB); - - if (fb->map && fb->size > 0) - munmap(fb->map, fb->size); - - memset(&destroy_arg, 0, sizeof(destroy_arg)); - destroy_arg.handle = fb->handles[0]; - drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); - - drm_fb_destroy(fb); -} - -static int -drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb) -{ - int ret = -EINVAL; - uint64_t mods[4] = { }; - size_t i; - - /* If we have a modifier set, we must only use the WithModifiers - * entrypoint; we cannot import it through legacy ioctls. */ - if (b->fb_modifiers && fb->modifier != DRM_FORMAT_MOD_INVALID) { - /* KMS demands that if a modifier is set, it must be the same - * for all planes. */ - for (i = 0; i < ARRAY_LENGTH(mods) && fb->handles[i]; i++) - mods[i] = fb->modifier; - ret = drmModeAddFB2WithModifiers(fb->fd, fb->width, fb->height, - fb->format->format, - fb->handles, fb->strides, - fb->offsets, mods, &fb->fb_id, - DRM_MODE_FB_MODIFIERS); - return ret; - } - - ret = drmModeAddFB2(fb->fd, fb->width, fb->height, fb->format->format, - fb->handles, fb->strides, fb->offsets, &fb->fb_id, - 0); - if (ret == 0) - return 0; - - /* Legacy AddFB can't always infer the format from depth/bpp alone, so - * check if our format is one of the lucky ones. */ - if (!fb->format->depth || !fb->format->bpp) - return ret; - - /* Cannot fall back to AddFB for multi-planar formats either. */ - if (fb->handles[1] || fb->handles[2] || fb->handles[3]) - return ret; - - ret = drmModeAddFB(fb->fd, fb->width, fb->height, - fb->format->depth, fb->format->bpp, - fb->strides[0], fb->handles[0], &fb->fb_id); - return ret; -} - -struct drm_fb * -drm_fb_create_dumb(struct drm_backend *b, int width, int height, - uint32_t format) -{ - struct drm_fb *fb; - int ret; - - struct drm_mode_create_dumb create_arg; - struct drm_mode_destroy_dumb destroy_arg; - struct drm_mode_map_dumb map_arg; - - fb = zalloc(sizeof *fb); - if (!fb) - return NULL; - fb->refcnt = 1; - - fb->format = pixel_format_get_info(format); - if (!fb->format) { - weston_log("failed to look up format 0x%lx\n", - (unsigned long) format); - goto err_fb; - } - - if (!fb->format->depth || !fb->format->bpp) { - weston_log("format 0x%lx is not compatible with dumb buffers\n", - (unsigned long) format); - goto err_fb; - } - - memset(&create_arg, 0, sizeof create_arg); - create_arg.bpp = fb->format->bpp; - create_arg.width = width; - create_arg.height = height; - - ret = drmIoctl(b->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg); - if (ret) - goto err_fb; - - fb->type = BUFFER_PIXMAN_DUMB; - fb->modifier = DRM_FORMAT_MOD_INVALID; - fb->handles[0] = create_arg.handle; - fb->strides[0] = create_arg.pitch; - fb->num_planes = 1; - fb->size = create_arg.size; - fb->width = width; - fb->height = height; - fb->fd = b->drm.fd; - - if (drm_fb_addfb(b, fb) != 0) { - weston_log("failed to create kms fb: %s\n", strerror(errno)); - goto err_bo; - } - - memset(&map_arg, 0, sizeof map_arg); - map_arg.handle = fb->handles[0]; - ret = drmIoctl(fb->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg); - if (ret) - goto err_add_fb; - - fb->map = mmap(NULL, fb->size, PROT_WRITE, - MAP_SHARED, b->drm.fd, map_arg.offset); - if (fb->map == MAP_FAILED) - goto err_add_fb; - - return fb; - -err_add_fb: - drmModeRmFB(b->drm.fd, fb->fb_id); -err_bo: - memset(&destroy_arg, 0, sizeof(destroy_arg)); - destroy_arg.handle = create_arg.handle; - drmIoctl(b->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); -err_fb: - free(fb); - return NULL; -} - -struct drm_fb * -drm_fb_ref(struct drm_fb *fb) -{ - fb->refcnt++; - return fb; -} - -#ifdef BUILD_DRM_GBM -static void -drm_fb_destroy_gbm(struct gbm_bo *bo, void *data) -{ - struct drm_fb *fb = data; - - assert(fb->type == BUFFER_GBM_SURFACE || fb->type == BUFFER_CLIENT || - fb->type == BUFFER_CURSOR); - drm_fb_destroy(fb); -} - -static void -drm_fb_destroy_dmabuf(struct drm_fb *fb) -{ - /* We deliberately do not close the GEM handles here; GBM manages - * their lifetime through the BO. */ - if (fb->bo) - gbm_bo_destroy(fb->bo); - drm_fb_destroy(fb); -} - -static struct drm_fb * -drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf, - struct drm_backend *backend, bool is_opaque) -{ - struct drm_fb *fb; - struct gbm_import_fd_data import_legacy = { - .width = dmabuf->attributes.width, - .height = dmabuf->attributes.height, - .format = dmabuf->attributes.format, - .stride = dmabuf->attributes.stride[0], - .fd = dmabuf->attributes.fd[0], - }; -#ifdef HAVE_GBM_FD_IMPORT - struct gbm_import_fd_modifier_data import_mod = { - .width = dmabuf->attributes.width, - .height = dmabuf->attributes.height, - .format = dmabuf->attributes.format, - .num_fds = dmabuf->attributes.n_planes, - .modifier = dmabuf->attributes.modifier[0], - }; -#endif /* HAVE_GBM_FD_IMPORT */ - - int i; - - /* XXX: TODO: - * - * Currently the buffer is rejected if any dmabuf attribute - * flag is set. This keeps us from passing an inverted / - * interlaced / bottom-first buffer (or any other type that may - * be added in the future) through to an overlay. Ultimately, - * these types of buffers should be handled through buffer - * transforms and not as spot-checks requiring specific - * knowledge. */ - if (dmabuf->attributes.flags) - return NULL; - - fb = zalloc(sizeof *fb); - if (fb == NULL) - return NULL; - - fb->refcnt = 1; - fb->type = BUFFER_DMABUF; - -#ifdef HAVE_GBM_FD_IMPORT - static_assert(ARRAY_LENGTH(import_mod.fds) == - ARRAY_LENGTH(dmabuf->attributes.fd), - "GBM and linux_dmabuf FD size must match"); - static_assert(sizeof(import_mod.fds) == sizeof(dmabuf->attributes.fd), - "GBM and linux_dmabuf FD size must match"); - memcpy(import_mod.fds, dmabuf->attributes.fd, sizeof(import_mod.fds)); - - static_assert(ARRAY_LENGTH(import_mod.strides) == - ARRAY_LENGTH(dmabuf->attributes.stride), - "GBM and linux_dmabuf stride size must match"); - static_assert(sizeof(import_mod.strides) == - sizeof(dmabuf->attributes.stride), - "GBM and linux_dmabuf stride size must match"); - memcpy(import_mod.strides, dmabuf->attributes.stride, - sizeof(import_mod.strides)); - - static_assert(ARRAY_LENGTH(import_mod.offsets) == - ARRAY_LENGTH(dmabuf->attributes.offset), - "GBM and linux_dmabuf offset size must match"); - static_assert(sizeof(import_mod.offsets) == - sizeof(dmabuf->attributes.offset), - "GBM and linux_dmabuf offset size must match"); - memcpy(import_mod.offsets, dmabuf->attributes.offset, - sizeof(import_mod.offsets)); -#endif /* NOT HAVE_GBM_FD_IMPORT */ - - /* The legacy FD-import path does not allow us to supply modifiers, - * multiple planes, or buffer offsets. */ - if (dmabuf->attributes.modifier[0] != DRM_FORMAT_MOD_INVALID || - dmabuf->attributes.n_planes > 1 || - dmabuf->attributes.offset[0] > 0) { -#ifdef HAVE_GBM_FD_IMPORT - fb->bo = gbm_bo_import(backend->gbm, GBM_BO_IMPORT_FD_MODIFIER, - &import_mod, - GBM_BO_USE_SCANOUT); -#else /* NOT HAVE_GBM_FD_IMPORT */ - drm_debug(backend, "\t\t\t[dmabuf] Unsupported use of modifiers.\n"); - goto err_free; -#endif /* NOT HAVE_GBM_FD_IMPORT */ - } else { - fb->bo = gbm_bo_import(backend->gbm, GBM_BO_IMPORT_FD, - &import_legacy, - GBM_BO_USE_SCANOUT); - } - - if (!fb->bo) - goto err_free; - - fb->width = dmabuf->attributes.width; - fb->height = dmabuf->attributes.height; - fb->modifier = dmabuf->attributes.modifier[0]; - fb->size = 0; - fb->fd = backend->drm.fd; - - static_assert(ARRAY_LENGTH(fb->strides) == - ARRAY_LENGTH(dmabuf->attributes.stride), - "drm_fb and dmabuf stride size must match"); - static_assert(sizeof(fb->strides) == sizeof(dmabuf->attributes.stride), - "drm_fb and dmabuf stride size must match"); - memcpy(fb->strides, dmabuf->attributes.stride, sizeof(fb->strides)); - static_assert(ARRAY_LENGTH(fb->offsets) == - ARRAY_LENGTH(dmabuf->attributes.offset), - "drm_fb and dmabuf offset size must match"); - static_assert(sizeof(fb->offsets) == sizeof(dmabuf->attributes.offset), - "drm_fb and dmabuf offset size must match"); - memcpy(fb->offsets, dmabuf->attributes.offset, sizeof(fb->offsets)); - - fb->format = pixel_format_get_info(dmabuf->attributes.format); - if (!fb->format) { - weston_log("couldn't look up format info for 0x%lx\n", - (unsigned long) dmabuf->attributes.format); - goto err_free; - } - - if (is_opaque) - fb->format = pixel_format_get_opaque_substitute(fb->format); - - if (backend->min_width > fb->width || - fb->width > backend->max_width || - backend->min_height > fb->height || - fb->height > backend->max_height) { - weston_log("bo geometry out of bounds\n"); - goto err_free; - } - -#ifdef HAVE_GBM_MODIFIERS - fb->num_planes = dmabuf->attributes.n_planes; - for (i = 0; i < dmabuf->attributes.n_planes; i++) { - union gbm_bo_handle handle; - - handle = gbm_bo_get_handle_for_plane(fb->bo, i); - if (handle.s32 == -1) - goto err_free; - fb->handles[i] = handle.u32; - } -#else /* NOT HAVE_GBM_MODIFIERS */ - { - union gbm_bo_handle handle; - - fb->num_planes = 1; - - handle = gbm_bo_get_handle(fb->bo); - - if (handle.s32 == -1) - goto err_free; - fb->handles[0] = handle.u32; - } -#endif /* NOT HAVE_GBM_MODIFIERS */ - - - if (drm_fb_addfb(backend, fb) != 0) - goto err_free; - - return fb; - -err_free: - drm_fb_destroy_dmabuf(fb); - return NULL; -} - -struct drm_fb * -drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend, - bool is_opaque, enum drm_fb_type type) -{ - struct drm_fb *fb = gbm_bo_get_user_data(bo); -#ifdef HAVE_GBM_MODIFIERS - int i; -#endif - - if (fb) { - assert(fb->type == type); - return drm_fb_ref(fb); - } - - fb = zalloc(sizeof *fb); - if (fb == NULL) - return NULL; - - fb->type = type; - fb->refcnt = 1; - fb->bo = bo; - fb->fd = backend->drm.fd; - - fb->width = gbm_bo_get_width(bo); - fb->height = gbm_bo_get_height(bo); - fb->format = pixel_format_get_info(gbm_bo_get_format(bo)); - fb->size = 0; - -#ifdef HAVE_GBM_MODIFIERS - fb->modifier = gbm_bo_get_modifier(bo); - fb->num_planes = gbm_bo_get_plane_count(bo); - for (i = 0; i < fb->num_planes; i++) { - fb->strides[i] = gbm_bo_get_stride_for_plane(bo, i); - fb->handles[i] = gbm_bo_get_handle_for_plane(bo, i).u32; - fb->offsets[i] = gbm_bo_get_offset(bo, i); - } -#else - fb->num_planes = 1; - fb->strides[0] = gbm_bo_get_stride(bo); - fb->handles[0] = gbm_bo_get_handle(bo).u32; - fb->modifier = DRM_FORMAT_MOD_INVALID; -#endif - - if (!fb->format) { - weston_log("couldn't look up format 0x%lx\n", - (unsigned long) gbm_bo_get_format(bo)); - goto err_free; - } - - /* We can scanout an ARGB buffer if the surface's opaque region covers - * the whole output, but we have to use XRGB as the KMS format code. */ - if (is_opaque) - fb->format = pixel_format_get_opaque_substitute(fb->format); - - if (backend->min_width > fb->width || - fb->width > backend->max_width || - backend->min_height > fb->height || - fb->height > backend->max_height) { - weston_log("bo geometry out of bounds\n"); - goto err_free; - } - - if (drm_fb_addfb(backend, fb) != 0) { - if (type == BUFFER_GBM_SURFACE) - weston_log("failed to create kms fb: %s\n", - strerror(errno)); - goto err_free; - } - - gbm_bo_set_user_data(bo, fb, drm_fb_destroy_gbm); - - return fb; - -err_free: - free(fb); - return NULL; -} - -static void -drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer, - struct weston_buffer_release *buffer_release) -{ - assert(fb->buffer_ref.buffer == NULL); - assert(fb->type == BUFFER_CLIENT || fb->type == BUFFER_DMABUF); - weston_buffer_reference(&fb->buffer_ref, buffer); - weston_buffer_release_reference(&fb->buffer_release_ref, - buffer_release); -} -#endif - -void -drm_fb_unref(struct drm_fb *fb) -{ - if (!fb) - return; - - assert(fb->refcnt > 0); - if (--fb->refcnt > 0) - return; - - switch (fb->type) { - case BUFFER_PIXMAN_DUMB: - drm_fb_destroy_dumb(fb); - break; -#ifdef BUILD_DRM_GBM - case BUFFER_CURSOR: - case BUFFER_CLIENT: - gbm_bo_destroy(fb->bo); - break; - case BUFFER_GBM_SURFACE: - gbm_surface_release_buffer(fb->gbm_surface, fb->bo); - break; - case BUFFER_DMABUF: - drm_fb_destroy_dmabuf(fb); - break; -#endif - default: - assert(NULL); - break; - } -} - -#ifdef BUILD_DRM_GBM -bool -drm_can_scanout_dmabuf(struct weston_compositor *ec, - struct linux_dmabuf_buffer *dmabuf) -{ - struct drm_fb *fb; - struct drm_backend *b = to_drm_backend(ec); - bool ret = false; - - fb = drm_fb_get_from_dmabuf(dmabuf, b, true); - if (fb) - ret = true; - - drm_fb_unref(fb); - drm_debug(b, "[dmabuf] dmabuf %p, import test %s\n", dmabuf, - ret ? "succeeded" : "failed"); - return ret; -} - -struct drm_fb * -drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev) -{ - struct drm_output *output = state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; - bool is_opaque = weston_view_is_opaque(ev, &ev->transform.boundingbox); - struct linux_dmabuf_buffer *dmabuf; - struct drm_fb *fb; - - if (ev->alpha != 1.0f) - return NULL; - - if (!drm_view_transform_supported(ev, &output->base)) - return NULL; - - if (ev->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED && - ev->surface->desired_protection > output->base.current_protection) - return NULL; - - if (!buffer) - return NULL; - - if (wl_shm_buffer_get(buffer->resource)) - return NULL; - - /* GBM is used for dmabuf import as well as from client wl_buffer. */ - if (!b->gbm) - return NULL; - - dmabuf = linux_dmabuf_buffer_get(buffer->resource); - if (dmabuf) { - fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque); - if (!fb) - return NULL; - } else { - struct gbm_bo *bo; - - bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER, - buffer->resource, GBM_BO_USE_SCANOUT); - if (!bo) - return NULL; - - fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT); - if (!fb) { - gbm_bo_destroy(bo); - return NULL; - } - } - - drm_debug(b, "\t\t\t[view] view %p format: %s\n", - ev, fb->format->drm_format_name); - drm_fb_set_buffer(fb, buffer, - ev->surface->buffer_release_ref.buffer_release); - return fb; -} -#endif diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c deleted file mode 100644 index 192435c7..00000000 --- a/libweston/backend-drm/kms.c +++ /dev/null @@ -1,1525 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2017, 2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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 <stdint.h> - -#include <xf86drm.h> -#include <xf86drmMode.h> -#include <drm_fourcc.h> - -#include <libweston/libweston.h> -#include <libweston/backend-drm.h> -#include "shared/helpers.h" -#include "drm-internal.h" -#include "pixel-formats.h" -#include "presentation-time-server-protocol.h" - -#ifndef DRM_FORMAT_MOD_LINEAR -#define DRM_FORMAT_MOD_LINEAR 0 -#endif - -struct drm_property_enum_info plane_type_enums[] = { - [WDRM_PLANE_TYPE_PRIMARY] = { - .name = "Primary", - }, - [WDRM_PLANE_TYPE_OVERLAY] = { - .name = "Overlay", - }, - [WDRM_PLANE_TYPE_CURSOR] = { - .name = "Cursor", - }, -}; - -const struct drm_property_info plane_props[] = { - [WDRM_PLANE_TYPE] = { - .name = "type", - .enum_values = plane_type_enums, - .num_enum_values = WDRM_PLANE_TYPE__COUNT, - }, - [WDRM_PLANE_SRC_X] = { .name = "SRC_X", }, - [WDRM_PLANE_SRC_Y] = { .name = "SRC_Y", }, - [WDRM_PLANE_SRC_W] = { .name = "SRC_W", }, - [WDRM_PLANE_SRC_H] = { .name = "SRC_H", }, - [WDRM_PLANE_CRTC_X] = { .name = "CRTC_X", }, - [WDRM_PLANE_CRTC_Y] = { .name = "CRTC_Y", }, - [WDRM_PLANE_CRTC_W] = { .name = "CRTC_W", }, - [WDRM_PLANE_CRTC_H] = { .name = "CRTC_H", }, - [WDRM_PLANE_FB_ID] = { .name = "FB_ID", }, - [WDRM_PLANE_CRTC_ID] = { .name = "CRTC_ID", }, - [WDRM_PLANE_IN_FORMATS] = { .name = "IN_FORMATS" }, - [WDRM_PLANE_IN_FENCE_FD] = { .name = "IN_FENCE_FD" }, - [WDRM_PLANE_FB_DAMAGE_CLIPS] = { .name = "FB_DAMAGE_CLIPS" }, - [WDRM_PLANE_ZPOS] = { .name = "zpos" }, -}; - -struct drm_property_enum_info dpms_state_enums[] = { - [WDRM_DPMS_STATE_OFF] = { - .name = "Off", - }, - [WDRM_DPMS_STATE_ON] = { - .name = "On", - }, - [WDRM_DPMS_STATE_STANDBY] = { - .name = "Standby", - }, - [WDRM_DPMS_STATE_SUSPEND] = { - .name = "Suspend", - }, -}; - -struct drm_property_enum_info content_protection_enums[] = { - [WDRM_CONTENT_PROTECTION_UNDESIRED] = { - .name = "Undesired", - }, - [WDRM_CONTENT_PROTECTION_DESIRED] = { - .name = "Desired", - }, - [WDRM_CONTENT_PROTECTION_ENABLED] = { - .name = "Enabled", - }, -}; - -struct drm_property_enum_info hdcp_content_type_enums[] = { - [WDRM_HDCP_CONTENT_TYPE0] = { - .name = "HDCP Type0", - }, - [WDRM_HDCP_CONTENT_TYPE1] = { - .name = "HDCP Type1", - }, -}; - -const struct drm_property_info connector_props[] = { - [WDRM_CONNECTOR_EDID] = { .name = "EDID" }, - [WDRM_CONNECTOR_DPMS] = { - .name = "DPMS", - .enum_values = dpms_state_enums, - .num_enum_values = WDRM_DPMS_STATE__COUNT, - }, - [WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", }, - [WDRM_CONNECTOR_NON_DESKTOP] = { .name = "non-desktop", }, - [WDRM_CONNECTOR_CONTENT_PROTECTION] = { - .name = "Content Protection", - .enum_values = content_protection_enums, - .num_enum_values = WDRM_CONTENT_PROTECTION__COUNT, - }, - [WDRM_CONNECTOR_HDCP_CONTENT_TYPE] = { - .name = "HDCP Content Type", - .enum_values = hdcp_content_type_enums, - .num_enum_values = WDRM_HDCP_CONTENT_TYPE__COUNT, - }, -}; - -const struct drm_property_info crtc_props[] = { - [WDRM_CRTC_MODE_ID] = { .name = "MODE_ID", }, - [WDRM_CRTC_ACTIVE] = { .name = "ACTIVE", }, -}; - - -/** - * Mode for drm_pending_state_apply and co. - */ -enum drm_state_apply_mode { - DRM_STATE_APPLY_SYNC, /**< state fully processed */ - DRM_STATE_APPLY_ASYNC, /**< state pending event delivery */ - DRM_STATE_TEST_ONLY, /**< test if the state can be applied */ -}; - -/** - * Get the current value of a KMS property - * - * Given a drmModeObjectGetProperties return, as well as the drm_property_info - * for the target property, return the current value of that property, - * with an optional default. If the property is a KMS enum type, the return - * value will be translated into the appropriate internal enum. - * - * If the property is not present, the default value will be returned. - * - * @param info Internal structure for property to look up - * @param props Raw KMS properties for the target object - * @param def Value to return if property is not found - */ -uint64_t -drm_property_get_value(struct drm_property_info *info, - const drmModeObjectProperties *props, - uint64_t def) -{ - unsigned int i; - - if (info->prop_id == 0) - return def; - - for (i = 0; i < props->count_props; i++) { - unsigned int j; - - if (props->props[i] != info->prop_id) - continue; - - /* Simple (non-enum) types can return the value directly */ - if (info->num_enum_values == 0) - return props->prop_values[i]; - - /* Map from raw value to enum value */ - for (j = 0; j < info->num_enum_values; j++) { - if (!info->enum_values[j].valid) - continue; - if (info->enum_values[j].value != props->prop_values[i]) - continue; - - return j; - } - - /* We don't have a mapping for this enum; return default. */ - break; - } - - return def; -} - -/** - * Get the current range values of a KMS property - * - * Given a drmModeObjectGetProperties return, as well as the drm_property_info - * for the target property, return the current range values of that property, - * - * If the property is not present, or there's no it is not a prop range then - * NULL will be returned. - * - * @param info Internal structure for property to look up - * @param props Raw KMS properties for the target object - */ -uint64_t * -drm_property_get_range_values(struct drm_property_info *info, - const drmModeObjectProperties *props) -{ - unsigned int i; - - if (info->prop_id == 0) - return NULL; - - for (i = 0; i < props->count_props; i++) { - - if (props->props[i] != info->prop_id) - continue; - - if (!(info->flags & DRM_MODE_PROP_RANGE) && - !(info->flags & DRM_MODE_PROP_SIGNED_RANGE)) - continue; - - return info->range_values; - } - - return NULL; -} - -/** - * Cache DRM property values - * - * Update a per-object array of drm_property_info structures, given the - * DRM properties of the object. - * - * Call this every time an object newly appears (note that only connectors - * can be hotplugged), the first time it is seen, or when its status changes - * in a way which invalidates the potential property values (currently, the - * only case for this is connector hotplug). - * - * This updates the property IDs and enum values within the drm_property_info - * array. - * - * DRM property enum values are dynamic at runtime; the user must query the - * property to find out the desired runtime value for a requested string - * name. Using the 'type' field on planes as an example, there is no single - * hardcoded constant for primary plane types; instead, the property must be - * queried at runtime to find the value associated with the string "Primary". - * - * This helper queries and caches the enum values, to allow us to use a set - * of compile-time-constant enums portably across various implementations. - * The values given in enum_names are searched for, and stored in the - * same-indexed field of the map array. - * - * @param b DRM backend object - * @param src DRM property info array to source from - * @param info DRM property info array to copy into - * @param num_infos Number of entries in the source array - * @param props DRM object properties for the object - */ -void -drm_property_info_populate(struct drm_backend *b, - const struct drm_property_info *src, - struct drm_property_info *info, - unsigned int num_infos, - drmModeObjectProperties *props) -{ - drmModePropertyRes *prop; - unsigned i, j; - - for (i = 0; i < num_infos; i++) { - unsigned int j; - - info[i].name = src[i].name; - info[i].prop_id = 0; - info[i].num_enum_values = src[i].num_enum_values; - - if (src[i].num_enum_values == 0) - continue; - - info[i].enum_values = - malloc(src[i].num_enum_values * - sizeof(*info[i].enum_values)); - assert(info[i].enum_values); - for (j = 0; j < info[i].num_enum_values; j++) { - info[i].enum_values[j].name = src[i].enum_values[j].name; - info[i].enum_values[j].valid = false; - } - } - - for (i = 0; i < props->count_props; i++) { - unsigned int k; - - prop = drmModeGetProperty(b->drm.fd, props->props[i]); - if (!prop) - continue; - - for (j = 0; j < num_infos; j++) { - if (!strcmp(prop->name, info[j].name)) - break; - } - - /* We don't know/care about this property. */ - if (j == num_infos) { -#ifdef DEBUG - weston_log("DRM debug: unrecognized property %u '%s'\n", - prop->prop_id, prop->name); -#endif - drmModeFreeProperty(prop); - continue; - } - - if (info[j].num_enum_values == 0 && - (prop->flags & DRM_MODE_PROP_ENUM)) { - weston_log("DRM: expected property %s to not be an" - " enum, but it is; ignoring\n", prop->name); - drmModeFreeProperty(prop); - continue; - } - - info[j].prop_id = props->props[i]; - info[j].flags = prop->flags; - - if (prop->flags & DRM_MODE_PROP_RANGE || - prop->flags & DRM_MODE_PROP_SIGNED_RANGE) { - info[j].num_range_values = prop->count_values; - for (int i = 0; i < prop->count_values; i++) - info[j].range_values[i] = prop->values[i]; - } - - - if (info[j].num_enum_values == 0) { - drmModeFreeProperty(prop); - continue; - } - - if (!(prop->flags & DRM_MODE_PROP_ENUM)) { - weston_log("DRM: expected property %s to be an enum," - " but it is not; ignoring\n", prop->name); - drmModeFreeProperty(prop); - info[j].prop_id = 0; - continue; - } - - for (k = 0; k < info[j].num_enum_values; k++) { - int l; - - for (l = 0; l < prop->count_enums; l++) { - if (!strcmp(prop->enums[l].name, - info[j].enum_values[k].name)) - break; - } - - if (l == prop->count_enums) - continue; - - info[j].enum_values[k].valid = true; - info[j].enum_values[k].value = prop->enums[l].value; - } - - drmModeFreeProperty(prop); - } - -#ifdef DEBUG - for (i = 0; i < num_infos; i++) { - if (info[i].prop_id == 0) - weston_log("DRM warning: property '%s' missing\n", - info[i].name); - } -#endif -} - -/** - * Free DRM property information - * - * Frees all memory associated with a DRM property info array and zeroes - * it out, leaving it usable for a further drm_property_info_update() or - * drm_property_info_free(). - * - * @param info DRM property info array - * @param num_props Number of entries in array to free - */ -void -drm_property_info_free(struct drm_property_info *info, int num_props) -{ - int i; - - for (i = 0; i < num_props; i++) - free(info[i].enum_values); - - memset(info, 0, sizeof(*info) * num_props); -} - -static inline uint32_t * -formats_ptr(struct drm_format_modifier_blob *blob) -{ - return (uint32_t *)(((char *)blob) + blob->formats_offset); -} - -static inline struct drm_format_modifier * -modifiers_ptr(struct drm_format_modifier_blob *blob) -{ - return (struct drm_format_modifier *) - (((char *)blob) + blob->modifiers_offset); -} - -/** - * Populates the plane's formats array, using either the IN_FORMATS blob - * property (if available), or the plane's format list if not. - */ -int -drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane, - const drmModeObjectProperties *props) -{ - unsigned i; - drmModePropertyBlobRes *blob; - struct drm_format_modifier_blob *fmt_mod_blob; - struct drm_format_modifier *blob_modifiers; - uint32_t *blob_formats; - uint32_t blob_id; - - blob_id = drm_property_get_value(&plane->props[WDRM_PLANE_IN_FORMATS], - props, - 0); - if (blob_id == 0) - goto fallback; - - blob = drmModeGetPropertyBlob(plane->backend->drm.fd, blob_id); - if (!blob) - goto fallback; - - fmt_mod_blob = blob->data; - blob_formats = formats_ptr(fmt_mod_blob); - blob_modifiers = modifiers_ptr(fmt_mod_blob); - - if (plane->count_formats != fmt_mod_blob->count_formats) { - weston_log("DRM backend: format count differs between " - "plane (%d) and IN_FORMATS (%d)\n", - plane->count_formats, - fmt_mod_blob->count_formats); - weston_log("This represents a kernel bug; Weston is " - "unable to continue.\n"); - abort(); - } - - for (i = 0; i < fmt_mod_blob->count_formats; i++) { - uint32_t count_modifiers = 0; - uint64_t *modifiers = NULL; - unsigned j; - - for (j = 0; j < fmt_mod_blob->count_modifiers; j++) { - struct drm_format_modifier *mod = &blob_modifiers[j]; - - if ((i < mod->offset) || (i > mod->offset + 63)) - continue; - if (!(mod->formats & (1 << (i - mod->offset)))) - continue; - - modifiers = realloc(modifiers, - (count_modifiers + 1) * - sizeof(modifiers[0])); - assert(modifiers); - modifiers[count_modifiers++] = mod->modifier; - } - - if (count_modifiers == 0) { - modifiers = malloc(sizeof(*modifiers)); - *modifiers = DRM_FORMAT_MOD_LINEAR; - count_modifiers = 1; - } - - plane->formats[i].format = blob_formats[i]; - plane->formats[i].modifiers = modifiers; - plane->formats[i].count_modifiers = count_modifiers; - } - - drmModeFreePropertyBlob(blob); - - return 0; - -fallback: - /* No IN_FORMATS blob available, so just use the old. */ - assert(plane->count_formats == kplane->count_formats); - for (i = 0; i < kplane->count_formats; i++) { - plane->formats[i].format = kplane->formats[i]; - plane->formats[i].modifiers = malloc(sizeof(uint64_t)); - plane->formats[i].modifiers[0] = DRM_FORMAT_MOD_LINEAR; - plane->formats[i].count_modifiers = 1; - } - - return 0; -} - -void -drm_output_set_gamma(struct weston_output *output_base, - uint16_t size, uint16_t *r, uint16_t *g, uint16_t *b) -{ - int rc; - struct drm_output *output = to_drm_output(output_base); - struct drm_backend *backend = - to_drm_backend(output->base.compositor); - - /* check */ - if (output_base->gamma_size != size) - return; - - rc = drmModeCrtcSetGamma(backend->drm.fd, - output->crtc_id, - size, r, g, b); - if (rc) - weston_log("set gamma failed: %s\n", strerror(errno)); -} - -/** - * Mark an output state as current on the output, i.e. it has been - * submitted to the kernel. The mode argument determines whether this - * update will be applied synchronously (e.g. when calling drmModeSetCrtc), - * or asynchronously (in which case we wait for events to complete). - */ -static void -drm_output_assign_state(struct drm_output_state *state, - enum drm_state_apply_mode mode) -{ - struct drm_output *output = state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_plane_state *plane_state; - struct drm_head *head; - - assert(!output->state_last); - - if (mode == DRM_STATE_APPLY_ASYNC) - output->state_last = output->state_cur; - else - drm_output_state_free(output->state_cur); - - wl_list_remove(&state->link); - wl_list_init(&state->link); - state->pending_state = NULL; - - output->state_cur = state; - - if (b->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) { - drm_debug(b, "\t[CRTC:%u] setting pending flip\n", output->crtc_id); - output->atomic_complete_pending = true; - } - - if (b->atomic_modeset && - state->protection == WESTON_HDCP_DISABLE) - wl_list_for_each(head, &output->base.head_list, base.output_link) - weston_head_set_content_protection_status(&head->base, - WESTON_HDCP_DISABLE); - - /* Replace state_cur on each affected plane with the new state, being - * careful to dispose of orphaned (but only orphaned) previous state. - * If the previous state is not orphaned (still has an output_state - * attached), it will be disposed of by freeing the output_state. */ - wl_list_for_each(plane_state, &state->plane_list, link) { - struct drm_plane *plane = plane_state->plane; - - if (plane->state_cur && !plane->state_cur->output_state) - drm_plane_state_free(plane->state_cur, true); - plane->state_cur = plane_state; - - if (mode != DRM_STATE_APPLY_ASYNC) { - plane_state->complete = true; - continue; - } - - if (b->atomic_modeset) - continue; - - assert(plane->type != WDRM_PLANE_TYPE_OVERLAY); - if (plane->type == WDRM_PLANE_TYPE_PRIMARY) - output->page_flip_pending = true; - } -} - -static void -drm_output_set_cursor(struct drm_output_state *output_state) -{ - struct drm_output *output = output_state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_plane *plane = output->cursor_plane; - struct drm_plane_state *state; - uint32_t handle; - - if (!plane) - return; - - state = drm_output_state_get_existing_plane(output_state, plane); - if (!state) - return; - - if (!state->fb) { - pixman_region32_fini(&plane->base.damage); - pixman_region32_init(&plane->base.damage); - drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); - return; - } - - assert(state->fb == output->gbm_cursor_fb[output->current_cursor]); - assert(!plane->state_cur->output || plane->state_cur->output == output); - - handle = output->gbm_cursor_handle[output->current_cursor]; - if (plane->state_cur->fb != state->fb) { - if (drmModeSetCursor(b->drm.fd, output->crtc_id, handle, - b->cursor_width, b->cursor_height)) { - weston_log("failed to set cursor: %s\n", - strerror(errno)); - goto err; - } - } - - pixman_region32_fini(&plane->base.damage); - pixman_region32_init(&plane->base.damage); - - if (drmModeMoveCursor(b->drm.fd, output->crtc_id, - state->dest_x, state->dest_y)) { - weston_log("failed to move cursor: %s\n", strerror(errno)); - goto err; - } - - return; - -err: - b->cursors_are_broken = true; - drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); -} - -static int -drm_output_apply_state_legacy(struct drm_output_state *state) -{ - struct drm_output *output = state->output; - struct drm_backend *backend = to_drm_backend(output->base.compositor); - struct drm_plane *scanout_plane = output->scanout_plane; - struct drm_property_info *dpms_prop; - struct drm_plane_state *scanout_state; - struct drm_mode *mode; - struct drm_head *head; - const struct pixel_format_info *pinfo = NULL; - uint32_t connectors[MAX_CLONED_CONNECTORS]; - int n_conn = 0; - struct timespec now; - int ret = 0; - - wl_list_for_each(head, &output->base.head_list, base.output_link) { - assert(n_conn < MAX_CLONED_CONNECTORS); - connectors[n_conn++] = head->connector_id; - } - - /* If disable_planes is set then assign_planes() wasn't - * called for this render, so we could still have a stale - * cursor plane set up. - */ - if (output->base.disable_planes) { - output->cursor_view = NULL; - if (output->cursor_plane) { - output->cursor_plane->base.x = INT32_MIN; - output->cursor_plane->base.y = INT32_MIN; - } - } - - if (state->dpms != WESTON_DPMS_ON) { - if (output->cursor_plane) { - ret = drmModeSetCursor(backend->drm.fd, output->crtc_id, - 0, 0, 0); - if (ret) - weston_log("drmModeSetCursor failed disable: %s\n", - strerror(errno)); - } - - ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, 0, 0, 0, - NULL, 0, NULL); - if (ret) - weston_log("drmModeSetCrtc failed disabling: %s\n", - strerror(errno)); - - drm_output_assign_state(state, DRM_STATE_APPLY_SYNC); - weston_compositor_read_presentation_clock(output->base.compositor, &now); - drm_output_update_complete(output, - WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION, - now.tv_sec, now.tv_nsec / 1000); - - return 0; - } - - scanout_state = - drm_output_state_get_existing_plane(state, scanout_plane); - - /* The legacy SetCrtc API doesn't allow us to do scaling, and the - * legacy PageFlip API doesn't allow us to do clipping either. */ - assert(scanout_state->src_x == 0); - assert(scanout_state->src_y == 0); - assert(scanout_state->src_w == - (unsigned) (output->base.current_mode->width << 16)); - assert(scanout_state->src_h == - (unsigned) (output->base.current_mode->height << 16)); - assert(scanout_state->dest_x == 0); - assert(scanout_state->dest_y == 0); - assert(scanout_state->dest_w == scanout_state->src_w >> 16); - assert(scanout_state->dest_h == scanout_state->src_h >> 16); - /* The legacy SetCrtc API doesn't support fences */ - assert(scanout_state->in_fence_fd == -1); - - mode = to_drm_mode(output->base.current_mode); - if (backend->state_invalid || - !scanout_plane->state_cur->fb || - scanout_plane->state_cur->fb->strides[0] != - scanout_state->fb->strides[0]) { - - ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, - scanout_state->fb->fb_id, - 0, 0, - connectors, n_conn, - &mode->mode_info); - if (ret) { - weston_log("set mode failed: %s\n", strerror(errno)); - goto err; - } - } - - pinfo = scanout_state->fb->format; - drm_debug(backend, "\t[CRTC:%u, PLANE:%u] FORMAT: %s\n", - output->crtc_id, scanout_state->plane->plane_id, - pinfo ? pinfo->drm_format_name : "UNKNOWN"); - - if (drmModePageFlip(backend->drm.fd, output->crtc_id, - scanout_state->fb->fb_id, - DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { - weston_log("queueing pageflip failed: %s\n", strerror(errno)); - goto err; - } - - assert(!output->page_flip_pending); - - if (output->pageflip_timer) - wl_event_source_timer_update(output->pageflip_timer, - backend->pageflip_timeout); - - drm_output_set_cursor(state); - - if (state->dpms != output->state_cur->dpms) { - wl_list_for_each(head, &output->base.head_list, base.output_link) { - dpms_prop = &head->props_conn[WDRM_CONNECTOR_DPMS]; - if (dpms_prop->prop_id == 0) - continue; - - ret = drmModeConnectorSetProperty(backend->drm.fd, - head->connector_id, - dpms_prop->prop_id, - state->dpms); - if (ret) { - weston_log("DRM: DPMS: failed property set for %s\n", - head->base.name); - } - } - } - - drm_output_assign_state(state, DRM_STATE_APPLY_ASYNC); - - return 0; - -err: - output->cursor_view = NULL; - drm_output_state_free(state); - return -1; -} - -static int -crtc_add_prop(drmModeAtomicReq *req, struct drm_output *output, - enum wdrm_crtc_property prop, uint64_t val) -{ - struct drm_property_info *info = &output->props_crtc[prop]; - int ret; - - if (info->prop_id == 0) - return -1; - - ret = drmModeAtomicAddProperty(req, output->crtc_id, info->prop_id, - val); - drm_debug(output->backend, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n", - (unsigned long) output->crtc_id, - (unsigned long) info->prop_id, info->name, - (unsigned long long) val, (unsigned long long) val); - return (ret <= 0) ? -1 : 0; -} - -static int -connector_add_prop(drmModeAtomicReq *req, struct drm_head *head, - enum wdrm_connector_property prop, uint64_t val) -{ - struct drm_property_info *info = &head->props_conn[prop]; - int ret; - - if (info->prop_id == 0) - return -1; - - ret = drmModeAtomicAddProperty(req, head->connector_id, - info->prop_id, val); - drm_debug(head->backend, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n", - (unsigned long) head->connector_id, - (unsigned long) info->prop_id, info->name, - (unsigned long long) val, (unsigned long long) val); - return (ret <= 0) ? -1 : 0; -} - -static int -plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane, - enum wdrm_plane_property prop, uint64_t val) -{ - struct drm_property_info *info = &plane->props[prop]; - int ret; - - if (info->prop_id == 0) - return -1; - - ret = drmModeAtomicAddProperty(req, plane->plane_id, info->prop_id, - val); - drm_debug(plane->backend, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n", - (unsigned long) plane->plane_id, - (unsigned long) info->prop_id, info->name, - (unsigned long long) val, (unsigned long long) val); - return (ret <= 0) ? -1 : 0; -} - - -static int -plane_add_damage(drmModeAtomicReq *req, struct drm_backend *backend, - struct drm_plane_state *plane_state) -{ - struct drm_plane *plane = plane_state->plane; - struct drm_property_info *info = - &plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS]; - pixman_box32_t *rects; - uint32_t blob_id; - int n_rects; - int ret; - - if (!pixman_region32_not_empty(&plane_state->damage)) - return 0; - - /* - * If a plane doesn't support fb damage blob property, kernel will - * perform full plane update. - */ - if (info->prop_id == 0) - return 0; - - rects = pixman_region32_rectangles(&plane_state->damage, &n_rects); - - ret = drmModeCreatePropertyBlob(backend->drm.fd, rects, - sizeof(*rects) * n_rects, &blob_id); - if (ret != 0) - return ret; - - ret = plane_add_prop(req, plane, WDRM_PLANE_FB_DAMAGE_CLIPS, blob_id); - if (ret != 0) - return ret; - - return 0; -} - -static bool -drm_head_has_prop(struct drm_head *head, - enum wdrm_connector_property prop) -{ - if (head && head->props_conn[prop].prop_id != 0) - return true; - - return false; -} - -/* - * This function converts the protection requests from weston_hdcp_protection - * corresponding drm values. These values can be set in "Content Protection" - * & "HDCP Content Type" connector properties. - */ -static void -get_drm_protection_from_weston(enum weston_hdcp_protection weston_protection, - enum wdrm_content_protection_state *drm_protection, - enum wdrm_hdcp_content_type *drm_cp_type) -{ - - switch (weston_protection) { - case WESTON_HDCP_DISABLE: - *drm_protection = WDRM_CONTENT_PROTECTION_UNDESIRED; - *drm_cp_type = WDRM_HDCP_CONTENT_TYPE0; - break; - case WESTON_HDCP_ENABLE_TYPE_0: - *drm_protection = WDRM_CONTENT_PROTECTION_DESIRED; - *drm_cp_type = WDRM_HDCP_CONTENT_TYPE0; - break; - case WESTON_HDCP_ENABLE_TYPE_1: - *drm_protection = WDRM_CONTENT_PROTECTION_DESIRED; - *drm_cp_type = WDRM_HDCP_CONTENT_TYPE1; - break; - default: - assert(0 && "bad weston_hdcp_protection"); - } -} - -static void -drm_head_set_hdcp_property(struct drm_head *head, - enum weston_hdcp_protection protection, - drmModeAtomicReq *req) -{ - int ret; - enum wdrm_content_protection_state drm_protection; - enum wdrm_hdcp_content_type drm_cp_type; - struct drm_property_enum_info *enum_info; - uint64_t prop_val; - - get_drm_protection_from_weston(protection, &drm_protection, - &drm_cp_type); - - if (!drm_head_has_prop(head, WDRM_CONNECTOR_CONTENT_PROTECTION)) - return; - - /* - * Content-type property is not exposed for platforms not supporting - * HDCP2.2, therefore, type-1 cannot be supported. The type-0 content - * still can be supported if the content-protection property is exposed. - */ - if (!drm_head_has_prop(head, WDRM_CONNECTOR_HDCP_CONTENT_TYPE) && - drm_cp_type != WDRM_HDCP_CONTENT_TYPE0) - return; - - enum_info = head->props_conn[WDRM_CONNECTOR_CONTENT_PROTECTION].enum_values; - prop_val = enum_info[drm_protection].value; - ret = connector_add_prop(req, head, WDRM_CONNECTOR_CONTENT_PROTECTION, - prop_val); - assert(ret == 0); - - if (!drm_head_has_prop(head, WDRM_CONNECTOR_HDCP_CONTENT_TYPE)) - return; - - enum_info = head->props_conn[WDRM_CONNECTOR_HDCP_CONTENT_TYPE].enum_values; - prop_val = enum_info[drm_cp_type].value; - ret = connector_add_prop(req, head, WDRM_CONNECTOR_HDCP_CONTENT_TYPE, - prop_val); - assert(ret == 0); -} - -static int -drm_output_apply_state_atomic(struct drm_output_state *state, - drmModeAtomicReq *req, - uint32_t *flags) -{ - struct drm_output *output = state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_plane_state *plane_state; - struct drm_mode *current_mode = to_drm_mode(output->base.current_mode); - struct drm_head *head; - int ret = 0; - - drm_debug(b, "\t\t[atomic] %s output %lu (%s) state\n", - (*flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "testing" : "applying", - (unsigned long) output->base.id, output->base.name); - - if (state->dpms != output->state_cur->dpms) { - drm_debug(b, "\t\t\t[atomic] DPMS state differs, modeset OK\n"); - *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; - } - - if (state->dpms == WESTON_DPMS_ON) { - ret = drm_mode_ensure_blob(b, current_mode); - if (ret != 0) - return ret; - - ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID, - current_mode->blob_id); - ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 1); - - /* No need for the DPMS property, since it is implicit in - * routing and CRTC activity. */ - wl_list_for_each(head, &output->base.head_list, base.output_link) { - ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID, - output->crtc_id); - } - } else { - ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID, 0); - ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 0); - - /* No need for the DPMS property, since it is implicit in - * routing and CRTC activity. */ - wl_list_for_each(head, &output->base.head_list, base.output_link) - ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID, 0); - } - - wl_list_for_each(head, &output->base.head_list, base.output_link) - drm_head_set_hdcp_property(head, state->protection, req); - - if (ret != 0) { - weston_log("couldn't set atomic CRTC/connector state\n"); - return ret; - } - - wl_list_for_each(plane_state, &state->plane_list, link) { - struct drm_plane *plane = plane_state->plane; - const struct pixel_format_info *pinfo = NULL; - - ret |= plane_add_prop(req, plane, WDRM_PLANE_FB_ID, - plane_state->fb ? plane_state->fb->fb_id : 0); - ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID, - plane_state->fb ? output->crtc_id : 0); - ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_X, - plane_state->src_x); - ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_Y, - plane_state->src_y); - ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_W, - plane_state->src_w); - ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_H, - plane_state->src_h); - ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_X, - plane_state->dest_x); - ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_Y, - plane_state->dest_y); - ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_W, - plane_state->dest_w); - ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_H, - plane_state->dest_h); - ret |= plane_add_damage(req, b, plane_state); - - if (plane_state->fb && plane_state->fb->format) - pinfo = plane_state->fb->format; - - drm_debug(plane->backend, "\t\t\t[PLANE:%lu] FORMAT: %s\n", - (unsigned long) plane->plane_id, - pinfo ? pinfo->drm_format_name : "UNKNOWN"); - - if (plane_state->in_fence_fd >= 0) { - ret |= plane_add_prop(req, plane, - WDRM_PLANE_IN_FENCE_FD, - plane_state->in_fence_fd); - } - - /* do note, that 'invented' zpos values are set as immutable */ - if (plane_state->zpos != DRM_PLANE_ZPOS_INVALID_PLANE && - plane_state->plane->zpos_min != plane_state->plane->zpos_max) - ret |= plane_add_prop(req, plane, - WDRM_PLANE_ZPOS, - plane_state->zpos); - - if (ret != 0) { - weston_log("couldn't set plane state\n"); - return ret; - } - } - - return 0; -} - -/** - * Helper function used only by drm_pending_state_apply, with the same - * guarantees and constraints as that function. - */ -static int -drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, - enum drm_state_apply_mode mode) -{ - struct drm_backend *b = pending_state->backend; - struct drm_output_state *output_state, *tmp; - struct drm_plane *plane; - drmModeAtomicReq *req = drmModeAtomicAlloc(); - uint32_t flags; - int ret = 0; - - if (!req) - return -1; - - switch (mode) { - case DRM_STATE_APPLY_SYNC: - flags = 0; - break; - case DRM_STATE_APPLY_ASYNC: - flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK; - break; - case DRM_STATE_TEST_ONLY: - flags = DRM_MODE_ATOMIC_TEST_ONLY; - break; - } - - if (b->state_invalid) { - struct weston_head *head_base; - struct drm_head *head; - uint32_t *unused; - int err; - - drm_debug(b, "\t\t[atomic] previous state invalid; " - "starting with fresh state\n"); - - /* If we need to reset all our state (e.g. because we've - * just started, or just been VT-switched in), explicitly - * disable all the CRTCs and connectors we aren't using. */ - wl_list_for_each(head_base, - &b->compositor->head_list, compositor_link) { - struct drm_property_info *info; - - if (weston_head_is_enabled(head_base)) - continue; - - head = to_drm_head(head_base); - - drm_debug(b, "\t\t[atomic] disabling inactive head %s\n", - head_base->name); - - info = &head->props_conn[WDRM_CONNECTOR_CRTC_ID]; - err = drmModeAtomicAddProperty(req, head->connector_id, - info->prop_id, 0); - drm_debug(b, "\t\t\t[CONN:%lu] %lu (%s) -> 0\n", - (unsigned long) head->connector_id, - (unsigned long) info->prop_id, - info->name); - if (err <= 0) - ret = -1; - } - - wl_array_for_each(unused, &b->unused_crtcs) { - struct drm_property_info infos[WDRM_CRTC__COUNT]; - struct drm_property_info *info; - drmModeObjectProperties *props; - uint64_t active; - - memset(infos, 0, sizeof(infos)); - - /* We can't emit a disable on a CRTC that's already - * off, as the kernel will refuse to generate an event - * for an off->off state and fail the commit. - */ - props = drmModeObjectGetProperties(b->drm.fd, - *unused, - DRM_MODE_OBJECT_CRTC); - if (!props) { - ret = -1; - continue; - } - - drm_property_info_populate(b, crtc_props, infos, - WDRM_CRTC__COUNT, - props); - - info = &infos[WDRM_CRTC_ACTIVE]; - active = drm_property_get_value(info, props, 0); - drmModeFreeObjectProperties(props); - if (active == 0) { - drm_property_info_free(infos, WDRM_CRTC__COUNT); - continue; - } - - drm_debug(b, "\t\t[atomic] disabling unused CRTC %lu\n", - (unsigned long) *unused); - - drm_debug(b, "\t\t\t[CRTC:%lu] %lu (%s) -> 0\n", - (unsigned long) *unused, - (unsigned long) info->prop_id, info->name); - err = drmModeAtomicAddProperty(req, *unused, - info->prop_id, 0); - if (err <= 0) - ret = -1; - - info = &infos[WDRM_CRTC_MODE_ID]; - drm_debug(b, "\t\t\t[CRTC:%lu] %lu (%s) -> 0\n", - (unsigned long) *unused, - (unsigned long) info->prop_id, info->name); - err = drmModeAtomicAddProperty(req, *unused, - info->prop_id, 0); - if (err <= 0) - ret = -1; - - drm_property_info_free(infos, WDRM_CRTC__COUNT); - } - - /* Disable all the planes; planes which are being used will - * override this state in the output-state application. */ - wl_list_for_each(plane, &b->plane_list, link) { - drm_debug(b, "\t\t[atomic] starting with plane %lu disabled\n", - (unsigned long) plane->plane_id); - plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID, 0); - plane_add_prop(req, plane, WDRM_PLANE_FB_ID, 0); - } - - flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; - } - - wl_list_for_each(output_state, &pending_state->output_list, link) { - if (output_state->output->virtual) - continue; - if (mode == DRM_STATE_APPLY_SYNC) - assert(output_state->dpms == WESTON_DPMS_OFF); - ret |= drm_output_apply_state_atomic(output_state, req, &flags); - } - - if (ret != 0) { - weston_log("atomic: couldn't compile atomic state\n"); - goto out; - } - - ret = drmModeAtomicCommit(b->drm.fd, req, flags, b); - drm_debug(b, "[atomic] drmModeAtomicCommit\n"); - - /* Test commits do not take ownership of the state; return - * without freeing here. */ - if (mode == DRM_STATE_TEST_ONLY) { - drmModeAtomicFree(req); - return ret; - } - - if (ret != 0) { - weston_log("atomic: couldn't commit new state: %s\n", - strerror(errno)); - goto out; - } - - wl_list_for_each_safe(output_state, tmp, &pending_state->output_list, - link) - drm_output_assign_state(output_state, mode); - - b->state_invalid = false; - - assert(wl_list_empty(&pending_state->output_list)); - -out: - drmModeAtomicFree(req); - drm_pending_state_free(pending_state); - return ret; -} - -/** - * Tests a pending state, to see if the kernel will accept the update as - * constructed. - * - * Using atomic modesetting, the kernel performs the same checks as it would - * on a real commit, returning success or failure without actually modifying - * the running state. It does not return -EBUSY if there are pending updates - * in flight, so states may be tested at any point, however this means a - * state which passed testing may fail on a real commit if the timing is not - * respected (e.g. committing before the previous commit has completed). - * - * Without atomic modesetting, we have no way to check, so we optimistically - * claim it will work. - * - * Unlike drm_pending_state_apply() and drm_pending_state_apply_sync(), this - * function does _not_ take ownership of pending_state, nor does it clear - * state_invalid. - */ -int -drm_pending_state_test(struct drm_pending_state *pending_state) -{ - struct drm_backend *b = pending_state->backend; - - if (b->atomic_modeset) - return drm_pending_state_apply_atomic(pending_state, - DRM_STATE_TEST_ONLY); - - /* We have no way to test state before application on the legacy - * modesetting API, so just claim it succeeded. */ - return 0; -} - -/** - * Applies all of a pending_state asynchronously: the primary entry point for - * applying KMS state to a device. Updates the state for all outputs in the - * pending_state, as well as disabling any unclaimed outputs. - * - * Unconditionally takes ownership of pending_state, and clears state_invalid. - */ -int -drm_pending_state_apply(struct drm_pending_state *pending_state) -{ - struct drm_backend *b = pending_state->backend; - struct drm_output_state *output_state, *tmp; - uint32_t *unused; - - if (b->atomic_modeset) - return drm_pending_state_apply_atomic(pending_state, - DRM_STATE_APPLY_ASYNC); - - if (b->state_invalid) { - /* If we need to reset all our state (e.g. because we've - * just started, or just been VT-switched in), explicitly - * disable all the CRTCs we aren't using. This also disables - * all connectors on these CRTCs, so we don't need to do that - * separately with the pre-atomic API. */ - wl_array_for_each(unused, &b->unused_crtcs) - drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0, NULL, 0, - NULL); - } - - wl_list_for_each_safe(output_state, tmp, &pending_state->output_list, - link) { - struct drm_output *output = output_state->output; - int ret; - - if (output->virtual) { - drm_output_assign_state(output_state, - DRM_STATE_APPLY_ASYNC); - continue; - } - - ret = drm_output_apply_state_legacy(output_state); - if (ret != 0) { - weston_log("Couldn't apply state for output %s\n", - output->base.name); - } - } - - b->state_invalid = false; - - assert(wl_list_empty(&pending_state->output_list)); - - drm_pending_state_free(pending_state); - - return 0; -} - -/** - * The synchronous version of drm_pending_state_apply. May only be used to - * disable outputs. Does so synchronously: the request is guaranteed to have - * completed on return, and the output will not be touched afterwards. - * - * Unconditionally takes ownership of pending_state, and clears state_invalid. - */ -int -drm_pending_state_apply_sync(struct drm_pending_state *pending_state) -{ - struct drm_backend *b = pending_state->backend; - struct drm_output_state *output_state, *tmp; - uint32_t *unused; - - if (b->atomic_modeset) - return drm_pending_state_apply_atomic(pending_state, - DRM_STATE_APPLY_SYNC); - - if (b->state_invalid) { - /* If we need to reset all our state (e.g. because we've - * just started, or just been VT-switched in), explicitly - * disable all the CRTCs we aren't using. This also disables - * all connectors on these CRTCs, so we don't need to do that - * separately with the pre-atomic API. */ - wl_array_for_each(unused, &b->unused_crtcs) - drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0, NULL, 0, - NULL); - } - - wl_list_for_each_safe(output_state, tmp, &pending_state->output_list, - link) { - int ret; - - assert(output_state->dpms == WESTON_DPMS_OFF); - ret = drm_output_apply_state_legacy(output_state); - if (ret != 0) { - weston_log("Couldn't apply state for output %s\n", - output_state->output->base.name); - } - } - - b->state_invalid = false; - - assert(wl_list_empty(&pending_state->output_list)); - - drm_pending_state_free(pending_state); - - return 0; -} - -void -drm_output_update_msc(struct drm_output *output, unsigned int seq) -{ - uint64_t msc_hi = output->base.msc >> 32; - - if (seq < (output->base.msc & 0xffffffff)) - msc_hi++; - - output->base.msc = (msc_hi << 32) + seq; -} - -static void -page_flip_handler(int fd, unsigned int frame, - unsigned int sec, unsigned int usec, void *data) -{ - struct drm_output *output = data; - struct drm_backend *b = to_drm_backend(output->base.compositor); - uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC | - WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION | - WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK; - - drm_output_update_msc(output, frame); - - assert(!b->atomic_modeset); - assert(output->page_flip_pending); - output->page_flip_pending = false; - - drm_output_update_complete(output, flags, sec, usec); -} - -static void -atomic_flip_handler(int fd, unsigned int frame, unsigned int sec, - unsigned int usec, unsigned int crtc_id, void *data) -{ - struct drm_backend *b = data; - struct drm_output *output = drm_output_find_by_crtc(b, crtc_id); - uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC | - WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION | - WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK; - - /* During the initial modeset, we can disable CRTCs which we don't - * actually handle during normal operation; this will give us events - * for unknown outputs. Ignore them. */ - if (!output || !output->base.enabled) - return; - - drm_output_update_msc(output, frame); - - drm_debug(b, "[atomic][CRTC:%u] flip processing started\n", crtc_id); - assert(b->atomic_modeset); - assert(output->atomic_complete_pending); - output->atomic_complete_pending = false; - - drm_output_update_complete(output, flags, sec, usec); - drm_debug(b, "[atomic][CRTC:%u] flip processing completed\n", crtc_id); -} - -int -on_drm_input(int fd, uint32_t mask, void *data) -{ - struct drm_backend *b = data; - drmEventContext evctx; - - memset(&evctx, 0, sizeof evctx); - evctx.version = 3; - if (b->atomic_modeset) - evctx.page_flip_handler2 = atomic_flip_handler; - else - evctx.page_flip_handler = page_flip_handler; - drmHandleEvent(fd, &evctx); - - return 1; -} - -int -init_kms_caps(struct drm_backend *b) -{ - uint64_t cap; - int ret; - clockid_t clk_id; - - weston_log("using %s\n", b->drm.filename); - - ret = drmGetCap(b->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap); - if (ret == 0 && cap == 1) - clk_id = CLOCK_MONOTONIC; - else - clk_id = CLOCK_REALTIME; - - if (weston_compositor_set_presentation_clock(b->compositor, clk_id) < 0) { - weston_log("Error: failed to set presentation clock %d.\n", - clk_id); - return -1; - } - - ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap); - if (ret == 0) - b->cursor_width = cap; - else - b->cursor_width = 64; - - ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap); - if (ret == 0) - b->cursor_height = cap; - else - b->cursor_height = 64; - - if (!getenv("WESTON_DISABLE_UNIVERSAL_PLANES")) { - ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); - b->universal_planes = (ret == 0); - } - weston_log("DRM: %s universal planes\n", - b->universal_planes ? "supports" : "does not support"); - - if (b->universal_planes && !getenv("WESTON_DISABLE_ATOMIC")) { - ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap); - if (ret != 0) - cap = 0; - ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1); - b->atomic_modeset = ((ret == 0) && (cap == 1)); - } - weston_log("DRM: %s atomic modesetting\n", - b->atomic_modeset ? "supports" : "does not support"); - - ret = drmGetCap(b->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap); - if (ret == 0) - b->fb_modifiers = cap; - else - b->fb_modifiers = 0; - - /* - * KMS support for hardware planes cannot properly synchronize - * without nuclear page flip. Without nuclear/atomic, hw plane - * and cursor plane updates would either tear or cause extra - * waits for vblanks which means dropping the compositor framerate - * to a fraction. For cursors, it's not so bad, so they are - * enabled. - */ - if (!b->atomic_modeset || getenv("WESTON_FORCE_RENDERER")) - b->sprites_are_broken = true; - - ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1); - b->aspect_ratio_supported = (ret == 0); - weston_log("DRM: %s picture aspect ratio\n", - b->aspect_ratio_supported ? "supports" : "does not support"); - - return 0; -} diff --git a/libweston/backend-drm/libbacklight.c b/libweston/backend-drm/libbacklight.c deleted file mode 100644 index 4bbc6db4..00000000 --- a/libweston/backend-drm/libbacklight.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * libbacklight - userspace interface to Linux backlight control - * - * Copyright © 2012 Intel Corporation - * Copyright 2010 Red Hat <mjg@redhat.com> - * - * 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. - * - * Authors: - * Matthew Garrett <mjg@redhat.com> - * Tiago Vignatti <vignatti@freedesktop.org> - */ - -#include "config.h" - -#include "libbacklight.h" -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <linux/types.h> -#include <dirent.h> -#include <drm.h> -#include <fcntl.h> -#include <malloc.h> -#include <string.h> -#include <errno.h> - -#include "shared/string-helpers.h" - -static long backlight_get(struct backlight *backlight, char *node) -{ - char buffer[100]; - char *path; - int fd, value; - long ret; - - if (asprintf(&path, "%s/%s", backlight->path, node) < 0) - return -ENOMEM; - fd = open(path, O_RDONLY); - if (fd < 0) { - ret = -1; - goto out; - } - - ret = read(fd, &buffer, sizeof(buffer)); - if (ret < 1) { - ret = -1; - goto out; - } - - if (!safe_strtoint(buffer, &value)) { - ret = -1; - goto out; - } - - ret = value; - -out: - if (fd >= 0) - close(fd); - free(path); - return ret; -} - -long backlight_get_brightness(struct backlight *backlight) -{ - return backlight_get(backlight, "brightness"); -} - -long backlight_get_max_brightness(struct backlight *backlight) -{ - return backlight_get(backlight, "max_brightness"); -} - -long backlight_get_actual_brightness(struct backlight *backlight) -{ - return backlight_get(backlight, "actual_brightness"); -} - -long backlight_set_brightness(struct backlight *backlight, long brightness) -{ - char *path; - char *buffer = NULL; - int fd; - long ret; - - if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0) - return -ENOMEM; - - fd = open(path, O_RDWR); - if (fd < 0) { - ret = -1; - goto out; - } - - ret = read(fd, &buffer, sizeof(buffer)); - if (ret < 1) { - ret = -1; - goto out; - } - - if (asprintf(&buffer, "%ld", brightness) < 0) { - ret = -1; - goto out; - } - - ret = write(fd, buffer, strlen(buffer)); - if (ret < 0) { - ret = -1; - goto out; - } - - ret = backlight_get_brightness(backlight); - backlight->brightness = ret; -out: - free(buffer); - free(path); - if (fd >= 0) - close(fd); - return ret; -} - -void backlight_destroy(struct backlight *backlight) -{ - if (!backlight) - return; - - if (backlight->path) - free(backlight->path); - - free(backlight); -} - -struct backlight *backlight_init(struct udev_device *drm_device, - uint32_t connector_type) -{ - const char *syspath = NULL; - char *pci_name = NULL; - char *chosen_path = NULL; - char *path = NULL; - DIR *backlights = NULL; - struct dirent *entry; - enum backlight_type type = 0; - char buffer[100]; - struct backlight *backlight = NULL; - int ret; - - if (!drm_device) - return NULL; - - syspath = udev_device_get_syspath(drm_device); - if (!syspath) - return NULL; - - if (asprintf(&path, "%s/%s", syspath, "device") < 0) - return NULL; - - ret = readlink(path, buffer, sizeof(buffer) - 1); - free(path); - if (ret < 0) - return NULL; - - buffer[ret] = '\0'; - pci_name = basename(buffer); - - if (connector_type <= 0) - return NULL; - - backlights = opendir("/sys/class/backlight"); - if (!backlights) - return NULL; - - /* Find the "best" backlight for the device. Firmware - interfaces are preferred over platform interfaces are - preferred over raw interfaces. For raw interfaces we'll - check if the device ID in the form of pci match, while - for firmware interfaces we require the pci ID to - match. It's assumed that platform interfaces always match, - since we can't actually associate them with IDs. - - A further awkwardness is that, while it's theoretically - possible for an ACPI interface to include support for - changing the backlight of external devices, it's unlikely - to ever be done. It's effectively impossible for a platform - interface to do so. So if we get asked about anything that - isn't LVDS or eDP, we pretty much have to require that the - control be supplied via a raw interface */ - - while ((entry = readdir(backlights))) { - char *backlight_path; - char *parent; - enum backlight_type entry_type; - int fd; - - if (entry->d_name[0] == '.') - continue; - - if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight", - entry->d_name) < 0) - goto err; - - if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) { - free(backlight_path); - goto err; - } - - fd = open(path, O_RDONLY); - - if (fd < 0) - goto out; - - ret = read (fd, &buffer, sizeof(buffer)); - close (fd); - - if (ret < 1) - goto out; - - buffer[ret] = '\0'; - - if (!strncmp(buffer, "raw\n", sizeof(buffer))) - entry_type = BACKLIGHT_RAW; - else if (!strncmp(buffer, "platform\n", sizeof(buffer))) - entry_type = BACKLIGHT_PLATFORM; - else if (!strncmp(buffer, "firmware\n", sizeof(buffer))) - entry_type = BACKLIGHT_FIRMWARE; - else - goto out; - - if (connector_type != DRM_MODE_CONNECTOR_LVDS && - connector_type != DRM_MODE_CONNECTOR_eDP) { - /* External displays are assumed to require - gpu control at the moment */ - if (entry_type != BACKLIGHT_RAW) - goto out; - } - - free (path); - - if (asprintf(&path, "%s/%s", backlight_path, "device") < 0) - goto err; - - ret = readlink(path, buffer, sizeof(buffer) - 1); - - if (ret < 0) - goto out; - - buffer[ret] = '\0'; - - parent = basename(buffer); - - /* Perform matching for raw and firmware backlights - - platform backlights have to be assumed to match */ - if (entry_type == BACKLIGHT_RAW || - entry_type == BACKLIGHT_FIRMWARE) { - if (!(pci_name && !strcmp(pci_name, parent))) - goto out; - } - - if (entry_type < type) - goto out; - - type = entry_type; - - if (chosen_path) - free(chosen_path); - chosen_path = strdup(backlight_path); - - out: - free(backlight_path); - free(path); - } - - if (!chosen_path) - goto err; - - backlight = malloc(sizeof(struct backlight)); - - if (!backlight) - goto err; - - backlight->path = chosen_path; - backlight->type = type; - - backlight->max_brightness = backlight_get_max_brightness(backlight); - if (backlight->max_brightness < 0) - goto err; - - backlight->brightness = backlight_get_actual_brightness(backlight); - if (backlight->brightness < 0) - goto err; - - closedir(backlights); - return backlight; -err: - closedir(backlights); - free (chosen_path); - free (backlight); - return NULL; -} diff --git a/libweston/backend-drm/libbacklight.h b/libweston/backend-drm/libbacklight.h deleted file mode 100644 index 20078242..00000000 --- a/libweston/backend-drm/libbacklight.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * libbacklight - userspace interface to Linux backlight control - * - * Copyright © 2012 Intel Corporation - * Copyright 2010 Red Hat <mjg@redhat.com> - * - * 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. - * - * Authors: - * Matthew Garrett <mjg@redhat.com> - * Tiago Vignatti <vignatti@freedesktop.org> - */ -#ifndef LIBBACKLIGHT_H -#define LIBBACKLIGHT_H -#include <libudev.h> -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -enum backlight_type { - BACKLIGHT_RAW, - BACKLIGHT_PLATFORM, - BACKLIGHT_FIRMWARE -}; - -struct backlight { - char *path; - int max_brightness; - int brightness; - enum backlight_type type; -}; - -/* - * Find and set up a backlight for a valid udev connector device, i.e. one - * matching drm subsystem and with status of connected. - */ -struct backlight *backlight_init(struct udev_device *drm_device, - uint32_t connector_type); - -/* Free backlight resources */ -void backlight_destroy(struct backlight *backlight); - -/* Provide the maximum backlight value */ -long backlight_get_max_brightness(struct backlight *backlight); - -/* Provide the cached backlight value */ -long backlight_get_brightness(struct backlight *backlight); - -/* Provide the hardware backlight value */ -long backlight_get_actual_brightness(struct backlight *backlight); - -/* Set the backlight to a value between 0 and max */ -long backlight_set_brightness(struct backlight *backlight, long brightness); - -#ifdef __cplusplus -} -#endif - -#endif /* LIBBACKLIGHT_H */ diff --git a/libweston/backend-drm/meson.build b/libweston/backend-drm/meson.build deleted file mode 100644 index a7f62965..00000000 --- a/libweston/backend-drm/meson.build +++ /dev/null @@ -1,93 +0,0 @@ -if not get_option('backend-drm') - subdir_done() -endif - -lib_backlight = static_library( - 'backlight', - 'libbacklight.c', - dependencies: [ - dep_libdrm_headers, - dependency('libudev') - ], - include_directories: common_inc, - install: false -) -dep_backlight = declare_dependency( - link_with: lib_backlight, - include_directories: include_directories('.') -) - -config_h.set('BUILD_DRM_COMPOSITOR', '1') - -srcs_drm = [ - 'drm.c', - 'fb.c', - 'modes.c', - 'kms.c', - 'state-helpers.c', - 'state-propose.c', - linux_dmabuf_unstable_v1_protocol_c, - linux_dmabuf_unstable_v1_server_protocol_h, - presentation_time_server_protocol_h, -] - -deps_drm = [ - dep_libdl, - dep_libweston_private, - dep_session_helper, - dep_libdrm, - dep_libinput_backend, - dependency('libudev', version: '>= 136'), - dep_backlight -] - -if get_option('renderer-gl') - dep_gbm = dependency('gbm', required: false) - if not dep_gbm.found() - error('drm-backend with GL renderer requires gbm which was not found. Or, you can use \'-Drenderer-gl=false\'.') - endif - if dep_gbm.version().version_compare('>= 17.1') - config_h.set('HAVE_GBM_MODIFIERS', '1') - endif - if dep_gbm.version().version_compare('>= 17.2') - config_h.set('HAVE_GBM_FD_IMPORT', '1') - endif - deps_drm += dep_gbm - srcs_drm += 'drm-gbm.c' - config_h.set('BUILD_DRM_GBM', '1') -endif - -if get_option('backend-drm-screencast-vaapi') - foreach name : [ 'libva', 'libva-drm' ] - d = dependency(name, version: '>= 0.34.0', required: false) - if not d.found() - error('VA-API recorder requires @0@ >= 0.34.0 which was not found. Or, you can use \'-Dbackend-drm-screencast-vaapi=false\'.'.format(name)) - endif - deps_drm += d - endforeach - - srcs_drm += 'vaapi-recorder.c' - deps_drm += dependency('threads') - config_h.set('BUILD_VAAPI_RECORDER', '1') -endif - -if get_option('remoting') - if not get_option('renderer-gl') - warning('DRM virtual requires renderer-gl.') - endif - srcs_drm += 'drm-virtual.c' - config_h.set('BUILD_DRM_VIRTUAL', '1') -endif - -plugin_drm = shared_library( - 'drm-backend', - srcs_drm, - include_directories: common_inc, - dependencies: deps_drm, - name_prefix: '', - install: true, - install_dir: dir_module_libweston -) -env_modmap += 'drm-backend.so=@0@;'.format(plugin_drm.full_path()) - -install_headers(backend_drm_h, subdir: dir_include_libweston_install) diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c deleted file mode 100644 index 7c45e50a..00000000 --- a/libweston/backend-drm/modes.c +++ /dev/null @@ -1,783 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2017, 2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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 <xf86drm.h> -#include <xf86drmMode.h> -#include <drm_fourcc.h> - -#include "drm-internal.h" - -static const char *const aspect_ratio_as_string[] = { - [WESTON_MODE_PIC_AR_NONE] = "", - [WESTON_MODE_PIC_AR_4_3] = " 4:3", - [WESTON_MODE_PIC_AR_16_9] = " 16:9", - [WESTON_MODE_PIC_AR_64_27] = " 64:27", - [WESTON_MODE_PIC_AR_256_135] = " 256:135", -}; - -/* - * Get the aspect-ratio from drmModeModeInfo mode flags. - * - * @param drm_mode_flags- flags from drmModeModeInfo structure. - * @returns aspect-ratio as encoded in enum 'weston_mode_aspect_ratio'. - */ -static enum weston_mode_aspect_ratio -drm_to_weston_mode_aspect_ratio(uint32_t drm_mode_flags) -{ - switch (drm_mode_flags & DRM_MODE_FLAG_PIC_AR_MASK) { - case DRM_MODE_FLAG_PIC_AR_4_3: - return WESTON_MODE_PIC_AR_4_3; - case DRM_MODE_FLAG_PIC_AR_16_9: - return WESTON_MODE_PIC_AR_16_9; - case DRM_MODE_FLAG_PIC_AR_64_27: - return WESTON_MODE_PIC_AR_64_27; - case DRM_MODE_FLAG_PIC_AR_256_135: - return WESTON_MODE_PIC_AR_256_135; - case DRM_MODE_FLAG_PIC_AR_NONE: - default: - return WESTON_MODE_PIC_AR_NONE; - } -} - -static const char * -aspect_ratio_to_string(enum weston_mode_aspect_ratio ratio) -{ - if (ratio < 0 || ratio >= ARRAY_LENGTH(aspect_ratio_as_string) || - !aspect_ratio_as_string[ratio]) - return " (unknown aspect ratio)"; - - return aspect_ratio_as_string[ratio]; -} - -static int -drm_subpixel_to_wayland(int drm_value) -{ - switch (drm_value) { - default: - case DRM_MODE_SUBPIXEL_UNKNOWN: - return WL_OUTPUT_SUBPIXEL_UNKNOWN; - case DRM_MODE_SUBPIXEL_NONE: - return WL_OUTPUT_SUBPIXEL_NONE; - case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: - return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; - case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: - return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; - case DRM_MODE_SUBPIXEL_VERTICAL_RGB: - return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; - case DRM_MODE_SUBPIXEL_VERTICAL_BGR: - return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; - } -} - -int -drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode) -{ - int ret; - - if (mode->blob_id) - return 0; - - ret = drmModeCreatePropertyBlob(backend->drm.fd, - &mode->mode_info, - sizeof(mode->mode_info), - &mode->blob_id); - if (ret != 0) - weston_log("failed to create mode property blob: %s\n", - strerror(errno)); - - drm_debug(backend, "\t\t\t[atomic] created new mode blob %lu for %s\n", - (unsigned long) mode->blob_id, mode->mode_info.name); - - return ret; -} - -static bool -check_non_desktop(struct drm_head *head, drmModeObjectPropertiesPtr props) -{ - struct drm_property_info *non_desktop_info = - &head->props_conn[WDRM_CONNECTOR_NON_DESKTOP]; - - return drm_property_get_value(non_desktop_info, props, 0); -} - -static int -parse_modeline(const char *s, drmModeModeInfo *mode) -{ - char hsync[16]; - char vsync[16]; - float fclock; - - memset(mode, 0, sizeof *mode); - - mode->type = DRM_MODE_TYPE_USERDEF; - mode->hskew = 0; - mode->vscan = 0; - mode->vrefresh = 0; - mode->flags = 0; - - if (sscanf(s, "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s", - &fclock, - &mode->hdisplay, - &mode->hsync_start, - &mode->hsync_end, - &mode->htotal, - &mode->vdisplay, - &mode->vsync_start, - &mode->vsync_end, - &mode->vtotal, hsync, vsync) != 11) - return -1; - - mode->clock = fclock * 1000; - if (strcasecmp(hsync, "+hsync") == 0) - mode->flags |= DRM_MODE_FLAG_PHSYNC; - else if (strcasecmp(hsync, "-hsync") == 0) - mode->flags |= DRM_MODE_FLAG_NHSYNC; - else - return -1; - - if (strcasecmp(vsync, "+vsync") == 0) - mode->flags |= DRM_MODE_FLAG_PVSYNC; - else if (strcasecmp(vsync, "-vsync") == 0) - mode->flags |= DRM_MODE_FLAG_NVSYNC; - else - return -1; - - snprintf(mode->name, sizeof mode->name, "%dx%d@%.3f", - mode->hdisplay, mode->vdisplay, fclock); - - return 0; -} - -static void -edid_parse_string(const uint8_t *data, char text[]) -{ - int i; - int replaced = 0; - - /* this is always 12 bytes, but we can't guarantee it's null - * terminated or not junk. */ - strncpy(text, (const char *) data, 12); - - /* guarantee our new string is null-terminated */ - text[12] = '\0'; - - /* remove insane chars */ - for (i = 0; text[i] != '\0'; i++) { - if (text[i] == '\n' || - text[i] == '\r') { - text[i] = '\0'; - break; - } - } - - /* ensure string is printable */ - for (i = 0; text[i] != '\0'; i++) { - if (!isprint(text[i])) { - text[i] = '-'; - replaced++; - } - } - - /* if the string is random junk, ignore the string */ - if (replaced > 4) - text[0] = '\0'; -} - -#define EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe -#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc -#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff -#define EDID_OFFSET_DATA_BLOCKS 0x36 -#define EDID_OFFSET_LAST_BLOCK 0x6c -#define EDID_OFFSET_PNPID 0x08 -#define EDID_OFFSET_SERIAL 0x0c - -static int -edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length) -{ - int i; - uint32_t serial_number; - - /* check header */ - if (length < 128) - return -1; - if (data[0] != 0x00 || data[1] != 0xff) - return -1; - - /* decode the PNP ID from three 5 bit words packed into 2 bytes - * /--08--\/--09--\ - * 7654321076543210 - * |\---/\---/\---/ - * R C1 C2 C3 */ - edid->pnp_id[0] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1; - edid->pnp_id[1] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x3) * 8) + ((data[EDID_OFFSET_PNPID + 1] & 0xe0) / 32) - 1; - edid->pnp_id[2] = 'A' + (data[EDID_OFFSET_PNPID + 1] & 0x1f) - 1; - edid->pnp_id[3] = '\0'; - - /* maybe there isn't a ASCII serial number descriptor, so use this instead */ - serial_number = (uint32_t) data[EDID_OFFSET_SERIAL + 0]; - serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 1] * 0x100; - serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 2] * 0x10000; - serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 3] * 0x1000000; - if (serial_number > 0) - sprintf(edid->serial_number, "%lu", (unsigned long) serial_number); - - /* parse EDID data */ - for (i = EDID_OFFSET_DATA_BLOCKS; - i <= EDID_OFFSET_LAST_BLOCK; - i += 18) { - /* ignore pixel clock data */ - if (data[i] != 0) - continue; - if (data[i+2] != 0) - continue; - - /* any useful blocks? */ - if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME) { - edid_parse_string(&data[i+5], - edid->monitor_name); - } else if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) { - edid_parse_string(&data[i+5], - edid->serial_number); - } else if (data[i+3] == EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) { - edid_parse_string(&data[i+5], - edid->eisa_id); - } - } - return 0; -} - -/** Parse monitor make, model and serial from EDID - * - * \param head The head whose \c drm_edid to fill in. - * \param props The DRM connector properties to get the EDID from. - * \param[out] make The monitor make (PNP ID). - * \param[out] model The monitor model (name). - * \param[out] serial_number The monitor serial number. - * - * Each of \c *make, \c *model and \c *serial_number are set only if the - * information is found in the EDID. The pointers they are set to must not - * be free()'d explicitly, instead they get implicitly freed when the - * \c drm_head is destroyed. - */ -static void -find_and_parse_output_edid(struct drm_head *head, - drmModeObjectPropertiesPtr props, - const char **make, - const char **model, - const char **serial_number) -{ - drmModePropertyBlobPtr edid_blob = NULL; - uint32_t blob_id; - int rc; - - blob_id = - drm_property_get_value(&head->props_conn[WDRM_CONNECTOR_EDID], - props, 0); - if (!blob_id) - return; - - edid_blob = drmModeGetPropertyBlob(head->backend->drm.fd, blob_id); - if (!edid_blob) - return; - - rc = edid_parse(&head->edid, - edid_blob->data, - edid_blob->length); - if (!rc) { - if (head->edid.pnp_id[0] != '\0') - *make = head->edid.pnp_id; - if (head->edid.monitor_name[0] != '\0') - *model = head->edid.monitor_name; - if (head->edid.serial_number[0] != '\0') - *serial_number = head->edid.serial_number; - } - drmModeFreePropertyBlob(edid_blob); -} - -static uint32_t -drm_refresh_rate_mHz(const drmModeModeInfo *info) -{ - uint64_t refresh; - - /* Calculate higher precision (mHz) refresh rate */ - refresh = (info->clock * 1000000LL / info->htotal + - info->vtotal / 2) / info->vtotal; - - if (info->flags & DRM_MODE_FLAG_INTERLACE) - refresh *= 2; - if (info->flags & DRM_MODE_FLAG_DBLSCAN) - refresh /= 2; - if (info->vscan > 1) - refresh /= info->vscan; - - return refresh; -} - -/** - * Add a mode to output's mode list - * - * Copy the supplied DRM mode into a Weston mode structure, and add it to the - * output's mode list. - * - * @param output DRM output to add mode to - * @param info DRM mode structure to add - * @returns Newly-allocated Weston/DRM mode structure - */ -static struct drm_mode * -drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) -{ - struct drm_mode *mode; - - mode = malloc(sizeof *mode); - if (mode == NULL) - return NULL; - - mode->base.flags = 0; - mode->base.width = info->hdisplay; - mode->base.height = info->vdisplay; - - mode->base.refresh = drm_refresh_rate_mHz(info); - mode->mode_info = *info; - mode->blob_id = 0; - - if (info->type & DRM_MODE_TYPE_PREFERRED) - mode->base.flags |= WL_OUTPUT_MODE_PREFERRED; - - mode->base.aspect_ratio = drm_to_weston_mode_aspect_ratio(info->flags); - - wl_list_insert(output->base.mode_list.prev, &mode->base.link); - - return mode; -} - -/** - * Destroys a mode, and removes it from the list. - */ -static void -drm_output_destroy_mode(struct drm_backend *backend, struct drm_mode *mode) -{ - if (mode->blob_id) - drmModeDestroyPropertyBlob(backend->drm.fd, mode->blob_id); - wl_list_remove(&mode->base.link); - free(mode); -} - -/** Destroy a list of drm_modes - * - * @param backend The backend for releasing mode property blobs. - * @param mode_list The list linked by drm_mode::base.link. - */ -void -drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list) -{ - struct drm_mode *mode, *next; - - wl_list_for_each_safe(mode, next, mode_list, base.link) - drm_output_destroy_mode(backend, mode); -} - -void -drm_output_print_modes(struct drm_output *output) -{ - struct weston_mode *m; - struct drm_mode *dm; - const char *aspect_ratio; - - wl_list_for_each(m, &output->base.mode_list, link) { - dm = to_drm_mode(m); - - aspect_ratio = aspect_ratio_to_string(m->aspect_ratio); - weston_log_continue(STAMP_SPACE "%dx%d@%.1f%s%s%s, %.1f MHz\n", - m->width, m->height, m->refresh / 1000.0, - aspect_ratio, - m->flags & WL_OUTPUT_MODE_PREFERRED ? - ", preferred" : "", - m->flags & WL_OUTPUT_MODE_CURRENT ? - ", current" : "", - dm->mode_info.clock / 1000.0); - } -} - - -/** - * Find the closest-matching mode for a given target - * - * Given a target mode, find the most suitable mode amongst the output's - * current mode list to use, preferring the current mode if possible, to - * avoid an expensive mode switch. - * - * @param output DRM output - * @param target_mode Mode to attempt to match - * @returns Pointer to a mode from the output's mode list - */ -struct drm_mode * -drm_output_choose_mode(struct drm_output *output, - struct weston_mode *target_mode) -{ - struct drm_mode *tmp_mode = NULL, *mode_fall_back = NULL, *mode; - enum weston_mode_aspect_ratio src_aspect = WESTON_MODE_PIC_AR_NONE; - enum weston_mode_aspect_ratio target_aspect = WESTON_MODE_PIC_AR_NONE; - struct drm_backend *b; - - b = to_drm_backend(output->base.compositor); - target_aspect = target_mode->aspect_ratio; - src_aspect = output->base.current_mode->aspect_ratio; - if (output->base.current_mode->width == target_mode->width && - output->base.current_mode->height == target_mode->height && - (output->base.current_mode->refresh == target_mode->refresh || - target_mode->refresh == 0)) { - if (!b->aspect_ratio_supported || src_aspect == target_aspect) - return to_drm_mode(output->base.current_mode); - } - - wl_list_for_each(mode, &output->base.mode_list, base.link) { - - src_aspect = mode->base.aspect_ratio; - if (mode->mode_info.hdisplay == target_mode->width && - mode->mode_info.vdisplay == target_mode->height) { - if (mode->base.refresh == target_mode->refresh || - target_mode->refresh == 0) { - if (!b->aspect_ratio_supported || - src_aspect == target_aspect) - return mode; - else if (!mode_fall_back) - mode_fall_back = mode; - } else if (!tmp_mode) { - tmp_mode = mode; - } - } - } - - if (mode_fall_back) - return mode_fall_back; - - return tmp_mode; -} - -void -update_head_from_connector(struct drm_head *head, - drmModeObjectProperties *props) -{ - const char *make = "unknown"; - const char *model = "unknown"; - const char *serial_number = "unknown"; - - find_and_parse_output_edid(head, props, &make, &model, &serial_number); - weston_head_set_monitor_strings(&head->base, make, model, serial_number); - weston_head_set_non_desktop(&head->base, - check_non_desktop(head, props)); - weston_head_set_subpixel(&head->base, - drm_subpixel_to_wayland(head->connector->subpixel)); - - weston_head_set_physical_size(&head->base, head->connector->mmWidth, - head->connector->mmHeight); - - /* Unknown connection status is assumed disconnected. */ - weston_head_set_connection_status(&head->base, - head->connector->connection == DRM_MODE_CONNECTED); -} - -/** - * Choose suitable mode for an output - * - * Find the most suitable mode to use for initial setup (or reconfiguration on - * hotplug etc) for a DRM output. - * - * @param backend the DRM backend - * @param output DRM output to choose mode for - * @param mode Strategy and preference to use when choosing mode - * @param modeline Manually-entered mode (may be NULL) - * @param current_mode Mode currently being displayed on this output - * @returns A mode from the output's mode list, or NULL if none available - */ -static struct drm_mode * -drm_output_choose_initial_mode(struct drm_backend *backend, - struct drm_output *output, - enum weston_drm_backend_output_mode mode, - const char *modeline, - const drmModeModeInfo *current_mode) -{ - struct drm_mode *preferred = NULL; - struct drm_mode *current = NULL; - struct drm_mode *configured = NULL; - struct drm_mode *config_fall_back = NULL; - struct drm_mode *best = NULL; - struct drm_mode *drm_mode; - drmModeModeInfo drm_modeline; - int32_t width = 0; - int32_t height = 0; - uint32_t refresh = 0; - uint32_t aspect_width = 0; - uint32_t aspect_height = 0; - enum weston_mode_aspect_ratio aspect_ratio = WESTON_MODE_PIC_AR_NONE; - int n; - - if (mode == WESTON_DRM_BACKEND_OUTPUT_PREFERRED && modeline) { - n = sscanf(modeline, "%dx%d@%d %u:%u", &width, &height, - &refresh, &aspect_width, &aspect_height); - if (backend->aspect_ratio_supported && n == 5) { - if (aspect_width == 4 && aspect_height == 3) - aspect_ratio = WESTON_MODE_PIC_AR_4_3; - else if (aspect_width == 16 && aspect_height == 9) - aspect_ratio = WESTON_MODE_PIC_AR_16_9; - else if (aspect_width == 64 && aspect_height == 27) - aspect_ratio = WESTON_MODE_PIC_AR_64_27; - else if (aspect_width == 256 && aspect_height == 135) - aspect_ratio = WESTON_MODE_PIC_AR_256_135; - else - weston_log("Invalid modeline \"%s\" for output %s\n", - modeline, output->base.name); - } - if (n != 2 && n != 3 && n != 5) { - width = -1; - - if (parse_modeline(modeline, &drm_modeline) == 0) { - configured = drm_output_add_mode(output, &drm_modeline); - if (!configured) - return NULL; - } else { - weston_log("Invalid modeline \"%s\" for output %s\n", - modeline, output->base.name); - } - } - } - - wl_list_for_each_reverse(drm_mode, &output->base.mode_list, base.link) { - if (width == drm_mode->base.width && - height == drm_mode->base.height && - (refresh == 0 || refresh == drm_mode->mode_info.vrefresh)) { - if (!backend->aspect_ratio_supported || - aspect_ratio == drm_mode->base.aspect_ratio) - configured = drm_mode; - else - config_fall_back = drm_mode; - } - - if (memcmp(current_mode, &drm_mode->mode_info, - sizeof *current_mode) == 0) - current = drm_mode; - - if (drm_mode->base.flags & WL_OUTPUT_MODE_PREFERRED) - preferred = drm_mode; - - best = drm_mode; - } - - if (current == NULL && current_mode->clock != 0) { - current = drm_output_add_mode(output, current_mode); - if (!current) - return NULL; - } - - if (mode == WESTON_DRM_BACKEND_OUTPUT_CURRENT) - configured = current; - - if (configured) - return configured; - - if (config_fall_back) - return config_fall_back; - - if (preferred) - return preferred; - - if (current) - return current; - - if (best) - return best; - - weston_log("no available modes for %s\n", output->base.name); - return NULL; -} - -static uint32_t -u32distance(uint32_t a, uint32_t b) -{ - if (a < b) - return b - a; - else - return a - b; -} - -/** Choose equivalent mode - * - * If the two modes are not equivalent, return NULL. - * Otherwise return the mode that is more likely to work in place of both. - * - * None of the fuzzy matching criteria in this function have any justification. - * - * typedef struct _drmModeModeInfo { - * uint32_t clock; - * uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; - * uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; - * - * uint32_t vrefresh; - * - * uint32_t flags; - * uint32_t type; - * char name[DRM_DISPLAY_MODE_LEN]; - * } drmModeModeInfo, *drmModeModeInfoPtr; - */ -static const drmModeModeInfo * -drm_mode_pick_equivalent(const drmModeModeInfo *a, const drmModeModeInfo *b) -{ - uint32_t refresh_a, refresh_b; - - if (a->hdisplay != b->hdisplay || a->vdisplay != b->vdisplay) - return NULL; - - if (a->flags != b->flags) - return NULL; - - /* kHz */ - if (u32distance(a->clock, b->clock) > 500) - return NULL; - - refresh_a = drm_refresh_rate_mHz(a); - refresh_b = drm_refresh_rate_mHz(b); - if (u32distance(refresh_a, refresh_b) > 50) - return NULL; - - if ((a->type ^ b->type) & DRM_MODE_TYPE_PREFERRED) { - if (a->type & DRM_MODE_TYPE_PREFERRED) - return a; - else - return b; - } - - return a; -} - -/* If the given mode info is not already in the list, add it. - * If it is in the list, either keep the existing or replace it, - * depending on which one is "better". - */ -static int -drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info) -{ - struct weston_mode *base; - struct drm_mode *mode; - struct drm_backend *backend; - const drmModeModeInfo *chosen = NULL; - - assert(info); - - wl_list_for_each(base, &output->base.mode_list, link) { - mode = to_drm_mode(base); - chosen = drm_mode_pick_equivalent(&mode->mode_info, info); - if (chosen) - break; - } - - if (chosen == info) { - backend = to_drm_backend(output->base.compositor); - drm_output_destroy_mode(backend, mode); - chosen = NULL; - } - - if (!chosen) { - mode = drm_output_add_mode(output, info); - if (!mode) - return -1; - } - /* else { the equivalent mode is already in the list } */ - - return 0; -} - -/** Rewrite the output's mode list - * - * @param output The output. - * @return 0 on success, -1 on failure. - * - * Destroy all existing modes in the list, and reconstruct a new list from - * scratch, based on the currently attached heads. - * - * On failure the output's mode list may contain some modes. - */ -static int -drm_output_update_modelist_from_heads(struct drm_output *output) -{ - struct drm_backend *backend = to_drm_backend(output->base.compositor); - struct weston_head *head_base; - struct drm_head *head; - int i; - int ret; - - assert(!output->base.enabled); - - drm_mode_list_destroy(backend, &output->base.mode_list); - - wl_list_for_each(head_base, &output->base.head_list, output_link) { - head = to_drm_head(head_base); - for (i = 0; i < head->connector->count_modes; i++) { - ret = drm_output_try_add_mode(output, - &head->connector->modes[i]); - if (ret < 0) - return -1; - } - } - - return 0; -} - -int -drm_output_set_mode(struct weston_output *base, - enum weston_drm_backend_output_mode mode, - const char *modeline) -{ - struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); - struct drm_head *head = to_drm_head(weston_output_get_first_head(base)); - - struct drm_mode *current; - - if (output->virtual) - return -1; - - if (drm_output_update_modelist_from_heads(output) < 0) - return -1; - - current = drm_output_choose_initial_mode(b, output, mode, modeline, - &head->inherited_mode); - if (!current) - return -1; - - output->base.current_mode = ¤t->base; - output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT; - - /* Set native_ fields, so weston_output_mode_switch_to_native() works */ - output->base.native_mode = output->base.current_mode; - output->base.native_scale = output->base.current_scale; - - return 0; -} diff --git a/libweston/backend-drm/state-helpers.c b/libweston/backend-drm/state-helpers.c deleted file mode 100644 index 7b1d9241..00000000 --- a/libweston/backend-drm/state-helpers.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2017, 2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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 <xf86drm.h> -#include <xf86drmMode.h> -#include <drm_fourcc.h> - -#include "drm-internal.h" - -/** - * Allocate a new, empty, plane state. - */ -struct drm_plane_state * -drm_plane_state_alloc(struct drm_output_state *state_output, - struct drm_plane *plane) -{ - struct drm_plane_state *state = zalloc(sizeof(*state)); - - assert(state); - state->output_state = state_output; - state->plane = plane; - state->in_fence_fd = -1; - state->zpos = DRM_PLANE_ZPOS_INVALID_PLANE; - pixman_region32_init(&state->damage); - - /* Here we only add the plane state to the desired link, and not - * set the member. Having an output pointer set means that the - * plane will be displayed on the output; this won't be the case - * when we go to disable a plane. In this case, it must be part of - * the commit (and thus the output state), but the member must be - * NULL, as it will not be on any output when the state takes - * effect. - */ - if (state_output) - wl_list_insert(&state_output->plane_list, &state->link); - else - wl_list_init(&state->link); - - return state; -} - -/** - * Free an existing plane state. As a special case, the state will not - * normally be freed if it is the current state; see drm_plane_set_state. - */ -void -drm_plane_state_free(struct drm_plane_state *state, bool force) -{ - if (!state) - return; - - wl_list_remove(&state->link); - wl_list_init(&state->link); - state->output_state = NULL; - state->in_fence_fd = -1; - state->zpos = DRM_PLANE_ZPOS_INVALID_PLANE; - pixman_region32_fini(&state->damage); - - if (force || state != state->plane->state_cur) { - drm_fb_unref(state->fb); - free(state); - } -} - -/** - * Duplicate an existing plane state into a new plane state, storing it within - * the given output state. If the output state already contains a plane state - * for the drm_plane referenced by 'src', that plane state is freed first. - */ -struct drm_plane_state * -drm_plane_state_duplicate(struct drm_output_state *state_output, - struct drm_plane_state *src) -{ - struct drm_plane_state *dst = malloc(sizeof(*dst)); - struct drm_plane_state *old, *tmp; - - assert(src); - assert(dst); - *dst = *src; - wl_list_init(&dst->link); - - wl_list_for_each_safe(old, tmp, &state_output->plane_list, link) { - /* Duplicating a plane state into the same output state, so - * it can replace itself with an identical copy of itself, - * makes no sense. */ - assert(old != src); - if (old->plane == dst->plane) - drm_plane_state_free(old, false); - } - - wl_list_insert(&state_output->plane_list, &dst->link); - if (src->fb) - dst->fb = drm_fb_ref(src->fb); - dst->output_state = state_output; - pixman_region32_init(&dst->damage); - dst->complete = false; - - return dst; -} - -/** - * Remove a plane state from an output state; if the plane was previously - * enabled, then replace it with a disabling state. This ensures that the - * output state was untouched from it was before the plane state was - * modified by the caller of this function. - * - * This is required as drm_output_state_get_plane may either allocate a - * new plane state, in which case this function will just perform a matching - * drm_plane_state_free, or it may instead repurpose an existing disabling - * state (if the plane was previously active), in which case this function - * will reset it. - */ -void -drm_plane_state_put_back(struct drm_plane_state *state) -{ - struct drm_output_state *state_output; - struct drm_plane *plane; - - if (!state) - return; - - state_output = state->output_state; - plane = state->plane; - drm_plane_state_free(state, false); - - /* Plane was previously disabled; no need to keep this temporary - * state around. */ - if (!plane->state_cur->fb) - return; - - (void) drm_plane_state_alloc(state_output, plane); -} - -/** - * Given a weston_view, fill the drm_plane_state's co-ordinates to display on - * a given plane. - */ -bool -drm_plane_state_coords_for_view(struct drm_plane_state *state, - struct weston_view *ev, uint64_t zpos) -{ - struct drm_output *output = state->output; - struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; - pixman_region32_t dest_rect, src_rect; - pixman_box32_t *box, tbox; - float sxf1, syf1, sxf2, syf2; - - if (!drm_view_transform_supported(ev, &output->base)) - return false; - - /* Update the base weston_plane co-ordinates. */ - box = pixman_region32_extents(&ev->transform.boundingbox); - state->plane->base.x = box->x1; - state->plane->base.y = box->y1; - - /* First calculate the destination co-ordinates by taking the - * area of the view which is visible on this output, performing any - * transforms to account for output rotation and scale as necessary. */ - pixman_region32_init(&dest_rect); - pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox, - &output->base.region); - pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y); - box = pixman_region32_extents(&dest_rect); - tbox = weston_transformed_rect(output->base.width, - output->base.height, - output->base.transform, - output->base.current_scale, - *box); - state->dest_x = tbox.x1; - state->dest_y = tbox.y1; - state->dest_w = tbox.x2 - tbox.x1; - state->dest_h = tbox.y2 - tbox.y1; - pixman_region32_fini(&dest_rect); - - /* Now calculate the source rectangle, by finding the extents of the - * view, and working backwards to source co-ordinates. */ - pixman_region32_init(&src_rect); - pixman_region32_intersect(&src_rect, &ev->transform.boundingbox, - &output->base.region); - box = pixman_region32_extents(&src_rect); - weston_view_from_global_float(ev, box->x1, box->y1, &sxf1, &syf1); - weston_surface_to_buffer_float(ev->surface, sxf1, syf1, &sxf1, &syf1); - weston_view_from_global_float(ev, box->x2, box->y2, &sxf2, &syf2); - weston_surface_to_buffer_float(ev->surface, sxf2, syf2, &sxf2, &syf2); - pixman_region32_fini(&src_rect); - - /* Buffer transforms may mean that x2 is to the left of x1, and/or that - * y2 is above y1. */ - if (sxf2 < sxf1) { - double tmp = sxf1; - sxf1 = sxf2; - sxf2 = tmp; - } - if (syf2 < syf1) { - double tmp = syf1; - syf1 = syf2; - syf2 = tmp; - } - - /* Shift from S23.8 wl_fixed to U16.16 KMS fixed-point encoding. */ - state->src_x = wl_fixed_from_double(sxf1) << 8; - state->src_y = wl_fixed_from_double(syf1) << 8; - state->src_w = wl_fixed_from_double(sxf2 - sxf1) << 8; - state->src_h = wl_fixed_from_double(syf2 - syf1) << 8; - - /* Clamp our source co-ordinates to surface bounds; it's possible - * for intermediate translations to give us slightly incorrect - * co-ordinates if we have, for example, multiple zooming - * transformations. View bounding boxes are also explicitly rounded - * greedily. */ - if (state->src_x < 0) - state->src_x = 0; - if (state->src_y < 0) - state->src_y = 0; - if (state->src_w > (uint32_t) ((buffer->width << 16) - state->src_x)) - state->src_w = (buffer->width << 16) - state->src_x; - if (state->src_h > (uint32_t) ((buffer->height << 16) - state->src_y)) - state->src_h = (buffer->height << 16) - state->src_y; - - /* apply zpos if available */ - state->zpos = zpos; - - return true; -} - -/** - * Return a plane state from a drm_output_state. - */ -struct drm_plane_state * -drm_output_state_get_existing_plane(struct drm_output_state *state_output, - struct drm_plane *plane) -{ - struct drm_plane_state *ps; - - wl_list_for_each(ps, &state_output->plane_list, link) { - if (ps->plane == plane) - return ps; - } - - return NULL; -} - -/** - * Return a plane state from a drm_output_state, either existing or - * freshly allocated. - */ -struct drm_plane_state * -drm_output_state_get_plane(struct drm_output_state *state_output, - struct drm_plane *plane) -{ - struct drm_plane_state *ps; - - ps = drm_output_state_get_existing_plane(state_output, plane); - if (ps) - return ps; - - return drm_plane_state_alloc(state_output, plane); -} - -/** - * Allocate a new, empty drm_output_state. This should not generally be used - * in the repaint cycle; see drm_output_state_duplicate. - */ -struct drm_output_state * -drm_output_state_alloc(struct drm_output *output, - struct drm_pending_state *pending_state) -{ - struct drm_output_state *state = zalloc(sizeof(*state)); - - assert(state); - state->output = output; - state->dpms = WESTON_DPMS_OFF; - state->protection = WESTON_HDCP_DISABLE; - state->pending_state = pending_state; - if (pending_state) - wl_list_insert(&pending_state->output_list, &state->link); - else - wl_list_init(&state->link); - - wl_list_init(&state->plane_list); - - return state; -} - -/** - * Duplicate an existing drm_output_state into a new one. This is generally - * used during the repaint cycle, to capture the existing state of an output - * and modify it to create a new state to be used. - * - * The mode determines whether the output will be reset to an a blank state, - * or an exact mirror of the current state. - */ -struct drm_output_state * -drm_output_state_duplicate(struct drm_output_state *src, - struct drm_pending_state *pending_state, - enum drm_output_state_duplicate_mode plane_mode) -{ - struct drm_output_state *dst = malloc(sizeof(*dst)); - struct drm_plane_state *ps; - - assert(dst); - - /* Copy the whole structure, then individually modify the - * pending_state, as well as the list link into our pending - * state. */ - *dst = *src; - - dst->pending_state = pending_state; - if (pending_state) - wl_list_insert(&pending_state->output_list, &dst->link); - else - wl_list_init(&dst->link); - - wl_list_init(&dst->plane_list); - - wl_list_for_each(ps, &src->plane_list, link) { - /* Don't carry planes which are now disabled; these should be - * free for other outputs to reuse. */ - if (!ps->output) - continue; - - if (plane_mode == DRM_OUTPUT_STATE_CLEAR_PLANES) - (void) drm_plane_state_alloc(dst, ps->plane); - else - (void) drm_plane_state_duplicate(dst, ps); - } - - return dst; -} - -/** - * Free an unused drm_output_state. - */ -void -drm_output_state_free(struct drm_output_state *state) -{ - struct drm_plane_state *ps, *next; - - if (!state) - return; - - wl_list_for_each_safe(ps, next, &state->plane_list, link) - drm_plane_state_free(ps, false); - - wl_list_remove(&state->link); - - free(state); -} - -/** - * Allocate a new drm_pending_state - * - * Allocate a new, empty, 'pending state' structure to be used across a - * repaint cycle or similar. - * - * @param backend DRM backend - * @returns Newly-allocated pending state structure - */ -struct drm_pending_state * -drm_pending_state_alloc(struct drm_backend *backend) -{ - struct drm_pending_state *ret; - - ret = calloc(1, sizeof(*ret)); - if (!ret) - return NULL; - - ret->backend = backend; - wl_list_init(&ret->output_list); - - return ret; -} - -/** - * Free a drm_pending_state structure - * - * Frees a pending_state structure, as well as any output_states connected - * to this pending state. - * - * @param pending_state Pending state structure to free - */ -void -drm_pending_state_free(struct drm_pending_state *pending_state) -{ - struct drm_output_state *output_state, *tmp; - - if (!pending_state) - return; - - wl_list_for_each_safe(output_state, tmp, &pending_state->output_list, - link) { - drm_output_state_free(output_state); - } - - free(pending_state); -} - -/** - * Find an output state in a pending state - * - * Given a pending_state structure, find the output_state for a particular - * output. - * - * @param pending_state Pending state structure to search - * @param output Output to find state for - * @returns Output state if present, or NULL if not - */ -struct drm_output_state * -drm_pending_state_get_output(struct drm_pending_state *pending_state, - struct drm_output *output) -{ - struct drm_output_state *output_state; - - wl_list_for_each(output_state, &pending_state->output_list, link) { - if (output_state->output == output) - return output_state; - } - - return NULL; -} diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c deleted file mode 100644 index 767c34f4..00000000 --- a/libweston/backend-drm/state-propose.c +++ /dev/null @@ -1,1149 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2017, 2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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 <xf86drm.h> -#include <xf86drmMode.h> - -#include <libweston/libweston.h> -#include <libweston/backend-drm.h> -#include <libweston/pixel-formats.h> - -#include "drm-internal.h" - -#include "linux-dmabuf.h" -#include "presentation-time-server-protocol.h" - -enum drm_output_propose_state_mode { - DRM_OUTPUT_PROPOSE_STATE_MIXED, /**< mix renderer & planes */ - DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY, /**< only assign to renderer & cursor */ - DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY, /**< no renderer use, only planes */ -}; - -static const char *const drm_output_propose_state_mode_as_string[] = { - [DRM_OUTPUT_PROPOSE_STATE_MIXED] = "mixed state", - [DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY] = "render-only state", - [DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY] = "plane-only state" -}; - -static const char * -drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode) -{ - if (mode < 0 || mode >= ARRAY_LENGTH(drm_output_propose_state_mode_as_string)) - return " unknown compositing mode"; - - return drm_output_propose_state_mode_as_string[mode]; -} - -static void -drm_output_add_zpos_plane(struct drm_plane *plane, struct wl_list *planes) -{ - struct drm_backend *b = plane->backend; - struct drm_plane_zpos *h_plane; - struct drm_plane_zpos *plane_zpos; - - plane_zpos = zalloc(sizeof(*plane_zpos)); - if (!plane_zpos) - return; - - plane_zpos->plane = plane; - - drm_debug(b, "\t\t\t\t[plane] plane %d added to candidate list\n", - plane->plane_id); - - if (wl_list_empty(planes)) { - wl_list_insert(planes, &plane_zpos->link); - return; - } - - h_plane = wl_container_of(planes->next, h_plane, link); - if (h_plane->plane->zpos_max >= plane->zpos_max) { - wl_list_insert(planes->prev, &plane_zpos->link); - } else { - struct drm_plane_zpos *p_zpos = NULL; - - if (wl_list_length(planes) == 1) { - wl_list_insert(planes->prev, &plane_zpos->link); - return; - } - - wl_list_for_each(p_zpos, planes, link) { - if (p_zpos->plane->zpos_max > - plane_zpos->plane->zpos_max) - break; - } - - wl_list_insert(p_zpos->link.prev, &plane_zpos->link); - } -} - -static void -drm_output_destroy_zpos_plane(struct drm_plane_zpos *plane_zpos) -{ - wl_list_remove(&plane_zpos->link); - free(plane_zpos); -} - -static bool -drm_output_check_plane_has_view_assigned(struct drm_plane *plane, - struct drm_output_state *output_state) -{ - struct drm_plane_state *ps; - wl_list_for_each(ps, &output_state->plane_list, link) { - if (ps->plane == plane && ps->fb) - return true; - } - return false; -} - -static bool -drm_output_plane_has_valid_format(struct drm_plane *plane, - struct drm_output_state *state, - struct drm_fb *fb) -{ - struct drm_backend *b = plane->backend; - unsigned int i; - - if (!fb) - return false; - - /* Check whether the format is supported */ - for (i = 0; i < plane->count_formats; i++) { - unsigned int j; - - if (plane->formats[i].format != fb->format->format) - continue; - - if (fb->modifier == DRM_FORMAT_MOD_INVALID) - return true; - - for (j = 0; j < plane->formats[i].count_modifiers; j++) { - if (plane->formats[i].modifiers[j] == fb->modifier) - return true; - } - } - - drm_debug(b, "\t\t\t\t[%s] not placing view on %s: " - "no free %s planes matching format %s (0x%lx) " - "modifier 0x%llx\n", - drm_output_get_plane_type_name(plane), - drm_output_get_plane_type_name(plane), - drm_output_get_plane_type_name(plane), - fb->format->drm_format_name, - (unsigned long) fb->format, - (unsigned long long) fb->modifier); - - return false; -} - -static bool -drm_output_plane_cursor_has_valid_format(struct weston_view *ev) -{ - struct wl_shm_buffer *shmbuf = - wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource); - - if (shmbuf && wl_shm_buffer_get_format(shmbuf) == WL_SHM_FORMAT_ARGB8888) - return true; - - return false; -} - -static struct drm_plane_state * -drm_output_prepare_overlay_view(struct drm_plane *plane, - struct drm_output_state *output_state, - struct weston_view *ev, - enum drm_output_propose_state_mode mode, - struct drm_fb *fb, uint64_t zpos) -{ - struct drm_output *output = output_state->output; - struct weston_compositor *ec = output->base.compositor; - struct drm_backend *b = to_drm_backend(ec); - struct drm_plane_state *state = NULL; - int ret; - - assert(!b->sprites_are_broken); - assert(b->atomic_modeset); - - if (!fb) { - drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: " - " couldn't get fb\n", ev); - return NULL; - } - - state = drm_output_state_get_plane(output_state, plane); - /* we can't have a 'pending' framebuffer as never set one before reaching here */ - assert(!state->fb); - - state->ev = ev; - state->output = output; - - if (!drm_plane_state_coords_for_view(state, ev, zpos)) { - drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: " - "unsuitable transform\n", ev); - drm_plane_state_put_back(state); - state = NULL; - goto out; - } - - /* If the surface buffer has an in-fence fd, but the plane - * doesn't support fences, we can't place the buffer on this - * plane. */ - if (ev->surface->acquire_fence_fd >= 0 && - plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) { - drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: " - "no in-fence support\n", ev); - drm_plane_state_put_back(state); - state = NULL; - goto out; - } - - /* We hold one reference for the lifetime of this function; from - * calling drm_fb_get_from_view() in drm_output_prepare_plane_view(), - * so, we take another reference here to live within the state. */ - state->fb = drm_fb_ref(fb); - - state->in_fence_fd = ev->surface->acquire_fence_fd; - - /* In planes-only mode, we don't have an incremental state to - * test against, so we just hope it'll work. */ - if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) { - drm_debug(b, "\t\t\t[overlay] provisionally placing " - "view %p on overlay %lu in planes-only mode\n", - ev, (unsigned long) plane->plane_id); - goto out; - } - - ret = drm_pending_state_test(output_state->pending_state); - if (ret == 0) { - drm_debug(b, "\t\t\t[overlay] provisionally placing " - "view %p on overlay %d in mixed mode\n", - ev, plane->plane_id); - goto out; - } - - drm_debug(b, "\t\t\t[overlay] not placing view %p on overlay %lu " - "in mixed mode: kernel test failed\n", - ev, (unsigned long) plane->plane_id); - - drm_plane_state_put_back(state); - state = NULL; - -out: - return state; -} - -#ifdef BUILD_DRM_GBM -/** - * Update the image for the current cursor surface - * - * @param plane_state DRM cursor plane state - * @param ev Source view for cursor - */ -static void -cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev) -{ - struct drm_backend *b = plane_state->plane->backend; - struct gbm_bo *bo = plane_state->fb->bo; - struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; - uint32_t buf[b->cursor_width * b->cursor_height]; - int32_t stride; - uint8_t *s; - int i; - - assert(buffer && buffer->shm_buffer); - assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource)); - assert(buffer->width <= b->cursor_width); - assert(buffer->height <= b->cursor_height); - - memset(buf, 0, sizeof buf); - stride = wl_shm_buffer_get_stride(buffer->shm_buffer); - s = wl_shm_buffer_get_data(buffer->shm_buffer); - - wl_shm_buffer_begin_access(buffer->shm_buffer); - for (i = 0; i < buffer->height; i++) - memcpy(buf + i * b->cursor_width, - s + i * stride, - buffer->width * 4); - wl_shm_buffer_end_access(buffer->shm_buffer); - - if (gbm_bo_write(bo, buf, sizeof buf) < 0) - weston_log("failed update cursor: %s\n", strerror(errno)); -} - -static struct drm_plane_state * -drm_output_prepare_cursor_view(struct drm_output_state *output_state, - struct weston_view *ev, uint64_t zpos) -{ - struct drm_output *output = output_state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_plane *plane = output->cursor_plane; - struct drm_plane_state *plane_state; - bool needs_update = false; - const char *p_name = drm_output_get_plane_type_name(plane); - - assert(!b->cursors_are_broken); - - if (!plane) - return NULL; - - if (!plane->state_cur->complete) - return NULL; - - if (plane->state_cur->output && plane->state_cur->output != output) - return NULL; - - /* We use GBM to import SHM buffers. */ - if (b->gbm == NULL) - return NULL; - - plane_state = - drm_output_state_get_plane(output_state, output->cursor_plane); - - if (plane_state && plane_state->fb) - return NULL; - - /* We can't scale with the legacy API, and we don't try to account for - * simple cropping/translation in cursor_bo_update. */ - plane_state->output = output; - if (!drm_plane_state_coords_for_view(plane_state, ev, zpos)) { - drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " - "unsuitable transform\n", p_name, ev, p_name); - goto err; - } - - if (plane_state->src_x != 0 || plane_state->src_y != 0 || - plane_state->src_w > (unsigned) b->cursor_width << 16 || - plane_state->src_h > (unsigned) b->cursor_height << 16 || - plane_state->src_w != plane_state->dest_w << 16 || - plane_state->src_h != plane_state->dest_h << 16) { - drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane " - "(positioning requires cropping or scaling)\n", - p_name, ev, p_name); - goto err; - } - - /* Since we're setting plane state up front, we need to work out - * whether or not we need to upload a new cursor. We can't use the - * plane damage, since the planes haven't actually been calculated - * yet: instead try to figure it out directly. KMS cursor planes are - * pretty unique here, in that they lie partway between a Weston plane - * (direct scanout) and a renderer. */ - if (ev != output->cursor_view || - pixman_region32_not_empty(&ev->surface->damage)) { - output->current_cursor++; - output->current_cursor = - output->current_cursor % - ARRAY_LENGTH(output->gbm_cursor_fb); - needs_update = true; - } - - output->cursor_view = ev; - plane_state->ev = ev; - - plane_state->fb = - drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]); - - if (needs_update) { - drm_debug(b, "\t\t\t\t[%s] copying new content to cursor BO\n", p_name); - cursor_bo_update(plane_state, ev); - } - - /* The cursor API is somewhat special: in cursor_bo_update(), we upload - * a buffer which is always cursor_width x cursor_height, even if the - * surface we want to promote is actually smaller than this. Manually - * mangle the plane state to deal with this. */ - plane_state->src_w = b->cursor_width << 16; - plane_state->src_h = b->cursor_height << 16; - plane_state->dest_w = b->cursor_width; - plane_state->dest_h = b->cursor_height; - - drm_debug(b, "\t\t\t\t[%s] provisionally assigned view %p to cursor\n", - p_name, ev); - - return plane_state; - -err: - drm_plane_state_put_back(plane_state); - return NULL; -} -#else -static struct drm_plane_state * -drm_output_prepare_cursor_view(struct drm_output_state *output_state, - struct weston_view *ev, uint64_t zpos) -{ - return NULL; -} -#endif - -static struct drm_plane_state * -drm_output_prepare_scanout_view(struct drm_output_state *output_state, - struct weston_view *ev, - enum drm_output_propose_state_mode mode, - struct drm_fb *fb, uint64_t zpos) -{ - struct drm_output *output = output_state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_plane *scanout_plane = output->scanout_plane; - struct drm_plane_state *state; - const char *p_name = drm_output_get_plane_type_name(scanout_plane); - - assert(!b->sprites_are_broken); - assert(b->atomic_modeset); - assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY); - - /* Check the view spans exactly the output size, calculated in the - * logical co-ordinate space. */ - if (!weston_view_matches_output_entirely(ev, &output->base)) { - drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " - " view does not match output entirely\n", - p_name, ev, p_name); - return NULL; - } - - /* If the surface buffer has an in-fence fd, but the plane doesn't - * support fences, we can't place the buffer on this plane. */ - if (ev->surface->acquire_fence_fd >= 0 && - scanout_plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) { - drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " - "no in-fence support\n", p_name, ev, p_name); - return NULL; - } - - if (!fb) { - drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " - " couldn't get fb\n", p_name, ev, p_name); - return NULL; - } - - state = drm_output_state_get_plane(output_state, scanout_plane); - - /* The only way we can already have a buffer in the scanout plane is - * if we are in mixed mode, or if a client buffer has already been - * placed into scanout. The former case will never call into here, - * and in the latter case, the view must have been marked as occluded, - * meaning we should never have ended up here. */ - assert(!state->fb); - - /* take another reference here to live within the state */ - state->fb = drm_fb_ref(fb); - state->ev = ev; - state->output = output; - if (!drm_plane_state_coords_for_view(state, ev, zpos)) { - drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " - "unsuitable transform\n", p_name, ev, p_name); - goto err; - } - - if (state->dest_x != 0 || state->dest_y != 0 || - state->dest_w != (unsigned) output->base.current_mode->width || - state->dest_h != (unsigned) output->base.current_mode->height) { - drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " - " invalid plane state\n", p_name, ev, p_name); - goto err; - } - - state->in_fence_fd = ev->surface->acquire_fence_fd; - - /* In plane-only mode, we don't need to test the state now, as we - * will only test it once at the end. */ - return state; - -err: - drm_plane_state_put_back(state); - return NULL; -} - -static bool -drm_output_plane_view_has_valid_format(struct drm_plane *plane, - struct drm_output_state *state, - struct weston_view *ev, - struct drm_fb *fb) -{ - /* depending on the type of the plane we have different requirements */ - switch (plane->type) { - case WDRM_PLANE_TYPE_CURSOR: - return drm_output_plane_cursor_has_valid_format(ev); - case WDRM_PLANE_TYPE_OVERLAY: - return drm_output_plane_has_valid_format(plane, state, fb); - case WDRM_PLANE_TYPE_PRIMARY: - return drm_output_plane_has_valid_format(plane, state, fb); - default: - assert(0); - return false; - } - - return false; -} - -static struct drm_plane_state * -drm_output_try_view_on_plane(struct drm_plane *plane, - struct drm_output_state *state, - struct weston_view *ev, - enum drm_output_propose_state_mode mode, - struct drm_fb *fb, uint64_t zpos) -{ - struct drm_backend *b = state->pending_state->backend; - struct weston_output *wet_output = &state->output->base; - bool view_matches_entire_output, scanout_has_view_assigned; - struct drm_plane *scanout_plane = state->output->scanout_plane; - struct drm_plane_state *ps = NULL; - const char *p_name = drm_output_get_plane_type_name(plane); - enum { - NO_PLANES, /* generic err-handle */ - NO_PLANES_ACCEPTED, - PLACED_ON_PLANE, - } availability = NO_PLANES; - - /* sanity checks in case we over/underflow zpos or pass incorrect - * values */ - assert(zpos <= plane->zpos_max || - zpos != DRM_PLANE_ZPOS_INVALID_PLANE); - - switch (plane->type) { - case WDRM_PLANE_TYPE_CURSOR: - if (b->cursors_are_broken) { - availability = NO_PLANES_ACCEPTED; - goto out; - } - - ps = drm_output_prepare_cursor_view(state, ev, zpos); - if (ps) - availability = PLACED_ON_PLANE; - break; - case WDRM_PLANE_TYPE_OVERLAY: - /* do not attempt to place it in the overlay if we don't have - * anything in the scanout/primary and the view doesn't cover - * the entire output */ - view_matches_entire_output = - weston_view_matches_output_entirely(ev, wet_output); - scanout_has_view_assigned = - drm_output_check_plane_has_view_assigned(scanout_plane, - state); - - if (view_matches_entire_output && !scanout_has_view_assigned) { - availability = NO_PLANES_ACCEPTED; - goto out; - } - - ps = drm_output_prepare_overlay_view(plane, state, ev, mode, - fb, zpos); - if (ps) - availability = PLACED_ON_PLANE; - break; - case WDRM_PLANE_TYPE_PRIMARY: - if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) { - availability = NO_PLANES_ACCEPTED; - goto out; - } - - ps = drm_output_prepare_scanout_view(state, ev, mode, - fb, zpos); - if (ps) - availability = PLACED_ON_PLANE; - break; - default: - assert(0); - break; - } - -out: - switch (availability) { - case NO_PLANES: - /* set initial to this catch-all case, such that - * prepare_cursor/overlay/scanout() should have/contain the - * reason for failling */ - break; - case NO_PLANES_ACCEPTED: - drm_debug(b, "\t\t\t\t[plane] plane %d refusing to " - "place view %p in %s\n", - plane->plane_id, ev, p_name); - break; - case PLACED_ON_PLANE: - break; - } - - - return ps; -} - -static void -drm_output_check_zpos_plane_states(struct drm_output_state *state) -{ - struct drm_plane_state *ps; - - wl_list_for_each(ps, &state->plane_list, link) { - struct wl_list *next_node = ps->link.next; - bool found_dup = false; - - /* skip any plane that is not enabled */ - if (!ps->fb) - continue; - - assert(ps->zpos != DRM_PLANE_ZPOS_INVALID_PLANE); - - /* find another plane with the same zpos value */ - if (next_node == &state->plane_list) - break; - - while (next_node && next_node != &state->plane_list) { - struct drm_plane_state *ps_next; - - ps_next = container_of(next_node, - struct drm_plane_state, - link); - - if (ps->zpos == ps_next->zpos) { - found_dup = true; - break; - } - next_node = next_node->next; - } - - /* this should never happen so exit hard in case - * we screwed up that bad */ - assert(!found_dup); - } -} - -static struct drm_plane_state * -drm_output_prepare_plane_view(struct drm_output_state *state, - struct weston_view *ev, - enum drm_output_propose_state_mode mode, - struct drm_plane_state *scanout_state, - uint64_t current_lowest_zpos) -{ - struct drm_output *output = state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - - struct drm_plane_state *ps = NULL; - struct drm_plane *plane; - struct drm_plane_zpos *p_zpos, *p_zpos_next; - struct wl_list zpos_candidate_list; - - struct drm_fb *fb; - - wl_list_init(&zpos_candidate_list); - - /* check view for valid buffer, doesn't make sense to even try */ - if (!weston_view_has_valid_buffer(ev)) - return ps; - - fb = drm_fb_get_from_view(state, ev); - - /* assemble a list with possible candidates */ - wl_list_for_each(plane, &b->plane_list, link) { - if (!drm_plane_is_available(plane, output)) - continue; - - if (drm_output_check_plane_has_view_assigned(plane, state)) { - drm_debug(b, "\t\t\t\t[plane] not adding plane %d to" - " candidate list: view already assigned " - "to a plane\n", plane->plane_id); - continue; - } - - if (plane->zpos_min >= current_lowest_zpos) { - drm_debug(b, "\t\t\t\t[plane] not adding plane %d to " - "candidate list: minium zpos (%"PRIu64") " - "plane's above current lowest zpos " - "(%"PRIu64")\n", plane->plane_id, - plane->zpos_min, current_lowest_zpos); - continue; - } - - if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) { - assert(scanout_state != NULL); - if (scanout_state->zpos >= plane->zpos_max) { - drm_debug(b, "\t\t\t\t[plane] not adding plane %d to " - "candidate list: primary's zpos " - "value (%"PRIu64") higher than " - "plane's maximum value (%"PRIu64")\n", - plane->plane_id, scanout_state->zpos, - plane->zpos_max); - continue; - } - } - - if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY && - (plane->type == WDRM_PLANE_TYPE_OVERLAY || - plane->type == WDRM_PLANE_TYPE_PRIMARY)) { - drm_debug(b, "\t\t\t\t[plane] not adding plane %d to " - "candidate list: renderer-only mode\n", - plane->plane_id); - continue; - } - - if (plane->type != WDRM_PLANE_TYPE_CURSOR && - b->sprites_are_broken) { - drm_debug(b, "\t\t\t\t[plane] not adding plane %d, type %s to " - "candidate list: sprites are broken!\n", - plane->plane_id, - drm_output_get_plane_type_name(plane)); - continue; - } - - if (!drm_output_plane_view_has_valid_format(plane, state, ev, fb)) { - drm_debug(b, "\t\t\t\t[plane] not adding plane %d to " - "candidate list: invalid pixel format\n", - plane->plane_id); - continue; - } - - drm_output_add_zpos_plane(plane, &zpos_candidate_list); - } - - /* go over the potential candidate list and try to find a possible - * plane suitable for \c ev; start with the highest zpos value of a - * plane to maximize our chances, but do note we pass the zpos value - * based on current tracked value by \c current_lowest_zpos_in_use */ - while (!wl_list_empty(&zpos_candidate_list)) { - struct drm_plane_zpos *head_p_zpos = - wl_container_of(zpos_candidate_list.next, - head_p_zpos, link); - struct drm_plane *plane = head_p_zpos->plane; - const char *p_name = drm_output_get_plane_type_name(plane); - uint64_t zpos; - - if (current_lowest_zpos == DRM_PLANE_ZPOS_INVALID_PLANE) - zpos = plane->zpos_max; - else - zpos = MIN(current_lowest_zpos - 1, plane->zpos_max); - - drm_debug(b, "\t\t\t\t[plane] plane %d picked " - "from candidate list, type: %s\n", - plane->plane_id, p_name); - - ps = drm_output_try_view_on_plane(plane, state, ev, - mode, fb, zpos); - drm_output_destroy_zpos_plane(head_p_zpos); - if (ps) { - drm_debug(b, "\t\t\t\t[view] view %p has been placed to " - "%s plane with computed zpos %"PRIu64"\n", - ev, p_name, zpos); - break; - } - } - - wl_list_for_each_safe(p_zpos, p_zpos_next, &zpos_candidate_list, link) - drm_output_destroy_zpos_plane(p_zpos); - - drm_fb_unref(fb); - return ps; -} - -static struct drm_output_state * -drm_output_propose_state(struct weston_output *output_base, - struct drm_pending_state *pending_state, - enum drm_output_propose_state_mode mode) -{ - struct drm_output *output = to_drm_output(output_base); - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_output_state *state; - struct drm_plane_state *scanout_state = NULL; - struct weston_view *ev; - - pixman_region32_t surface_overlap, renderer_region, planes_region; - pixman_region32_t occluded_region; - - bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY); - int ret; - uint64_t current_lowest_zpos = DRM_PLANE_ZPOS_INVALID_PLANE; - - assert(!output->state_last); - state = drm_output_state_duplicate(output->state_cur, - pending_state, - DRM_OUTPUT_STATE_CLEAR_PLANES); - - /* We implement mixed mode by progressively creating and testing - * incremental states, of scanout + overlay + cursor. Since we - * walk our views top to bottom, the scanout plane is last, however - * we always need it in our scene for the test modeset to be - * meaningful. To do this, we steal a reference to the last - * renderer framebuffer we have, if we think it's basically - * compatible. If we don't have that, then we conservatively fall - * back to only using the renderer for this repaint. */ - if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) { - struct drm_plane *plane = output->scanout_plane; - struct drm_fb *scanout_fb = plane->state_cur->fb; - - if (!scanout_fb || - (scanout_fb->type != BUFFER_GBM_SURFACE && - scanout_fb->type != BUFFER_PIXMAN_DUMB)) { - drm_debug(b, "\t\t[state] cannot propose mixed mode: " - "for output %s (%lu): no previous renderer " - "fb\n", - output->base.name, - (unsigned long) output->base.id); - drm_output_state_free(state); - return NULL; - } - - if (scanout_fb->width != output_base->current_mode->width || - scanout_fb->height != output_base->current_mode->height) { - drm_debug(b, "\t\t[state] cannot propose mixed mode " - "for output %s (%lu): previous fb has " - "different size\n", - output->base.name, - (unsigned long) output->base.id); - drm_output_state_free(state); - return NULL; - } - - scanout_state = drm_plane_state_duplicate(state, - plane->state_cur); - /* assign the primary primary the lowest zpos value */ - scanout_state->zpos = plane->zpos_min; - drm_debug(b, "\t\t[state] using renderer FB ID %lu for mixed " - "mode for output %s (%lu)\n", - (unsigned long) scanout_fb->fb_id, output->base.name, - (unsigned long) output->base.id); - drm_debug(b, "\t\t[state] scanout will use for zpos %"PRIu64"\n", - scanout_state->zpos); - } - - /* - renderer_region contains the total region which which will be - * covered by the renderer - * - planes_region contains the total region which has been covered by - * hardware planes - * - occluded_region contains the total region which which will be - * covered by the renderer and hardware planes, where the view's - * visible-and-opaque region is added in both cases (the view's - * opaque region accumulates there for each view); it is being used - * to skip the view, if it is completely occluded; includes the - * situation where occluded_region covers entire output's region. - */ - pixman_region32_init(&renderer_region); - pixman_region32_init(&planes_region); - pixman_region32_init(&occluded_region); - - wl_list_for_each(ev, &output_base->compositor->view_list, link) { - struct drm_plane_state *ps = NULL; - bool force_renderer = false; - pixman_region32_t clipped_view; - bool totally_occluded = false; - - drm_debug(b, "\t\t\t[view] evaluating view %p for " - "output %s (%lu)\n", - ev, output->base.name, - (unsigned long) output->base.id); - - /* If this view doesn't touch our output at all, there's no - * reason to do anything with it. */ - if (!(ev->output_mask & (1u << output->base.id))) { - drm_debug(b, "\t\t\t\t[view] ignoring view %p " - "(not on our output)\n", ev); - continue; - } - - /* We only assign planes to views which are exclusively present - * on our output. */ - if (ev->output_mask != (1u << output->base.id)) { - drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " - "(on multiple outputs)\n", ev); - force_renderer = true; - } - - if (!weston_view_has_valid_buffer(ev)) { - drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " - "(no buffer available)\n", ev); - force_renderer = true; - } - - /* Ignore views we know to be totally occluded. */ - pixman_region32_init(&clipped_view); - pixman_region32_intersect(&clipped_view, - &ev->transform.boundingbox, - &output->base.region); - - pixman_region32_init(&surface_overlap); - pixman_region32_subtract(&surface_overlap, &clipped_view, - &occluded_region); - /* if the view is completely occluded then ignore that - * view; includes the case where occluded_region covers - * the entire output */ - totally_occluded = !pixman_region32_not_empty(&surface_overlap); - if (totally_occluded) { - drm_debug(b, "\t\t\t\t[view] ignoring view %p " - "(occluded on our output)\n", ev); - pixman_region32_fini(&surface_overlap); - pixman_region32_fini(&clipped_view); - continue; - } - - /* Since we process views from top to bottom, we know that if - * the view intersects the calculated renderer region, it must - * be part of, or occluded by, it, and cannot go on a plane. */ - pixman_region32_intersect(&surface_overlap, &renderer_region, - &clipped_view); - if (pixman_region32_not_empty(&surface_overlap)) { - drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " - "(occluded by renderer views)\n", ev); - force_renderer = true; - } - pixman_region32_fini(&surface_overlap); - - /* In case of enforced mode of content-protection do not - * assign planes for a protected surface on an unsecured output. - */ - if (ev->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED && - ev->surface->desired_protection > output_base->current_protection) { - drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " - "(enforced protection mode on unsecured output)\n", ev); - force_renderer = true; - } - - if (!force_renderer) { - drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n", - current_lowest_zpos); - ps = drm_output_prepare_plane_view(state, ev, mode, - scanout_state, - current_lowest_zpos); - } - - if (ps) { - current_lowest_zpos = ps->zpos; - drm_debug(b, "\t\t\t[plane] next zpos to use %"PRIu64"\n", - current_lowest_zpos); - - /* If we have been assigned to an overlay or scanout - * plane, add this area to the occluded region, so - * other views are known to be behind it. The cursor - * plane, however, is special, in that it blends with - * the content underneath it: the area should neither - * be added to the renderer region nor the occluded - * region. */ - if (ps->plane->type != WDRM_PLANE_TYPE_CURSOR) { - pixman_region32_union(&planes_region, - &planes_region, - &clipped_view); - - if (!weston_view_is_opaque(ev, &clipped_view)) - pixman_region32_intersect(&clipped_view, - &clipped_view, - &ev->transform.opaque); - /* the visible-and-opaque region of this view - * will occlude views underneath it */ - pixman_region32_union(&occluded_region, - &occluded_region, - &clipped_view); - - pixman_region32_fini(&clipped_view); - - } - continue; - } - - /* We have been assigned to the primary (renderer) plane: - * check if this is OK, and add ourselves to the renderer - * region if so. */ - if (!renderer_ok) { - drm_debug(b, "\t\t[view] failing state generation: " - "placing view %p to renderer not allowed\n", - ev); - pixman_region32_fini(&clipped_view); - goto err_region; - } - - pixman_region32_union(&renderer_region, - &renderer_region, - &clipped_view); - - if (!weston_view_is_opaque(ev, &clipped_view)) - pixman_region32_intersect(&clipped_view, - &clipped_view, - &ev->transform.opaque); - - pixman_region32_union(&occluded_region, - &occluded_region, - &clipped_view); - - pixman_region32_fini(&clipped_view); - - drm_debug(b, "\t\t\t\t[view] view %p will be placed " - "on the renderer\n", ev); - } - - pixman_region32_fini(&renderer_region); - pixman_region32_fini(&planes_region); - pixman_region32_fini(&occluded_region); - - /* In renderer-only mode, we can't test the state as we don't have a - * renderer buffer yet. */ - if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY) - return state; - - /* check if we have invalid zpos values, like duplicate(s) */ - drm_output_check_zpos_plane_states(state); - - /* Check to see if this state will actually work. */ - ret = drm_pending_state_test(state->pending_state); - if (ret != 0) { - drm_debug(b, "\t\t[view] failing state generation: " - "atomic test not OK\n"); - goto err; - } - - /* Counterpart to duplicating scanout state at the top of this - * function: if we have taken a renderer framebuffer and placed it in - * the pending state in order to incrementally test overlay planes, - * remove it now. */ - if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) { - assert(scanout_state->fb->type == BUFFER_GBM_SURFACE || - scanout_state->fb->type == BUFFER_PIXMAN_DUMB); - drm_plane_state_put_back(scanout_state); - } - return state; - -err_region: - pixman_region32_fini(&renderer_region); - pixman_region32_fini(&occluded_region); -err: - drm_output_state_free(state); - return NULL; -} - -void -drm_assign_planes(struct weston_output *output_base, void *repaint_data) -{ - struct drm_backend *b = to_drm_backend(output_base->compositor); - struct drm_pending_state *pending_state = repaint_data; - struct drm_output *output = to_drm_output(output_base); - struct drm_output_state *state = NULL; - struct drm_plane_state *plane_state; - struct weston_view *ev; - struct weston_plane *primary = &output_base->compositor->primary_plane; - enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY; - - drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n", - output_base->name, (unsigned long) output_base->id); - - if (!b->sprites_are_broken && !output->virtual) { - drm_debug(b, "\t[repaint] trying planes-only build state\n"); - state = drm_output_propose_state(output_base, pending_state, mode); - if (!state) { - drm_debug(b, "\t[repaint] could not build planes-only " - "state, trying mixed\n"); - mode = DRM_OUTPUT_PROPOSE_STATE_MIXED; - state = drm_output_propose_state(output_base, - pending_state, - mode); - } - if (!state) { - drm_debug(b, "\t[repaint] could not build mixed-mode " - "state, trying renderer-only\n"); - } - } else { - drm_debug(b, "\t[state] no overlay plane support\n"); - } - - if (!state) { - mode = DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY; - state = drm_output_propose_state(output_base, pending_state, - mode); - } - - assert(state); - drm_debug(b, "\t[repaint] Using %s composition\n", - drm_propose_state_mode_to_string(mode)); - - wl_list_for_each(ev, &output_base->compositor->view_list, link) { - struct drm_plane *target_plane = NULL; - - /* If this view doesn't touch our output at all, there's no - * reason to do anything with it. */ - if (!(ev->output_mask & (1u << output->base.id))) - continue; - - /* Test whether this buffer can ever go into a plane: - * non-shm, or small enough to be a cursor. - * - * Also, keep a reference when using the pixman renderer. - * That makes it possible to do a seamless switch to the GL - * renderer and since the pixman renderer keeps a reference - * to the buffer anyway, there is no side effects. - */ - if (b->use_pixman || - (weston_view_has_valid_buffer(ev) && - (!wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) || - (ev->surface->width <= b->cursor_width && - ev->surface->height <= b->cursor_height)))) - ev->surface->keep_buffer = true; - else - ev->surface->keep_buffer = false; - - /* This is a bit unpleasant, but lacking a temporary place to - * hang a plane off the view, we have to do a nested walk. - * Our first-order iteration has to be planes rather than - * views, because otherwise we won't reset views which were - * previously on planes to being on the primary plane. */ - wl_list_for_each(plane_state, &state->plane_list, link) { - if (plane_state->ev == ev) { - plane_state->ev = NULL; - target_plane = plane_state->plane; - break; - } - } - - if (target_plane) { - drm_debug(b, "\t[repaint] view %p on %s plane %lu\n", - ev, plane_type_enums[target_plane->type].name, - (unsigned long) target_plane->plane_id); - weston_view_move_to_plane(ev, &target_plane->base); - } else { - drm_debug(b, "\t[repaint] view %p using renderer " - "composition\n", ev); - weston_view_move_to_plane(ev, primary); - } - - if (!target_plane || - target_plane->type == WDRM_PLANE_TYPE_CURSOR) { - /* cursor plane & renderer involve a copy */ - ev->psf_flags = 0; - } else { - /* All other planes are a direct scanout of a - * single client buffer. - */ - ev->psf_flags = WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY; - } - } - - /* We rely on output->cursor_view being both an accurate reflection of - * the cursor plane's state, but also being maintained across repaints - * to avoid unnecessary damage uploads, per the comment in - * drm_output_prepare_cursor_view. In the event that we go from having - * a cursor view to not having a cursor view, we need to clear it. */ - if (output->cursor_view) { - plane_state = - drm_output_state_get_existing_plane(state, - output->cursor_plane); - if (!plane_state || !plane_state->fb) - output->cursor_view = NULL; - } -} diff --git a/libweston/backend-drm/vaapi-recorder.c b/libweston/backend-drm/vaapi-recorder.c deleted file mode 100644 index 0a743570..00000000 --- a/libweston/backend-drm/vaapi-recorder.c +++ /dev/null @@ -1,1161 +0,0 @@ -/* - * Copyright (c) 2012 Intel Corporation. All Rights Reserved. - * Copyright © 2013 Intel Corporation - * - * 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 <stdlib.h> -#include <stdint.h> -#include <string.h> -#include <unistd.h> -#include <assert.h> -#include <errno.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -#include <pthread.h> - -#include <va/va.h> -#include <va/va_drm.h> -#include <va/va_drmcommon.h> -#include <va/va_enc_h264.h> -#include <va/va_vpp.h> - -#include <libweston/libweston.h> -#include "vaapi-recorder.h" - -#define NAL_REF_IDC_NONE 0 -#define NAL_REF_IDC_LOW 1 -#define NAL_REF_IDC_MEDIUM 2 -#define NAL_REF_IDC_HIGH 3 - -#define NAL_NON_IDR 1 -#define NAL_IDR 5 -#define NAL_SPS 7 -#define NAL_PPS 8 -#define NAL_SEI 6 - -#define SLICE_TYPE_P 0 -#define SLICE_TYPE_B 1 -#define SLICE_TYPE_I 2 - -#define ENTROPY_MODE_CAVLC 0 -#define ENTROPY_MODE_CABAC 1 - -#define PROFILE_IDC_BASELINE 66 -#define PROFILE_IDC_MAIN 77 -#define PROFILE_IDC_HIGH 100 - -struct vaapi_recorder { - int drm_fd, output_fd; - int width, height; - int frame_count; - - int error; - int destroying; - pthread_t worker_thread; - pthread_mutex_t mutex; - pthread_cond_t input_cond; - - struct { - int valid; - int prime_fd, stride; - } input; - - VADisplay va_dpy; - - /* video post processing is used for colorspace conversion */ - struct { - VAConfigID cfg; - VAContextID ctx; - VABufferID pipeline_buf; - VASurfaceID output; - } vpp; - - struct { - VAConfigID cfg; - VAContextID ctx; - VASurfaceID reference_picture[3]; - - int intra_period; - int output_size; - int constraint_set_flag; - - struct { - VAEncSequenceParameterBufferH264 seq; - VAEncPictureParameterBufferH264 pic; - VAEncSliceParameterBufferH264 slice; - } param; - } encoder; -}; - -static void * -worker_thread_function(void *); - -/* bitstream code used for writing the packed headers */ - -#define BITSTREAM_ALLOCATE_STEPPING 4096 - -struct bitstream { - unsigned int *buffer; - int bit_offset; - int max_size_in_dword; -}; - -static unsigned int -va_swap32(unsigned int val) -{ - unsigned char *pval = (unsigned char *)&val; - - return ((pval[0] << 24) | - (pval[1] << 16) | - (pval[2] << 8) | - (pval[3] << 0)); -} - -static void -bitstream_start(struct bitstream *bs) -{ - bs->max_size_in_dword = BITSTREAM_ALLOCATE_STEPPING; - bs->buffer = calloc(bs->max_size_in_dword * sizeof(unsigned int), 1); - bs->bit_offset = 0; -} - -static void -bitstream_end(struct bitstream *bs) -{ - int pos = (bs->bit_offset >> 5); - int bit_offset = (bs->bit_offset & 0x1f); - int bit_left = 32 - bit_offset; - - if (bit_offset) { - bs->buffer[pos] = va_swap32((bs->buffer[pos] << bit_left)); - } -} - -static void -bitstream_put_ui(struct bitstream *bs, unsigned int val, int size_in_bits) -{ - int pos = (bs->bit_offset >> 5); - int bit_offset = (bs->bit_offset & 0x1f); - int bit_left = 32 - bit_offset; - - if (!size_in_bits) - return; - - bs->bit_offset += size_in_bits; - - if (bit_left > size_in_bits) { - bs->buffer[pos] = (bs->buffer[pos] << size_in_bits | val); - return; - } - - size_in_bits -= bit_left; - bs->buffer[pos] = - (bs->buffer[pos] << bit_left) | (val >> size_in_bits); - bs->buffer[pos] = va_swap32(bs->buffer[pos]); - - if (pos + 1 == bs->max_size_in_dword) { - bs->max_size_in_dword += BITSTREAM_ALLOCATE_STEPPING; - bs->buffer = - realloc(bs->buffer, - bs->max_size_in_dword * sizeof(unsigned int)); - } - - bs->buffer[pos + 1] = val; -} - -static void -bitstream_put_ue(struct bitstream *bs, unsigned int val) -{ - int size_in_bits = 0; - int tmp_val = ++val; - - while (tmp_val) { - tmp_val >>= 1; - size_in_bits++; - } - - bitstream_put_ui(bs, 0, size_in_bits - 1); /* leading zero */ - bitstream_put_ui(bs, val, size_in_bits); -} - -static void -bitstream_put_se(struct bitstream *bs, int val) -{ - unsigned int new_val; - - if (val <= 0) - new_val = -2 * val; - else - new_val = 2 * val - 1; - - bitstream_put_ue(bs, new_val); -} - -static void -bitstream_byte_aligning(struct bitstream *bs, int bit) -{ - int bit_offset = (bs->bit_offset & 0x7); - int bit_left = 8 - bit_offset; - int new_val; - - if (!bit_offset) - return; - - if (bit) - new_val = (1 << bit_left) - 1; - else - new_val = 0; - - bitstream_put_ui(bs, new_val, bit_left); -} - -static VAStatus -encoder_create_config(struct vaapi_recorder *r) -{ - VAConfigAttrib attrib[2]; - VAStatus status; - - /* FIXME: should check if VAEntrypointEncSlice is supported */ - - /* FIXME: should check if specified attributes are supported */ - - attrib[0].type = VAConfigAttribRTFormat; - attrib[0].value = VA_RT_FORMAT_YUV420; - - attrib[1].type = VAConfigAttribRateControl; - attrib[1].value = VA_RC_CQP; - - status = vaCreateConfig(r->va_dpy, VAProfileH264Main, - VAEntrypointEncSlice, attrib, 2, - &r->encoder.cfg); - if (status != VA_STATUS_SUCCESS) - return status; - - status = vaCreateContext(r->va_dpy, r->encoder.cfg, - r->width, r->height, VA_PROGRESSIVE, 0, 0, - &r->encoder.ctx); - if (status != VA_STATUS_SUCCESS) { - vaDestroyConfig(r->va_dpy, r->encoder.cfg); - return status; - } - - return VA_STATUS_SUCCESS; -} - -static void -encoder_destroy_config(struct vaapi_recorder *r) -{ - vaDestroyContext(r->va_dpy, r->encoder.ctx); - vaDestroyConfig(r->va_dpy, r->encoder.cfg); -} - -static void -encoder_init_seq_parameters(struct vaapi_recorder *r) -{ - int width_in_mbs, height_in_mbs; - int frame_cropping_flag = 0; - int frame_crop_bottom_offset = 0; - - width_in_mbs = (r->width + 15) / 16; - height_in_mbs = (r->height + 15) / 16; - - r->encoder.param.seq.level_idc = 41; - r->encoder.param.seq.intra_period = r->encoder.intra_period; - r->encoder.param.seq.max_num_ref_frames = 4; - r->encoder.param.seq.picture_width_in_mbs = width_in_mbs; - r->encoder.param.seq.picture_height_in_mbs = height_in_mbs; - r->encoder.param.seq.seq_fields.bits.frame_mbs_only_flag = 1; - - /* Tc = num_units_in_tick / time_scale */ - r->encoder.param.seq.time_scale = 1800; - r->encoder.param.seq.num_units_in_tick = 15; - - if (height_in_mbs * 16 - r->height > 0) { - frame_cropping_flag = 1; - frame_crop_bottom_offset = (height_in_mbs * 16 - r->height) / 2; - } - - r->encoder.param.seq.frame_cropping_flag = frame_cropping_flag; - r->encoder.param.seq.frame_crop_bottom_offset = frame_crop_bottom_offset; - - r->encoder.param.seq.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = 2; -} - -static VABufferID -encoder_update_seq_parameters(struct vaapi_recorder *r) -{ - VABufferID seq_buf; - VAStatus status; - - status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, - VAEncSequenceParameterBufferType, - sizeof(r->encoder.param.seq), - 1, &r->encoder.param.seq, - &seq_buf); - - if (status == VA_STATUS_SUCCESS) - return seq_buf; - else - return VA_INVALID_ID; -} - -static void -encoder_init_pic_parameters(struct vaapi_recorder *r) -{ - VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic; - - pic->pic_init_qp = 0; - - /* ENTROPY_MODE_CABAC */ - pic->pic_fields.bits.entropy_coding_mode_flag = 1; - - pic->pic_fields.bits.deblocking_filter_control_present_flag = 1; -} - -static VABufferID -encoder_update_pic_parameters(struct vaapi_recorder *r, - VABufferID output_buf) -{ - VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic; - VAStatus status; - VABufferID pic_param_buf; - VASurfaceID curr_pic, pic0; - - curr_pic = r->encoder.reference_picture[r->frame_count % 2]; - pic0 = r->encoder.reference_picture[(r->frame_count + 1) % 2]; - - pic->CurrPic.picture_id = curr_pic; - pic->CurrPic.TopFieldOrderCnt = r->frame_count * 2; - pic->ReferenceFrames[0].picture_id = pic0; - pic->ReferenceFrames[1].picture_id = r->encoder.reference_picture[2]; - pic->ReferenceFrames[2].picture_id = VA_INVALID_ID; - - pic->coded_buf = output_buf; - pic->frame_num = r->frame_count; - - pic->pic_fields.bits.idr_pic_flag = (r->frame_count == 0); - pic->pic_fields.bits.reference_pic_flag = 1; - - status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, - VAEncPictureParameterBufferType, - sizeof(VAEncPictureParameterBufferH264), 1, - pic, &pic_param_buf); - - if (status == VA_STATUS_SUCCESS) - return pic_param_buf; - else - return VA_INVALID_ID; -} - -static VABufferID -encoder_update_slice_parameter(struct vaapi_recorder *r, int slice_type) -{ - VABufferID slice_param_buf; - VAStatus status; - - int width_in_mbs = (r->width + 15) / 16; - int height_in_mbs = (r->height + 15) / 16; - - memset(&r->encoder.param.slice, 0, sizeof r->encoder.param.slice); - - r->encoder.param.slice.num_macroblocks = width_in_mbs * height_in_mbs; - r->encoder.param.slice.slice_type = slice_type; - - r->encoder.param.slice.slice_alpha_c0_offset_div2 = 2; - r->encoder.param.slice.slice_beta_offset_div2 = 2; - - status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, - VAEncSliceParameterBufferType, - sizeof(r->encoder.param.slice), 1, - &r->encoder.param.slice, - &slice_param_buf); - - if (status == VA_STATUS_SUCCESS) - return slice_param_buf; - else - return VA_INVALID_ID; -} - -static VABufferID -encoder_update_misc_hdr_parameter(struct vaapi_recorder *r) -{ - VAEncMiscParameterBuffer *misc_param; - VAEncMiscParameterHRD *hrd; - VABufferID buffer; - VAStatus status; - - int total_size = - sizeof(VAEncMiscParameterBuffer) + - sizeof(VAEncMiscParameterRateControl); - - status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, - VAEncMiscParameterBufferType, total_size, - 1, NULL, &buffer); - if (status != VA_STATUS_SUCCESS) - return VA_INVALID_ID; - - status = vaMapBuffer(r->va_dpy, buffer, (void **) &misc_param); - if (status != VA_STATUS_SUCCESS) { - vaDestroyBuffer(r->va_dpy, buffer); - return VA_INVALID_ID; - } - - misc_param->type = VAEncMiscParameterTypeHRD; - hrd = (VAEncMiscParameterHRD *) misc_param->data; - - hrd->initial_buffer_fullness = 0; - hrd->buffer_size = 0; - - vaUnmapBuffer(r->va_dpy, buffer); - - return buffer; -} - -static int -setup_encoder(struct vaapi_recorder *r) -{ - VAStatus status; - - status = encoder_create_config(r); - if (status != VA_STATUS_SUCCESS) { - return -1; - } - - status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420, - r->width, r->height, - r->encoder.reference_picture, 3, - NULL, 0); - if (status != VA_STATUS_SUCCESS) { - encoder_destroy_config(r); - return -1; - } - - /* VAProfileH264Main */ - r->encoder.constraint_set_flag |= (1 << 1); /* Annex A.2.2 */ - - r->encoder.output_size = r->width * r->height; - - r->encoder.intra_period = 30; - - encoder_init_seq_parameters(r); - encoder_init_pic_parameters(r); - - return 0; -} - -static void -encoder_destroy(struct vaapi_recorder *r) -{ - vaDestroySurfaces(r->va_dpy, r->encoder.reference_picture, 3); - - encoder_destroy_config(r); -} - -static void -nal_start_code_prefix(struct bitstream *bs) -{ - bitstream_put_ui(bs, 0x00000001, 32); -} - -static void -nal_header(struct bitstream *bs, int nal_ref_idc, int nal_unit_type) -{ - /* forbidden_zero_bit: 0 */ - bitstream_put_ui(bs, 0, 1); - - bitstream_put_ui(bs, nal_ref_idc, 2); - bitstream_put_ui(bs, nal_unit_type, 5); -} - -static void -rbsp_trailing_bits(struct bitstream *bs) -{ - bitstream_put_ui(bs, 1, 1); - bitstream_byte_aligning(bs, 0); -} - -static void sps_rbsp(struct bitstream *bs, - VAEncSequenceParameterBufferH264 *seq, - int constraint_set_flag) -{ - int i; - - bitstream_put_ui(bs, PROFILE_IDC_MAIN, 8); - - /* constraint_set[0-3] flag */ - for (i = 0; i < 4; i++) { - int set = (constraint_set_flag & (1 << i)) ? 1 : 0; - bitstream_put_ui(bs, set, 1); - } - - /* reserved_zero_4bits */ - bitstream_put_ui(bs, 0, 4); - bitstream_put_ui(bs, seq->level_idc, 8); - bitstream_put_ue(bs, seq->seq_parameter_set_id); - - bitstream_put_ue(bs, seq->seq_fields.bits.log2_max_frame_num_minus4); - bitstream_put_ue(bs, seq->seq_fields.bits.pic_order_cnt_type); - bitstream_put_ue(bs, - seq->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4); - - bitstream_put_ue(bs, seq->max_num_ref_frames); - - /* gaps_in_frame_num_value_allowed_flag */ - bitstream_put_ui(bs, 0, 1); - - /* pic_width_in_mbs_minus1, pic_height_in_map_units_minus1 */ - bitstream_put_ue(bs, seq->picture_width_in_mbs - 1); - bitstream_put_ue(bs, seq->picture_height_in_mbs - 1); - - bitstream_put_ui(bs, seq->seq_fields.bits.frame_mbs_only_flag, 1); - bitstream_put_ui(bs, seq->seq_fields.bits.direct_8x8_inference_flag, 1); - - bitstream_put_ui(bs, seq->frame_cropping_flag, 1); - - if (seq->frame_cropping_flag) { - bitstream_put_ue(bs, seq->frame_crop_left_offset); - bitstream_put_ue(bs, seq->frame_crop_right_offset); - bitstream_put_ue(bs, seq->frame_crop_top_offset); - bitstream_put_ue(bs, seq->frame_crop_bottom_offset); - } - - /* vui_parameters_present_flag */ - bitstream_put_ui(bs, 1, 1); - - /* aspect_ratio_info_present_flag */ - bitstream_put_ui(bs, 0, 1); - /* overscan_info_present_flag */ - bitstream_put_ui(bs, 0, 1); - - /* video_signal_type_present_flag */ - bitstream_put_ui(bs, 0, 1); - /* chroma_loc_info_present_flag */ - bitstream_put_ui(bs, 0, 1); - - /* timing_info_present_flag */ - bitstream_put_ui(bs, 1, 1); - bitstream_put_ui(bs, seq->num_units_in_tick, 32); - bitstream_put_ui(bs, seq->time_scale, 32); - /* fixed_frame_rate_flag */ - bitstream_put_ui(bs, 1, 1); - - /* nal_hrd_parameters_present_flag */ - bitstream_put_ui(bs, 0, 1); - - /* vcl_hrd_parameters_present_flag */ - bitstream_put_ui(bs, 0, 1); - - /* low_delay_hrd_flag */ - bitstream_put_ui(bs, 0, 1); - - /* pic_struct_present_flag */ - bitstream_put_ui(bs, 0, 1); - /* bitstream_restriction_flag */ - bitstream_put_ui(bs, 0, 1); - - rbsp_trailing_bits(bs); -} - -static void pps_rbsp(struct bitstream *bs, - VAEncPictureParameterBufferH264 *pic) -{ - /* pic_parameter_set_id, seq_parameter_set_id */ - bitstream_put_ue(bs, pic->pic_parameter_set_id); - bitstream_put_ue(bs, pic->seq_parameter_set_id); - - bitstream_put_ui(bs, pic->pic_fields.bits.entropy_coding_mode_flag, 1); - - /* pic_order_present_flag: 0 */ - bitstream_put_ui(bs, 0, 1); - - /* num_slice_groups_minus1 */ - bitstream_put_ue(bs, 0); - - bitstream_put_ue(bs, pic->num_ref_idx_l0_active_minus1); - bitstream_put_ue(bs, pic->num_ref_idx_l1_active_minus1); - - bitstream_put_ui(bs, pic->pic_fields.bits.weighted_pred_flag, 1); - bitstream_put_ui(bs, pic->pic_fields.bits.weighted_bipred_idc, 2); - - /* pic_init_qp_minus26, pic_init_qs_minus26, chroma_qp_index_offset */ - bitstream_put_se(bs, pic->pic_init_qp - 26); - bitstream_put_se(bs, 0); - bitstream_put_se(bs, 0); - - bitstream_put_ui(bs, pic->pic_fields.bits.deblocking_filter_control_present_flag, 1); - - /* constrained_intra_pred_flag, redundant_pic_cnt_present_flag */ - bitstream_put_ui(bs, 0, 1); - bitstream_put_ui(bs, 0, 1); - - bitstream_put_ui(bs, pic->pic_fields.bits.transform_8x8_mode_flag, 1); - - /* pic_scaling_matrix_present_flag */ - bitstream_put_ui(bs, 0, 1); - bitstream_put_se(bs, pic->second_chroma_qp_index_offset ); - - rbsp_trailing_bits(bs); -} - -static int -build_packed_pic_buffer(struct vaapi_recorder *r, - void **header_buffer) -{ - struct bitstream bs; - - bitstream_start(&bs); - nal_start_code_prefix(&bs); - nal_header(&bs, NAL_REF_IDC_HIGH, NAL_PPS); - pps_rbsp(&bs, &r->encoder.param.pic); - bitstream_end(&bs); - - *header_buffer = bs.buffer; - return bs.bit_offset; -} - -static int -build_packed_seq_buffer(struct vaapi_recorder *r, - void **header_buffer) -{ - struct bitstream bs; - - bitstream_start(&bs); - nal_start_code_prefix(&bs); - nal_header(&bs, NAL_REF_IDC_HIGH, NAL_SPS); - sps_rbsp(&bs, &r->encoder.param.seq, r->encoder.constraint_set_flag); - bitstream_end(&bs); - - *header_buffer = bs.buffer; - return bs.bit_offset; -} - -static int -create_packed_header_buffers(struct vaapi_recorder *r, VABufferID *buffers, - VAEncPackedHeaderType type, - void *data, int bit_length) -{ - VAEncPackedHeaderParameterBuffer packed_header; - VAStatus status; - - packed_header.type = type; - packed_header.bit_length = bit_length; - packed_header.has_emulation_bytes = 0; - - status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, - VAEncPackedHeaderParameterBufferType, - sizeof packed_header, 1, &packed_header, - &buffers[0]); - if (status != VA_STATUS_SUCCESS) - return 0; - - status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, - VAEncPackedHeaderDataBufferType, - (bit_length + 7) / 8, 1, data, &buffers[1]); - if (status != VA_STATUS_SUCCESS) { - vaDestroyBuffer(r->va_dpy, buffers[0]); - return 0; - } - - return 2; -} - -static int -encoder_prepare_headers(struct vaapi_recorder *r, VABufferID *buffers) -{ - VABufferID *p; - - int bit_length; - void *data; - - p = buffers; - - bit_length = build_packed_seq_buffer(r, &data); - p += create_packed_header_buffers(r, p, VAEncPackedHeaderSequence, - data, bit_length); - free(data); - - bit_length = build_packed_pic_buffer(r, &data); - p += create_packed_header_buffers(r, p, VAEncPackedHeaderPicture, - data, bit_length); - free(data); - - return p - buffers; -} - -static VAStatus -encoder_render_picture(struct vaapi_recorder *r, VASurfaceID input, - VABufferID *buffers, int count) -{ - VAStatus status; - - status = vaBeginPicture(r->va_dpy, r->encoder.ctx, input); - if (status != VA_STATUS_SUCCESS) - return status; - - status = vaRenderPicture(r->va_dpy, r->encoder.ctx, buffers, count); - if (status != VA_STATUS_SUCCESS) - return status; - - status = vaEndPicture(r->va_dpy, r->encoder.ctx); - if (status != VA_STATUS_SUCCESS) - return status; - - return vaSyncSurface(r->va_dpy, input); -} - -static VABufferID -encoder_create_output_buffer(struct vaapi_recorder *r) -{ - VABufferID output_buf; - VAStatus status; - - status = vaCreateBuffer(r->va_dpy, r->encoder.ctx, - VAEncCodedBufferType, r->encoder.output_size, - 1, NULL, &output_buf); - if (status == VA_STATUS_SUCCESS) - return output_buf; - else - return VA_INVALID_ID; -} - -enum output_write_status { - OUTPUT_WRITE_SUCCESS, - OUTPUT_WRITE_OVERFLOW, - OUTPUT_WRITE_FATAL -}; - -static enum output_write_status -encoder_write_output(struct vaapi_recorder *r, VABufferID output_buf) -{ - VACodedBufferSegment *segment; - VAStatus status; - int count; - - status = vaMapBuffer(r->va_dpy, output_buf, (void **) &segment); - if (status != VA_STATUS_SUCCESS) - return OUTPUT_WRITE_FATAL; - - if (segment->status & VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK) { - r->encoder.output_size *= 2; - vaUnmapBuffer(r->va_dpy, output_buf); - return OUTPUT_WRITE_OVERFLOW; - } - - count = write(r->output_fd, segment->buf, segment->size); - - vaUnmapBuffer(r->va_dpy, output_buf); - - if (count < 0) - return OUTPUT_WRITE_FATAL; - - return OUTPUT_WRITE_SUCCESS; -} - -static void -encoder_encode(struct vaapi_recorder *r, VASurfaceID input) -{ - VABufferID output_buf = VA_INVALID_ID; - - VABufferID buffers[8]; - int count = 0; - int i, slice_type; - enum output_write_status ret; - - if ((r->frame_count % r->encoder.intra_period) == 0) - slice_type = SLICE_TYPE_I; - else - slice_type = SLICE_TYPE_P; - - buffers[count++] = encoder_update_seq_parameters(r); - buffers[count++] = encoder_update_misc_hdr_parameter(r); - buffers[count++] = encoder_update_slice_parameter(r, slice_type); - - for (i = 0; i < count; i++) - if (buffers[i] == VA_INVALID_ID) - goto bail; - - if (r->frame_count == 0) - count += encoder_prepare_headers(r, buffers + count); - - do { - output_buf = encoder_create_output_buffer(r); - if (output_buf == VA_INVALID_ID) - goto bail; - - buffers[count++] = - encoder_update_pic_parameters(r, output_buf); - if (buffers[count - 1] == VA_INVALID_ID) - goto bail; - - encoder_render_picture(r, input, buffers, count); - ret = encoder_write_output(r, output_buf); - - vaDestroyBuffer(r->va_dpy, output_buf); - output_buf = VA_INVALID_ID; - - vaDestroyBuffer(r->va_dpy, buffers[--count]); - } while (ret == OUTPUT_WRITE_OVERFLOW); - - if (ret == OUTPUT_WRITE_FATAL) - r->error = errno; - - for (i = 0; i < count; i++) - vaDestroyBuffer(r->va_dpy, buffers[i]); - - r->frame_count++; - return; - -bail: - for (i = 0; i < count; i++) - vaDestroyBuffer(r->va_dpy, buffers[i]); - if (output_buf != VA_INVALID_ID) - vaDestroyBuffer(r->va_dpy, output_buf); -} - - -static int -setup_vpp(struct vaapi_recorder *r) -{ - VAStatus status; - - status = vaCreateConfig(r->va_dpy, VAProfileNone, - VAEntrypointVideoProc, NULL, 0, - &r->vpp.cfg); - if (status != VA_STATUS_SUCCESS) { - weston_log("vaapi: failed to create VPP config\n"); - return -1; - } - - status = vaCreateContext(r->va_dpy, r->vpp.cfg, r->width, r->height, - 0, NULL, 0, &r->vpp.ctx); - if (status != VA_STATUS_SUCCESS) { - weston_log("vaapi: failed to create VPP context\n"); - goto err_cfg; - } - - status = vaCreateBuffer(r->va_dpy, r->vpp.ctx, - VAProcPipelineParameterBufferType, - sizeof(VAProcPipelineParameterBuffer), - 1, NULL, &r->vpp.pipeline_buf); - if (status != VA_STATUS_SUCCESS) { - weston_log("vaapi: failed to create VPP pipeline buffer\n"); - goto err_ctx; - } - - status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420, - r->width, r->height, &r->vpp.output, 1, - NULL, 0); - if (status != VA_STATUS_SUCCESS) { - weston_log("vaapi: failed to create YUV surface\n"); - goto err_buf; - } - - return 0; - -err_buf: - vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf); -err_ctx: - vaDestroyConfig(r->va_dpy, r->vpp.ctx); -err_cfg: - vaDestroyConfig(r->va_dpy, r->vpp.cfg); - - return -1; -} - -static void -vpp_destroy(struct vaapi_recorder *r) -{ - vaDestroySurfaces(r->va_dpy, &r->vpp.output, 1); - vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf); - vaDestroyConfig(r->va_dpy, r->vpp.ctx); - vaDestroyConfig(r->va_dpy, r->vpp.cfg); -} - -static int -setup_worker_thread(struct vaapi_recorder *r) -{ - pthread_mutex_init(&r->mutex, NULL); - pthread_cond_init(&r->input_cond, NULL); - pthread_create(&r->worker_thread, NULL, worker_thread_function, r); - - return 1; -} - -static void -destroy_worker_thread(struct vaapi_recorder *r) -{ - pthread_mutex_lock(&r->mutex); - - /* Make sure the worker thread finishes */ - r->destroying = 1; - pthread_cond_signal(&r->input_cond); - - pthread_mutex_unlock(&r->mutex); - - pthread_join(r->worker_thread, NULL); - - pthread_mutex_destroy(&r->mutex); - pthread_cond_destroy(&r->input_cond); -} - -struct vaapi_recorder * -vaapi_recorder_create(int drm_fd, int width, int height, const char *filename) -{ - struct vaapi_recorder *r; - VAStatus status; - int major, minor; - int flags; - - r = zalloc(sizeof *r); - if (r == NULL) - return NULL; - - r->width = width; - r->height = height; - r->drm_fd = drm_fd; - - if (setup_worker_thread(r) < 0) - goto err_free; - - flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC; - r->output_fd = open(filename, flags, 0644); - if (r->output_fd < 0) - goto err_thread; - - r->va_dpy = vaGetDisplayDRM(drm_fd); - if (!r->va_dpy) { - weston_log("failed to create VA display\n"); - goto err_fd; - } - - status = vaInitialize(r->va_dpy, &major, &minor); - if (status != VA_STATUS_SUCCESS) { - weston_log("vaapi: failed to initialize display\n"); - goto err_fd; - } - - if (setup_vpp(r) < 0) { - weston_log("vaapi: failed to initialize VPP pipeline\n"); - goto err_va_dpy; - } - - if (setup_encoder(r) < 0) { - goto err_vpp; - } - - return r; - -err_vpp: - vpp_destroy(r); -err_va_dpy: - vaTerminate(r->va_dpy); -err_fd: - close(r->output_fd); -err_thread: - destroy_worker_thread(r); -err_free: - free(r); - - return NULL; -} - -void -vaapi_recorder_destroy(struct vaapi_recorder *r) -{ - destroy_worker_thread(r); - - encoder_destroy(r); - vpp_destroy(r); - - vaTerminate(r->va_dpy); - - close(r->output_fd); - close(r->drm_fd); - - free(r); -} - -static VAStatus -create_surface_from_fd(struct vaapi_recorder *r, int prime_fd, - int stride, VASurfaceID *surface) -{ - VASurfaceAttrib va_attribs[2]; - VASurfaceAttribExternalBuffers va_attrib_extbuf; - VAStatus status; - - unsigned long buffer_fd = prime_fd; - - va_attrib_extbuf.pixel_format = VA_FOURCC_BGRX; - va_attrib_extbuf.width = r->width; - va_attrib_extbuf.height = r->height; - va_attrib_extbuf.data_size = r->height * stride; - va_attrib_extbuf.num_planes = 1; - va_attrib_extbuf.pitches[0] = stride; - va_attrib_extbuf.offsets[0] = 0; - va_attrib_extbuf.buffers = &buffer_fd; - va_attrib_extbuf.num_buffers = 1; - va_attrib_extbuf.flags = 0; - va_attrib_extbuf.private_data = NULL; - - va_attribs[0].type = VASurfaceAttribMemoryType; - va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; - va_attribs[0].value.type = VAGenericValueTypeInteger; - va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; - - va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor; - va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; - va_attribs[1].value.type = VAGenericValueTypePointer; - va_attribs[1].value.value.p = &va_attrib_extbuf; - - status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_RGB32, - r->width, r->height, surface, 1, - va_attribs, 2); - - return status; -} - -static VAStatus -convert_rgb_to_yuv(struct vaapi_recorder *r, VASurfaceID rgb_surface) -{ - VAProcPipelineParameterBuffer *pipeline_param; - VAStatus status; - - status = vaMapBuffer(r->va_dpy, r->vpp.pipeline_buf, - (void **) &pipeline_param); - if (status != VA_STATUS_SUCCESS) - return status; - - memset(pipeline_param, 0, sizeof *pipeline_param); - - pipeline_param->surface = rgb_surface; - pipeline_param->surface_color_standard = VAProcColorStandardNone; - - pipeline_param->output_background_color = 0xff000000; - pipeline_param->output_color_standard = VAProcColorStandardNone; - - status = vaUnmapBuffer(r->va_dpy, r->vpp.pipeline_buf); - if (status != VA_STATUS_SUCCESS) - return status; - - status = vaBeginPicture(r->va_dpy, r->vpp.ctx, r->vpp.output); - if (status != VA_STATUS_SUCCESS) - return status; - - status = vaRenderPicture(r->va_dpy, r->vpp.ctx, - &r->vpp.pipeline_buf, 1); - if (status != VA_STATUS_SUCCESS) - return status; - - status = vaEndPicture(r->va_dpy, r->vpp.ctx); - if (status != VA_STATUS_SUCCESS) - return status; - - return status; -} - -static void -recorder_frame(struct vaapi_recorder *r) -{ - VASurfaceID rgb_surface; - VAStatus status; - - status = create_surface_from_fd(r, r->input.prime_fd, - r->input.stride, &rgb_surface); - if (status != VA_STATUS_SUCCESS) { - weston_log("[libva recorder] " - "failed to create surface from bo\n"); - return; - } - - close(r->input.prime_fd); - - status = convert_rgb_to_yuv(r, rgb_surface); - if (status != VA_STATUS_SUCCESS) { - weston_log("[libva recorder] " - "color space conversion failed\n"); - return; - } - - encoder_encode(r, r->vpp.output); - - vaDestroySurfaces(r->va_dpy, &rgb_surface, 1); -} - -static void * -worker_thread_function(void *data) -{ - struct vaapi_recorder *r = data; - - pthread_mutex_lock(&r->mutex); - - while (!r->destroying) { - if (!r->input.valid) - pthread_cond_wait(&r->input_cond, &r->mutex); - - /* If the thread is awaken by destroy_worker_thread(), - * there might not be valid input */ - if (!r->input.valid) - continue; - - recorder_frame(r); - r->input.valid = 0; - } - - pthread_mutex_unlock(&r->mutex); - - return NULL; -} - -int -vaapi_recorder_frame(struct vaapi_recorder *r, int prime_fd, int stride) -{ - int ret = 0; - - pthread_mutex_lock(&r->mutex); - - if (r->error) { - errno = r->error; - ret = -1; - goto unlock; - } - - /* The mutex is never released while encoding, so this point should - * never be reached if input.valid is true. */ - assert(!r->input.valid); - - r->input.prime_fd = prime_fd; - r->input.stride = stride; - r->input.valid = 1; - pthread_cond_signal(&r->input_cond); - -unlock: - pthread_mutex_unlock(&r->mutex); - - return ret; -} diff --git a/libweston/backend-drm/vaapi-recorder.h b/libweston/backend-drm/vaapi-recorder.h deleted file mode 100644 index 6b194aa8..00000000 --- a/libweston/backend-drm/vaapi-recorder.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © 2013 Intel Corporation - * - * 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. - */ - -#ifndef _VAAPI_RECORDER_H_ -#define _VAAPI_RECORDER_H_ - -struct vaapi_recorder; - -struct vaapi_recorder * -vaapi_recorder_create(int drm_fd, int width, int height, const char *filename); -void -vaapi_recorder_destroy(struct vaapi_recorder *r); -int -vaapi_recorder_frame(struct vaapi_recorder *r, int fd, int stride); - -#endif /* _VAAPI_RECORDER_H_ */ diff --git a/libweston/backend-fbdev/fbdev.c b/libweston/backend-fbdev/fbdev.c deleted file mode 100644 index ae289cc0..00000000 --- a/libweston/backend-fbdev/fbdev.c +++ /dev/null @@ -1,994 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2012 Raspberry Pi Foundation - * Copyright © 2013 Philip Withnall - * - * 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 <errno.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdio.h> -#include <string.h> -#include <math.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <fcntl.h> -#include <unistd.h> -#include <linux/fb.h> -#include <linux/input.h> -#include <assert.h> - -#include <libudev.h> - -#include "shared/helpers.h" -#include <libweston/libweston.h> -#include <libweston/backend-fbdev.h> -#include "launcher-util.h" -#include "pixman-renderer.h" -#include "libinput-seat.h" -#include "presentation-time-server-protocol.h" - -struct fbdev_backend { - struct weston_backend base; - struct weston_compositor *compositor; - uint32_t prev_state; - - struct udev *udev; - struct udev_input input; - uint32_t output_transform; - struct wl_listener session_listener; -}; - -struct fbdev_screeninfo { - unsigned int x_resolution; /* pixels, visible area */ - unsigned int y_resolution; /* pixels, visible area */ - unsigned int width_mm; /* visible screen width in mm */ - unsigned int height_mm; /* visible screen height in mm */ - unsigned int bits_per_pixel; - - size_t buffer_length; /* length of frame buffer memory in bytes */ - size_t line_length; /* length of a line in bytes */ - char id[16]; /* screen identifier */ - - pixman_format_code_t pixel_format; /* frame buffer pixel format */ - unsigned int refresh_rate; /* Hertz */ -}; - -struct fbdev_head { - struct weston_head base; - - /* Frame buffer details. */ - char *device; - struct fbdev_screeninfo fb_info; -}; - -struct fbdev_output { - struct fbdev_backend *backend; - struct weston_output base; - - struct weston_mode mode; - struct wl_event_source *finish_frame_timer; - - /* framebuffer mmap details */ - size_t buffer_length; - void *fb; - - /* pixman details. */ - pixman_image_t *hw_surface; -}; - -static const char default_seat[] = "seat0"; - -static inline struct fbdev_head * -to_fbdev_head(struct weston_head *base) -{ - return container_of(base, struct fbdev_head, base); -} - -static inline struct fbdev_output * -to_fbdev_output(struct weston_output *base) -{ - return container_of(base, struct fbdev_output, base); -} - -static inline struct fbdev_backend * -to_fbdev_backend(struct weston_compositor *base) -{ - return container_of(base->backend, struct fbdev_backend, base); -} - -static struct fbdev_head * -fbdev_output_get_head(struct fbdev_output *output) -{ - if (wl_list_length(&output->base.head_list) != 1) - return NULL; - - return container_of(output->base.head_list.next, - struct fbdev_head, base.output_link); -} - -static int -fbdev_output_start_repaint_loop(struct weston_output *output) -{ - struct timespec ts; - - weston_compositor_read_presentation_clock(output->compositor, &ts); - weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID); - - return 0; -} - -static int -fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage, - void *repaint_data) -{ - struct fbdev_output *output = to_fbdev_output(base); - struct weston_compositor *ec = output->base.compositor; - - /* Repaint the damaged region onto the back buffer. */ - pixman_renderer_output_set_buffer(base, output->hw_surface); - ec->renderer->repaint_output(base, damage); - - /* Update the damage region. */ - pixman_region32_subtract(&ec->primary_plane.damage, - &ec->primary_plane.damage, damage); - - /* Schedule the end of the frame. We do not sync this to the frame - * buffer clock because users who want that should be using the DRM - * compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires - * panning, which is broken in most kernel drivers. - * - * Finish the frame synchronised to the specified refresh rate. The - * refresh rate is given in mHz and the interval in ms. */ - wl_event_source_timer_update(output->finish_frame_timer, - 1000000 / output->mode.refresh); - - return 0; -} - -static int -finish_frame_handler(void *data) -{ - struct fbdev_output *output = data; - struct timespec ts; - - weston_compositor_read_presentation_clock(output->base.compositor, &ts); - weston_output_finish_frame(&output->base, &ts, 0); - - return 1; -} - -static pixman_format_code_t -calculate_pixman_format(struct fb_var_screeninfo *vinfo, - struct fb_fix_screeninfo *finfo) -{ - /* Calculate the pixman format supported by the frame buffer from the - * buffer's metadata. Return 0 if no known pixman format is supported - * (since this has depth 0 it's guaranteed to not conflict with any - * actual pixman format). - * - * Documentation on the vinfo and finfo structures: - * http://www.mjmwired.net/kernel/Documentation/fb/api.txt - * - * TODO: Try a bit harder to support other formats, including setting - * the preferred format in the hardware. */ - int type; - - weston_log("Calculating pixman format from:\n" - STAMP_SPACE " - type: %i (aux: %i)\n" - STAMP_SPACE " - visual: %i\n" - STAMP_SPACE " - bpp: %i (grayscale: %i)\n" - STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n" - STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n" - STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n" - STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n", - finfo->type, finfo->type_aux, finfo->visual, - vinfo->bits_per_pixel, vinfo->grayscale, - vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right, - vinfo->green.offset, vinfo->green.length, - vinfo->green.msb_right, - vinfo->blue.offset, vinfo->blue.length, - vinfo->blue.msb_right, - vinfo->transp.offset, vinfo->transp.length, - vinfo->transp.msb_right); - - /* We only handle packed formats at the moment. */ - if (finfo->type != FB_TYPE_PACKED_PIXELS) - return 0; - - /* We only handle true-colour frame buffers at the moment. */ - switch(finfo->visual) { - case FB_VISUAL_TRUECOLOR: - case FB_VISUAL_DIRECTCOLOR: - if (vinfo->grayscale != 0) - return 0; - break; - default: - return 0; - } - - /* We only support formats with MSBs on the left. */ - if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 || - vinfo->blue.msb_right != 0) - return 0; - - /* Work out the format type from the offsets. We only support RGBA, ARGB - * and ABGR at the moment. */ - type = PIXMAN_TYPE_OTHER; - - if ((vinfo->transp.offset >= vinfo->red.offset || - vinfo->transp.length == 0) && - vinfo->red.offset >= vinfo->green.offset && - vinfo->green.offset >= vinfo->blue.offset) - type = PIXMAN_TYPE_ARGB; - else if (vinfo->red.offset >= vinfo->green.offset && - vinfo->green.offset >= vinfo->blue.offset && - vinfo->blue.offset >= vinfo->transp.offset) - type = PIXMAN_TYPE_RGBA; - else if (vinfo->transp.offset >= vinfo->blue.offset && - vinfo->blue.offset >= vinfo->green.offset && - vinfo->green.offset >= vinfo->red.offset) - type = PIXMAN_TYPE_ABGR; - - if (type == PIXMAN_TYPE_OTHER) - return 0; - - /* Build the format. */ - return PIXMAN_FORMAT(vinfo->bits_per_pixel, type, - vinfo->transp.length, - vinfo->red.length, - vinfo->green.length, - vinfo->blue.length); -} - -static int -calculate_refresh_rate(struct fb_var_screeninfo *vinfo) -{ - uint64_t quot; - - /* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */ - quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres); - quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres); - quot *= vinfo->pixclock; - - if (quot > 0) { - uint64_t refresh_rate; - - refresh_rate = 1000000000000000LLU / quot; - if (refresh_rate > 200000) - refresh_rate = 200000; /* cap at 200 Hz */ - - if (refresh_rate >= 1000) /* at least 1 Hz */ - return refresh_rate; - } - - return 60 * 1000; /* default to 60 Hz */ -} - -static int -fbdev_query_screen_info(int fd, struct fbdev_screeninfo *info) -{ - struct fb_var_screeninfo varinfo; - struct fb_fix_screeninfo fixinfo; - - /* Probe the device for screen information. */ - if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 || - ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) { - return -1; - } - - /* Store the pertinent data. */ - info->x_resolution = varinfo.xres; - info->y_resolution = varinfo.yres; - info->width_mm = varinfo.width; - info->height_mm = varinfo.height; - info->bits_per_pixel = varinfo.bits_per_pixel; - - info->buffer_length = fixinfo.smem_len; - info->line_length = fixinfo.line_length; - strncpy(info->id, fixinfo.id, sizeof(info->id)); - info->id[sizeof(info->id)-1] = '\0'; - - info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo); - info->refresh_rate = calculate_refresh_rate(&varinfo); - - if (info->pixel_format == 0) { - weston_log("Frame buffer uses an unsupported format.\n"); - return -1; - } - - return 1; -} - -static int -fbdev_set_screen_info(int fd, struct fbdev_screeninfo *info) -{ - struct fb_var_screeninfo varinfo; - - /* Grab the current screen information. */ - if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) { - return -1; - } - - /* Update the information. */ - varinfo.xres = info->x_resolution; - varinfo.yres = info->y_resolution; - varinfo.width = info->width_mm; - varinfo.height = info->height_mm; - varinfo.bits_per_pixel = info->bits_per_pixel; - - /* Try to set up an ARGB (x8r8g8b8) pixel format. */ - varinfo.grayscale = 0; - varinfo.transp.offset = 24; - varinfo.transp.length = 0; - varinfo.transp.msb_right = 0; - varinfo.red.offset = 16; - varinfo.red.length = 8; - varinfo.red.msb_right = 0; - varinfo.green.offset = 8; - varinfo.green.length = 8; - varinfo.green.msb_right = 0; - varinfo.blue.offset = 0; - varinfo.blue.length = 8; - varinfo.blue.msb_right = 0; - - /* Set the device's screen information. */ - if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) { - return -1; - } - - return 1; -} - -static int -fbdev_wakeup_screen(int fd, struct fbdev_screeninfo *info) -{ - struct fb_var_screeninfo varinfo; - - /* Grab the current screen information. */ - if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) { - return -1; - } - - /* force the framebuffer to wake up */ - varinfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; - - /* Set the device's screen information. */ - if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) { - return -1; - } - - return 1; -} - -/* Returns an FD for the frame buffer device. */ -static int -fbdev_frame_buffer_open(const char *fb_dev, - struct fbdev_screeninfo *screen_info) -{ - int fd = -1; - - weston_log("Opening fbdev frame buffer.\n"); - - /* Open the frame buffer device. */ - fd = open(fb_dev, O_RDWR | O_CLOEXEC); - if (fd < 0) { - weston_log("Failed to open frame buffer device ‘%s’: %s\n", - fb_dev, strerror(errno)); - return -1; - } - - /* Grab the screen info. */ - if (fbdev_query_screen_info(fd, screen_info) < 0) { - weston_log("Failed to get frame buffer info: %s\n", - strerror(errno)); - - close(fd); - return -1; - } - - /* Attempt to wake up the framebuffer device, needed for secondary - * framebuffer devices */ - if (fbdev_wakeup_screen(fd, screen_info) < 0) { - weston_log("Failed to activate framebuffer display. " - "Attempting to open output anyway.\n"); - } - - - return fd; -} - -/* Closes the FD on success or failure. */ -static int -fbdev_frame_buffer_map(struct fbdev_output *output, int fd) -{ - struct fbdev_head *head; - int retval = -1; - - head = fbdev_output_get_head(output); - - weston_log("Mapping fbdev frame buffer.\n"); - - /* Map the frame buffer. Write-only mode, since we don't want to read - * anything back (because it's slow). */ - output->buffer_length = head->fb_info.buffer_length; - output->fb = mmap(NULL, output->buffer_length, - PROT_WRITE, MAP_SHARED, fd, 0); - if (output->fb == MAP_FAILED) { - weston_log("Failed to mmap frame buffer: %s\n", - strerror(errno)); - output->fb = NULL; - goto out_close; - } - - /* Create a pixman image to wrap the memory mapped frame buffer. */ - output->hw_surface = - pixman_image_create_bits(head->fb_info.pixel_format, - head->fb_info.x_resolution, - head->fb_info.y_resolution, - output->fb, - head->fb_info.line_length); - if (output->hw_surface == NULL) { - weston_log("Failed to create surface for frame buffer.\n"); - goto out_unmap; - } - - /* Success! */ - retval = 0; - -out_unmap: - if (retval != 0 && output->fb != NULL) { - munmap(output->fb, output->buffer_length); - output->fb = NULL; - } - -out_close: - if (fd >= 0) - close(fd); - - return retval; -} - -static void -fbdev_frame_buffer_unmap(struct fbdev_output *output) -{ - if (!output->fb) { - assert(!output->hw_surface); - return; - } - - weston_log("Unmapping fbdev frame buffer.\n"); - - if (output->hw_surface) - pixman_image_unref(output->hw_surface); - output->hw_surface = NULL; - - if (munmap(output->fb, output->buffer_length) < 0) - weston_log("Failed to munmap frame buffer: %s\n", - strerror(errno)); - - output->fb = NULL; -} - - -static int -fbdev_output_attach_head(struct weston_output *output_base, - struct weston_head *head_base) -{ - struct fbdev_output *output = to_fbdev_output(output_base); - struct fbdev_head *head = to_fbdev_head(head_base); - - /* Clones not supported. */ - if (!wl_list_empty(&output->base.head_list)) - return -1; - - /* only one static mode in list */ - output->mode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; - output->mode.width = head->fb_info.x_resolution; - output->mode.height = head->fb_info.y_resolution; - output->mode.refresh = head->fb_info.refresh_rate; - wl_list_init(&output->base.mode_list); - wl_list_insert(&output->base.mode_list, &output->mode.link); - output->base.current_mode = &output->mode; - - return 0; -} - -static void fbdev_output_destroy(struct weston_output *base); - -static int -fbdev_output_enable(struct weston_output *base) -{ - struct fbdev_output *output = to_fbdev_output(base); - struct fbdev_backend *backend = to_fbdev_backend(base->compositor); - struct fbdev_head *head; - int fb_fd; - struct wl_event_loop *loop; - - head = fbdev_output_get_head(output); - - /* Create the frame buffer. */ - fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info); - if (fb_fd < 0) { - weston_log("Creating frame buffer failed.\n"); - return -1; - } - - if (fbdev_frame_buffer_map(output, fb_fd) < 0) { - weston_log("Mapping frame buffer failed.\n"); - return -1; - } - - output->base.start_repaint_loop = fbdev_output_start_repaint_loop; - output->base.repaint = fbdev_output_repaint; - - if (pixman_renderer_output_create(&output->base, - PIXMAN_RENDERER_OUTPUT_USE_SHADOW) < 0) - goto out_hw_surface; - - loop = wl_display_get_event_loop(backend->compositor->wl_display); - output->finish_frame_timer = - wl_event_loop_add_timer(loop, finish_frame_handler, output); - - weston_log("fbdev output %d×%d px\n", - output->mode.width, output->mode.height); - weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n", - output->mode.refresh / 1000); - - return 0; - -out_hw_surface: - fbdev_frame_buffer_unmap(output); - - return -1; -} - -static int -fbdev_output_disable(struct weston_output *base) -{ - struct fbdev_output *output = to_fbdev_output(base); - - if (!base->enabled) - return 0; - - wl_event_source_remove(output->finish_frame_timer); - output->finish_frame_timer = NULL; - - pixman_renderer_output_destroy(&output->base); - fbdev_frame_buffer_unmap(output); - - return 0; -} - -static struct fbdev_head * -fbdev_head_create(struct fbdev_backend *backend, const char *device) -{ - struct fbdev_head *head; - int fb_fd; - - head = zalloc(sizeof *head); - if (!head) - return NULL; - - head->device = strdup(device); - - /* Create the frame buffer. */ - fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info); - if (fb_fd < 0) { - weston_log("Creating frame buffer head failed.\n"); - goto out_free; - } - close(fb_fd); - - weston_head_init(&head->base, "fbdev"); - weston_head_set_connection_status(&head->base, true); - weston_head_set_monitor_strings(&head->base, "unknown", - head->fb_info.id, NULL); - weston_head_set_subpixel(&head->base, WL_OUTPUT_SUBPIXEL_UNKNOWN); - weston_head_set_physical_size(&head->base, head->fb_info.width_mm, - head->fb_info.height_mm); - - weston_compositor_add_head(backend->compositor, &head->base); - - weston_log("Created head '%s' for device %s (%s)\n", - head->base.name, head->device, head->base.model); - - return head; - -out_free: - free(head->device); - free(head); - - return NULL; -} - -static void -fbdev_head_destroy(struct fbdev_head *head) -{ - weston_head_release(&head->base); - free(head->device); - free(head); -} - -static struct weston_output * -fbdev_output_create(struct weston_compositor *compositor, - const char *name) -{ - struct fbdev_output *output; - - weston_log("Creating fbdev output.\n"); - - output = zalloc(sizeof *output); - if (output == NULL) - return NULL; - - output->backend = to_fbdev_backend(compositor); - - weston_output_init(&output->base, compositor, name); - - output->base.destroy = fbdev_output_destroy; - output->base.disable = fbdev_output_disable; - output->base.enable = fbdev_output_enable; - output->base.attach_head = fbdev_output_attach_head; - - weston_compositor_add_pending_output(&output->base, compositor); - - return &output->base; -} - -static void -fbdev_output_destroy(struct weston_output *base) -{ - struct fbdev_output *output = to_fbdev_output(base); - - weston_log("Destroying fbdev output.\n"); - - fbdev_output_disable(base); - - /* Remove the output. */ - weston_output_release(&output->base); - - free(output); -} - -/* strcmp()-style return values. */ -static int -compare_screen_info (const struct fbdev_screeninfo *a, - const struct fbdev_screeninfo *b) -{ - if (a->x_resolution == b->x_resolution && - a->y_resolution == b->y_resolution && - a->width_mm == b->width_mm && - a->height_mm == b->height_mm && - a->bits_per_pixel == b->bits_per_pixel && - a->pixel_format == b->pixel_format && - a->refresh_rate == b->refresh_rate) - return 0; - - return 1; -} - -static int -fbdev_output_reenable(struct fbdev_backend *backend, - struct weston_output *base) -{ - struct fbdev_output *output = to_fbdev_output(base); - struct fbdev_head *head; - struct fbdev_screeninfo new_screen_info; - int fb_fd; - - head = fbdev_output_get_head(output); - - weston_log("Re-enabling fbdev output.\n"); - assert(output->base.enabled); - - /* Create the frame buffer. */ - fb_fd = fbdev_frame_buffer_open(head->device, &new_screen_info); - if (fb_fd < 0) { - weston_log("Creating frame buffer failed.\n"); - return -1; - } - - /* Check whether the frame buffer details have changed since we were - * disabled. */ - if (compare_screen_info(&head->fb_info, &new_screen_info) != 0) { - /* Perform a mode-set to restore the old mode. */ - if (fbdev_set_screen_info(fb_fd, &head->fb_info) < 0) { - weston_log("Failed to restore mode settings. " - "Attempting to re-open output anyway.\n"); - } - - close(fb_fd); - - /* Disable and enable the output so that resources depending on - * the frame buffer X/Y resolution (such as the shadow buffer) - * are re-initialised. */ - fbdev_output_disable(&output->base); - return fbdev_output_enable(&output->base); - } - - /* Map the device if it has the same details as before. */ - if (fbdev_frame_buffer_map(output, fb_fd) < 0) { - weston_log("Mapping frame buffer failed.\n"); - return -1; - } - - return 0; -} - -static void -fbdev_backend_destroy(struct weston_compositor *base) -{ - struct fbdev_backend *backend = to_fbdev_backend(base); - struct weston_head *head, *next; - - udev_input_destroy(&backend->input); - - /* Destroy the output. */ - weston_compositor_shutdown(base); - - wl_list_for_each_safe(head, next, &base->head_list, compositor_link) - fbdev_head_destroy(to_fbdev_head(head)); - - /* Chain up. */ - weston_launcher_destroy(base->launcher); - - udev_unref(backend->udev); - - free(backend); -} - -static void -session_notify(struct wl_listener *listener, void *data) -{ - struct weston_compositor *compositor = data; - struct fbdev_backend *backend = to_fbdev_backend(compositor); - struct weston_output *output; - - if (compositor->session_active) { - weston_log("entering VT\n"); - compositor->state = backend->prev_state; - - wl_list_for_each(output, &compositor->output_list, link) { - fbdev_output_reenable(backend, output); - } - - weston_compositor_damage_all(compositor); - - udev_input_enable(&backend->input); - } else { - weston_log("leaving VT\n"); - udev_input_disable(&backend->input); - - wl_list_for_each(output, &compositor->output_list, link) { - fbdev_frame_buffer_unmap(to_fbdev_output(output)); - } - - backend->prev_state = compositor->state; - weston_compositor_offscreen(compositor); - - /* If we have a repaint scheduled (from the idle handler), make - * sure we cancel that so we don't try to pageflip when we're - * vt switched away. The OFFSCREEN state will prevent - * further attempts at repainting. When we switch - * back, we schedule a repaint, which will process - * pending frame callbacks. */ - - wl_list_for_each(output, - &compositor->output_list, link) { - output->repaint_needed = false; - } - } -} - -static char * -find_framebuffer_device(struct fbdev_backend *b, const char *seat) -{ - struct udev_enumerate *e; - struct udev_list_entry *entry; - const char *path, *device_seat, *id; - char *fb_device_path = NULL; - struct udev_device *device, *fb_device, *pci; - - e = udev_enumerate_new(b->udev); - udev_enumerate_add_match_subsystem(e, "graphics"); - udev_enumerate_add_match_sysname(e, "fb[0-9]*"); - - udev_enumerate_scan_devices(e); - fb_device = NULL; - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { - bool is_boot_vga = false; - - path = udev_list_entry_get_name(entry); - device = udev_device_new_from_syspath(b->udev, path); - if (!device) - continue; - device_seat = udev_device_get_property_value(device, "ID_SEAT"); - if (!device_seat) - device_seat = default_seat; - if (strcmp(device_seat, seat)) { - udev_device_unref(device); - continue; - } - - pci = udev_device_get_parent_with_subsystem_devtype(device, - "pci", NULL); - if (pci) { - id = udev_device_get_sysattr_value(pci, "boot_vga"); - if (id && !strcmp(id, "1")) - is_boot_vga = true; - } - - /* If a framebuffer device was found, and this device isn't - * the boot-VGA device, don't use it. */ - if (!is_boot_vga && fb_device) { - udev_device_unref(device); - continue; - } - - /* There can only be one boot_vga device. Try to use it - * at all costs. */ - if (is_boot_vga) { - if (fb_device) - udev_device_unref(fb_device); - fb_device = device; - break; - } - - /* Per the (!is_boot_vga && fb_device) test above, only - * trump existing saved devices with boot-VGA devices, so if - * the test ends up here, this must be the first device seen. */ - assert(!fb_device); - fb_device = device; - } - - udev_enumerate_unref(e); - - if (fb_device) { - fb_device_path = strdup(udev_device_get_devnode(fb_device)); - udev_device_unref(fb_device); - } - - return fb_device_path; -} - -static struct fbdev_backend * -fbdev_backend_create(struct weston_compositor *compositor, - struct weston_fbdev_backend_config *param) -{ - struct fbdev_backend *backend; - const char *seat_id = default_seat; - const char *session_seat; - - session_seat = getenv("XDG_SEAT"); - if (session_seat) - seat_id = session_seat; - if (param->seat_id) - seat_id = param->seat_id; - - weston_log("initializing fbdev backend\n"); - - backend = zalloc(sizeof *backend); - if (backend == NULL) - return NULL; - - backend->compositor = compositor; - compositor->backend = &backend->base; - if (weston_compositor_set_presentation_clock_software( - compositor) < 0) - goto out_compositor; - - backend->udev = udev_new(); - if (backend->udev == NULL) { - weston_log("Failed to initialize udev context.\n"); - goto out_compositor; - } - - if (!param->device) - param->device = find_framebuffer_device(backend, seat_id); - if (!param->device) { - weston_log("fatal: no framebuffer devices detected.\n"); - goto out_udev; - } - - /* Set up the TTY. */ - backend->session_listener.notify = session_notify; - wl_signal_add(&compositor->session_signal, - &backend->session_listener); - compositor->launcher = - weston_launcher_connect(compositor, param->tty, seat_id, false); - if (!compositor->launcher) { - weston_log("fatal: fbdev backend should be run using " - "weston-launch binary, or your system should " - "provide the logind D-Bus API.\n"); - goto out_udev; - } - - backend->base.destroy = fbdev_backend_destroy; - backend->base.create_output = fbdev_output_create; - - backend->prev_state = WESTON_COMPOSITOR_ACTIVE; - - weston_setup_vt_switch_bindings(compositor); - - if (pixman_renderer_init(compositor) < 0) - goto out_launcher; - - if (!fbdev_head_create(backend, param->device)) - goto out_launcher; - - free(param->device); - - udev_input_init(&backend->input, compositor, backend->udev, - seat_id, param->configure_device); - - return backend; - -out_launcher: - free(param->device); - weston_launcher_destroy(compositor->launcher); - -out_udev: - udev_unref(backend->udev); - -out_compositor: - weston_compositor_shutdown(compositor); - free(backend); - - return NULL; -} - -static void -config_init_to_defaults(struct weston_fbdev_backend_config *config) -{ - config->tty = 0; /* default to current tty */ - config->device = NULL; - config->seat_id = NULL; -} - -WL_EXPORT int -weston_backend_init(struct weston_compositor *compositor, - struct weston_backend_config *config_base) -{ - struct fbdev_backend *b; - struct weston_fbdev_backend_config config = {{ 0, }}; - - if (config_base == NULL || - config_base->struct_version != WESTON_FBDEV_BACKEND_CONFIG_VERSION || - config_base->struct_size > sizeof(struct weston_fbdev_backend_config)) { - weston_log("fbdev backend config structure is invalid\n"); - return -1; - } - - config_init_to_defaults(&config); - memcpy(&config, config_base, config_base->struct_size); - - b = fbdev_backend_create(compositor, &config); - if (b == NULL) - return -1; - return 0; -} diff --git a/libweston/backend-fbdev/meson.build b/libweston/backend-fbdev/meson.build deleted file mode 100644 index e7b1544c..00000000 --- a/libweston/backend-fbdev/meson.build +++ /dev/null @@ -1,30 +0,0 @@ -if not get_option('backend-fbdev') - subdir_done() -endif - -config_h.set('BUILD_FBDEV_COMPOSITOR', '1') - -srcs_fbdev = [ - 'fbdev.c', - presentation_time_server_protocol_h, -] - -deps_fbdev = [ - dep_libweston_private, - dep_session_helper, - dep_libinput_backend, - dependency('libudev', version: '>= 136'), -] - -plugin_fbdev = shared_library( - 'fbdev-backend', - srcs_fbdev, - include_directories: common_inc, - dependencies: deps_fbdev, - name_prefix: '', - install: true, - install_dir: dir_module_libweston -) -env_modmap += 'fbdev-backend.so=@0@;'.format(plugin_fbdev.full_path()) - -install_headers(backend_fbdev_h, subdir: dir_include_libweston_install) diff --git a/libweston/backend-headless/headless.c b/libweston/backend-headless/headless.c deleted file mode 100644 index c98bdc24..00000000 --- a/libweston/backend-headless/headless.c +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Copyright © 2010-2011 Benjamin Franzke - * Copyright © 2012 Intel Corporation - * - * 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 <assert.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <sys/time.h> -#include <stdbool.h> -#include <drm_fourcc.h> - -#include <libweston/libweston.h> -#include <libweston/backend-headless.h> -#include "shared/helpers.h" -#include "linux-explicit-synchronization.h" -#include "pixman-renderer.h" -#include "renderer-gl/gl-renderer.h" -#include "shared/weston-egl-ext.h" -#include "linux-dmabuf.h" -#include "presentation-time-server-protocol.h" -#include <libweston/windowed-output-api.h> - -enum headless_renderer_type { - HEADLESS_NOOP, - HEADLESS_PIXMAN, - HEADLESS_GL, -}; - -struct headless_backend { - struct weston_backend base; - struct weston_compositor *compositor; - - struct weston_seat fake_seat; - enum headless_renderer_type renderer_type; - - struct gl_renderer_interface *glri; -}; - -struct headless_head { - struct weston_head base; -}; - -struct headless_output { - struct weston_output base; - - struct weston_mode mode; - struct wl_event_source *finish_frame_timer; - uint32_t *image_buf; - pixman_image_t *image; -}; - -static const uint32_t headless_formats[] = { - DRM_FORMAT_XRGB8888, - DRM_FORMAT_ARGB8888, -}; - -static inline struct headless_head * -to_headless_head(struct weston_head *base) -{ - return container_of(base, struct headless_head, base); -} - -static inline struct headless_output * -to_headless_output(struct weston_output *base) -{ - return container_of(base, struct headless_output, base); -} - -static inline struct headless_backend * -to_headless_backend(struct weston_compositor *base) -{ - return container_of(base->backend, struct headless_backend, base); -} - -static int -headless_output_start_repaint_loop(struct weston_output *output) -{ - struct timespec ts; - - weston_compositor_read_presentation_clock(output->compositor, &ts); - weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID); - - return 0; -} - -static int -finish_frame_handler(void *data) -{ - struct headless_output *output = data; - struct timespec ts; - - weston_compositor_read_presentation_clock(output->base.compositor, &ts); - weston_output_finish_frame(&output->base, &ts, 0); - - return 1; -} - -static int -headless_output_repaint(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) -{ - struct headless_output *output = to_headless_output(output_base); - struct weston_compositor *ec = output->base.compositor; - - ec->renderer->repaint_output(&output->base, damage); - - pixman_region32_subtract(&ec->primary_plane.damage, - &ec->primary_plane.damage, damage); - - wl_event_source_timer_update(output->finish_frame_timer, 16); - - return 0; -} - -static void -headless_output_disable_gl(struct headless_output *output) -{ - struct weston_compositor *compositor = output->base.compositor; - struct headless_backend *b = to_headless_backend(compositor); - - b->glri->output_destroy(&output->base); -} - -static void -headless_output_disable_pixman(struct headless_output *output) -{ - pixman_renderer_output_destroy(&output->base); - pixman_image_unref(output->image); - free(output->image_buf); -} - -static int -headless_output_disable(struct weston_output *base) -{ - struct headless_output *output = to_headless_output(base); - struct headless_backend *b = to_headless_backend(base->compositor); - - if (!output->base.enabled) - return 0; - - wl_event_source_remove(output->finish_frame_timer); - - switch (b->renderer_type) { - case HEADLESS_GL: - headless_output_disable_gl(output); - break; - case HEADLESS_PIXMAN: - headless_output_disable_pixman(output); - break; - case HEADLESS_NOOP: - break; - } - - return 0; -} - -static void -headless_output_destroy(struct weston_output *base) -{ - struct headless_output *output = to_headless_output(base); - - headless_output_disable(&output->base); - weston_output_release(&output->base); - - free(output); -} - -static int -headless_output_enable_gl(struct headless_output *output) -{ - struct weston_compositor *compositor = output->base.compositor; - struct headless_backend *b = to_headless_backend(compositor); - - if (b->glri->output_pbuffer_create(&output->base, - output->base.current_mode->width, - output->base.current_mode->height, - headless_formats, - ARRAY_LENGTH(headless_formats)) < 0) { - weston_log("failed to create gl renderer output state\n"); - return -1; - } - - return 0; -} - -static int -headless_output_enable_pixman(struct headless_output *output) -{ - output->image_buf = malloc(output->base.current_mode->width * - output->base.current_mode->height * 4); - if (!output->image_buf) - return -1; - - output->image = pixman_image_create_bits(PIXMAN_x8r8g8b8, - output->base.current_mode->width, - output->base.current_mode->height, - output->image_buf, - output->base.current_mode->width * 4); - - if (pixman_renderer_output_create(&output->base, - PIXMAN_RENDERER_OUTPUT_USE_SHADOW) < 0) - goto err_renderer; - - pixman_renderer_output_set_buffer(&output->base, output->image); - - return 0; - -err_renderer: - pixman_image_unref(output->image); - free(output->image_buf); - - return -1; -} - -static int -headless_output_enable(struct weston_output *base) -{ - struct headless_output *output = to_headless_output(base); - struct headless_backend *b = to_headless_backend(base->compositor); - struct wl_event_loop *loop; - int ret = 0; - - loop = wl_display_get_event_loop(b->compositor->wl_display); - output->finish_frame_timer = - wl_event_loop_add_timer(loop, finish_frame_handler, output); - - switch (b->renderer_type) { - case HEADLESS_GL: - ret = headless_output_enable_gl(output); - break; - case HEADLESS_PIXMAN: - ret = headless_output_enable_pixman(output); - break; - case HEADLESS_NOOP: - break; - } - - if (ret < 0) { - wl_event_source_remove(output->finish_frame_timer); - return -1; - } - - return 0; -} - -static int -headless_output_set_size(struct weston_output *base, - int width, int height) -{ - struct headless_output *output = to_headless_output(base); - struct weston_head *head; - int output_width, output_height; - - /* We can only be called once. */ - assert(!output->base.current_mode); - - /* Make sure we have scale set. */ - assert(output->base.scale); - - wl_list_for_each(head, &output->base.head_list, output_link) { - weston_head_set_monitor_strings(head, "weston", "headless", - NULL); - - /* XXX: Calculate proper size. */ - weston_head_set_physical_size(head, width, height); - } - - output_width = width * output->base.scale; - output_height = height * output->base.scale; - - output->mode.flags = - WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; - output->mode.width = output_width; - output->mode.height = output_height; - output->mode.refresh = 60000; - wl_list_insert(&output->base.mode_list, &output->mode.link); - - output->base.current_mode = &output->mode; - - output->base.start_repaint_loop = headless_output_start_repaint_loop; - output->base.repaint = headless_output_repaint; - output->base.assign_planes = NULL; - output->base.set_backlight = NULL; - output->base.set_dpms = NULL; - output->base.switch_mode = NULL; - - return 0; -} - -static struct weston_output * -headless_output_create(struct weston_compositor *compositor, const char *name) -{ - struct headless_output *output; - - /* name can't be NULL. */ - assert(name); - - output = zalloc(sizeof *output); - if (!output) - return NULL; - - weston_output_init(&output->base, compositor, name); - - output->base.destroy = headless_output_destroy; - output->base.disable = headless_output_disable; - output->base.enable = headless_output_enable; - output->base.attach_head = NULL; - - weston_compositor_add_pending_output(&output->base, compositor); - - return &output->base; -} - -static int -headless_head_create(struct weston_compositor *compositor, - const char *name) -{ - struct headless_head *head; - - /* name can't be NULL. */ - assert(name); - - head = zalloc(sizeof *head); - if (head == NULL) - return -1; - - weston_head_init(&head->base, name); - weston_head_set_connection_status(&head->base, true); - - /* Ideally all attributes of the head would be set here, so that the - * user has all the information when deciding to create outputs. - * We do not have those until set_size() time through. - */ - - weston_compositor_add_head(compositor, &head->base); - - return 0; -} - -static void -headless_head_destroy(struct headless_head *head) -{ - weston_head_release(&head->base); - free(head); -} - -static void -headless_destroy(struct weston_compositor *ec) -{ - struct headless_backend *b = to_headless_backend(ec); - struct weston_head *base, *next; - - weston_compositor_shutdown(ec); - - wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) - headless_head_destroy(to_headless_head(base)); - - free(b); -} - -static int -headless_gl_renderer_init(struct headless_backend *b) -{ - b->glri = weston_load_module("gl-renderer.so", "gl_renderer_interface"); - if (!b->glri) - return -1; - - if (b->glri->display_create(b->compositor, - EGL_PLATFORM_SURFACELESS_MESA, - EGL_DEFAULT_DISPLAY, - EGL_PBUFFER_BIT, - headless_formats, - ARRAY_LENGTH(headless_formats)) < 0) { - return -1; - } - - return 0; -} - -static const struct weston_windowed_output_api api = { - headless_output_set_size, - headless_head_create, -}; - -static struct headless_backend * -headless_backend_create(struct weston_compositor *compositor, - struct weston_headless_backend_config *config) -{ - struct headless_backend *b; - int ret; - - b = zalloc(sizeof *b); - if (b == NULL) - return NULL; - - b->compositor = compositor; - compositor->backend = &b->base; - - if (weston_compositor_set_presentation_clock_software(compositor) < 0) - goto err_free; - - b->base.destroy = headless_destroy; - b->base.create_output = headless_output_create; - - if (config->use_pixman && config->use_gl) { - weston_log("Error: cannot use both Pixman *and* GL renderers.\n"); - goto err_free; - } - - if (config->use_gl) - b->renderer_type = HEADLESS_GL; - else if (config->use_pixman) - b->renderer_type = HEADLESS_PIXMAN; - else - b->renderer_type = HEADLESS_NOOP; - - switch (b->renderer_type) { - case HEADLESS_GL: - ret = headless_gl_renderer_init(b); - break; - case HEADLESS_PIXMAN: - ret = pixman_renderer_init(compositor); - break; - case HEADLESS_NOOP: - ret = noop_renderer_init(compositor); - break; - } - - if (ret < 0) - goto err_input; - - if (compositor->renderer->import_dmabuf) { - if (linux_dmabuf_setup(compositor) < 0) { - weston_log("Error: dmabuf protocol setup failed.\n"); - goto err_input; - } - } - - /* Support zwp_linux_explicit_synchronization_unstable_v1 to enable - * testing. */ - if (linux_explicit_synchronization_setup(compositor) < 0) - goto err_input; - - ret = weston_plugin_api_register(compositor, WESTON_WINDOWED_OUTPUT_API_NAME, - &api, sizeof(api)); - - if (ret < 0) { - weston_log("Failed to register output API.\n"); - goto err_input; - } - - return b; - -err_input: - weston_compositor_shutdown(compositor); -err_free: - free(b); - return NULL; -} - -static void -config_init_to_defaults(struct weston_headless_backend_config *config) -{ -} - -WL_EXPORT int -weston_backend_init(struct weston_compositor *compositor, - struct weston_backend_config *config_base) -{ - struct headless_backend *b; - struct weston_headless_backend_config config = {{ 0, }}; - - if (config_base == NULL || - config_base->struct_version != WESTON_HEADLESS_BACKEND_CONFIG_VERSION || - config_base->struct_size > sizeof(struct weston_headless_backend_config)) { - weston_log("headless backend config structure is invalid\n"); - return -1; - } - - config_init_to_defaults(&config); - memcpy(&config, config_base, config_base->struct_size); - - b = headless_backend_create(compositor, &config); - if (b == NULL) - return -1; - - return 0; -} diff --git a/libweston/backend-headless/meson.build b/libweston/backend-headless/meson.build deleted file mode 100644 index c603bb0b..00000000 --- a/libweston/backend-headless/meson.build +++ /dev/null @@ -1,21 +0,0 @@ -if not get_option('backend-headless') - subdir_done() -endif - -config_h.set('BUILD_HEADLESS_COMPOSITOR', '1') - -srcs_headless = [ - 'headless.c', - presentation_time_server_protocol_h, -] -plugin_headless = shared_library( - 'headless-backend', - srcs_headless, - include_directories: common_inc, - dependencies: [ dep_libweston_private, dep_libdrm_headers ], - name_prefix: '', - install: true, - install_dir: dir_module_libweston, -) -env_modmap += 'headless-backend.so=@0@;'.format(plugin_headless.full_path()) -install_headers(backend_headless_h, subdir: dir_include_libweston_install) diff --git a/libweston/backend-rdp/meson.build b/libweston/backend-rdp/meson.build deleted file mode 100644 index 237cd0a7..00000000 --- a/libweston/backend-rdp/meson.build +++ /dev/null @@ -1,39 +0,0 @@ -if not get_option('backend-rdp') - subdir_done() -endif - -config_h.set('BUILD_RDP_COMPOSITOR', '1') - -dep_frdp = dependency('freerdp2', version: '>= 2.0.0', required: false) -if not dep_frdp.found() - error('RDP-backend requires freerdp2 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') -endif - -if cc.has_header('freerdp/version.h', dependencies: dep_frdp) - config_h.set('HAVE_FREERDP_VERSION_H', '1') -endif - -if cc.has_member( - 'SURFACE_BITS_COMMAND', 'bmp', - dependencies : dep_frdp, - prefix : '#include <freerdp/update.h>' -) - config_h.set('HAVE_SURFACE_BITS_BMP', '1') -endif - -deps_rdp = [ - dep_libweston_private, - dep_frdp, -] -plugin_rdp = shared_library( - 'rdp-backend', - 'rdp.c', - include_directories: common_inc, - dependencies: deps_rdp, - name_prefix: '', - override_options: [ 'b_lundef=false' ], - install: true, - install_dir: dir_module_libweston -) -env_modmap += 'rdp-backend.so=@0@;'.format(plugin_rdp.full_path()) -install_headers(backend_rdp_h, subdir: dir_include_libweston_install) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c deleted file mode 100644 index ce91cedd..00000000 --- a/libweston/backend-rdp/rdp.c +++ /dev/null @@ -1,1501 +0,0 @@ -/* - * Copyright © 2013 Hardening <rdp.effort@gmail.com> - * - * 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 <assert.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <linux/input.h> - -#if HAVE_FREERDP_VERSION_H -#include <freerdp/version.h> -#else -/* assume it's a early 1.1 version */ -#define FREERDP_VERSION_MAJOR 1 -#define FREERDP_VERSION_MINOR 1 -#define FREERDP_VERSION_REVISION 0 -#endif - -#define FREERDP_VERSION_NUMBER ((FREERDP_VERSION_MAJOR * 0x10000) + \ - (FREERDP_VERSION_MINOR * 0x100) + FREERDP_VERSION_REVISION) - - -#if FREERDP_VERSION_NUMBER >= 0x10201 -#define HAVE_SKIP_COMPRESSION -#endif - -#if FREERDP_VERSION_NUMBER < 0x10202 -# define FREERDP_CB_RET_TYPE void -# define FREERDP_CB_RETURN(V) return -# define NSC_RESET(C, W, H) -# define RFX_RESET(C, W, H) do { rfx_context_reset(C); C->width = W; C->height = H; } while(0) -#else -#if FREERDP_VERSION_MAJOR >= 2 -# define NSC_RESET(C, W, H) nsc_context_reset(C, W, H) -# define RFX_RESET(C, W, H) rfx_context_reset(C, W, H) -#else -# define NSC_RESET(C, W, H) do { nsc_context_reset(C); C->width = W; C->height = H; } while(0) -# define RFX_RESET(C, W, H) do { rfx_context_reset(C); C->width = W; C->height = H; } while(0) -#endif -#define FREERDP_CB_RET_TYPE BOOL -#define FREERDP_CB_RETURN(V) return TRUE -#endif - -#ifdef HAVE_SURFACE_BITS_BMP -#define SURFACE_BPP(cmd) cmd.bmp.bpp -#define SURFACE_CODECID(cmd) cmd.bmp.codecID -#define SURFACE_WIDTH(cmd) cmd.bmp.width -#define SURFACE_HEIGHT(cmd) cmd.bmp.height -#define SURFACE_BITMAP_DATA(cmd) cmd.bmp.bitmapData -#define SURFACE_BITMAP_DATA_LEN(cmd) cmd.bmp.bitmapDataLength -#else -#define SURFACE_BPP(cmd) cmd.bpp -#define SURFACE_CODECID(cmd) cmd.codecID -#define SURFACE_WIDTH(cmd) cmd.width -#define SURFACE_HEIGHT(cmd) cmd.height -#define SURFACE_BITMAP_DATA(cmd) cmd.bitmapData -#define SURFACE_BITMAP_DATA_LEN(cmd) cmd.bitmapDataLength -#endif - -#include <freerdp/freerdp.h> -#include <freerdp/listener.h> -#include <freerdp/update.h> -#include <freerdp/input.h> -#include <freerdp/codec/color.h> -#include <freerdp/codec/rfx.h> -#include <freerdp/codec/nsc.h> -#include <freerdp/locale/keyboard.h> -#include <winpr/input.h> - -#if FREERDP_VERSION_MAJOR >= 2 -#include <winpr/ssl.h> -#endif - -#include "shared/helpers.h" -#include "shared/timespec-util.h" -#include <libweston/libweston.h> -#include <libweston/backend-rdp.h> -#include "pixman-renderer.h" - -#define MAX_FREERDP_FDS 32 -#define DEFAULT_AXIS_STEP_DISTANCE 10 -#define RDP_MODE_FREQ 60 * 1000 - -#if FREERDP_VERSION_MAJOR >= 2 && defined(PIXEL_FORMAT_BGRA32) && !defined(PIXEL_FORMAT_B8G8R8A8) - /* The RDP API is truly wonderful: the pixel format definition changed - * from BGRA32 to B8G8R8A8, but some versions ship with a definition of - * PIXEL_FORMAT_BGRA32 which doesn't actually build. Try really, really, - * hard to find one which does. */ -# define DEFAULT_PIXEL_FORMAT PIXEL_FORMAT_BGRA32 -#else -# define DEFAULT_PIXEL_FORMAT RDP_PIXEL_FORMAT_B8G8R8A8 -#endif - -struct rdp_output; - -struct rdp_backend { - struct weston_backend base; - struct weston_compositor *compositor; - - freerdp_listener *listener; - struct wl_event_source *listener_events[MAX_FREERDP_FDS]; - struct rdp_output *output; - - char *server_cert; - char *server_key; - char *rdp_key; - int tls_enabled; - int no_clients_resize; - int force_no_compression; -}; - -enum peer_item_flags { - RDP_PEER_ACTIVATED = (1 << 0), - RDP_PEER_OUTPUT_ENABLED = (1 << 1), -}; - -struct rdp_peers_item { - int flags; - freerdp_peer *peer; - struct weston_seat *seat; - - struct wl_list link; -}; - -struct rdp_head { - struct weston_head base; -}; - -struct rdp_output { - struct weston_output base; - struct wl_event_source *finish_frame_timer; - pixman_image_t *shadow_surface; - - struct wl_list peers; -}; - -struct rdp_peer_context { - rdpContext _p; - - struct rdp_backend *rdpBackend; - struct wl_event_source *events[MAX_FREERDP_FDS]; - RFX_CONTEXT *rfx_context; - wStream *encode_stream; - RFX_RECT *rfx_rects; - NSC_CONTEXT *nsc_context; - - struct rdp_peers_item item; -}; -typedef struct rdp_peer_context RdpPeerContext; - -static inline struct rdp_head * -to_rdp_head(struct weston_head *base) -{ - return container_of(base, struct rdp_head, base); -} - -static inline struct rdp_output * -to_rdp_output(struct weston_output *base) -{ - return container_of(base, struct rdp_output, base); -} - -static inline struct rdp_backend * -to_rdp_backend(struct weston_compositor *base) -{ - return container_of(base->backend, struct rdp_backend, base); -} - -static void -rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer) -{ - int width, height, nrects, i; - pixman_box32_t *region, *rects; - uint32_t *ptr; - RFX_RECT *rfxRect; - rdpUpdate *update = peer->update; - SURFACE_BITS_COMMAND cmd; - RdpPeerContext *context = (RdpPeerContext *)peer->context; - - Stream_Clear(context->encode_stream); - Stream_SetPosition(context->encode_stream, 0); - - width = (damage->extents.x2 - damage->extents.x1); - height = (damage->extents.y2 - damage->extents.y1); - -#ifdef HAVE_SKIP_COMPRESSION - cmd.skipCompression = TRUE; -#else - memset(&cmd, 0, sizeof(*cmd)); -#endif - cmd.destLeft = damage->extents.x1; - cmd.destTop = damage->extents.y1; - cmd.destRight = damage->extents.x2; - cmd.destBottom = damage->extents.y2; - SURFACE_BPP(cmd) = 32; - SURFACE_CODECID(cmd) = peer->settings->RemoteFxCodecId; - SURFACE_WIDTH(cmd) = width; - SURFACE_HEIGHT(cmd) = height; - - ptr = pixman_image_get_data(image) + damage->extents.x1 + - damage->extents.y1 * (pixman_image_get_stride(image) / sizeof(uint32_t)); - - rects = pixman_region32_rectangles(damage, &nrects); - context->rfx_rects = realloc(context->rfx_rects, nrects * sizeof *rfxRect); - - for (i = 0; i < nrects; i++) { - region = &rects[i]; - rfxRect = &context->rfx_rects[i]; - - rfxRect->x = (region->x1 - damage->extents.x1); - rfxRect->y = (region->y1 - damage->extents.y1); - rfxRect->width = (region->x2 - region->x1); - rfxRect->height = (region->y2 - region->y1); - } - - rfx_compose_message(context->rfx_context, context->encode_stream, context->rfx_rects, nrects, - (BYTE *)ptr, width, height, - pixman_image_get_stride(image) - ); - - SURFACE_BITMAP_DATA_LEN(cmd) = Stream_GetPosition(context->encode_stream); - SURFACE_BITMAP_DATA(cmd) = Stream_Buffer(context->encode_stream); - - update->SurfaceBits(update->context, &cmd); -} - - -static void -rdp_peer_refresh_nsc(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer) -{ - int width, height; - uint32_t *ptr; - rdpUpdate *update = peer->update; - SURFACE_BITS_COMMAND cmd; - RdpPeerContext *context = (RdpPeerContext *)peer->context; - - Stream_Clear(context->encode_stream); - Stream_SetPosition(context->encode_stream, 0); - - width = (damage->extents.x2 - damage->extents.x1); - height = (damage->extents.y2 - damage->extents.y1); - -#ifdef HAVE_SKIP_COMPRESSION - cmd.skipCompression = TRUE; -#else - memset(cmd, 0, sizeof(*cmd)); -#endif - - cmd.destLeft = damage->extents.x1; - cmd.destTop = damage->extents.y1; - cmd.destRight = damage->extents.x2; - cmd.destBottom = damage->extents.y2; - SURFACE_BPP(cmd) = 32; - SURFACE_CODECID(cmd) = peer->settings->NSCodecId; - SURFACE_WIDTH(cmd) = width; - SURFACE_HEIGHT(cmd) = height; - - ptr = pixman_image_get_data(image) + damage->extents.x1 + - damage->extents.y1 * (pixman_image_get_stride(image) / sizeof(uint32_t)); - - nsc_compose_message(context->nsc_context, context->encode_stream, (BYTE *)ptr, - width, height, - pixman_image_get_stride(image)); - - SURFACE_BITMAP_DATA_LEN(cmd) = Stream_GetPosition(context->encode_stream); - SURFACE_BITMAP_DATA(cmd) = Stream_Buffer(context->encode_stream); - - update->SurfaceBits(update->context, &cmd); -} - -static void -pixman_image_flipped_subrect(const pixman_box32_t *rect, pixman_image_t *img, BYTE *dest) -{ - int stride = pixman_image_get_stride(img); - int h; - int toCopy = (rect->x2 - rect->x1) * 4; - int height = (rect->y2 - rect->y1); - const BYTE *src = (const BYTE *)pixman_image_get_data(img); - src += ((rect->y2-1) * stride) + (rect->x1 * 4); - - for (h = 0; h < height; h++, src -= stride, dest += toCopy) - memcpy(dest, src, toCopy); -} - -static void -rdp_peer_refresh_raw(pixman_region32_t *region, pixman_image_t *image, freerdp_peer *peer) -{ - rdpUpdate *update = peer->update; - SURFACE_BITS_COMMAND cmd; - SURFACE_FRAME_MARKER marker; - pixman_box32_t *rect, subrect; - int nrects, i; - int heightIncrement, remainingHeight, top; - - rect = pixman_region32_rectangles(region, &nrects); - if (!nrects) - return; - - marker.frameId++; - marker.frameAction = SURFACECMD_FRAMEACTION_BEGIN; - update->SurfaceFrameMarker(peer->context, &marker); - - memset(&cmd, 0, sizeof(cmd)); - SURFACE_BPP(cmd) = 32; - SURFACE_CODECID(cmd) = 0; - - for (i = 0; i < nrects; i++, rect++) { - /*weston_log("rect(%d,%d, %d,%d)\n", rect->x1, rect->y1, rect->x2, rect->y2);*/ - cmd.destLeft = rect->x1; - cmd.destRight = rect->x2; - SURFACE_WIDTH(cmd) = rect->x2 - rect->x1; - - heightIncrement = peer->settings->MultifragMaxRequestSize / (16 + SURFACE_WIDTH(cmd) * 4); - remainingHeight = rect->y2 - rect->y1; - top = rect->y1; - - subrect.x1 = rect->x1; - subrect.x2 = rect->x2; - - while (remainingHeight) { - SURFACE_HEIGHT(cmd) = (remainingHeight > heightIncrement) ? heightIncrement : remainingHeight; - cmd.destTop = top; - cmd.destBottom = top + SURFACE_HEIGHT(cmd); - SURFACE_BITMAP_DATA_LEN(cmd) = SURFACE_WIDTH(cmd) * SURFACE_HEIGHT(cmd) * 4; - SURFACE_BITMAP_DATA(cmd) = (BYTE *)realloc(SURFACE_BITMAP_DATA(cmd), SURFACE_BITMAP_DATA_LEN(cmd)); - - subrect.y1 = top; - subrect.y2 = top + SURFACE_HEIGHT(cmd); - pixman_image_flipped_subrect(&subrect, image, SURFACE_BITMAP_DATA(cmd)); - - /*weston_log("* sending (%d,%d, %d,%d)\n", subrect.x1, subrect.y1, subrect.x2, subrect.y2); */ - update->SurfaceBits(peer->context, &cmd); - - remainingHeight -= SURFACE_HEIGHT(cmd); - top += SURFACE_HEIGHT(cmd); - } - } - - free(SURFACE_BITMAP_DATA(cmd)); - - marker.frameAction = SURFACECMD_FRAMEACTION_END; - update->SurfaceFrameMarker(peer->context, &marker); -} - -static void -rdp_peer_refresh_region(pixman_region32_t *region, freerdp_peer *peer) -{ - RdpPeerContext *context = (RdpPeerContext *)peer->context; - struct rdp_output *output = context->rdpBackend->output; - rdpSettings *settings = peer->settings; - - if (settings->RemoteFxCodec) - rdp_peer_refresh_rfx(region, output->shadow_surface, peer); - else if (settings->NSCodec) - rdp_peer_refresh_nsc(region, output->shadow_surface, peer); - else - rdp_peer_refresh_raw(region, output->shadow_surface, peer); -} - -static int -rdp_output_start_repaint_loop(struct weston_output *output) -{ - struct timespec ts; - - weston_compositor_read_presentation_clock(output->compositor, &ts); - weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID); - - return 0; -} - -static int -rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage, - void *repaint_data) -{ - struct rdp_output *output = container_of(output_base, struct rdp_output, base); - struct weston_compositor *ec = output->base.compositor; - struct rdp_peers_item *outputPeer; - - pixman_renderer_output_set_buffer(output_base, output->shadow_surface); - ec->renderer->repaint_output(&output->base, damage); - - if (pixman_region32_not_empty(damage)) { - wl_list_for_each(outputPeer, &output->peers, link) { - if ((outputPeer->flags & RDP_PEER_ACTIVATED) && - (outputPeer->flags & RDP_PEER_OUTPUT_ENABLED)) - { - rdp_peer_refresh_region(damage, outputPeer->peer); - } - } - } - - pixman_region32_subtract(&ec->primary_plane.damage, - &ec->primary_plane.damage, damage); - - wl_event_source_timer_update(output->finish_frame_timer, 16); - return 0; -} - -static int -finish_frame_handler(void *data) -{ - struct rdp_output *output = data; - struct timespec ts; - - weston_compositor_read_presentation_clock(output->base.compositor, &ts); - weston_output_finish_frame(&output->base, &ts, 0); - - return 1; -} - -static struct weston_mode * -rdp_insert_new_mode(struct weston_output *output, int width, int height, int rate) -{ - struct weston_mode *ret; - ret = zalloc(sizeof *ret); - if (!ret) - return NULL; - ret->width = width; - ret->height = height; - ret->refresh = rate; - wl_list_insert(&output->mode_list, &ret->link); - return ret; -} - -static struct weston_mode * -ensure_matching_mode(struct weston_output *output, struct weston_mode *target) -{ - struct weston_mode *local; - - wl_list_for_each(local, &output->mode_list, link) { - if ((local->width == target->width) && (local->height == target->height)) - return local; - } - - return rdp_insert_new_mode(output, target->width, target->height, RDP_MODE_FREQ); -} - -static int -rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode) -{ - struct rdp_output *rdpOutput = container_of(output, struct rdp_output, base); - struct rdp_peers_item *rdpPeer; - rdpSettings *settings; - pixman_image_t *new_shadow_buffer; - struct weston_mode *local_mode; - - local_mode = ensure_matching_mode(output, target_mode); - if (!local_mode) { - weston_log("mode %dx%d not available\n", target_mode->width, target_mode->height); - return -ENOENT; - } - - if (local_mode == output->current_mode) - return 0; - - output->current_mode->flags &= ~WL_OUTPUT_MODE_CURRENT; - - output->current_mode = local_mode; - output->current_mode->flags |= WL_OUTPUT_MODE_CURRENT; - - pixman_renderer_output_destroy(output); - pixman_renderer_output_create(output, 0); - - new_shadow_buffer = pixman_image_create_bits(PIXMAN_x8r8g8b8, target_mode->width, - target_mode->height, 0, target_mode->width * 4); - pixman_image_composite32(PIXMAN_OP_SRC, rdpOutput->shadow_surface, 0, new_shadow_buffer, - 0, 0, 0, 0, 0, 0, target_mode->width, target_mode->height); - pixman_image_unref(rdpOutput->shadow_surface); - rdpOutput->shadow_surface = new_shadow_buffer; - - wl_list_for_each(rdpPeer, &rdpOutput->peers, link) { - settings = rdpPeer->peer->settings; - if (settings->DesktopWidth == (UINT32)target_mode->width && - settings->DesktopHeight == (UINT32)target_mode->height) - continue; - - if (!settings->DesktopResize) { - /* too bad this peer does not support desktop resize */ - rdpPeer->peer->Close(rdpPeer->peer); - } else { - settings->DesktopWidth = target_mode->width; - settings->DesktopHeight = target_mode->height; - rdpPeer->peer->update->DesktopResize(rdpPeer->peer->context); - } - } - return 0; -} - -static int -rdp_output_set_size(struct weston_output *base, - int width, int height) -{ - struct rdp_output *output = to_rdp_output(base); - struct weston_head *head; - struct weston_mode *currentMode; - struct weston_mode initMode; - - /* We can only be called once. */ - assert(!output->base.current_mode); - - wl_list_for_each(head, &output->base.head_list, output_link) { - weston_head_set_monitor_strings(head, "weston", "rdp", NULL); - - /* This is a virtual output, so report a zero physical size. - * It's better to let frontends/clients use their defaults. */ - weston_head_set_physical_size(head, 0, 0); - } - - wl_list_init(&output->peers); - - initMode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; - initMode.width = width; - initMode.height = height; - initMode.refresh = RDP_MODE_FREQ; - - currentMode = ensure_matching_mode(&output->base, &initMode); - if (!currentMode) - return -1; - - output->base.current_mode = output->base.native_mode = currentMode; - - output->base.start_repaint_loop = rdp_output_start_repaint_loop; - output->base.repaint = rdp_output_repaint; - output->base.assign_planes = NULL; - output->base.set_backlight = NULL; - output->base.set_dpms = NULL; - output->base.switch_mode = rdp_switch_mode; - - return 0; -} - -static int -rdp_output_enable(struct weston_output *base) -{ - struct rdp_output *output = to_rdp_output(base); - struct rdp_backend *b = to_rdp_backend(base->compositor); - struct wl_event_loop *loop; - - output->shadow_surface = pixman_image_create_bits(PIXMAN_x8r8g8b8, - output->base.current_mode->width, - output->base.current_mode->height, - NULL, - output->base.current_mode->width * 4); - if (output->shadow_surface == NULL) { - weston_log("Failed to create surface for frame buffer.\n"); - return -1; - } - - if (pixman_renderer_output_create(&output->base, - PIXMAN_RENDERER_OUTPUT_USE_SHADOW) < 0) { - pixman_image_unref(output->shadow_surface); - return -1; - } - - loop = wl_display_get_event_loop(b->compositor->wl_display); - output->finish_frame_timer = wl_event_loop_add_timer(loop, finish_frame_handler, output); - - b->output = output; - - return 0; -} - -static int -rdp_output_disable(struct weston_output *base) -{ - struct rdp_output *output = to_rdp_output(base); - struct rdp_backend *b = to_rdp_backend(base->compositor); - - if (!output->base.enabled) - return 0; - - pixman_image_unref(output->shadow_surface); - pixman_renderer_output_destroy(&output->base); - - wl_event_source_remove(output->finish_frame_timer); - b->output = NULL; - - return 0; -} - -static void -rdp_output_destroy(struct weston_output *base) -{ - struct rdp_output *output = to_rdp_output(base); - - rdp_output_disable(&output->base); - weston_output_release(&output->base); - - free(output); -} - -static struct weston_output * -rdp_output_create(struct weston_compositor *compositor, const char *name) -{ - struct rdp_output *output; - - output = zalloc(sizeof *output); - if (output == NULL) - return NULL; - - weston_output_init(&output->base, compositor, name); - - output->base.destroy = rdp_output_destroy; - output->base.disable = rdp_output_disable; - output->base.enable = rdp_output_enable; - output->base.attach_head = NULL; - - weston_compositor_add_pending_output(&output->base, compositor); - - return &output->base; -} - -static int -rdp_head_create(struct weston_compositor *compositor, const char *name) -{ - struct rdp_head *head; - - head = zalloc(sizeof *head); - if (!head) - return -1; - - weston_head_init(&head->base, name); - weston_head_set_connection_status(&head->base, true); - weston_compositor_add_head(compositor, &head->base); - - return 0; -} - -static void -rdp_head_destroy(struct rdp_head *head) -{ - weston_head_release(&head->base); - free(head); -} - -static void -rdp_destroy(struct weston_compositor *ec) -{ - struct rdp_backend *b = to_rdp_backend(ec); - struct weston_head *base, *next; - struct rdp_peers_item *rdp_peer, *tmp; - int i; - - wl_list_for_each_safe(rdp_peer, tmp, &b->output->peers, link) { - freerdp_peer* client = rdp_peer->peer; - - client->Disconnect(client); - freerdp_peer_context_free(client); - freerdp_peer_free(client); - } - - for (i = 0; i < MAX_FREERDP_FDS; i++) - if (b->listener_events[i]) - wl_event_source_remove(b->listener_events[i]); - - weston_compositor_shutdown(ec); - - wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) - rdp_head_destroy(to_rdp_head(base)); - - freerdp_listener_free(b->listener); - - free(b->server_cert); - free(b->server_key); - free(b->rdp_key); - free(b); -} - -static -int rdp_listener_activity(int fd, uint32_t mask, void *data) -{ - freerdp_listener* instance = (freerdp_listener *)data; - - if (!(mask & WL_EVENT_READABLE)) - return 0; - if (!instance->CheckFileDescriptor(instance)) { - weston_log("failed to check FreeRDP file descriptor\n"); - return -1; - } - return 0; -} - -static -int rdp_implant_listener(struct rdp_backend *b, freerdp_listener* instance) -{ - int i, fd; - int rcount = 0; - void* rfds[MAX_FREERDP_FDS]; - struct wl_event_loop *loop; - - if (!instance->GetFileDescriptor(instance, rfds, &rcount)) { - weston_log("Failed to get FreeRDP file descriptor\n"); - return -1; - } - - loop = wl_display_get_event_loop(b->compositor->wl_display); - for (i = 0; i < rcount; i++) { - fd = (int)(long)(rfds[i]); - b->listener_events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, - rdp_listener_activity, instance); - } - - for ( ; i < MAX_FREERDP_FDS; i++) - b->listener_events[i] = 0; - return 0; -} - - -static FREERDP_CB_RET_TYPE -rdp_peer_context_new(freerdp_peer* client, RdpPeerContext* context) -{ - context->item.peer = client; - context->item.flags = RDP_PEER_OUTPUT_ENABLED; - -#if FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR == 1 - context->rfx_context = rfx_context_new(); -#else - context->rfx_context = rfx_context_new(TRUE); -#endif - if (!context->rfx_context) { - FREERDP_CB_RETURN(FALSE); - } - - context->rfx_context->mode = RLGR3; - context->rfx_context->width = client->settings->DesktopWidth; - context->rfx_context->height = client->settings->DesktopHeight; - rfx_context_set_pixel_format(context->rfx_context, DEFAULT_PIXEL_FORMAT); - - context->nsc_context = nsc_context_new(); - if (!context->nsc_context) - goto out_error_nsc; - - nsc_context_set_pixel_format(context->nsc_context, DEFAULT_PIXEL_FORMAT); - - context->encode_stream = Stream_New(NULL, 65536); - if (!context->encode_stream) - goto out_error_stream; - - FREERDP_CB_RETURN(TRUE); - -out_error_nsc: - rfx_context_free(context->rfx_context); -out_error_stream: - nsc_context_free(context->nsc_context); - FREERDP_CB_RETURN(FALSE); -} - -static void -rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context) -{ - int i; - if (!context) - return; - - wl_list_remove(&context->item.link); - for (i = 0; i < MAX_FREERDP_FDS; i++) { - if (context->events[i]) - wl_event_source_remove(context->events[i]); - } - - if (context->item.flags & RDP_PEER_ACTIVATED) { - weston_seat_release_keyboard(context->item.seat); - weston_seat_release_pointer(context->item.seat); - /* XXX we should weston_seat_release(context->item.seat); here - * but it would crash on reconnect */ - } - - Stream_Free(context->encode_stream, TRUE); - nsc_context_free(context->nsc_context); - rfx_context_free(context->rfx_context); - free(context->rfx_rects); -} - - -static int -rdp_client_activity(int fd, uint32_t mask, void *data) -{ - freerdp_peer* client = (freerdp_peer *)data; - - if (!client->CheckFileDescriptor(client)) { - weston_log("unable to checkDescriptor for %p\n", client); - goto out_clean; - } - return 0; - -out_clean: - freerdp_peer_context_free(client); - freerdp_peer_free(client); - return 0; -} - -static BOOL -xf_peer_capabilities(freerdp_peer* client) -{ - return TRUE; -} - -struct rdp_to_xkb_keyboard_layout { - UINT32 rdpLayoutCode; - const char *xkbLayout; - const char *xkbVariant; -}; - -/* table reversed from - https://github.com/awakecoding/FreeRDP/blob/master/libfreerdp/locale/xkb_layout_ids.c#L811 */ -static const -struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = { - {KBD_ARABIC_101, "ara", 0}, - {KBD_BULGARIAN, 0, 0}, - {KBD_CHINESE_TRADITIONAL_US, 0}, - {KBD_CZECH, "cz", 0}, - {KBD_CZECH_PROGRAMMERS, "cz", "bksl"}, - {KBD_CZECH_QWERTY, "cz", "qwerty"}, - {KBD_DANISH, "dk", 0}, - {KBD_GERMAN, "de", 0}, - {KBD_GERMAN_NEO, "de", "neo"}, - {KBD_GERMAN_IBM, "de", "qwerty"}, - {KBD_GREEK, "gr", 0}, - {KBD_GREEK_220, "gr", "simple"}, - {KBD_GREEK_319, "gr", "extended"}, - {KBD_GREEK_POLYTONIC, "gr", "polytonic"}, - {KBD_US, "us", 0}, - {KBD_US_ENGLISH_TABLE_FOR_IBM_ARABIC_238_L, "ara", "buckwalter"}, - {KBD_SPANISH, "es", 0}, - {KBD_SPANISH_VARIATION, "es", "nodeadkeys"}, - {KBD_FINNISH, "fi", 0}, - {KBD_FRENCH, "fr", 0}, - {KBD_HEBREW, "il", 0}, - {KBD_HUNGARIAN, "hu", 0}, - {KBD_HUNGARIAN_101_KEY, "hu", "standard"}, - {KBD_ICELANDIC, "is", 0}, - {KBD_ITALIAN, "it", 0}, - {KBD_ITALIAN_142, "it", "nodeadkeys"}, - {KBD_JAPANESE, "jp", 0}, - {KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002, "jp", "kana"}, - {KBD_KOREAN, "kr", 0}, - {KBD_KOREAN_INPUT_SYSTEM_IME_2000, "kr", "kr104"}, - {KBD_DUTCH, "nl", 0}, - {KBD_NORWEGIAN, "no", 0}, - {KBD_POLISH_PROGRAMMERS, "pl", 0}, - {KBD_POLISH_214, "pl", "qwertz"}, - {KBD_ROMANIAN, "ro", 0}, - {KBD_RUSSIAN, "ru", 0}, - {KBD_RUSSIAN_TYPEWRITER, "ru", "typewriter"}, - {KBD_CROATIAN, "hr", 0}, - {KBD_SLOVAK, "sk", 0}, - {KBD_SLOVAK_QWERTY, "sk", "qwerty"}, - {KBD_ALBANIAN, 0, 0}, - {KBD_SWEDISH, "se", 0}, - {KBD_THAI_KEDMANEE, "th", 0}, - {KBD_THAI_KEDMANEE_NON_SHIFTLOCK, "th", "tis"}, - {KBD_TURKISH_Q, "tr", 0}, - {KBD_TURKISH_F, "tr", "f"}, - {KBD_URDU, "in", "urd-phonetic3"}, - {KBD_UKRAINIAN, "ua", 0}, - {KBD_BELARUSIAN, "by", 0}, - {KBD_SLOVENIAN, "si", 0}, - {KBD_ESTONIAN, "ee", 0}, - {KBD_LATVIAN, "lv", 0}, - {KBD_LITHUANIAN_IBM, "lt", "ibm"}, - {KBD_FARSI, "af", 0}, - {KBD_VIETNAMESE, "vn", 0}, - {KBD_ARMENIAN_EASTERN, "am", 0}, - {KBD_AZERI_LATIN, 0, 0}, - {KBD_FYRO_MACEDONIAN, "mk", 0}, - {KBD_GEORGIAN, "ge", 0}, - {KBD_FAEROESE, 0, 0}, - {KBD_DEVANAGARI_INSCRIPT, 0, 0}, - {KBD_MALTESE_47_KEY, 0, 0}, - {KBD_NORWEGIAN_WITH_SAMI, "no", "smi"}, - {KBD_KAZAKH, "kz", 0}, - {KBD_KYRGYZ_CYRILLIC, "kg", "phonetic"}, - {KBD_TATAR, "ru", "tt"}, - {KBD_BENGALI, "bd", 0}, - {KBD_BENGALI_INSCRIPT, "bd", "probhat"}, - {KBD_PUNJABI, 0, 0}, - {KBD_GUJARATI, "in", "guj"}, - {KBD_TAMIL, "in", "tam"}, - {KBD_TELUGU, "in", "tel"}, - {KBD_KANNADA, "in", "kan"}, - {KBD_MALAYALAM, "in", "mal"}, - {KBD_HINDI_TRADITIONAL, "in", 0}, - {KBD_MARATHI, 0, 0}, - {KBD_MONGOLIAN_CYRILLIC, "mn", 0}, - {KBD_UNITED_KINGDOM_EXTENDED, "gb", "intl"}, - {KBD_SYRIAC, "syc", 0}, - {KBD_SYRIAC_PHONETIC, "syc", "syc_phonetic"}, - {KBD_NEPALI, "np", 0}, - {KBD_PASHTO, "af", "ps"}, - {KBD_DIVEHI_PHONETIC, 0, 0}, - {KBD_LUXEMBOURGISH, 0, 0}, - {KBD_MAORI, "mao", 0}, - {KBD_CHINESE_SIMPLIFIED_US, 0, 0}, - {KBD_SWISS_GERMAN, "ch", "de_nodeadkeys"}, - {KBD_UNITED_KINGDOM, "gb", 0}, - {KBD_LATIN_AMERICAN, "latam", 0}, - {KBD_BELGIAN_FRENCH, "be", 0}, - {KBD_BELGIAN_PERIOD, "be", "oss_sundeadkeys"}, - {KBD_PORTUGUESE, "pt", 0}, - {KBD_SERBIAN_LATIN, "rs", 0}, - {KBD_AZERI_CYRILLIC, "az", "cyrillic"}, - {KBD_SWEDISH_WITH_SAMI, "se", "smi"}, - {KBD_UZBEK_CYRILLIC, "af", "uz"}, - {KBD_INUKTITUT_LATIN, "ca", "ike"}, - {KBD_CANADIAN_FRENCH_LEGACY, "ca", "fr-legacy"}, - {KBD_SERBIAN_CYRILLIC, "rs", 0}, - {KBD_CANADIAN_FRENCH, "ca", "fr-legacy"}, - {KBD_SWISS_FRENCH, "ch", "fr"}, - {KBD_BOSNIAN, "ba", 0}, - {KBD_IRISH, 0, 0}, - {KBD_BOSNIAN_CYRILLIC, "ba", "us"}, - {KBD_UNITED_STATES_DVORAK, "us", "dvorak"}, - {KBD_PORTUGUESE_BRAZILIAN_ABNT2, "br", "nativo"}, - {KBD_CANADIAN_MULTILINGUAL_STANDARD, "ca", "multix"}, - {KBD_GAELIC, "ie", "CloGaelach"}, - - {0x00000000, 0, 0}, -}; - -/* taken from 2.2.7.1.6 Input Capability Set (TS_INPUT_CAPABILITYSET) */ -static const char *rdp_keyboard_types[] = { - "", /* 0: unused */ - "", /* 1: IBM PC/XT or compatible (83-key) keyboard */ - "", /* 2: Olivetti "ICO" (102-key) keyboard */ - "", /* 3: IBM PC/AT (84-key) or similar keyboard */ - "pc102",/* 4: IBM enhanced (101- or 102-key) keyboard */ - "", /* 5: Nokia 1050 and similar keyboards */ - "", /* 6: Nokia 9140 and similar keyboards */ - "" /* 7: Japanese keyboard */ -}; - -static BOOL -xf_peer_activate(freerdp_peer* client) -{ - RdpPeerContext *peerCtx; - struct rdp_backend *b; - struct rdp_output *output; - rdpSettings *settings; - rdpPointerUpdate *pointer; - struct rdp_peers_item *peersItem; - struct xkb_rule_names xkbRuleNames; - struct xkb_keymap *keymap; - struct weston_output *weston_output; - int i; - pixman_box32_t box; - pixman_region32_t damage; - char seat_name[50]; - POINTER_SYSTEM_UPDATE pointer_system; - - peerCtx = (RdpPeerContext *)client->context; - b = peerCtx->rdpBackend; - peersItem = &peerCtx->item; - output = b->output; - settings = client->settings; - - if (!settings->SurfaceCommandsEnabled) { - weston_log("client doesn't support required SurfaceCommands\n"); - return FALSE; - } - - if (b->force_no_compression && settings->CompressionEnabled) { - weston_log("Forcing compression off\n"); - settings->CompressionEnabled = FALSE; - } - - if (output->base.width != (int)settings->DesktopWidth || - output->base.height != (int)settings->DesktopHeight) - { - if (b->no_clients_resize) { - /* RDP peers don't dictate their resolution to weston */ - if (!settings->DesktopResize) { - /* peer does not support desktop resize */ - weston_log("%s: client doesn't support resizing, closing connection\n", __FUNCTION__); - return FALSE; - } else { - settings->DesktopWidth = output->base.width; - settings->DesktopHeight = output->base.height; - client->update->DesktopResize(client->context); - } - } else { - /* ask weston to adjust size */ - struct weston_mode new_mode; - struct weston_mode *target_mode; - new_mode.width = (int)settings->DesktopWidth; - new_mode.height = (int)settings->DesktopHeight; - target_mode = ensure_matching_mode(&output->base, &new_mode); - if (!target_mode) { - weston_log("client mode not found\n"); - return FALSE; - } - weston_output_mode_set_native(&output->base, target_mode, 1); - output->base.width = new_mode.width; - output->base.height = new_mode.height; - } - } - - weston_output = &output->base; - RFX_RESET(peerCtx->rfx_context, weston_output->width, weston_output->height); - NSC_RESET(peerCtx->nsc_context, weston_output->width, weston_output->height); - - if (peersItem->flags & RDP_PEER_ACTIVATED) - return TRUE; - - /* when here it's the first reactivation, we need to setup a little more */ - weston_log("kbd_layout:0x%x kbd_type:0x%x kbd_subType:0x%x kbd_functionKeys:0x%x\n", - settings->KeyboardLayout, settings->KeyboardType, settings->KeyboardSubType, - settings->KeyboardFunctionKey); - - memset(&xkbRuleNames, 0, sizeof(xkbRuleNames)); - if (settings->KeyboardType <= 7) - xkbRuleNames.model = rdp_keyboard_types[settings->KeyboardType]; - for (i = 0; rdp_keyboards[i].rdpLayoutCode; i++) { - if (rdp_keyboards[i].rdpLayoutCode == settings->KeyboardLayout) { - xkbRuleNames.layout = rdp_keyboards[i].xkbLayout; - xkbRuleNames.variant = rdp_keyboards[i].xkbVariant; - weston_log("%s: matching layout=%s variant=%s\n", __FUNCTION__, - xkbRuleNames.layout, xkbRuleNames.variant); - break; - } - } - - keymap = NULL; - if (xkbRuleNames.layout) { - keymap = xkb_keymap_new_from_names(b->compositor->xkb_context, - &xkbRuleNames, 0); - } - - if (settings->ClientHostname) - snprintf(seat_name, sizeof(seat_name), "RDP %s", settings->ClientHostname); - else - snprintf(seat_name, sizeof(seat_name), "RDP peer @%s", settings->ClientAddress); - - peersItem->seat = zalloc(sizeof(*peersItem->seat)); - if (!peersItem->seat) { - xkb_keymap_unref(keymap); - weston_log("unable to create a weston_seat\n"); - return FALSE; - } - - weston_seat_init(peersItem->seat, b->compositor, seat_name); - weston_seat_init_keyboard(peersItem->seat, keymap); - xkb_keymap_unref(keymap); - weston_seat_init_pointer(peersItem->seat); - - peersItem->flags |= RDP_PEER_ACTIVATED; - - /* disable pointer on the client side */ - pointer = client->update->pointer; - pointer_system.type = SYSPTR_NULL; - pointer->PointerSystem(client->context, &pointer_system); - - /* sends a full refresh */ - box.x1 = 0; - box.y1 = 0; - box.x2 = output->base.width; - box.y2 = output->base.height; - pixman_region32_init_with_extents(&damage, &box); - - rdp_peer_refresh_region(&damage, client); - - pixman_region32_fini(&damage); - - return TRUE; -} - -static BOOL -xf_peer_post_connect(freerdp_peer *client) -{ - return TRUE; -} - -static FREERDP_CB_RET_TYPE -xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) -{ - RdpPeerContext *peerContext = (RdpPeerContext *)input->context; - struct rdp_output *output; - uint32_t button = 0; - bool need_frame = false; - struct timespec time; - - if (flags & PTR_FLAGS_MOVE) { - output = peerContext->rdpBackend->output; - if (x < output->base.width && y < output->base.height) { - weston_compositor_get_time(&time); - notify_motion_absolute(peerContext->item.seat, &time, - x, y); - need_frame = true; - } - } - - if (flags & PTR_FLAGS_BUTTON1) - button = BTN_LEFT; - else if (flags & PTR_FLAGS_BUTTON2) - button = BTN_RIGHT; - else if (flags & PTR_FLAGS_BUTTON3) - button = BTN_MIDDLE; - - if (button) { - weston_compositor_get_time(&time); - notify_button(peerContext->item.seat, &time, button, - (flags & PTR_FLAGS_DOWN) ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED - ); - need_frame = true; - } - - if (flags & PTR_FLAGS_WHEEL) { - struct weston_pointer_axis_event weston_event; - double value; - - /* DEFAULT_AXIS_STEP_DISTANCE is stolen from compositor-x11.c - * The RDP specs says the lower bits of flags contains the "the number of rotation - * units the mouse wheel was rotated". - * - * https://blogs.msdn.microsoft.com/oldnewthing/20130123-00/?p=5473 explains the 120 value - */ - value = -(flags & 0xff) / 120.0; - if (flags & PTR_FLAGS_WHEEL_NEGATIVE) - value = -value; - - weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL; - weston_event.value = DEFAULT_AXIS_STEP_DISTANCE * value; - weston_event.discrete = (int)value; - weston_event.has_discrete = true; - - weston_compositor_get_time(&time); - - notify_axis(peerContext->item.seat, &time, &weston_event); - need_frame = true; - } - - if (need_frame) - notify_pointer_frame(peerContext->item.seat); - - FREERDP_CB_RETURN(TRUE); -} - -static FREERDP_CB_RET_TYPE -xf_extendedMouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) -{ - RdpPeerContext *peerContext = (RdpPeerContext *)input->context; - struct rdp_output *output; - struct timespec time; - - output = peerContext->rdpBackend->output; - if (x < output->base.width && y < output->base.height) { - weston_compositor_get_time(&time); - notify_motion_absolute(peerContext->item.seat, &time, x, y); - } - - FREERDP_CB_RETURN(TRUE); -} - - -static FREERDP_CB_RET_TYPE -xf_input_synchronize_event(rdpInput *input, UINT32 flags) -{ - freerdp_peer *client = input->context->peer; - RdpPeerContext *peerCtx = (RdpPeerContext *)input->context; - struct rdp_output *output = peerCtx->rdpBackend->output; - pixman_box32_t box; - pixman_region32_t damage; - - /* sends a full refresh */ - box.x1 = 0; - box.y1 = 0; - box.x2 = output->base.width; - box.y2 = output->base.height; - pixman_region32_init_with_extents(&damage, &box); - - rdp_peer_refresh_region(&damage, client); - - pixman_region32_fini(&damage); - FREERDP_CB_RETURN(TRUE); -} - - -static FREERDP_CB_RET_TYPE -xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) -{ - uint32_t scan_code, vk_code, full_code; - enum wl_keyboard_key_state keyState; - RdpPeerContext *peerContext = (RdpPeerContext *)input->context; - int notify = 0; - struct timespec time; - - if (!(peerContext->item.flags & RDP_PEER_ACTIVATED)) - FREERDP_CB_RETURN(TRUE); - - if (flags & KBD_FLAGS_DOWN) { - keyState = WL_KEYBOARD_KEY_STATE_PRESSED; - notify = 1; - } else if (flags & KBD_FLAGS_RELEASE) { - keyState = WL_KEYBOARD_KEY_STATE_RELEASED; - notify = 1; - } - - if (notify) { - full_code = code; - if (flags & KBD_FLAGS_EXTENDED) - full_code |= KBD_FLAGS_EXTENDED; - - vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, 4); - if (flags & KBD_FLAGS_EXTENDED) - vk_code |= KBDEXT; - - scan_code = GetKeycodeFromVirtualKeyCode(vk_code, KEYCODE_TYPE_EVDEV); - - /*weston_log("code=%x ext=%d vk_code=%x scan_code=%x\n", code, (flags & KBD_FLAGS_EXTENDED) ? 1 : 0, - vk_code, scan_code);*/ - weston_compositor_get_time(&time); - notify_key(peerContext->item.seat, &time, - scan_code - 8, keyState, STATE_UPDATE_AUTOMATIC); - } - - FREERDP_CB_RETURN(TRUE); -} - -static FREERDP_CB_RET_TYPE -xf_input_unicode_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) -{ - weston_log("Client sent a unicode keyboard event (flags:0x%X code:0x%X)\n", flags, code); - FREERDP_CB_RETURN(TRUE); -} - - -static FREERDP_CB_RET_TYPE -xf_suppress_output(rdpContext *context, BYTE allow, const RECTANGLE_16 *area) -{ - RdpPeerContext *peerContext = (RdpPeerContext *)context; - - if (allow) - peerContext->item.flags |= RDP_PEER_OUTPUT_ENABLED; - else - peerContext->item.flags &= (~RDP_PEER_OUTPUT_ENABLED); - - FREERDP_CB_RETURN(TRUE); -} - -static int -rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) -{ - int rcount = 0; - void *rfds[MAX_FREERDP_FDS]; - int i, fd; - struct wl_event_loop *loop; - rdpSettings *settings; - rdpInput *input; - RdpPeerContext *peerCtx; - - client->ContextSize = sizeof(RdpPeerContext); - client->ContextNew = (psPeerContextNew)rdp_peer_context_new; - client->ContextFree = (psPeerContextFree)rdp_peer_context_free; - freerdp_peer_context_new(client); - - peerCtx = (RdpPeerContext *) client->context; - peerCtx->rdpBackend = b; - - settings = client->settings; - /* configure security settings */ - if (b->rdp_key) - settings->RdpKeyFile = strdup(b->rdp_key); - if (b->tls_enabled) { - settings->CertificateFile = strdup(b->server_cert); - settings->PrivateKeyFile = strdup(b->server_key); - } else { - settings->TlsSecurity = FALSE; - } - settings->NlaSecurity = FALSE; - - if (!client->Initialize(client)) { - weston_log("peer initialization failed\n"); - goto error_initialize; - } - - settings->OsMajorType = OSMAJORTYPE_UNIX; - settings->OsMinorType = OSMINORTYPE_PSEUDO_XSERVER; - settings->ColorDepth = 32; - settings->RefreshRect = TRUE; - settings->RemoteFxCodec = TRUE; - settings->NSCodec = TRUE; - settings->FrameMarkerCommandEnabled = TRUE; - settings->SurfaceFrameMarkerEnabled = TRUE; - - client->Capabilities = xf_peer_capabilities; - client->PostConnect = xf_peer_post_connect; - client->Activate = xf_peer_activate; - - client->update->SuppressOutput = (pSuppressOutput)xf_suppress_output; - - input = client->input; - input->SynchronizeEvent = xf_input_synchronize_event; - input->MouseEvent = xf_mouseEvent; - input->ExtendedMouseEvent = xf_extendedMouseEvent; - input->KeyboardEvent = xf_input_keyboard_event; - input->UnicodeKeyboardEvent = xf_input_unicode_keyboard_event; - - if (!client->GetFileDescriptor(client, rfds, &rcount)) { - weston_log("unable to retrieve client fds\n"); - goto error_initialize; - } - - loop = wl_display_get_event_loop(b->compositor->wl_display); - for (i = 0; i < rcount; i++) { - fd = (int)(long)(rfds[i]); - - peerCtx->events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, - rdp_client_activity, client); - } - for ( ; i < MAX_FREERDP_FDS; i++) - peerCtx->events[i] = 0; - - wl_list_insert(&b->output->peers, &peerCtx->item.link); - return 0; - -error_initialize: - client->Close(client); - return -1; -} - - -static FREERDP_CB_RET_TYPE -rdp_incoming_peer(freerdp_listener *instance, freerdp_peer *client) -{ - struct rdp_backend *b = (struct rdp_backend *)instance->param4; - if (rdp_peer_init(client, b) < 0) { - weston_log("error when treating incoming peer\n"); - FREERDP_CB_RETURN(FALSE); - } - - FREERDP_CB_RETURN(TRUE); -} - -static const struct weston_rdp_output_api api = { - rdp_output_set_size, -}; - -static struct rdp_backend * -rdp_backend_create(struct weston_compositor *compositor, - struct weston_rdp_backend_config *config) -{ - struct rdp_backend *b; - char *fd_str; - char *fd_tail; - int fd, ret; - - b = zalloc(sizeof *b); - if (b == NULL) - return NULL; - - b->compositor = compositor; - b->base.destroy = rdp_destroy; - b->base.create_output = rdp_output_create; - b->rdp_key = config->rdp_key ? strdup(config->rdp_key) : NULL; - b->no_clients_resize = config->no_clients_resize; - b->force_no_compression = config->force_no_compression; - - compositor->backend = &b->base; - - /* activate TLS only if certificate/key are available */ - if (config->server_cert && config->server_key) { - weston_log("TLS support activated\n"); - b->server_cert = strdup(config->server_cert); - b->server_key = strdup(config->server_key); - if (!b->server_cert || !b->server_key) - goto err_free_strings; - b->tls_enabled = 1; - } - - if (weston_compositor_set_presentation_clock_software(compositor) < 0) - goto err_compositor; - - if (pixman_renderer_init(compositor) < 0) - goto err_compositor; - - if (rdp_head_create(compositor, "rdp") < 0) - goto err_compositor; - - compositor->capabilities |= WESTON_CAP_ARBITRARY_MODES; - - if (!config->env_socket) { - b->listener = freerdp_listener_new(); - b->listener->PeerAccepted = rdp_incoming_peer; - b->listener->param4 = b; - if (!b->listener->Open(b->listener, config->bind_address, config->port)) { - weston_log("unable to bind rdp socket\n"); - goto err_listener; - } - - if (rdp_implant_listener(b, b->listener) < 0) - goto err_compositor; - } else { - /* get the socket from RDP_FD var */ - fd_str = getenv("RDP_FD"); - if (!fd_str) { - weston_log("RDP_FD env variable not set\n"); - goto err_output; - } - - fd = strtoul(fd_str, &fd_tail, 10); - if (errno != 0 || fd_tail == fd_str || *fd_tail != '\0' - || rdp_peer_init(freerdp_peer_new(fd), b)) - goto err_output; - } - - ret = weston_plugin_api_register(compositor, WESTON_RDP_OUTPUT_API_NAME, - &api, sizeof(api)); - - if (ret < 0) { - weston_log("Failed to register output API.\n"); - goto err_output; - } - - return b; - -err_listener: - freerdp_listener_free(b->listener); -err_output: - weston_output_release(&b->output->base); -err_compositor: - weston_compositor_shutdown(compositor); -err_free_strings: - free(b->rdp_key); - free(b->server_cert); - free(b->server_key); - free(b); - return NULL; -} - -static void -config_init_to_defaults(struct weston_rdp_backend_config *config) -{ - config->bind_address = NULL; - config->port = 3389; - config->rdp_key = NULL; - config->server_cert = NULL; - config->server_key = NULL; - config->env_socket = 0; - config->no_clients_resize = 0; - config->force_no_compression = 0; -} - -WL_EXPORT int -weston_backend_init(struct weston_compositor *compositor, - struct weston_backend_config *config_base) -{ - struct rdp_backend *b; - struct weston_rdp_backend_config config = {{ 0, }}; - int major, minor, revision; - -#if FREERDP_VERSION_MAJOR >= 2 - winpr_InitializeSSL(0); -#endif - freerdp_get_version(&major, &minor, &revision); - weston_log("using FreeRDP version %d.%d.%d\n", major, minor, revision); - - if (config_base == NULL || - config_base->struct_version != WESTON_RDP_BACKEND_CONFIG_VERSION || - config_base->struct_size > sizeof(struct weston_rdp_backend_config)) { - weston_log("RDP backend config structure is invalid\n"); - return -1; - } - - config_init_to_defaults(&config); - memcpy(&config, config_base, config_base->struct_size); - - if (!config.rdp_key && (!config.server_cert || !config.server_key)) { - weston_log("the RDP compositor requires keys and an optional certificate for RDP or TLS security (" - "--rdp4-key or --rdp-tls-cert/--rdp-tls-key)\n"); - return -1; - } - - b = rdp_backend_create(compositor, &config); - if (b == NULL) - return -1; - return 0; -} diff --git a/libweston/backend-tdm/meson.build b/libweston/backend-tdm/meson.build deleted file mode 100644 index bec333da..00000000 --- a/libweston/backend-tdm/meson.build +++ /dev/null @@ -1,35 +0,0 @@ -if not get_option('backend-tdm') - subdir_done() -endif - -config_h.set('BUILD_TDM_COMPOSITOR', '1') - -srcs_tdm = [ - 'tdm.c', - linux_dmabuf_unstable_v1_protocol_c, - linux_dmabuf_unstable_v1_server_protocol_h, - presentation_time_server_protocol_h, -] - -deps_tdm = [ - dep_libdl, - dep_libweston_private, - dep_session_helper, - dep_libinput_backend, - dependency('libtbm'), - dependency('libtdm'), - dependency('libudev', version: '>= 136'), -] - -plugin_tdm = shared_library( - 'tdm-backend', - srcs_tdm, - include_directories: common_inc, - dependencies: deps_tdm, - name_prefix: '', - install: true, - install_dir: dir_module_libweston -) -env_modmap += 'tdm-backend.so=@0@;'.format(plugin_tdm.full_path()) - -install_headers(backend_drm_h, subdir: dir_include_libweston_install) diff --git a/libweston/backend-tdm/tdm-internal.h b/libweston/backend-tdm/tdm-internal.h deleted file mode 100644 index 6fb8818d..00000000 --- a/libweston/backend-tdm/tdm-internal.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2017, 2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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 <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <ctype.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <linux/input.h> -#include <linux/vt.h> -#include <assert.h> -#include <sys/mman.h> -#include <time.h> - -#include <libudev.h> - -#include <libweston/libweston.h> -#include <libweston/backend-drm.h> -#include <libweston/weston-log.h> -#include <tdm.h> -#include <tbm_bufmgr.h> -#include <tbm_surface_internal.h> -#include <tbm_dummy_display.h> -#include "shared/helpers.h" -#include "libinput-seat.h" -#include "backend.h" -#include "libweston-internal.h" - -#define tdm_debug(b, ...) \ - weston_log_scope_printf((b)->debug, __VA_ARGS__) - -/** - * Possible values for the WTDM_PLANE_TYPE property. - */ -enum wtdm_backend_plane_type { - WTDM_PLANE_TYPE_PRIMARY = 0, - WTDM_PLANE_TYPE_OVERLAY, -}; - -struct tdm_backend { - struct weston_backend base; - struct weston_compositor *compositor; - - struct udev *udev; - struct wl_event_source *tdm_source; - - struct wl_listener session_listener; - uint32_t gbm_format; - - struct udev_input input; - - bool use_pixman; - bool use_pixman_shadow; - - uint32_t pageflip_timeout; - - struct weston_log_scope *debug; - - tbm_bufmgr tbufmgr; - tbm_dummy_display *tbm_display; - tdm_display *tdisplay; - - int tdm_fd; - - struct wl_list plane_list; -}; - -struct tdm_backend_mode { - struct weston_mode base; - - struct wl_list head_link; - const tdm_output_mode *tmode; -}; - -struct tdm_backend_plane { - struct weston_plane base; - struct tdm_backend *backend; - enum wtdm_backend_plane_type type; - struct wl_list link; -}; - -struct tdm_backend_hwc { - struct tdm_backend *backend; - - tdm_hwc *thwc; - tbm_surface_queue_h target_buffer_queue; - - tbm_surface_h commit_fb; - tbm_surface_h display_fb; -}; - -struct tdm_backend_head { - struct weston_head base; - struct tdm_backend *backend; - - int index; - unsigned int pipe; - - tdm_output *toutput; - tdm_output_type toutput_type; - struct tdm_backend_hwc *hwc; - - struct wl_list mode_list; - - tdm_output_dpms dpms; - tdm_output_conn_status conn_status; -}; - -struct tdm_backend_output { - struct weston_output base; - struct tdm_backend *backend; - - tbm_surface_h pending_fb; - tbm_surface_h current_fb; - - tbm_surface_h pixman_tsurfaces[2]; - pixman_image_t *image[2]; - int current_image; - pixman_region32_t previous_damage; -}; - -struct tdm_pending_state { - struct tdm_backend *backend; - struct wl_list output_list; -}; - -static inline struct tdm_backend_head * -to_tdm_head(struct weston_head *base) -{ - return container_of(base, struct tdm_backend_head, base); -} - -static inline struct tdm_backend_output * -to_tdm_output(struct weston_output *base) -{ - return container_of(base, struct tdm_backend_output, base); -} - -static inline struct tdm_backend * -to_tdm_backend(struct weston_compositor *base) -{ - return container_of(base->backend, struct tdm_backend, base); -} - -static inline struct tdm_backend_mode * -to_tdm_mode(struct weston_mode *base) -{ - return container_of(base, struct tdm_backend_mode, base); -} diff --git a/libweston/backend-tdm/tdm.c b/libweston/backend-tdm/tdm.c deleted file mode 100644 index 3140ca35..00000000 --- a/libweston/backend-tdm/tdm.c +++ /dev/null @@ -1,1280 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2017, 2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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 <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <ctype.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <linux/input.h> -#include <linux/vt.h> -#include <assert.h> -#include <sys/mman.h> -#include <time.h> - -#include <libudev.h> - -#include <libweston/weston-log.h> -#include "tdm-internal.h" -#include "launcher-util.h" -#include "renderer-gl/gl-renderer.h" -#include "pixman-renderer.h" -#include "presentation-time-server-protocol.h" -#include "linux-dmabuf.h" - -struct gl_renderer_interface *gl_renderer; - -static const char default_seat[] = "seat0"; - -static struct tdm_backend_head * -tdm_backend_output_get_primary_head(struct tdm_backend_output *output) -{ - struct weston_head *base; - struct tdm_backend_head *head, *primary_head = NULL; - - wl_list_for_each(base, &output->base.head_list, output_link) { - head = to_tdm_head(base); - if (head->pipe == 0) { - primary_head = head; - break; - } - } - - return primary_head; -} - -static void -tdm_backend_hwc_destroy(struct tdm_backend_hwc *hwc) -{ - if (!hwc) return; - - free(hwc); -} - -static struct tdm_backend_hwc * -tdm_backend_hwc_create(struct tdm_backend_head *head) -{ - struct tdm_backend_hwc *hwc; - tdm_hwc *thwc; - tdm_error terror; - - hwc = zalloc(sizeof *hwc); - if (!hwc) - return NULL; - - thwc = tdm_output_get_hwc(head->toutput, &terror); - if (terror != TDM_ERROR_NONE) goto err_alloc; - - hwc->thwc = thwc; - - hwc->target_buffer_queue = tdm_hwc_get_client_target_buffer_queue(thwc, &terror); - if (terror != TDM_ERROR_NONE) goto err_alloc; - - return hwc; - -err_alloc: - free(hwc); - - return NULL; -} - -static int -tdm_backend_hwc_target_acquire_buffer(struct tdm_backend_hwc *hwc, tbm_surface_h *tsurface) -{ - tbm_surface_queue_error_e tsq_err; - - if (!tbm_surface_queue_can_acquire(hwc->target_buffer_queue, 1)) - return 0; - - tsq_err = tbm_surface_queue_acquire(hwc->target_buffer_queue, tsurface); - if (tsq_err != TBM_SURFACE_QUEUE_ERROR_NONE) { - weston_log("failed to tbm_surface_queue_acquire\n"); - return tsq_err; - } - - return 0; -} - -static int -tdm_backend_hwc_target_release_buffer(struct tdm_backend_hwc *hwc, tbm_surface_h tsurface) -{ - tbm_surface_queue_error_e tsq_err; - - tsq_err = tbm_surface_queue_release(hwc->target_buffer_queue, tsurface); - if (tsq_err != TBM_SURFACE_QUEUE_ERROR_NONE) { - weston_log("failed to tbm_surface_queue_release\n"); - return tsq_err; - } - - return 0; -} - -static void -tdm_backend_hwc_commit_handler(tdm_hwc *thwc, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data) -{ - struct tdm_backend_output *output = (struct tdm_backend_output *)user_data; - struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output); - struct tdm_backend *b = output->backend;; - struct tdm_backend_hwc *hwc = head->hwc; - struct timespec ts; - uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC | - WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION | - WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK; - - if (output->current_fb) { - tbm_surface_internal_unref(output->current_fb); - - if (!b->use_pixman) - tdm_backend_hwc_target_release_buffer(hwc, output->current_fb); - } - - output->current_fb = output->pending_fb; - output->pending_fb = NULL; - - ts.tv_sec = tv_sec; - ts.tv_nsec = tv_usec * 1000; - - weston_output_finish_frame(&output->base, &ts, flags); -} - -static tbm_surface_h -tdm_backend_output_render_gl(struct tdm_backend_output *output, - pixman_region32_t *damage) -{ - struct weston_compositor *ec = output->base.compositor; - struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output); - tbm_surface_h tsurface; - - ec->renderer->repaint_output(&output->base, damage); - - if (tdm_backend_hwc_target_acquire_buffer(head->hwc, &tsurface) < 0) - return NULL; - - tbm_surface_internal_ref(tsurface); - - return tsurface; -} - -static tbm_surface_h -tdm_backend_output_render_pixman(struct tdm_backend_output *output, - pixman_region32_t *damage) -{ - struct weston_compositor *ec = output->base.compositor; - - output->current_image ^= 1; - - pixman_renderer_output_set_buffer(&output->base, - output->image[output->current_image]); - pixman_renderer_output_set_hw_extra_damage(&output->base, - &output->previous_damage); - - ec->renderer->repaint_output(&output->base, damage); - - pixman_region32_copy(&output->previous_damage, damage); - - tbm_surface_internal_ref(output->pixman_tsurfaces[output->current_image]); - - return output->pixman_tsurfaces[output->current_image]; -} - -static void -tdm_backend_output_render(struct tdm_backend_output *output, pixman_region32_t *damage) -{ - struct weston_compositor *c = output->base.compositor; - struct tdm_backend *b = to_tdm_backend(c); - tbm_surface_h fb; - - if (b->use_pixman) { - fb = tdm_backend_output_render_pixman(output, damage); - } else { - fb = tdm_backend_output_render_gl(output, damage); - } - - if (!fb) return; - - if (output->pending_fb) - tbm_surface_internal_unref(output->pending_fb); - - output->pending_fb = fb; - - pixman_region32_subtract(&c->primary_plane.damage, - &c->primary_plane.damage, damage); -} - -static int -tdm_backend_output_repaint(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) -{ - struct tdm_backend_output *output = to_tdm_output(output_base); - - tdm_backend_output_render(output, damage); - - return 0; -} - -static int -tdm_backend_output_start_repaint_loop(struct weston_output *output_base) -{ - weston_output_finish_frame(output_base, NULL, - WP_PRESENTATION_FEEDBACK_INVALID); - return 0; -} - -/** - * Begin a new repaint cycle - */ -static void * -tdm_repaint_begin(struct weston_compositor *compositor) -{ - struct tdm_backend *b = to_tdm_backend(compositor); - - if (weston_log_scope_is_enabled(b->debug)) { - char *dbg = weston_compositor_print_scene_graph(compositor); - tdm_debug(b, "[repaint] Beginning repaint\n"); - tdm_debug(b, "%s", dbg); - free(dbg); - } - - return NULL; -} - -/** - * Flush a repaint set - */ -static int -tdm_repaint_flush(struct weston_compositor *compositor, void *repaint_data) -{ - struct weston_output *output_base = NULL; - uint32_t num_types; - tdm_region tdamage; - - wl_list_for_each(output_base, &compositor->output_list, link) { - struct tdm_backend_output *output = to_tdm_output(output_base); - struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output); - - if (!output->pending_fb) - continue; - - memset(&tdamage, 0, sizeof(tdamage)); - - tdm_hwc_set_client_target_buffer(head->hwc->thwc, output->pending_fb, tdamage); - tdm_hwc_validate(head->hwc->thwc, NULL, 0, &num_types); - tdm_hwc_accept_validation(head->hwc->thwc); - tdm_hwc_commit(head->hwc->thwc, 0, tdm_backend_hwc_commit_handler, output); - } - - return 0; -} - -/** - * Cancel a repaint set - */ -static void -tdm_repaint_cancel(struct weston_compositor *compositor, void *repaint_data) -{ - struct tdm_backend *b = to_tdm_backend(compositor); - struct weston_output *output_base = NULL; - - wl_list_for_each(output_base, &compositor->output_list, link) { - struct tdm_backend_output *output = to_tdm_output(output_base); - struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output); - - if (!output->pending_fb) - continue; - - if (!b->use_pixman) { - tdm_backend_hwc_target_release_buffer(head->hwc, output->pending_fb); - } - - tbm_surface_internal_unref(output->pending_fb); - output->pending_fb = NULL; - } -} - -static int -tdm_backend_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode) -{ - return 0; -} - -static struct tdm_backend_plane * -tdm_backend_plane_create(struct tdm_backend *b, struct tdm_backend_output *output, - enum wtdm_backend_plane_type type) -{ - struct tdm_backend_plane *plane; - - plane = zalloc(sizeof(*plane)); - if (!plane) { - weston_log("%s: out of memory\n", __func__); - return NULL; - } - - plane->backend = b; - plane->type = type; - - weston_plane_init(&plane->base, b->compositor, 0, 0); - wl_list_insert(&b->plane_list, &plane->link); - - return plane; -} - -/** - * Destroy one TDM plane - * - * Destroy a TDM plane, removing it from screen and releasing its retained - * buffers in the process. The counterpart to tdm_backend_plane_create. - * - * @param plane Plane to deallocate (will be freed) - */ -static void -tdm_backend_plane_destroy(struct tdm_backend_plane *plane) -{ - weston_plane_release(&plane->base); - wl_list_remove(&plane->link); - free(plane); -} - -/** - * Initialise sprites (overlay planes) - * - * Walk the list of provided TDM planes, and add overlay planes. - * - * Call destroy_sprites to free these planes. - * - * @param b TDM compositor backend - */ -static void -create_sprites(struct tdm_backend *b) -{ - struct tdm_backend_plane *primary_plane, *overlay_plane; - - primary_plane = tdm_backend_plane_create(b, NULL, WTDM_PLANE_TYPE_PRIMARY); - if (!primary_plane) { - weston_log("failed to tdm_backend_plane_create\n"); - return; - } - - overlay_plane = tdm_backend_plane_create(b, NULL, WTDM_PLANE_TYPE_OVERLAY); - if (!overlay_plane) { - weston_log("failed to tdm_backend_plane_create\n"); - tdm_backend_plane_destroy(primary_plane); - return; - } - - weston_compositor_stack_plane(b->compositor, - &primary_plane->base, - &b->compositor->primary_plane); -} - -/** - * Clean up sprites (overlay planes) - * - * The counterpart to create_sprites. - * - * @param b TDM compositor backend - */ -static void -destroy_sprites(struct tdm_backend *b) -{ - struct tdm_backend_plane *plane, *next; - - wl_list_for_each_safe(plane, next, &b->plane_list, link) - tdm_backend_plane_destroy(plane); -} - -/** - * Power output on or off - * - * The DPMS/power level of an output is used to switch it on or off. This - * is TDM's hook for doing so, which can called either as part of repaint, - * or independently of the repaint loop. - * - * If we are called as part of repaint, we simply set the relevant bit in - * state and return. - * - * This function is never called on a virtual output. - */ -static void -tdm_backend_output_set_dpms(struct weston_output *output_base, enum dpms_enum level) -{ - tdm_error terror; - struct tdm_backend_output *output = to_tdm_output(output_base); - struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output); - - terror = tdm_output_set_dpms(head->toutput, level); - if (terror != TDM_ERROR_NONE) - weston_log("fail to tdm_output_set_dpms\n"); -} - -static int -tdm_backend_output_init_egl(struct tdm_backend_output *output, struct tdm_backend *b) -{ - struct tdm_backend_head *head; - struct tdm_backend_hwc *hwc; - tbm_format format; - - head = tdm_backend_output_get_primary_head(output); - if (!head) { - weston_log("fail to get primary head\n"); - return -1; - } - - hwc = head->hwc; - if (!hwc) { - weston_log("fail to get hwc\n"); - return -1; - } - - if (!hwc->target_buffer_queue) { - weston_log("fail to get target buffer queue\n"); - return -1; - } - - format = tbm_surface_queue_get_format(hwc->target_buffer_queue); - - if (gl_renderer->output_window_create(&output->base, - (EGLNativeWindowType)hwc->target_buffer_queue, - hwc->target_buffer_queue, - &format, - 0) < 0) { - weston_log("failed to create gl renderer output state\n"); - return -1; - } - - return 1; -} - -static void -tdm_backend_output_fini_egl(struct tdm_backend_output *output) -{ -} - -static int -tdm_backend_output_init_pixman(struct tdm_backend_output *output, struct tdm_backend *b) -{ - int w = output->base.current_mode->width; - int h = output->base.current_mode->height; - /* only support xrgb8888 */ - uint32_t pixman_format = PIXMAN_x8r8g8b8; - unsigned int i; - uint32_t flags = 0; - - /* FIXME error checking */ - for (i = 0; i < ARRAY_LENGTH(output->pixman_tsurfaces); i++) { - tbm_surface_info_s info; - - output->pixman_tsurfaces[i] = tbm_surface_create(w, h, TBM_FORMAT_XRGB8888); - if (!output->pixman_tsurfaces[i]) - goto err; - - tbm_surface_get_info(output->pixman_tsurfaces[i], &info); - - output->image[i] = - pixman_image_create_bits(pixman_format, w, h, - (uint32_t *)info.planes[0].ptr, - info.planes[0].stride); - if (!output->image[i]) - goto err; - } - - if (b->use_pixman_shadow) - flags |= PIXMAN_RENDERER_OUTPUT_USE_SHADOW; - - if (pixman_renderer_output_create(&output->base, flags) < 0) - goto err; - - weston_log("TDM: output %s %s shadow framebuffer.\n", output->base.name, - b->use_pixman_shadow ? "uses" : "does not use"); - - pixman_region32_init_rect(&output->previous_damage, - output->base.x, output->base.y, output->base.width, output->base.height); - - return 0; - -err: - for (i = 0; i < ARRAY_LENGTH(output->pixman_tsurfaces); i++) { - if (output->pixman_tsurfaces[i]) - tbm_surface_internal_unref(output->pixman_tsurfaces[i]); - if (output->image[i]) - pixman_image_unref(output->image[i]); - - output->pixman_tsurfaces[i] = NULL; - output->image[i] = NULL; - } - - return -1; -} - -static void -tdm_backend_output_fini_pixman(struct tdm_backend_output *output) -{ - unsigned int i; - - pixman_renderer_output_destroy(&output->base); - pixman_region32_fini(&output->previous_damage); - - for (i = 0; i < ARRAY_LENGTH(output->pixman_tsurfaces); i++) { - pixman_image_unref(output->image[i]); - tbm_surface_internal_unref(output->pixman_tsurfaces[i]); - output->pixman_tsurfaces[i] = NULL; - output->image[i] = NULL; - } -} - -static void -setup_output_seat_constraint(struct tdm_backend *b, - struct weston_output *output, - const char *s) -{ - if (strcmp(s, "") != 0) { - struct weston_pointer *pointer; - struct udev_seat *seat; - - seat = udev_seat_get_named(&b->input, s); - if (!seat) - return; - - seat->base.output = output; - - pointer = weston_seat_get_pointer(&seat->base); - if (pointer) - weston_pointer_clamp(pointer, - &pointer->x, - &pointer->y); - } -} - -static int -tdm_backend_output_attach_head(struct weston_output *output_base, - struct weston_head *head_base) -{ - if (!output_base->enabled) - return 0; - - weston_output_schedule_repaint(output_base); - - return 0; -} - -static void -tdm_backend_output_detach_head(struct weston_output *output_base, - struct weston_head *head_base) -{ - if (!output_base->enabled) - return; - - weston_output_schedule_repaint(output_base); -} - -static void -tdm_backend_output_set_seat(struct weston_output *base, - const char *seat) -{ - struct tdm_backend_output *output = to_tdm_output(base); - struct tdm_backend *b = to_tdm_backend(base->compositor); - - setup_output_seat_constraint(b, &output->base, - seat ? seat : ""); -} - -static int -tdm_backend_output_enable(struct weston_output *base) -{ - struct tdm_backend_output *output = to_tdm_output(base); - struct tdm_backend *b = to_tdm_backend(base->compositor); - struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output); - - head->hwc = tdm_backend_hwc_create(head); - if (!head->hwc) goto err; - - if (b->use_pixman) { - if (tdm_backend_output_init_pixman(output, b) < 0) { - weston_log("Failed to init output pixman state\n"); - goto err; - } - } else if (tdm_backend_output_init_egl(output, b) < 0) { - weston_log("Failed to init output gl state\n"); - goto err; - } - - output->base.start_repaint_loop = tdm_backend_output_start_repaint_loop; - output->base.repaint = tdm_backend_output_repaint; - output->base.assign_planes = NULL;// todo - output->base.set_dpms = tdm_backend_output_set_dpms; - output->base.switch_mode = tdm_backend_output_switch_mode; - output->base.set_gamma = NULL; - - weston_log("Output %s (crtc %d) video modes:\n", - output->base.name, /*output->crtc_id*/ 0); - - return 0; - -err: - return -1; -} - -static void -tdm_backend_output_deinit(struct weston_output *base) -{ - struct tdm_backend_output *output = to_tdm_output(base); - struct tdm_backend *b = to_tdm_backend(base->compositor); - - if (b->use_pixman) - tdm_backend_output_fini_pixman(output); - else - tdm_backend_output_fini_egl(output); -} - -static void -tdm_backend_output_destroy(struct weston_output *base) -{ - struct tdm_backend_output *output = to_tdm_output(base); - - if (output->base.enabled) - tdm_backend_output_deinit(&output->base); - - weston_output_release(&output->base); - - free(output); -} - -static int -tdm_backend_output_disable(struct weston_output *base) -{ - struct tdm_backend_output *output = to_tdm_output(base); - - weston_log("Disabling output %s\n", output->base.name); - - if (output->base.enabled) - tdm_backend_output_deinit(&output->base); - - return 0; -} - -static char * -_output_type_to_str(tdm_output_type output_type) -{ - if (output_type == TDM_OUTPUT_TYPE_Unknown) return "Unknown"; - else if (output_type == TDM_OUTPUT_TYPE_VGA) return "VGA"; - else if (output_type == TDM_OUTPUT_TYPE_DVII) return "DVII"; - else if (output_type == TDM_OUTPUT_TYPE_DVID) return "DVID"; - else if (output_type == TDM_OUTPUT_TYPE_DVIA) return "DVIA"; - else if (output_type == TDM_OUTPUT_TYPE_SVIDEO) return "SVIDEO"; - else if (output_type == TDM_OUTPUT_TYPE_LVDS) return "LVDS"; - else if (output_type == TDM_OUTPUT_TYPE_Component) return "Component"; - else if (output_type == TDM_OUTPUT_TYPE_9PinDIN) return "9PinDIN"; - else if (output_type == TDM_OUTPUT_TYPE_DisplayPort) return "DisplayPort"; - else if (output_type == TDM_OUTPUT_TYPE_HDMIA) return "HDMIA"; - else if (output_type == TDM_OUTPUT_TYPE_HDMIB) return "HDMIB"; - else if (output_type == TDM_OUTPUT_TYPE_TV) return "TV"; - else if (output_type == TDM_OUTPUT_TYPE_eDP) return "eDP"; - else if (output_type == TDM_OUTPUT_TYPE_DSI) return "DSI"; - else return "Unknown"; -} - -static int -tdm_backend_head_update(struct tdm_backend_head *head) -{ - tdm_error terror; - tdm_output_conn_status status; - int i; - - terror = tdm_output_get_conn_status(head->toutput, &status); - if (terror != TDM_ERROR_NONE) { - weston_log("fail to tdm_backend_output_get_conn_status\n"); - return terror; - } - - if ((head->conn_status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) && - (status == TDM_OUTPUT_CONN_STATUS_CONNECTED)) { - const tdm_output_mode *tmodes = NULL; - int num_tmodes = 0; - const char *maker, *model; - unsigned int phy_w, phy_h; - - terror = tdm_output_get_model_info(head->toutput, &maker, &model, NULL); - if (terror != TDM_ERROR_NONE) { - weston_log("fail to get model info\n"); - return terror; - } - - terror = tdm_output_get_physical_size(head->toutput, &phy_w, &phy_h); - if (terror != TDM_ERROR_NONE) { - weston_log("fail to get physical size\n"); - return terror; - } - - weston_head_set_monitor_strings(&head->base, maker, model, NULL); - weston_head_set_physical_size(&head->base, phy_w, phy_h); - weston_head_set_connection_status(&head->base, status == TDM_OUTPUT_CONN_STATUS_CONNECTED); - - terror = tdm_output_get_available_modes(head->toutput, &tmodes, &num_tmodes); - if (terror != TDM_ERROR_NONE || num_tmodes == 0) { - weston_log("fail to get tmodes\n"); - return terror; - } - - for (i = 0; i < num_tmodes; i++) { - struct tdm_backend_mode *mode; - - mode = zalloc(sizeof *mode); - if (!mode) { - weston_log("fail to alloc tmode\n"); - return -1; - } - - mode->tmode = &tmodes[i]; - mode->base.flags = 0; - mode->base.width = tmodes[i].hdisplay; - mode->base.height = tmodes[i].vdisplay;; - mode->base.refresh = tmodes[i].clock; - - wl_list_insert(&head->mode_list, &mode->head_link); - } - } - - head->conn_status = status; - - return 0; -} - -static void -_tdm_backend_head_cb_toutput_change(tdm_output *toutput, tdm_output_change_type type, - tdm_value value, void *user_data) -{ - struct tdm_backend_head *head = NULL; - tdm_output_dpms tdpms; - tdm_output_conn_status status; - - head = (struct tdm_backend_head *)user_data; - - switch (type) { - case TDM_OUTPUT_CHANGE_CONNECTION: - status = (tdm_output_conn_status)value.u32; - head->conn_status = status; - if ((status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) || - (status == TDM_OUTPUT_CONN_STATUS_CONNECTED)) - tdm_backend_head_update(head); - break; - case TDM_OUTPUT_CHANGE_DPMS: - tdpms = (tdm_output_dpms)value.u32; - head->dpms = tdpms; - default: - break; - } -} - -/** - * Create a Weston head for a connector - * - * Given a TDM connector, create a matching tdm_backend_head structure and add it - * to Weston's head list. - * - * @param backend Weston backend structure - * @param index tdm_output index - * @returns The new head, or NULL on failure. - */ -static struct tdm_backend_head * -tdm_backend_head_create(struct tdm_backend *backend, int index) -{ - struct tdm_backend_head *head; - tdm_output *toutput = NULL; - tdm_output_type output_type; - tdm_error terror; - const char *name; - - head = zalloc(sizeof *head); - if (!head) - return NULL; - - head->index = index; - - toutput = tdm_display_get_output(backend->tdisplay, index, NULL); - if (!toutput) goto err_alloc; - - terror = tdm_output_get_output_type(toutput, &output_type); - if (terror != TDM_ERROR_NONE) goto err_alloc; - - terror = tdm_output_get_pipe(toutput, &head->pipe); - if (terror != TDM_ERROR_NONE) goto err_alloc; - - terror = tdm_output_add_change_handler(toutput, _tdm_backend_head_cb_toutput_change, head); - if (terror != TDM_ERROR_NONE) { - weston_log("failed to tdm_backend_output_add_change_handler\n"); - goto err_alloc; - } - - head->index = index; - head->toutput = toutput; - head->toutput_type = output_type; - head->toutput = toutput; - head->backend = backend; - wl_list_init(&head->mode_list); - - name = _output_type_to_str(output_type); - - if ((output_type == TDM_OUTPUT_TYPE_LVDS) || - (output_type == TDM_OUTPUT_TYPE_eDP) || - (output_type == TDM_OUTPUT_TYPE_DSI)) - weston_head_set_internal(&head->base); - - weston_head_init(&head->base, name); - head->backend = backend; - - tdm_backend_head_update(head); - weston_compositor_add_head(backend->compositor, &head->base); - - return head; - -err_alloc: - free(head); - - return NULL; -} - -static void -tdm_backend_head_destroy(struct tdm_backend_head *head) -{ - weston_head_release(&head->base); - - if (head->hwc) - tdm_backend_hwc_destroy(head->hwc); - - tdm_output_remove_change_handler(head->toutput, _tdm_backend_head_cb_toutput_change, head); - - free(head); -} - -/** - * Create a Weston output structure - * - * Create an "empty" tdm_output. This is the implementation of - * weston_backend::create_output. - * - * Creating an output is usually followed by tdm_backend_output_attach_head() - * and tdm_backend_output_enable() to make use of it. - * - * @param compositor The compositor instance. - * @param name Name for the new output. - * @returns The output, or NULL on failure. - */ -static struct weston_output * -tdm_backend_output_create(struct weston_compositor *compositor, const char *name) -{ - struct tdm_backend *b = to_tdm_backend(compositor); - struct tdm_backend_output *output; - - output = zalloc(sizeof *output); - if (output == NULL) - return NULL; - - output->backend = b; - - weston_output_init(&output->base, compositor, name); - - output->base.enable = tdm_backend_output_enable; - output->base.destroy = tdm_backend_output_destroy; - output->base.disable = tdm_backend_output_disable; - output->base.attach_head = tdm_backend_output_attach_head; - output->base.detach_head = tdm_backend_output_detach_head; - - weston_compositor_add_pending_output(&output->base, b->compositor); - - return &output->base; -} - -static int -tdm_backend_create_heads(struct tdm_backend *b) -{ - struct tdm_backend_head *head; - tdm_error terror; - int num_outputs; - int i; - - terror = tdm_display_get_output_count(b->tdisplay, &num_outputs); - if ((terror != TDM_ERROR_NONE) || (num_outputs < 1)) { - weston_log("fail to get tdm_display_get_output_count\n"); - return -1; - } - - for (i = 0; i < num_outputs; i++) { - head = tdm_backend_head_create(b, i); - if (!head) - weston_log("TDM: failed to create head index:%d.\n", i); - } - - return 0; -} - -static void -tdm_destroy(struct weston_compositor *ec) -{ - struct tdm_backend *b = to_tdm_backend(ec); - struct weston_head *base, *next; - - udev_input_destroy(&b->input); - - wl_event_source_remove(b->tdm_source); - - destroy_sprites(b); - - weston_compositor_log_scope_destroy(b->debug); - b->debug = NULL; - weston_compositor_shutdown(ec); - - wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) - tdm_backend_head_destroy(to_tdm_head(base)); - - udev_unref(b->udev); - - weston_launcher_destroy(ec->launcher); - - tbm_dummy_display_destroy(b->tbm_display); - tbm_bufmgr_deinit(b->tbufmgr); - tdm_display_deinit(b->tdisplay); - - close(b->tdm_fd); - free(b); -} - -static void -session_notify(struct wl_listener *listener, void *data) -{ - struct weston_compositor *compositor = data; - struct tdm_backend *b = to_tdm_backend(compositor); - - if (compositor->session_active) { - weston_log("activating session\n"); - weston_compositor_wake(compositor); - weston_compositor_damage_all(compositor); - udev_input_enable(&b->input); - } else { - weston_log("deactivating session\n"); - udev_input_disable(&b->input); - - weston_compositor_offscreen(compositor); - - /* If we have a repaint scheduled (either from a - * pending pageflip or the idle handler), make sure we - * cancel that so we don't try to pageflip when we're - * vt switched away. The OFFSCREEN state will prevent - * further attempts at repainting. When we switch - * back, we schedule a repaint, which will process - * pending frame callbacks. */ - - // unset all plane? - } -} - -static int -init_pixman(struct tdm_backend *b) -{ - return pixman_renderer_init(b->compositor); -} - -static int -tdm_backend_create_gl_renderer(struct tdm_backend *b) -{ - if (gl_renderer->display_create(b->compositor, - 0, - b->tbm_display, - EGL_WINDOW_BIT, - NULL, - 0) < 0) - return -1; - - return 0; -} - -static int -init_egl(struct tdm_backend *b) -{ - gl_renderer = weston_load_module("gl-renderer.so", - "gl_renderer_interface"); - if (!gl_renderer) - return -1; - - b->tbm_display = tbm_dummy_display_create(); - if (!b->tbm_display) { - weston_log("Failed to tbm_dummy_display_create\n"); - return -1; - } - - if (tdm_backend_create_gl_renderer(b) < 0) { - tbm_dummy_display_destroy(b->tbm_display); - return -1; - } - - return 0; -} - -static int -on_tdm_input(int fd, uint32_t mask, void *data) -{ - struct tdm_backend *b = data; - tdm_error terror; - - terror = tdm_display_handle_events(b->tdisplay); - if (terror != TDM_ERROR_NONE) - weston_log("tdm_display_handle_events failed\n"); - - return 1; -} - -static int -tdm_backend_output_set_mode(struct weston_output *base, - enum weston_drm_backend_output_mode mode, - const char *modeline) -{ - struct tdm_backend_output *output = to_tdm_output(base); - struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output); - struct tdm_backend_mode *backend_mode; - tdm_error terror; - - if (!head) return -1; - - /* temporary use only preferred */ - wl_list_for_each(backend_mode, &head->mode_list, head_link) { - if (backend_mode->tmode->type & TDM_OUTPUT_MODE_TYPE_PREFERRED) { - output->base.current_mode = &backend_mode->base; - output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT; - - output->base.native_mode = output->base.current_mode; - output->base.native_scale = output->base.current_scale; - - wl_list_insert(output->base.mode_list.prev, &backend_mode->base.link); - break; - } - - } - - terror = tdm_output_set_mode(head->toutput, backend_mode->tmode); - if (terror != TDM_ERROR_NONE) { - weston_log("fail to set tmode.\n"); - return terror; - } - - return 0; -} - -static void -tdm_backend_output_set_gbm_format(struct weston_output *base, - const char *gbm_format) -{ - /* not support */ -} - -static const struct weston_drm_output_api api = { - tdm_backend_output_set_mode, - tdm_backend_output_set_gbm_format, - tdm_backend_output_set_seat, -}; - -static struct tdm_backend * -tdm_backend_create(struct weston_compositor *compositor, - struct weston_drm_backend_config *config) -{ - struct tdm_backend *b; - struct wl_event_loop *loop; - const char *seat_id = default_seat; - const char *session_seat; - int ret; - tdm_error terror; - int tdm_fd; - - session_seat = getenv("XDG_SEAT"); - if (session_seat) - seat_id = session_seat; - - if (config->seat_id) - seat_id = config->seat_id; - - weston_log("initializing tdm backend\n"); - - b = zalloc(sizeof *b); - if (b == NULL) - return NULL; - - b->compositor = compositor; - b->use_pixman = config->use_pixman; - b->pageflip_timeout = config->pageflip_timeout; - b->use_pixman_shadow = config->use_pixman_shadow; - - b->debug = weston_compositor_add_log_scope(compositor->weston_log_ctx, "tdm-backend", - "Debug messages from TDM backend\n", - NULL, NULL, NULL); - - compositor->backend = &b->base; - - /* Check if we run tdm-backend using weston-launch */ - compositor->launcher = weston_launcher_connect(compositor, config->tty, - seat_id, true); - if (compositor->launcher == NULL) { - weston_log("fatal: tdm backend should be run using " - "weston-launch binary, or your system should " - "provide the logind D-Bus API.\n"); - goto err_compositor; - } - - b->udev = udev_new(); - if (b->udev == NULL) { - weston_log("failed to initialize udev context\n"); - goto err_launcher; - } - - b->session_listener.notify = session_notify; - wl_signal_add(&compositor->session_signal, &b->session_listener); - - b->tbufmgr = tbm_bufmgr_server_init(); - if (!b->tbufmgr) { - weston_log("failed to initialize tbm_bufmgr\n"); - goto err_udev; - } - - b->tdisplay = tdm_display_init(&terror); - if (!b->tdisplay) { - weston_log("failed to initialize tdm_display\n"); - goto err_tbm; - } - - if (b->use_pixman) { - if (init_pixman(b) < 0) { - weston_log("failed to initialize pixman renderer\n"); - goto err_tdm; - } - } - else { - if (init_egl(b) < 0) { - weston_log("failed to initialize egl\n"); - goto err_tdm; - } - } - - if (tdm_backend_create_heads(b) < 0) { - weston_log("Failed to create heads for\n"); - goto err_tdm; - } - - b->base.destroy = tdm_destroy; - b->base.repaint_begin = tdm_repaint_begin; - b->base.repaint_flush = tdm_repaint_flush; - b->base.repaint_cancel = tdm_repaint_cancel; - b->base.create_output = tdm_backend_output_create; - b->base.device_changed = NULL; - b->base.can_scanout_dmabuf = NULL; - - weston_setup_vt_switch_bindings(compositor); - - wl_list_init(&b->plane_list); - create_sprites(b); - - if (udev_input_init(&b->input, - compositor, b->udev, seat_id, - config->configure_device) < 0) { - weston_log("failed to create input devices\n"); - goto err_tdm; - } - - tdm_display_get_fd(b->tdisplay, &tdm_fd); - b->tdm_fd = dup(tdm_fd); - - loop = wl_display_get_event_loop(compositor->wl_display); - b->tdm_source = - wl_event_loop_add_fd(loop, b->tdm_fd, - WL_EVENT_READABLE, on_tdm_input, b); - - if (compositor->renderer->import_dmabuf) { - if (linux_dmabuf_setup(compositor) < 0) - weston_log("Error: initializing dmabuf " - "support failed.\n"); - } - - ret = weston_plugin_api_register(compositor, WESTON_DRM_OUTPUT_API_NAME, - &api, sizeof(api)); - - if (ret < 0) { - weston_log("Failed to register output API.\n"); - goto err_tdm_source; - } - - return b; - -err_tdm_source: - wl_event_source_remove(b->tdm_source); - udev_input_destroy(&b->input); -err_tdm: - tdm_display_deinit(b->tdisplay); -err_tbm: - tbm_bufmgr_deinit(b->tbufmgr); -err_udev: - udev_unref(b->udev); -err_launcher: - weston_launcher_destroy(compositor->launcher); -err_compositor: - weston_compositor_shutdown(compositor); - free(b); - return NULL; -} - -static void -config_init_to_defaults(struct weston_drm_backend_config *config) -{ - config->use_pixman_shadow = true; -} - -WL_EXPORT int -weston_backend_init(struct weston_compositor *compositor, - struct weston_backend_config *config_base) -{ - struct tdm_backend *b; - struct weston_drm_backend_config config = {{ 0, }}; - - if (config_base == NULL || - config_base->struct_version != WESTON_DRM_BACKEND_CONFIG_VERSION || - config_base->struct_size > sizeof(struct weston_drm_backend_config)) { - weston_log("tdm backend config structure is invalid\n"); - return -1; - } - - config_init_to_defaults(&config); - memcpy(&config, config_base, config_base->struct_size); - - b = tdm_backend_create(compositor, &config); - if (b == NULL) - return -1; - - return 0; -} diff --git a/libweston/backend-wayland/meson.build b/libweston/backend-wayland/meson.build deleted file mode 100644 index 7e82513a..00000000 --- a/libweston/backend-wayland/meson.build +++ /dev/null @@ -1,44 +0,0 @@ -if not get_option('backend-wayland') - subdir_done() -endif - -config_h.set('BUILD_WAYLAND_COMPOSITOR', '1') - -srcs_wlwl = [ - 'wayland.c', - fullscreen_shell_unstable_v1_client_protocol_h, - fullscreen_shell_unstable_v1_protocol_c, - presentation_time_protocol_c, - presentation_time_server_protocol_h, - xdg_shell_server_protocol_h, - xdg_shell_protocol_c, -] - -deps_wlwl = [ - dependency('wayland-client'), - dependency('wayland-cursor'), - dep_pixman, - dep_libweston_private, - dep_libdrm_headers, - dep_lib_cairo_shared, -] - -if get_option('renderer-gl') - d = dependency('wayland-egl', required: false) - if not d.found() - error('wayland-backend + gl-renderer requires wayland-egl which was not found. Or, you can use \'-Dbackend-wayland=false\' or \'-Drenderer-gl=false\'.') - endif - deps_wlwl += d -endif - -plugin_wlwl = shared_library( - 'wayland-backend', - srcs_wlwl, - include_directories: common_inc, - dependencies: deps_wlwl, - name_prefix: '', - install: true, - install_dir: dir_module_libweston -) -env_modmap += 'wayland-backend.so=@0@;'.format(plugin_wlwl.full_path()) -install_headers(backend_wayland_h, subdir: dir_include_libweston_install) diff --git a/libweston/backend-wayland/wayland.c b/libweston/backend-wayland/wayland.c deleted file mode 100644 index 42af0c80..00000000 --- a/libweston/backend-wayland/wayland.c +++ /dev/null @@ -1,2906 +0,0 @@ -/* - * Copyright © 2010-2011 Benjamin Franzke - * Copyright © 2013 Jason Ekstrand - * - * 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 <assert.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> -#include <sys/mman.h> -#include <linux/input.h> - -#include <drm_fourcc.h> -#include <wayland-client.h> -#include <wayland-cursor.h> - -#ifdef ENABLE_EGL -#include <wayland-egl.h> -#endif - -#include <libweston/libweston.h> -#include <libweston/backend-wayland.h> -#include "renderer-gl/gl-renderer.h" -#include "shared/weston-egl-ext.h" -#include "pixman-renderer.h" -#include "shared/helpers.h" -#include "shared/image-loader.h" -#include "shared/os-compatibility.h" -#include "shared/cairo-util.h" -#include "shared/timespec-util.h" -#include "fullscreen-shell-unstable-v1-client-protocol.h" -#include "xdg-shell-client-protocol.h" -#include "presentation-time-server-protocol.h" -#include "linux-dmabuf.h" -#include <libweston/windowed-output-api.h> - -#define WINDOW_TITLE "Weston Compositor" - -static const uint32_t wayland_formats[] = { - DRM_FORMAT_ARGB8888, -}; - -struct wayland_backend { - struct weston_backend base; - struct weston_compositor *compositor; - - struct { - struct wl_display *wl_display; - struct wl_registry *registry; - struct wl_compositor *compositor; - struct wl_shell *shell; - struct xdg_wm_base *xdg_wm_base; - struct zwp_fullscreen_shell_v1 *fshell; - struct wl_shm *shm; - - struct wl_list output_list; - - struct wl_event_source *wl_source; - uint32_t event_mask; - } parent; - - bool use_pixman; - bool sprawl_across_outputs; - bool fullscreen; - - struct theme *theme; - cairo_device_t *frame_device; - struct wl_cursor_theme *cursor_theme; - struct wl_cursor *cursor; - - struct wl_list input_list; -}; - -struct wayland_output { - struct weston_output base; - - struct { - bool draw_initial_frame; - struct wl_surface *surface; - - struct wl_output *output; - uint32_t global_id; - - struct wl_shell_surface *shell_surface; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; - int configure_width, configure_height; - bool wait_for_configure; - } parent; - - int keyboard_count; - - char *title; - struct frame *frame; - - struct { - struct wl_egl_window *egl_window; - struct { - cairo_surface_t *top; - cairo_surface_t *left; - cairo_surface_t *right; - cairo_surface_t *bottom; - } border; - } gl; - - struct { - struct wl_list buffers; - struct wl_list free_buffers; - } shm; - - struct weston_mode mode; - - struct wl_callback *frame_cb; -}; - -struct wayland_parent_output { - struct wayland_backend *backend; /**< convenience */ - struct wayland_head *head; - struct wl_list link; - - struct wl_output *global; - uint32_t id; - - struct { - char *make; - char *model; - int32_t width, height; - uint32_t subpixel; - } physical; - - int32_t x, y; - uint32_t transform; - uint32_t scale; - - struct wl_callback *sync_cb; /**< wl_output < 2 done replacement */ - - struct wl_list mode_list; - struct weston_mode *preferred_mode; - struct weston_mode *current_mode; -}; - -struct wayland_head { - struct weston_head base; - struct wayland_parent_output *parent_output; -}; - -struct wayland_shm_buffer { - struct wayland_output *output; - struct wl_list link; - struct wl_list free_link; - - struct wl_buffer *buffer; - void *data; - size_t size; - pixman_region32_t damage; /**< in global coords */ - int frame_damaged; - - pixman_image_t *pm_image; - cairo_surface_t *c_surface; -}; - -struct wayland_input { - struct weston_seat base; - struct wayland_backend *backend; - struct wl_list link; - - struct { - struct wl_seat *seat; - struct wl_pointer *pointer; - struct wl_keyboard *keyboard; - struct wl_touch *touch; - - struct { - struct wl_surface *surface; - int32_t hx, hy; - } cursor; - } parent; - - struct weston_touch_device *touch_device; - - enum weston_key_state_update keyboard_state_update; - uint32_t key_serial; - uint32_t enter_serial; - uint32_t touch_points; - bool touch_active; - bool has_focus; - int seat_version; - - struct wayland_output *output; - struct wayland_output *touch_focus; - struct wayland_output *keyboard_focus; - - struct weston_pointer_axis_event vert, horiz; -}; - -struct gl_renderer_interface *gl_renderer; - -static inline struct wayland_head * -to_wayland_head(struct weston_head *base) -{ - return container_of(base, struct wayland_head, base); -} - -static inline struct wayland_output * -to_wayland_output(struct weston_output *base) -{ - return container_of(base, struct wayland_output, base); -} - -static inline struct wayland_backend * -to_wayland_backend(struct weston_compositor *base) -{ - return container_of(base->backend, struct wayland_backend, base); -} - -static void -wayland_shm_buffer_destroy(struct wayland_shm_buffer *buffer) -{ - cairo_surface_destroy(buffer->c_surface); - pixman_image_unref(buffer->pm_image); - - wl_buffer_destroy(buffer->buffer); - munmap(buffer->data, buffer->size); - - pixman_region32_fini(&buffer->damage); - - wl_list_remove(&buffer->link); - wl_list_remove(&buffer->free_link); - free(buffer); -} - -static void -buffer_release(void *data, struct wl_buffer *buffer) -{ - struct wayland_shm_buffer *sb = data; - - if (sb->output) { - wl_list_insert(&sb->output->shm.free_buffers, &sb->free_link); - } else { - wayland_shm_buffer_destroy(sb); - } -} - -static const struct wl_buffer_listener buffer_listener = { - buffer_release -}; - -static struct wayland_shm_buffer * -wayland_output_get_shm_buffer(struct wayland_output *output) -{ - struct wayland_backend *b = - to_wayland_backend(output->base.compositor); - struct wl_shm *shm = b->parent.shm; - struct wayland_shm_buffer *sb; - - struct wl_shm_pool *pool; - int width, height, stride; - int32_t fx, fy; - int fd; - unsigned char *data; - - if (!wl_list_empty(&output->shm.free_buffers)) { - sb = container_of(output->shm.free_buffers.next, - struct wayland_shm_buffer, free_link); - wl_list_remove(&sb->free_link); - wl_list_init(&sb->free_link); - - return sb; - } - - if (output->frame) { - width = frame_width(output->frame); - height = frame_height(output->frame); - } else { - width = output->base.current_mode->width; - height = output->base.current_mode->height; - } - - stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - - fd = os_create_anonymous_file(height * stride); - if (fd < 0) { - weston_log("could not create an anonymous file buffer: %s\n", - strerror(errno)); - return NULL; - } - - data = mmap(NULL, height * stride, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) { - weston_log("could not mmap %d memory for data: %s\n", height * stride, - strerror(errno)); - close(fd); - return NULL; - } - - sb = zalloc(sizeof *sb); - if (sb == NULL) { - weston_log("could not zalloc %zu memory for sb: %s\n", sizeof *sb, - strerror(errno)); - close(fd); - munmap(data, height * stride); - return NULL; - } - - sb->output = output; - wl_list_init(&sb->free_link); - wl_list_insert(&output->shm.buffers, &sb->link); - - pixman_region32_init(&sb->damage); - pixman_region32_copy(&sb->damage, &output->base.region); - sb->frame_damaged = 1; - - sb->data = data; - sb->size = height * stride; - - pool = wl_shm_create_pool(shm, fd, sb->size); - - sb->buffer = wl_shm_pool_create_buffer(pool, 0, - width, height, - stride, - WL_SHM_FORMAT_ARGB8888); - wl_buffer_add_listener(sb->buffer, &buffer_listener, sb); - wl_shm_pool_destroy(pool); - close(fd); - - memset(data, 0, sb->size); - - sb->c_surface = - cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, - width, height, stride); - - fx = 0; - fy = 0; - if (output->frame) - frame_interior(output->frame, &fx, &fy, 0, 0); - sb->pm_image = - pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, - (uint32_t *)(data + fy * stride) + fx, - stride); - - return sb; -} - -static void -frame_done(void *data, struct wl_callback *callback, uint32_t time) -{ - struct wayland_output *output = data; - struct timespec ts; - - assert(callback == output->frame_cb); - wl_callback_destroy(callback); - output->frame_cb = NULL; - - /* XXX: use the presentation extension for proper timings */ - - /* - * This is the fallback case, where Presentation extension is not - * available from the parent compositor. We do not know the base for - * 'time', so we cannot feed it to finish_frame(). Do the only thing - * we can, and pretend finish_frame time is when we process this - * event. - */ - weston_compositor_read_presentation_clock(output->base.compositor, &ts); - weston_output_finish_frame(&output->base, &ts, 0); -} - -static const struct wl_callback_listener frame_listener = { - frame_done -}; - -static void -draw_initial_frame(struct wayland_output *output) -{ - struct wayland_shm_buffer *sb; - - sb = wayland_output_get_shm_buffer(output); - - /* If we are rendering with GL, then orphan it so that it gets - * destroyed immediately */ - if (output->gl.egl_window) - sb->output = NULL; - - wl_surface_attach(output->parent.surface, sb->buffer, 0, 0); - wl_surface_damage(output->parent.surface, 0, 0, - output->base.current_mode->width, - output->base.current_mode->height); -} - -#ifdef ENABLE_EGL -static void -wayland_output_update_gl_border(struct wayland_output *output) -{ - int32_t ix, iy, iwidth, iheight, fwidth, fheight; - cairo_t *cr; - - if (!output->frame) - return; - if (!(frame_status(output->frame) & FRAME_STATUS_REPAINT)) - return; - - fwidth = frame_width(output->frame); - fheight = frame_height(output->frame); - frame_interior(output->frame, &ix, &iy, &iwidth, &iheight); - - if (!output->gl.border.top) - output->gl.border.top = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - fwidth, iy); - cr = cairo_create(output->gl.border.top); - frame_repaint(output->frame, cr); - cairo_destroy(cr); - gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_TOP, - fwidth, iy, - cairo_image_surface_get_stride(output->gl.border.top) / 4, - cairo_image_surface_get_data(output->gl.border.top)); - - - if (!output->gl.border.left) - output->gl.border.left = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - ix, 1); - cr = cairo_create(output->gl.border.left); - cairo_translate(cr, 0, -iy); - frame_repaint(output->frame, cr); - cairo_destroy(cr); - gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_LEFT, - ix, 1, - cairo_image_surface_get_stride(output->gl.border.left) / 4, - cairo_image_surface_get_data(output->gl.border.left)); - - - if (!output->gl.border.right) - output->gl.border.right = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - fwidth - (ix + iwidth), 1); - cr = cairo_create(output->gl.border.right); - cairo_translate(cr, -(iwidth + ix), -iy); - frame_repaint(output->frame, cr); - cairo_destroy(cr); - gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_RIGHT, - fwidth - (ix + iwidth), 1, - cairo_image_surface_get_stride(output->gl.border.right) / 4, - cairo_image_surface_get_data(output->gl.border.right)); - - - if (!output->gl.border.bottom) - output->gl.border.bottom = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - fwidth, fheight - (iy + iheight)); - cr = cairo_create(output->gl.border.bottom); - cairo_translate(cr, 0, -(iy + iheight)); - frame_repaint(output->frame, cr); - cairo_destroy(cr); - gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_BOTTOM, - fwidth, fheight - (iy + iheight), - cairo_image_surface_get_stride(output->gl.border.bottom) / 4, - cairo_image_surface_get_data(output->gl.border.bottom)); -} -#endif - -static int -wayland_output_start_repaint_loop(struct weston_output *output_base) -{ - struct wayland_output *output = to_wayland_output(output_base); - struct wayland_backend *wb = - to_wayland_backend(output->base.compositor); - - /* If this is the initial frame, we need to attach a buffer so that - * the compositor can map the surface and include it in its render - * loop. If the surface doesn't end up in the render loop, the frame - * callback won't be invoked. The buffer is transparent and of the - * same size as the future real output buffer. */ - if (output->parent.draw_initial_frame) { - output->parent.draw_initial_frame = false; - - draw_initial_frame(output); - } - - output->frame_cb = wl_surface_frame(output->parent.surface); - wl_callback_add_listener(output->frame_cb, &frame_listener, output); - wl_surface_commit(output->parent.surface); - wl_display_flush(wb->parent.wl_display); - - return 0; -} - -#ifdef ENABLE_EGL -static int -wayland_output_repaint_gl(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) -{ - struct wayland_output *output = to_wayland_output(output_base); - struct weston_compositor *ec = output->base.compositor; - - output->frame_cb = wl_surface_frame(output->parent.surface); - wl_callback_add_listener(output->frame_cb, &frame_listener, output); - - wayland_output_update_gl_border(output); - - ec->renderer->repaint_output(&output->base, damage); - - pixman_region32_subtract(&ec->primary_plane.damage, - &ec->primary_plane.damage, damage); - return 0; -} -#endif - -static void -wayland_output_update_shm_border(struct wayland_shm_buffer *buffer) -{ - int32_t ix, iy, iwidth, iheight, fwidth, fheight; - cairo_t *cr; - - if (!buffer->output->frame || !buffer->frame_damaged) - return; - - cr = cairo_create(buffer->c_surface); - - frame_interior(buffer->output->frame, &ix, &iy, &iwidth, &iheight); - fwidth = frame_width(buffer->output->frame); - fheight = frame_height(buffer->output->frame); - - /* Set the clip so we don't unnecisaraly damage the surface */ - cairo_move_to(cr, ix, iy); - cairo_rel_line_to(cr, iwidth, 0); - cairo_rel_line_to(cr, 0, iheight); - cairo_rel_line_to(cr, -iwidth, 0); - cairo_line_to(cr, ix, iy); - cairo_line_to(cr, 0, iy); - cairo_line_to(cr, 0, fheight); - cairo_line_to(cr, fwidth, fheight); - cairo_line_to(cr, fwidth, 0); - cairo_line_to(cr, 0, 0); - cairo_line_to(cr, 0, iy); - cairo_close_path(cr); - cairo_clip(cr); - - /* Draw using a pattern so that the final result gets clipped */ - cairo_push_group(cr); - frame_repaint(buffer->output->frame, cr); - cairo_pop_group_to_source(cr); - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_paint(cr); - - cairo_destroy(cr); -} - -static void -wayland_shm_buffer_attach(struct wayland_shm_buffer *sb) -{ - pixman_region32_t damage; - pixman_box32_t *rects; - int32_t ix, iy, iwidth, iheight, fwidth, fheight; - int i, n; - - pixman_region32_init(&damage); - pixman_region32_copy(&damage, &sb->damage); - pixman_region32_translate(&damage, -sb->output->base.x, - -sb->output->base.y); - - weston_transformed_region(sb->output->base.width, - sb->output->base.height, - sb->output->base.transform, - sb->output->base.current_scale, - &damage, &damage); - - if (sb->output->frame) { - frame_interior(sb->output->frame, &ix, &iy, &iwidth, &iheight); - fwidth = frame_width(sb->output->frame); - fheight = frame_height(sb->output->frame); - - pixman_region32_translate(&damage, ix, iy); - - if (sb->frame_damaged) { - pixman_region32_union_rect(&damage, &damage, - 0, 0, fwidth, iy); - pixman_region32_union_rect(&damage, &damage, - 0, iy, ix, iheight); - pixman_region32_union_rect(&damage, &damage, - ix + iwidth, iy, - fwidth - (ix + iwidth), iheight); - pixman_region32_union_rect(&damage, &damage, - 0, iy + iheight, - fwidth, fheight - (iy + iheight)); - } - } - - rects = pixman_region32_rectangles(&damage, &n); - wl_surface_attach(sb->output->parent.surface, sb->buffer, 0, 0); - for (i = 0; i < n; ++i) - wl_surface_damage(sb->output->parent.surface, rects[i].x1, - rects[i].y1, rects[i].x2 - rects[i].x1, - rects[i].y2 - rects[i].y1); - - if (sb->output->frame) - pixman_region32_fini(&damage); -} - -static int -wayland_output_repaint_pixman(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) -{ - struct wayland_output *output = to_wayland_output(output_base); - struct wayland_backend *b = - to_wayland_backend(output->base.compositor); - struct wayland_shm_buffer *sb; - - if (output->frame) { - if (frame_status(output->frame) & FRAME_STATUS_REPAINT) - wl_list_for_each(sb, &output->shm.buffers, link) - sb->frame_damaged = 1; - } - - wl_list_for_each(sb, &output->shm.buffers, link) - pixman_region32_union(&sb->damage, &sb->damage, damage); - - sb = wayland_output_get_shm_buffer(output); - - wayland_output_update_shm_border(sb); - pixman_renderer_output_set_buffer(output_base, sb->pm_image); - b->compositor->renderer->repaint_output(output_base, &sb->damage); - - wayland_shm_buffer_attach(sb); - - output->frame_cb = wl_surface_frame(output->parent.surface); - wl_callback_add_listener(output->frame_cb, &frame_listener, output); - wl_surface_commit(output->parent.surface); - wl_display_flush(b->parent.wl_display); - - pixman_region32_fini(&sb->damage); - pixman_region32_init(&sb->damage); - sb->frame_damaged = 0; - - pixman_region32_subtract(&b->compositor->primary_plane.damage, - &b->compositor->primary_plane.damage, damage); - return 0; -} - -static void -wayland_backend_destroy_output_surface(struct wayland_output *output) -{ - assert(output->parent.surface); - - if (output->parent.xdg_toplevel) { - xdg_toplevel_destroy(output->parent.xdg_toplevel); - output->parent.xdg_toplevel = NULL; - } - - if (output->parent.xdg_surface) { - xdg_surface_destroy(output->parent.xdg_surface); - output->parent.xdg_surface = NULL; - } - - if (output->parent.shell_surface) { - wl_shell_surface_destroy(output->parent.shell_surface); - output->parent.shell_surface = NULL; - } - - wl_surface_destroy(output->parent.surface); - output->parent.surface = NULL; -} - -static void -wayland_output_destroy_shm_buffers(struct wayland_output *output) -{ - struct wayland_shm_buffer *buffer, *next; - - /* Throw away any remaining SHM buffers */ - wl_list_for_each_safe(buffer, next, &output->shm.free_buffers, free_link) - wayland_shm_buffer_destroy(buffer); - /* These will get thrown away when they get released */ - wl_list_for_each(buffer, &output->shm.buffers, link) - buffer->output = NULL; -} - -static int -wayland_output_disable(struct weston_output *base) -{ - struct wayland_output *output = to_wayland_output(base); - struct wayland_backend *b = to_wayland_backend(base->compositor); - - if (!output->base.enabled) - return 0; - - if (b->use_pixman) { - pixman_renderer_output_destroy(&output->base); -#ifdef ENABLE_EGL - } else { - gl_renderer->output_destroy(&output->base); - wl_egl_window_destroy(output->gl.egl_window); -#endif - } - - wayland_output_destroy_shm_buffers(output); - - wayland_backend_destroy_output_surface(output); - - if (output->frame) - frame_destroy(output->frame); - - cairo_surface_destroy(output->gl.border.top); - cairo_surface_destroy(output->gl.border.left); - cairo_surface_destroy(output->gl.border.right); - cairo_surface_destroy(output->gl.border.bottom); - - return 0; -} - -static void -wayland_output_destroy(struct weston_output *base) -{ - struct wayland_output *output = to_wayland_output(base); - - wayland_output_disable(&output->base); - - weston_output_release(&output->base); - - if (output->frame_cb) - wl_callback_destroy(output->frame_cb); - - free(output->title); - free(output); -} - -static const struct wl_shell_surface_listener shell_surface_listener; - -#ifdef ENABLE_EGL -static int -wayland_output_init_gl_renderer(struct wayland_output *output) -{ - int32_t fwidth = 0, fheight = 0; - - if (output->frame) { - fwidth = frame_width(output->frame); - fheight = frame_height(output->frame); - } else { - fwidth = output->base.current_mode->width; - fheight = output->base.current_mode->height; - } - - output->gl.egl_window = - wl_egl_window_create(output->parent.surface, - fwidth, fheight); - if (!output->gl.egl_window) { - weston_log("failure to create wl_egl_window\n"); - return -1; - } - - if (gl_renderer->output_window_create(&output->base, - output->gl.egl_window, - output->gl.egl_window, - wayland_formats, - ARRAY_LENGTH(wayland_formats)) < 0) - goto cleanup_window; - - return 0; - -cleanup_window: - wl_egl_window_destroy(output->gl.egl_window); - return -1; -} -#endif - -static int -wayland_output_init_pixman_renderer(struct wayland_output *output) -{ - return pixman_renderer_output_create(&output->base, - PIXMAN_RENDERER_OUTPUT_USE_SHADOW); -} - -static void -wayland_output_resize_surface(struct wayland_output *output) -{ - struct wayland_backend *b = - to_wayland_backend(output->base.compositor); - int32_t ix, iy, iwidth, iheight; - int32_t width, height; - struct wl_region *region; - - width = output->base.current_mode->width; - height = output->base.current_mode->height; - - if (output->frame) { - frame_resize_inside(output->frame, width, height); - - frame_input_rect(output->frame, &ix, &iy, &iwidth, &iheight); - region = wl_compositor_create_region(b->parent.compositor); - wl_region_add(region, ix, iy, iwidth, iheight); - wl_surface_set_input_region(output->parent.surface, region); - wl_region_destroy(region); - - if (output->parent.xdg_surface) { - xdg_surface_set_window_geometry(output->parent.xdg_surface, - ix, - iy, - iwidth, - iheight); - } - - frame_opaque_rect(output->frame, &ix, &iy, &iwidth, &iheight); - region = wl_compositor_create_region(b->parent.compositor); - wl_region_add(region, ix, iy, iwidth, iheight); - wl_surface_set_opaque_region(output->parent.surface, region); - wl_region_destroy(region); - - width = frame_width(output->frame); - height = frame_height(output->frame); - } else { - region = wl_compositor_create_region(b->parent.compositor); - wl_region_add(region, 0, 0, width, height); - wl_surface_set_input_region(output->parent.surface, region); - wl_region_destroy(region); - - region = wl_compositor_create_region(b->parent.compositor); - wl_region_add(region, 0, 0, width, height); - wl_surface_set_opaque_region(output->parent.surface, region); - wl_region_destroy(region); - - if (output->parent.xdg_surface) { - xdg_surface_set_window_geometry(output->parent.xdg_surface, - 0, - 0, - width, - height); - } - } - -#ifdef ENABLE_EGL - if (output->gl.egl_window) { - wl_egl_window_resize(output->gl.egl_window, - width, height, 0, 0); - - /* These will need to be re-created due to the resize */ - gl_renderer->output_set_border(&output->base, - GL_RENDERER_BORDER_TOP, - 0, 0, 0, NULL); - cairo_surface_destroy(output->gl.border.top); - output->gl.border.top = NULL; - gl_renderer->output_set_border(&output->base, - GL_RENDERER_BORDER_LEFT, - 0, 0, 0, NULL); - cairo_surface_destroy(output->gl.border.left); - output->gl.border.left = NULL; - gl_renderer->output_set_border(&output->base, - GL_RENDERER_BORDER_RIGHT, - 0, 0, 0, NULL); - cairo_surface_destroy(output->gl.border.right); - output->gl.border.right = NULL; - gl_renderer->output_set_border(&output->base, - GL_RENDERER_BORDER_BOTTOM, - 0, 0, 0, NULL); - cairo_surface_destroy(output->gl.border.bottom); - output->gl.border.bottom = NULL; - } -#endif - - wayland_output_destroy_shm_buffers(output); -} - -static int -wayland_output_set_windowed(struct wayland_output *output) -{ - struct wayland_backend *b = - to_wayland_backend(output->base.compositor); - - if (output->frame) - return 0; - - if (!b->theme) { - b->theme = theme_create(); - if (!b->theme) - return -1; - } - output->frame = frame_create(b->theme, 100, 100, - FRAME_BUTTON_CLOSE, output->title, NULL); - if (!output->frame) - return -1; - - if (output->keyboard_count) - frame_set_flag(output->frame, FRAME_FLAG_ACTIVE); - - wayland_output_resize_surface(output); - - if (output->parent.xdg_toplevel) { - xdg_toplevel_unset_fullscreen(output->parent.xdg_toplevel); - } else if (output->parent.shell_surface) { - wl_shell_surface_set_toplevel(output->parent.shell_surface); - } else { - abort(); - } - - return 0; -} - -static void -wayland_output_set_fullscreen(struct wayland_output *output, - enum wl_shell_surface_fullscreen_method method, - uint32_t framerate, struct wl_output *target) -{ - if (output->frame) { - frame_destroy(output->frame); - output->frame = NULL; - } - - wayland_output_resize_surface(output); - - if (output->parent.xdg_toplevel) { - xdg_toplevel_set_fullscreen(output->parent.xdg_toplevel, target); - } else if (output->parent.shell_surface) { - wl_shell_surface_set_fullscreen(output->parent.shell_surface, - method, framerate, target); - } else { - abort(); - } -} - -static struct weston_mode * -wayland_output_choose_mode(struct wayland_output *output, - struct weston_mode *ref_mode) -{ - struct weston_mode *mode; - - /* First look for an exact match */ - wl_list_for_each(mode, &output->base.mode_list, link) - if (mode->width == ref_mode->width && - mode->height == ref_mode->height && - mode->refresh == ref_mode->refresh) - return mode; - - /* If we can't find an exact match, ignore refresh and try again */ - wl_list_for_each(mode, &output->base.mode_list, link) - if (mode->width == ref_mode->width && - mode->height == ref_mode->height) - return mode; - - /* Yeah, we failed */ - return NULL; -} - -enum mode_status { - MODE_STATUS_UNKNOWN, - MODE_STATUS_SUCCESS, - MODE_STATUS_FAIL, - MODE_STATUS_CANCEL, -}; - -static void -mode_feedback_successful(void *data, - struct zwp_fullscreen_shell_mode_feedback_v1 *fb) -{ - enum mode_status *value = data; - - printf("Mode switch successful\n"); - - *value = MODE_STATUS_SUCCESS; -} - -static void -mode_feedback_failed(void *data, struct zwp_fullscreen_shell_mode_feedback_v1 *fb) -{ - enum mode_status *value = data; - - printf("Mode switch failed\n"); - - *value = MODE_STATUS_FAIL; -} - -static void -mode_feedback_cancelled(void *data, struct zwp_fullscreen_shell_mode_feedback_v1 *fb) -{ - enum mode_status *value = data; - - printf("Mode switch cancelled\n"); - - *value = MODE_STATUS_CANCEL; -} - -struct zwp_fullscreen_shell_mode_feedback_v1_listener mode_feedback_listener = { - mode_feedback_successful, - mode_feedback_failed, - mode_feedback_cancelled, -}; - -static enum mode_status -wayland_output_fullscreen_shell_mode_feedback(struct wayland_output *output, - struct weston_mode *mode) -{ - struct wayland_backend *b = to_wayland_backend(output->base.compositor); - struct zwp_fullscreen_shell_mode_feedback_v1 *mode_feedback; - enum mode_status mode_status; - int ret = 0; - - mode_feedback = - zwp_fullscreen_shell_v1_present_surface_for_mode(b->parent.fshell, - output->parent.surface, - output->parent.output, - mode->refresh); - - zwp_fullscreen_shell_mode_feedback_v1_add_listener(mode_feedback, - &mode_feedback_listener, - &mode_status); - - output->parent.draw_initial_frame = false; - draw_initial_frame(output); - wl_surface_commit(output->parent.surface); - - mode_status = MODE_STATUS_UNKNOWN; - while (mode_status == MODE_STATUS_UNKNOWN && ret >= 0) - ret = wl_display_dispatch(b->parent.wl_display); - - zwp_fullscreen_shell_mode_feedback_v1_destroy(mode_feedback); - - return mode_status; -} - -static int -wayland_output_switch_mode(struct weston_output *output_base, - struct weston_mode *mode) -{ - struct wayland_output *output = to_wayland_output(output_base); - struct wayland_backend *b; - struct wl_surface *old_surface; - struct weston_mode *old_mode; - enum mode_status mode_status; - - if (output_base == NULL) { - weston_log("output is NULL.\n"); - return -1; - } - - if (mode == NULL) { - weston_log("mode is NULL.\n"); - return -1; - } - - b = to_wayland_backend(output_base->compositor); - - if (output->parent.xdg_surface || output->parent.shell_surface || !b->parent.fshell) - return -1; - - mode = wayland_output_choose_mode(output, mode); - if (mode == NULL) - return -1; - - if (output->base.current_mode == mode) - return 0; - - old_mode = output->base.current_mode; - old_surface = output->parent.surface; - output->base.current_mode = mode; - output->parent.surface = - wl_compositor_create_surface(b->parent.compositor); - wl_surface_set_user_data(output->parent.surface, output); - - /* Blow the old buffers because we changed size/surfaces */ - wayland_output_resize_surface(output); - - mode_status = wayland_output_fullscreen_shell_mode_feedback(output, mode); - - /* This should kick-start things again */ - wayland_output_start_repaint_loop(&output->base); - - if (mode_status == MODE_STATUS_FAIL) { - output->base.current_mode = old_mode; - wl_surface_destroy(output->parent.surface); - output->parent.surface = old_surface; - wayland_output_resize_surface(output); - - return -1; - } - - old_mode->flags &= ~WL_OUTPUT_MODE_CURRENT; - output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT; - - if (b->use_pixman) { - pixman_renderer_output_destroy(output_base); - if (wayland_output_init_pixman_renderer(output) < 0) - goto err_output; -#ifdef ENABLE_EGL - } else { - gl_renderer->output_destroy(output_base); - wl_egl_window_destroy(output->gl.egl_window); - if (wayland_output_init_gl_renderer(output) < 0) - goto err_output; -#endif - } - wl_surface_destroy(old_surface); - - weston_output_schedule_repaint(&output->base); - - return 0; - -err_output: - /* XXX */ - return -1; -} - -static void -handle_xdg_surface_configure(void *data, struct xdg_surface *surface, - uint32_t serial) -{ - xdg_surface_ack_configure(surface, serial); -} - -static const struct xdg_surface_listener xdg_surface_listener = { - handle_xdg_surface_configure -}; - -static void -handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *toplevel, - int32_t width, int32_t height, - struct wl_array *states) -{ - struct wayland_output *output = data; - - output->parent.configure_width = width; - output->parent.configure_height = height; - - output->parent.wait_for_configure = false; - /* FIXME: implement resizing */ -} - -static void -handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) -{ - struct wayland_output *output = data; - struct weston_compositor *compositor = output->base.compositor; - - wayland_output_destroy(&output->base); - - if (wl_list_empty(&compositor->output_list)) - weston_compositor_exit(compositor); -} - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - handle_xdg_toplevel_configure, - handle_xdg_toplevel_close, -}; - -static int -wayland_backend_create_output_surface(struct wayland_output *output) -{ - struct wayland_backend *b = to_wayland_backend(output->base.compositor); - - assert(!output->parent.surface); - - output->parent.surface = - wl_compositor_create_surface(b->parent.compositor); - if (!output->parent.surface) - return -1; - - wl_surface_set_user_data(output->parent.surface, output); - - output->parent.draw_initial_frame = true; - - if (b->parent.xdg_wm_base) { - output->parent.xdg_surface = - xdg_wm_base_get_xdg_surface(b->parent.xdg_wm_base, - output->parent.surface); - xdg_surface_add_listener(output->parent.xdg_surface, - &xdg_surface_listener, output); - - output->parent.xdg_toplevel = - xdg_surface_get_toplevel(output->parent.xdg_surface); - xdg_toplevel_add_listener(output->parent.xdg_toplevel, - &xdg_toplevel_listener, output); - - xdg_toplevel_set_title(output->parent.xdg_toplevel, output->title); - - wl_surface_commit(output->parent.surface); - - output->parent.wait_for_configure = true; - - while (output->parent.wait_for_configure) - wl_display_dispatch(b->parent.wl_display); - - weston_log("wayland-backend: Using xdg_wm_base\n"); - } - else if (b->parent.shell) { - output->parent.shell_surface = - wl_shell_get_shell_surface(b->parent.shell, - output->parent.surface); - if (!output->parent.shell_surface) { - wl_surface_destroy(output->parent.surface); - return -1; - } - - wl_shell_surface_add_listener(output->parent.shell_surface, - &shell_surface_listener, output); - - weston_log("wayland-backend: Using wl_shell\n"); - } - - return 0; -} - -static int -wayland_output_enable(struct weston_output *base) -{ - struct wayland_output *output = to_wayland_output(base); - struct wayland_backend *b = to_wayland_backend(base->compositor); - enum mode_status mode_status; - int ret = 0; - - weston_log("Creating %dx%d wayland output at (%d, %d)\n", - output->base.current_mode->width, - output->base.current_mode->height, - output->base.x, output->base.y); - - if (!output->parent.surface) - ret = wayland_backend_create_output_surface(output); - - if (ret < 0) - return -1; - - wl_list_init(&output->shm.buffers); - wl_list_init(&output->shm.free_buffers); - - if (b->use_pixman) { - if (wayland_output_init_pixman_renderer(output) < 0) - goto err_output; - - output->base.repaint = wayland_output_repaint_pixman; -#ifdef ENABLE_EGL - } else { - if (wayland_output_init_gl_renderer(output) < 0) - goto err_output; - - output->base.repaint = wayland_output_repaint_gl; -#endif - } - - output->base.start_repaint_loop = wayland_output_start_repaint_loop; - output->base.assign_planes = NULL; - output->base.set_backlight = NULL; - output->base.set_dpms = NULL; - output->base.switch_mode = wayland_output_switch_mode; - - if (b->sprawl_across_outputs) { - if (b->parent.fshell) { - wayland_output_resize_surface(output); - - mode_status = wayland_output_fullscreen_shell_mode_feedback(output, &output->mode); - - if (mode_status == MODE_STATUS_FAIL) { - zwp_fullscreen_shell_v1_present_surface(b->parent.fshell, - output->parent.surface, - ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_CENTER, - output->parent.output); - - output->parent.draw_initial_frame = true; - } - } else { - wayland_output_set_fullscreen(output, - WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER, - output->mode.refresh, output->parent.output); - } - } else if (b->fullscreen) { - wayland_output_set_fullscreen(output, 0, 0, NULL); - } else { - wayland_output_set_windowed(output); - } - - return 0; - -err_output: - wayland_backend_destroy_output_surface(output); - - return -1; -} - -static int -wayland_output_setup_for_parent_output(struct wayland_output *output, - struct wayland_parent_output *poutput); - -static int -wayland_output_setup_fullscreen(struct wayland_output *output, - struct wayland_head *head); - -static int -wayland_output_attach_head(struct weston_output *output_base, - struct weston_head *head_base) -{ - struct wayland_backend *b = to_wayland_backend(output_base->compositor); - struct wayland_output *output = to_wayland_output(output_base); - struct wayland_head *head = to_wayland_head(head_base); - - if (!wl_list_empty(&output->base.head_list)) - return -1; - - if (head->parent_output) { - if (wayland_output_setup_for_parent_output(output, - head->parent_output) < 0) - return -1; - } else if (b->fullscreen) { - if (wayland_output_setup_fullscreen(output, head) < 0) - return -1; - } else { - /* A floating window, nothing to do. */ - } - - return 0; -} - -static void -wayland_output_detach_head(struct weston_output *output_base, - struct weston_head *head) -{ - struct wayland_output *output = to_wayland_output(output_base); - - /* Rely on the disable hook if the output was enabled. We do not - * support cloned heads, so detaching is guaranteed to disable the - * output. - */ - if (output->base.enabled) - return; - - /* undo setup fullscreen */ - if (output->parent.surface) - wayland_backend_destroy_output_surface(output); -} - -static struct weston_output * -wayland_output_create(struct weston_compositor *compositor, const char *name) -{ - struct wayland_output *output; - char *title; - - /* name can't be NULL. */ - assert(name); - - output = zalloc(sizeof *output); - if (output == NULL) { - perror("zalloc"); - return NULL; - } - - if (asprintf(&title, "%s - %s", WINDOW_TITLE, name) < 0) { - free(output); - return NULL; - } - output->title = title; - - weston_output_init(&output->base, compositor, name); - - output->base.destroy = wayland_output_destroy; - output->base.disable = wayland_output_disable; - output->base.enable = wayland_output_enable; - output->base.attach_head = wayland_output_attach_head; - output->base.detach_head = wayland_output_detach_head; - - weston_compositor_add_pending_output(&output->base, compositor); - - return &output->base; -} - -static struct wayland_head * -wayland_head_create(struct weston_compositor *compositor, const char *name) -{ - struct wayland_head *head; - - assert(name); - - head = zalloc(sizeof *head); - if (!head) - return NULL; - - weston_head_init(&head->base, name); - weston_head_set_connection_status(&head->base, true); - weston_compositor_add_head(compositor, &head->base); - - return head; -} - -static int -wayland_head_create_windowed(struct weston_compositor *compositor, - const char *name) -{ - if (!wayland_head_create(compositor, name)) - return -1; - - return 0; -} - -static int -wayland_head_create_for_parent_output(struct weston_compositor *compositor, - struct wayland_parent_output *poutput) -{ - struct wayland_head *head; - char name[100]; - int ret; - - ret = snprintf(name, sizeof(name), "wlparent-%d", poutput->id); - if (ret < 1 || (unsigned)ret >= sizeof(name)) - return -1; - - head = wayland_head_create(compositor, name); - if (!head) - return -1; - - assert(!poutput->head); - head->parent_output = poutput; - poutput->head = head; - - weston_head_set_monitor_strings(&head->base, - poutput->physical.make, - poutput->physical.model, NULL); - weston_head_set_physical_size(&head->base, - poutput->physical.width, - poutput->physical.height); - - return 0; -} - -static void -wayland_head_destroy(struct wayland_head *head) -{ - if (head->parent_output) - head->parent_output->head = NULL; - - weston_head_release(&head->base); - free(head); -} - -static int -wayland_output_set_size(struct weston_output *base, int width, int height) -{ - struct wayland_output *output = to_wayland_output(base); - struct weston_head *head; - int output_width, output_height; - - /* We can only be called once. */ - assert(!output->base.current_mode); - - /* Make sure we have scale set. */ - assert(output->base.scale); - - if (width < 1) { - weston_log("Invalid width \"%d\" for output %s\n", - width, output->base.name); - return -1; - } - - if (height < 1) { - weston_log("Invalid height \"%d\" for output %s\n", - height, output->base.name); - return -1; - } - - wl_list_for_each(head, &output->base.head_list, output_link) { - weston_head_set_monitor_strings(head, "wayland", "none", NULL); - - /* XXX: Calculate proper size. */ - weston_head_set_physical_size(head, width, height); - } - - output_width = width * output->base.scale; - output_height = height * output->base.scale; - - output->mode.flags = - WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; - - output->mode.width = output_width; - output->mode.height = output_height; - output->mode.refresh = 60000; - wl_list_insert(&output->base.mode_list, &output->mode.link); - - output->base.current_mode = &output->mode; - - return 0; -} - -static int -wayland_output_setup_for_parent_output(struct wayland_output *output, - struct wayland_parent_output *poutput) -{ - struct weston_mode *mode; - - if (poutput->current_mode) { - mode = poutput->current_mode; - } else if (poutput->preferred_mode) { - mode = poutput->preferred_mode; - } else if (!wl_list_empty(&poutput->mode_list)) { - mode = container_of(poutput->mode_list.next, - struct weston_mode, link); - } else { - weston_log("No valid modes found. Skipping output.\n"); - return -1; - } - - output->base.scale = 1; - output->base.transform = WL_OUTPUT_TRANSFORM_NORMAL; - - output->parent.output = poutput->global; - - wl_list_insert_list(&output->base.mode_list, &poutput->mode_list); - wl_list_init(&poutput->mode_list); - - /* No other mode should have CURRENT already. */ - mode->flags |= WL_OUTPUT_MODE_CURRENT; - output->base.current_mode = mode; - - /* output->mode is unused in this path. */ - - return 0; -} - -static int -wayland_output_setup_fullscreen(struct wayland_output *output, - struct wayland_head *head) -{ - struct wayland_backend *b = to_wayland_backend(output->base.compositor); - int width = 0, height = 0; - - output->base.scale = 1; - output->base.transform = WL_OUTPUT_TRANSFORM_NORMAL; - - if (wayland_backend_create_output_surface(output) < 0) - return -1; - - /* What should size be set if conditional is false? */ - if (b->parent.xdg_wm_base || b->parent.shell) { - if (output->parent.xdg_toplevel) - xdg_toplevel_set_fullscreen(output->parent.xdg_toplevel, - output->parent.output); - else if (output->parent.shell_surface) - wl_shell_surface_set_fullscreen(output->parent.shell_surface, - 0, 0, NULL); - - wl_display_roundtrip(b->parent.wl_display); - - width = output->parent.configure_width; - height = output->parent.configure_height; - } - - if (wayland_output_set_size(&output->base, width, height) < 0) - goto err_set_size; - - /* The head is not attached yet, so set_size did not set these. */ - weston_head_set_monitor_strings(&head->base, "wayland", "none", NULL); - /* XXX: Calculate proper size. */ - weston_head_set_physical_size(&head->base, width, height); - - return 0; - -err_set_size: - wayland_backend_destroy_output_surface(output); - - return -1; -} - -static void -shell_surface_ping(void *data, struct wl_shell_surface *shell_surface, - uint32_t serial) -{ - wl_shell_surface_pong(shell_surface, serial); -} - -static void -shell_surface_configure(void *data, struct wl_shell_surface *shell_surface, - uint32_t edges, int32_t width, int32_t height) -{ - struct wayland_output *output = data; - - output->parent.configure_width = width; - output->parent.configure_height = height; - - /* FIXME: implement resizing */ -} - -static void -shell_surface_popup_done(void *data, struct wl_shell_surface *shell_surface) -{ -} - -static const struct wl_shell_surface_listener shell_surface_listener = { - shell_surface_ping, - shell_surface_configure, - shell_surface_popup_done -}; - -/* Events received from the wayland-server this compositor is client of: */ - -/* parent input interface */ -static void -input_set_cursor(struct wayland_input *input) -{ - - struct wl_buffer *buffer; - struct wl_cursor_image *image; - - if (!input->backend->cursor) - return; /* Couldn't load the cursor. Can't set it */ - - image = input->backend->cursor->images[0]; - buffer = wl_cursor_image_get_buffer(image); - if (!buffer) - return; - - wl_pointer_set_cursor(input->parent.pointer, input->enter_serial, - input->parent.cursor.surface, - image->hotspot_x, image->hotspot_y); - - wl_surface_attach(input->parent.cursor.surface, buffer, 0, 0); - wl_surface_damage(input->parent.cursor.surface, 0, 0, - image->width, image->height); - wl_surface_commit(input->parent.cursor.surface); -} - -static void -input_handle_pointer_enter(void *data, struct wl_pointer *pointer, - uint32_t serial, struct wl_surface *surface, - wl_fixed_t fixed_x, wl_fixed_t fixed_y) -{ - struct wayland_input *input = data; - int32_t fx, fy; - enum theme_location location; - double x, y; - - if (!surface) { - input->output = NULL; - input->has_focus = false; - notify_pointer_focus(&input->base, NULL, 0, 0); - return; - } - - x = wl_fixed_to_double(fixed_x); - y = wl_fixed_to_double(fixed_y); - - /* XXX: If we get a modifier event immediately before the focus, - * we should try to keep the same serial. */ - input->enter_serial = serial; - input->output = wl_surface_get_user_data(surface); - - if (input->output->frame) { - location = frame_pointer_enter(input->output->frame, input, - x, y); - frame_interior(input->output->frame, &fx, &fy, NULL, NULL); - x -= fx; - y -= fy; - - if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT) - weston_output_schedule_repaint(&input->output->base); - } else { - location = THEME_LOCATION_CLIENT_AREA; - } - - weston_output_transform_coordinate(&input->output->base, x, y, &x, &y); - - if (location == THEME_LOCATION_CLIENT_AREA) { - input->has_focus = true; - notify_pointer_focus(&input->base, &input->output->base, x, y); - wl_pointer_set_cursor(input->parent.pointer, - input->enter_serial, NULL, 0, 0); - } else { - input->has_focus = false; - notify_pointer_focus(&input->base, NULL, 0, 0); - input_set_cursor(input); - } -} - -static void -input_handle_pointer_leave(void *data, struct wl_pointer *pointer, - uint32_t serial, struct wl_surface *surface) -{ - struct wayland_input *input = data; - - if (!input->output) - return; - - if (input->output->frame) { - frame_pointer_leave(input->output->frame, input); - - if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT) - weston_output_schedule_repaint(&input->output->base); - } - - notify_pointer_focus(&input->base, NULL, 0, 0); - input->output = NULL; - input->has_focus = false; -} - -static void -input_handle_motion(void *data, struct wl_pointer *pointer, - uint32_t time, wl_fixed_t fixed_x, wl_fixed_t fixed_y) -{ - struct wayland_input *input = data; - int32_t fx, fy; - enum theme_location location; - bool want_frame = false; - double x, y; - struct timespec ts; - - if (!input->output) - return; - - x = wl_fixed_to_double(fixed_x); - y = wl_fixed_to_double(fixed_y); - - if (input->output->frame) { - location = frame_pointer_motion(input->output->frame, input, - x, y); - frame_interior(input->output->frame, &fx, &fy, NULL, NULL); - x -= fx; - y -= fy; - - if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT) - weston_output_schedule_repaint(&input->output->base); - } else { - location = THEME_LOCATION_CLIENT_AREA; - } - - weston_output_transform_coordinate(&input->output->base, x, y, &x, &y); - - if (input->has_focus && location != THEME_LOCATION_CLIENT_AREA) { - input_set_cursor(input); - notify_pointer_focus(&input->base, NULL, 0, 0); - input->has_focus = false; - want_frame = true; - } else if (!input->has_focus && - location == THEME_LOCATION_CLIENT_AREA) { - wl_pointer_set_cursor(input->parent.pointer, - input->enter_serial, NULL, 0, 0); - notify_pointer_focus(&input->base, &input->output->base, x, y); - input->has_focus = true; - want_frame = true; - } - - if (location == THEME_LOCATION_CLIENT_AREA) { - timespec_from_msec(&ts, time); - notify_motion_absolute(&input->base, &ts, x, y); - want_frame = true; - } - - if (want_frame && input->seat_version < WL_POINTER_FRAME_SINCE_VERSION) - notify_pointer_frame(&input->base); -} - -static void -input_handle_button(void *data, struct wl_pointer *pointer, - uint32_t serial, uint32_t time, uint32_t button, - enum wl_pointer_button_state state) -{ - struct wayland_input *input = data; - enum theme_location location; - struct timespec ts; - - if (!input->output) - return; - - if (input->output->frame) { - location = frame_pointer_button(input->output->frame, input, - button, state); - - if (frame_status(input->output->frame) & FRAME_STATUS_MOVE) { - if (input->output->parent.xdg_toplevel) - xdg_toplevel_move(input->output->parent.xdg_toplevel, - input->parent.seat, serial); - else if (input->output->parent.shell_surface) - wl_shell_surface_move(input->output->parent.shell_surface, - input->parent.seat, serial); - frame_status_clear(input->output->frame, - FRAME_STATUS_MOVE); - return; - } - - if (frame_status(input->output->frame) & FRAME_STATUS_CLOSE) { - wayland_output_destroy(&input->output->base); - input->output = NULL; - input->keyboard_focus = NULL; - - if (wl_list_empty(&input->backend->compositor->output_list)) - weston_compositor_exit(input->backend->compositor); - - return; - } - - if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT) - weston_output_schedule_repaint(&input->output->base); - } else { - location = THEME_LOCATION_CLIENT_AREA; - } - - if (location == THEME_LOCATION_CLIENT_AREA) { - timespec_from_msec(&ts, time); - notify_button(&input->base, &ts, button, state); - if (input->seat_version < WL_POINTER_FRAME_SINCE_VERSION) - notify_pointer_frame(&input->base); - } -} - -static void -input_handle_axis(void *data, struct wl_pointer *pointer, - uint32_t time, uint32_t axis, wl_fixed_t value) -{ - struct wayland_input *input = data; - struct weston_pointer_axis_event weston_event; - struct timespec ts; - - weston_event.axis = axis; - weston_event.value = wl_fixed_to_double(value); - weston_event.has_discrete = false; - - if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL && - input->vert.has_discrete) { - weston_event.has_discrete = true; - weston_event.discrete = input->vert.discrete; - input->vert.has_discrete = false; - } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL && - input->horiz.has_discrete) { - weston_event.has_discrete = true; - weston_event.discrete = input->horiz.discrete; - input->horiz.has_discrete = false; - } - - timespec_from_msec(&ts, time); - - notify_axis(&input->base, &ts, &weston_event); - - if (input->seat_version < WL_POINTER_FRAME_SINCE_VERSION) - notify_pointer_frame(&input->base); -} - -static void -input_handle_frame(void *data, struct wl_pointer *pointer) -{ - struct wayland_input *input = data; - - notify_pointer_frame(&input->base); -} - -static void -input_handle_axis_source(void *data, struct wl_pointer *pointer, - uint32_t source) -{ - struct wayland_input *input = data; - - notify_axis_source(&input->base, source); -} - -static void -input_handle_axis_stop(void *data, struct wl_pointer *pointer, - uint32_t time, uint32_t axis) -{ - struct wayland_input *input = data; - struct weston_pointer_axis_event weston_event; - struct timespec ts; - - weston_event.axis = axis; - weston_event.value = 0; - - timespec_from_msec(&ts, time); - - notify_axis(&input->base, &ts, &weston_event); -} - -static void -input_handle_axis_discrete(void *data, struct wl_pointer *pointer, - uint32_t axis, int32_t discrete) -{ - struct wayland_input *input = data; - - if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - input->vert.has_discrete = true; - input->vert.discrete = discrete; - } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - input->horiz.has_discrete = true; - input->horiz.discrete = discrete; - } -} - -static const struct wl_pointer_listener pointer_listener = { - input_handle_pointer_enter, - input_handle_pointer_leave, - input_handle_motion, - input_handle_button, - input_handle_axis, - input_handle_frame, - input_handle_axis_source, - input_handle_axis_stop, - input_handle_axis_discrete, -}; - -static void -input_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, - int fd, uint32_t size) -{ - struct wayland_input *input = data; - struct xkb_keymap *keymap; - char *map_str; - - if (!data) { - close(fd); - return; - } - - if (format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { - map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - if (map_str == MAP_FAILED) { - weston_log("mmap failed: %s\n", strerror(errno)); - goto error; - } - - keymap = xkb_keymap_new_from_string(input->backend->compositor->xkb_context, - map_str, - XKB_KEYMAP_FORMAT_TEXT_V1, - 0); - munmap(map_str, size); - - if (!keymap) { - weston_log("failed to compile keymap\n"); - goto error; - } - - input->keyboard_state_update = STATE_UPDATE_NONE; - } else if (format == WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) { - weston_log("No keymap provided; falling back to default\n"); - keymap = NULL; - input->keyboard_state_update = STATE_UPDATE_AUTOMATIC; - } else { - weston_log("Invalid keymap\n"); - goto error; - } - - close(fd); - - if (weston_seat_get_keyboard(&input->base)) - weston_seat_update_keymap(&input->base, keymap); - else - weston_seat_init_keyboard(&input->base, keymap); - - xkb_keymap_unref(keymap); - - return; - -error: - wl_keyboard_release(input->parent.keyboard); - close(fd); -} - -static void -input_handle_keyboard_enter(void *data, - struct wl_keyboard *keyboard, - uint32_t serial, - struct wl_surface *surface, - struct wl_array *keys) -{ - struct wayland_input *input = data; - struct wayland_output *focus; - - focus = input->keyboard_focus; - if (focus) { - /* This shouldn't happen */ - focus->keyboard_count--; - if (!focus->keyboard_count && focus->frame) - frame_unset_flag(focus->frame, FRAME_FLAG_ACTIVE); - if (frame_status(focus->frame) & FRAME_STATUS_REPAINT) - weston_output_schedule_repaint(&focus->base); - } - - if (!surface) { - input->keyboard_focus = NULL; - return; - } - - input->keyboard_focus = wl_surface_get_user_data(surface); - input->keyboard_focus->keyboard_count++; - - focus = input->keyboard_focus; - if (focus->frame) { - frame_set_flag(focus->frame, FRAME_FLAG_ACTIVE); - if (frame_status(focus->frame) & FRAME_STATUS_REPAINT) - weston_output_schedule_repaint(&focus->base); - } - - - /* XXX: If we get a modifier event immediately before the focus, - * we should try to keep the same serial. */ - notify_keyboard_focus_in(&input->base, keys, - STATE_UPDATE_AUTOMATIC); -} - -static void -input_handle_keyboard_leave(void *data, - struct wl_keyboard *keyboard, - uint32_t serial, - struct wl_surface *surface) -{ - struct wayland_input *input = data; - struct wayland_output *focus; - - notify_keyboard_focus_out(&input->base); - - focus = input->keyboard_focus; - if (!focus) - return; - - focus->keyboard_count--; - if (!focus->keyboard_count && focus->frame) { - frame_unset_flag(focus->frame, FRAME_FLAG_ACTIVE); - if (frame_status(focus->frame) & FRAME_STATUS_REPAINT) - weston_output_schedule_repaint(&focus->base); - } - - input->keyboard_focus = NULL; -} - -static void -input_handle_key(void *data, struct wl_keyboard *keyboard, - uint32_t serial, uint32_t time, uint32_t key, uint32_t state) -{ - struct wayland_input *input = data; - struct timespec ts; - - if (!input->keyboard_focus) - return; - - timespec_from_msec(&ts, time); - - input->key_serial = serial; - notify_key(&input->base, &ts, key, - state ? WL_KEYBOARD_KEY_STATE_PRESSED : - WL_KEYBOARD_KEY_STATE_RELEASED, - input->keyboard_state_update); -} - -static void -input_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial_in, uint32_t mods_depressed, - uint32_t mods_latched, uint32_t mods_locked, - uint32_t group) -{ - struct weston_keyboard *keyboard; - struct wayland_input *input = data; - struct wayland_backend *b = input->backend; - uint32_t serial_out; - - /* If we get a key event followed by a modifier event with the - * same serial number, then we try to preserve those semantics by - * reusing the same serial number on the way out too. */ - if (serial_in == input->key_serial) - serial_out = wl_display_get_serial(b->compositor->wl_display); - else - serial_out = wl_display_next_serial(b->compositor->wl_display); - - keyboard = weston_seat_get_keyboard(&input->base); - xkb_state_update_mask(keyboard->xkb_state.state, - mods_depressed, mods_latched, - mods_locked, 0, 0, group); - notify_modifiers(&input->base, serial_out); -} - -static void -input_handle_repeat_info(void *data, struct wl_keyboard *keyboard, - int32_t rate, int32_t delay) -{ - struct wayland_input *input = data; - struct wayland_backend *b = input->backend; - - b->compositor->kb_repeat_rate = rate; - b->compositor->kb_repeat_delay = delay; -} - -static const struct wl_keyboard_listener keyboard_listener = { - input_handle_keymap, - input_handle_keyboard_enter, - input_handle_keyboard_leave, - input_handle_key, - input_handle_modifiers, - input_handle_repeat_info, -}; - -static void -input_handle_touch_down(void *data, struct wl_touch *wl_touch, - uint32_t serial, uint32_t time, - struct wl_surface *surface, int32_t id, - wl_fixed_t fixed_x, wl_fixed_t fixed_y) -{ - struct wayland_input *input = data; - struct wayland_output *output; - enum theme_location location; - bool first_touch; - int32_t fx, fy; - double x, y; - struct timespec ts; - - x = wl_fixed_to_double(fixed_x); - y = wl_fixed_to_double(fixed_y); - - timespec_from_msec(&ts, time); - - first_touch = (input->touch_points == 0); - input->touch_points++; - - input->touch_focus = wl_surface_get_user_data(surface); - output = input->touch_focus; - if (!first_touch && !input->touch_active) - return; - - if (output->frame) { - location = frame_touch_down(output->frame, input, id, x, y); - - frame_interior(output->frame, &fx, &fy, NULL, NULL); - x -= fx; - y -= fy; - - if (frame_status(output->frame) & FRAME_STATUS_REPAINT) - weston_output_schedule_repaint(&output->base); - - if (first_touch && (frame_status(output->frame) & FRAME_STATUS_MOVE)) { - input->touch_points--; - if (output->parent.xdg_toplevel) - xdg_toplevel_move(output->parent.xdg_toplevel, - input->parent.seat, serial); - else if (output->parent.shell_surface) - wl_shell_surface_move(output->parent.shell_surface, - input->parent.seat, serial); - frame_status_clear(output->frame, - FRAME_STATUS_MOVE); - return; - } - - if (first_touch && location != THEME_LOCATION_CLIENT_AREA) - return; - } - - weston_output_transform_coordinate(&output->base, x, y, &x, &y); - - notify_touch(input->touch_device, &ts, id, x, y, WL_TOUCH_DOWN); - input->touch_active = true; -} - -static void -input_handle_touch_up(void *data, struct wl_touch *wl_touch, - uint32_t serial, uint32_t time, int32_t id) -{ - struct wayland_input *input = data; - struct wayland_output *output = input->touch_focus; - bool active = input->touch_active; - struct timespec ts; - - timespec_from_msec(&ts, time); - - input->touch_points--; - if (input->touch_points == 0) { - input->touch_focus = NULL; - input->touch_active = false; - } - - if (!output) - return; - - if (output->frame) { - frame_touch_up(output->frame, input, id); - - if (frame_status(output->frame) & FRAME_STATUS_CLOSE) { - wayland_output_destroy(&output->base); - input->touch_focus = NULL; - input->keyboard_focus = NULL; - if (wl_list_empty(&input->backend->compositor->output_list)) - weston_compositor_exit(input->backend->compositor); - - return; - } - if (frame_status(output->frame) & FRAME_STATUS_REPAINT) - weston_output_schedule_repaint(&output->base); - } - - if (active) - notify_touch(input->touch_device, &ts, id, 0, 0, WL_TOUCH_UP); -} - -static void -input_handle_touch_motion(void *data, struct wl_touch *wl_touch, - uint32_t time, int32_t id, - wl_fixed_t fixed_x, wl_fixed_t fixed_y) -{ - struct wayland_input *input = data; - struct wayland_output *output = input->touch_focus; - int32_t fx, fy; - double x, y; - struct timespec ts; - - x = wl_fixed_to_double(fixed_x); - y = wl_fixed_to_double(fixed_y); - timespec_from_msec(&ts, time); - - if (!output || !input->touch_active) - return; - - if (output->frame) { - frame_interior(output->frame, &fx, &fy, NULL, NULL); - x -= fx; - y -= fy; - } - - weston_output_transform_coordinate(&output->base, x, y, &x, &y); - - notify_touch(input->touch_device, &ts, id, x, y, WL_TOUCH_MOTION); -} - -static void -input_handle_touch_frame(void *data, struct wl_touch *wl_touch) -{ - struct wayland_input *input = data; - - if (!input->touch_focus || !input->touch_active) - return; - - notify_touch_frame(input->touch_device); -} - -static void -input_handle_touch_cancel(void *data, struct wl_touch *wl_touch) -{ - struct wayland_input *input = data; - - if (!input->touch_focus || !input->touch_active) - return; - - notify_touch_cancel(input->touch_device); -} - -static const struct wl_touch_listener touch_listener = { - input_handle_touch_down, - input_handle_touch_up, - input_handle_touch_motion, - input_handle_touch_frame, - input_handle_touch_cancel, -}; - - -static struct weston_touch_device * -create_touch_device(struct wayland_input *input) -{ - struct weston_touch_device *touch_device; - char str[128]; - - /* manufacture a unique'ish name */ - snprintf(str, sizeof str, "wayland-touch[%u]", - wl_proxy_get_id((struct wl_proxy *)input->parent.seat)); - - touch_device = weston_touch_create_touch_device(input->base.touch_state, - str, NULL, NULL); - - return touch_device; -} - -static void -input_handle_capabilities(void *data, struct wl_seat *seat, - enum wl_seat_capability caps) -{ - struct wayland_input *input = data; - - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->parent.pointer) { - input->parent.pointer = wl_seat_get_pointer(seat); - wl_pointer_set_user_data(input->parent.pointer, input); - wl_pointer_add_listener(input->parent.pointer, - &pointer_listener, input); - weston_seat_init_pointer(&input->base); - } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->parent.pointer) { - if (input->seat_version >= WL_POINTER_RELEASE_SINCE_VERSION) - wl_pointer_release(input->parent.pointer); - else - wl_pointer_destroy(input->parent.pointer); - input->parent.pointer = NULL; - weston_seat_release_pointer(&input->base); - } - - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->parent.keyboard) { - input->parent.keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_set_user_data(input->parent.keyboard, input); - wl_keyboard_add_listener(input->parent.keyboard, - &keyboard_listener, input); - } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->parent.keyboard) { - if (input->seat_version >= WL_KEYBOARD_RELEASE_SINCE_VERSION) - wl_keyboard_release(input->parent.keyboard); - else - wl_keyboard_destroy(input->parent.keyboard); - input->parent.keyboard = NULL; - weston_seat_release_keyboard(&input->base); - } - - if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->parent.touch) { - input->parent.touch = wl_seat_get_touch(seat); - wl_touch_set_user_data(input->parent.touch, input); - wl_touch_add_listener(input->parent.touch, - &touch_listener, input); - weston_seat_init_touch(&input->base); - input->touch_device = create_touch_device(input); - } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->parent.touch) { - weston_touch_device_destroy(input->touch_device); - input->touch_device = NULL; - if (input->seat_version >= WL_TOUCH_RELEASE_SINCE_VERSION) - wl_touch_release(input->parent.touch); - else - wl_touch_destroy(input->parent.touch); - input->parent.touch = NULL; - weston_seat_release_touch(&input->base); - } -} - -static void -input_handle_name(void *data, struct wl_seat *seat, - const char *name) -{ -} - -static const struct wl_seat_listener seat_listener = { - input_handle_capabilities, - input_handle_name, -}; - -static void -display_add_seat(struct wayland_backend *b, uint32_t id, uint32_t available_version) -{ - struct wayland_input *input; - uint32_t version = MIN(available_version, 4); - - input = zalloc(sizeof *input); - if (input == NULL) - return; - - weston_seat_init(&input->base, b->compositor, "default"); - input->backend = b; - input->parent.seat = wl_registry_bind(b->parent.registry, id, - &wl_seat_interface, version); - input->seat_version = version; - wl_list_insert(b->input_list.prev, &input->link); - - wl_seat_add_listener(input->parent.seat, &seat_listener, input); - wl_seat_set_user_data(input->parent.seat, input); - - input->parent.cursor.surface = - wl_compositor_create_surface(b->parent.compositor); - - input->vert.axis = WL_POINTER_AXIS_VERTICAL_SCROLL; - input->horiz.axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL; -} - -static void -wayland_parent_output_geometry(void *data, struct wl_output *output_proxy, - int32_t x, int32_t y, - int32_t physical_width, int32_t physical_height, - int32_t subpixel, const char *make, - const char *model, int32_t transform) -{ - struct wayland_parent_output *output = data; - - output->x = x; - output->y = y; - output->physical.width = physical_width; - output->physical.height = physical_height; - output->physical.subpixel = subpixel; - - free(output->physical.make); - output->physical.make = strdup(make); - free(output->physical.model); - output->physical.model = strdup(model); - - output->transform = transform; -} - -static struct weston_mode * -find_mode(struct wl_list *list, int32_t width, int32_t height, uint32_t refresh) -{ - struct weston_mode *mode; - - wl_list_for_each(mode, list, link) { - if (mode->width == width && mode->height == height && - mode->refresh == refresh) - return mode; - } - - mode = zalloc(sizeof *mode); - if (!mode) - return NULL; - - mode->width = width; - mode->height = height; - mode->refresh = refresh; - wl_list_insert(list, &mode->link); - - return mode; -} - -static struct weston_output * -wayland_parent_output_get_enabled_output(struct wayland_parent_output *poutput) -{ - struct wayland_head *head = poutput->head; - - if (!head) - return NULL; - - if (!weston_head_is_enabled(&head->base)) - return NULL; - - return weston_head_get_output(&head->base); -} - -static void -wayland_parent_output_mode(void *data, struct wl_output *wl_output_proxy, - uint32_t flags, int32_t width, int32_t height, - int32_t refresh) -{ - struct wayland_parent_output *output = data; - struct weston_output *enabled_output; - struct weston_mode *mode; - - enabled_output = wayland_parent_output_get_enabled_output(output); - if (enabled_output) { - mode = find_mode(&enabled_output->mode_list, - width, height, refresh); - if (!mode) - return; - mode->flags = flags; - /* Do a mode-switch on current mode change? */ - } else { - mode = find_mode(&output->mode_list, width, height, refresh); - if (!mode) - return; - mode->flags = flags; - if (flags & WL_OUTPUT_MODE_CURRENT) - output->current_mode = mode; - if (flags & WL_OUTPUT_MODE_PREFERRED) - output->preferred_mode = mode; - } -} - -static const struct wl_output_listener output_listener = { - wayland_parent_output_geometry, - wayland_parent_output_mode -}; - -static void -output_sync_callback(void *data, struct wl_callback *callback, uint32_t unused) -{ - struct wayland_parent_output *output = data; - - assert(output->sync_cb == callback); - wl_callback_destroy(callback); - output->sync_cb = NULL; - - assert(output->backend->sprawl_across_outputs); - - wayland_head_create_for_parent_output(output->backend->compositor, output); -} - -static const struct wl_callback_listener output_sync_listener = { - output_sync_callback -}; - -static void -wayland_backend_register_output(struct wayland_backend *b, uint32_t id) -{ - struct wayland_parent_output *output; - - output = zalloc(sizeof *output); - if (!output) - return; - - output->backend = b; - output->id = id; - output->global = wl_registry_bind(b->parent.registry, id, - &wl_output_interface, 1); - if (!output->global) { - free(output); - return; - } - - wl_output_add_listener(output->global, &output_listener, output); - - output->scale = 0; - output->transform = WL_OUTPUT_TRANSFORM_NORMAL; - output->physical.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; - wl_list_init(&output->mode_list); - wl_list_insert(&b->parent.output_list, &output->link); - - if (b->sprawl_across_outputs) { - output->sync_cb = wl_display_sync(b->parent.wl_display); - wl_callback_add_listener(output->sync_cb, - &output_sync_listener, output); - } -} - -static void -wayland_parent_output_destroy(struct wayland_parent_output *output) -{ - struct weston_mode *mode, *next; - - if (output->sync_cb) - wl_callback_destroy(output->sync_cb); - - if (output->head) - wayland_head_destroy(output->head); - - wl_output_destroy(output->global); - free(output->physical.make); - free(output->physical.model); - - wl_list_for_each_safe(mode, next, &output->mode_list, link) { - wl_list_remove(&mode->link); - free(mode); - } - - wl_list_remove(&output->link); - free(output); -} - -static void -xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) -{ - xdg_wm_base_pong(shell, serial); -} - -static const struct xdg_wm_base_listener wm_base_listener = { - xdg_wm_base_ping, -}; - -static void -registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, - const char *interface, uint32_t version) -{ - struct wayland_backend *b = data; - - if (strcmp(interface, "wl_compositor") == 0) { - b->parent.compositor = - wl_registry_bind(registry, name, - &wl_compositor_interface, - MIN(version, 4)); - } else if (strcmp(interface, "xdg_wm_base") == 0) { - b->parent.xdg_wm_base = - wl_registry_bind(registry, name, - &xdg_wm_base_interface, 1); - xdg_wm_base_add_listener(b->parent.xdg_wm_base, - &wm_base_listener, b); - } else if (strcmp(interface, "wl_shell") == 0) { - b->parent.shell = - wl_registry_bind(registry, name, - &wl_shell_interface, 1); - } else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) { - b->parent.fshell = - wl_registry_bind(registry, name, - &zwp_fullscreen_shell_v1_interface, 1); - } else if (strcmp(interface, "wl_seat") == 0) { - display_add_seat(b, name, version); - } else if (strcmp(interface, "wl_output") == 0) { - wayland_backend_register_output(b, name); - } else if (strcmp(interface, "wl_shm") == 0) { - b->parent.shm = - wl_registry_bind(registry, name, &wl_shm_interface, 1); - } -} - -static void -registry_handle_global_remove(void *data, struct wl_registry *registry, - uint32_t name) -{ - struct wayland_backend *b = data; - struct wayland_parent_output *output, *next; - - wl_list_for_each_safe(output, next, &b->parent.output_list, link) - if (output->id == name) - wayland_parent_output_destroy(output); -} - -static const struct wl_registry_listener registry_listener = { - registry_handle_global, - registry_handle_global_remove -}; - -static int -wayland_backend_handle_event(int fd, uint32_t mask, void *data) -{ - struct wayland_backend *b = data; - int count = 0; - - if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { - weston_compositor_exit(b->compositor); - return 0; - } - - if (mask & WL_EVENT_READABLE) - count = wl_display_dispatch(b->parent.wl_display); - if (mask & WL_EVENT_WRITABLE) - wl_display_flush(b->parent.wl_display); - - if (mask == 0) { - count = wl_display_dispatch_pending(b->parent.wl_display); - wl_display_flush(b->parent.wl_display); - } - - return count; -} - -static void -wayland_destroy(struct weston_compositor *ec) -{ - struct wayland_backend *b = to_wayland_backend(ec); - struct weston_head *base, *next; - - wl_event_source_remove(b->parent.wl_source); - - weston_compositor_shutdown(ec); - - wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) - wayland_head_destroy(to_wayland_head(base)); - - if (b->parent.shm) - wl_shm_destroy(b->parent.shm); - - if (b->parent.xdg_wm_base) - xdg_wm_base_destroy(b->parent.xdg_wm_base); - - if (b->parent.shell) - wl_shell_destroy(b->parent.shell); - - if (b->parent.fshell) - zwp_fullscreen_shell_v1_release(b->parent.fshell); - - if (b->parent.compositor) - wl_compositor_destroy(b->parent.compositor); - - if (b->theme) - theme_destroy(b->theme); - - if (b->frame_device) - cairo_device_destroy(b->frame_device); - - wl_cursor_theme_destroy(b->cursor_theme); - - wl_registry_destroy(b->parent.registry); - wl_display_flush(b->parent.wl_display); - wl_display_disconnect(b->parent.wl_display); - - free(b); -} - -static const char *left_ptrs[] = { - "left_ptr", - "default", - "top_left_arrow", - "left-arrow" -}; - -static void -create_cursor(struct wayland_backend *b, - struct weston_wayland_backend_config *config) -{ - unsigned int i; - - b->cursor_theme = wl_cursor_theme_load(config->cursor_theme, - config->cursor_size, - b->parent.shm); - if (!b->cursor_theme) { - fprintf(stderr, "could not load cursor theme\n"); - return; - } - - b->cursor = NULL; - for (i = 0; !b->cursor && i < ARRAY_LENGTH(left_ptrs); ++i) - b->cursor = wl_cursor_theme_get_cursor(b->cursor_theme, - left_ptrs[i]); - if (!b->cursor) { - fprintf(stderr, "could not load left cursor\n"); - return; - } -} - -static void -fullscreen_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, void *data) -{ - struct wayland_backend *b = data; - struct wayland_input *input = NULL; - - wl_list_for_each(input, &b->input_list, link) - if (&input->base == keyboard->seat) - break; - - if (!input || !input->output) - return; - - if (input->output->frame) - wayland_output_set_fullscreen(input->output, 0, 0, NULL); - else - wayland_output_set_windowed(input->output); - - weston_output_schedule_repaint(&input->output->base); -} - -static struct wayland_backend * -wayland_backend_create(struct weston_compositor *compositor, - struct weston_wayland_backend_config *new_config) -{ - struct wayland_backend *b; - struct wl_event_loop *loop; - int fd; - - b = zalloc(sizeof *b); - if (b == NULL) - return NULL; - - b->compositor = compositor; - compositor->backend = &b->base; - - if (weston_compositor_set_presentation_clock_software(compositor) < 0) - goto err_compositor; - - b->parent.wl_display = wl_display_connect(new_config->display_name); - if (b->parent.wl_display == NULL) { - weston_log("Error: Failed to connect to parent Wayland compositor: %s\n", - strerror(errno)); - weston_log_continue(STAMP_SPACE "display option: %s, WAYLAND_DISPLAY=%s\n", - new_config->display_name ?: "(none)", - getenv("WAYLAND_DISPLAY") ?: "(not set)"); - goto err_compositor; - } - - wl_list_init(&b->parent.output_list); - wl_list_init(&b->input_list); - b->parent.registry = wl_display_get_registry(b->parent.wl_display); - wl_registry_add_listener(b->parent.registry, ®istry_listener, b); - wl_display_roundtrip(b->parent.wl_display); - - create_cursor(b, new_config); - -#ifdef ENABLE_EGL - b->use_pixman = new_config->use_pixman; -#else - b->use_pixman = true; -#endif - b->fullscreen = new_config->fullscreen; - - if (!b->use_pixman) { - gl_renderer = weston_load_module("gl-renderer.so", - "gl_renderer_interface"); - if (!gl_renderer) - b->use_pixman = true; - } - - if (!b->use_pixman) { - if (gl_renderer->display_create(compositor, - EGL_PLATFORM_WAYLAND_KHR, - b->parent.wl_display, - EGL_WINDOW_BIT, - wayland_formats, - ARRAY_LENGTH(wayland_formats)) < 0) { - weston_log("Failed to initialize the GL renderer; " - "falling back to pixman.\n"); - b->use_pixman = true; - } - } - - if (b->use_pixman) { - if (pixman_renderer_init(compositor) < 0) { - weston_log("Failed to initialize pixman renderer\n"); - goto err_display; - } - } - - b->base.destroy = wayland_destroy; - b->base.create_output = wayland_output_create; - - loop = wl_display_get_event_loop(compositor->wl_display); - - fd = wl_display_get_fd(b->parent.wl_display); - b->parent.wl_source = - wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, - wayland_backend_handle_event, b); - if (b->parent.wl_source == NULL) - goto err_display; - - wl_event_source_check(b->parent.wl_source); - - if (compositor->renderer->import_dmabuf) { - if (linux_dmabuf_setup(compositor) < 0) - weston_log("Error: initializing dmabuf " - "support failed.\n"); - } - - return b; -err_display: - wl_display_disconnect(b->parent.wl_display); -err_compositor: - weston_compositor_shutdown(compositor); - free(b); - return NULL; -} - -static void -wayland_backend_destroy(struct wayland_backend *b) -{ - wl_display_disconnect(b->parent.wl_display); - - if (b->theme) - theme_destroy(b->theme); - if (b->frame_device) - cairo_device_destroy(b->frame_device); - wl_cursor_theme_destroy(b->cursor_theme); - - weston_compositor_shutdown(b->compositor); - free(b); -} - -static const struct weston_windowed_output_api windowed_api = { - wayland_output_set_size, - wayland_head_create_windowed, -}; - -static void -config_init_to_defaults(struct weston_wayland_backend_config *config) -{ -} - -WL_EXPORT int -weston_backend_init(struct weston_compositor *compositor, - struct weston_backend_config *config_base) -{ - struct wayland_backend *b; - struct wayland_parent_output *poutput; - struct weston_wayland_backend_config new_config; - int ret; - - if (config_base == NULL || - config_base->struct_version != WESTON_WAYLAND_BACKEND_CONFIG_VERSION || - config_base->struct_size > sizeof(struct weston_wayland_backend_config)) { - weston_log("wayland backend config structure is invalid\n"); - return -1; - } - - config_init_to_defaults(&new_config); - memcpy(&new_config, config_base, config_base->struct_size); - - b = wayland_backend_create(compositor, &new_config); - - if (!b) - return -1; - - if (new_config.sprawl || b->parent.fshell) { - b->sprawl_across_outputs = true; - wl_display_roundtrip(b->parent.wl_display); - - wl_list_for_each(poutput, &b->parent.output_list, link) - wayland_head_create_for_parent_output(compositor, poutput); - - return 0; - } - - if (new_config.fullscreen) { - if (!wayland_head_create(compositor, "wayland-fullscreen")) { - weston_log("Unable to create a fullscreen head.\n"); - goto err_outputs; - } - - return 0; - } - - ret = weston_plugin_api_register(compositor, WESTON_WINDOWED_OUTPUT_API_NAME, - &windowed_api, sizeof(windowed_api)); - - if (ret < 0) { - weston_log("Failed to register output API.\n"); - wayland_backend_destroy(b); - return -1; - } - - weston_compositor_add_key_binding(compositor, KEY_F, - MODIFIER_CTRL | MODIFIER_ALT, - fullscreen_binding, b); - return 0; - -err_outputs: - wayland_backend_destroy(b); - return -1; -} diff --git a/libweston/backend-x11/meson.build b/libweston/backend-x11/meson.build deleted file mode 100644 index 3e6ca54f..00000000 --- a/libweston/backend-x11/meson.build +++ /dev/null @@ -1,58 +0,0 @@ - -if not get_option('backend-x11') - subdir_done() -endif - -config_h.set('BUILD_X11_COMPOSITOR', '1') - -srcs_x11 = [ - 'x11.c', - presentation_time_server_protocol_h, -] - -dep_x11_xcb = dependency('xcb', version: '>= 1.8', required: false) -if not dep_x11_xcb.found() - error('x11-backend requires xcb >= 1.8 which was not found. Or, you can use \'-Dbackend-x11=false\'.') -endif - -deps_x11 = [ - dep_libweston_private, - dep_libdrm_headers, - dep_x11_xcb, - dep_lib_cairo_shared, - dep_pixman, -] - -foreach name : [ 'xcb-shm', 'x11', 'x11-xcb' ] - d = dependency(name, required: false) - if not d.found() - error('x11-backend requires @0@ which was not found. Or, you can use \'-Dbackend-x11=false\'.'.format(name)) - endif - deps_x11 += d -endforeach - -dep_xcb_xkb = dependency('xcb-xkb', version: '>= 1.9', required: false) -if dep_xcb_xkb.found() - deps_x11 += dep_xcb_xkb - config_h.set('HAVE_XCB_XKB', '1') -endif - -if get_option('renderer-gl') - if not dep_egl.found() - error('x11-backend + gl-renderer requires egl which was not found. Or, you can use \'-Dbackend-x11=false\' or \'-Drenderer-gl=false\'.') - endif - deps_x11 += dep_egl -endif - -plugin_x11 = shared_library( - 'x11-backend', - srcs_x11, - include_directories: common_inc, - dependencies: deps_x11, - name_prefix: '', - install: true, - install_dir: dir_module_libweston -) -env_modmap += 'x11-backend.so=@0@;'.format(plugin_x11.full_path()) - -install_headers(backend_x11_h, subdir: dir_include_libweston_install) diff --git a/libweston/backend-x11/x11.c b/libweston/backend-x11/x11.c deleted file mode 100644 index 5fda0568..00000000 --- a/libweston/backend-x11/x11.c +++ /dev/null @@ -1,1959 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2010-2011 Intel Corporation - * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com> - * - * 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 <assert.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> -#include <sys/time.h> -#include <sys/shm.h> -#include <linux/input.h> - -#include <drm_fourcc.h> -#include <xcb/xcb.h> -#include <xcb/shm.h> -#ifdef HAVE_XCB_XKB -#include <xcb/xkb.h> -#endif - -#include <X11/Xlib.h> -#include <X11/Xlib-xcb.h> - -#include <xkbcommon/xkbcommon.h> - -#include <libweston/libweston.h> -#include <libweston/backend-x11.h> -#include "shared/helpers.h" -#include "shared/image-loader.h" -#include "shared/timespec-util.h" -#include "shared/file-util.h" -#include "renderer-gl/gl-renderer.h" -#include "shared/weston-egl-ext.h" -#include "pixman-renderer.h" -#include "presentation-time-server-protocol.h" -#include "linux-dmabuf.h" -#include "linux-explicit-synchronization.h" -#include <libweston/windowed-output-api.h> - -#define DEFAULT_AXIS_STEP_DISTANCE 10 - -#define WINDOW_MIN_WIDTH 128 -#define WINDOW_MIN_HEIGHT 128 - -#define WINDOW_MAX_WIDTH 8192 -#define WINDOW_MAX_HEIGHT 8192 - -static const uint32_t x11_formats[] = { - DRM_FORMAT_XRGB8888, -}; - -struct x11_backend { - struct weston_backend base; - struct weston_compositor *compositor; - - Display *dpy; - xcb_connection_t *conn; - xcb_screen_t *screen; - xcb_cursor_t null_cursor; - struct wl_array keys; - struct wl_event_source *xcb_source; - struct xkb_keymap *xkb_keymap; - unsigned int has_xkb; - uint8_t xkb_event_base; - int fullscreen; - int no_input; - int use_pixman; - - int has_net_wm_state_fullscreen; - - /* We could map multi-pointer X to multiple wayland seats, but - * for now we only support core X input. */ - struct weston_seat core_seat; - double prev_x; - double prev_y; - - struct { - xcb_atom_t wm_protocols; - xcb_atom_t wm_normal_hints; - xcb_atom_t wm_size_hints; - xcb_atom_t wm_delete_window; - xcb_atom_t wm_class; - xcb_atom_t net_wm_name; - xcb_atom_t net_supporting_wm_check; - xcb_atom_t net_supported; - xcb_atom_t net_wm_icon; - xcb_atom_t net_wm_state; - xcb_atom_t net_wm_state_fullscreen; - xcb_atom_t string; - xcb_atom_t utf8_string; - xcb_atom_t cardinal; - xcb_atom_t xkb_names; - } atom; -}; - -struct x11_head { - struct weston_head base; -}; - -struct x11_output { - struct weston_output base; - - xcb_window_t window; - struct weston_mode mode; - struct weston_mode native; - struct wl_event_source *finish_frame_timer; - - xcb_gc_t gc; - xcb_shm_seg_t segment; - pixman_image_t *hw_surface; - int shm_id; - void *buf; - uint8_t depth; - int32_t scale; - bool resize_pending; - bool window_resized; -}; - -struct window_delete_data { - struct x11_backend *backend; - xcb_window_t window; -}; - -struct gl_renderer_interface *gl_renderer; - -static inline struct x11_head * -to_x11_head(struct weston_head *base) -{ - return container_of(base, struct x11_head, base); -} - -static inline struct x11_output * -to_x11_output(struct weston_output *base) -{ - return container_of(base, struct x11_output, base); -} - -static inline struct x11_backend * -to_x11_backend(struct weston_compositor *base) -{ - return container_of(base->backend, struct x11_backend, base); -} - -static xcb_screen_t * -x11_compositor_get_default_screen(struct x11_backend *b) -{ - xcb_screen_iterator_t iter; - int i, screen_nbr = XDefaultScreen(b->dpy); - - iter = xcb_setup_roots_iterator(xcb_get_setup(b->conn)); - for (i = 0; iter.rem; xcb_screen_next(&iter), i++) - if (i == screen_nbr) - return iter.data; - - return xcb_setup_roots_iterator(xcb_get_setup(b->conn)).data; -} - -static struct xkb_keymap * -x11_backend_get_keymap(struct x11_backend *b) -{ - xcb_get_property_cookie_t cookie; - xcb_get_property_reply_t *reply; - struct xkb_rule_names names; - struct xkb_keymap *ret; - const char *value_all, *value_part; - int length_all, length_part; - - memset(&names, 0, sizeof(names)); - - cookie = xcb_get_property(b->conn, 0, b->screen->root, - b->atom.xkb_names, b->atom.string, 0, 1024); - reply = xcb_get_property_reply(b->conn, cookie, NULL); - if (reply == NULL) - return NULL; - - value_all = xcb_get_property_value(reply); - length_all = xcb_get_property_value_length(reply); - value_part = value_all; - -#define copy_prop_value(to) \ - length_part = strlen(value_part); \ - if (value_part + length_part < (value_all + length_all) && \ - length_part > 0) \ - names.to = value_part; \ - value_part += length_part + 1; - - copy_prop_value(rules); - copy_prop_value(model); - copy_prop_value(layout); - copy_prop_value(variant); - copy_prop_value(options); -#undef copy_prop_value - - ret = xkb_keymap_new_from_names(b->compositor->xkb_context, &names, 0); - - free(reply); - return ret; -} - -static uint32_t -get_xkb_mod_mask(struct x11_backend *b, uint32_t in) -{ - struct weston_keyboard *keyboard = - weston_seat_get_keyboard(&b->core_seat); - struct weston_xkb_info *info = keyboard->xkb_info; - uint32_t ret = 0; - - if ((in & ShiftMask) && info->shift_mod != XKB_MOD_INVALID) - ret |= (1 << info->shift_mod); - if ((in & LockMask) && info->caps_mod != XKB_MOD_INVALID) - ret |= (1 << info->caps_mod); - if ((in & ControlMask) && info->ctrl_mod != XKB_MOD_INVALID) - ret |= (1 << info->ctrl_mod); - if ((in & Mod1Mask) && info->alt_mod != XKB_MOD_INVALID) - ret |= (1 << info->alt_mod); - if ((in & Mod2Mask) && info->mod2_mod != XKB_MOD_INVALID) - ret |= (1 << info->mod2_mod); - if ((in & Mod3Mask) && info->mod3_mod != XKB_MOD_INVALID) - ret |= (1 << info->mod3_mod); - if ((in & Mod4Mask) && info->super_mod != XKB_MOD_INVALID) - ret |= (1 << info->super_mod); - if ((in & Mod5Mask) && info->mod5_mod != XKB_MOD_INVALID) - ret |= (1 << info->mod5_mod); - - return ret; -} - -static void -x11_backend_setup_xkb(struct x11_backend *b) -{ -#ifndef HAVE_XCB_XKB - weston_log("XCB-XKB not available during build\n"); - b->has_xkb = 0; - b->xkb_event_base = 0; - return; -#else - struct weston_keyboard *keyboard; - const xcb_query_extension_reply_t *ext; - xcb_generic_error_t *error; - xcb_void_cookie_t select; - xcb_xkb_use_extension_cookie_t use_ext; - xcb_xkb_use_extension_reply_t *use_ext_reply; - xcb_xkb_per_client_flags_cookie_t pcf; - xcb_xkb_per_client_flags_reply_t *pcf_reply; - xcb_xkb_get_state_cookie_t state; - xcb_xkb_get_state_reply_t *state_reply; - uint32_t values[1] = { XCB_EVENT_MASK_PROPERTY_CHANGE }; - - b->has_xkb = 0; - b->xkb_event_base = 0; - - ext = xcb_get_extension_data(b->conn, &xcb_xkb_id); - if (!ext) { - weston_log("XKB extension not available on host X11 server\n"); - return; - } - b->xkb_event_base = ext->first_event; - - select = xcb_xkb_select_events_checked(b->conn, - XCB_XKB_ID_USE_CORE_KBD, - XCB_XKB_EVENT_TYPE_STATE_NOTIFY, - 0, - XCB_XKB_EVENT_TYPE_STATE_NOTIFY, - 0, - 0, - NULL); - error = xcb_request_check(b->conn, select); - if (error) { - weston_log("error: failed to select for XKB state events\n"); - free(error); - return; - } - - use_ext = xcb_xkb_use_extension(b->conn, - XCB_XKB_MAJOR_VERSION, - XCB_XKB_MINOR_VERSION); - use_ext_reply = xcb_xkb_use_extension_reply(b->conn, use_ext, NULL); - if (!use_ext_reply) { - weston_log("couldn't start using XKB extension\n"); - return; - } - - if (!use_ext_reply->supported) { - weston_log("XKB extension version on the server is too old " - "(want %d.%d, has %d.%d)\n", - XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION, - use_ext_reply->serverMajor, use_ext_reply->serverMinor); - free(use_ext_reply); - return; - } - free(use_ext_reply); - - pcf = xcb_xkb_per_client_flags(b->conn, - XCB_XKB_ID_USE_CORE_KBD, - XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, - XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, - 0, - 0, - 0); - pcf_reply = xcb_xkb_per_client_flags_reply(b->conn, pcf, NULL); - if (!pcf_reply || - !(pcf_reply->value & XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT)) { - weston_log("failed to set XKB per-client flags, not using " - "detectable repeat\n"); - free(pcf_reply); - return; - } - free(pcf_reply); - - state = xcb_xkb_get_state(b->conn, XCB_XKB_ID_USE_CORE_KBD); - state_reply = xcb_xkb_get_state_reply(b->conn, state, NULL); - if (!state_reply) { - weston_log("failed to get initial XKB state\n"); - return; - } - - keyboard = weston_seat_get_keyboard(&b->core_seat); - xkb_state_update_mask(keyboard->xkb_state.state, - get_xkb_mod_mask(b, state_reply->baseMods), - get_xkb_mod_mask(b, state_reply->latchedMods), - get_xkb_mod_mask(b, state_reply->lockedMods), - 0, - 0, - state_reply->group); - - free(state_reply); - - xcb_change_window_attributes(b->conn, b->screen->root, - XCB_CW_EVENT_MASK, values); - - b->has_xkb = 1; -#endif -} - -#ifdef HAVE_XCB_XKB -static void -update_xkb_keymap(struct x11_backend *b) -{ - struct xkb_keymap *keymap; - - keymap = x11_backend_get_keymap(b); - if (!keymap) { - weston_log("failed to get XKB keymap\n"); - return; - } - weston_seat_update_keymap(&b->core_seat, keymap); - xkb_keymap_unref(keymap); -} -#endif - -static int -x11_input_create(struct x11_backend *b, int no_input) -{ - struct xkb_keymap *keymap; - - weston_seat_init(&b->core_seat, b->compositor, "default"); - - if (no_input) - return 0; - - weston_seat_init_pointer(&b->core_seat); - - keymap = x11_backend_get_keymap(b); - if (weston_seat_init_keyboard(&b->core_seat, keymap) < 0) - return -1; - xkb_keymap_unref(keymap); - - x11_backend_setup_xkb(b); - - return 0; -} - -static void -x11_input_destroy(struct x11_backend *b) -{ - weston_seat_release(&b->core_seat); -} - -static int -x11_output_start_repaint_loop(struct weston_output *output) -{ - struct timespec ts; - - weston_compositor_read_presentation_clock(output->compositor, &ts); - weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID); - - return 0; -} - -static int -x11_output_repaint_gl(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) -{ - struct x11_output *output = to_x11_output(output_base); - struct weston_compositor *ec = output->base.compositor; - - ec->renderer->repaint_output(output_base, damage); - - pixman_region32_subtract(&ec->primary_plane.damage, - &ec->primary_plane.damage, damage); - - wl_event_source_timer_update(output->finish_frame_timer, 10); - return 0; -} - -static void -set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region) -{ - struct x11_output *output = to_x11_output(output_base); - struct weston_compositor *ec = output->base.compositor; - struct x11_backend *b = to_x11_backend(ec); - pixman_region32_t transformed_region; - pixman_box32_t *rects; - xcb_rectangle_t *output_rects; - xcb_void_cookie_t cookie; - int nrects, i; - xcb_generic_error_t *err; - - pixman_region32_init(&transformed_region); - pixman_region32_copy(&transformed_region, region); - pixman_region32_translate(&transformed_region, - -output_base->x, -output_base->y); - weston_transformed_region(output_base->width, output_base->height, - output_base->transform, - output_base->current_scale, - &transformed_region, &transformed_region); - - rects = pixman_region32_rectangles(&transformed_region, &nrects); - output_rects = calloc(nrects, sizeof(xcb_rectangle_t)); - - if (output_rects == NULL) { - pixman_region32_fini(&transformed_region); - return; - } - - for (i = 0; i < nrects; i++) { - output_rects[i].x = rects[i].x1; - output_rects[i].y = rects[i].y1; - output_rects[i].width = rects[i].x2 - rects[i].x1; - output_rects[i].height = rects[i].y2 - rects[i].y1; - } - - pixman_region32_fini(&transformed_region); - - cookie = xcb_set_clip_rectangles_checked(b->conn, XCB_CLIP_ORDERING_UNSORTED, - output->gc, - 0, 0, nrects, - output_rects); - err = xcb_request_check(b->conn, cookie); - if (err != NULL) { - weston_log("Failed to set clip rects, err: %d\n", err->error_code); - free(err); - } - free(output_rects); -} - - -static int -x11_output_repaint_shm(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) -{ - struct x11_output *output = to_x11_output(output_base); - struct weston_compositor *ec = output->base.compositor; - struct x11_backend *b = to_x11_backend(ec); - xcb_void_cookie_t cookie; - xcb_generic_error_t *err; - - pixman_renderer_output_set_buffer(output_base, output->hw_surface); - ec->renderer->repaint_output(output_base, damage); - - pixman_region32_subtract(&ec->primary_plane.damage, - &ec->primary_plane.damage, damage); - set_clip_for_output(output_base, damage); - cookie = xcb_shm_put_image_checked(b->conn, output->window, output->gc, - pixman_image_get_width(output->hw_surface), - pixman_image_get_height(output->hw_surface), - 0, 0, - pixman_image_get_width(output->hw_surface), - pixman_image_get_height(output->hw_surface), - 0, 0, output->depth, XCB_IMAGE_FORMAT_Z_PIXMAP, - 0, output->segment, 0); - err = xcb_request_check(b->conn, cookie); - if (err != NULL) { - weston_log("Failed to put shm image, err: %d\n", err->error_code); - free(err); - } - - wl_event_source_timer_update(output->finish_frame_timer, 10); - return 0; -} - -static int -finish_frame_handler(void *data) -{ - struct x11_output *output = data; - struct timespec ts; - - weston_compositor_read_presentation_clock(output->base.compositor, &ts); - weston_output_finish_frame(&output->base, &ts, 0); - - return 1; -} - -static void -x11_output_deinit_shm(struct x11_backend *b, struct x11_output *output) -{ - xcb_void_cookie_t cookie; - xcb_generic_error_t *err; - xcb_free_gc(b->conn, output->gc); - - pixman_image_unref(output->hw_surface); - output->hw_surface = NULL; - cookie = xcb_shm_detach_checked(b->conn, output->segment); - err = xcb_request_check(b->conn, cookie); - if (err) { - weston_log("xcb_shm_detach failed, error %d\n", err->error_code); - free(err); - } - shmdt(output->buf); -} - -static void -x11_output_set_wm_protocols(struct x11_backend *b, - struct x11_output *output) -{ - xcb_atom_t list[1]; - - list[0] = b->atom.wm_delete_window; - xcb_change_property (b->conn, - XCB_PROP_MODE_REPLACE, - output->window, - b->atom.wm_protocols, - XCB_ATOM_ATOM, - 32, - ARRAY_LENGTH(list), - list); -} - -struct wm_normal_hints { - uint32_t flags; - uint32_t pad[4]; - int32_t min_width, min_height; - int32_t max_width, max_height; - int32_t width_inc, height_inc; - int32_t min_aspect_x, min_aspect_y; - int32_t max_aspect_x, max_aspect_y; - int32_t base_width, base_height; - int32_t win_gravity; -}; - -#define WM_NORMAL_HINTS_MIN_SIZE 16 -#define WM_NORMAL_HINTS_MAX_SIZE 32 - -static void -x11_output_set_icon(struct x11_backend *b, - struct x11_output *output, const char *filename) -{ - uint32_t *icon; - int32_t width, height; - pixman_image_t *image; - - image = load_image(filename); - if (!image) - return; - width = pixman_image_get_width(image); - height = pixman_image_get_height(image); - icon = malloc(width * height * 4 + 8); - if (!icon) { - pixman_image_unref(image); - return; - } - - icon[0] = width; - icon[1] = height; - memcpy(icon + 2, pixman_image_get_data(image), width * height * 4); - xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window, - b->atom.net_wm_icon, b->atom.cardinal, 32, - width * height + 2, icon); - free(icon); - pixman_image_unref(image); -} - -static void -x11_output_wait_for_map(struct x11_backend *b, struct x11_output *output) -{ - xcb_map_notify_event_t *map_notify; - xcb_configure_notify_event_t *configure_notify; - xcb_generic_event_t *event; - int mapped = 0, configured = 0; - uint8_t response_type; - - /* This isn't the nicest way to do this. Ideally, we could - * just go back to the main loop and once we get the configure - * notify, we add the output to the compositor. While we do - * support output hotplug, we can't start up with no outputs. - * We could add the output and then resize once we get the - * configure notify, but we don't want to start up and - * immediately resize the output. - * - * Also, some window managers don't give us our final - * fullscreen size before map_notify, so if we don't get a - * configure_notify before map_notify, we just wait for the - * first one and hope that's our size. */ - - xcb_flush(b->conn); - - while (!mapped || !configured) { - event = xcb_wait_for_event(b->conn); - response_type = event->response_type & ~0x80; - - switch (response_type) { - case XCB_MAP_NOTIFY: - map_notify = (xcb_map_notify_event_t *) event; - if (map_notify->window == output->window) - mapped = 1; - break; - - case XCB_CONFIGURE_NOTIFY: - configure_notify = - (xcb_configure_notify_event_t *) event; - - - if (configure_notify->width % output->scale != 0 || - configure_notify->height % output->scale != 0) - weston_log("Resolution is not a multiple of screen size, rounding\n"); - output->mode.width = configure_notify->width; - output->mode.height = configure_notify->height; - configured = 1; - break; - } - } -} - -static xcb_visualtype_t * -find_visual_by_id(xcb_screen_t *screen, - xcb_visualid_t id) -{ - xcb_depth_iterator_t i; - xcb_visualtype_iterator_t j; - for (i = xcb_screen_allowed_depths_iterator(screen); - i.rem; - xcb_depth_next(&i)) { - for (j = xcb_depth_visuals_iterator(i.data); - j.rem; - xcb_visualtype_next(&j)) { - if (j.data->visual_id == id) - return j.data; - } - } - return 0; -} - -static uint8_t -get_depth_of_visual(xcb_screen_t *screen, - xcb_visualid_t id) -{ - xcb_depth_iterator_t i; - xcb_visualtype_iterator_t j; - for (i = xcb_screen_allowed_depths_iterator(screen); - i.rem; - xcb_depth_next(&i)) { - for (j = xcb_depth_visuals_iterator(i.data); - j.rem; - xcb_visualtype_next(&j)) { - if (j.data->visual_id == id) - return i.data->depth; - } - } - return 0; -} - -static int -x11_output_init_shm(struct x11_backend *b, struct x11_output *output, - int width, int height) -{ - xcb_visualtype_t *visual_type; - xcb_screen_t *screen; - xcb_format_iterator_t fmt; - xcb_void_cookie_t cookie; - xcb_generic_error_t *err; - const xcb_query_extension_reply_t *ext; - int bitsperpixel = 0; - pixman_format_code_t pixman_format; - - /* Check if SHM is available */ - ext = xcb_get_extension_data(b->conn, &xcb_shm_id); - if (ext == NULL || !ext->present) { - /* SHM is missing */ - weston_log("SHM extension is not available\n"); - errno = ENOENT; - return -1; - } - - screen = x11_compositor_get_default_screen(b); - visual_type = find_visual_by_id(screen, screen->root_visual); - if (!visual_type) { - weston_log("Failed to lookup visual for root window\n"); - errno = ENOENT; - return -1; - } - weston_log("Found visual, bits per value: %d, red_mask: %.8x, green_mask: %.8x, blue_mask: %.8x\n", - visual_type->bits_per_rgb_value, - visual_type->red_mask, - visual_type->green_mask, - visual_type->blue_mask); - output->depth = get_depth_of_visual(screen, screen->root_visual); - weston_log("Visual depth is %d\n", output->depth); - - for (fmt = xcb_setup_pixmap_formats_iterator(xcb_get_setup(b->conn)); - fmt.rem; - xcb_format_next(&fmt)) { - if (fmt.data->depth == output->depth) { - bitsperpixel = fmt.data->bits_per_pixel; - break; - } - } - weston_log("Found format for depth %d, bpp: %d\n", - output->depth, bitsperpixel); - - if (bitsperpixel == 32 && - visual_type->red_mask == 0xff0000 && - visual_type->green_mask == 0x00ff00 && - visual_type->blue_mask == 0x0000ff) { - weston_log("Will use x8r8g8b8 format for SHM surfaces\n"); - pixman_format = PIXMAN_x8r8g8b8; - } else if (bitsperpixel == 16 && - visual_type->red_mask == 0x00f800 && - visual_type->green_mask == 0x0007e0 && - visual_type->blue_mask == 0x00001f) { - weston_log("Will use r5g6b5 format for SHM surfaces\n"); - pixman_format = PIXMAN_r5g6b5; - } else { - weston_log("Can't find appropriate format for SHM pixmap\n"); - errno = ENOTSUP; - return -1; - } - - - /* Create SHM segment and attach it */ - output->shm_id = shmget(IPC_PRIVATE, width * height * (bitsperpixel / 8), IPC_CREAT | S_IRWXU); - if (output->shm_id == -1) { - weston_log("x11shm: failed to allocate SHM segment\n"); - return -1; - } - output->buf = shmat(output->shm_id, NULL, 0 /* read/write */); - if (-1 == (long)output->buf) { - weston_log("x11shm: failed to attach SHM segment\n"); - return -1; - } - output->segment = xcb_generate_id(b->conn); - cookie = xcb_shm_attach_checked(b->conn, output->segment, output->shm_id, 1); - err = xcb_request_check(b->conn, cookie); - if (err) { - weston_log("x11shm: xcb_shm_attach error %d, op code %d, resource id %d\n", - err->error_code, err->major_code, err->minor_code); - free(err); - return -1; - } - - shmctl(output->shm_id, IPC_RMID, NULL); - - /* Now create pixman image */ - output->hw_surface = pixman_image_create_bits(pixman_format, width, height, output->buf, - width * (bitsperpixel / 8)); - - output->gc = xcb_generate_id(b->conn); - xcb_create_gc(b->conn, output->gc, output->window, 0, NULL); - - return 0; -} - -static int -x11_output_switch_mode(struct weston_output *base, struct weston_mode *mode) -{ - struct x11_backend *b; - struct x11_output *output; - static uint32_t values[2]; - int ret; - - if (base == NULL) { - weston_log("output is NULL.\n"); - return -1; - } - - if (mode == NULL) { - weston_log("mode is NULL.\n"); - return -1; - } - - b = to_x11_backend(base->compositor); - output = to_x11_output(base); - - if (mode->width == output->mode.width && - mode->height == output->mode.height) - return 0; - - if (mode->width < WINDOW_MIN_WIDTH || mode->width > WINDOW_MAX_WIDTH) - return -1; - - if (mode->height < WINDOW_MIN_HEIGHT || mode->height > WINDOW_MAX_HEIGHT) - return -1; - - /* xcb_configure_window will create an event, and we could end up - being called twice */ - output->resize_pending = true; - - /* window could've been resized by the user, so don't do it twice */ - if (!output->window_resized) { - values[0] = mode->width; - values[1] = mode->height; - xcb_configure_window(b->conn, output->window, XCB_CONFIG_WINDOW_WIDTH | - XCB_CONFIG_WINDOW_HEIGHT, values); - } - - output->mode.width = mode->width; - output->mode.height = mode->height; - - if (b->use_pixman) { - pixman_renderer_output_destroy(&output->base); - x11_output_deinit_shm(b, output); - - if (x11_output_init_shm(b, output, - output->base.current_mode->width, - output->base.current_mode->height) < 0) { - weston_log("Failed to initialize SHM for the X11 output\n"); - return -1; - } - - if (pixman_renderer_output_create(&output->base, - PIXMAN_RENDERER_OUTPUT_USE_SHADOW) < 0) { - weston_log("Failed to create pixman renderer for output\n"); - x11_output_deinit_shm(b, output); - return -1; - } - } else { - Window xid = (Window) output->window; - - gl_renderer->output_destroy(&output->base); - - ret = gl_renderer->output_window_create(&output->base, - (EGLNativeWindowType) output->window, - &xid, - x11_formats, - ARRAY_LENGTH(x11_formats)); - if (ret < 0) - return -1; - } - - output->resize_pending = false; - output->window_resized = false; - - return 0; -} - -static int -x11_output_disable(struct weston_output *base) -{ - struct x11_output *output = to_x11_output(base); - struct x11_backend *backend = to_x11_backend(base->compositor); - - if (!output->base.enabled) - return 0; - - wl_event_source_remove(output->finish_frame_timer); - - if (backend->use_pixman) { - pixman_renderer_output_destroy(&output->base); - x11_output_deinit_shm(backend, output); - } else { - gl_renderer->output_destroy(&output->base); - } - - xcb_destroy_window(backend->conn, output->window); - xcb_flush(backend->conn); - - return 0; -} - -static void -x11_output_destroy(struct weston_output *base) -{ - struct x11_output *output = to_x11_output(base); - - x11_output_disable(&output->base); - weston_output_release(&output->base); - - free(output); -} - -static int -x11_output_enable(struct weston_output *base) -{ - struct x11_output *output = to_x11_output(base); - struct x11_backend *b = to_x11_backend(base->compositor); - - static const char name[] = "Weston Compositor"; - static const char class[] = "weston-1\0Weston Compositor"; - char *title = NULL; - xcb_screen_t *screen; - struct wm_normal_hints normal_hints; - struct wl_event_loop *loop; - char *icon_filename; - - int ret; - uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_CURSOR; - xcb_atom_t atom_list[1]; - uint32_t values[2] = { - XCB_EVENT_MASK_EXPOSURE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY, - 0 - }; - - if (!b->no_input) - values[0] |= - XCB_EVENT_MASK_KEY_PRESS | - XCB_EVENT_MASK_KEY_RELEASE | - XCB_EVENT_MASK_BUTTON_PRESS | - XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_POINTER_MOTION | - XCB_EVENT_MASK_ENTER_WINDOW | - XCB_EVENT_MASK_LEAVE_WINDOW | - XCB_EVENT_MASK_KEYMAP_STATE | - XCB_EVENT_MASK_FOCUS_CHANGE; - - values[1] = b->null_cursor; - output->window = xcb_generate_id(b->conn); - screen = x11_compositor_get_default_screen(b); - xcb_create_window(b->conn, - XCB_COPY_FROM_PARENT, - output->window, - screen->root, - 0, 0, - output->base.current_mode->width, - output->base.current_mode->height, - 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - screen->root_visual, - mask, values); - - if (b->fullscreen) { - atom_list[0] = b->atom.net_wm_state_fullscreen; - xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, - output->window, - b->atom.net_wm_state, - XCB_ATOM_ATOM, 32, - ARRAY_LENGTH(atom_list), atom_list); - } else { - memset(&normal_hints, 0, sizeof normal_hints); - normal_hints.flags = - WM_NORMAL_HINTS_MAX_SIZE | WM_NORMAL_HINTS_MIN_SIZE; - normal_hints.min_width = WINDOW_MIN_WIDTH; - normal_hints.min_height = WINDOW_MIN_HEIGHT; - normal_hints.max_width = WINDOW_MAX_WIDTH; - normal_hints.max_height = WINDOW_MAX_HEIGHT; - xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window, - b->atom.wm_normal_hints, - b->atom.wm_size_hints, 32, - sizeof normal_hints / 4, - (uint8_t *) &normal_hints); - } - - /* Set window name. Don't bother with non-EWMH WMs. */ - if (output->base.name) { - if (asprintf(&title, "%s - %s", name, output->base.name) < 0) - title = NULL; - } else { - title = strdup(name); - } - - if (title) { - xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window, - b->atom.net_wm_name, b->atom.utf8_string, 8, - strlen(title), title); - free(title); - } else { - goto err; - } - - xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window, - b->atom.wm_class, b->atom.string, 8, - sizeof class, class); - - icon_filename = file_name_with_datadir("wayland.png"); - x11_output_set_icon(b, output, icon_filename); - free(icon_filename); - - x11_output_set_wm_protocols(b, output); - - xcb_map_window(b->conn, output->window); - - if (b->fullscreen) - x11_output_wait_for_map(b, output); - - if (b->use_pixman) { - if (x11_output_init_shm(b, output, - output->base.current_mode->width, - output->base.current_mode->height) < 0) { - weston_log("Failed to initialize SHM for the X11 output\n"); - goto err; - } - if (pixman_renderer_output_create(&output->base, - PIXMAN_RENDERER_OUTPUT_USE_SHADOW) < 0) { - weston_log("Failed to create pixman renderer for output\n"); - x11_output_deinit_shm(b, output); - goto err; - } - - output->base.repaint = x11_output_repaint_shm; - } else { - /* eglCreatePlatformWindowSurfaceEXT takes a Window* - * but eglCreateWindowSurface takes a Window. */ - Window xid = (Window) output->window; - - ret = gl_renderer->output_window_create( - &output->base, - (EGLNativeWindowType) output->window, - &xid, - x11_formats, - ARRAY_LENGTH(x11_formats)); - if (ret < 0) - goto err; - - output->base.repaint = x11_output_repaint_gl; - } - - output->base.start_repaint_loop = x11_output_start_repaint_loop; - output->base.assign_planes = NULL; - output->base.set_backlight = NULL; - output->base.set_dpms = NULL; - output->base.switch_mode = x11_output_switch_mode; - - loop = wl_display_get_event_loop(b->compositor->wl_display); - output->finish_frame_timer = - wl_event_loop_add_timer(loop, finish_frame_handler, output); - - weston_log("x11 output %dx%d, window id %d\n", - output->base.current_mode->width, - output->base.current_mode->height, - output->window); - - return 0; - -err: - xcb_destroy_window(b->conn, output->window); - xcb_flush(b->conn); - - return -1; -} - -static int -x11_output_set_size(struct weston_output *base, int width, int height) -{ - struct x11_output *output = to_x11_output(base); - struct x11_backend *b = to_x11_backend(base->compositor); - struct weston_head *head; - xcb_screen_t *scrn = b->screen; - int output_width, output_height; - - /* We can only be called once. */ - assert(!output->base.current_mode); - - /* Make sure we have scale set. */ - assert(output->base.scale); - - if (width < WINDOW_MIN_WIDTH) { - weston_log("Invalid width \"%d\" for output %s\n", - width, output->base.name); - return -1; - } - - if (height < WINDOW_MIN_HEIGHT) { - weston_log("Invalid height \"%d\" for output %s\n", - height, output->base.name); - return -1; - } - - wl_list_for_each(head, &output->base.head_list, output_link) { - weston_head_set_monitor_strings(head, "weston-X11", "none", NULL); - weston_head_set_physical_size(head, - width * scrn->width_in_millimeters / scrn->width_in_pixels, - height * scrn->height_in_millimeters / scrn->height_in_pixels); - } - - output_width = width * output->base.scale; - output_height = height * output->base.scale; - - output->mode.flags = - WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; - - output->mode.width = output_width; - output->mode.height = output_height; - output->mode.refresh = 60000; - output->native = output->mode; - output->scale = output->base.scale; - wl_list_insert(&output->base.mode_list, &output->mode.link); - - output->base.current_mode = &output->mode; - output->base.native_mode = &output->native; - output->base.native_scale = output->base.scale; - - return 0; -} - -static struct weston_output * -x11_output_create(struct weston_compositor *compositor, const char *name) -{ - struct x11_output *output; - - /* name can't be NULL. */ - assert(name); - - output = zalloc(sizeof *output); - if (!output) - return NULL; - - weston_output_init(&output->base, compositor, name); - - output->base.destroy = x11_output_destroy; - output->base.disable = x11_output_disable; - output->base.enable = x11_output_enable; - output->base.attach_head = NULL; - - weston_compositor_add_pending_output(&output->base, compositor); - - return &output->base; -} - -static int -x11_head_create(struct weston_compositor *compositor, const char *name) -{ - struct x11_head *head; - - assert(name); - - head = zalloc(sizeof *head); - if (!head) - return -1; - - weston_head_init(&head->base, name); - weston_head_set_connection_status(&head->base, true); - weston_compositor_add_head(compositor, &head->base); - - return 0; -} - -static void -x11_head_destroy(struct x11_head *head) -{ - weston_head_release(&head->base); - free(head); -} - -static struct x11_output * -x11_backend_find_output(struct x11_backend *b, xcb_window_t window) -{ - struct x11_output *output; - - wl_list_for_each(output, &b->compositor->output_list, base.link) { - if (output->window == window) - return output; - } - - return NULL; -} - -static void -x11_backend_delete_window(struct x11_backend *b, xcb_window_t window) -{ - struct x11_output *output; - - output = x11_backend_find_output(b, window); - if (output) - x11_output_destroy(&output->base); - - if (wl_list_empty(&b->compositor->output_list)) - weston_compositor_exit(b->compositor); -} - -static void delete_cb(void *data) -{ - struct window_delete_data *wd = data; - - x11_backend_delete_window(wd->backend, wd->window); - free(wd); -} - -#ifdef HAVE_XCB_XKB -static void -update_xkb_state(struct x11_backend *b, xcb_xkb_state_notify_event_t *state) -{ - struct weston_keyboard *keyboard = - weston_seat_get_keyboard(&b->core_seat); - - xkb_state_update_mask(keyboard->xkb_state.state, - get_xkb_mod_mask(b, state->baseMods), - get_xkb_mod_mask(b, state->latchedMods), - get_xkb_mod_mask(b, state->lockedMods), - 0, - 0, - state->group); - - notify_modifiers(&b->core_seat, - wl_display_next_serial(b->compositor->wl_display)); -} -#endif - -/** - * This is monumentally unpleasant. If we don't have XCB-XKB bindings, - * the best we can do (given that XCB also lacks XI2 support), is to take - * the state from the core key events. Unfortunately that only gives us - * the effective (i.e. union of depressed/latched/locked) state, and we - * need the granularity. - * - * So we still update the state with every key event we see, but also use - * the state field from X11 events as a mask so we don't get any stuck - * modifiers. - */ -static void -update_xkb_state_from_core(struct x11_backend *b, uint16_t x11_mask) -{ - uint32_t mask = get_xkb_mod_mask(b, x11_mask); - struct weston_keyboard *keyboard - = weston_seat_get_keyboard(&b->core_seat); - - xkb_state_update_mask(keyboard->xkb_state.state, - keyboard->modifiers.mods_depressed & mask, - keyboard->modifiers.mods_latched & mask, - keyboard->modifiers.mods_locked & mask, - 0, - 0, - (x11_mask >> 13) & 3); - notify_modifiers(&b->core_seat, - wl_display_next_serial(b->compositor->wl_display)); -} - -static void -x11_backend_deliver_button_event(struct x11_backend *b, - xcb_generic_event_t *event) -{ - xcb_button_press_event_t *button_event = - (xcb_button_press_event_t *) event; - uint32_t button; - struct x11_output *output; - struct weston_pointer_axis_event weston_event; - bool is_button_pressed = event->response_type == XCB_BUTTON_PRESS; - struct timespec time = { 0 }; - - assert(event->response_type == XCB_BUTTON_PRESS || - event->response_type == XCB_BUTTON_RELEASE); - - output = x11_backend_find_output(b, button_event->event); - if (!output) - return; - - if (is_button_pressed) - xcb_grab_pointer(b->conn, 0, output->window, - XCB_EVENT_MASK_BUTTON_PRESS | - XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_POINTER_MOTION | - XCB_EVENT_MASK_ENTER_WINDOW | - XCB_EVENT_MASK_LEAVE_WINDOW, - XCB_GRAB_MODE_ASYNC, - XCB_GRAB_MODE_ASYNC, - output->window, XCB_CURSOR_NONE, - button_event->time); - else - xcb_ungrab_pointer(b->conn, button_event->time); - - if (!b->has_xkb) - update_xkb_state_from_core(b, button_event->state); - - switch (button_event->detail) { - case 1: - button = BTN_LEFT; - break; - case 2: - button = BTN_MIDDLE; - break; - case 3: - button = BTN_RIGHT; - break; - case 4: - /* Axis are measured in pixels, but the xcb events are discrete - * steps. Therefore move the axis by some pixels every step. */ - if (is_button_pressed) { - weston_event.value = -DEFAULT_AXIS_STEP_DISTANCE; - weston_event.discrete = -1; - weston_event.has_discrete = true; - weston_event.axis = - WL_POINTER_AXIS_VERTICAL_SCROLL; - weston_compositor_get_time(&time); - notify_axis(&b->core_seat, &time, &weston_event); - notify_pointer_frame(&b->core_seat); - } - return; - case 5: - if (is_button_pressed) { - weston_event.value = DEFAULT_AXIS_STEP_DISTANCE; - weston_event.discrete = 1; - weston_event.has_discrete = true; - weston_event.axis = - WL_POINTER_AXIS_VERTICAL_SCROLL; - weston_compositor_get_time(&time); - notify_axis(&b->core_seat, &time, &weston_event); - notify_pointer_frame(&b->core_seat); - } - return; - case 6: - if (is_button_pressed) { - weston_event.value = -DEFAULT_AXIS_STEP_DISTANCE; - weston_event.discrete = -1; - weston_event.has_discrete = true; - weston_event.axis = - WL_POINTER_AXIS_HORIZONTAL_SCROLL; - weston_compositor_get_time(&time); - notify_axis(&b->core_seat, &time, &weston_event); - notify_pointer_frame(&b->core_seat); - } - return; - case 7: - if (is_button_pressed) { - weston_event.value = DEFAULT_AXIS_STEP_DISTANCE; - weston_event.discrete = 1; - weston_event.has_discrete = true; - weston_event.axis = - WL_POINTER_AXIS_HORIZONTAL_SCROLL; - weston_compositor_get_time(&time); - notify_axis(&b->core_seat, &time, &weston_event); - notify_pointer_frame(&b->core_seat); - } - return; - default: - button = button_event->detail + BTN_SIDE - 8; - break; - } - - weston_compositor_get_time(&time); - - notify_button(&b->core_seat, &time, button, - is_button_pressed ? WL_POINTER_BUTTON_STATE_PRESSED : - WL_POINTER_BUTTON_STATE_RELEASED); - notify_pointer_frame(&b->core_seat); -} - -static void -x11_backend_deliver_motion_event(struct x11_backend *b, - xcb_generic_event_t *event) -{ - struct x11_output *output; - double x, y; - struct weston_pointer_motion_event motion_event = { 0 }; - xcb_motion_notify_event_t *motion_notify = - (xcb_motion_notify_event_t *) event; - struct timespec time; - - if (!b->has_xkb) - update_xkb_state_from_core(b, motion_notify->state); - output = x11_backend_find_output(b, motion_notify->event); - if (!output) - return; - - weston_output_transform_coordinate(&output->base, - motion_notify->event_x, - motion_notify->event_y, - &x, &y); - - motion_event = (struct weston_pointer_motion_event) { - .mask = WESTON_POINTER_MOTION_REL, - .dx = x - b->prev_x, - .dy = y - b->prev_y - }; - - weston_compositor_get_time(&time); - notify_motion(&b->core_seat, &time, &motion_event); - notify_pointer_frame(&b->core_seat); - - b->prev_x = x; - b->prev_y = y; -} - -static void -x11_backend_deliver_enter_event(struct x11_backend *b, - xcb_generic_event_t *event) -{ - struct x11_output *output; - double x, y; - - xcb_enter_notify_event_t *enter_notify = - (xcb_enter_notify_event_t *) event; - if (enter_notify->state >= Button1Mask) - return; - if (!b->has_xkb) - update_xkb_state_from_core(b, enter_notify->state); - output = x11_backend_find_output(b, enter_notify->event); - if (!output) - return; - - weston_output_transform_coordinate(&output->base, - enter_notify->event_x, - enter_notify->event_y, &x, &y); - - notify_pointer_focus(&b->core_seat, &output->base, x, y); - - b->prev_x = x; - b->prev_y = y; -} - -static int -x11_backend_next_event(struct x11_backend *b, - xcb_generic_event_t **event, uint32_t mask) -{ - if (mask & WL_EVENT_READABLE) - *event = xcb_poll_for_event(b->conn); - else - *event = xcb_poll_for_queued_event(b->conn); - - return *event != NULL; -} - -static int -x11_backend_handle_event(int fd, uint32_t mask, void *data) -{ - struct x11_backend *b = data; - struct x11_output *output; - xcb_generic_event_t *event, *prev; - xcb_client_message_event_t *client_message; - xcb_enter_notify_event_t *enter_notify; - xcb_key_press_event_t *key_press, *key_release; - xcb_keymap_notify_event_t *keymap_notify; - xcb_focus_in_event_t *focus_in; - xcb_expose_event_t *expose; - xcb_configure_notify_event_t *configure; - xcb_atom_t atom; - xcb_window_t window; - uint32_t *k; - uint32_t i, set; - uint8_t response_type; - int count; - struct timespec time; - - prev = NULL; - count = 0; - while (x11_backend_next_event(b, &event, mask)) { - response_type = event->response_type & ~0x80; - - switch (prev ? prev->response_type & ~0x80 : 0x80) { - case XCB_KEY_RELEASE: - /* Suppress key repeat events; this is only used if we - * don't have XCB XKB support. */ - key_release = (xcb_key_press_event_t *) prev; - key_press = (xcb_key_press_event_t *) event; - if (response_type == XCB_KEY_PRESS && - key_release->time == key_press->time && - key_release->detail == key_press->detail) { - /* Don't deliver the held key release - * event or the new key press event. */ - free(event); - free(prev); - prev = NULL; - continue; - } else { - /* Deliver the held key release now - * and fall through and handle the new - * event below. */ - update_xkb_state_from_core(b, key_release->state); - weston_compositor_get_time(&time); - notify_key(&b->core_seat, - &time, - key_release->detail - 8, - WL_KEYBOARD_KEY_STATE_RELEASED, - STATE_UPDATE_AUTOMATIC); - free(prev); - prev = NULL; - break; - } - - case XCB_FOCUS_IN: - assert(response_type == XCB_KEYMAP_NOTIFY); - keymap_notify = (xcb_keymap_notify_event_t *) event; - b->keys.size = 0; - for (i = 0; i < ARRAY_LENGTH(keymap_notify->keys) * 8; i++) { - set = keymap_notify->keys[i >> 3] & - (1 << (i & 7)); - if (set) { - k = wl_array_add(&b->keys, sizeof *k); - *k = i; - } - } - - /* Unfortunately the state only comes with the enter - * event, rather than with the focus event. I'm not - * sure of the exact semantics around it and whether - * we can ensure that we get both? */ - notify_keyboard_focus_in(&b->core_seat, &b->keys, - STATE_UPDATE_AUTOMATIC); - - free(prev); - prev = NULL; - break; - - default: - /* No previous event held */ - break; - } - - switch (response_type) { - case XCB_KEY_PRESS: - key_press = (xcb_key_press_event_t *) event; - if (!b->has_xkb) - update_xkb_state_from_core(b, key_press->state); - weston_compositor_get_time(&time); - notify_key(&b->core_seat, - &time, - key_press->detail - 8, - WL_KEYBOARD_KEY_STATE_PRESSED, - b->has_xkb ? STATE_UPDATE_NONE : - STATE_UPDATE_AUTOMATIC); - break; - case XCB_KEY_RELEASE: - /* If we don't have XKB, we need to use the lame - * autorepeat detection above. */ - if (!b->has_xkb) { - prev = event; - break; - } - key_release = (xcb_key_press_event_t *) event; - weston_compositor_get_time(&time); - notify_key(&b->core_seat, - &time, - key_release->detail - 8, - WL_KEYBOARD_KEY_STATE_RELEASED, - STATE_UPDATE_NONE); - break; - case XCB_BUTTON_PRESS: - case XCB_BUTTON_RELEASE: - x11_backend_deliver_button_event(b, event); - break; - case XCB_MOTION_NOTIFY: - x11_backend_deliver_motion_event(b, event); - break; - - case XCB_EXPOSE: - expose = (xcb_expose_event_t *) event; - output = x11_backend_find_output(b, expose->window); - if (!output) - break; - - weston_output_damage(&output->base); - weston_output_schedule_repaint(&output->base); - break; - - case XCB_ENTER_NOTIFY: - x11_backend_deliver_enter_event(b, event); - break; - - case XCB_LEAVE_NOTIFY: - enter_notify = (xcb_enter_notify_event_t *) event; - if (enter_notify->state >= Button1Mask) - break; - if (!b->has_xkb) - update_xkb_state_from_core(b, enter_notify->state); - notify_pointer_focus(&b->core_seat, NULL, 0, 0); - break; - - case XCB_CLIENT_MESSAGE: - client_message = (xcb_client_message_event_t *) event; - atom = client_message->data.data32[0]; - window = client_message->window; - if (atom == b->atom.wm_delete_window) { - struct wl_event_loop *loop; - struct window_delete_data *data = malloc(sizeof *data); - - /* if malloc failed we should at least try to - * delete the window, even if it may result in - * a crash. - */ - if (!data) { - x11_backend_delete_window(b, window); - break; - } - data->backend = b; - data->window = window; - loop = wl_display_get_event_loop(b->compositor->wl_display); - wl_event_loop_add_idle(loop, delete_cb, data); - } - break; - - case XCB_CONFIGURE_NOTIFY: - configure = (struct xcb_configure_notify_event_t *) event; - struct x11_output *output = - x11_backend_find_output(b, configure->window); - - if (!output || output->resize_pending) - break; - - struct weston_mode mode = output->mode; - - if (mode.width == configure->width && - mode.height == configure->height) - break; - - output->window_resized = true; - - mode.width = configure->width; - mode.height = configure->height; - - if (weston_output_mode_set_native(&output->base, - &mode, output->scale) < 0) - weston_log("Mode switch failed\n"); - - break; - - case XCB_FOCUS_IN: - focus_in = (xcb_focus_in_event_t *) event; - if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED) - break; - - prev = event; - break; - - case XCB_FOCUS_OUT: - focus_in = (xcb_focus_in_event_t *) event; - if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED || - focus_in->mode == XCB_NOTIFY_MODE_UNGRAB) - break; - notify_keyboard_focus_out(&b->core_seat); - break; - - default: - break; - } - -#ifdef HAVE_XCB_XKB - if (b->has_xkb) { - if (response_type == b->xkb_event_base) { - xcb_xkb_state_notify_event_t *state = - (xcb_xkb_state_notify_event_t *) event; - if (state->xkbType == XCB_XKB_STATE_NOTIFY) - update_xkb_state(b, state); - } else if (response_type == XCB_PROPERTY_NOTIFY) { - xcb_property_notify_event_t *prop_notify = - (xcb_property_notify_event_t *) event; - if (prop_notify->window == b->screen->root && - prop_notify->atom == b->atom.xkb_names && - prop_notify->state == XCB_PROPERTY_NEW_VALUE) - update_xkb_keymap(b); - } - } -#endif - - count++; - if (prev != event) - free (event); - } - - switch (prev ? prev->response_type & ~0x80 : 0x80) { - case XCB_KEY_RELEASE: - key_release = (xcb_key_press_event_t *) prev; - update_xkb_state_from_core(b, key_release->state); - weston_compositor_get_time(&time); - notify_key(&b->core_seat, - &time, - key_release->detail - 8, - WL_KEYBOARD_KEY_STATE_RELEASED, - STATE_UPDATE_AUTOMATIC); - free(prev); - prev = NULL; - break; - default: - break; - } - - return count; -} - -#define F(field) offsetof(struct x11_backend, field) - -static void -x11_backend_get_resources(struct x11_backend *b) -{ - static const struct { const char *name; int offset; } atoms[] = { - { "WM_PROTOCOLS", F(atom.wm_protocols) }, - { "WM_NORMAL_HINTS", F(atom.wm_normal_hints) }, - { "WM_SIZE_HINTS", F(atom.wm_size_hints) }, - { "WM_DELETE_WINDOW", F(atom.wm_delete_window) }, - { "WM_CLASS", F(atom.wm_class) }, - { "_NET_WM_NAME", F(atom.net_wm_name) }, - { "_NET_WM_ICON", F(atom.net_wm_icon) }, - { "_NET_WM_STATE", F(atom.net_wm_state) }, - { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) }, - { "_NET_SUPPORTING_WM_CHECK", - F(atom.net_supporting_wm_check) }, - { "_NET_SUPPORTED", F(atom.net_supported) }, - { "STRING", F(atom.string) }, - { "UTF8_STRING", F(atom.utf8_string) }, - { "CARDINAL", F(atom.cardinal) }, - { "_XKB_RULES_NAMES", F(atom.xkb_names) }, - }; - - xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)]; - xcb_intern_atom_reply_t *reply; - xcb_pixmap_t pixmap; - xcb_gc_t gc; - unsigned int i; - uint8_t data[] = { 0, 0, 0, 0 }; - - for (i = 0; i < ARRAY_LENGTH(atoms); i++) - cookies[i] = xcb_intern_atom (b->conn, 0, - strlen(atoms[i].name), - atoms[i].name); - - for (i = 0; i < ARRAY_LENGTH(atoms); i++) { - reply = xcb_intern_atom_reply (b->conn, cookies[i], NULL); - *(xcb_atom_t *) ((char *) b + atoms[i].offset) = reply->atom; - free(reply); - } - - pixmap = xcb_generate_id(b->conn); - gc = xcb_generate_id(b->conn); - xcb_create_pixmap(b->conn, 1, pixmap, b->screen->root, 1, 1); - xcb_create_gc(b->conn, gc, pixmap, 0, NULL); - xcb_put_image(b->conn, XCB_IMAGE_FORMAT_XY_PIXMAP, - pixmap, gc, 1, 1, 0, 0, 0, 32, sizeof data, data); - b->null_cursor = xcb_generate_id(b->conn); - xcb_create_cursor (b->conn, b->null_cursor, - pixmap, pixmap, 0, 0, 0, 0, 0, 0, 1, 1); - xcb_free_gc(b->conn, gc); - xcb_free_pixmap(b->conn, pixmap); -} - -static void -x11_backend_get_wm_info(struct x11_backend *c) -{ - xcb_get_property_cookie_t cookie; - xcb_get_property_reply_t *reply; - xcb_atom_t *atom; - unsigned int i; - - cookie = xcb_get_property(c->conn, 0, c->screen->root, - c->atom.net_supported, - XCB_ATOM_ATOM, 0, 1024); - reply = xcb_get_property_reply(c->conn, cookie, NULL); - if (reply == NULL) - return; - - atom = (xcb_atom_t *) xcb_get_property_value(reply); - - for (i = 0; i < reply->value_len; i++) { - if (atom[i] == c->atom.net_wm_state_fullscreen) - c->has_net_wm_state_fullscreen = 1; - } - - free(reply); -} - -static void -x11_destroy(struct weston_compositor *ec) -{ - struct x11_backend *backend = to_x11_backend(ec); - struct weston_head *base, *next; - - wl_event_source_remove(backend->xcb_source); - x11_input_destroy(backend); - - weston_compositor_shutdown(ec); /* destroys outputs, too */ - - wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) - x11_head_destroy(to_x11_head(base)); - - XCloseDisplay(backend->dpy); - free(backend); -} - -static int -init_gl_renderer(struct x11_backend *b) -{ - int ret; - - gl_renderer = weston_load_module("gl-renderer.so", - "gl_renderer_interface"); - if (!gl_renderer) - return -1; - - ret = gl_renderer->display_create(b->compositor, EGL_PLATFORM_X11_KHR, - (void *) b->dpy, - EGL_WINDOW_BIT, - x11_formats, - ARRAY_LENGTH(x11_formats)); - - return ret; -} - -static const struct weston_windowed_output_api api = { - x11_output_set_size, - x11_head_create, -}; - -static struct x11_backend * -x11_backend_create(struct weston_compositor *compositor, - struct weston_x11_backend_config *config) -{ - struct x11_backend *b; - struct wl_event_loop *loop; - int ret; - - b = zalloc(sizeof *b); - if (b == NULL) - return NULL; - - b->compositor = compositor; - b->fullscreen = config->fullscreen; - b->no_input = config->no_input; - - compositor->backend = &b->base; - - if (weston_compositor_set_presentation_clock_software(compositor) < 0) - goto err_free; - - b->dpy = XOpenDisplay(NULL); - if (b->dpy == NULL) - goto err_free; - - b->conn = XGetXCBConnection(b->dpy); - XSetEventQueueOwner(b->dpy, XCBOwnsEventQueue); - - if (xcb_connection_has_error(b->conn)) - goto err_xdisplay; - - b->screen = x11_compositor_get_default_screen(b); - wl_array_init(&b->keys); - - x11_backend_get_resources(b); - x11_backend_get_wm_info(b); - - if (!b->has_net_wm_state_fullscreen && config->fullscreen) { - weston_log("Can not fullscreen without window manager support" - "(need _NET_WM_STATE_FULLSCREEN)\n"); - config->fullscreen = 0; - } - - b->use_pixman = config->use_pixman; - if (b->use_pixman) { - if (pixman_renderer_init(compositor) < 0) { - weston_log("Failed to initialize pixman renderer for X11 backend\n"); - goto err_xdisplay; - } - } - else if (init_gl_renderer(b) < 0) { - goto err_xdisplay; - } - weston_log("Using %s renderer\n", config->use_pixman ? "pixman" : "gl"); - - b->base.destroy = x11_destroy; - b->base.create_output = x11_output_create; - - if (x11_input_create(b, config->no_input) < 0) { - weston_log("Failed to create X11 input\n"); - goto err_renderer; - } - - loop = wl_display_get_event_loop(compositor->wl_display); - b->xcb_source = - wl_event_loop_add_fd(loop, - xcb_get_file_descriptor(b->conn), - WL_EVENT_READABLE, - x11_backend_handle_event, b); - wl_event_source_check(b->xcb_source); - - if (compositor->renderer->import_dmabuf) { - if (linux_dmabuf_setup(compositor) < 0) - weston_log("Error: initializing dmabuf " - "support failed.\n"); - } - - if (compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC) { - if (linux_explicit_synchronization_setup(compositor) < 0) - weston_log("Error: initializing explicit " - " synchronization support failed.\n"); - } - - ret = weston_plugin_api_register(compositor, WESTON_WINDOWED_OUTPUT_API_NAME, - &api, sizeof(api)); - - if (ret < 0) { - weston_log("Failed to register output API.\n"); - goto err_x11_input; - } - - return b; - -err_x11_input: - x11_input_destroy(b); -err_renderer: - compositor->renderer->destroy(compositor); -err_xdisplay: - XCloseDisplay(b->dpy); -err_free: - free(b); - return NULL; -} - -static void -config_init_to_defaults(struct weston_x11_backend_config *config) -{ -} - -WL_EXPORT int -weston_backend_init(struct weston_compositor *compositor, - struct weston_backend_config *config_base) -{ - struct x11_backend *b; - struct weston_x11_backend_config config = {{ 0, }}; - - if (config_base == NULL || - config_base->struct_version != WESTON_X11_BACKEND_CONFIG_VERSION || - config_base->struct_size > sizeof(struct weston_x11_backend_config)) { - weston_log("X11 backend config structure is invalid\n"); - return -1; - } - - config_init_to_defaults(&config); - memcpy(&config, config_base, config_base->struct_size); - - b = x11_backend_create(compositor, &config); - if (b == NULL) - return -1; - - return 0; -} diff --git a/libweston/backend.h b/libweston/backend.h deleted file mode 100644 index ff10b363..00000000 --- a/libweston/backend.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2017, 2018 General Electric Company - * Copyright © 2012, 2017-2019 Collabora, Ltd. - * - * 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. - */ - -/* - * This header contains the libweston ABI exported only for internal backends. - */ - -#ifndef LIBWESTON_BACKEND_INTERNAL_H -#define LIBWESTON_BACKEND_INTERNAL_H - -struct weston_backend { - void (*destroy)(struct weston_compositor *compositor); - - /** Begin a repaint sequence - * - * Provides the backend with explicit markers around repaint - * sequences, which may allow the backend to aggregate state - * application. This call will be bracketed by the repaint_flush (on - * success), or repaint_cancel (when any output in the grouping fails - * repaint). - * - * Returns an opaque pointer, which the backend may use as private - * data referring to the repaint cycle. - */ - void * (*repaint_begin)(struct weston_compositor *compositor); - - /** Cancel a repaint sequence - * - * Cancels a repaint sequence, when an error has occurred during - * one output's repaint; see repaint_begin. - * - * @param repaint_data Data returned by repaint_begin - */ - void (*repaint_cancel)(struct weston_compositor *compositor, - void *repaint_data); - - /** Conclude a repaint sequence - * - * Called on successful completion of a repaint sequence; see - * repaint_begin. - * - * @param repaint_data Data returned by repaint_begin - */ - int (*repaint_flush)(struct weston_compositor *compositor, - void *repaint_data); - - /** Allocate a new output - * - * @param compositor The compositor. - * @param name Name for the new output. - * - * Allocates a new output structure that embeds a weston_output, - * initializes it, and returns the pointer to the weston_output - * member. - * - * Must set weston_output members @c destroy, @c enable and @c disable. - */ - struct weston_output * - (*create_output)(struct weston_compositor *compositor, - const char *name); - - /** Notify of device addition/removal - * - * @param compositor The compositor. - * @param device The device that has changed. - * @param added Where it was added (or removed) - * - * Called when a device has been added/removed from the session. - * The backend can decide what to do based on whether it is a - * device that it is controlling or not. - */ - void (*device_changed)(struct weston_compositor *compositor, - dev_t device, bool added); - - /** Verifies if the dmabuf can be used directly/scanned-out by the HW. - * - * @param compositor The compositor. - * @param buffer The dmabuf to verify. - * - * Determines if the buffer can be imported directly by the display - * controller/HW. Back-ends can use this to check if the supplied - * buffer can be scanned-out, as to void importing it into the GPU. - */ - bool (*can_scanout_dmabuf)(struct weston_compositor *compositor, - struct linux_dmabuf_buffer *buffer); -}; - -/* weston_head */ - -void -weston_head_init(struct weston_head *head, const char *name); - -void -weston_head_release(struct weston_head *head); - -void -weston_head_set_connection_status(struct weston_head *head, bool connected); - -void -weston_head_set_internal(struct weston_head *head); - -void -weston_head_set_monitor_strings(struct weston_head *head, - const char *make, - const char *model, - const char *serialno); -void -weston_head_set_non_desktop(struct weston_head *head, bool non_desktop); - -void -weston_head_set_physical_size(struct weston_head *head, - int32_t mm_width, int32_t mm_height); - -void -weston_head_set_subpixel(struct weston_head *head, - enum wl_output_subpixel sp); -/* weston_output */ - -void -weston_output_init(struct weston_output *output, - struct weston_compositor *compositor, - const char *name); -void -weston_output_damage(struct weston_output *output); - -void -weston_output_release(struct weston_output *output); - -void -weston_output_init_zoom(struct weston_output *output); - -void -weston_output_finish_frame(struct weston_output *output, - const struct timespec *stamp, - uint32_t presented_flags); -int -weston_output_mode_set_native(struct weston_output *output, - struct weston_mode *mode, - int32_t scale); -void -weston_output_transform_coordinate(struct weston_output *output, - double device_x, double device_y, - double *x, double *y); - -/* weston_seat */ - -void -notify_axis(struct weston_seat *seat, const struct timespec *time, - struct weston_pointer_axis_event *event); -void -notify_axis_source(struct weston_seat *seat, uint32_t source); - -void -notify_button(struct weston_seat *seat, const struct timespec *time, - int32_t button, enum wl_pointer_button_state state); - -void -notify_key(struct weston_seat *seat, const struct timespec *time, uint32_t key, - enum wl_keyboard_key_state state, - enum weston_key_state_update update_state); -void -notify_keyboard_focus_in(struct weston_seat *seat, struct wl_array *keys, - enum weston_key_state_update update_state); -void -notify_keyboard_focus_out(struct weston_seat *seat); - -void -notify_motion(struct weston_seat *seat, const struct timespec *time, - struct weston_pointer_motion_event *event); -void -notify_motion_absolute(struct weston_seat *seat, const struct timespec *time, - double x, double y); -void -notify_modifiers(struct weston_seat *seat, uint32_t serial); - -void -notify_pointer_frame(struct weston_seat *seat); - -void -notify_pointer_focus(struct weston_seat *seat, struct weston_output *output, - double x, double y); - -/* weston_touch_device */ - -void -notify_touch_normalized(struct weston_touch_device *device, - const struct timespec *time, - int touch_id, - double x, double y, - const struct weston_point2d_device_normalized *norm, - int touch_type); - -/** Feed in touch down, motion, and up events, non-calibratable device. - * - * @sa notify_touch_cal - */ -static inline void -notify_touch(struct weston_touch_device *device, const struct timespec *time, - int touch_id, double x, double y, int touch_type) -{ - notify_touch_normalized(device, time, touch_id, x, y, NULL, touch_type); -} - -void -notify_touch_frame(struct weston_touch_device *device); - -void -notify_touch_cancel(struct weston_touch_device *device); - -void -notify_touch_calibrator(struct weston_touch_device *device, - const struct timespec *time, int32_t slot, - const struct weston_point2d_device_normalized *norm, - int touch_type); -void -notify_touch_calibrator_cancel(struct weston_touch_device *device); -void -notify_touch_calibrator_frame(struct weston_touch_device *device); - -#endif diff --git a/libweston/bindings.c b/libweston/bindings.c deleted file mode 100644 index 68c07a22..00000000 --- a/libweston/bindings.c +++ /dev/null @@ -1,588 +0,0 @@ -/* - * Copyright © 2011-2012 Intel Corporation - * - * 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 <stdint.h> -#include <stdlib.h> -#include <linux/input.h> - -#include <libweston/libweston.h> -#include "shared/helpers.h" -#include "shared/timespec-util.h" - -struct weston_binding { - uint32_t key; - uint32_t button; - uint32_t axis; - uint32_t modifier; - void *handler; - void *data; - struct wl_list link; -}; - -static struct weston_binding * -weston_compositor_add_binding(struct weston_compositor *compositor, - uint32_t key, uint32_t button, uint32_t axis, - uint32_t modifier, void *handler, void *data) -{ - struct weston_binding *binding; - - binding = malloc(sizeof *binding); - if (binding == NULL) - return NULL; - - binding->key = key; - binding->button = button; - binding->axis = axis; - binding->modifier = modifier; - binding->handler = handler; - binding->data = data; - - return binding; -} - -WL_EXPORT struct weston_binding * -weston_compositor_add_key_binding(struct weston_compositor *compositor, - uint32_t key, uint32_t modifier, - weston_key_binding_handler_t handler, - void *data) -{ - struct weston_binding *binding; - - binding = weston_compositor_add_binding(compositor, key, 0, 0, - modifier, handler, data); - if (binding == NULL) - return NULL; - - wl_list_insert(compositor->key_binding_list.prev, &binding->link); - - return binding; -} - -WL_EXPORT struct weston_binding * -weston_compositor_add_modifier_binding(struct weston_compositor *compositor, - uint32_t modifier, - weston_modifier_binding_handler_t handler, - void *data) -{ - struct weston_binding *binding; - - binding = weston_compositor_add_binding(compositor, 0, 0, 0, - modifier, handler, data); - if (binding == NULL) - return NULL; - - wl_list_insert(compositor->modifier_binding_list.prev, &binding->link); - - return binding; -} - -WL_EXPORT struct weston_binding * -weston_compositor_add_button_binding(struct weston_compositor *compositor, - uint32_t button, uint32_t modifier, - weston_button_binding_handler_t handler, - void *data) -{ - struct weston_binding *binding; - - binding = weston_compositor_add_binding(compositor, 0, button, 0, - modifier, handler, data); - if (binding == NULL) - return NULL; - - wl_list_insert(compositor->button_binding_list.prev, &binding->link); - - return binding; -} - -WL_EXPORT struct weston_binding * -weston_compositor_add_touch_binding(struct weston_compositor *compositor, - uint32_t modifier, - weston_touch_binding_handler_t handler, - void *data) -{ - struct weston_binding *binding; - - binding = weston_compositor_add_binding(compositor, 0, 0, 0, - modifier, handler, data); - if (binding == NULL) - return NULL; - - wl_list_insert(compositor->touch_binding_list.prev, &binding->link); - - return binding; -} - -WL_EXPORT struct weston_binding * -weston_compositor_add_axis_binding(struct weston_compositor *compositor, - uint32_t axis, uint32_t modifier, - weston_axis_binding_handler_t handler, - void *data) -{ - struct weston_binding *binding; - - binding = weston_compositor_add_binding(compositor, 0, 0, axis, - modifier, handler, data); - if (binding == NULL) - return NULL; - - wl_list_insert(compositor->axis_binding_list.prev, &binding->link); - - return binding; -} - -WL_EXPORT struct weston_binding * -weston_compositor_add_debug_binding(struct weston_compositor *compositor, - uint32_t key, - weston_key_binding_handler_t handler, - void *data) -{ - struct weston_binding *binding; - - binding = weston_compositor_add_binding(compositor, key, 0, 0, 0, - handler, data); - - wl_list_insert(compositor->debug_binding_list.prev, &binding->link); - - return binding; -} - -WL_EXPORT void -weston_binding_destroy(struct weston_binding *binding) -{ - wl_list_remove(&binding->link); - free(binding); -} - -void -weston_binding_list_destroy_all(struct wl_list *list) -{ - struct weston_binding *binding, *tmp; - - wl_list_for_each_safe(binding, tmp, list, link) - weston_binding_destroy(binding); -} - -struct binding_keyboard_grab { - uint32_t key; - struct weston_keyboard_grab grab; -}; - -static void -binding_key(struct weston_keyboard_grab *grab, - const struct timespec *time, uint32_t key, uint32_t state_w) -{ - struct binding_keyboard_grab *b = - container_of(grab, struct binding_keyboard_grab, grab); - struct wl_resource *resource; - enum wl_keyboard_key_state state = state_w; - uint32_t serial; - struct weston_keyboard *keyboard = grab->keyboard; - struct wl_display *display = keyboard->seat->compositor->wl_display; - uint32_t msecs; - - if (key == b->key) { - if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { - weston_keyboard_end_grab(grab->keyboard); - if (keyboard->input_method_resource) - keyboard->grab = &keyboard->input_method_grab; - free(b); - } else { - /* Don't send the key press event for the binding key */ - return; - } - } - if (!wl_list_empty(&keyboard->focus_resource_list)) { - serial = wl_display_next_serial(display); - msecs = timespec_to_msec(time); - wl_resource_for_each(resource, &keyboard->focus_resource_list) { - wl_keyboard_send_key(resource, - serial, - msecs, - key, - state); - } - } -} - -static void -binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial, - uint32_t mods_depressed, uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) -{ - struct wl_resource *resource; - - wl_resource_for_each(resource, &grab->keyboard->focus_resource_list) { - wl_keyboard_send_modifiers(resource, serial, mods_depressed, - mods_latched, mods_locked, group); - } -} - -static void -binding_cancel(struct weston_keyboard_grab *grab) -{ - struct binding_keyboard_grab *binding_grab = - container_of(grab, struct binding_keyboard_grab, grab); - - weston_keyboard_end_grab(grab->keyboard); - free(binding_grab); -} - -static const struct weston_keyboard_grab_interface binding_grab = { - binding_key, - binding_modifiers, - binding_cancel, -}; - -static void -install_binding_grab(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, - struct weston_surface *focus) -{ - struct binding_keyboard_grab *grab; - - grab = malloc(sizeof *grab); - grab->key = key; - grab->grab.interface = &binding_grab; - weston_keyboard_start_grab(keyboard, &grab->grab); - - /* Notify the surface which had the focus before this binding - * triggered that we stole a keypress from under it, by forcing - * a wl_keyboard leave/enter pair. The enter event will contain - * the pressed key in the keys array, so the client will know - * the exact state of the keyboard. - * If the old focus surface is different than the new one it - * means it was changed in the binding handler, so it received - * the enter event already. */ - if (focus && keyboard->focus == focus) { - weston_keyboard_set_focus(keyboard, NULL); - weston_keyboard_set_focus(keyboard, focus); - } -} - -void -weston_compositor_run_key_binding(struct weston_compositor *compositor, - struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, - enum wl_keyboard_key_state state) -{ - struct weston_binding *b, *tmp; - struct weston_surface *focus; - struct weston_seat *seat = keyboard->seat; - - if (state == WL_KEYBOARD_KEY_STATE_RELEASED) - return; - - /* Invalidate all active modifier bindings. */ - wl_list_for_each(b, &compositor->modifier_binding_list, link) - b->key = key; - - wl_list_for_each_safe(b, tmp, &compositor->key_binding_list, link) { - if (b->key == key && b->modifier == seat->modifier_state) { - weston_key_binding_handler_t handler = b->handler; - focus = keyboard->focus; - handler(keyboard, time, key, b->data); - - /* If this was a key binding and it didn't - * install a keyboard grab, install one now to - * swallow the key press. */ - if (keyboard->grab == - &keyboard->default_grab) - install_binding_grab(keyboard, - time, - key, - focus); - } - } -} - -void -weston_compositor_run_modifier_binding(struct weston_compositor *compositor, - struct weston_keyboard *keyboard, - enum weston_keyboard_modifier modifier, - enum wl_keyboard_key_state state) -{ - struct weston_binding *b, *tmp; - - if (keyboard->grab != &keyboard->default_grab) - return; - - wl_list_for_each_safe(b, tmp, &compositor->modifier_binding_list, link) { - weston_modifier_binding_handler_t handler = b->handler; - - if (b->modifier != modifier) - continue; - - /* Prime the modifier binding. */ - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - b->key = 0; - continue; - } - /* Ignore the binding if a key was pressed in between. */ - else if (b->key != 0) { - return; - } - - handler(keyboard, modifier, b->data); - } -} - -void -weston_compositor_run_button_binding(struct weston_compositor *compositor, - struct weston_pointer *pointer, - const struct timespec *time, - uint32_t button, - enum wl_pointer_button_state state) -{ - struct weston_binding *b, *tmp; - - if (state == WL_POINTER_BUTTON_STATE_RELEASED) - return; - - /* Invalidate all active modifier bindings. */ - wl_list_for_each(b, &compositor->modifier_binding_list, link) - b->key = button; - - wl_list_for_each_safe(b, tmp, &compositor->button_binding_list, link) { - if (b->button == button && - b->modifier == pointer->seat->modifier_state) { - weston_button_binding_handler_t handler = b->handler; - handler(pointer, time, button, b->data); - } - } -} - -void -weston_compositor_run_touch_binding(struct weston_compositor *compositor, - struct weston_touch *touch, - const struct timespec *time, - int touch_type) -{ - struct weston_binding *b, *tmp; - - if (touch->num_tp != 1 || touch_type != WL_TOUCH_DOWN) - return; - - wl_list_for_each_safe(b, tmp, &compositor->touch_binding_list, link) { - if (b->modifier == touch->seat->modifier_state) { - weston_touch_binding_handler_t handler = b->handler; - handler(touch, time, b->data); - } - } -} - -int -weston_compositor_run_axis_binding(struct weston_compositor *compositor, - struct weston_pointer *pointer, - const struct timespec *time, - struct weston_pointer_axis_event *event) -{ - struct weston_binding *b, *tmp; - - /* Invalidate all active modifier bindings. */ - wl_list_for_each(b, &compositor->modifier_binding_list, link) - b->key = event->axis; - - wl_list_for_each_safe(b, tmp, &compositor->axis_binding_list, link) { - if (b->axis == event->axis && - b->modifier == pointer->seat->modifier_state) { - weston_axis_binding_handler_t handler = b->handler; - handler(pointer, time, event, b->data); - return 1; - } - } - - return 0; -} - -int -weston_compositor_run_debug_binding(struct weston_compositor *compositor, - struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, - enum wl_keyboard_key_state state) -{ - weston_key_binding_handler_t handler; - struct weston_binding *binding, *tmp; - int count = 0; - - wl_list_for_each_safe(binding, tmp, &compositor->debug_binding_list, link) { - if (key != binding->key) - continue; - - count++; - handler = binding->handler; - handler(keyboard, time, key, binding->data); - } - - return count; -} - -struct debug_binding_grab { - struct weston_keyboard_grab grab; - struct weston_seat *seat; - uint32_t key[2]; - int key_released[2]; -}; - -static void -debug_binding_key(struct weston_keyboard_grab *grab, const struct timespec *time, - uint32_t key, uint32_t state) -{ - struct debug_binding_grab *db = (struct debug_binding_grab *) grab; - struct weston_compositor *ec = db->seat->compositor; - struct wl_display *display = ec->wl_display; - struct wl_resource *resource; - uint32_t serial; - int send = 0, terminate = 0; - int check_binding = 1; - int i; - struct wl_list *resource_list; - uint32_t msecs; - - if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { - /* Do not run bindings on key releases */ - check_binding = 0; - - for (i = 0; i < 2; i++) - if (key == db->key[i]) - db->key_released[i] = 1; - - if (db->key_released[0] && db->key_released[1]) { - /* All key releases been swalled so end the grab */ - terminate = 1; - } else if (key != db->key[0] && key != db->key[1]) { - /* Should not swallow release of other keys */ - send = 1; - } - } else if (key == db->key[0] && !db->key_released[0]) { - /* Do not check bindings for the first press of the binding - * key. This allows it to be used as a debug shortcut. - * We still need to swallow this event. */ - check_binding = 0; - } else if (db->key[1]) { - /* If we already ran a binding don't process another one since - * we can't keep track of all the binding keys that were - * pressed in order to swallow the release events. */ - send = 1; - check_binding = 0; - } - - if (check_binding) { - if (weston_compositor_run_debug_binding(ec, grab->keyboard, - time, key, state)) { - /* We ran a binding so swallow the press and keep the - * grab to swallow the released too. */ - send = 0; - terminate = 0; - db->key[1] = key; - } else { - /* Terminate the grab since the key pressed is not a - * debug binding key. */ - send = 1; - terminate = 1; - } - } - - if (send) { - serial = wl_display_next_serial(display); - resource_list = &grab->keyboard->focus_resource_list; - msecs = timespec_to_msec(time); - wl_resource_for_each(resource, resource_list) { - wl_keyboard_send_key(resource, serial, msecs, key, state); - } - } - - if (terminate) { - weston_keyboard_end_grab(grab->keyboard); - if (grab->keyboard->input_method_resource) - grab->keyboard->grab = &grab->keyboard->input_method_grab; - free(db); - } -} - -static void -debug_binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial, - uint32_t mods_depressed, uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) -{ - struct wl_resource *resource; - struct wl_list *resource_list; - - resource_list = &grab->keyboard->focus_resource_list; - - wl_resource_for_each(resource, resource_list) { - wl_keyboard_send_modifiers(resource, serial, mods_depressed, - mods_latched, mods_locked, group); - } -} - -static void -debug_binding_cancel(struct weston_keyboard_grab *grab) -{ - struct debug_binding_grab *db = (struct debug_binding_grab *) grab; - - weston_keyboard_end_grab(grab->keyboard); - free(db); -} - -struct weston_keyboard_grab_interface debug_binding_keyboard_grab = { - debug_binding_key, - debug_binding_modifiers, - debug_binding_cancel, -}; - -static void -debug_binding(struct weston_keyboard *keyboard, const struct timespec *time, - uint32_t key, void *data) -{ - struct debug_binding_grab *grab; - - grab = calloc(1, sizeof *grab); - if (!grab) - return; - - grab->seat = keyboard->seat; - grab->key[0] = key; - grab->grab.interface = &debug_binding_keyboard_grab; - weston_keyboard_start_grab(keyboard, &grab->grab); -} - -/** Install the trigger binding for debug bindings. - * - * \param compositor The compositor. - * \param mod The modifier. - * - * This will add a key binding for modifier+SHIFT+SPACE that will trigger - * debug key bindings. - */ -WL_EXPORT void -weston_install_debug_key_binding(struct weston_compositor *compositor, - uint32_t mod) -{ - weston_compositor_add_key_binding(compositor, KEY_SPACE, - mod | MODIFIER_SHIFT, - debug_binding, NULL); -} diff --git a/libweston/clipboard.c b/libweston/clipboard.c deleted file mode 100644 index c8296b01..00000000 --- a/libweston/clipboard.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright © 2012 Intel Corporation - * - * 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 <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <linux/input.h> -#include <fcntl.h> -#include <unistd.h> -#include <sys/uio.h> - -#include <libweston/libweston.h> -#include "shared/helpers.h" - -struct clipboard_source { - struct weston_data_source base; - struct wl_array contents; - struct clipboard *clipboard; - struct wl_event_source *event_source; - uint32_t serial; - int refcount; - int fd; -}; - -struct clipboard { - struct weston_seat *seat; - struct wl_listener selection_listener; - struct wl_listener destroy_listener; - struct clipboard_source *source; -}; - -static void clipboard_client_create(struct clipboard_source *source, int fd); - -static void -clipboard_source_unref(struct clipboard_source *source) -{ - char **s; - - source->refcount--; - if (source->refcount > 0) - return; - - if (source->event_source) { - wl_event_source_remove(source->event_source); - close(source->fd); - } - wl_signal_emit(&source->base.destroy_signal, - &source->base); - s = source->base.mime_types.data; - free(*s); - wl_array_release(&source->base.mime_types); - wl_array_release(&source->contents); - free(source); -} - -static int -clipboard_source_data(int fd, uint32_t mask, void *data) -{ - struct clipboard_source *source = data; - struct clipboard *clipboard = source->clipboard; - char *p; - int len, size; - - if (source->contents.alloc - source->contents.size < 1024) { - wl_array_add(&source->contents, 1024); - source->contents.size -= 1024; - } - - p = source->contents.data + source->contents.size; - size = source->contents.alloc - source->contents.size; - len = read(fd, p, size); - if (len == 0) { - wl_event_source_remove(source->event_source); - close(fd); - source->event_source = NULL; - } else if (len < 0) { - clipboard_source_unref(source); - clipboard->source = NULL; - } else { - source->contents.size += len; - } - - return 1; -} - -static void -clipboard_source_accept(struct weston_data_source *source, - uint32_t time, const char *mime_type) -{ -} - -static void -clipboard_source_send(struct weston_data_source *base, - const char *mime_type, int32_t fd) -{ - struct clipboard_source *source = - container_of(base, struct clipboard_source, base); - char **s; - - s = source->base.mime_types.data; - if (strcmp(mime_type, s[0]) == 0) - clipboard_client_create(source, fd); - else - close(fd); -} - -static void -clipboard_source_cancel(struct weston_data_source *source) -{ -} - -static struct clipboard_source * -clipboard_source_create(struct clipboard *clipboard, - const char *mime_type, uint32_t serial, int fd) -{ - struct wl_display *display = clipboard->seat->compositor->wl_display; - struct wl_event_loop *loop = wl_display_get_event_loop(display); - struct clipboard_source *source; - char **s; - - source = zalloc(sizeof *source); - if (source == NULL) - return NULL; - - wl_array_init(&source->contents); - wl_array_init(&source->base.mime_types); - source->base.resource = NULL; - source->base.accept = clipboard_source_accept; - source->base.send = clipboard_source_send; - source->base.cancel = clipboard_source_cancel; - wl_signal_init(&source->base.destroy_signal); - source->refcount = 1; - source->clipboard = clipboard; - source->serial = serial; - source->fd = fd; - - s = wl_array_add(&source->base.mime_types, sizeof *s); - if (s == NULL) - goto err_add; - *s = strdup(mime_type); - if (*s == NULL) - goto err_strdup; - source->event_source = - wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, - clipboard_source_data, source); - if (source->event_source == NULL) - goto err_source; - - return source; - - err_source: - free(*s); - err_strdup: - wl_array_release(&source->base.mime_types); - err_add: - free(source); - - return NULL; -} - -struct clipboard_client { - struct wl_event_source *event_source; - size_t offset; - struct clipboard_source *source; -}; - -static int -clipboard_client_data(int fd, uint32_t mask, void *data) -{ - struct clipboard_client *client = data; - char *p; - size_t size; - int len; - - size = client->source->contents.size; - p = client->source->contents.data; - len = write(fd, p + client->offset, size - client->offset); - if (len > 0) - client->offset += len; - - if (client->offset == size || len <= 0) { - close(fd); - wl_event_source_remove(client->event_source); - clipboard_source_unref(client->source); - free(client); - } - - return 1; -} - -static void -clipboard_client_create(struct clipboard_source *source, int fd) -{ - struct weston_seat *seat = source->clipboard->seat; - struct clipboard_client *client; - struct wl_event_loop *loop = - wl_display_get_event_loop(seat->compositor->wl_display); - - client = zalloc(sizeof *client); - if (client == NULL) - return; - - client->source = source; - source->refcount++; - client->event_source = - wl_event_loop_add_fd(loop, fd, WL_EVENT_WRITABLE, - clipboard_client_data, client); -} - -static void -clipboard_set_selection(struct wl_listener *listener, void *data) -{ - struct clipboard *clipboard = - container_of(listener, struct clipboard, selection_listener); - struct weston_seat *seat = data; - struct weston_data_source *source = seat->selection_data_source; - const char **mime_types; - int p[2]; - - if (source == NULL) { - if (clipboard->source) - weston_seat_set_selection(seat, - &clipboard->source->base, - clipboard->source->serial); - return; - } else if (source->accept == clipboard_source_accept) { - /* Callback for our data source. */ - return; - } - - if (clipboard->source) - clipboard_source_unref(clipboard->source); - - clipboard->source = NULL; - - mime_types = source->mime_types.data; - - if (!mime_types || pipe2(p, O_CLOEXEC) == -1) - return; - - source->send(source, mime_types[0], p[1]); - - clipboard->source = - clipboard_source_create(clipboard, mime_types[0], - seat->selection_serial, p[0]); - if (clipboard->source == NULL) { - close(p[0]); - return; - } -} - -static void -clipboard_destroy(struct wl_listener *listener, void *data) -{ - struct clipboard *clipboard = - container_of(listener, struct clipboard, destroy_listener); - - wl_list_remove(&clipboard->selection_listener.link); - wl_list_remove(&clipboard->destroy_listener.link); - - free(clipboard); -} - -struct clipboard * -clipboard_create(struct weston_seat *seat) -{ - struct clipboard *clipboard; - - clipboard = zalloc(sizeof *clipboard); - if (clipboard == NULL) - return NULL; - - clipboard->seat = seat; - clipboard->selection_listener.notify = clipboard_set_selection; - clipboard->destroy_listener.notify = clipboard_destroy; - - wl_signal_add(&seat->selection_signal, - &clipboard->selection_listener); - wl_signal_add(&seat->destroy_signal, - &clipboard->destroy_listener); - - return clipboard; -} diff --git a/libweston/compositor.c b/libweston/compositor.c deleted file mode 100644 index 1e7c2b94..00000000 --- a/libweston/compositor.c +++ /dev/null @@ -1,7882 +0,0 @@ -/* - * Copyright © 2010-2011 Intel Corporation - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2012-2018 Collabora, Ltd. - * Copyright © 2017, 2018 General Electric Company - * - * 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 <fcntl.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <stdint.h> -#include <limits.h> -#include <stdarg.h> -#include <assert.h> -#include <sys/ioctl.h> -#include <sys/mman.h> -#include <sys/wait.h> -#include <sys/socket.h> -#include <sys/utsname.h> -#include <sys/stat.h> -#include <unistd.h> -#include <math.h> -#include <linux/input.h> -#include <dlfcn.h> -#include <signal.h> -#include <setjmp.h> -#include <sys/time.h> -#include <time.h> -#include <errno.h> -#include <inttypes.h> - -#include "timeline.h" - -#include <libweston/libweston.h> -#include <libweston/weston-log.h> -#include "linux-dmabuf.h" -#include "viewporter-server-protocol.h" -#include "presentation-time-server-protocol.h" -#include "xdg-output-unstable-v1-server-protocol.h" -#include "linux-explicit-synchronization-unstable-v1-server-protocol.h" -#include "linux-explicit-synchronization.h" -#include "shared/fd-util.h" -#include "shared/helpers.h" -#include "shared/os-compatibility.h" -#include "shared/string-helpers.h" -#include "shared/timespec-util.h" -#include "git-version.h" -#include <libweston/version.h> -#include <libweston/plugin-registry.h> -#include "pixel-formats.h" -#include "backend.h" -#include "libweston-internal.h" - -#include "weston-log-internal.h" - -/** - * \defgroup head Head - * \defgroup output Output - * \defgroup compositor Compositor - */ - -#define DEFAULT_REPAINT_WINDOW 7 /* milliseconds */ - -static void -weston_output_update_matrix(struct weston_output *output); - -static void -weston_output_transform_scale_init(struct weston_output *output, - uint32_t transform, uint32_t scale); - -static void -weston_compositor_build_view_list(struct weston_compositor *compositor); - -static char * -weston_output_create_heads_string(struct weston_output *output); - -/** Send wl_output events for mode and scale changes - * - * \param head Send on all resources bound to this head. - * \param mode_changed If true, send the current mode. - * \param scale_changed If true, send the current scale. - */ -static void -weston_mode_switch_send_events(struct weston_head *head, - bool mode_changed, bool scale_changed) -{ - struct weston_output *output = head->output; - struct wl_resource *resource; - int version; - - wl_resource_for_each(resource, &head->resource_list) { - if (mode_changed) { - wl_output_send_mode(resource, - output->current_mode->flags, - output->current_mode->width, - output->current_mode->height, - output->current_mode->refresh); - } - - version = wl_resource_get_version(resource); - if (version >= WL_OUTPUT_SCALE_SINCE_VERSION && scale_changed) - wl_output_send_scale(resource, output->current_scale); - - if (version >= WL_OUTPUT_DONE_SINCE_VERSION) - wl_output_send_done(resource); - } - wl_resource_for_each(resource, &head->xdg_output_resource_list) { - zxdg_output_v1_send_logical_position(resource, - output->x, - output->y); - zxdg_output_v1_send_logical_size(resource, - output->width, - output->height); - zxdg_output_v1_send_done(resource); - } -} - -static void -weston_mode_switch_finish(struct weston_output *output, - int mode_changed, int scale_changed) -{ - struct weston_seat *seat; - struct weston_head *head; - pixman_region32_t old_output_region; - - pixman_region32_init(&old_output_region); - pixman_region32_copy(&old_output_region, &output->region); - - /* Update output region and transformation matrix */ - weston_output_transform_scale_init(output, output->transform, output->current_scale); - - pixman_region32_init_rect(&output->region, output->x, output->y, - output->width, output->height); - - weston_output_update_matrix(output); - - /* If a pointer falls outside the outputs new geometry, move it to its - * lower-right corner */ - wl_list_for_each(seat, &output->compositor->seat_list, link) { - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - int32_t x, y; - - if (!pointer) - continue; - - x = wl_fixed_to_int(pointer->x); - y = wl_fixed_to_int(pointer->y); - - if (!pixman_region32_contains_point(&old_output_region, - x, y, NULL) || - pixman_region32_contains_point(&output->region, - x, y, NULL)) - continue; - - if (x >= output->x + output->width) - x = output->x + output->width - 1; - if (y >= output->y + output->height) - y = output->y + output->height - 1; - - pointer->x = wl_fixed_from_int(x); - pointer->y = wl_fixed_from_int(y); - } - - pixman_region32_fini(&old_output_region); - - if (!mode_changed && !scale_changed) - return; - - /* notify clients of the changes */ - wl_list_for_each(head, &output->head_list, output_link) - weston_mode_switch_send_events(head, - mode_changed, scale_changed); -} - -static void -weston_compositor_reflow_outputs(struct weston_compositor *compositor, - struct weston_output *resized_output, int delta_width); - -/** - * \ingroup output - */ -WL_EXPORT int -weston_output_mode_set_native(struct weston_output *output, - struct weston_mode *mode, - int32_t scale) -{ - int ret; - int mode_changed = 0, scale_changed = 0; - int32_t old_width; - - if (!output->switch_mode) - return -1; - - if (!output->original_mode) { - mode_changed = 1; - ret = output->switch_mode(output, mode); - if (ret < 0) - return ret; - if (output->current_scale != scale) { - scale_changed = 1; - output->current_scale = scale; - } - } - - old_width = output->width; - output->native_mode = mode; - output->native_scale = scale; - - weston_mode_switch_finish(output, mode_changed, scale_changed); - - if (mode_changed || scale_changed) { - weston_compositor_reflow_outputs(output->compositor, output, output->width - old_width); - - wl_signal_emit(&output->compositor->output_resized_signal, output); - } - return 0; -} - -/** - * \ingroup output - */ -WL_EXPORT int -weston_output_mode_switch_to_native(struct weston_output *output) -{ - int ret; - int mode_changed = 0, scale_changed = 0; - - if (!output->switch_mode) - return -1; - - if (!output->original_mode) { - weston_log("already in the native mode\n"); - return -1; - } - /* the non fullscreen clients haven't seen a mode set since we - * switched into a temporary, so we need to notify them if the - * mode at that time is different from the native mode now. - */ - mode_changed = (output->original_mode != output->native_mode); - scale_changed = (output->original_scale != output->native_scale); - - ret = output->switch_mode(output, output->native_mode); - if (ret < 0) - return ret; - - output->current_scale = output->native_scale; - - output->original_mode = NULL; - output->original_scale = 0; - - weston_mode_switch_finish(output, mode_changed, scale_changed); - - return 0; -} - -/** - * \ingroup output - */ -WL_EXPORT int -weston_output_mode_switch_to_temporary(struct weston_output *output, - struct weston_mode *mode, - int32_t scale) -{ - int ret; - - if (!output->switch_mode) - return -1; - - /* original_mode is the last mode non full screen clients have seen, - * so we shouldn't change it if we already have one set. - */ - if (!output->original_mode) { - output->original_mode = output->native_mode; - output->original_scale = output->native_scale; - } - ret = output->switch_mode(output, mode); - if (ret < 0) - return ret; - - output->current_scale = scale; - - weston_mode_switch_finish(output, 0, 0); - - return 0; -} - -static void -region_init_infinite(pixman_region32_t *region) -{ - pixman_region32_init_rect(region, INT32_MIN, INT32_MIN, - UINT32_MAX, UINT32_MAX); -} - -static struct weston_subsurface * -weston_surface_to_subsurface(struct weston_surface *surface); - -WL_EXPORT struct weston_view * -weston_view_create(struct weston_surface *surface) -{ - struct weston_view *view; - - view = zalloc(sizeof *view); - if (view == NULL) - return NULL; - - view->surface = surface; - view->plane = &surface->compositor->primary_plane; - - /* Assign to surface */ - wl_list_insert(&surface->views, &view->surface_link); - - wl_signal_init(&view->destroy_signal); - wl_list_init(&view->link); - wl_list_init(&view->layer_link.link); - - pixman_region32_init(&view->clip); - - view->alpha = 1.0; - pixman_region32_init(&view->transform.opaque); - - wl_list_init(&view->geometry.transformation_list); - wl_list_insert(&view->geometry.transformation_list, - &view->transform.position.link); - weston_matrix_init(&view->transform.position.matrix); - wl_list_init(&view->geometry.child_list); - pixman_region32_init(&view->geometry.scissor); - pixman_region32_init(&view->transform.boundingbox); - view->transform.dirty = 1; - - return view; -} - -struct weston_frame_callback { - struct wl_resource *resource; - struct wl_list link; -}; - -struct weston_presentation_feedback { - struct wl_resource *resource; - - /* XXX: could use just wl_resource_get_link() instead */ - struct wl_list link; - - /* The per-surface feedback flags */ - uint32_t psf_flags; -}; - -static void -weston_presentation_feedback_discard( - struct weston_presentation_feedback *feedback) -{ - wp_presentation_feedback_send_discarded(feedback->resource); - wl_resource_destroy(feedback->resource); -} - -static void -weston_presentation_feedback_discard_list(struct wl_list *list) -{ - struct weston_presentation_feedback *feedback, *tmp; - - wl_list_for_each_safe(feedback, tmp, list, link) - weston_presentation_feedback_discard(feedback); -} - -static void -weston_presentation_feedback_present( - struct weston_presentation_feedback *feedback, - struct weston_output *output, - uint32_t refresh_nsec, - const struct timespec *ts, - uint64_t seq, - uint32_t flags) -{ - struct wl_client *client = wl_resource_get_client(feedback->resource); - struct weston_head *head; - struct wl_resource *o; - uint32_t tv_sec_hi; - uint32_t tv_sec_lo; - uint32_t tv_nsec; - bool done = false; - - wl_list_for_each(head, &output->head_list, output_link) { - wl_resource_for_each(o, &head->resource_list) { - if (wl_resource_get_client(o) != client) - continue; - - wp_presentation_feedback_send_sync_output(feedback->resource, o); - done = true; - } - - /* For clone mode, send it for just one wl_output global, - * they are all equivalent anyway. - */ - if (done) - break; - } - - timespec_to_proto(ts, &tv_sec_hi, &tv_sec_lo, &tv_nsec); - wp_presentation_feedback_send_presented(feedback->resource, - tv_sec_hi, tv_sec_lo, tv_nsec, - refresh_nsec, - seq >> 32, seq & 0xffffffff, - flags | feedback->psf_flags); - wl_resource_destroy(feedback->resource); -} - -static void -weston_presentation_feedback_present_list(struct wl_list *list, - struct weston_output *output, - uint32_t refresh_nsec, - const struct timespec *ts, - uint64_t seq, - uint32_t flags) -{ - struct weston_presentation_feedback *feedback, *tmp; - - assert(!(flags & WP_PRESENTATION_FEEDBACK_INVALID) || - wl_list_empty(list)); - - wl_list_for_each_safe(feedback, tmp, list, link) - weston_presentation_feedback_present(feedback, output, - refresh_nsec, ts, seq, - flags); -} - -static void -surface_state_handle_buffer_destroy(struct wl_listener *listener, void *data) -{ - struct weston_surface_state *state = - container_of(listener, struct weston_surface_state, - buffer_destroy_listener); - - state->buffer = NULL; -} - -static void -weston_surface_state_init(struct weston_surface_state *state) -{ - state->newly_attached = 0; - state->buffer = NULL; - state->buffer_destroy_listener.notify = - surface_state_handle_buffer_destroy; - state->sx = 0; - state->sy = 0; - - pixman_region32_init(&state->damage_surface); - pixman_region32_init(&state->damage_buffer); - pixman_region32_init(&state->opaque); - region_init_infinite(&state->input); - - wl_list_init(&state->frame_callback_list); - wl_list_init(&state->feedback_list); - - state->buffer_viewport.buffer.transform = WL_OUTPUT_TRANSFORM_NORMAL; - state->buffer_viewport.buffer.scale = 1; - state->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1); - state->buffer_viewport.surface.width = -1; - state->buffer_viewport.changed = 0; - - state->acquire_fence_fd = -1; - - state->desired_protection = WESTON_HDCP_DISABLE; - state->protection_mode = WESTON_SURFACE_PROTECTION_MODE_RELAXED; -} - -static void -weston_surface_state_fini(struct weston_surface_state *state) -{ - struct weston_frame_callback *cb, *next; - - wl_list_for_each_safe(cb, next, - &state->frame_callback_list, link) - wl_resource_destroy(cb->resource); - - weston_presentation_feedback_discard_list(&state->feedback_list); - - pixman_region32_fini(&state->input); - pixman_region32_fini(&state->opaque); - pixman_region32_fini(&state->damage_surface); - pixman_region32_fini(&state->damage_buffer); - - if (state->buffer) - wl_list_remove(&state->buffer_destroy_listener.link); - state->buffer = NULL; - - fd_clear(&state->acquire_fence_fd); - weston_buffer_release_reference(&state->buffer_release_ref, NULL); -} - -static void -weston_surface_state_set_buffer(struct weston_surface_state *state, - struct weston_buffer *buffer) -{ - if (state->buffer == buffer) - return; - - if (state->buffer) - wl_list_remove(&state->buffer_destroy_listener.link); - state->buffer = buffer; - if (state->buffer) - wl_signal_add(&state->buffer->destroy_signal, - &state->buffer_destroy_listener); -} - -WL_EXPORT struct weston_surface * -weston_surface_create(struct weston_compositor *compositor) -{ - struct weston_surface *surface; - - surface = zalloc(sizeof *surface); - if (surface == NULL) - return NULL; - - wl_signal_init(&surface->destroy_signal); - wl_signal_init(&surface->commit_signal); - - surface->compositor = compositor; - surface->ref_count = 1; - - surface->buffer_viewport.buffer.transform = WL_OUTPUT_TRANSFORM_NORMAL; - surface->buffer_viewport.buffer.scale = 1; - surface->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1); - surface->buffer_viewport.surface.width = -1; - - weston_surface_state_init(&surface->pending); - - pixman_region32_init(&surface->damage); - pixman_region32_init(&surface->opaque); - region_init_infinite(&surface->input); - - wl_list_init(&surface->views); - - wl_list_init(&surface->frame_callback_list); - wl_list_init(&surface->feedback_list); - - wl_list_init(&surface->subsurface_list); - wl_list_init(&surface->subsurface_list_pending); - - weston_matrix_init(&surface->buffer_to_surface_matrix); - weston_matrix_init(&surface->surface_to_buffer_matrix); - - wl_list_init(&surface->pointer_constraints); - - surface->acquire_fence_fd = -1; - - surface->desired_protection = WESTON_HDCP_DISABLE; - surface->current_protection = WESTON_HDCP_DISABLE; - surface->protection_mode = WESTON_SURFACE_PROTECTION_MODE_RELAXED; - - return surface; -} - -WL_EXPORT void -weston_surface_set_color(struct weston_surface *surface, - float red, float green, float blue, float alpha) -{ - surface->compositor->renderer->surface_set_color(surface, red, green, blue, alpha); - surface->is_opaque = !(alpha < 1.0); -} - -WL_EXPORT void -weston_view_to_global_float(struct weston_view *view, - float sx, float sy, float *x, float *y) -{ - if (view->transform.enabled) { - struct weston_vector v = { { sx, sy, 0.0f, 1.0f } }; - - weston_matrix_transform(&view->transform.matrix, &v); - - if (fabsf(v.f[3]) < 1e-6) { - weston_log("warning: numerical instability in " - "%s(), divisor = %g\n", __func__, - v.f[3]); - *x = 0; - *y = 0; - return; - } - - *x = v.f[0] / v.f[3]; - *y = v.f[1] / v.f[3]; - } else { - *x = sx + view->geometry.x; - *y = sy + view->geometry.y; - } -} - -WL_EXPORT void -weston_transformed_coord(int width, int height, - enum wl_output_transform transform, - int32_t scale, - float sx, float sy, float *bx, float *by) -{ - switch (transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - default: - *bx = sx; - *by = sy; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED: - *bx = width - sx; - *by = sy; - break; - case WL_OUTPUT_TRANSFORM_90: - *bx = height - sy; - *by = sx; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - *bx = height - sy; - *by = width - sx; - break; - case WL_OUTPUT_TRANSFORM_180: - *bx = width - sx; - *by = height - sy; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - *bx = sx; - *by = height - sy; - break; - case WL_OUTPUT_TRANSFORM_270: - *bx = sy; - *by = width - sx; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - *bx = sy; - *by = sx; - break; - } - - *bx *= scale; - *by *= scale; -} - -WL_EXPORT pixman_box32_t -weston_transformed_rect(int width, int height, - enum wl_output_transform transform, - int32_t scale, - pixman_box32_t rect) -{ - float x1, x2, y1, y2; - - pixman_box32_t ret; - - weston_transformed_coord(width, height, transform, scale, - rect.x1, rect.y1, &x1, &y1); - weston_transformed_coord(width, height, transform, scale, - rect.x2, rect.y2, &x2, &y2); - - if (x1 <= x2) { - ret.x1 = x1; - ret.x2 = x2; - } else { - ret.x1 = x2; - ret.x2 = x1; - } - - if (y1 <= y2) { - ret.y1 = y1; - ret.y2 = y2; - } else { - ret.y1 = y2; - ret.y2 = y1; - } - - return ret; -} - -/** Transform a region by a matrix, restricted to axis-aligned transformations - * - * Warning: This function does not work for projective, affine, or matrices - * that encode arbitrary rotations. Only 90-degree step rotations are - * supported. - */ -WL_EXPORT void -weston_matrix_transform_region(pixman_region32_t *dest, - struct weston_matrix *matrix, - pixman_region32_t *src) -{ - pixman_box32_t *src_rects, *dest_rects; - int nrects, i; - - src_rects = pixman_region32_rectangles(src, &nrects); - dest_rects = malloc(nrects * sizeof(*dest_rects)); - if (!dest_rects) - return; - - for (i = 0; i < nrects; i++) { - struct weston_vector vec1 = {{ - src_rects[i].x1, src_rects[i].y1, 0, 1 - }}; - weston_matrix_transform(matrix, &vec1); - vec1.f[0] /= vec1.f[3]; - vec1.f[1] /= vec1.f[3]; - - struct weston_vector vec2 = {{ - src_rects[i].x2, src_rects[i].y2, 0, 1 - }}; - weston_matrix_transform(matrix, &vec2); - vec2.f[0] /= vec2.f[3]; - vec2.f[1] /= vec2.f[3]; - - if (vec1.f[0] < vec2.f[0]) { - dest_rects[i].x1 = floor(vec1.f[0]); - dest_rects[i].x2 = ceil(vec2.f[0]); - } else { - dest_rects[i].x1 = floor(vec2.f[0]); - dest_rects[i].x2 = ceil(vec1.f[0]); - } - - if (vec1.f[1] < vec2.f[1]) { - dest_rects[i].y1 = floor(vec1.f[1]); - dest_rects[i].y2 = ceil(vec2.f[1]); - } else { - dest_rects[i].y1 = floor(vec2.f[1]); - dest_rects[i].y2 = ceil(vec1.f[1]); - } - } - - pixman_region32_clear(dest); - pixman_region32_init_rects(dest, dest_rects, nrects); - free(dest_rects); -} - -WL_EXPORT void -weston_transformed_region(int width, int height, - enum wl_output_transform transform, - int32_t scale, - pixman_region32_t *src, pixman_region32_t *dest) -{ - pixman_box32_t *src_rects, *dest_rects; - int nrects, i; - - if (transform == WL_OUTPUT_TRANSFORM_NORMAL && scale == 1) { - if (src != dest) - pixman_region32_copy(dest, src); - return; - } - - src_rects = pixman_region32_rectangles(src, &nrects); - dest_rects = malloc(nrects * sizeof(*dest_rects)); - if (!dest_rects) - return; - - if (transform == WL_OUTPUT_TRANSFORM_NORMAL) { - memcpy(dest_rects, src_rects, nrects * sizeof(*dest_rects)); - } else { - for (i = 0; i < nrects; i++) { - switch (transform) { - default: - case WL_OUTPUT_TRANSFORM_NORMAL: - dest_rects[i].x1 = src_rects[i].x1; - dest_rects[i].y1 = src_rects[i].y1; - dest_rects[i].x2 = src_rects[i].x2; - dest_rects[i].y2 = src_rects[i].y2; - break; - case WL_OUTPUT_TRANSFORM_90: - dest_rects[i].x1 = height - src_rects[i].y2; - dest_rects[i].y1 = src_rects[i].x1; - dest_rects[i].x2 = height - src_rects[i].y1; - dest_rects[i].y2 = src_rects[i].x2; - break; - case WL_OUTPUT_TRANSFORM_180: - dest_rects[i].x1 = width - src_rects[i].x2; - dest_rects[i].y1 = height - src_rects[i].y2; - dest_rects[i].x2 = width - src_rects[i].x1; - dest_rects[i].y2 = height - src_rects[i].y1; - break; - case WL_OUTPUT_TRANSFORM_270: - dest_rects[i].x1 = src_rects[i].y1; - dest_rects[i].y1 = width - src_rects[i].x2; - dest_rects[i].x2 = src_rects[i].y2; - dest_rects[i].y2 = width - src_rects[i].x1; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED: - dest_rects[i].x1 = width - src_rects[i].x2; - dest_rects[i].y1 = src_rects[i].y1; - dest_rects[i].x2 = width - src_rects[i].x1; - dest_rects[i].y2 = src_rects[i].y2; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - dest_rects[i].x1 = height - src_rects[i].y2; - dest_rects[i].y1 = width - src_rects[i].x2; - dest_rects[i].x2 = height - src_rects[i].y1; - dest_rects[i].y2 = width - src_rects[i].x1; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - dest_rects[i].x1 = src_rects[i].x1; - dest_rects[i].y1 = height - src_rects[i].y2; - dest_rects[i].x2 = src_rects[i].x2; - dest_rects[i].y2 = height - src_rects[i].y1; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - dest_rects[i].x1 = src_rects[i].y1; - dest_rects[i].y1 = src_rects[i].x1; - dest_rects[i].x2 = src_rects[i].y2; - dest_rects[i].y2 = src_rects[i].x2; - break; - } - } - } - - if (scale != 1) { - for (i = 0; i < nrects; i++) { - dest_rects[i].x1 *= scale; - dest_rects[i].x2 *= scale; - dest_rects[i].y1 *= scale; - dest_rects[i].y2 *= scale; - } - } - - pixman_region32_clear(dest); - pixman_region32_init_rects(dest, dest_rects, nrects); - free(dest_rects); -} - -static void -viewport_surface_to_buffer(struct weston_surface *surface, - float sx, float sy, float *bx, float *by) -{ - struct weston_buffer_viewport *vp = &surface->buffer_viewport; - double src_width, src_height; - double src_x, src_y; - - if (vp->buffer.src_width == wl_fixed_from_int(-1)) { - if (vp->surface.width == -1) { - *bx = sx; - *by = sy; - return; - } - - src_x = 0.0; - src_y = 0.0; - src_width = surface->width_from_buffer; - src_height = surface->height_from_buffer; - } else { - src_x = wl_fixed_to_double(vp->buffer.src_x); - src_y = wl_fixed_to_double(vp->buffer.src_y); - src_width = wl_fixed_to_double(vp->buffer.src_width); - src_height = wl_fixed_to_double(vp->buffer.src_height); - } - - *bx = sx * src_width / surface->width + src_x; - *by = sy * src_height / surface->height + src_y; -} - -WL_EXPORT void -weston_surface_to_buffer_float(struct weston_surface *surface, - float sx, float sy, float *bx, float *by) -{ - struct weston_buffer_viewport *vp = &surface->buffer_viewport; - - /* first transform coordinates if the viewport is set */ - viewport_surface_to_buffer(surface, sx, sy, bx, by); - - weston_transformed_coord(surface->width_from_buffer, - surface->height_from_buffer, - vp->buffer.transform, vp->buffer.scale, - *bx, *by, bx, by); -} - -/** Transform a rectangle from surface coordinates to buffer coordinates - * - * \param surface The surface to fetch wp_viewport and buffer transformation - * from. - * \param rect The rectangle to transform. - * \return The transformed rectangle. - * - * Viewport and buffer transformations can only do translation, scaling, - * and rotations in 90-degree steps. Therefore the only loss in the - * conversion is coordinate rounding. - * - * However, some coordinate rounding takes place as an intermediate - * step before the buffer scale factor is applied, so the rectangle - * boundary may not be exactly as expected. - * - * This is OK for damage tracking since a little extra coverage is - * not a problem. - */ -WL_EXPORT pixman_box32_t -weston_surface_to_buffer_rect(struct weston_surface *surface, - pixman_box32_t rect) -{ - struct weston_buffer_viewport *vp = &surface->buffer_viewport; - float xf, yf; - - /* first transform box coordinates if the viewport is set */ - viewport_surface_to_buffer(surface, rect.x1, rect.y1, &xf, &yf); - rect.x1 = floorf(xf); - rect.y1 = floorf(yf); - - viewport_surface_to_buffer(surface, rect.x2, rect.y2, &xf, &yf); - rect.x2 = ceilf(xf); - rect.y2 = ceilf(yf); - - return weston_transformed_rect(surface->width_from_buffer, - surface->height_from_buffer, - vp->buffer.transform, vp->buffer.scale, - rect); -} - -/** Transform a region from surface coordinates to buffer coordinates - * - * \param surface The surface to fetch wp_viewport and buffer transformation - * from. - * \param[in] surface_region The region in surface coordinates. - * \param[out] buffer_region The region converted to buffer coordinates. - * - * Buffer_region must be init'd, but will be completely overwritten. - * - * Viewport and buffer transformations can only do translation, scaling, - * and rotations in 90-degree steps. Therefore the only loss in the - * conversion is from the coordinate rounding that takes place in - * \ref weston_surface_to_buffer_rect. - * - */ -WL_EXPORT void -weston_surface_to_buffer_region(struct weston_surface *surface, - pixman_region32_t *surface_region, - pixman_region32_t *buffer_region) -{ - pixman_box32_t *src_rects, *dest_rects; - int nrects, i; - - src_rects = pixman_region32_rectangles(surface_region, &nrects); - dest_rects = malloc(nrects * sizeof(*dest_rects)); - if (!dest_rects) - return; - - for (i = 0; i < nrects; i++) { - dest_rects[i] = weston_surface_to_buffer_rect(surface, - src_rects[i]); - } - - pixman_region32_fini(buffer_region); - pixman_region32_init_rects(buffer_region, dest_rects, nrects); - free(dest_rects); -} - -WL_EXPORT void -weston_view_move_to_plane(struct weston_view *view, - struct weston_plane *plane) -{ - if (view->plane == plane) - return; - - weston_view_damage_below(view); - view->plane = plane; - weston_surface_damage(view->surface); -} - -/** Inflict damage on the plane where the view is visible. - * - * \param view The view that causes the damage. - * - * If the view is currently on a plane (including the primary plane), - * take the view's boundingbox, subtract all the opaque views that cover it, - * and add the remaining region as damage to the plane. This corresponds - * to the damage inflicted to the plane if this view disappeared. - * - * A repaint is scheduled for this view. - * - * The region of all opaque views covering this view is stored in - * weston_view::clip and updated by view_accumulate_damage() during - * weston_output_repaint(). Specifically, that region matches the - * scenegraph as it was last painted. - */ -WL_EXPORT void -weston_view_damage_below(struct weston_view *view) -{ - pixman_region32_t damage; - - pixman_region32_init(&damage); - pixman_region32_subtract(&damage, &view->transform.boundingbox, - &view->clip); - if (view->plane) - pixman_region32_union(&view->plane->damage, - &view->plane->damage, &damage); - pixman_region32_fini(&damage); - weston_view_schedule_repaint(view); -} - -/** Send wl_surface.enter/leave events - * - * \param surface The surface. - * \param head A head of the entered/left output. - * \param enter True if entered. - * \param leave True if left. - * - * Send the enter/leave events for all protocol objects bound to the given - * output by the client owning the surface. - */ -static void -weston_surface_send_enter_leave(struct weston_surface *surface, - struct weston_head *head, - bool enter, - bool leave) -{ - struct wl_resource *wloutput; - struct wl_client *client; - - assert(enter != leave); - - client = wl_resource_get_client(surface->resource); - wl_resource_for_each(wloutput, &head->resource_list) { - if (wl_resource_get_client(wloutput) != client) - continue; - - if (enter) - wl_surface_send_enter(surface->resource, wloutput); - if (leave) - wl_surface_send_leave(surface->resource, wloutput); - } -} - -static void -weston_surface_compute_protection(struct protected_surface *psurface) -{ - enum weston_hdcp_protection min_protection; - bool min_protection_valid = false; - struct weston_surface *surface = psurface->surface; - struct weston_output *output; - - wl_list_for_each(output, &surface->compositor->output_list, link) - if (surface->output_mask & (1u << output->id)) { - /* - * If the content-protection is enabled with protection - * mode as RELAXED for a surface, and if - * content-recording features like: screen-shooter, - * recorder, screen-sharing, etc are on, then notify the - * client, that the protection is disabled. - * - * Note: If the protection mode is ENFORCED then there - * is no need to bother the client as the renderer takes - * care of censoring the visibility of the protected - * content. - */ - - if (output->disable_planes > 0 && - surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_RELAXED) { - min_protection = WESTON_HDCP_DISABLE; - min_protection_valid = true; - break; - } - if (!min_protection_valid) { - min_protection = output->current_protection; - min_protection_valid = true; - } - if (output->current_protection < min_protection) - min_protection = output->current_protection; - } - if (!min_protection_valid) - min_protection = WESTON_HDCP_DISABLE; - - surface->current_protection = min_protection; - - weston_protected_surface_send_event(psurface, surface->current_protection); -} - -static void -notify_surface_protection_change(void *data) -{ - struct weston_compositor *compositor = data; - struct content_protection *cp; - struct protected_surface *psurface; - - cp = compositor->content_protection; - cp->surface_protection_update = NULL; - - /* Notify the clients, whose surfaces are changed */ - wl_list_for_each(psurface, &cp->protected_list, link) - if (psurface && psurface->surface) - weston_surface_compute_protection(psurface); -} - -/** - * \param compositor weston_compositor - * - * Schedule an idle task to notify surface about the update in protection, - * if not already scheduled. - */ -static void -weston_schedule_surface_protection_update(struct weston_compositor *compositor) -{ - struct content_protection *cp = compositor->content_protection; - struct wl_event_loop *loop; - - if (!cp || cp->surface_protection_update) - return; - loop = wl_display_get_event_loop(compositor->wl_display); - cp->surface_protection_update = wl_event_loop_add_idle(loop, - notify_surface_protection_change, - compositor); -} - -/** - * \param es The surface - * \param mask The new set of outputs for the surface - * - * Sets the surface's set of outputs to the ones specified by - * the new output mask provided. Identifies the outputs that - * have changed, the posts enter and leave events for these - * outputs as appropriate. - */ -static void -weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask) -{ - uint32_t different = es->output_mask ^ mask; - uint32_t entered = mask & different; - uint32_t left = es->output_mask & different; - uint32_t output_bit; - struct weston_output *output; - struct weston_head *head; - - es->output_mask = mask; - if (es->resource == NULL) - return; - if (different == 0) - return; - - wl_list_for_each(output, &es->compositor->output_list, link) { - output_bit = 1u << output->id; - if (!(output_bit & different)) - continue; - - wl_list_for_each(head, &output->head_list, output_link) { - weston_surface_send_enter_leave(es, head, - output_bit & entered, - output_bit & left); - } - } - /* - * Change in surfaces' output mask might trigger a change in its - * protection. - */ - weston_schedule_surface_protection_update(es->compositor); -} - -static void -notify_view_output_destroy(struct wl_listener *listener, void *data) -{ - struct weston_view *view = - container_of(listener, - struct weston_view, output_destroy_listener); - - view->output = NULL; - view->output_destroy_listener.notify = NULL; -} - -/** Set the primary output of the view - * - * \param view The view whose primary output to set - * \param output The new primary output for the view - * - * Set \a output to be the primary output of the \a view. - * - * Notice that the assignment may be temporary; the primary output could be - * automatically changed. Hence, one cannot rely on the value persisting. - * - * Passing NULL as /a output will set the primary output to NULL. - */ -WL_EXPORT void -weston_view_set_output(struct weston_view *view, struct weston_output *output) -{ - if (view->output_destroy_listener.notify) { - wl_list_remove(&view->output_destroy_listener.link); - view->output_destroy_listener.notify = NULL; - } - view->output = output; - if (output) { - view->output_destroy_listener.notify = - notify_view_output_destroy; - wl_signal_add(&output->destroy_signal, - &view->output_destroy_listener); - } -} - -/** Recalculate which output(s) the surface has views displayed on - * - * \param es The surface to remap to outputs - * - * Finds the output that is showing the largest amount of one - * of the surface's various views. This output becomes the - * surface's primary output for vsync and frame callback purposes. - * - * Also notes all outputs of all of the surface's views - * in the output_mask for the surface. - */ -static void -weston_surface_assign_output(struct weston_surface *es) -{ - struct weston_output *new_output; - struct weston_view *view; - pixman_region32_t region; - uint32_t max, area, mask; - pixman_box32_t *e; - - new_output = NULL; - max = 0; - mask = 0; - pixman_region32_init(®ion); - wl_list_for_each(view, &es->views, surface_link) { - if (!view->output) - continue; - - pixman_region32_intersect(®ion, &view->transform.boundingbox, - &view->output->region); - - e = pixman_region32_extents(®ion); - area = (e->x2 - e->x1) * (e->y2 - e->y1); - - mask |= view->output_mask; - - if (area >= max) { - new_output = view->output; - max = area; - } - } - pixman_region32_fini(®ion); - - es->output = new_output; - weston_surface_update_output_mask(es, mask); -} - -/** Recalculate which output(s) the view is displayed on - * - * \param ev The view to remap to outputs - * - * Identifies the set of outputs that the view is visible on, - * noting them into the output_mask. The output that the view - * is most visible on is set as the view's primary output. - * - * Also does the same for the view's surface. See - * weston_surface_assign_output(). - */ -static void -weston_view_assign_output(struct weston_view *ev) -{ - struct weston_compositor *ec = ev->surface->compositor; - struct weston_output *output, *new_output; - pixman_region32_t region; - uint32_t max, area, mask; - pixman_box32_t *e; - - new_output = NULL; - max = 0; - mask = 0; - pixman_region32_init(®ion); - wl_list_for_each(output, &ec->output_list, link) { - if (output->destroying) - continue; - - pixman_region32_intersect(®ion, &ev->transform.boundingbox, - &output->region); - - e = pixman_region32_extents(®ion); - area = (e->x2 - e->x1) * (e->y2 - e->y1); - - if (area > 0) - mask |= 1u << output->id; - - if (area >= max) { - new_output = output; - max = area; - } - } - pixman_region32_fini(®ion); - - weston_view_set_output(ev, new_output); - ev->output_mask = mask; - - weston_surface_assign_output(ev->surface); -} - -static void -weston_view_to_view_map(struct weston_view *from, struct weston_view *to, - int from_x, int from_y, int *to_x, int *to_y) -{ - float x, y; - - weston_view_to_global_float(from, from_x, from_y, &x, &y); - weston_view_from_global_float(to, x, y, &x, &y); - - *to_x = round(x); - *to_y = round(y); -} - -static void -weston_view_transfer_scissor(struct weston_view *from, struct weston_view *to) -{ - pixman_box32_t *a; - pixman_box32_t b; - - a = pixman_region32_extents(&from->geometry.scissor); - - weston_view_to_view_map(from, to, a->x1, a->y1, &b.x1, &b.y1); - weston_view_to_view_map(from, to, a->x2, a->y2, &b.x2, &b.y2); - - pixman_region32_fini(&to->geometry.scissor); - pixman_region32_init_with_extents(&to->geometry.scissor, &b); -} - -static void -view_compute_bbox(struct weston_view *view, const pixman_box32_t *inbox, - pixman_region32_t *bbox) -{ - float min_x = HUGE_VALF, min_y = HUGE_VALF; - float max_x = -HUGE_VALF, max_y = -HUGE_VALF; - int32_t s[4][2] = { - { inbox->x1, inbox->y1 }, - { inbox->x1, inbox->y2 }, - { inbox->x2, inbox->y1 }, - { inbox->x2, inbox->y2 }, - }; - float int_x, int_y; - int i; - - if (inbox->x1 == inbox->x2 || inbox->y1 == inbox->y2) { - /* avoid rounding empty bbox to 1x1 */ - pixman_region32_init(bbox); - return; - } - - for (i = 0; i < 4; ++i) { - float x, y; - weston_view_to_global_float(view, s[i][0], s[i][1], &x, &y); - if (x < min_x) - min_x = x; - if (x > max_x) - max_x = x; - if (y < min_y) - min_y = y; - if (y > max_y) - max_y = y; - } - - int_x = floorf(min_x); - int_y = floorf(min_y); - pixman_region32_init_rect(bbox, int_x, int_y, - ceilf(max_x) - int_x, ceilf(max_y) - int_y); -} - -static void -weston_view_update_transform_disable(struct weston_view *view) -{ - view->transform.enabled = 0; - - /* round off fractions when not transformed */ - view->geometry.x = roundf(view->geometry.x); - view->geometry.y = roundf(view->geometry.y); - - /* Otherwise identity matrix, but with x and y translation. */ - view->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE; - view->transform.position.matrix.d[12] = view->geometry.x; - view->transform.position.matrix.d[13] = view->geometry.y; - - view->transform.matrix = view->transform.position.matrix; - - view->transform.inverse = view->transform.position.matrix; - view->transform.inverse.d[12] = -view->geometry.x; - view->transform.inverse.d[13] = -view->geometry.y; - - pixman_region32_init_rect(&view->transform.boundingbox, - 0, 0, - view->surface->width, - view->surface->height); - if (view->geometry.scissor_enabled) - pixman_region32_intersect(&view->transform.boundingbox, - &view->transform.boundingbox, - &view->geometry.scissor); - - pixman_region32_translate(&view->transform.boundingbox, - view->geometry.x, view->geometry.y); - - if (view->alpha == 1.0) { - pixman_region32_copy(&view->transform.opaque, - &view->surface->opaque); - pixman_region32_translate(&view->transform.opaque, - view->geometry.x, - view->geometry.y); - } -} - -static int -weston_view_update_transform_enable(struct weston_view *view) -{ - struct weston_view *parent = view->geometry.parent; - struct weston_matrix *matrix = &view->transform.matrix; - struct weston_matrix *inverse = &view->transform.inverse; - struct weston_transform *tform; - pixman_region32_t surfregion; - const pixman_box32_t *surfbox; - - view->transform.enabled = 1; - - /* Otherwise identity matrix, but with x and y translation. */ - view->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE; - view->transform.position.matrix.d[12] = view->geometry.x; - view->transform.position.matrix.d[13] = view->geometry.y; - - weston_matrix_init(matrix); - wl_list_for_each(tform, &view->geometry.transformation_list, link) - weston_matrix_multiply(matrix, &tform->matrix); - - if (parent) - weston_matrix_multiply(matrix, &parent->transform.matrix); - - if (weston_matrix_invert(inverse, matrix) < 0) { - /* Oops, bad total transformation, not invertible */ - weston_log("error: weston_view %p" - " transformation not invertible.\n", view); - return -1; - } - - if (view->alpha == 1.0 && - matrix->type == WESTON_MATRIX_TRANSFORM_TRANSLATE) { - pixman_region32_copy(&view->transform.opaque, - &view->surface->opaque); - pixman_region32_translate(&view->transform.opaque, - matrix->d[12], - matrix->d[13]); - } - - pixman_region32_init_rect(&surfregion, 0, 0, - view->surface->width, view->surface->height); - if (view->geometry.scissor_enabled) - pixman_region32_intersect(&surfregion, &surfregion, - &view->geometry.scissor); - surfbox = pixman_region32_extents(&surfregion); - - view_compute_bbox(view, surfbox, &view->transform.boundingbox); - pixman_region32_fini(&surfregion); - - return 0; -} - -static struct weston_layer * -get_view_layer(struct weston_view *view) -{ - if (view->parent_view) - return get_view_layer(view->parent_view); - return view->layer_link.layer; -} - -WL_EXPORT void -weston_view_update_transform(struct weston_view *view) -{ - struct weston_view *parent = view->geometry.parent; - struct weston_layer *layer; - pixman_region32_t mask; - - if (!view->transform.dirty) - return; - - if (parent) - weston_view_update_transform(parent); - - view->transform.dirty = 0; - - weston_view_damage_below(view); - - pixman_region32_fini(&view->transform.boundingbox); - pixman_region32_fini(&view->transform.opaque); - pixman_region32_init(&view->transform.opaque); - - /* transform.position is always in transformation_list */ - if (view->geometry.transformation_list.next == - &view->transform.position.link && - view->geometry.transformation_list.prev == - &view->transform.position.link && - !parent) { - weston_view_update_transform_disable(view); - } else { - if (weston_view_update_transform_enable(view) < 0) - weston_view_update_transform_disable(view); - } - - layer = get_view_layer(view); - if (layer) { - pixman_region32_init_with_extents(&mask, &layer->mask); - pixman_region32_intersect(&view->transform.boundingbox, - &view->transform.boundingbox, &mask); - pixman_region32_intersect(&view->transform.opaque, - &view->transform.opaque, &mask); - pixman_region32_fini(&mask); - } - - if (parent) { - if (parent->geometry.scissor_enabled) { - view->geometry.scissor_enabled = true; - weston_view_transfer_scissor(parent, view); - } else { - view->geometry.scissor_enabled = false; - } - } - - weston_view_damage_below(view); - - weston_view_assign_output(view); - - wl_signal_emit(&view->surface->compositor->transform_signal, - view->surface); -} - -WL_EXPORT void -weston_view_geometry_dirty(struct weston_view *view) -{ - struct weston_view *child; - - /* - * The invariant: if view->geometry.dirty, then all views - * in view->geometry.child_list have geometry.dirty too. - * Corollary: if not parent->geometry.dirty, then all ancestors - * are not dirty. - */ - - if (view->transform.dirty) - return; - - view->transform.dirty = 1; - - wl_list_for_each(child, &view->geometry.child_list, - geometry.parent_link) - weston_view_geometry_dirty(child); -} - -WL_EXPORT void -weston_view_to_global_fixed(struct weston_view *view, - wl_fixed_t vx, wl_fixed_t vy, - wl_fixed_t *x, wl_fixed_t *y) -{ - float xf, yf; - - weston_view_to_global_float(view, - wl_fixed_to_double(vx), - wl_fixed_to_double(vy), - &xf, &yf); - *x = wl_fixed_from_double(xf); - *y = wl_fixed_from_double(yf); -} - -WL_EXPORT void -weston_view_from_global_float(struct weston_view *view, - float x, float y, float *vx, float *vy) -{ - if (view->transform.enabled) { - struct weston_vector v = { { x, y, 0.0f, 1.0f } }; - - weston_matrix_transform(&view->transform.inverse, &v); - - if (fabsf(v.f[3]) < 1e-6) { - weston_log("warning: numerical instability in " - "weston_view_from_global(), divisor = %g\n", - v.f[3]); - *vx = 0; - *vy = 0; - return; - } - - *vx = v.f[0] / v.f[3]; - *vy = v.f[1] / v.f[3]; - } else { - *vx = x - view->geometry.x; - *vy = y - view->geometry.y; - } -} - -WL_EXPORT void -weston_view_from_global_fixed(struct weston_view *view, - wl_fixed_t x, wl_fixed_t y, - wl_fixed_t *vx, wl_fixed_t *vy) -{ - float vxf, vyf; - - weston_view_from_global_float(view, - wl_fixed_to_double(x), - wl_fixed_to_double(y), - &vxf, &vyf); - *vx = wl_fixed_from_double(vxf); - *vy = wl_fixed_from_double(vyf); -} - -WL_EXPORT void -weston_view_from_global(struct weston_view *view, - int32_t x, int32_t y, int32_t *vx, int32_t *vy) -{ - float vxf, vyf; - - weston_view_from_global_float(view, x, y, &vxf, &vyf); - *vx = floorf(vxf); - *vy = floorf(vyf); -} - -/** - * \param surface The surface to be repainted - * - * Marks the output(s) that the surface is shown on as needing to be - * repainted. See weston_output_schedule_repaint(). - */ -WL_EXPORT void -weston_surface_schedule_repaint(struct weston_surface *surface) -{ - struct weston_output *output; - - wl_list_for_each(output, &surface->compositor->output_list, link) - if (surface->output_mask & (1u << output->id)) - weston_output_schedule_repaint(output); -} - -/** - * \param view The view to be repainted - * - * Marks the output(s) that the view is shown on as needing to be - * repainted. See weston_output_schedule_repaint(). - */ -WL_EXPORT void -weston_view_schedule_repaint(struct weston_view *view) -{ - struct weston_output *output; - - wl_list_for_each(output, &view->surface->compositor->output_list, link) - if (view->output_mask & (1u << output->id)) - weston_output_schedule_repaint(output); -} - -/** - * XXX: This function does it the wrong way. - * surface->damage is the damage from the client, and causes - * surface_flush_damage() to copy pixels. No window management action can - * cause damage to the client-provided content, warranting re-upload! - * - * Instead of surface->damage, this function should record the damage - * with all the views for this surface to avoid extraneous texture - * uploads. - */ -WL_EXPORT void -weston_surface_damage(struct weston_surface *surface) -{ - pixman_region32_union_rect(&surface->damage, &surface->damage, - 0, 0, surface->width, - surface->height); - - weston_surface_schedule_repaint(surface); -} - -WL_EXPORT void -weston_view_set_position(struct weston_view *view, float x, float y) -{ - if (view->geometry.x == x && view->geometry.y == y) - return; - - view->geometry.x = x; - view->geometry.y = y; - weston_view_geometry_dirty(view); -} - -static void -transform_parent_handle_parent_destroy(struct wl_listener *listener, - void *data) -{ - struct weston_view *view = - container_of(listener, struct weston_view, - geometry.parent_destroy_listener); - - weston_view_set_transform_parent(view, NULL); -} - -WL_EXPORT void -weston_view_set_transform_parent(struct weston_view *view, - struct weston_view *parent) -{ - if (view->geometry.parent) { - wl_list_remove(&view->geometry.parent_destroy_listener.link); - wl_list_remove(&view->geometry.parent_link); - - if (!parent) - view->geometry.scissor_enabled = false; - } - - view->geometry.parent = parent; - - view->geometry.parent_destroy_listener.notify = - transform_parent_handle_parent_destroy; - if (parent) { - wl_signal_add(&parent->destroy_signal, - &view->geometry.parent_destroy_listener); - wl_list_insert(&parent->geometry.child_list, - &view->geometry.parent_link); - } - - weston_view_geometry_dirty(view); -} - -/** Set a clip mask rectangle on a view - * - * \param view The view to set the clip mask on. - * \param x Top-left corner X coordinate of the clip rectangle. - * \param y Top-left corner Y coordinate of the clip rectangle. - * \param width Width of the clip rectangle, non-negative. - * \param height Height of the clip rectangle, non-negative. - * - * A shell may set a clip mask rectangle on a view. Everything outside - * the rectangle is cut away for input and output purposes: it is - * not drawn and cannot be hit by hit-test based input like pointer - * motion or touch-downs. Everything inside the rectangle will behave - * normally. Clients are unaware of clipping. - * - * The rectangle is set in surface-local coordinates. Setting a clip - * mask rectangle does not affect the view position, the view is positioned - * as it would be without a clip. The clip also does not change - * weston_surface::width,height. - * - * The clip mask rectangle is part of transformation inheritance - * (weston_view_set_transform_parent()). A clip set in the root of the - * transformation inheritance tree will affect all views in the tree. - * A clip can be set only on the root view. Attempting to set a clip - * on view that has a transformation parent will fail. Assigning a parent - * to a view that has a clip set will cause the clip to be forgotten. - * - * Because the clip mask is an axis-aligned rectangle, it poses restrictions - * on the additional transformations in the child views. These transformations - * may not rotate the coordinate axes, i.e., only translation and scaling - * are allowed. Violating this restriction causes the clipping to malfunction. - * Furthermore, using scaling may cause rounding errors in child clipping. - * - * The clip mask rectangle is not automatically adjusted based on - * wl_surface.attach dx and dy arguments. - * - * A clip mask rectangle can be set only if the compositor capability - * WESTON_CAP_VIEW_CLIP_MASK is present. - * - * This function sets the clip mask rectangle and schedules a repaint for - * the view. - */ -WL_EXPORT void -weston_view_set_mask(struct weston_view *view, - int x, int y, int width, int height) -{ - struct weston_compositor *compositor = view->surface->compositor; - - if (!(compositor->capabilities & WESTON_CAP_VIEW_CLIP_MASK)) { - weston_log("%s not allowed without capability!\n", __func__); - return; - } - - if (view->geometry.parent) { - weston_log("view %p has a parent, clip forbidden!\n", view); - return; - } - - if (width < 0 || height < 0) { - weston_log("%s: illegal args %d, %d, %d, %d\n", __func__, - x, y, width, height); - return; - } - - pixman_region32_fini(&view->geometry.scissor); - pixman_region32_init_rect(&view->geometry.scissor, x, y, width, height); - view->geometry.scissor_enabled = true; - weston_view_geometry_dirty(view); - weston_view_schedule_repaint(view); -} - -/** Remove the clip mask from a view - * - * \param view The view to remove the clip mask from. - * - * Removed the clip mask rectangle and schedules a repaint. - * - * \sa weston_view_set_mask - */ -WL_EXPORT void -weston_view_set_mask_infinite(struct weston_view *view) -{ - view->geometry.scissor_enabled = false; - weston_view_geometry_dirty(view); - weston_view_schedule_repaint(view); -} - -/* Check if view should be displayed - * - * The indicator is set manually when assigning - * a view to a surface. - * - * This needs reworking. See the thread starting at: - * - * https://lists.freedesktop.org/archives/wayland-devel/2016-June/029656.html - */ -WL_EXPORT bool -weston_view_is_mapped(struct weston_view *view) -{ - return view->is_mapped; -} - -/* Check if view is opaque in specified region - * - * \param view The view to check for opacity. - * \param region The region to check for opacity, in view coordinates. - * - * Returns true if the view is opaque in the specified region, because view - * alpha is 1.0 and either the opaque region set by the client contains the - * specified region, or the buffer pixel format or solid color is opaque. - */ -WL_EXPORT bool -weston_view_is_opaque(struct weston_view *ev, pixman_region32_t *region) -{ - pixman_region32_t r; - bool ret = false; - - if (ev->alpha < 1.0) - return false; - - if (ev->surface->is_opaque) - return true; - - if (ev->transform.dirty) { - weston_log("%s: transform dirty", __func__); - return false; - } - - pixman_region32_init(&r); - pixman_region32_subtract(&r, region, &ev->transform.opaque); - - if (!pixman_region32_not_empty(&r)) - ret = true; - - pixman_region32_fini(&r); - - return ret; -} - -/** Check if the view has a valid buffer available - * - * @param ev The view to check if it has a valid buffer. - * - * Returns true if the view has a valid buffer or false otherwise. - */ -WL_EXPORT bool -weston_view_has_valid_buffer(struct weston_view *ev) -{ - return ev->surface->buffer_ref.buffer != NULL; -} - -/** Check if the view matches the entire output - * - * @param ev The view to check. - * @param output The output to check against. - * - * Returns true if the view does indeed matches the entire output. - */ -WL_EXPORT bool -weston_view_matches_output_entirely(struct weston_view *ev, - struct weston_output *output) -{ - pixman_box32_t *extents = - pixman_region32_extents(&ev->transform.boundingbox); - - if (extents->x1 != output->x || - extents->y1 != output->y || - extents->x2 != output->x + output->width || - extents->y2 != output->y + output->height) - return false; - - return true; -} - -/* Check if a surface has a view assigned to it - * - * The indicator is set manually when mapping - * a surface and creating a view for it. - * - * This needs to go. See the thread starting at: - * - * https://lists.freedesktop.org/archives/wayland-devel/2016-June/029656.html - * - */ -WL_EXPORT bool -weston_surface_is_mapped(struct weston_surface *surface) -{ - return surface->is_mapped; -} - -static void -surface_set_size(struct weston_surface *surface, int32_t width, int32_t height) -{ - struct weston_view *view; - - if (surface->width == width && surface->height == height) - return; - - surface->width = width; - surface->height = height; - - wl_list_for_each(view, &surface->views, surface_link) - weston_view_geometry_dirty(view); -} - -WL_EXPORT void -weston_surface_set_size(struct weston_surface *surface, - int32_t width, int32_t height) -{ - assert(!surface->resource); - surface_set_size(surface, width, height); -} - -static int -fixed_round_up_to_int(wl_fixed_t f) -{ - return wl_fixed_to_int(wl_fixed_from_int(1) - 1 + f); -} - -static void -convert_size_by_transform_scale(int32_t *width_out, int32_t *height_out, - int32_t width, int32_t height, - uint32_t transform, - int32_t scale) -{ - assert(scale > 0); - - switch (transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - case WL_OUTPUT_TRANSFORM_180: - case WL_OUTPUT_TRANSFORM_FLIPPED: - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - *width_out = width / scale; - *height_out = height / scale; - break; - case WL_OUTPUT_TRANSFORM_90: - case WL_OUTPUT_TRANSFORM_270: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - *width_out = height / scale; - *height_out = width / scale; - break; - default: - assert(0 && "invalid transform"); - } -} - -static void -weston_surface_calculate_size_from_buffer(struct weston_surface *surface) -{ - struct weston_buffer_viewport *vp = &surface->buffer_viewport; - - if (!surface->buffer_ref.buffer) { - surface->width_from_buffer = 0; - surface->height_from_buffer = 0; - return; - } - - convert_size_by_transform_scale(&surface->width_from_buffer, - &surface->height_from_buffer, - surface->buffer_ref.buffer->width, - surface->buffer_ref.buffer->height, - vp->buffer.transform, - vp->buffer.scale); -} - -static void -weston_surface_update_size(struct weston_surface *surface) -{ - struct weston_buffer_viewport *vp = &surface->buffer_viewport; - int32_t width, height; - - width = surface->width_from_buffer; - height = surface->height_from_buffer; - - if (width != 0 && vp->surface.width != -1) { - surface_set_size(surface, - vp->surface.width, vp->surface.height); - return; - } - - if (width != 0 && vp->buffer.src_width != wl_fixed_from_int(-1)) { - int32_t w = fixed_round_up_to_int(vp->buffer.src_width); - int32_t h = fixed_round_up_to_int(vp->buffer.src_height); - - surface_set_size(surface, w ?: 1, h ?: 1); - return; - } - - surface_set_size(surface, width, height); -} - -/** weston_compositor_get_time - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_get_time(struct timespec *time) -{ - clock_gettime(CLOCK_REALTIME, time); -} - -/** weston_compositor_pick_view - * \ingroup compositor - */ -WL_EXPORT struct weston_view * -weston_compositor_pick_view(struct weston_compositor *compositor, - wl_fixed_t x, wl_fixed_t y, - wl_fixed_t *vx, wl_fixed_t *vy) -{ - struct weston_view *view; - wl_fixed_t view_x, view_y; - int view_ix, view_iy; - int ix = wl_fixed_to_int(x); - int iy = wl_fixed_to_int(y); - - wl_list_for_each(view, &compositor->view_list, link) { - if (!pixman_region32_contains_point( - &view->transform.boundingbox, ix, iy, NULL)) - continue; - - weston_view_from_global_fixed(view, x, y, &view_x, &view_y); - view_ix = wl_fixed_to_int(view_x); - view_iy = wl_fixed_to_int(view_y); - - if (!pixman_region32_contains_point(&view->surface->input, - view_ix, view_iy, NULL)) - continue; - - if (view->geometry.scissor_enabled && - !pixman_region32_contains_point(&view->geometry.scissor, - view_ix, view_iy, NULL)) - continue; - - *vx = view_x; - *vy = view_y; - return view; - } - - *vx = wl_fixed_from_int(-1000000); - *vy = wl_fixed_from_int(-1000000); - return NULL; -} - -static void -weston_compositor_repick(struct weston_compositor *compositor) -{ - struct weston_seat *seat; - - if (!compositor->session_active) - return; - - wl_list_for_each(seat, &compositor->seat_list, link) - weston_seat_repick(seat); -} - -WL_EXPORT void -weston_view_unmap(struct weston_view *view) -{ - struct weston_seat *seat; - - if (!weston_view_is_mapped(view)) - return; - - weston_view_damage_below(view); - weston_view_set_output(view, NULL); - view->plane = NULL; - view->is_mapped = false; - weston_layer_entry_remove(&view->layer_link); - wl_list_remove(&view->link); - wl_list_init(&view->link); - view->output_mask = 0; - weston_surface_assign_output(view->surface); - - if (weston_surface_is_mapped(view->surface)) - return; - - wl_list_for_each(seat, &view->surface->compositor->seat_list, link) { - struct weston_touch *touch = weston_seat_get_touch(seat); - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - struct weston_keyboard *keyboard = - weston_seat_get_keyboard(seat); - - if (keyboard && keyboard->focus == view->surface) - weston_keyboard_set_focus(keyboard, NULL); - if (pointer && pointer->focus == view) - weston_pointer_clear_focus(pointer); - if (touch && touch->focus == view) - weston_touch_set_focus(touch, NULL); - } -} - -WL_EXPORT void -weston_surface_unmap(struct weston_surface *surface) -{ - struct weston_view *view; - - surface->is_mapped = false; - wl_list_for_each(view, &surface->views, surface_link) - weston_view_unmap(view); - surface->output = NULL; -} - -static void -weston_surface_reset_pending_buffer(struct weston_surface *surface) -{ - weston_surface_state_set_buffer(&surface->pending, NULL); - surface->pending.sx = 0; - surface->pending.sy = 0; - surface->pending.newly_attached = 0; - surface->pending.buffer_viewport.changed = 0; -} - -WL_EXPORT void -weston_view_destroy(struct weston_view *view) -{ - wl_signal_emit(&view->destroy_signal, view); - - assert(wl_list_empty(&view->geometry.child_list)); - - if (weston_view_is_mapped(view)) { - weston_view_unmap(view); - weston_compositor_build_view_list(view->surface->compositor); - } - - wl_list_remove(&view->link); - weston_layer_entry_remove(&view->layer_link); - - pixman_region32_fini(&view->clip); - pixman_region32_fini(&view->geometry.scissor); - pixman_region32_fini(&view->transform.boundingbox); - pixman_region32_fini(&view->transform.opaque); - - weston_view_set_transform_parent(view, NULL); - weston_view_set_output(view, NULL); - - wl_list_remove(&view->surface_link); - - free(view); -} - -WL_EXPORT void -weston_surface_destroy(struct weston_surface *surface) -{ - struct weston_frame_callback *cb, *next; - struct weston_view *ev, *nv; - struct weston_pointer_constraint *constraint, *next_constraint; - - if (--surface->ref_count > 0) - return; - - assert(surface->resource == NULL); - - wl_signal_emit(&surface->destroy_signal, surface); - - assert(wl_list_empty(&surface->subsurface_list_pending)); - assert(wl_list_empty(&surface->subsurface_list)); - - wl_list_for_each_safe(ev, nv, &surface->views, surface_link) - weston_view_destroy(ev); - - weston_surface_state_fini(&surface->pending); - - weston_buffer_reference(&surface->buffer_ref, NULL); - weston_buffer_release_reference(&surface->buffer_release_ref, NULL); - - pixman_region32_fini(&surface->damage); - pixman_region32_fini(&surface->opaque); - pixman_region32_fini(&surface->input); - - wl_list_for_each_safe(cb, next, &surface->frame_callback_list, link) - wl_resource_destroy(cb->resource); - - weston_presentation_feedback_discard_list(&surface->feedback_list); - - wl_list_for_each_safe(constraint, next_constraint, - &surface->pointer_constraints, - link) - weston_pointer_constraint_destroy(constraint); - - fd_clear(&surface->acquire_fence_fd); - - free(surface); -} - -static void -destroy_surface(struct wl_resource *resource) -{ - struct weston_surface *surface = wl_resource_get_user_data(resource); - - assert(surface); - - /* Set the resource to NULL, since we don't want to leave a - * dangling pointer if the surface was refcounted and survives - * the weston_surface_destroy() call. */ - surface->resource = NULL; - - if (surface->viewport_resource) - wl_resource_set_user_data(surface->viewport_resource, NULL); - - if (surface->synchronization_resource) { - wl_resource_set_user_data(surface->synchronization_resource, - NULL); - } - - weston_surface_destroy(surface); -} - -static void -weston_buffer_destroy_handler(struct wl_listener *listener, void *data) -{ - struct weston_buffer *buffer = - container_of(listener, struct weston_buffer, destroy_listener); - - wl_signal_emit(&buffer->destroy_signal, buffer); - free(buffer); -} - -WL_EXPORT struct weston_buffer * -weston_buffer_from_resource(struct wl_resource *resource) -{ - struct weston_buffer *buffer; - struct wl_listener *listener; - - listener = wl_resource_get_destroy_listener(resource, - weston_buffer_destroy_handler); - - if (listener) - return container_of(listener, struct weston_buffer, - destroy_listener); - - buffer = zalloc(sizeof *buffer); - if (buffer == NULL) - return NULL; - - buffer->resource = resource; - wl_signal_init(&buffer->destroy_signal); - buffer->destroy_listener.notify = weston_buffer_destroy_handler; - buffer->y_inverted = 1; - wl_resource_add_destroy_listener(resource, &buffer->destroy_listener); - - return buffer; -} - -static void -weston_buffer_reference_handle_destroy(struct wl_listener *listener, - void *data) -{ - struct weston_buffer_reference *ref = - container_of(listener, struct weston_buffer_reference, - destroy_listener); - - assert((struct weston_buffer *)data == ref->buffer); - ref->buffer = NULL; -} - -WL_EXPORT void -weston_buffer_reference(struct weston_buffer_reference *ref, - struct weston_buffer *buffer) -{ - if (ref->buffer && buffer != ref->buffer) { - ref->buffer->busy_count--; - if (ref->buffer->busy_count == 0) { - assert(wl_resource_get_client(ref->buffer->resource)); - wl_buffer_send_release(ref->buffer->resource); - } - wl_list_remove(&ref->destroy_listener.link); - } - - if (buffer && buffer != ref->buffer) { - buffer->busy_count++; - wl_signal_add(&buffer->destroy_signal, - &ref->destroy_listener); - } - - ref->buffer = buffer; - ref->destroy_listener.notify = weston_buffer_reference_handle_destroy; -} - -static void -weston_buffer_release_reference_handle_destroy(struct wl_listener *listener, - void *data) -{ - struct weston_buffer_release_reference *ref = - container_of(listener, struct weston_buffer_release_reference, - destroy_listener); - - assert((struct wl_resource *)data == ref->buffer_release->resource); - ref->buffer_release = NULL; -} - -static void -weston_buffer_release_destroy(struct weston_buffer_release *buffer_release) -{ - struct wl_resource *resource = buffer_release->resource; - int release_fence_fd = buffer_release->fence_fd; - - if (release_fence_fd >= 0) { - zwp_linux_buffer_release_v1_send_fenced_release( - resource, release_fence_fd); - } else { - zwp_linux_buffer_release_v1_send_immediate_release( - resource); - } - - wl_resource_destroy(resource); -} - -WL_EXPORT void -weston_buffer_release_reference(struct weston_buffer_release_reference *ref, - struct weston_buffer_release *buffer_release) -{ - if (buffer_release == ref->buffer_release) - return; - - if (ref->buffer_release) { - ref->buffer_release->ref_count--; - wl_list_remove(&ref->destroy_listener.link); - if (ref->buffer_release->ref_count == 0) - weston_buffer_release_destroy(ref->buffer_release); - } - - if (buffer_release) { - buffer_release->ref_count++; - wl_resource_add_destroy_listener(buffer_release->resource, - &ref->destroy_listener); - } - - ref->buffer_release = buffer_release; - ref->destroy_listener.notify = - weston_buffer_release_reference_handle_destroy; -} - -WL_EXPORT void -weston_buffer_release_move(struct weston_buffer_release_reference *dest, - struct weston_buffer_release_reference *src) -{ - weston_buffer_release_reference(dest, src->buffer_release); - weston_buffer_release_reference(src, NULL); -} - -static void -weston_surface_attach(struct weston_surface *surface, - struct weston_buffer *buffer) -{ - weston_buffer_reference(&surface->buffer_ref, buffer); - - if (!buffer) { - if (weston_surface_is_mapped(surface)) - weston_surface_unmap(surface); - } - - surface->compositor->renderer->attach(surface, buffer); - - weston_surface_calculate_size_from_buffer(surface); - weston_presentation_feedback_discard_list(&surface->feedback_list); -} - -/** weston_compositor_damage_all - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_damage_all(struct weston_compositor *compositor) -{ - struct weston_output *output; - - wl_list_for_each(output, &compositor->output_list, link) - weston_output_damage(output); -} - -/** - * \ingroup output - */ -WL_EXPORT void -weston_output_damage(struct weston_output *output) -{ - struct weston_compositor *compositor = output->compositor; - - pixman_region32_union(&compositor->primary_plane.damage, - &compositor->primary_plane.damage, - &output->region); - weston_output_schedule_repaint(output); -} - -static void -surface_flush_damage(struct weston_surface *surface) -{ - if (surface->buffer_ref.buffer && - wl_shm_buffer_get(surface->buffer_ref.buffer->resource)) - surface->compositor->renderer->flush_damage(surface); - - if (pixman_region32_not_empty(&surface->damage)) - TL_POINT(surface->compositor, "core_flush_damage", TLP_SURFACE(surface), - TLP_OUTPUT(surface->output), TLP_END); - - pixman_region32_clear(&surface->damage); -} - -static void -view_accumulate_damage(struct weston_view *view, - pixman_region32_t *opaque) -{ - pixman_region32_t damage; - - pixman_region32_init(&damage); - if (view->transform.enabled) { - pixman_box32_t *extents; - - extents = pixman_region32_extents(&view->surface->damage); - view_compute_bbox(view, extents, &damage); - } else { - pixman_region32_copy(&damage, &view->surface->damage); - pixman_region32_translate(&damage, - view->geometry.x, view->geometry.y); - } - - pixman_region32_intersect(&damage, &damage, - &view->transform.boundingbox); - pixman_region32_subtract(&damage, &damage, opaque); - pixman_region32_union(&view->plane->damage, - &view->plane->damage, &damage); - pixman_region32_fini(&damage); - pixman_region32_copy(&view->clip, opaque); - pixman_region32_union(opaque, opaque, &view->transform.opaque); -} - -static void -compositor_accumulate_damage(struct weston_compositor *ec) -{ - struct weston_plane *plane; - struct weston_view *ev; - pixman_region32_t opaque, clip; - - pixman_region32_init(&clip); - - wl_list_for_each(plane, &ec->plane_list, link) { - pixman_region32_copy(&plane->clip, &clip); - - pixman_region32_init(&opaque); - - wl_list_for_each(ev, &ec->view_list, link) { - if (ev->plane != plane) - continue; - - view_accumulate_damage(ev, &opaque); - } - - pixman_region32_union(&clip, &clip, &opaque); - pixman_region32_fini(&opaque); - } - - pixman_region32_fini(&clip); - - wl_list_for_each(ev, &ec->view_list, link) - ev->surface->touched = false; - - wl_list_for_each(ev, &ec->view_list, link) { - if (ev->surface->touched) - continue; - ev->surface->touched = true; - - surface_flush_damage(ev->surface); - - /* Both the renderer and the backend have seen the buffer - * by now. If renderer needs the buffer, it has its own - * reference set. If the backend wants to keep the buffer - * around for migrating the surface into a non-primary plane - * later, keep_buffer is true. Otherwise, drop the core - * reference now, and allow early buffer release. This enables - * clients to use single-buffering. - */ - if (!ev->surface->keep_buffer) { - weston_buffer_reference(&ev->surface->buffer_ref, NULL); - weston_buffer_release_reference( - &ev->surface->buffer_release_ref, NULL); - } - } -} - -static void -surface_stash_subsurface_views(struct weston_surface *surface) -{ - struct weston_subsurface *sub; - - wl_list_for_each(sub, &surface->subsurface_list, parent_link) { - if (sub->surface == surface) - continue; - - wl_list_insert_list(&sub->unused_views, &sub->surface->views); - wl_list_init(&sub->surface->views); - - surface_stash_subsurface_views(sub->surface); - } -} - -static void -surface_free_unused_subsurface_views(struct weston_surface *surface) -{ - struct weston_subsurface *sub; - struct weston_view *view, *nv; - - wl_list_for_each(sub, &surface->subsurface_list, parent_link) { - if (sub->surface == surface) - continue; - - wl_list_for_each_safe(view, nv, &sub->unused_views, surface_link) { - weston_view_unmap (view); - weston_view_destroy(view); - } - - surface_free_unused_subsurface_views(sub->surface); - } -} - -static void -view_list_add_subsurface_view(struct weston_compositor *compositor, - struct weston_subsurface *sub, - struct weston_view *parent) -{ - struct weston_subsurface *child; - struct weston_view *view = NULL, *iv; - - if (!weston_surface_is_mapped(sub->surface)) - return; - - wl_list_for_each(iv, &sub->unused_views, surface_link) { - if (iv->geometry.parent == parent) { - view = iv; - break; - } - } - - if (view) { - /* Put it back in the surface's list of views */ - wl_list_remove(&view->surface_link); - wl_list_insert(&sub->surface->views, &view->surface_link); - } else { - view = weston_view_create(sub->surface); - weston_view_set_position(view, - sub->position.x, - sub->position.y); - weston_view_set_transform_parent(view, parent); - } - - view->parent_view = parent; - weston_view_update_transform(view); - view->is_mapped = true; - - if (wl_list_empty(&sub->surface->subsurface_list)) { - wl_list_insert(compositor->view_list.prev, &view->link); - return; - } - - wl_list_for_each(child, &sub->surface->subsurface_list, parent_link) { - if (child->surface == sub->surface) - wl_list_insert(compositor->view_list.prev, &view->link); - else - view_list_add_subsurface_view(compositor, child, view); - } -} - -/* This recursively adds the sub-surfaces for a view, relying on the - * sub-surface order. Thus, if a client restacks the sub-surfaces, that - * change first happens to the sub-surface list, and then automatically - * propagates here. See weston_surface_damage_subsurfaces() for how the - * sub-surfaces receive damage when the client changes the state. - */ -static void -view_list_add(struct weston_compositor *compositor, - struct weston_view *view) -{ - struct weston_subsurface *sub; - - weston_view_update_transform(view); - - if (wl_list_empty(&view->surface->subsurface_list)) { - wl_list_insert(compositor->view_list.prev, &view->link); - return; - } - - wl_list_for_each(sub, &view->surface->subsurface_list, parent_link) { - if (sub->surface == view->surface) - wl_list_insert(compositor->view_list.prev, &view->link); - else - view_list_add_subsurface_view(compositor, sub, view); - } -} - -static void -weston_compositor_build_view_list(struct weston_compositor *compositor) -{ - struct weston_view *view, *tmp; - struct weston_layer *layer; - - wl_list_for_each(layer, &compositor->layer_list, link) - wl_list_for_each(view, &layer->view_list.link, layer_link.link) - surface_stash_subsurface_views(view->surface); - - wl_list_for_each_safe(view, tmp, &compositor->view_list, link) - wl_list_init(&view->link); - wl_list_init(&compositor->view_list); - - wl_list_for_each(layer, &compositor->layer_list, link) { - wl_list_for_each(view, &layer->view_list.link, layer_link.link) { - view_list_add(compositor, view); - } - } - - wl_list_for_each(layer, &compositor->layer_list, link) - wl_list_for_each(view, &layer->view_list.link, layer_link.link) - surface_free_unused_subsurface_views(view->surface); -} - -static void -weston_output_take_feedback_list(struct weston_output *output, - struct weston_surface *surface) -{ - struct weston_view *view; - struct weston_presentation_feedback *feedback; - uint32_t flags = 0xffffffff; - - if (wl_list_empty(&surface->feedback_list)) - return; - - /* All views must have the flag for the flag to survive. */ - wl_list_for_each(view, &surface->views, surface_link) { - /* ignore views that are not on this output at all */ - if (view->output_mask & (1u << output->id)) - flags &= view->psf_flags; - } - - wl_list_for_each(feedback, &surface->feedback_list, link) - feedback->psf_flags = flags; - - wl_list_insert_list(&output->feedback_list, &surface->feedback_list); - wl_list_init(&surface->feedback_list); -} - -static int -weston_output_repaint(struct weston_output *output, void *repaint_data) -{ - struct weston_compositor *ec = output->compositor; - struct weston_view *ev; - struct weston_animation *animation, *next; - struct weston_frame_callback *cb, *cnext; - struct wl_list frame_callback_list; - pixman_region32_t output_damage; - int r; - uint32_t frame_time_msec; - enum weston_hdcp_protection highest_requested = WESTON_HDCP_DISABLE; - - if (output->destroying) - return 0; - - TL_POINT(ec, "core_repaint_begin", TLP_OUTPUT(output), TLP_END); - - /* Rebuild the surface list and update surface transforms up front. */ - weston_compositor_build_view_list(ec); - - /* Find the highest protection desired for an output */ - wl_list_for_each(ev, &ec->view_list, link) { - if (ev->surface->output_mask & (1u << output->id)) { - /* - * The desired_protection of the output should be the - * maximum of the desired_protection of the surfaces, - * that are displayed on that output, to avoid - * reducing the protection for existing surfaces. - */ - if (ev->surface->desired_protection > highest_requested) - highest_requested = - ev->surface->desired_protection; - } - } - - output->desired_protection = highest_requested; - - if (output->assign_planes && !output->disable_planes) { - output->assign_planes(output, repaint_data); - } else { - wl_list_for_each(ev, &ec->view_list, link) { - weston_view_move_to_plane(ev, &ec->primary_plane); - ev->psf_flags = 0; - } - } - - wl_list_init(&frame_callback_list); - wl_list_for_each(ev, &ec->view_list, link) { - /* Note: This operation is safe to do multiple times on the - * same surface. - */ - if (ev->surface->output == output) { - wl_list_insert_list(&frame_callback_list, - &ev->surface->frame_callback_list); - wl_list_init(&ev->surface->frame_callback_list); - - weston_output_take_feedback_list(output, ev->surface); - } - } - - compositor_accumulate_damage(ec); - - pixman_region32_init(&output_damage); - pixman_region32_intersect(&output_damage, - &ec->primary_plane.damage, &output->region); - pixman_region32_subtract(&output_damage, - &output_damage, &ec->primary_plane.clip); - - if (output->dirty) - weston_output_update_matrix(output); - - r = output->repaint(output, &output_damage, repaint_data); - - pixman_region32_fini(&output_damage); - - output->repaint_needed = false; - if (r == 0) - output->repaint_status = REPAINT_AWAITING_COMPLETION; - - weston_compositor_repick(ec); - - frame_time_msec = timespec_to_msec(&output->frame_time); - - wl_list_for_each_safe(cb, cnext, &frame_callback_list, link) { - wl_callback_send_done(cb->resource, frame_time_msec); - wl_resource_destroy(cb->resource); - } - - wl_list_for_each_safe(animation, next, &output->animation_list, link) { - animation->frame_counter++; - animation->frame(animation, output, &output->frame_time); - } - - TL_POINT(ec, "core_repaint_posted", TLP_OUTPUT(output), TLP_END); - - return r; -} - -static void -weston_output_schedule_repaint_reset(struct weston_output *output) -{ - output->repaint_status = REPAINT_NOT_SCHEDULED; - TL_POINT(output->compositor, "core_repaint_exit_loop", - TLP_OUTPUT(output), TLP_END); -} - -static int -weston_output_maybe_repaint(struct weston_output *output, struct timespec *now, - void *repaint_data) -{ - struct weston_compositor *compositor = output->compositor; - int ret = 0; - int64_t msec_to_repaint; - - /* We're not ready yet; come back to make a decision later. */ - if (output->repaint_status != REPAINT_SCHEDULED) - return ret; - - msec_to_repaint = timespec_sub_to_msec(&output->next_repaint, now); - if (msec_to_repaint > 1) - return ret; - - /* If we're sleeping, drop the repaint machinery entirely; we will - * explicitly repaint all outputs when we come back. */ - if (compositor->state == WESTON_COMPOSITOR_SLEEPING || - compositor->state == WESTON_COMPOSITOR_OFFSCREEN) - goto err; - - /* We don't actually need to repaint this output; drop it from - * repaint until something causes damage. */ - if (!output->repaint_needed) - goto err; - - /* If repaint fails, we aren't going to get weston_output_finish_frame - * to trigger a new repaint, so drop it from repaint and hope - * something schedules a successful repaint later. As repainting may - * take some time, re-read our clock as a courtesy to the next - * output. */ - ret = weston_output_repaint(output, repaint_data); - weston_compositor_read_presentation_clock(compositor, now); - if (ret != 0) - goto err; - - output->repainted = true; - return ret; - -err: - weston_output_schedule_repaint_reset(output); - return ret; -} - -static void -output_repaint_timer_arm(struct weston_compositor *compositor) -{ - struct weston_output *output; - bool any_should_repaint = false; - struct timespec now; - int64_t msec_to_next = INT64_MAX; - - weston_compositor_read_presentation_clock(compositor, &now); - - wl_list_for_each(output, &compositor->output_list, link) { - int64_t msec_to_this; - - if (output->repaint_status != REPAINT_SCHEDULED) - continue; - - msec_to_this = timespec_sub_to_msec(&output->next_repaint, - &now); - if (!any_should_repaint || msec_to_this < msec_to_next) - msec_to_next = msec_to_this; - - any_should_repaint = true; - } - - if (!any_should_repaint) - return; - - /* Even if we should repaint immediately, add the minimum 1 ms delay. - * This is a workaround to allow coalescing multiple output repaints - * particularly from weston_output_finish_frame() - * into the same call, which would not happen if we called - * output_repaint_timer_handler() directly. - */ - if (msec_to_next < 1) - msec_to_next = 1; - - wl_event_source_timer_update(compositor->repaint_timer, msec_to_next); -} - -static int -output_repaint_timer_handler(void *data) -{ - struct weston_compositor *compositor = data; - struct weston_output *output; - struct timespec now; - void *repaint_data = NULL; - int ret = 0; - - weston_compositor_read_presentation_clock(compositor, &now); - - if (compositor->backend->repaint_begin) - repaint_data = compositor->backend->repaint_begin(compositor); - - wl_list_for_each(output, &compositor->output_list, link) { - ret = weston_output_maybe_repaint(output, &now, repaint_data); - if (ret) - break; - } - - if (ret == 0) { - if (compositor->backend->repaint_flush) - ret = compositor->backend->repaint_flush(compositor, - repaint_data); - } else { - if (compositor->backend->repaint_cancel) - compositor->backend->repaint_cancel(compositor, - repaint_data); - } - - if (ret != 0) { - wl_list_for_each(output, &compositor->output_list, link) { - if (output->repainted) - weston_output_schedule_repaint_reset(output); - } - } - - wl_list_for_each(output, &compositor->output_list, link) - output->repainted = false; - - output_repaint_timer_arm(compositor); - - return 0; -} - -/** - * \ingroup output - */ -WL_EXPORT void -weston_output_finish_frame(struct weston_output *output, - const struct timespec *stamp, - uint32_t presented_flags) -{ - struct weston_compositor *compositor = output->compositor; - int32_t refresh_nsec; - struct timespec now; - int64_t msec_rel; - - - assert(output->repaint_status == REPAINT_AWAITING_COMPLETION); - assert(stamp || (presented_flags & WP_PRESENTATION_FEEDBACK_INVALID)); - - weston_compositor_read_presentation_clock(compositor, &now); - - /* If we haven't been supplied any timestamp at all, we don't have a - * timebase to work against, so any delay just wastes time. Push a - * repaint as soon as possible so we can get on with it. */ - if (!stamp) { - output->next_repaint = now; - goto out; - } - - TL_POINT(compositor, "core_repaint_finished", TLP_OUTPUT(output), - TLP_VBLANK(stamp), TLP_END); - - refresh_nsec = millihz_to_nsec(output->current_mode->refresh); - weston_presentation_feedback_present_list(&output->feedback_list, - output, refresh_nsec, stamp, - output->msc, - presented_flags); - - output->frame_time = *stamp; - - timespec_add_nsec(&output->next_repaint, stamp, refresh_nsec); - timespec_add_msec(&output->next_repaint, &output->next_repaint, - -compositor->repaint_msec); - msec_rel = timespec_sub_to_msec(&output->next_repaint, &now); - - if (msec_rel < -1000 || msec_rel > 1000) { - static bool warned; - - if (!warned) - weston_log("Warning: computed repaint delay is " - "insane: %lld msec\n", (long long) msec_rel); - warned = true; - - output->next_repaint = now; - } - - /* Called from restart_repaint_loop and restart happens already after - * the deadline given by repaint_msec? In that case we delay until - * the deadline of the next frame, to give clients a more predictable - * timing of the repaint cycle to lock on. */ - if (presented_flags == WP_PRESENTATION_FEEDBACK_INVALID && - msec_rel < 0) { - while (timespec_sub_to_nsec(&output->next_repaint, &now) < 0) { - timespec_add_nsec(&output->next_repaint, - &output->next_repaint, - refresh_nsec); - } - } - -out: - output->repaint_status = REPAINT_SCHEDULED; - output_repaint_timer_arm(compositor); -} - -static void -idle_repaint(void *data) -{ - struct weston_output *output = data; - int ret; - - assert(output->repaint_status == REPAINT_BEGIN_FROM_IDLE); - output->repaint_status = REPAINT_AWAITING_COMPLETION; - output->idle_repaint_source = NULL; - ret = output->start_repaint_loop(output); - if (ret != 0) - weston_output_schedule_repaint_reset(output); -} - -WL_EXPORT void -weston_layer_entry_insert(struct weston_layer_entry *list, - struct weston_layer_entry *entry) -{ - wl_list_insert(&list->link, &entry->link); - entry->layer = list->layer; -} - -WL_EXPORT void -weston_layer_entry_remove(struct weston_layer_entry *entry) -{ - wl_list_remove(&entry->link); - wl_list_init(&entry->link); - entry->layer = NULL; -} - - -/** Initialize the weston_layer struct. - * - * \param compositor The compositor instance - * \param layer The layer to initialize - */ -WL_EXPORT void -weston_layer_init(struct weston_layer *layer, - struct weston_compositor *compositor) -{ - layer->compositor = compositor; - wl_list_init(&layer->link); - wl_list_init(&layer->view_list.link); - layer->view_list.layer = layer; - weston_layer_set_mask_infinite(layer); -} - -/** Sets the position of the layer in the layer list. The layer will be placed - * below any layer with the same position value, if any. - * This function is safe to call if the layer is already on the list, but the - * layer may be moved below other layers at the same position, if any. - * - * \param layer The layer to modify - * \param position The position the layer will be placed at - */ -WL_EXPORT void -weston_layer_set_position(struct weston_layer *layer, - enum weston_layer_position position) -{ - struct weston_layer *below; - - wl_list_remove(&layer->link); - - /* layer_list is ordered from top to bottom, the last layer being the - * background with the smallest position value */ - - layer->position = position; - wl_list_for_each_reverse(below, &layer->compositor->layer_list, link) { - if (below->position >= layer->position) { - wl_list_insert(&below->link, &layer->link); - return; - } - } - wl_list_insert(&layer->compositor->layer_list, &layer->link); -} - -/** Hide a layer by taking it off the layer list. - * This function is safe to call if the layer is not on the list. - * - * \param layer The layer to hide - */ -WL_EXPORT void -weston_layer_unset_position(struct weston_layer *layer) -{ - wl_list_remove(&layer->link); - wl_list_init(&layer->link); -} - -WL_EXPORT void -weston_layer_set_mask(struct weston_layer *layer, - int x, int y, int width, int height) -{ - struct weston_view *view; - - layer->mask.x1 = x; - layer->mask.x2 = x + width; - layer->mask.y1 = y; - layer->mask.y2 = y + height; - - wl_list_for_each(view, &layer->view_list.link, layer_link.link) { - weston_view_geometry_dirty(view); - } -} - -WL_EXPORT void -weston_layer_set_mask_infinite(struct weston_layer *layer) -{ - struct weston_view *view; - - layer->mask.x1 = INT32_MIN; - layer->mask.x2 = INT32_MAX; - layer->mask.y1 = INT32_MIN; - layer->mask.y2 = INT32_MAX; - - wl_list_for_each(view, &layer->view_list.link, layer_link.link) { - weston_view_geometry_dirty(view); - } -} - -WL_EXPORT bool -weston_layer_mask_is_infinite(struct weston_layer *layer) -{ - return layer->mask.x1 == INT32_MIN && - layer->mask.y1 == INT32_MIN && - layer->mask.x2 == INT32_MAX && - layer->mask.y2 == INT32_MAX; -} - -/** - * \ingroup output - */ -WL_EXPORT void -weston_output_schedule_repaint(struct weston_output *output) -{ - struct weston_compositor *compositor = output->compositor; - struct wl_event_loop *loop; - - if (compositor->state == WESTON_COMPOSITOR_SLEEPING || - compositor->state == WESTON_COMPOSITOR_OFFSCREEN) - return; - - if (!output->repaint_needed) - TL_POINT(compositor, "core_repaint_req", TLP_OUTPUT(output), TLP_END); - - loop = wl_display_get_event_loop(compositor->wl_display); - output->repaint_needed = true; - - /* If we already have a repaint scheduled for our idle handler, - * no need to set it again. If the repaint has been called but - * not finished, then weston_output_finish_frame() will notice - * that a repaint is needed and schedule one. */ - if (output->repaint_status != REPAINT_NOT_SCHEDULED) - return; - - output->repaint_status = REPAINT_BEGIN_FROM_IDLE; - assert(!output->idle_repaint_source); - output->idle_repaint_source = wl_event_loop_add_idle(loop, idle_repaint, - output); - TL_POINT(compositor, "core_repaint_enter_loop", TLP_OUTPUT(output), TLP_END); -} - -/** weston_compositor_schedule_repaint - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_schedule_repaint(struct weston_compositor *compositor) -{ - struct weston_output *output; - - wl_list_for_each(output, &compositor->output_list, link) - weston_output_schedule_repaint(output); -} - -static void -surface_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -surface_attach(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *buffer_resource, int32_t sx, int32_t sy) -{ - struct weston_surface *surface = wl_resource_get_user_data(resource); - struct weston_buffer *buffer = NULL; - - if (buffer_resource) { - buffer = weston_buffer_from_resource(buffer_resource); - if (buffer == NULL) { - wl_client_post_no_memory(client); - return; - } - } - - /* Attach, attach, without commit in between does not send - * wl_buffer.release. */ - weston_surface_state_set_buffer(&surface->pending, buffer); - - surface->pending.sx = sx; - surface->pending.sy = sy; - surface->pending.newly_attached = 1; -} - -static void -surface_damage(struct wl_client *client, - struct wl_resource *resource, - int32_t x, int32_t y, int32_t width, int32_t height) -{ - struct weston_surface *surface = wl_resource_get_user_data(resource); - - if (width <= 0 || height <= 0) - return; - - pixman_region32_union_rect(&surface->pending.damage_surface, - &surface->pending.damage_surface, - x, y, width, height); -} - -static void -surface_damage_buffer(struct wl_client *client, - struct wl_resource *resource, - int32_t x, int32_t y, int32_t width, int32_t height) -{ - struct weston_surface *surface = wl_resource_get_user_data(resource); - - if (width <= 0 || height <= 0) - return; - - pixman_region32_union_rect(&surface->pending.damage_buffer, - &surface->pending.damage_buffer, - x, y, width, height); -} - -static void -destroy_frame_callback(struct wl_resource *resource) -{ - struct weston_frame_callback *cb = wl_resource_get_user_data(resource); - - wl_list_remove(&cb->link); - free(cb); -} - -static void -surface_frame(struct wl_client *client, - struct wl_resource *resource, uint32_t callback) -{ - struct weston_frame_callback *cb; - struct weston_surface *surface = wl_resource_get_user_data(resource); - - cb = malloc(sizeof *cb); - if (cb == NULL) { - wl_resource_post_no_memory(resource); - return; - } - - cb->resource = wl_resource_create(client, &wl_callback_interface, 1, - callback); - if (cb->resource == NULL) { - free(cb); - wl_resource_post_no_memory(resource); - return; - } - - wl_resource_set_implementation(cb->resource, NULL, cb, - destroy_frame_callback); - - wl_list_insert(surface->pending.frame_callback_list.prev, &cb->link); -} - -static void -surface_set_opaque_region(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *region_resource) -{ - struct weston_surface *surface = wl_resource_get_user_data(resource); - struct weston_region *region; - - if (region_resource) { - region = wl_resource_get_user_data(region_resource); - pixman_region32_copy(&surface->pending.opaque, - ®ion->region); - } else { - pixman_region32_clear(&surface->pending.opaque); - } -} - -static void -surface_set_input_region(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *region_resource) -{ - struct weston_surface *surface = wl_resource_get_user_data(resource); - struct weston_region *region; - - if (region_resource) { - region = wl_resource_get_user_data(region_resource); - pixman_region32_copy(&surface->pending.input, - ®ion->region); - } else { - pixman_region32_fini(&surface->pending.input); - region_init_infinite(&surface->pending.input); - } -} - -/* Cause damage to this sub-surface and all its children. - * - * This is useful when there are state changes that need an implicit - * damage, e.g. a z-order change. - */ -static void -weston_surface_damage_subsurfaces(struct weston_subsurface *sub) -{ - struct weston_subsurface *child; - - weston_surface_damage(sub->surface); - sub->reordered = false; - - wl_list_for_each(child, &sub->surface->subsurface_list, parent_link) - if (child != sub) - weston_surface_damage_subsurfaces(child); -} - -static void -weston_surface_commit_subsurface_order(struct weston_surface *surface) -{ - struct weston_subsurface *sub; - - wl_list_for_each_reverse(sub, &surface->subsurface_list_pending, - parent_link_pending) { - wl_list_remove(&sub->parent_link); - wl_list_insert(&surface->subsurface_list, &sub->parent_link); - - if (sub->reordered) - weston_surface_damage_subsurfaces(sub); - } -} - -static void -weston_surface_build_buffer_matrix(const struct weston_surface *surface, - struct weston_matrix *matrix) -{ - const struct weston_buffer_viewport *vp = &surface->buffer_viewport; - double src_width, src_height, dest_width, dest_height; - - weston_matrix_init(matrix); - - if (vp->buffer.src_width == wl_fixed_from_int(-1)) { - src_width = surface->width_from_buffer; - src_height = surface->height_from_buffer; - } else { - src_width = wl_fixed_to_double(vp->buffer.src_width); - src_height = wl_fixed_to_double(vp->buffer.src_height); - } - - if (vp->surface.width == -1) { - dest_width = src_width; - dest_height = src_height; - } else { - dest_width = vp->surface.width; - dest_height = vp->surface.height; - } - - if (src_width != dest_width || src_height != dest_height) - weston_matrix_scale(matrix, - src_width / dest_width, - src_height / dest_height, 1); - - if (vp->buffer.src_width != wl_fixed_from_int(-1)) - weston_matrix_translate(matrix, - wl_fixed_to_double(vp->buffer.src_x), - wl_fixed_to_double(vp->buffer.src_y), - 0); - - switch (vp->buffer.transform) { - case WL_OUTPUT_TRANSFORM_FLIPPED: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - weston_matrix_scale(matrix, -1, 1, 1); - weston_matrix_translate(matrix, - surface->width_from_buffer, 0, 0); - break; - } - - switch (vp->buffer.transform) { - default: - case WL_OUTPUT_TRANSFORM_NORMAL: - case WL_OUTPUT_TRANSFORM_FLIPPED: - break; - case WL_OUTPUT_TRANSFORM_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - weston_matrix_rotate_xy(matrix, 0, 1); - weston_matrix_translate(matrix, - surface->height_from_buffer, 0, 0); - break; - case WL_OUTPUT_TRANSFORM_180: - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - weston_matrix_rotate_xy(matrix, -1, 0); - weston_matrix_translate(matrix, - surface->width_from_buffer, - surface->height_from_buffer, 0); - break; - case WL_OUTPUT_TRANSFORM_270: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - weston_matrix_rotate_xy(matrix, 0, -1); - weston_matrix_translate(matrix, - 0, surface->width_from_buffer, 0); - break; - } - - weston_matrix_scale(matrix, vp->buffer.scale, vp->buffer.scale, 1); -} - -/** - * Compute a + b > c while being safe to overflows. - */ -static bool -fixed_sum_gt(wl_fixed_t a, wl_fixed_t b, wl_fixed_t c) -{ - return (int64_t)a + (int64_t)b > (int64_t)c; -} - -static bool -weston_surface_is_pending_viewport_source_valid( - const struct weston_surface *surface) -{ - const struct weston_surface_state *pend = &surface->pending; - const struct weston_buffer_viewport *vp = &pend->buffer_viewport; - int width_from_buffer = 0; - int height_from_buffer = 0; - wl_fixed_t w; - wl_fixed_t h; - - /* If viewport source rect is not set, it is always ok. */ - if (vp->buffer.src_width == wl_fixed_from_int(-1)) - return true; - - if (pend->newly_attached) { - if (pend->buffer) { - convert_size_by_transform_scale(&width_from_buffer, - &height_from_buffer, - pend->buffer->width, - pend->buffer->height, - vp->buffer.transform, - vp->buffer.scale); - } else { - /* No buffer: viewport is irrelevant. */ - return true; - } - } else { - width_from_buffer = surface->width_from_buffer; - height_from_buffer = surface->height_from_buffer; - } - - assert((width_from_buffer == 0) == (height_from_buffer == 0)); - assert(width_from_buffer >= 0 && height_from_buffer >= 0); - - /* No buffer: viewport is irrelevant. */ - if (width_from_buffer == 0 || height_from_buffer == 0) - return true; - - /* overflow checks for wl_fixed_from_int() */ - if (width_from_buffer > wl_fixed_to_int(INT32_MAX)) - return false; - if (height_from_buffer > wl_fixed_to_int(INT32_MAX)) - return false; - - w = wl_fixed_from_int(width_from_buffer); - h = wl_fixed_from_int(height_from_buffer); - - if (fixed_sum_gt(vp->buffer.src_x, vp->buffer.src_width, w)) - return false; - if (fixed_sum_gt(vp->buffer.src_y, vp->buffer.src_height, h)) - return false; - - return true; -} - -static bool -fixed_is_integer(wl_fixed_t v) -{ - return (v & 0xff) == 0; -} - -static bool -weston_surface_is_pending_viewport_dst_size_int( - const struct weston_surface *surface) -{ - const struct weston_buffer_viewport *vp = - &surface->pending.buffer_viewport; - - if (vp->surface.width != -1) { - assert(vp->surface.width > 0 && vp->surface.height > 0); - return true; - } - - return fixed_is_integer(vp->buffer.src_width) && - fixed_is_integer(vp->buffer.src_height); -} - -/* Translate pending damage in buffer co-ordinates to surface - * co-ordinates and union it with a pixman_region32_t. - * This should only be called after the buffer is attached. - */ -static void -apply_damage_buffer(pixman_region32_t *dest, - struct weston_surface *surface, - struct weston_surface_state *state) -{ - struct weston_buffer *buffer = surface->buffer_ref.buffer; - - /* wl_surface.damage_buffer needs to be clipped to the buffer, - * translated into surface co-ordinates and unioned with - * any other surface damage. - * None of this makes sense if there is no buffer though. - */ - if (buffer && pixman_region32_not_empty(&state->damage_buffer)) { - pixman_region32_t buffer_damage; - - pixman_region32_intersect_rect(&state->damage_buffer, - &state->damage_buffer, - 0, 0, buffer->width, - buffer->height); - pixman_region32_init(&buffer_damage); - weston_matrix_transform_region(&buffer_damage, - &surface->buffer_to_surface_matrix, - &state->damage_buffer); - pixman_region32_union(dest, dest, &buffer_damage); - pixman_region32_fini(&buffer_damage); - } - /* We should clear this on commit even if there was no buffer */ - pixman_region32_clear(&state->damage_buffer); -} - -static void -weston_surface_set_desired_protection(struct weston_surface *surface, - enum weston_hdcp_protection protection) -{ - if (surface->desired_protection == protection) - return; - surface->desired_protection = protection; - weston_surface_damage(surface); -} - -static void -weston_surface_set_protection_mode(struct weston_surface *surface, - enum weston_surface_protection_mode p_mode) -{ - struct content_protection *cp = surface->compositor->content_protection; - struct protected_surface *psurface; - - surface->protection_mode = p_mode; - wl_list_for_each(psurface, &cp->protected_list, link) { - if (!psurface || psurface->surface != surface) - continue; - weston_protected_surface_send_event(psurface, - surface->current_protection); - } -} - -static void -weston_surface_commit_state(struct weston_surface *surface, - struct weston_surface_state *state) -{ - struct weston_view *view; - pixman_region32_t opaque; - - /* wl_surface.set_buffer_transform */ - /* wl_surface.set_buffer_scale */ - /* wp_viewport.set_source */ - /* wp_viewport.set_destination */ - surface->buffer_viewport = state->buffer_viewport; - - /* wl_surface.attach */ - if (state->newly_attached) { - /* zwp_surface_synchronization_v1.set_acquire_fence */ - fd_move(&surface->acquire_fence_fd, - &state->acquire_fence_fd); - /* zwp_surface_synchronization_v1.get_release */ - weston_buffer_release_move(&surface->buffer_release_ref, - &state->buffer_release_ref); - weston_surface_attach(surface, state->buffer); - } - weston_surface_state_set_buffer(state, NULL); - assert(state->acquire_fence_fd == -1); - assert(state->buffer_release_ref.buffer_release == NULL); - - weston_surface_build_buffer_matrix(surface, - &surface->surface_to_buffer_matrix); - weston_matrix_invert(&surface->buffer_to_surface_matrix, - &surface->surface_to_buffer_matrix); - - if (state->newly_attached || state->buffer_viewport.changed) { - weston_surface_update_size(surface); - if (surface->committed) - surface->committed(surface, state->sx, state->sy); - } - - state->sx = 0; - state->sy = 0; - state->newly_attached = 0; - state->buffer_viewport.changed = 0; - - /* wl_surface.damage and wl_surface.damage_buffer */ - if (pixman_region32_not_empty(&state->damage_surface) || - pixman_region32_not_empty(&state->damage_buffer)) - TL_POINT(surface->compositor, "core_commit_damage", TLP_SURFACE(surface), TLP_END); - - pixman_region32_union(&surface->damage, &surface->damage, - &state->damage_surface); - - apply_damage_buffer(&surface->damage, surface, state); - - pixman_region32_intersect_rect(&surface->damage, &surface->damage, - 0, 0, surface->width, surface->height); - pixman_region32_clear(&state->damage_surface); - - /* wl_surface.set_opaque_region */ - pixman_region32_init(&opaque); - pixman_region32_intersect_rect(&opaque, &state->opaque, - 0, 0, surface->width, surface->height); - - if (!pixman_region32_equal(&opaque, &surface->opaque)) { - pixman_region32_copy(&surface->opaque, &opaque); - wl_list_for_each(view, &surface->views, surface_link) - weston_view_geometry_dirty(view); - } - - pixman_region32_fini(&opaque); - - /* wl_surface.set_input_region */ - pixman_region32_intersect_rect(&surface->input, &state->input, - 0, 0, surface->width, surface->height); - - /* wl_surface.frame */ - wl_list_insert_list(&surface->frame_callback_list, - &state->frame_callback_list); - wl_list_init(&state->frame_callback_list); - - /* XXX: - * What should happen with a feedback request, if there - * is no wl_buffer attached for this commit? - */ - - /* presentation.feedback */ - wl_list_insert_list(&surface->feedback_list, - &state->feedback_list); - wl_list_init(&state->feedback_list); - - /* weston_protected_surface.enforced/relaxed */ - if (surface->protection_mode != state->protection_mode) - weston_surface_set_protection_mode(surface, - state->protection_mode); - - /* weston_protected_surface.set_type */ - weston_surface_set_desired_protection(surface, state->desired_protection); - - wl_signal_emit(&surface->commit_signal, surface); -} - -static void -weston_surface_commit(struct weston_surface *surface) -{ - weston_surface_commit_state(surface, &surface->pending); - - weston_surface_commit_subsurface_order(surface); - - weston_surface_schedule_repaint(surface); -} - -static void -weston_subsurface_commit(struct weston_subsurface *sub); - -static void -weston_subsurface_parent_commit(struct weston_subsurface *sub, - int parent_is_synchronized); - -static void -surface_commit(struct wl_client *client, struct wl_resource *resource) -{ - struct weston_surface *surface = wl_resource_get_user_data(resource); - struct weston_subsurface *sub = weston_surface_to_subsurface(surface); - - if (!weston_surface_is_pending_viewport_source_valid(surface)) { - assert(surface->viewport_resource); - - wl_resource_post_error(surface->viewport_resource, - WP_VIEWPORT_ERROR_OUT_OF_BUFFER, - "wl_surface@%d has viewport source outside buffer", - wl_resource_get_id(resource)); - return; - } - - if (!weston_surface_is_pending_viewport_dst_size_int(surface)) { - assert(surface->viewport_resource); - - wl_resource_post_error(surface->viewport_resource, - WP_VIEWPORT_ERROR_BAD_SIZE, - "wl_surface@%d viewport dst size not integer", - wl_resource_get_id(resource)); - return; - } - - if (surface->pending.acquire_fence_fd >= 0) { - assert(surface->synchronization_resource); - - if (!surface->pending.buffer) { - fd_clear(&surface->pending.acquire_fence_fd); - wl_resource_post_error(surface->synchronization_resource, - ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_BUFFER, - "wl_surface@%"PRIu32" no buffer for synchronization", - wl_resource_get_id(resource)); - return; - } - - /* We support fences for both wp_linux_dmabuf and opaque EGL - * buffers, as mandated by minor version 2 of the - * zwp_linux_explicit_synchronization_v1 protocol. Since - * renderers that support fences currently only support these - * two buffer types plus SHM buffers, we can just check for the - * SHM buffer case here. - */ - if (wl_shm_buffer_get(surface->pending.buffer->resource)) { - fd_clear(&surface->pending.acquire_fence_fd); - wl_resource_post_error(surface->synchronization_resource, - ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_UNSUPPORTED_BUFFER, - "wl_surface@%"PRIu32" unsupported buffer for synchronization", - wl_resource_get_id(resource)); - return; - } - } - - if (surface->pending.buffer_release_ref.buffer_release && - !surface->pending.buffer) { - assert(surface->synchronization_resource); - - wl_resource_post_error(surface->synchronization_resource, - ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_BUFFER, - "wl_surface@%"PRIu32" no buffer for synchronization", - wl_resource_get_id(resource)); - return; - } - - if (sub) { - weston_subsurface_commit(sub); - return; - } - - weston_surface_commit(surface); - - wl_list_for_each(sub, &surface->subsurface_list, parent_link) { - if (sub->surface != surface) - weston_subsurface_parent_commit(sub, 0); - } -} - -static void -surface_set_buffer_transform(struct wl_client *client, - struct wl_resource *resource, int transform) -{ - struct weston_surface *surface = wl_resource_get_user_data(resource); - - /* if wl_output.transform grows more members this will need to be updated. */ - if (transform < 0 || - transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) { - wl_resource_post_error(resource, - WL_SURFACE_ERROR_INVALID_TRANSFORM, - "buffer transform must be a valid transform " - "('%d' specified)", transform); - return; - } - - surface->pending.buffer_viewport.buffer.transform = transform; - surface->pending.buffer_viewport.changed = 1; -} - -static void -surface_set_buffer_scale(struct wl_client *client, - struct wl_resource *resource, - int32_t scale) -{ - struct weston_surface *surface = wl_resource_get_user_data(resource); - - if (scale < 1) { - wl_resource_post_error(resource, - WL_SURFACE_ERROR_INVALID_SCALE, - "buffer scale must be at least one " - "('%d' specified)", scale); - return; - } - - surface->pending.buffer_viewport.buffer.scale = scale; - surface->pending.buffer_viewport.changed = 1; -} - -static const struct wl_surface_interface surface_interface = { - surface_destroy, - surface_attach, - surface_damage, - surface_frame, - surface_set_opaque_region, - surface_set_input_region, - surface_commit, - surface_set_buffer_transform, - surface_set_buffer_scale, - surface_damage_buffer -}; - -static void -compositor_create_surface(struct wl_client *client, - struct wl_resource *resource, uint32_t id) -{ - struct weston_compositor *ec = wl_resource_get_user_data(resource); - struct weston_surface *surface; - - surface = weston_surface_create(ec); - if (surface == NULL) { - wl_resource_post_no_memory(resource); - return; - } - - surface->resource = - wl_resource_create(client, &wl_surface_interface, - wl_resource_get_version(resource), id); - if (surface->resource == NULL) { - weston_surface_destroy(surface); - wl_resource_post_no_memory(resource); - return; - } - wl_resource_set_implementation(surface->resource, &surface_interface, - surface, destroy_surface); - - wl_signal_emit(&ec->create_surface_signal, surface); -} - -static void -destroy_region(struct wl_resource *resource) -{ - struct weston_region *region = wl_resource_get_user_data(resource); - - pixman_region32_fini(®ion->region); - free(region); -} - -static void -region_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -region_add(struct wl_client *client, struct wl_resource *resource, - int32_t x, int32_t y, int32_t width, int32_t height) -{ - struct weston_region *region = wl_resource_get_user_data(resource); - - pixman_region32_union_rect(®ion->region, ®ion->region, - x, y, width, height); -} - -static void -region_subtract(struct wl_client *client, struct wl_resource *resource, - int32_t x, int32_t y, int32_t width, int32_t height) -{ - struct weston_region *region = wl_resource_get_user_data(resource); - pixman_region32_t rect; - - pixman_region32_init_rect(&rect, x, y, width, height); - pixman_region32_subtract(®ion->region, ®ion->region, &rect); - pixman_region32_fini(&rect); -} - -static const struct wl_region_interface region_interface = { - region_destroy, - region_add, - region_subtract -}; - -static void -compositor_create_region(struct wl_client *client, - struct wl_resource *resource, uint32_t id) -{ - struct weston_region *region; - - region = malloc(sizeof *region); - if (region == NULL) { - wl_resource_post_no_memory(resource); - return; - } - - pixman_region32_init(®ion->region); - - region->resource = - wl_resource_create(client, &wl_region_interface, 1, id); - if (region->resource == NULL) { - free(region); - wl_resource_post_no_memory(resource); - return; - } - wl_resource_set_implementation(region->resource, ®ion_interface, - region, destroy_region); -} - -static const struct wl_compositor_interface compositor_interface = { - compositor_create_surface, - compositor_create_region -}; - -static void -weston_subsurface_commit_from_cache(struct weston_subsurface *sub) -{ - struct weston_surface *surface = sub->surface; - - weston_surface_commit_state(surface, &sub->cached); - weston_buffer_reference(&sub->cached_buffer_ref, NULL); - - weston_surface_commit_subsurface_order(surface); - - weston_surface_schedule_repaint(surface); - - sub->has_cached_data = 0; -} - -static void -weston_subsurface_commit_to_cache(struct weston_subsurface *sub) -{ - struct weston_surface *surface = sub->surface; - - /* - * If this commit would cause the surface to move by the - * attach(dx, dy) parameters, the old damage region must be - * translated to correspond to the new surface coordinate system - * origin. - */ - pixman_region32_translate(&sub->cached.damage_surface, - -surface->pending.sx, -surface->pending.sy); - pixman_region32_union(&sub->cached.damage_surface, - &sub->cached.damage_surface, - &surface->pending.damage_surface); - pixman_region32_clear(&surface->pending.damage_surface); - - if (surface->pending.newly_attached) { - sub->cached.newly_attached = 1; - weston_surface_state_set_buffer(&sub->cached, - surface->pending.buffer); - weston_buffer_reference(&sub->cached_buffer_ref, - surface->pending.buffer); - weston_presentation_feedback_discard_list( - &sub->cached.feedback_list); - /* zwp_surface_synchronization_v1.set_acquire_fence */ - fd_move(&sub->cached.acquire_fence_fd, - &surface->pending.acquire_fence_fd); - /* zwp_surface_synchronization_v1.get_release */ - weston_buffer_release_move(&sub->cached.buffer_release_ref, - &surface->pending.buffer_release_ref); - } - sub->cached.desired_protection = surface->pending.desired_protection; - sub->cached.protection_mode = surface->pending.protection_mode; - assert(surface->pending.acquire_fence_fd == -1); - assert(surface->pending.buffer_release_ref.buffer_release == NULL); - sub->cached.sx += surface->pending.sx; - sub->cached.sy += surface->pending.sy; - - apply_damage_buffer(&sub->cached.damage_surface, surface, &surface->pending); - - sub->cached.buffer_viewport.changed |= - surface->pending.buffer_viewport.changed; - sub->cached.buffer_viewport.buffer = - surface->pending.buffer_viewport.buffer; - sub->cached.buffer_viewport.surface = - surface->pending.buffer_viewport.surface; - - weston_surface_reset_pending_buffer(surface); - - pixman_region32_copy(&sub->cached.opaque, &surface->pending.opaque); - - pixman_region32_copy(&sub->cached.input, &surface->pending.input); - - wl_list_insert_list(&sub->cached.frame_callback_list, - &surface->pending.frame_callback_list); - wl_list_init(&surface->pending.frame_callback_list); - - wl_list_insert_list(&sub->cached.feedback_list, - &surface->pending.feedback_list); - wl_list_init(&surface->pending.feedback_list); - - sub->has_cached_data = 1; -} - -static bool -weston_subsurface_is_synchronized(struct weston_subsurface *sub) -{ - while (sub) { - if (sub->synchronized) - return true; - - if (!sub->parent) - return false; - - sub = weston_surface_to_subsurface(sub->parent); - } - - return false; -} - -static void -weston_subsurface_commit(struct weston_subsurface *sub) -{ - struct weston_surface *surface = sub->surface; - struct weston_subsurface *tmp; - - /* Recursive check for effectively synchronized. */ - if (weston_subsurface_is_synchronized(sub)) { - weston_subsurface_commit_to_cache(sub); - } else { - if (sub->has_cached_data) { - /* flush accumulated state from cache */ - weston_subsurface_commit_to_cache(sub); - weston_subsurface_commit_from_cache(sub); - } else { - weston_surface_commit(surface); - } - - wl_list_for_each(tmp, &surface->subsurface_list, parent_link) { - if (tmp->surface != surface) - weston_subsurface_parent_commit(tmp, 0); - } - } -} - -static void -weston_subsurface_synchronized_commit(struct weston_subsurface *sub) -{ - struct weston_surface *surface = sub->surface; - struct weston_subsurface *tmp; - - /* From now on, commit_from_cache the whole sub-tree, regardless of - * the synchronized mode of each child. This sub-surface or some - * of its ancestors were synchronized, so we are synchronized - * all the way down. - */ - - if (sub->has_cached_data) - weston_subsurface_commit_from_cache(sub); - - wl_list_for_each(tmp, &surface->subsurface_list, parent_link) { - if (tmp->surface != surface) - weston_subsurface_parent_commit(tmp, 1); - } -} - -static void -weston_subsurface_parent_commit(struct weston_subsurface *sub, - int parent_is_synchronized) -{ - struct weston_view *view; - if (sub->position.set) { - wl_list_for_each(view, &sub->surface->views, surface_link) - weston_view_set_position(view, - sub->position.x, - sub->position.y); - - sub->position.set = 0; - } - - if (parent_is_synchronized || sub->synchronized) - weston_subsurface_synchronized_commit(sub); -} - -static int -subsurface_get_label(struct weston_surface *surface, char *buf, size_t len) -{ - return snprintf(buf, len, "sub-surface"); -} - -static void -subsurface_committed(struct weston_surface *surface, int32_t dx, int32_t dy) -{ - struct weston_view *view; - - wl_list_for_each(view, &surface->views, surface_link) - weston_view_set_position(view, - view->geometry.x + dx, - view->geometry.y + dy); - - /* No need to check parent mappedness, because if parent is not - * mapped, parent is not in a visible layer, so this sub-surface - * will not be drawn either. - */ - - if (!weston_surface_is_mapped(surface)) { - surface->is_mapped = true; - - /* Cannot call weston_view_update_transform(), - * because that would call it also for the parent surface, - * which might not be mapped yet. That would lead to - * inconsistent state, where the window could never be - * mapped. - * - * Instead just force the is_mapped flag on, to make - * weston_surface_is_mapped() return true, so that when the - * parent surface does get mapped, this one will get - * included, too. See view_list_add(). - */ - } -} - -static struct weston_subsurface * -weston_surface_to_subsurface(struct weston_surface *surface) -{ - if (surface->committed == subsurface_committed) - return surface->committed_private; - - return NULL; -} - -WL_EXPORT struct weston_surface * -weston_surface_get_main_surface(struct weston_surface *surface) -{ - struct weston_subsurface *sub; - - while (surface && (sub = weston_surface_to_subsurface(surface))) - surface = sub->parent; - - return surface; -} - -WL_EXPORT int -weston_surface_set_role(struct weston_surface *surface, - const char *role_name, - struct wl_resource *error_resource, - uint32_t error_code) -{ - assert(role_name); - - if (surface->role_name == NULL || - surface->role_name == role_name || - strcmp(surface->role_name, role_name) == 0) { - surface->role_name = role_name; - - return 0; - } - - wl_resource_post_error(error_resource, error_code, - "Cannot assign role %s to wl_surface@%d," - " already has role %s\n", - role_name, - wl_resource_get_id(surface->resource), - surface->role_name); - return -1; -} - -WL_EXPORT const char * -weston_surface_get_role(struct weston_surface *surface) -{ - return surface->role_name; -} - -WL_EXPORT void -weston_surface_set_label_func(struct weston_surface *surface, - int (*desc)(struct weston_surface *, - char *, size_t)) -{ - surface->get_label = desc; - weston_timeline_refresh_subscription_objects(surface->compositor, - surface); -} - -/** Get the size of surface contents - * - * \param surface The surface to query. - * \param width Returns the width of raw contents. - * \param height Returns the height of raw contents. - * - * Retrieves the raw surface content size in pixels for the given surface. - * This is the whole content size in buffer pixels. If the surface - * has no content or the renderer does not implement this feature, - * zeroes are returned. - * - * This function is used to determine the buffer size needed for - * a weston_surface_copy_content() call. - */ -WL_EXPORT void -weston_surface_get_content_size(struct weston_surface *surface, - int *width, int *height) -{ - struct weston_renderer *rer = surface->compositor->renderer; - - if (!rer->surface_get_content_size) { - *width = 0; - *height = 0; - return; - } - - rer->surface_get_content_size(surface, width, height); -} - -/** Get the bounding box of a surface and its subsurfaces - * - * \param surface The surface to query. - * \return The bounding box relative to the surface origin. - * - */ -WL_EXPORT struct weston_geometry -weston_surface_get_bounding_box(struct weston_surface *surface) -{ - pixman_region32_t region; - pixman_box32_t *box; - struct weston_subsurface *subsurface; - - pixman_region32_init_rect(®ion, - 0, 0, - surface->width, surface->height); - - wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) - pixman_region32_union_rect(®ion, ®ion, - subsurface->position.x, - subsurface->position.y, - subsurface->surface->width, - subsurface->surface->height); - - box = pixman_region32_extents(®ion); - struct weston_geometry geometry = { - .x = box->x1, - .y = box->y1, - .width = box->x2 - box->x1, - .height = box->y2 - box->y1, - }; - - pixman_region32_fini(®ion); - - return geometry; -} - -/** Copy surface contents to system memory. - * - * \param surface The surface to copy from. - * \param target Pointer to the target memory buffer. - * \param size Size of the target buffer in bytes. - * \param src_x X location on contents to copy from. - * \param src_y Y location on contents to copy from. - * \param width Width in pixels of the area to copy. - * \param height Height in pixels of the area to copy. - * \return 0 for success, -1 for failure. - * - * Surface contents are maintained by the renderer. They can be in a - * reserved weston_buffer or as a copy, e.g. a GL texture, or something - * else. - * - * Surface contents are copied into memory pointed to by target, - * which has size bytes of space available. The target memory - * may be larger than needed, but being smaller returns an error. - * The extra bytes in target may or may not be written; their content is - * unspecified. Size must be large enough to hold the image. - * - * The image in the target memory will be arranged in rows from - * top to bottom, and pixels on a row from left to right. The pixel - * format is PIXMAN_a8b8g8r8, 4 bytes per pixel, and stride is exactly - * width * 4. - * - * Parameters src_x and src_y define the upper-left corner in buffer - * coordinates (pixels) to copy from. Parameters width and height - * define the size of the area to copy in pixels. - * - * The rectangle defined by src_x, src_y, width, height must fit in - * the surface contents. Otherwise an error is returned. - * - * Use weston_surface_get_content_size to determine the content size; the - * needed target buffer size and rectangle limits. - * - * CURRENT IMPLEMENTATION RESTRICTIONS: - * - the machine must be little-endian due to Pixman formats. - * - * NOTE: Pixman formats are premultiplied. - */ -WL_EXPORT int -weston_surface_copy_content(struct weston_surface *surface, - void *target, size_t size, - int src_x, int src_y, - int width, int height) -{ - struct weston_renderer *rer = surface->compositor->renderer; - int cw, ch; - const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */ - - if (!rer->surface_copy_content) - return -1; - - weston_surface_get_content_size(surface, &cw, &ch); - - if (src_x < 0 || src_y < 0) - return -1; - - if (width <= 0 || height <= 0) - return -1; - - if (src_x + width > cw || src_y + height > ch) - return -1; - - if (width * bytespp * height > size) - return -1; - - return rer->surface_copy_content(surface, target, size, - src_x, src_y, width, height); -} - -static void -subsurface_set_position(struct wl_client *client, - struct wl_resource *resource, int32_t x, int32_t y) -{ - struct weston_subsurface *sub = wl_resource_get_user_data(resource); - - if (!sub) - return; - - sub->position.x = x; - sub->position.y = y; - sub->position.set = 1; -} - -static struct weston_subsurface * -subsurface_find_sibling(struct weston_subsurface *sub, - struct weston_surface *surface) -{ - struct weston_surface *parent = sub->parent; - struct weston_subsurface *sibling; - - wl_list_for_each(sibling, &parent->subsurface_list, parent_link) { - if (sibling->surface == surface && sibling != sub) - return sibling; - } - - return NULL; -} - -static struct weston_subsurface * -subsurface_sibling_check(struct weston_subsurface *sub, - struct weston_surface *surface, - const char *request) -{ - struct weston_subsurface *sibling; - - sibling = subsurface_find_sibling(sub, surface); - if (!sibling) { - wl_resource_post_error(sub->resource, - WL_SUBSURFACE_ERROR_BAD_SURFACE, - "%s: wl_surface@%d is not a parent or sibling", - request, wl_resource_get_id(surface->resource)); - return NULL; - } - - assert(sibling->parent == sub->parent); - - return sibling; -} - -static void -subsurface_place_above(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *sibling_resource) -{ - struct weston_subsurface *sub = wl_resource_get_user_data(resource); - struct weston_surface *surface = - wl_resource_get_user_data(sibling_resource); - struct weston_subsurface *sibling; - - if (!sub) - return; - - sibling = subsurface_sibling_check(sub, surface, "place_above"); - if (!sibling) - return; - - wl_list_remove(&sub->parent_link_pending); - wl_list_insert(sibling->parent_link_pending.prev, - &sub->parent_link_pending); - - sub->reordered = true; -} - -static void -subsurface_place_below(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *sibling_resource) -{ - struct weston_subsurface *sub = wl_resource_get_user_data(resource); - struct weston_surface *surface = - wl_resource_get_user_data(sibling_resource); - struct weston_subsurface *sibling; - - if (!sub) - return; - - sibling = subsurface_sibling_check(sub, surface, "place_below"); - if (!sibling) - return; - - wl_list_remove(&sub->parent_link_pending); - wl_list_insert(&sibling->parent_link_pending, - &sub->parent_link_pending); - - sub->reordered = true; -} - -static void -subsurface_set_sync(struct wl_client *client, struct wl_resource *resource) -{ - struct weston_subsurface *sub = wl_resource_get_user_data(resource); - - if (sub) - sub->synchronized = 1; -} - -static void -subsurface_set_desync(struct wl_client *client, struct wl_resource *resource) -{ - struct weston_subsurface *sub = wl_resource_get_user_data(resource); - - if (sub && sub->synchronized) { - sub->synchronized = 0; - - /* If sub became effectively desynchronized, flush. */ - if (!weston_subsurface_is_synchronized(sub)) - weston_subsurface_synchronized_commit(sub); - } -} - -static void -weston_subsurface_unlink_parent(struct weston_subsurface *sub) -{ - wl_list_remove(&sub->parent_link); - wl_list_remove(&sub->parent_link_pending); - wl_list_remove(&sub->parent_destroy_listener.link); - sub->parent = NULL; -} - -static void -weston_subsurface_destroy(struct weston_subsurface *sub); - -static void -subsurface_handle_surface_destroy(struct wl_listener *listener, void *data) -{ - struct weston_subsurface *sub = - container_of(listener, struct weston_subsurface, - surface_destroy_listener); - assert(data == sub->surface); - - /* The protocol object (wl_resource) is left inert. */ - if (sub->resource) - wl_resource_set_user_data(sub->resource, NULL); - - weston_subsurface_destroy(sub); -} - -static void -subsurface_handle_parent_destroy(struct wl_listener *listener, void *data) -{ - struct weston_subsurface *sub = - container_of(listener, struct weston_subsurface, - parent_destroy_listener); - assert(data == sub->parent); - assert(sub->surface != sub->parent); - - if (weston_surface_is_mapped(sub->surface)) - weston_surface_unmap(sub->surface); - - weston_subsurface_unlink_parent(sub); -} - -static void -subsurface_resource_destroy(struct wl_resource *resource) -{ - struct weston_subsurface *sub = wl_resource_get_user_data(resource); - - if (sub) - weston_subsurface_destroy(sub); -} - -static void -subsurface_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -weston_subsurface_link_parent(struct weston_subsurface *sub, - struct weston_surface *parent) -{ - sub->parent = parent; - sub->parent_destroy_listener.notify = subsurface_handle_parent_destroy; - wl_signal_add(&parent->destroy_signal, - &sub->parent_destroy_listener); - - wl_list_insert(&parent->subsurface_list, &sub->parent_link); - wl_list_insert(&parent->subsurface_list_pending, - &sub->parent_link_pending); -} - -static void -weston_subsurface_link_surface(struct weston_subsurface *sub, - struct weston_surface *surface) -{ - sub->surface = surface; - sub->surface_destroy_listener.notify = - subsurface_handle_surface_destroy; - wl_signal_add(&surface->destroy_signal, - &sub->surface_destroy_listener); -} - -static void -weston_subsurface_destroy(struct weston_subsurface *sub) -{ - struct weston_view *view, *next; - - assert(sub->surface); - - if (sub->resource) { - assert(weston_surface_to_subsurface(sub->surface) == sub); - assert(sub->parent_destroy_listener.notify == - subsurface_handle_parent_destroy); - - wl_list_for_each_safe(view, next, &sub->surface->views, surface_link) { - weston_view_unmap(view); - weston_view_destroy(view); - } - - if (sub->parent) - weston_subsurface_unlink_parent(sub); - - weston_surface_state_fini(&sub->cached); - weston_buffer_reference(&sub->cached_buffer_ref, NULL); - - sub->surface->committed = NULL; - sub->surface->committed_private = NULL; - weston_surface_set_label_func(sub->surface, NULL); - } else { - /* the dummy weston_subsurface for the parent itself */ - assert(sub->parent_destroy_listener.notify == NULL); - wl_list_remove(&sub->parent_link); - wl_list_remove(&sub->parent_link_pending); - } - - wl_list_remove(&sub->surface_destroy_listener.link); - free(sub); -} - -static const struct wl_subsurface_interface subsurface_implementation = { - subsurface_destroy, - subsurface_set_position, - subsurface_place_above, - subsurface_place_below, - subsurface_set_sync, - subsurface_set_desync -}; - -static struct weston_subsurface * -weston_subsurface_create(uint32_t id, struct weston_surface *surface, - struct weston_surface *parent) -{ - struct weston_subsurface *sub; - struct wl_client *client = wl_resource_get_client(surface->resource); - - sub = zalloc(sizeof *sub); - if (sub == NULL) - return NULL; - - wl_list_init(&sub->unused_views); - - sub->resource = - wl_resource_create(client, &wl_subsurface_interface, 1, id); - if (!sub->resource) { - free(sub); - return NULL; - } - - wl_resource_set_implementation(sub->resource, - &subsurface_implementation, - sub, subsurface_resource_destroy); - weston_subsurface_link_surface(sub, surface); - weston_subsurface_link_parent(sub, parent); - weston_surface_state_init(&sub->cached); - sub->cached_buffer_ref.buffer = NULL; - sub->synchronized = 1; - - return sub; -} - -/* Create a dummy subsurface for having the parent itself in its - * sub-surface lists. Makes stacking order manipulation easy. - */ -static struct weston_subsurface * -weston_subsurface_create_for_parent(struct weston_surface *parent) -{ - struct weston_subsurface *sub; - - sub = zalloc(sizeof *sub); - if (sub == NULL) - return NULL; - - weston_subsurface_link_surface(sub, parent); - sub->parent = parent; - wl_list_insert(&parent->subsurface_list, &sub->parent_link); - wl_list_insert(&parent->subsurface_list_pending, - &sub->parent_link_pending); - - return sub; -} - -static void -subcompositor_get_subsurface(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, - struct wl_resource *surface_resource, - struct wl_resource *parent_resource) -{ - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - struct weston_surface *parent = - wl_resource_get_user_data(parent_resource); - struct weston_subsurface *sub; - static const char where[] = "get_subsurface: wl_subsurface@"; - - if (surface == parent) { - wl_resource_post_error(resource, - WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%d: wl_surface@%d cannot be its own parent", - where, id, wl_resource_get_id(surface_resource)); - return; - } - - if (weston_surface_to_subsurface(surface)) { - wl_resource_post_error(resource, - WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%d: wl_surface@%d is already a sub-surface", - where, id, wl_resource_get_id(surface_resource)); - return; - } - - if (weston_surface_set_role(surface, "wl_subsurface", resource, - WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE) < 0) - return; - - if (weston_surface_get_main_surface(parent) == surface) { - wl_resource_post_error(resource, - WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%d: wl_surface@%d is an ancestor of parent", - where, id, wl_resource_get_id(surface_resource)); - return; - } - - /* make sure the parent is in its own list */ - if (wl_list_empty(&parent->subsurface_list)) { - if (!weston_subsurface_create_for_parent(parent)) { - wl_resource_post_no_memory(resource); - return; - } - } - - sub = weston_subsurface_create(id, surface, parent); - if (!sub) { - wl_resource_post_no_memory(resource); - return; - } - - surface->committed = subsurface_committed; - surface->committed_private = sub; - weston_surface_set_label_func(surface, subsurface_get_label); -} - -static void -subcompositor_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct wl_subcompositor_interface subcompositor_interface = { - subcompositor_destroy, - subcompositor_get_subsurface -}; - -static void -bind_subcompositor(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct weston_compositor *compositor = data; - struct wl_resource *resource; - - resource = - wl_resource_create(client, &wl_subcompositor_interface, 1, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(resource, &subcompositor_interface, - compositor, NULL); -} - -/** Set a DPMS mode on all of the compositor's outputs - * - * \param compositor The compositor instance - * \param state The DPMS state the outputs will be set to - */ -static void -weston_compositor_dpms(struct weston_compositor *compositor, - enum dpms_enum state) -{ - struct weston_output *output; - - wl_list_for_each(output, &compositor->output_list, link) - if (output->set_dpms) - output->set_dpms(output, state); -} - -/** Restores the compositor to active status - * - * \param compositor The compositor instance - * - * If the compositor was in a sleeping mode, all outputs are powered - * back on via DPMS. Otherwise if the compositor was inactive - * (idle/locked, offscreen, or sleeping) then the compositor's wake - * signal will fire. - * - * Restarts the idle timer. - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_wake(struct weston_compositor *compositor) -{ - uint32_t old_state = compositor->state; - - /* The state needs to be changed before emitting the wake - * signal because that may try to schedule a repaint which - * will not work if the compositor is still sleeping */ - compositor->state = WESTON_COMPOSITOR_ACTIVE; - - switch (old_state) { - case WESTON_COMPOSITOR_SLEEPING: - case WESTON_COMPOSITOR_IDLE: - case WESTON_COMPOSITOR_OFFSCREEN: - weston_compositor_dpms(compositor, WESTON_DPMS_ON); - wl_signal_emit(&compositor->wake_signal, compositor); - /* fall through */ - default: - wl_event_source_timer_update(compositor->idle_source, - compositor->idle_time * 1000); - } -} - -/** Turns off rendering and frame events for the compositor. - * - * \param compositor The compositor instance - * - * This is used for example to prevent further rendering while the - * compositor is shutting down. - * - * Stops the idle timer. - * - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_offscreen(struct weston_compositor *compositor) -{ - switch (compositor->state) { - case WESTON_COMPOSITOR_OFFSCREEN: - return; - case WESTON_COMPOSITOR_SLEEPING: - default: - compositor->state = WESTON_COMPOSITOR_OFFSCREEN; - wl_event_source_timer_update(compositor->idle_source, 0); - } -} - -/** Powers down all attached output devices - * - * \param compositor The compositor instance - * - * Causes rendering to the outputs to cease, and no frame events to be - * sent. Only powers down the outputs if the compositor is not already - * in sleep mode. - * - * Stops the idle timer. - * - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_sleep(struct weston_compositor *compositor) -{ - if (compositor->state == WESTON_COMPOSITOR_SLEEPING) - return; - - wl_event_source_timer_update(compositor->idle_source, 0); - compositor->state = WESTON_COMPOSITOR_SLEEPING; - weston_compositor_dpms(compositor, WESTON_DPMS_OFF); -} - -/** Sets compositor to idle mode - * - * \param data The compositor instance - * - * This is called when the idle timer fires. Once the compositor is in - * idle mode it requires a wake action (e.g. via - * weston_compositor_wake()) to restore it. The compositor's - * idle_signal will be triggered when the idle event occurs. - * - * Idleness can be inhibited by setting the compositor's idle_inhibit - * property. - */ -static int -idle_handler(void *data) -{ - struct weston_compositor *compositor = data; - - if (compositor->idle_inhibit) - return 1; - - compositor->state = WESTON_COMPOSITOR_IDLE; - wl_signal_emit(&compositor->idle_signal, compositor); - - return 1; -} - -WL_EXPORT void -weston_plane_init(struct weston_plane *plane, - struct weston_compositor *ec, - int32_t x, int32_t y) -{ - pixman_region32_init(&plane->damage); - pixman_region32_init(&plane->clip); - plane->x = x; - plane->y = y; - plane->compositor = ec; - - /* Init the link so that the call to wl_list_remove() when releasing - * the plane without ever stacking doesn't lead to a crash */ - wl_list_init(&plane->link); -} - -WL_EXPORT void -weston_plane_release(struct weston_plane *plane) -{ - struct weston_view *view; - - pixman_region32_fini(&plane->damage); - pixman_region32_fini(&plane->clip); - - wl_list_for_each(view, &plane->compositor->view_list, link) { - if (view->plane == plane) - view->plane = NULL; - } - - wl_list_remove(&plane->link); -} - -/** weston_compositor_stack_plane - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_stack_plane(struct weston_compositor *ec, - struct weston_plane *plane, - struct weston_plane *above) -{ - if (above) - wl_list_insert(above->link.prev, &plane->link); - else - wl_list_insert(&ec->plane_list, &plane->link); -} - -static void -output_release(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct wl_output_interface output_interface = { - output_release, -}; - - -static void unbind_resource(struct wl_resource *resource) -{ - wl_list_remove(wl_resource_get_link(resource)); -} - -static void -bind_output(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct weston_head *head = data; - struct weston_output *output = head->output; - struct weston_mode *mode; - struct wl_resource *resource; - - resource = wl_resource_create(client, &wl_output_interface, - version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_list_insert(&head->resource_list, wl_resource_get_link(resource)); - wl_resource_set_implementation(resource, &output_interface, head, - unbind_resource); - - assert(output); - wl_output_send_geometry(resource, - output->x, - output->y, - head->mm_width, - head->mm_height, - head->subpixel, - head->make, head->model, - output->transform); - if (version >= WL_OUTPUT_SCALE_SINCE_VERSION) - wl_output_send_scale(resource, - output->current_scale); - - wl_list_for_each (mode, &output->mode_list, link) { - wl_output_send_mode(resource, - mode->flags, - mode->width, - mode->height, - mode->refresh); - } - - if (version >= WL_OUTPUT_DONE_SINCE_VERSION) - wl_output_send_done(resource); -} - -static void -weston_head_add_global(struct weston_head *head) -{ - head->global = wl_global_create(head->compositor->wl_display, - &wl_output_interface, 3, - head, bind_output); -} - -/** Remove the global wl_output protocol object - * - * \param head The head whose global to remove. - * - * Also orphans the wl_resources for this head (wl_output). - */ -static void -weston_head_remove_global(struct weston_head *head) -{ - struct wl_resource *resource, *tmp; - - if (head->global) - wl_global_destroy(head->global); - head->global = NULL; - - wl_resource_for_each_safe(resource, tmp, &head->resource_list) { - unbind_resource(resource); - wl_resource_set_destructor(resource, NULL); - wl_resource_set_user_data(resource, NULL); - } - - wl_resource_for_each(resource, &head->xdg_output_resource_list) { - /* It's sufficient to unset the destructor, then the list elements - * won't be accessed. - */ - wl_resource_set_destructor(resource, NULL); - } - wl_list_init(&head->xdg_output_resource_list); -} - -/** Get the backing object of wl_output - * - * \param resource A wl_output protocol object. - * \return The backing object (user data) of a wl_resource representing a - * wl_output protocol object. - * - * \ingroup head - */ -WL_EXPORT struct weston_head * -weston_head_from_resource(struct wl_resource *resource) -{ - assert(wl_resource_instance_of(resource, &wl_output_interface, - &output_interface)); - - return wl_resource_get_user_data(resource); -} - -/** Initialize a pre-allocated weston_head - * - * \param head The head to initialize. - * \param name The head name, e.g. the connector name or equivalent. - * - * The head will be safe to attach, detach and release. - * - * The name is used in logs, and can be used by compositors as a configuration - * identifier. - * - * \ingroup head - * \internal - */ -WL_EXPORT void -weston_head_init(struct weston_head *head, const char *name) -{ - /* Add some (in)sane defaults which can be used - * for checking if an output was properly configured - */ - memset(head, 0, sizeof *head); - - wl_list_init(&head->compositor_link); - wl_signal_init(&head->destroy_signal); - wl_list_init(&head->output_link); - wl_list_init(&head->resource_list); - wl_list_init(&head->xdg_output_resource_list); - head->name = strdup(name); - head->current_protection = WESTON_HDCP_DISABLE; -} - -/** Send output heads changed signal - * - * \param output The output that changed. - * - * Notify that the enabled output gained and/or lost heads, or that the - * associated heads may have changed their connection status. This does not - * include cases where the output becomes enabled or disabled. The registered - * callbacks are called after the change has successfully happened. - * - * If connection status change causes the compositor to attach or detach a head - * to an enabled output, the registered callbacks may be called multiple times. - * - * \ingroup output - */ -static void -weston_output_emit_heads_changed(struct weston_output *output) -{ - wl_signal_emit(&output->compositor->output_heads_changed_signal, - output); -} - -/** Idle task for emitting heads_changed_signal */ -static void -weston_compositor_call_heads_changed(void *data) -{ - struct weston_compositor *compositor = data; - struct weston_head *head; - - compositor->heads_changed_source = NULL; - - wl_signal_emit(&compositor->heads_changed_signal, compositor); - - wl_list_for_each(head, &compositor->head_list, compositor_link) { - if (head->output && head->output->enabled) - weston_output_emit_heads_changed(head->output); - } -} - -/** Schedule a call on idle to heads_changed callback - * - * \param compositor The Compositor. - * - * \ingroup compositor - * \internal - */ -static void -weston_compositor_schedule_heads_changed(struct weston_compositor *compositor) -{ - struct wl_event_loop *loop; - - if (compositor->heads_changed_source) - return; - - loop = wl_display_get_event_loop(compositor->wl_display); - compositor->heads_changed_source = wl_event_loop_add_idle(loop, - weston_compositor_call_heads_changed, compositor); -} - -/** Register a new head - * - * \param compositor The compositor. - * \param head The head to register, must not be already registered. - * - * This signals the core that a new head has become available, leading to - * heads_changed hook being called later. - * - * \ingroup compositor - * \internal - */ -WL_EXPORT void -weston_compositor_add_head(struct weston_compositor *compositor, - struct weston_head *head) -{ - assert(wl_list_empty(&head->compositor_link)); - assert(head->name); - - wl_list_insert(compositor->head_list.prev, &head->compositor_link); - head->compositor = compositor; - weston_compositor_schedule_heads_changed(compositor); -} - -/** Adds a listener to be called when heads change - * - * \param compositor The compositor. - * \param listener The listener to add. - * - * The listener notify function argument is weston_compositor. - * - * The listener function will be called after heads are added or their - * connection status has changed. Several changes may be accumulated into a - * single call. The user is expected to iterate over the existing heads and - * check their statuses to find out what changed. - * - * \sa weston_compositor_iterate_heads, weston_head_is_connected, - * weston_head_is_enabled - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_add_heads_changed_listener(struct weston_compositor *compositor, - struct wl_listener *listener) -{ - wl_signal_add(&compositor->heads_changed_signal, listener); -} - -/** Iterate over available heads - * - * \param compositor The compositor. - * \param iter The iterator, or NULL for start. - * \return The next available head in the list. - * - * Returns all available heads, regardless of being connected or enabled. - * - * You can iterate over all heads as follows: - * \code - * struct weston_head *head = NULL; - * - * while ((head = weston_compositor_iterate_heads(compositor, head))) { - * ... - * } - * \endcode - * - * If you cause \c iter to be removed from the list, you cannot use it to - * continue iterating. Removing any other item is safe. - * - * \ingroup compositor - */ -WL_EXPORT struct weston_head * -weston_compositor_iterate_heads(struct weston_compositor *compositor, - struct weston_head *iter) -{ - struct wl_list *list = &compositor->head_list; - struct wl_list *node; - - assert(compositor); - assert(!iter || iter->compositor == compositor); - - if (iter) - node = iter->compositor_link.next; - else - node = list->next; - - assert(node); - assert(!iter || node != &iter->compositor_link); - - if (node == list) - return NULL; - - return container_of(node, struct weston_head, compositor_link); -} - -/** Iterate over attached heads - * - * \param output The output whose heads to iterate. - * \param iter The iterator, or NULL for start. - * \return The next attached head in the list. - * - * Returns all heads currently attached to the output. - * - * You can iterate over all heads as follows: - * \code - * struct weston_head *head = NULL; - * - * while ((head = weston_output_iterate_heads(output, head))) { - * ... - * } - * \endcode - * - * If you cause \c iter to be removed from the list, you cannot use it to - * continue iterating. Removing any other item is safe. - * - * \ingroup ouput - */ -WL_EXPORT struct weston_head * -weston_output_iterate_heads(struct weston_output *output, - struct weston_head *iter) -{ - struct wl_list *list = &output->head_list; - struct wl_list *node; - - assert(output); - assert(!iter || iter->output == output); - - if (iter) - node = iter->output_link.next; - else - node = list->next; - - assert(node); - assert(!iter || node != &iter->output_link); - - if (node == list) - return NULL; - - return container_of(node, struct weston_head, output_link); -} - -/** Attach a head to an output - * - * \param output The output to attach to. - * \param head The head that is not yet attached. - * \return 0 on success, -1 on failure. - * - * Attaches the given head to the output. All heads of an output are clones - * and share the resolution and timings. - * - * Cloning heads this way uses less resources than creating an output for - * each head, but is not always possible due to environment, driver and hardware - * limitations. - * - * On failure, the head remains unattached. Success of this function does not - * guarantee the output configuration is actually valid. The final checks are - * made on weston_output_enable() unless the output was already enabled. - * - * \ingroup output - */ -WL_EXPORT int -weston_output_attach_head(struct weston_output *output, - struct weston_head *head) -{ - char *head_names; - - if (!wl_list_empty(&head->output_link)) - return -1; - - if (output->attach_head) { - if (output->attach_head(output, head) < 0) - return -1; - } else if (!wl_list_empty(&output->head_list)) { - /* No support for clones in the legacy path. */ - return -1; - } - - head->output = output; - wl_list_insert(output->head_list.prev, &head->output_link); - - if (output->enabled) { - weston_head_add_global(head); - - head_names = weston_output_create_heads_string(output); - weston_log("Output '%s' updated to have head(s) %s\n", - output->name, head_names); - free(head_names); - - weston_output_emit_heads_changed(output); - } - - return 0; -} - -/** Detach a head from its output - * - * \param head The head to detach. - * - * It is safe to detach a non-attached head. - * - * If the head is attached to an enabled output and the output will be left - * with no heads, the output will be disabled. - * - * \ingroup head - * \sa weston_output_disable - */ -WL_EXPORT void -weston_head_detach(struct weston_head *head) -{ - struct weston_output *output = head->output; - char *head_names; - - wl_list_remove(&head->output_link); - wl_list_init(&head->output_link); - head->output = NULL; - - if (!output) - return; - - if (output->detach_head) - output->detach_head(output, head); - - if (output->enabled) { - weston_head_remove_global(head); - - if (wl_list_empty(&output->head_list)) { - weston_log("Output '%s' no heads left, disabling.\n", - output->name); - weston_output_disable(output); - } else { - head_names = weston_output_create_heads_string(output); - weston_log("Output '%s' updated to have head(s) %s\n", - output->name, head_names); - free(head_names); - - weston_output_emit_heads_changed(output); - } - } -} - -/** Destroy a head - * - * \param head The head to be released. - * - * Destroys the head. The caller is responsible for freeing the memory pointed - * to by \c head. - * - * \ingroup head - * \internal - */ -WL_EXPORT void -weston_head_release(struct weston_head *head) -{ - wl_signal_emit(&head->destroy_signal, head); - - weston_head_detach(head); - - free(head->make); - free(head->model); - free(head->serial_number); - free(head->name); - - wl_list_remove(&head->compositor_link); -} - -static void -weston_head_set_device_changed(struct weston_head *head) -{ - head->device_changed = true; - - if (head->compositor) - weston_compositor_schedule_heads_changed(head->compositor); -} - -/** String equal comparison with NULLs being equal */ -static bool -str_null_eq(const char *a, const char *b) -{ - if (!a && !b) - return true; - - if (!!a != !!b) - return false; - - return strcmp(a, b) == 0; -} - -/** Store monitor make, model and serial number - * - * \param head The head to modify. - * \param make The monitor make. If EDID is available, the PNP ID. Otherwise - * any string, or NULL for none. - * \param model The monitor model or name, or a made-up string, or NULL for - * none. - * \param serialno The monitor serial number, a made-up string, or NULL for - * none. - * - * This may set the device_changed flag. - * - * \ingroup head - * \internal - */ -WL_EXPORT void -weston_head_set_monitor_strings(struct weston_head *head, - const char *make, - const char *model, - const char *serialno) -{ - if (str_null_eq(head->make, make) && - str_null_eq(head->model, model) && - str_null_eq(head->serial_number, serialno)) - return; - - free(head->make); - free(head->model); - free(head->serial_number); - - head->make = make ? strdup(make) : NULL; - head->model = model ? strdup(model) : NULL; - head->serial_number = serialno ? strdup(serialno) : NULL; - - weston_head_set_device_changed(head); -} - -/** Store display non-desktop status - * - * \param head The head to modify. - * \param non_desktop Whether the head connects to a non-desktop display. - * - * \ingroup head - * \internal - */ -WL_EXPORT void -weston_head_set_non_desktop(struct weston_head *head, bool non_desktop) -{ - if (head->non_desktop == non_desktop) - return; - - head->non_desktop = non_desktop; - - weston_head_set_device_changed(head); -} - -/** Store physical image size - * - * \param head The head to modify. - * \param mm_width Image area width in millimeters. - * \param mm_height Image area height in millimeters. - * - * This may set the device_changed flag. - * - * \ingroup head - * \internal - */ -WL_EXPORT void -weston_head_set_physical_size(struct weston_head *head, - int32_t mm_width, int32_t mm_height) -{ - if (head->mm_width == mm_width && - head->mm_height == mm_height) - return; - - head->mm_width = mm_width; - head->mm_height = mm_height; - - weston_head_set_device_changed(head); -} - -/** Store monitor sub-pixel layout - * - * \param head The head to modify. - * \param sp Sub-pixel layout. The possible values are: - * - WL_OUTPUT_SUBPIXEL_UNKNOWN, - * - WL_OUTPUT_SUBPIXEL_NONE, - * - WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB, - * - WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR, - * - WL_OUTPUT_SUBPIXEL_VERTICAL_RGB, - * - WL_OUTPUT_SUBPIXEL_VERTICAL_BGR - * - * This may set the device_changed flag. - * - * \ingroup head - * \internal - */ -WL_EXPORT void -weston_head_set_subpixel(struct weston_head *head, - enum wl_output_subpixel sp) -{ - if (head->subpixel == sp) - return; - - head->subpixel = sp; - - weston_head_set_device_changed(head); -} - -/** Mark the monitor as internal - * - * This is used for embedded screens, like laptop panels. - * - * \param head The head to mark as internal. - * - * By default a head is external. The type is often inferred from the physical - * connector type. - * - * \ingroup head - * \internal - */ -WL_EXPORT void -weston_head_set_internal(struct weston_head *head) -{ - head->connection_internal = true; -} - -/** Store connector status - * - * \param head The head to modify. - * \param connected Whether the head is connected. - * - * Connectors are created as disconnected. This function can be used to - * set the connector status. - * - * The status should be set to true when a physical connector is connected to - * a video sink device like a monitor and to false when the connector is - * disconnected. For nested backends, the connection status should reflect the - * connection to the parent display server. - * - * When the connection status changes, it schedules a call to the heads_changed - * hook and sets the device_changed flag. - * - * \sa weston_compositor_set_heads_changed_cb - * \ingroup head - * \internal - */ -WL_EXPORT void -weston_head_set_connection_status(struct weston_head *head, bool connected) -{ - if (head->connected == connected) - return; - - head->connected = connected; - - weston_head_set_device_changed(head); -} - -static void -weston_output_compute_protection(struct weston_output *output) -{ - struct weston_head *head; - enum weston_hdcp_protection op_protection; - bool op_protection_valid = false; - struct weston_compositor *wc = output->compositor; - - wl_list_for_each(head, &output->head_list, output_link) { - if (!op_protection_valid) { - op_protection = head->current_protection; - op_protection_valid = true; - } - if (head->current_protection < op_protection) - op_protection = head->current_protection; - } - - if (!op_protection_valid) - op_protection = WESTON_HDCP_DISABLE; - - if (output->current_protection != op_protection) { - output->current_protection = op_protection; - weston_output_damage(output); - weston_schedule_surface_protection_update(wc); - } -} - -WL_EXPORT void -weston_head_set_content_protection_status(struct weston_head *head, - enum weston_hdcp_protection status) -{ - head->current_protection = status; - if (head->output) - weston_output_compute_protection(head->output); -} - -/** Is the head currently connected? - * - * \param head The head to query. - * \return Connection status. - * - * Returns true if the head is physically connected to a monitor, or in - * case of a nested backend returns true when there is a connection to the - * parent display server. - * - * This is independent from the head being enabled. - * - * \sa weston_head_is_enabled - * \ingroup head - */ -WL_EXPORT bool -weston_head_is_connected(struct weston_head *head) -{ - return head->connected; -} - -/** Is the head currently enabled? - * - * \param head The head to query. - * \return Video status. - * - * Returns true if the head is currently transmitting a video stream. - * - * This is independent of the head being connected. - * - * \sa weston_head_is_connected - * \ingroup head - */ -WL_EXPORT bool -weston_head_is_enabled(struct weston_head *head) -{ - if (!head->output) - return false; - - return head->output->enabled; -} - -/** Has the device information changed? - * - * \param head The head to query. - * \return True if the device information has changed since last reset. - * - * The information about the connected display device, e.g. a monitor, may - * change without being disconnected in between. Changing information - * causes a call to the heads_changed hook. - * - * The information includes make, model, serial number, physical size, - * and sub-pixel type. The connection status is also included. - * - * \sa weston_head_reset_device_changed, weston_compositor_set_heads_changed_cb - * \ingroup head - */ -WL_EXPORT bool -weston_head_is_device_changed(struct weston_head *head) -{ - return head->device_changed; -} - -/** Does the head represent a non-desktop display? - * - * \param head The head to query. - * \return True if the device is a non-desktop display. - * - * Non-desktop heads are not attached to outputs by default. - * This stops weston from extending the desktop onto head mounted displays. - * - * \ingroup head - */ -WL_EXPORT bool -weston_head_is_non_desktop(struct weston_head *head) -{ - return head->non_desktop; -} - -/** Acknowledge device information change - * - * \param head The head to acknowledge. - * - * Clears the device changed flag on this head. When a compositor has processed - * device information, it should call this to be able to notice further - * changes. - * - * \sa weston_head_is_device_changed - * \ingroup head - */ -WL_EXPORT void -weston_head_reset_device_changed(struct weston_head *head) -{ - head->device_changed = false; -} - -/** Get the name of a head - * - * \param head The head to query. - * \return The head's name, not NULL. - * - * The name depends on the backend. The DRM backend uses connector names, - * other backends may use hardcoded names or user-given names. - * - * \ingroup head - */ -WL_EXPORT const char * -weston_head_get_name(struct weston_head *head) -{ - return head->name; -} - -/** Get the output the head is attached to - * - * \param head The head to query. - * \return The output the head is attached to, or NULL if detached. - * \ingroup head - */ -WL_EXPORT struct weston_output * -weston_head_get_output(struct weston_head *head) -{ - return head->output; -} - -/** Add destroy callback for a head - * - * \param head The head to watch for. - * \param listener The listener to add. The \c notify member must be set. - * - * Heads may get destroyed for various reasons by the backends. If a head is - * attached to an output, the compositor should listen for head destruction - * and reconfigure or destroy the output if necessary. - * - * The destroy callbacks will be called on weston_head destruction before any - * automatic detaching from an associated weston_output and before any - * weston_head information is lost. - * - * The \c data argument to the notify callback is the weston_head being - * destroyed. - * - * \ingroup head - */ -WL_EXPORT void -weston_head_add_destroy_listener(struct weston_head *head, - struct wl_listener *listener) -{ - wl_signal_add(&head->destroy_signal, listener); -} - -/** Look up destroy listener for a head - * - * \param head The head to query. - * \param notify The notify function used used for the added destroy listener. - * \return The listener, or NULL if not found. - * - * This looks up the previously added destroy listener struct based on the - * notify function it has. The listener can be used to access user data - * through \c container_of(). - * - * \sa wl_signal_get() - * \ingroup head - */ -WL_EXPORT struct wl_listener * -weston_head_get_destroy_listener(struct weston_head *head, - wl_notify_func_t notify) -{ - return wl_signal_get(&head->destroy_signal, notify); -} - -/* Move other outputs when one is resized so the space remains contiguous. */ -static void -weston_compositor_reflow_outputs(struct weston_compositor *compositor, - struct weston_output *resized_output, int delta_width) -{ - struct weston_output *output; - bool start_resizing = false; - - if (!delta_width) - return; - - wl_list_for_each(output, &compositor->output_list, link) { - if (output == resized_output) { - start_resizing = true; - continue; - } - - if (start_resizing) { - weston_output_move(output, output->x + delta_width, output->y); - output->dirty = 1; - } - } -} - -static void -weston_output_update_matrix(struct weston_output *output) -{ - float magnification; - - weston_matrix_init(&output->matrix); - weston_matrix_translate(&output->matrix, -output->x, -output->y, 0); - - if (output->zoom.active) { - magnification = 1 / (1 - output->zoom.spring_z.current); - weston_output_update_zoom(output); - weston_matrix_translate(&output->matrix, -output->zoom.trans_x, - -output->zoom.trans_y, 0); - weston_matrix_scale(&output->matrix, magnification, - magnification, 1.0); - } - - switch (output->transform) { - case WL_OUTPUT_TRANSFORM_FLIPPED: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - weston_matrix_translate(&output->matrix, -output->width, 0, 0); - weston_matrix_scale(&output->matrix, -1, 1, 1); - break; - } - - switch (output->transform) { - default: - case WL_OUTPUT_TRANSFORM_NORMAL: - case WL_OUTPUT_TRANSFORM_FLIPPED: - break; - case WL_OUTPUT_TRANSFORM_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - weston_matrix_translate(&output->matrix, 0, -output->height, 0); - weston_matrix_rotate_xy(&output->matrix, 0, 1); - break; - case WL_OUTPUT_TRANSFORM_180: - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - weston_matrix_translate(&output->matrix, - -output->width, -output->height, 0); - weston_matrix_rotate_xy(&output->matrix, -1, 0); - break; - case WL_OUTPUT_TRANSFORM_270: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - weston_matrix_translate(&output->matrix, -output->width, 0, 0); - weston_matrix_rotate_xy(&output->matrix, 0, -1); - break; - } - - if (output->current_scale != 1) - weston_matrix_scale(&output->matrix, - output->current_scale, - output->current_scale, 1); - - output->dirty = 0; - - weston_matrix_invert(&output->inverse_matrix, &output->matrix); -} - -static void -weston_output_transform_scale_init(struct weston_output *output, uint32_t transform, uint32_t scale) -{ - output->transform = transform; - output->native_scale = scale; - output->current_scale = scale; - - convert_size_by_transform_scale(&output->width, &output->height, - output->current_mode->width, - output->current_mode->height, - transform, scale); -} - -static void -weston_output_init_geometry(struct weston_output *output, int x, int y) -{ - output->x = x; - output->y = y; - - pixman_region32_fini(&output->region); - pixman_region32_init_rect(&output->region, x, y, - output->width, - output->height); -} - -/** - * \ingroup output - */ -WL_EXPORT void -weston_output_move(struct weston_output *output, int x, int y) -{ - struct weston_head *head; - struct wl_resource *resource; - int ver; - - output->move_x = x - output->x; - output->move_y = y - output->y; - - if (output->move_x == 0 && output->move_y == 0) - return; - - weston_output_init_geometry(output, x, y); - - output->dirty = 1; - - /* Move views on this output. */ - wl_signal_emit(&output->compositor->output_moved_signal, output); - - /* Notify clients of the change for output position. */ - wl_list_for_each(head, &output->head_list, output_link) { - wl_resource_for_each(resource, &head->resource_list) { - wl_output_send_geometry(resource, - output->x, - output->y, - head->mm_width, - head->mm_height, - head->subpixel, - head->make, - head->model, - output->transform); - - ver = wl_resource_get_version(resource); - if (ver >= WL_OUTPUT_DONE_SINCE_VERSION) - wl_output_send_done(resource); - } - - wl_resource_for_each(resource, &head->xdg_output_resource_list) { - zxdg_output_v1_send_logical_position(resource, - output->x, - output->y); - zxdg_output_v1_send_done(resource); - } - } -} - -/** Signal that a pending output is taken into use. - * - * Removes the output from the pending list and adds it to the compositor's - * list of enabled outputs. The output created signal is emitted. - * - * The output gets an internal ID assigned, and the wl_output global is - * created. - * - * \param compositor The compositor instance. - * \param output The output to be added. - * - * \internal - * \ingroup compositor - */ -static void -weston_compositor_add_output(struct weston_compositor *compositor, - struct weston_output *output) -{ - struct weston_view *view, *next; - struct weston_head *head; - - assert(!output->enabled); - - /* Verify we haven't reached the limit of 32 available output IDs */ - assert(ffs(~compositor->output_id_pool) > 0); - - /* Invert the output id pool and look for the lowest numbered - * switch (the least significant bit). Take that bit's position - * as our ID, and mark it used in the compositor's output_id_pool. - */ - output->id = ffs(~compositor->output_id_pool) - 1; - compositor->output_id_pool |= 1u << output->id; - - wl_list_remove(&output->link); - wl_list_insert(compositor->output_list.prev, &output->link); - output->enabled = true; - - wl_list_for_each(head, &output->head_list, output_link) - weston_head_add_global(head); - - wl_signal_emit(&compositor->output_created_signal, output); - - wl_list_for_each_safe(view, next, &compositor->view_list, link) - weston_view_geometry_dirty(view); -} - -/** Transform device coordinates into global coordinates - * - * \param output the weston_output object - * \param[in] device_x X coordinate in device units. - * \param[in] device_y Y coordinate in device units. - * \param[out] x X coordinate in the global space. - * \param[out] y Y coordinate in the global space. - * - * Transforms coordinates from the device coordinate space (physical pixel - * units) to the global coordinate space (logical pixel units). This takes - * into account output transform and scale. - * - * \ingroup output - * \internal - */ -WL_EXPORT void -weston_output_transform_coordinate(struct weston_output *output, - double device_x, double device_y, - double *x, double *y) -{ - struct weston_vector p = { { - device_x, - device_y, - 0.0, - 1.0 } }; - - weston_matrix_transform(&output->inverse_matrix, &p); - - *x = p.f[0] / p.f[3]; - *y = p.f[1] / p.f[3]; -} - -/** Removes output from compositor's list of enabled outputs - * - * \param output The weston_output object that is being removed. - * - * The following happens: - * - * - The output assignments of all views in the current scenegraph are - * recomputed. - * - * - Presentation feedback is discarded. - * - * - Compositor is notified that outputs were changed and - * applies the necessary changes to re-layout outputs. - * - * - The output is put back in the pending outputs list. - * - * - Signal is emitted to notify all users of the weston_output - * object that the output is being destroyed. - * - * - wl_output protocol objects referencing this weston_output - * are made inert, and the wl_output global is removed. - * - * - The output's internal ID is released. - * - * \ingroup compositor - * \internal - */ -static void -weston_compositor_remove_output(struct weston_output *output) -{ - struct weston_compositor *compositor = output->compositor; - struct weston_view *view; - struct weston_head *head; - - assert(output->destroying); - assert(output->enabled); - - wl_list_for_each(view, &compositor->view_list, link) { - if (view->output_mask & (1u << output->id)) - weston_view_assign_output(view); - } - - weston_presentation_feedback_discard_list(&output->feedback_list); - - weston_compositor_reflow_outputs(compositor, output, -output->width); - - wl_list_remove(&output->link); - wl_list_insert(compositor->pending_output_list.prev, &output->link); - output->enabled = false; - - wl_signal_emit(&compositor->output_destroyed_signal, output); - wl_signal_emit(&output->destroy_signal, output); - - wl_list_for_each(head, &output->head_list, output_link) - weston_head_remove_global(head); - - compositor->output_id_pool &= ~(1u << output->id); - output->id = 0xffffffff; /* invalid */ -} - -/** Sets the output scale for a given output. - * - * \param output The weston_output object that the scale is set for. - * \param scale Scale factor for the given output. - * - * It only supports setting scale for an output that - * is not enabled and it can only be ran once. - * - * \ingroup ouput - */ -WL_EXPORT void -weston_output_set_scale(struct weston_output *output, - int32_t scale) -{ - /* We can only set scale on a disabled output */ - assert(!output->enabled); - - /* We only want to set scale once */ - assert(!output->scale); - - output->scale = scale; -} - -/** Sets the output transform for a given output. - * - * \param output The weston_output object that the transform is set for. - * \param transform Transform value for the given output. - * - * Refer to wl_output::transform section located at - * https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_output - * for list of values that can be passed to this function. - * - * \ingroup output - */ -WL_EXPORT void -weston_output_set_transform(struct weston_output *output, - uint32_t transform) -{ - struct weston_pointer_motion_event ev; - struct wl_resource *resource; - struct weston_seat *seat; - pixman_region32_t old_region; - int mid_x, mid_y; - struct weston_head *head; - int ver; - - if (!output->enabled && output->transform == UINT32_MAX) { - output->transform = transform; - return; - } - - weston_output_transform_scale_init(output, transform, output->scale); - - pixman_region32_init(&old_region); - pixman_region32_copy(&old_region, &output->region); - - weston_output_init_geometry(output, output->x, output->y); - - output->dirty = 1; - - /* Notify clients of the change for output transform. */ - wl_list_for_each(head, &output->head_list, output_link) { - wl_resource_for_each(resource, &head->resource_list) { - wl_output_send_geometry(resource, - output->x, - output->y, - head->mm_width, - head->mm_height, - head->subpixel, - head->make, - head->model, - output->transform); - - ver = wl_resource_get_version(resource); - if (ver >= WL_OUTPUT_DONE_SINCE_VERSION) - wl_output_send_done(resource); - } - wl_resource_for_each(resource, &head->xdg_output_resource_list) { - zxdg_output_v1_send_logical_position(resource, - output->x, - output->y); - zxdg_output_v1_send_logical_size(resource, - output->width, - output->height); - zxdg_output_v1_send_done(resource); - } - } - - /* we must ensure that pointers are inside output, otherwise they disappear */ - mid_x = output->x + output->width / 2; - mid_y = output->y + output->height / 2; - - ev.mask = WESTON_POINTER_MOTION_ABS; - ev.x = wl_fixed_to_double(wl_fixed_from_int(mid_x)); - ev.y = wl_fixed_to_double(wl_fixed_from_int(mid_y)); - - wl_list_for_each(seat, &output->compositor->seat_list, link) { - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - if (pointer && pixman_region32_contains_point(&old_region, - wl_fixed_to_int(pointer->x), - wl_fixed_to_int(pointer->y), - NULL)) - weston_pointer_move(pointer, &ev); - } -} - -/** Initializes a weston_output object with enough data so - ** an output can be configured. - * - * \param output The weston_output object to initialize - * \param compositor The compositor instance. - * \param name Name for the output (the string is copied). - * - * Sets initial values for fields that are expected to be - * configured either by compositors or backends. - * - * The name is used in logs, and can be used by compositors as a configuration - * identifier. - * - * \ingroup output - * \internal - */ -WL_EXPORT void -weston_output_init(struct weston_output *output, - struct weston_compositor *compositor, - const char *name) -{ - output->compositor = compositor; - output->destroying = 0; - output->name = strdup(name); - wl_list_init(&output->link); - wl_signal_init(&output->user_destroy_signal); - output->enabled = false; - output->desired_protection = WESTON_HDCP_DISABLE; - output->allow_protection = true; - - wl_list_init(&output->head_list); - - /* Add some (in)sane defaults which can be used - * for checking if an output was properly configured - */ - output->scale = 0; - /* Can't use -1 on uint32_t and 0 is valid enum value */ - output->transform = UINT32_MAX; - - pixman_region32_init(&output->region); - wl_list_init(&output->mode_list); -} - -/** Adds weston_output object to pending output list. - * - * \param output The weston_output object to add - * \param compositor The compositor instance. - * - * The opposite of this operation is built into weston_output_release(). - * - * \ingroup compositor - * \internal - */ -WL_EXPORT void -weston_compositor_add_pending_output(struct weston_output *output, - struct weston_compositor *compositor) -{ - assert(output->disable); - assert(output->enable); - - wl_list_remove(&output->link); - wl_list_insert(compositor->pending_output_list.prev, &output->link); -} - -/** Create a string with the attached heads' names. - * - * The string must be free()'d. - * - * \ingroup output - */ -static char * -weston_output_create_heads_string(struct weston_output *output) -{ - FILE *fp; - char *str = NULL; - size_t size = 0; - struct weston_head *head; - const char *sep = ""; - - fp = open_memstream(&str, &size); - if (!fp) - return NULL; - - wl_list_for_each(head, &output->head_list, output_link) { - fprintf(fp, "%s%s", sep, head->name); - sep = ", "; - } - fclose(fp); - - return str; -} - -/** Constructs a weston_output object that can be used by the compositor. - * - * \param output The weston_output object that needs to be enabled. Must not - * be enabled already. Must have at least one head attached. - * - * Output coordinates are calculated and each new output is by default - * assigned to the right of previous one. - * - * Sets up the transformation, zoom, and geometry of the output using - * the properties that need to be configured by the compositor. - * - * Establishes a repaint timer for the output with the relevant display - * object's event loop. See output_repaint_timer_handler(). - * - * The output is assigned an ID. Weston can support up to 32 distinct - * outputs, with IDs numbered from 0-31; the compositor's output_id_pool - * is referred to and used to find the first available ID number, and - * then this ID is marked as used in output_id_pool. - * - * The output is also assigned a Wayland global with the wl_output - * external interface. - * - * Backend specific function is called to set up the output output. - * - * Output is added to the compositor's output list - * - * If the backend specific function fails, the weston_output object - * is returned to a state it was before calling this function and - * is added to the compositor's pending_output_list in case it needs - * to be reconfigured or just so it can be destroyed at shutdown. - * - * 0 is returned on success, -1 on failure. - * - * \ingroup output - */ -WL_EXPORT int -weston_output_enable(struct weston_output *output) -{ - struct weston_compositor *c = output->compositor; - struct weston_output *iterator; - struct weston_head *head; - char *head_names; - int x = 0, y = 0; - - if (output->enabled) { - weston_log("Error: attempt to enable an enabled output '%s'\n", - output->name); - return -1; - } - - if (wl_list_empty(&output->head_list)) { - weston_log("Error: cannot enable output '%s' without heads.\n", - output->name); - return -1; - } - - if (wl_list_empty(&output->mode_list) || !output->current_mode) { - weston_log("Error: no video mode for output '%s'.\n", - output->name); - return -1; - } - - wl_list_for_each(head, &output->head_list, output_link) { - assert(head->make); - assert(head->model); - } - - iterator = container_of(c->output_list.prev, - struct weston_output, link); - - if (!wl_list_empty(&c->output_list)) - x = iterator->x + iterator->width; - - /* Make sure the scale is set up */ - assert(output->scale); - - /* Make sure we have a transform set */ - assert(output->transform != UINT32_MAX); - - output->x = x; - output->y = y; - output->dirty = 1; - output->original_scale = output->scale; - - wl_signal_init(&output->frame_signal); - wl_signal_init(&output->destroy_signal); - - weston_output_transform_scale_init(output, output->transform, output->scale); - weston_output_init_zoom(output); - - weston_output_init_geometry(output, x, y); - weston_output_damage(output); - - wl_list_init(&output->animation_list); - wl_list_init(&output->feedback_list); - - /* Enable the output (set up the crtc or create a - * window representing the output, set up the - * renderer, etc) - */ - if (output->enable(output) < 0) { - weston_log("Enabling output \"%s\" failed.\n", output->name); - return -1; - } - - weston_compositor_add_output(output->compositor, output); - - head_names = weston_output_create_heads_string(output); - weston_log("Output '%s' enabled with head(s) %s\n", - output->name, head_names); - free(head_names); - - return 0; -} - -/** Converts a weston_output object to a pending output state, so it - ** can be configured again or destroyed. - * - * \param output The weston_output object that needs to be disabled. - * - * Calls a backend specific function to disable an output, in case - * such function exists. - * - * The backend specific disable function may choose to postpone the disabling - * by returning a negative value, in which case this function returns early. - * In that case the backend will guarantee the output will be disabled soon - * by the backend calling this function again. One must not attempt to re-enable - * the output until that happens. - * - * Otherwise, if the output is being used by the compositor, it is removed - * from weston's output_list (see weston_compositor_remove_output()) - * and is returned to a state it was before weston_output_enable() - * was ran (see weston_output_enable_undo()). - * - * See weston_output_init() for more information on the - * state output is returned to. - * - * If the output has never been enabled yet, this function can still be - * called to ensure that the output is actually turned off rather than left - * in the state it was discovered in. - * - * \ingroup output - */ -WL_EXPORT void -weston_output_disable(struct weston_output *output) -{ - /* Should we rename this? */ - output->destroying = 1; - - /* Disable is called unconditionally also for not-enabled outputs, - * because at compositor start-up, if there is an output that is - * already on but the compositor wants to turn it off, we have to - * forward the turn-off to the backend so it knows to do it. - * The backend cannot initially turn off everything, because it - * would cause unnecessary mode-sets for all outputs the compositor - * wants to be on. - */ - if (output->disable(output) < 0) - return; - - if (output->enabled) - weston_compositor_remove_output(output); - - output->destroying = 0; -} - -/** Forces a synchronous call to heads_changed hook - * - * \param compositor The compositor instance - * - * If there are new or changed heads, calls the heads_changed hook and - * returns after the hook returns. - * - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_flush_heads_changed(struct weston_compositor *compositor) -{ - if (compositor->heads_changed_source) { - wl_event_source_remove(compositor->heads_changed_source); - weston_compositor_call_heads_changed(compositor); - } -} - -/** Add destroy callback for an output - * - * \param output The output to watch. - * \param listener The listener to add. The \c notify member must be set. - * - * The listener callback will be called when user destroys an output. This - * may be delayed by a backend in some cases. The main purpose of the - * listener is to allow hooking up custom data to the output. The custom data - * can be fetched via weston_output_get_destroy_listener() followed by - * container_of(). - * - * The \c data argument to the notify callback is the weston_output being - * destroyed. - * - * @note This is for the final destruction of an output, not when it gets - * disabled. If you want to keep track of enabled outputs, this is not it. - * - * \ingroup ouput - */ -WL_EXPORT void -weston_output_add_destroy_listener(struct weston_output *output, - struct wl_listener *listener) -{ - wl_signal_add(&output->user_destroy_signal, listener); -} - -/** Look up destroy listener for an output - * - * \param output The output to query. - * \param notify The notify function used used for the added destroy listener. - * \return The listener, or NULL if not found. - * - * This looks up the previously added destroy listener struct based on the - * notify function it has. The listener can be used to access user data - * through \c container_of(). - * - * \sa wl_signal_get() weston_output_add_destroy_listener() - * \ingroup output - */ -WL_EXPORT struct wl_listener * -weston_output_get_destroy_listener(struct weston_output *output, - wl_notify_func_t notify) -{ - return wl_signal_get(&output->user_destroy_signal, notify); -} - -/** Uninitialize an output - * - * Removes the output from the list of enabled outputs if necessary, but - * does not call the backend's output disable function. The output will no - * longer be in the list of pending outputs either. - * - * All fields of weston_output become uninitialized, i.e. should not be used - * anymore. The caller can free the memory after this. - * - * \ingroup ouput - * \internal - */ -WL_EXPORT void -weston_output_release(struct weston_output *output) -{ - struct weston_head *head, *tmp; - - output->destroying = 1; - - wl_signal_emit(&output->user_destroy_signal, output); - - if (output->idle_repaint_source) - wl_event_source_remove(output->idle_repaint_source); - - if (output->enabled) - weston_compositor_remove_output(output); - - pixman_region32_fini(&output->region); - wl_list_remove(&output->link); - - wl_list_for_each_safe(head, tmp, &output->head_list, output_link) - weston_head_detach(head); - - free(output->name); -} - -/** Find an output by its given name - * - * \param compositor The compositor to search in. - * \param name The output name to search for. - * \return An existing output with the given name, or NULL if not found. - * - * \ingroup compositor - */ -WL_EXPORT struct weston_output * -weston_compositor_find_output_by_name(struct weston_compositor *compositor, - const char *name) -{ - struct weston_output *output; - - wl_list_for_each(output, &compositor->output_list, link) - if (strcmp(output->name, name) == 0) - return output; - - wl_list_for_each(output, &compositor->pending_output_list, link) - if (strcmp(output->name, name) == 0) - return output; - - return NULL; -} - -/** Create a named output - * - * \param compositor The compositor. - * \param name The name for the output. - * \return A new \c weston_output, or NULL on failure. - * - * This creates a new weston_output that starts with no heads attached. - * - * An output must be configured and it must have at least one head before - * it can be enabled. - * - * \ingroup compositor - */ -WL_EXPORT struct weston_output * -weston_compositor_create_output(struct weston_compositor *compositor, - const char *name) -{ - assert(compositor->backend->create_output); - - if (weston_compositor_find_output_by_name(compositor, name)) { - weston_log("Warning: attempted to create an output with a " - "duplicate name '%s'.\n", name); - return NULL; - } - - return compositor->backend->create_output(compositor, name); -} - -/** Create an output for an unused head - * - * \param compositor The compositor. - * \param head The head to attach to the output. - * \return A new \c weston_output, or NULL on failure. - * - * This creates a new weston_output that starts with the given head attached. - * The output inherits the name of the head. The head must not be already - * attached to another output. - * - * An output must be configured before it can be enabled. - * - * \ingroup compositor - */ -WL_EXPORT struct weston_output * -weston_compositor_create_output_with_head(struct weston_compositor *compositor, - struct weston_head *head) -{ - struct weston_output *output; - - output = weston_compositor_create_output(compositor, head->name); - if (!output) - return NULL; - - if (weston_output_attach_head(output, head) < 0) { - weston_output_destroy(output); - return NULL; - } - - return output; -} - -/** Destroy an output - * - * \param output The output to destroy. - * - * The heads attached to the given output are detached and become unused again. - * - * It is not necessary to explicitly destroy all outputs at compositor exit. - * weston_compositor_tear_down() will automatically destroy any remaining - * outputs. - * - * \ingroup ouput - */ -WL_EXPORT void -weston_output_destroy(struct weston_output *output) -{ - output->destroy(output); -} - -/** When you need a head... - * - * This function is a hack, used until all code has been converted to become - * multi-head aware. - * - * \param output The weston_output whose head to get. - * \return The first head in the output's list. - * - * \ingroup ouput - */ -WL_EXPORT struct weston_head * -weston_output_get_first_head(struct weston_output *output) -{ - if (wl_list_empty(&output->head_list)) - return NULL; - - return container_of(output->head_list.next, - struct weston_head, output_link); -} - -/** Allow/Disallow content-protection support for an output - * - * This function sets the allow_protection member for an output. Setting of - * this field will allow the compositor to attempt content-protection for this - * output, for a backend that supports the content-protection protocol. - * - * \param output The weston_output for whom the content-protection is to be - * allowed. - * \param allow_protection The bool value which is to be set. - */ -WL_EXPORT void -weston_output_allow_protection(struct weston_output *output, - bool allow_protection) -{ - output->allow_protection = allow_protection; -} - -static void -xdg_output_unlist(struct wl_resource *resource) -{ - wl_list_remove(wl_resource_get_link(resource)); -} - -static void -xdg_output_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct zxdg_output_v1_interface xdg_output_interface = { - xdg_output_destroy -}; - -static void -xdg_output_manager_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -xdg_output_manager_get_xdg_output(struct wl_client *client, - struct wl_resource *manager, - uint32_t id, - struct wl_resource *output_resource) -{ - int version = wl_resource_get_version(manager); - struct weston_head *head = wl_resource_get_user_data(output_resource); - struct weston_output *output = head->output; - struct wl_resource *resource; - - resource = wl_resource_create(client, &zxdg_output_v1_interface, - version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_list_insert(&head->xdg_output_resource_list, - wl_resource_get_link(resource)); - - wl_resource_set_implementation(resource, &xdg_output_interface, - NULL, xdg_output_unlist); - - zxdg_output_v1_send_logical_position(resource, output->x, output->y); - zxdg_output_v1_send_logical_size(resource, - output->width, - output->height); - if (version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) - zxdg_output_v1_send_name(resource, head->name); - - zxdg_output_v1_send_done(resource); -} - -static const struct zxdg_output_manager_v1_interface xdg_output_manager_interface = { - xdg_output_manager_destroy, - xdg_output_manager_get_xdg_output -}; - -static void -bind_xdg_output_manager(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct wl_resource *resource; - - resource = wl_resource_create(client, &zxdg_output_manager_v1_interface, - version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, &xdg_output_manager_interface, - NULL, NULL); -} - -static void -destroy_viewport(struct wl_resource *resource) -{ - struct weston_surface *surface = - wl_resource_get_user_data(resource); - - if (!surface) - return; - - surface->viewport_resource = NULL; - surface->pending.buffer_viewport.buffer.src_width = - wl_fixed_from_int(-1); - surface->pending.buffer_viewport.surface.width = -1; - surface->pending.buffer_viewport.changed = 1; -} - -static void -viewport_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -viewport_set_source(struct wl_client *client, - struct wl_resource *resource, - wl_fixed_t src_x, - wl_fixed_t src_y, - wl_fixed_t src_width, - wl_fixed_t src_height) -{ - struct weston_surface *surface = - wl_resource_get_user_data(resource); - - if (!surface) { - wl_resource_post_error(resource, - WP_VIEWPORT_ERROR_NO_SURFACE, - "wl_surface for this viewport is no longer exists"); - return; - } - - assert(surface->viewport_resource == resource); - assert(surface->resource); - - if (src_width == wl_fixed_from_int(-1) && - src_height == wl_fixed_from_int(-1) && - src_x == wl_fixed_from_int(-1) && - src_y == wl_fixed_from_int(-1)) { - /* unset source rect */ - surface->pending.buffer_viewport.buffer.src_width = - wl_fixed_from_int(-1); - surface->pending.buffer_viewport.changed = 1; - return; - } - - if (src_width <= 0 || src_height <= 0 || src_x < 0 || src_y < 0) { - wl_resource_post_error(resource, - WP_VIEWPORT_ERROR_BAD_VALUE, - "wl_surface@%d viewport source " - "w=%f <= 0, h=%f <= 0, x=%f < 0, or y=%f < 0", - wl_resource_get_id(surface->resource), - wl_fixed_to_double(src_width), - wl_fixed_to_double(src_height), - wl_fixed_to_double(src_x), - wl_fixed_to_double(src_y)); - return; - } - - surface->pending.buffer_viewport.buffer.src_x = src_x; - surface->pending.buffer_viewport.buffer.src_y = src_y; - surface->pending.buffer_viewport.buffer.src_width = src_width; - surface->pending.buffer_viewport.buffer.src_height = src_height; - surface->pending.buffer_viewport.changed = 1; -} - -static void -viewport_set_destination(struct wl_client *client, - struct wl_resource *resource, - int32_t dst_width, - int32_t dst_height) -{ - struct weston_surface *surface = - wl_resource_get_user_data(resource); - - if (!surface) { - wl_resource_post_error(resource, - WP_VIEWPORT_ERROR_NO_SURFACE, - "wl_surface for this viewport no longer exists"); - return; - } - - assert(surface->viewport_resource == resource); - - if (dst_width == -1 && dst_height == -1) { - /* unset destination size */ - surface->pending.buffer_viewport.surface.width = -1; - surface->pending.buffer_viewport.changed = 1; - return; - } - - if (dst_width <= 0 || dst_height <= 0) { - wl_resource_post_error(resource, - WP_VIEWPORT_ERROR_BAD_VALUE, - "destination size must be positive (%dx%d)", - dst_width, dst_height); - return; - } - - surface->pending.buffer_viewport.surface.width = dst_width; - surface->pending.buffer_viewport.surface.height = dst_height; - surface->pending.buffer_viewport.changed = 1; -} - -static const struct wp_viewport_interface viewport_interface = { - viewport_destroy, - viewport_set_source, - viewport_set_destination -}; - -static void -viewporter_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -viewporter_get_viewport(struct wl_client *client, - struct wl_resource *viewporter, - uint32_t id, - struct wl_resource *surface_resource) -{ - int version = wl_resource_get_version(viewporter); - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - struct wl_resource *resource; - - if (surface->viewport_resource) { - wl_resource_post_error(viewporter, - WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS, - "a viewport for that surface already exists"); - return; - } - - resource = wl_resource_create(client, &wp_viewport_interface, - version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, &viewport_interface, - surface, destroy_viewport); - - surface->viewport_resource = resource; -} - -static const struct wp_viewporter_interface viewporter_interface = { - viewporter_destroy, - viewporter_get_viewport -}; - -static void -bind_viewporter(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct wl_resource *resource; - - resource = wl_resource_create(client, &wp_viewporter_interface, - version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, &viewporter_interface, - NULL, NULL); -} - -static void -destroy_presentation_feedback(struct wl_resource *feedback_resource) -{ - struct weston_presentation_feedback *feedback; - - feedback = wl_resource_get_user_data(feedback_resource); - - wl_list_remove(&feedback->link); - free(feedback); -} - -static void -presentation_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -presentation_feedback(struct wl_client *client, - struct wl_resource *presentation_resource, - struct wl_resource *surface_resource, - uint32_t callback) -{ - struct weston_surface *surface; - struct weston_presentation_feedback *feedback; - - surface = wl_resource_get_user_data(surface_resource); - - feedback = zalloc(sizeof *feedback); - if (feedback == NULL) - goto err_calloc; - - feedback->resource = wl_resource_create(client, - &wp_presentation_feedback_interface, - 1, callback); - if (!feedback->resource) - goto err_create; - - wl_resource_set_implementation(feedback->resource, NULL, feedback, - destroy_presentation_feedback); - - wl_list_insert(&surface->pending.feedback_list, &feedback->link); - - return; - -err_create: - free(feedback); - -err_calloc: - wl_client_post_no_memory(client); -} - -static const struct wp_presentation_interface presentation_implementation = { - presentation_destroy, - presentation_feedback -}; - -static void -bind_presentation(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct weston_compositor *compositor = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, &wp_presentation_interface, - version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, &presentation_implementation, - compositor, NULL); - wp_presentation_send_clock_id(resource, compositor->presentation_clock); -} - -static void -compositor_bind(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct weston_compositor *compositor = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, &wl_compositor_interface, - version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, &compositor_interface, - compositor, NULL); -} - -WL_EXPORT int -weston_environment_get_fd(const char *env) -{ - char *e; - int fd, flags; - - e = getenv(env); - if (!e || !safe_strtoint(e, &fd)) - return -1; - - flags = fcntl(fd, F_GETFD); - if (flags == -1) - return -1; - - fcntl(fd, F_SETFD, flags | FD_CLOEXEC); - unsetenv(env); - - return fd; -} - -static const char * -output_repaint_status_text(struct weston_output *output) -{ - switch (output->repaint_status) { - case REPAINT_NOT_SCHEDULED: - return "no repaint"; - case REPAINT_BEGIN_FROM_IDLE: - return "start_repaint_loop scheduled"; - case REPAINT_SCHEDULED: - return "repaint scheduled"; - case REPAINT_AWAITING_COMPLETION: - return "awaiting completion"; - } - - assert(!"output_repaint_status_text missing enum"); - return NULL; -} - -static void -debug_scene_view_print_buffer(FILE *fp, struct weston_view *view) -{ - struct weston_buffer *buffer = view->surface->buffer_ref.buffer; - struct wl_shm_buffer *shm; - struct linux_dmabuf_buffer *dmabuf; - const struct pixel_format_info *pixel_info = NULL; - - if (!buffer) { - fprintf(fp, "\t\t[buffer not available]\n"); - return; - } - - shm = wl_shm_buffer_get(buffer->resource); - if (shm) { - uint32_t _format = wl_shm_buffer_get_format(shm); - pixel_info = pixel_format_get_info_shm(_format); - fprintf(fp, "\t\tSHM buffer\n"); - fprintf(fp, "\t\t\tformat: 0x%lx %s\n", - (unsigned long) _format, - pixel_info ? pixel_info->drm_format_name : "UNKNOWN"); - return; - } - - dmabuf = linux_dmabuf_buffer_get(buffer->resource); - if (dmabuf) { - pixel_info = pixel_format_get_info(dmabuf->attributes.format); - fprintf(fp, "\t\tdmabuf buffer\n"); - fprintf(fp, "\t\t\tformat: 0x%lx %s\n", - (unsigned long) dmabuf->attributes.format, - pixel_info ? pixel_info->drm_format_name : "UNKNOWN"); - fprintf(fp, "\t\t\tmodifier: 0x%llx\n", - (unsigned long long) dmabuf->attributes.modifier[0]); - return; - } - - fprintf(fp, "\t\tEGL buffer\n"); -} - -static void -debug_scene_view_print(FILE *fp, struct weston_view *view, int view_idx) -{ - struct weston_compositor *ec = view->surface->compositor; - struct weston_output *output; - char desc[512]; - pixman_box32_t *box; - uint32_t surface_id = 0; - pid_t pid = 0; - - if (view->surface->resource) { - struct wl_resource *resource = view->surface->resource; - wl_client_get_credentials(wl_resource_get_client(resource), - &pid, NULL, NULL); - surface_id = wl_resource_get_id(view->surface->resource); - } - - if (!view->surface->get_label || - view->surface->get_label(view->surface, desc, sizeof(desc)) < 0) { - strcpy(desc, "[no description available]"); - } - fprintf(fp, "\tView %d (role %s, PID %d, surface ID %u, %s, %p):\n", - view_idx, view->surface->role_name, pid, surface_id, - desc, view); - - box = pixman_region32_extents(&view->transform.boundingbox); - fprintf(fp, "\t\tposition: (%d, %d) -> (%d, %d)\n", - box->x1, box->y1, box->x2, box->y2); - box = pixman_region32_extents(&view->transform.opaque); - - if (pixman_region32_equal(&view->transform.opaque, - &view->transform.boundingbox)) { - fprintf(fp, "\t\t[fully opaque]\n"); - } else if (!pixman_region32_not_empty(&view->transform.opaque)) { - fprintf(fp, "\t\t[not opaque]\n"); - } else { - fprintf(fp, "\t\t[opaque: (%d, %d) -> (%d, %d)]\n", - box->x1, box->y1, box->x2, box->y2); - } - - if (view->alpha < 1.0) - fprintf(fp, "\t\talpha: %f\n", view->alpha); - - if (view->output_mask != 0) { - bool first_output = true; - fprintf(fp, "\t\toutputs: "); - wl_list_for_each(output, &ec->output_list, link) { - if (!(view->output_mask & (1 << output->id))) - continue; - fprintf(fp, "%s%d (%s)%s", - (first_output) ? "" : ", ", - output->id, output->name, - (view->output == output) ? " (primary)" : ""); - first_output = false; - } - } else { - fprintf(fp, "\t\t[no outputs]"); - } - - fprintf(fp, "\n"); - - debug_scene_view_print_buffer(fp, view); -} - -static void -debug_scene_view_print_tree(struct weston_view *view, - FILE *fp, int *view_idx) -{ - struct weston_subsurface *sub; - struct weston_view *ev; - - /* - * print the view first, then we recursively go on printing - * sub-surfaces. We bail out once no more sub-surfaces are available. - */ - debug_scene_view_print(fp, view, *view_idx); - - /* no more sub-surfaces */ - if (wl_list_empty(&view->surface->subsurface_list)) - return; - - wl_list_for_each(sub, &view->surface->subsurface_list, parent_link) { - wl_list_for_each(ev, &sub->surface->views, surface_link) { - /* do not print again the parent view */ - if (view == ev) - continue; - - (*view_idx)++; - debug_scene_view_print_tree(ev, fp, view_idx); - } - } -} - -/** - * Output information on how libweston is currently composing the scene - * graph. - * - * \ingroup compositor - */ -WL_EXPORT char * -weston_compositor_print_scene_graph(struct weston_compositor *ec) -{ - struct weston_output *output; - struct weston_layer *layer; - struct timespec now; - int layer_idx = 0; - FILE *fp; - char *ret; - size_t len; - int err; - - fp = open_memstream(&ret, &len); - assert(fp); - - weston_compositor_read_presentation_clock(ec, &now); - fprintf(fp, "Weston scene graph at %ld.%09ld:\n\n", - now.tv_sec, now.tv_nsec); - - wl_list_for_each(output, &ec->output_list, link) { - struct weston_head *head; - int head_idx = 0; - - fprintf(fp, "Output %d (%s):\n", output->id, output->name); - assert(output->enabled); - - fprintf(fp, "\tposition: (%d, %d) -> (%d, %d)\n", - output->x, output->y, - output->x + output->width, - output->y + output->height); - fprintf(fp, "\tmode: %dx%d@%.3fHz\n", - output->current_mode->width, - output->current_mode->height, - output->current_mode->refresh / 1000.0); - fprintf(fp, "\tscale: %d\n", output->scale); - - fprintf(fp, "\trepaint status: %s\n", - output_repaint_status_text(output)); - if (output->repaint_status == REPAINT_SCHEDULED) - fprintf(fp, "\tnext repaint: %ld.%09ld\n", - output->next_repaint.tv_sec, - output->next_repaint.tv_nsec); - - wl_list_for_each(head, &output->head_list, output_link) { - fprintf(fp, "\tHead %d (%s): %sconnected\n", - head_idx++, head->name, - (head->connected) ? "" : "not "); - } - } - - fprintf(fp, "\n"); - - wl_list_for_each(layer, &ec->layer_list, link) { - struct weston_view *view; - int view_idx = 0; - - fprintf(fp, "Layer %d (pos 0x%lx):\n", layer_idx++, - (unsigned long) layer->position); - - if (!weston_layer_mask_is_infinite(layer)) { - fprintf(fp, "\t[mask: (%d, %d) -> (%d,%d)]\n\n", - layer->mask.x1, layer->mask.y1, - layer->mask.x2, layer->mask.y2); - } - - wl_list_for_each(view, &layer->view_list.link, layer_link.link) { - debug_scene_view_print_tree(view, fp, &view_idx); - view_idx++; - } - - if (wl_list_empty(&layer->view_list.link)) - fprintf(fp, "\t[no views]\n"); - - fprintf(fp, "\n"); - } - - err = fclose(fp); - assert(err == 0); - - return ret; -} - -/** - * Called when the 'scene-graph' debug scope is bound by a client. This - * one-shot weston-debug scope prints the current scene graph when bound, - * and then terminates the stream. - */ -static void -debug_scene_graph_cb(struct weston_log_subscription *sub, void *data) -{ - struct weston_compositor *ec = data; - char *str = weston_compositor_print_scene_graph(ec); - - weston_log_subscription_printf(sub, "%s", str); - free(str); - weston_log_subscription_complete(sub); -} - -/** Create the compositor. - * - * This functions creates and initializes a compositor instance. - * - * \param display The Wayland display to be used. - * \param user_data A pointer to an object that can later be retrieved - * \param log_ctx A pointer to weston_debug_compositor - * using the \ref weston_compositor_get_user_data function. - * \return The compositor instance on success or NULL on failure. - * - * \ingroup compositor - */ -WL_EXPORT struct weston_compositor * -weston_compositor_create(struct wl_display *display, - struct weston_log_context *log_ctx, - void *user_data) -{ - struct weston_compositor *ec; - struct wl_event_loop *loop; - - ec = zalloc(sizeof *ec); - if (!ec) - return NULL; - - ec->wl_display = display; - ec->user_data = user_data; - wl_signal_init(&ec->destroy_signal); - wl_signal_init(&ec->create_surface_signal); - wl_signal_init(&ec->activate_signal); - wl_signal_init(&ec->transform_signal); - wl_signal_init(&ec->kill_signal); - wl_signal_init(&ec->idle_signal); - wl_signal_init(&ec->wake_signal); - wl_signal_init(&ec->show_input_panel_signal); - wl_signal_init(&ec->hide_input_panel_signal); - wl_signal_init(&ec->update_input_panel_signal); - wl_signal_init(&ec->seat_created_signal); - wl_signal_init(&ec->output_created_signal); - wl_signal_init(&ec->output_destroyed_signal); - wl_signal_init(&ec->output_moved_signal); - wl_signal_init(&ec->output_resized_signal); - wl_signal_init(&ec->heads_changed_signal); - wl_signal_init(&ec->output_heads_changed_signal); - wl_signal_init(&ec->session_signal); - ec->session_active = true; - - ec->output_id_pool = 0; - ec->repaint_msec = DEFAULT_REPAINT_WINDOW; - - ec->activate_serial = 1; - - ec->touch_mode = WESTON_TOUCH_MODE_NORMAL; - - ec->content_protection = NULL; - - if (!wl_global_create(ec->wl_display, &wl_compositor_interface, 4, - ec, compositor_bind)) - goto fail; - - if (!wl_global_create(ec->wl_display, &wl_subcompositor_interface, 1, - ec, bind_subcompositor)) - goto fail; - - if (!wl_global_create(ec->wl_display, &wp_viewporter_interface, 1, - ec, bind_viewporter)) - goto fail; - - if (!wl_global_create(ec->wl_display, &zxdg_output_manager_v1_interface, 2, - ec, bind_xdg_output_manager)) - goto fail; - - if (!wl_global_create(ec->wl_display, &wp_presentation_interface, 1, - ec, bind_presentation)) - goto fail; - - if (weston_log_ctx_compositor_setup(ec, log_ctx) < 0) - goto fail; - - if (weston_input_init(ec) != 0) - goto fail; - - wl_list_init(&ec->view_list); - wl_list_init(&ec->plane_list); - wl_list_init(&ec->layer_list); - wl_list_init(&ec->seat_list); - wl_list_init(&ec->pending_output_list); - wl_list_init(&ec->output_list); - wl_list_init(&ec->head_list); - wl_list_init(&ec->key_binding_list); - wl_list_init(&ec->modifier_binding_list); - wl_list_init(&ec->button_binding_list); - wl_list_init(&ec->touch_binding_list); - wl_list_init(&ec->axis_binding_list); - wl_list_init(&ec->debug_binding_list); - - wl_list_init(&ec->plugin_api_list); - - weston_plane_init(&ec->primary_plane, ec, 0, 0); - weston_compositor_stack_plane(ec, &ec->primary_plane, NULL); - - wl_data_device_manager_init(ec->wl_display); - - wl_display_init_shm(ec->wl_display); - - loop = wl_display_get_event_loop(ec->wl_display); - ec->idle_source = wl_event_loop_add_timer(loop, idle_handler, ec); - ec->repaint_timer = - wl_event_loop_add_timer(loop, output_repaint_timer_handler, - ec); - - weston_layer_init(&ec->fade_layer, ec); - weston_layer_init(&ec->cursor_layer, ec); - - weston_layer_set_position(&ec->fade_layer, WESTON_LAYER_POSITION_FADE); - weston_layer_set_position(&ec->cursor_layer, - WESTON_LAYER_POSITION_CURSOR); - - ec->debug_scene = - weston_compositor_add_log_scope(ec->weston_log_ctx, "scene-graph", - "Scene graph details\n", - debug_scene_graph_cb, NULL, - ec); - - ec->timeline = - weston_compositor_add_log_scope(ec->weston_log_ctx, "timeline", - "Timeline event points\n", - weston_timeline_create_subscription, - weston_timeline_destroy_subscription, - ec); - return ec; - -fail: - free(ec); - return NULL; -} - -/** weston_compositor_shutdown - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_shutdown(struct weston_compositor *ec) -{ - struct weston_output *output, *next; - - wl_event_source_remove(ec->idle_source); - - /* Destroy all outputs associated with this compositor */ - wl_list_for_each_safe(output, next, &ec->output_list, link) - output->destroy(output); - - /* Destroy all pending outputs associated with this compositor */ - wl_list_for_each_safe(output, next, &ec->pending_output_list, link) - output->destroy(output); - - if (ec->renderer) - ec->renderer->destroy(ec); - - weston_binding_list_destroy_all(&ec->key_binding_list); - weston_binding_list_destroy_all(&ec->modifier_binding_list); - weston_binding_list_destroy_all(&ec->button_binding_list); - weston_binding_list_destroy_all(&ec->touch_binding_list); - weston_binding_list_destroy_all(&ec->axis_binding_list); - weston_binding_list_destroy_all(&ec->debug_binding_list); - - weston_plane_release(&ec->primary_plane); -} - -/** weston_compositor_exit_with_code - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_exit_with_code(struct weston_compositor *compositor, - int exit_code) -{ - if (compositor->exit_code == EXIT_SUCCESS) - compositor->exit_code = exit_code; - - weston_compositor_exit(compositor); -} - -/** weston_compositor_set_default_pointer_grab - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_set_default_pointer_grab(struct weston_compositor *ec, - const struct weston_pointer_grab_interface *interface) -{ - struct weston_seat *seat; - - ec->default_pointer_grab = interface; - wl_list_for_each(seat, &ec->seat_list, link) { - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - if (pointer) - weston_pointer_set_default_grab(pointer, interface); - } -} - -/** weston_compositor_set_presentation_clock - * \ingroup compositor - */ -WL_EXPORT int -weston_compositor_set_presentation_clock(struct weston_compositor *compositor, - clockid_t clk_id) -{ - struct timespec ts; - - if (clock_gettime(clk_id, &ts) < 0) - return -1; - - compositor->presentation_clock = clk_id; - - return 0; -} - -/** For choosing the software clock, when the display hardware or API - * does not expose a compatible presentation timestamp. - * - * \ingroup compositor - */ -WL_EXPORT int -weston_compositor_set_presentation_clock_software( - struct weston_compositor *compositor) -{ - /* In order of preference */ - static const clockid_t clocks[] = { - CLOCK_MONOTONIC_RAW, /* no jumps, no crawling */ - CLOCK_MONOTONIC_COARSE, /* no jumps, may crawl, fast & coarse */ - CLOCK_MONOTONIC, /* no jumps, may crawl */ - CLOCK_REALTIME_COARSE, /* may jump and crawl, fast & coarse */ - CLOCK_REALTIME /* may jump and crawl */ - }; - unsigned i; - - for (i = 0; i < ARRAY_LENGTH(clocks); i++) - if (weston_compositor_set_presentation_clock(compositor, - clocks[i]) == 0) - return 0; - - weston_log("Error: no suitable presentation clock available.\n"); - - return -1; -} - -/** Read the current time from the Presentation clock - * - * \param compositor - * \param[out] ts The current time. - * - * \note Reading the current time in user space is always imprecise to some - * degree. - * - * This function is never meant to fail. If reading the clock does fail, - * an error message is logged and a zero time is returned. Callers are not - * supposed to detect or react to failures. - * - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_read_presentation_clock( - const struct weston_compositor *compositor, - struct timespec *ts) -{ - static bool warned; - int ret; - - ret = clock_gettime(compositor->presentation_clock, ts); - if (ret < 0) { - ts->tv_sec = 0; - ts->tv_nsec = 0; - - if (!warned) - weston_log("Error: failure to read " - "the presentation clock %#x: '%s' (%d)\n", - compositor->presentation_clock, - strerror(errno), errno); - warned = true; - } -} - -/** Import dmabuf buffer into current renderer - * - * \param compositor - * \param buffer the dmabuf buffer to import - * \return true on usable buffers, false otherwise - * - * This function tests that the linux_dmabuf_buffer is usable - * for the current renderer. Returns false on unusable buffers. Usually - * usability is tested by importing the dmabufs for composition. - * - * This hook is also used for detecting if the renderer supports - * dmabufs at all. If the renderer hook is NULL, dmabufs are not - * supported. - * - * \ingroup compositor - */ -WL_EXPORT bool -weston_compositor_import_dmabuf(struct weston_compositor *compositor, - struct linux_dmabuf_buffer *buffer) -{ - struct weston_renderer *renderer; - - renderer = compositor->renderer; - - if (renderer->import_dmabuf == NULL) - return false; - - return renderer->import_dmabuf(compositor, buffer); -} - -WL_EXPORT bool -weston_compositor_dmabuf_can_scanout(struct weston_compositor *compositor, - struct linux_dmabuf_buffer *buffer) -{ - struct weston_backend *backend = compositor->backend; - - if (backend->can_scanout_dmabuf == NULL) - return false; - - return backend->can_scanout_dmabuf(compositor, buffer); -} - -WL_EXPORT void -weston_version(int *major, int *minor, int *micro) -{ - *major = WESTON_VERSION_MAJOR; - *minor = WESTON_VERSION_MINOR; - *micro = WESTON_VERSION_MICRO; -} - -/** - * Attempts to find a module path from the module map specified in the - * environment. If found, writes the full path into the path variable. - * - * The module map is a string in environment variable WESTON_MODULE_MAP, where - * each entry is of the form "name=path" and entries are separated by - * semicolons. Whitespace is significant. - * - * \param name The name to search for. - * \param path Where the path is written to if found. - * \param path_len Allocated bytes at \c path . - * \returns The length of the string written to path on success, or 0 if the - * module was not specified in the environment map or path_len was too small. - */ -WL_EXPORT size_t -weston_module_path_from_env(const char *name, char *path, size_t path_len) -{ - const char *mapping = getenv("WESTON_MODULE_MAP"); - const char *end; - const int name_len = strlen(name); - - if (!mapping) - return 0; - - end = mapping + strlen(mapping); - while (mapping < end && *mapping) { - const char *filename, *next; - - /* early out: impossibly short string */ - if (end - mapping < name_len + 1) - return 0; - - filename = &mapping[name_len + 1]; - next = strchrnul(mapping, ';'); - - if (strncmp(mapping, name, name_len) == 0 && - mapping[name_len] == '=') { - size_t file_len = next - filename; /* no trailing NUL */ - if (file_len >= path_len) - return 0; - strncpy(path, filename, file_len); - path[file_len] = '\0'; - return file_len; - } - - mapping = next + 1; - } - - return 0; -} - -WL_EXPORT void * -weston_load_module(const char *name, const char *entrypoint) -{ - char path[PATH_MAX]; - void *module, *init; - size_t len; - - if (name == NULL) - return NULL; - - if (name[0] != '/') { - len = weston_module_path_from_env(name, path, sizeof path); - if (len == 0) - len = snprintf(path, sizeof path, "%s/%s", - LIBWESTON_MODULEDIR, name); - } else { - len = snprintf(path, sizeof path, "%s", name); - } - - /* snprintf returns the length of the string it would've written, - * _excluding_ the NUL byte. So even being equal to the size of - * our buffer is an error here. */ - if (len >= sizeof path) - return NULL; - - module = dlopen(path, RTLD_NOW | RTLD_NOLOAD); - if (module) { - weston_log("Module '%s' already loaded\n", path); - } else { - weston_log("Loading module '%s'\n", path); - module = dlopen(path, RTLD_NOW); - if (!module) { - weston_log("Failed to load module: %s\n", dlerror()); - return NULL; - } - } - - init = dlsym(module, entrypoint); - if (!init) { - weston_log("Failed to lookup init function: %s\n", dlerror()); - dlclose(module); - return NULL; - } - - return init; -} - -/** Add a compositor destroy listener only once - * - * \param compositor The compositor whose destroy to watch for. - * \param listener The listener struct to initialize. - * \param destroy_handler The callback when compositor is destroyed. - * \return True if listener is added, or false if there already is a listener - * with the given \c destroy_handler. - * - * This function does nothing and returns false if the given callback function - * is already present in the weston_compositor destroy callbacks list. - * Otherwise, this function initializes the given listener with the given - * callback pointer and adds it to the compositor's destroy callbacks list. - * - * This can be used to ensure that plugin initialization is done only once - * in case the same plugin is loaded multiple times. If this function returns - * false, the plugin should be already initialized successfully. - * - * All plugins should register a destroy listener for cleaning up. Note, that - * the plugin destruction order is not guaranteed: plugins that depend on other - * plugins must be able to be torn down in arbitrary order. - * - * \sa weston_compositor_tear_down, weston_compositor_destroy - */ -WL_EXPORT bool -weston_compositor_add_destroy_listener_once(struct weston_compositor *compositor, - struct wl_listener *listener, - wl_notify_func_t destroy_handler) -{ - if (wl_signal_get(&compositor->destroy_signal, destroy_handler)) - return false; - - listener->notify = destroy_handler; - wl_signal_add(&compositor->destroy_signal, listener); - return true; -} - -/** Tear down the compositor. - * - * This function cleans up the compositor state. While the compositor state has - * been cleaned do note that **only** weston_compositor_destroy() can be called - * afterwards, in order to destroy the compositor instance. - * - * @param compositor The compositor to be tear-down/cleaned. - * - * @ingroup compositor - * @sa weston_compositor_destroy - */ -WL_EXPORT void -weston_compositor_tear_down(struct weston_compositor *compositor) -{ - /* prevent further rendering while shutting down */ - compositor->state = WESTON_COMPOSITOR_OFFSCREEN; - - wl_signal_emit(&compositor->destroy_signal, compositor); - - weston_compositor_xkb_destroy(compositor); - - if (compositor->backend) - compositor->backend->destroy(compositor); - - /* The backend is responsible for destroying the heads. */ - assert(wl_list_empty(&compositor->head_list)); - - weston_plugin_api_destroy_list(compositor); - - if (compositor->heads_changed_source) - wl_event_source_remove(compositor->heads_changed_source); - - weston_compositor_log_scope_destroy(compositor->debug_scene); - compositor->debug_scene = NULL; - - weston_compositor_log_scope_destroy(compositor->timeline); - compositor->timeline = NULL; -} - -/** Destroys the compositor. - * - * This function destroys the compositor. **Do not** call this before - * calling weston_compositor_tear_down() - * - * @param compositor The compositor to be destroyed. - * @ingroup compositor - * @sa weston_compositor_tear_down() - */ -WL_EXPORT void -weston_compositor_destroy(struct weston_compositor *compositor) -{ - free(compositor); -} - -/** Instruct the compositor to exit. - * - * This functions does not directly destroy the compositor object, it merely - * command it to start the tear down process. It is not guaranteed that the - * tear down will happen immediately. - * - * \param compositor The compositor to tear down. - * - * \ingroup compositor - */ -WL_EXPORT void -weston_compositor_exit(struct weston_compositor *compositor) -{ - compositor->exit(compositor); -} - -/** Return the user data stored in the compositor. - * - * This function returns the user data pointer set with user_data parameter - * to the \ref weston_compositor_create function. - * - * \ingroup compositor - */ -WL_EXPORT void * -weston_compositor_get_user_data(struct weston_compositor *compositor) -{ - return compositor->user_data; -} - -static const char * const backend_map[] = { - [WESTON_BACKEND_DRM] = "drm-backend.so", - [WESTON_BACKEND_FBDEV] = "fbdev-backend.so", - [WESTON_BACKEND_HEADLESS] = "headless-backend.so", - [WESTON_BACKEND_RDP] = "rdp-backend.so", - [WESTON_BACKEND_WAYLAND] = "wayland-backend.so", - [WESTON_BACKEND_X11] = "x11-backend.so", - [WESTON_BACKEND_TDM] = "tdm-backend.so", -}; - -/** Load a backend into a weston_compositor - * - * A backend must be loaded to make a weston_compositor work. A backend - * provides input and output capabilities, and determines the renderer to use. - * - * \param compositor A compositor that has not had a backend loaded yet. - * \param backend Name of the backend file. - * \param config_base A pointer to a backend-specific configuration - * structure's 'base' member. - * - * \return 0 on success, or -1 on error. - * - * \ingroup compositor - */ -WL_EXPORT int -weston_compositor_load_backend(struct weston_compositor *compositor, - enum weston_compositor_backend backend, - struct weston_backend_config *config_base) -{ - int (*backend_init)(struct weston_compositor *c, - struct weston_backend_config *config_base); - - if (compositor->backend) { - weston_log("Error: attempt to load a backend when one is already loaded\n"); - return -1; - } - - if (backend >= ARRAY_LENGTH(backend_map)) - return -1; - - backend_init = weston_load_module(backend_map[backend], "weston_backend_init"); - if (!backend_init) - return -1; - - if (backend_init(compositor, config_base) < 0) { - compositor->backend = NULL; - return -1; - } - - return 0; -} - -/** weston_compositor_load_xwayland - * \ingroup compositor - */ -WL_EXPORT int -weston_compositor_load_xwayland(struct weston_compositor *compositor) -{ - int (*module_init)(struct weston_compositor *ec); - - module_init = weston_load_module("xwayland.so", "weston_module_init"); - if (!module_init) - return -1; - if (module_init(compositor) < 0) - return -1; - return 0; -} - -/** Resolve an internal compositor error by disconnecting the client. - * - * This function is used in cases when the wl_buffer turns out - * unusable and there is no fallback path. - * - * It is possible the fault is caused by a compositor bug, the underlying - * graphics stack bug or normal behaviour, or perhaps a client mistake. - * In any case, the options are to either composite garbage or nothing, - * or disconnect the client. This is a helper function for the latter. - * - * The error is sent as an INVALID_OBJECT error on the client's wl_display. - * - * \param buffer The weston buffer that is unusable. - * \param msg A custom error message attached to the protocol error. - */ -WL_EXPORT void -weston_buffer_send_server_error(struct weston_buffer *buffer, - const char *msg) -{ - struct wl_client *client; - struct wl_resource *display_resource; - uint32_t id; - - assert(buffer->resource); - id = wl_resource_get_id(buffer->resource); - client = wl_resource_get_client(buffer->resource); - display_resource = wl_client_get_object(client, 1); - - assert(display_resource); - wl_resource_post_error(display_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "server error with " - "wl_buffer@%u: %s", id, msg); -} - -WL_EXPORT void -weston_output_disable_planes_incr(struct weston_output *output) -{ - output->disable_planes++; - /* - * If disable_planes changes from 0 to non-zero, it means some type of - * recording of content has started, and therefore protection level of - * the protected surfaces must be updated to avoid the recording of - * the protected content. - */ - if (output->disable_planes == 1) - weston_schedule_surface_protection_update(output->compositor); -} - -WL_EXPORT void -weston_output_disable_planes_decr(struct weston_output *output) -{ - output->disable_planes--; - /* - * If disable_planes changes from non-zero to 0, it means no content - * recording is going on any more, and the protected and surfaces can be - * shown without any apprehensions about content being recorded. - */ - if (output->disable_planes == 0) - weston_schedule_surface_protection_update(output->compositor); - -} diff --git a/libweston/content-protection.c b/libweston/content-protection.c deleted file mode 100644 index 23661453..00000000 --- a/libweston/content-protection.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright © 2019 Intel Corporation - * - * 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 <stdint.h> -#include <stdlib.h> -#include <assert.h> -#include <signal.h> -#include <unistd.h> -#include <string.h> -#include <libweston/libweston.h> -#include <libweston/weston-log.h> -#include "weston-content-protection-server-protocol.h" -#include "shared/helpers.h" -#include "shared/timespec-util.h" - -#define content_protection_log(cp, ...) \ - weston_log_scope_printf((cp)->debug, __VA_ARGS__) - -static const char * const content_type_name [] = { - [WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED] = "UNPROTECTED", - [WESTON_PROTECTED_SURFACE_TYPE_HDCP_0] = "TYPE-0", - [WESTON_PROTECTED_SURFACE_TYPE_HDCP_1] = "TYPE-1", -}; - -void -weston_protected_surface_send_event(struct protected_surface *psurface, - enum weston_hdcp_protection protection) -{ - struct wl_resource *p_resource; - enum weston_protected_surface_type protection_type; - struct content_protection *cp; - struct wl_resource *surface_resource; - - p_resource = psurface->protection_resource; - if (!p_resource) - return; - /* No event to be sent to client, in case of enforced mode */ - if (psurface->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED) - return; - protection_type = (enum weston_protected_surface_type) protection; - weston_protected_surface_send_status(p_resource, protection_type); - - cp = psurface->cp_backptr; - surface_resource = psurface->surface->resource; - content_protection_log(cp, "wl_surface@%"PRIu32" Protection type set to %s\n", - wl_resource_get_id(surface_resource), - content_type_name[protection_type]); -} - -static void -set_type(struct wl_client *client, struct wl_resource *resource, - enum weston_protected_surface_type content_type) -{ - struct content_protection *cp; - struct protected_surface *psurface; - enum weston_hdcp_protection weston_cp; - struct wl_resource *surface_resource; - - psurface = wl_resource_get_user_data(resource); - if (!psurface) - return; - cp = psurface->cp_backptr; - surface_resource = psurface->surface->resource; - - if (content_type < WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED || - content_type > WESTON_PROTECTED_SURFACE_TYPE_HDCP_1) { - wl_resource_post_error(resource, - WESTON_PROTECTED_SURFACE_ERROR_INVALID_TYPE, - "wl_surface@%"PRIu32" Invalid content-type %d for request:set_type\n", - wl_resource_get_id(surface_resource), content_type); - - content_protection_log(cp, "wl_surface@%"PRIu32" Invalid content-type %d for resquest:set_type\n", - wl_resource_get_id(surface_resource), content_type); - return; - } - - content_protection_log(cp, "wl_surface@%"PRIu32" Request: Enable Content-Protection Type: %s\n", - wl_resource_get_id(surface_resource), - content_type_name[content_type]); - - weston_cp = (enum weston_hdcp_protection) content_type; - psurface->surface->pending.desired_protection = weston_cp; -} - -static void -protected_surface_destroy(struct wl_client *client, struct wl_resource *resource) -{ - struct protected_surface *psurface; - - psurface = wl_resource_get_user_data(resource); - if (!psurface) - return; - psurface->surface->pending.desired_protection = WESTON_HDCP_DISABLE; -} - -static void -set_enforce_mode(struct wl_client *client, struct wl_resource *resource) -{ - /* - * Enforce Censored-Visibility. Compositor censors the protected - * surface on an unsecured output. - * In case of a surface, being shown on an unprotected output, the - * compositor hides the surface, not allowing it to be displayed on - * the unprotected output, without bothering the client. No difference - * for the protected outputs. - * - * The member 'protection_mode' is "double-buffered", so setting it in - * pending_state will cause the setting of the corresponding - * 'protection_mode' in weston_surface, after the commit. - * - * This function sets the 'protection_mode' of the weston_surface_state - * to 'enfoced'. The renderers inspect the flag and compare the - * desired_protection of the surface, to the current_protection of the - * output, based on that the real surface or a place-holder content, - * (e.g. solid color) are shown. - */ - - struct protected_surface *psurface; - - psurface = wl_resource_get_user_data(resource); - if (!psurface) - return; - - psurface->surface->pending.protection_mode = - WESTON_SURFACE_PROTECTION_MODE_ENFORCED; -} - -static void -set_relax_mode(struct wl_client *client, struct wl_resource *resource) -{ - /* - * Relaxed mode. By default this mode will be activated. - * In case of a surface, being shown in unprotected output, - * compositor just sends the event for protection status changed. - * - * On setting the relaxed mode, the 'protection_mode' member is queued - * to be set to 'relax' from the existing 'enforce' mode. - */ - - struct protected_surface *psurface; - - psurface = wl_resource_get_user_data(resource); - if (!psurface) - return; - - psurface->surface->pending.protection_mode = - WESTON_SURFACE_PROTECTION_MODE_RELAXED; -} - -static const struct weston_protected_surface_interface protected_surface_implementation = { - protected_surface_destroy, - set_type, - set_enforce_mode, - set_relax_mode, -}; - -static void -cp_destroy_listener(struct wl_listener *listener, void *data) -{ - struct content_protection *cp; - - cp = container_of(listener, struct content_protection, - destroy_listener); - wl_list_remove(&cp->destroy_listener.link); - wl_list_remove(&cp->protected_list); - weston_compositor_log_scope_destroy(cp->debug); - cp->debug = NULL; - cp->surface_protection_update = NULL; - free(cp); -} - -static void -free_protected_surface(struct protected_surface *psurface) -{ - psurface->surface->pending.desired_protection = WESTON_HDCP_DISABLE; - wl_resource_set_user_data(psurface->protection_resource, NULL); - wl_list_remove(&psurface->surface_destroy_listener.link); - wl_list_remove(&psurface->link); - free(psurface); -} - -static void -surface_destroyed(struct wl_listener *listener, void *data) -{ - struct protected_surface *psurface; - - psurface = container_of(listener, struct protected_surface, - surface_destroy_listener); - free_protected_surface(psurface); -} - -static void -destroy_protected_surface(struct wl_resource *resource) -{ - struct protected_surface *psurface; - - psurface = wl_resource_get_user_data(resource); - if (!psurface) - return; - free_protected_surface(psurface); -} - -static void -get_protection(struct wl_client *client, struct wl_resource *cp_resource, - uint32_t id, struct wl_resource *surface_resource) -{ - struct wl_resource *resource; - struct weston_surface *surface; - struct content_protection *cp; - struct protected_surface *psurface; - struct wl_listener *listener; - - surface = wl_resource_get_user_data(surface_resource); - assert(surface); - cp = wl_resource_get_user_data(cp_resource); - assert(cp); - - /* - * Check if this client has a corresponding protected-surface - */ - - listener = wl_resource_get_destroy_listener(surface->resource, - surface_destroyed); - - if (listener) { - wl_resource_post_error(cp_resource, - WESTON_CONTENT_PROTECTION_ERROR_SURFACE_EXISTS, - "wl_surface@%"PRIu32" Protection already exists", - wl_resource_get_id(surface_resource)); - return; - } - - psurface = zalloc(sizeof(struct protected_surface)); - if (!psurface) { - wl_client_post_no_memory(client); - return; - } - psurface->cp_backptr = cp; - resource = wl_resource_create(client, &weston_protected_surface_interface, - 1, id); - if (!resource) { - free(psurface); - wl_client_post_no_memory(client); - return; - } - - wl_list_insert(&cp->protected_list, &psurface->link); - wl_resource_set_implementation(resource, &protected_surface_implementation, - psurface, - destroy_protected_surface); - - psurface->protection_resource = resource; - psurface->surface = surface; - psurface->surface_destroy_listener.notify = surface_destroyed; - wl_resource_add_destroy_listener(surface->resource, - &psurface->surface_destroy_listener); - weston_protected_surface_send_event(psurface, - psurface->surface->current_protection); -} - -static void -destroy_protection(struct wl_client *client, struct wl_resource *cp_resource) -{ - wl_resource_destroy(cp_resource); -} - -static const -struct weston_content_protection_interface content_protection_implementation = { - destroy_protection, - get_protection, -}; - -static void -bind_weston_content_protection(struct wl_client *client, void *data, - uint32_t version, uint32_t id) -{ - struct content_protection *cp = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, - &weston_content_protection_interface, - 1, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, - &content_protection_implementation, - cp, NULL); -} -/* Advertise the content-protection support. - * - * Calling this function sets up the content-protection support via HDCP. - * This exposes the global interface, visible to the client, enabling them to - * request for content-protection for their surfaces according to the type of - * content. - */ - -WL_EXPORT int -weston_compositor_enable_content_protection(struct weston_compositor *compositor) -{ - struct content_protection *cp; - - cp = zalloc(sizeof(*cp)); - if (cp == NULL) - return -1; - cp->compositor = compositor; - - compositor->content_protection = cp; - wl_list_init(&cp->protected_list); - if (wl_global_create(compositor->wl_display, - &weston_content_protection_interface, 1, cp, - bind_weston_content_protection) == NULL) - return -1; - - cp->destroy_listener.notify = cp_destroy_listener; - wl_signal_add(&compositor->destroy_signal, &cp->destroy_listener); - cp->debug = weston_compositor_add_log_scope(compositor->weston_log_ctx, - "content-protection-debug", - "debug-logs for content-protection", - NULL, NULL, NULL); - return 0; -} diff --git a/libweston/data-device.c b/libweston/data-device.c deleted file mode 100644 index 865d7497..00000000 --- a/libweston/data-device.c +++ /dev/null @@ -1,1369 +0,0 @@ -/* - * Copyright © 2011 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 <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <stdint.h> -#include <stdio.h> -#include <assert.h> - -#include <libweston/libweston.h> -#include "shared/helpers.h" -#include "shared/timespec-util.h" - -struct weston_drag { - struct wl_client *client; - struct weston_data_source *data_source; - struct wl_listener data_source_listener; - struct weston_view *focus; - struct wl_resource *focus_resource; - struct wl_listener focus_listener; - struct weston_view *icon; - struct wl_listener icon_destroy_listener; - int32_t dx, dy; - struct weston_keyboard_grab keyboard_grab; -}; - -struct weston_pointer_drag { - struct weston_drag base; - struct weston_pointer_grab grab; -}; - -struct weston_touch_drag { - struct weston_drag base; - struct weston_touch_grab grab; -}; - -#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \ - WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \ - WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) - -static void -data_offer_accept(struct wl_client *client, struct wl_resource *resource, - uint32_t serial, const char *mime_type) -{ - struct weston_data_offer *offer = wl_resource_get_user_data(resource); - - /* Protect against untimely calls from older data offers */ - if (!offer->source || offer != offer->source->offer) - return; - - /* FIXME: Check that client is currently focused by the input - * device that is currently dragging this data source. Should - * this be a wl_data_device request? */ - - offer->source->accept(offer->source, serial, mime_type); - offer->source->accepted = mime_type != NULL; -} - -static void -data_offer_receive(struct wl_client *client, struct wl_resource *resource, - const char *mime_type, int32_t fd) -{ - struct weston_data_offer *offer = wl_resource_get_user_data(resource); - - if (offer->source && offer == offer->source->offer) - offer->source->send(offer->source, mime_type, fd); - else - close(fd); -} - -static void -data_offer_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -data_source_notify_finish(struct weston_data_source *source) -{ - if (!source->actions_set) - return; - - if (source->offer->in_ask && - wl_resource_get_version(source->resource) >= - WL_DATA_SOURCE_ACTION_SINCE_VERSION) { - wl_data_source_send_action(source->resource, - source->current_dnd_action); - } - - if (wl_resource_get_version(source->resource) >= - WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { - wl_data_source_send_dnd_finished(source->resource); - } - - source->offer = NULL; -} - -static uint32_t -data_offer_choose_action(struct weston_data_offer *offer) -{ - uint32_t available_actions, preferred_action = 0; - uint32_t source_actions, offer_actions; - - if (wl_resource_get_version(offer->resource) >= - WL_DATA_OFFER_ACTION_SINCE_VERSION) { - offer_actions = offer->dnd_actions; - preferred_action = offer->preferred_dnd_action; - } else { - offer_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - } - - if (wl_resource_get_version(offer->source->resource) >= - WL_DATA_SOURCE_ACTION_SINCE_VERSION) - source_actions = offer->source->dnd_actions; - else - source_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - - available_actions = offer_actions & source_actions; - - if (!available_actions) - return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - - if (offer->source->seat && - offer->source->compositor_action & available_actions) - return offer->source->compositor_action; - - /* If the dest side has a preferred DnD action, use it */ - if ((preferred_action & available_actions) != 0) - return preferred_action; - - /* Use the first found action, in bit order */ - return 1 << (ffs(available_actions) - 1); -} - -static void -data_offer_update_action(struct weston_data_offer *offer) -{ - uint32_t action; - - if (!offer->source) - return; - - action = data_offer_choose_action(offer); - - if (offer->source->current_dnd_action == action) - return; - - offer->source->current_dnd_action = action; - - if (offer->in_ask) - return; - - if (wl_resource_get_version(offer->source->resource) >= - WL_DATA_SOURCE_ACTION_SINCE_VERSION) - wl_data_source_send_action(offer->source->resource, action); - - if (wl_resource_get_version(offer->resource) >= - WL_DATA_OFFER_ACTION_SINCE_VERSION) - wl_data_offer_send_action(offer->resource, action); -} - -static void -data_offer_set_actions(struct wl_client *client, - struct wl_resource *resource, - uint32_t dnd_actions, uint32_t preferred_action) -{ - struct weston_data_offer *offer = wl_resource_get_user_data(resource); - - if (dnd_actions & ~ALL_ACTIONS) { - wl_resource_post_error(offer->resource, - WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK, - "invalid action mask %x", dnd_actions); - return; - } - - if (preferred_action && - (!(preferred_action & dnd_actions) || - __builtin_popcount(preferred_action) > 1)) { - wl_resource_post_error(offer->resource, - WL_DATA_OFFER_ERROR_INVALID_ACTION, - "invalid action %x", preferred_action); - return; - } - - offer->dnd_actions = dnd_actions; - offer->preferred_dnd_action = preferred_action; - data_offer_update_action(offer); -} - -static void -data_offer_finish(struct wl_client *client, struct wl_resource *resource) -{ - struct weston_data_offer *offer = wl_resource_get_user_data(resource); - - if (!offer->source || offer->source->offer != offer) - return; - - if (offer->source->set_selection) { - wl_resource_post_error(offer->resource, - WL_DATA_OFFER_ERROR_INVALID_FINISH, - "finish only valid for drag n drop"); - return; - } - - /* Disallow finish while we have a grab driving drag-and-drop, or - * if the negotiation is not at the right stage - */ - if (offer->source->seat || - !offer->source->accepted) { - wl_resource_post_error(offer->resource, - WL_DATA_OFFER_ERROR_INVALID_FINISH, - "premature finish request"); - return; - } - - switch (offer->source->current_dnd_action) { - case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: - case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK: - wl_resource_post_error(offer->resource, - WL_DATA_OFFER_ERROR_INVALID_OFFER, - "offer finished with an invalid action"); - return; - default: - break; - } - - data_source_notify_finish(offer->source); -} - -static const struct wl_data_offer_interface data_offer_interface = { - data_offer_accept, - data_offer_receive, - data_offer_destroy, - data_offer_finish, - data_offer_set_actions, -}; - -static void -destroy_data_offer(struct wl_resource *resource) -{ - struct weston_data_offer *offer = wl_resource_get_user_data(resource); - - if (!offer->source) - goto out; - - wl_list_remove(&offer->source_destroy_listener.link); - - if (offer->source->offer != offer) - goto out; - - /* If the drag destination has version < 3, wl_data_offer.finish - * won't be called, so do this here as a safety net, because - * we still want the version >=3 drag source to be happy. - */ - if (wl_resource_get_version(offer->resource) < - WL_DATA_OFFER_ACTION_SINCE_VERSION) { - data_source_notify_finish(offer->source); - } else if (offer->source->resource && - wl_resource_get_version(offer->source->resource) >= - WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { - wl_data_source_send_cancelled(offer->source->resource); - } - - offer->source->offer = NULL; -out: - free(offer); -} - -static void -destroy_offer_data_source(struct wl_listener *listener, void *data) -{ - struct weston_data_offer *offer; - - offer = container_of(listener, struct weston_data_offer, - source_destroy_listener); - - offer->source = NULL; -} - -static struct weston_data_offer * -weston_data_source_send_offer(struct weston_data_source *source, - struct wl_resource *target) -{ - struct weston_data_offer *offer; - char **p; - - offer = malloc(sizeof *offer); - if (offer == NULL) - return NULL; - - offer->resource = - wl_resource_create(wl_resource_get_client(target), - &wl_data_offer_interface, - wl_resource_get_version(target), 0); - if (offer->resource == NULL) { - free(offer); - return NULL; - } - - wl_resource_set_implementation(offer->resource, &data_offer_interface, - offer, destroy_data_offer); - - offer->in_ask = false; - offer->dnd_actions = 0; - offer->preferred_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - offer->source = source; - offer->source_destroy_listener.notify = destroy_offer_data_source; - wl_signal_add(&source->destroy_signal, - &offer->source_destroy_listener); - - wl_data_device_send_data_offer(target, offer->resource); - - wl_array_for_each(p, &source->mime_types) - wl_data_offer_send_offer(offer->resource, *p); - - source->offer = offer; - source->accepted = false; - - return offer; -} - -static void -data_source_offer(struct wl_client *client, - struct wl_resource *resource, - const char *type) -{ - struct weston_data_source *source = - wl_resource_get_user_data(resource); - char **p; - - p = wl_array_add(&source->mime_types, sizeof *p); - if (p) - *p = strdup(type); - if (!p || !*p) - wl_resource_post_no_memory(resource); -} - -static void -data_source_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -data_source_set_actions(struct wl_client *client, - struct wl_resource *resource, - uint32_t dnd_actions) -{ - struct weston_data_source *source = - wl_resource_get_user_data(resource); - - if (source->actions_set) { - wl_resource_post_error(source->resource, - WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, - "cannot set actions more than once"); - return; - } - - if (dnd_actions & ~ALL_ACTIONS) { - wl_resource_post_error(source->resource, - WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, - "invalid action mask %x", dnd_actions); - return; - } - - if (source->seat) { - wl_resource_post_error(source->resource, - WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, - "invalid action change after " - "wl_data_device.start_drag"); - return; - } - - source->dnd_actions = dnd_actions; - source->actions_set = true; -} - -static struct wl_data_source_interface data_source_interface = { - data_source_offer, - data_source_destroy, - data_source_set_actions -}; - -static void -drag_surface_configure(struct weston_drag *drag, - struct weston_pointer *pointer, - struct weston_touch *touch, - struct weston_surface *es, - int32_t sx, int32_t sy) -{ - struct weston_layer_entry *list; - float fx, fy; - - assert((pointer != NULL && touch == NULL) || - (pointer == NULL && touch != NULL)); - - if (!weston_surface_is_mapped(es) && es->buffer_ref.buffer) { - if (pointer && pointer->sprite && - weston_view_is_mapped(pointer->sprite)) - list = &pointer->sprite->layer_link; - else - list = &es->compositor->cursor_layer.view_list; - - weston_layer_entry_remove(&drag->icon->layer_link); - weston_layer_entry_insert(list, &drag->icon->layer_link); - weston_view_update_transform(drag->icon); - pixman_region32_clear(&es->pending.input); - es->is_mapped = true; - drag->icon->is_mapped = true; - } - - drag->dx += sx; - drag->dy += sy; - - /* init to 0 for avoiding a compile warning */ - fx = fy = 0; - if (pointer) { - fx = wl_fixed_to_double(pointer->x) + drag->dx; - fy = wl_fixed_to_double(pointer->y) + drag->dy; - } else if (touch) { - fx = wl_fixed_to_double(touch->grab_x) + drag->dx; - fy = wl_fixed_to_double(touch->grab_y) + drag->dy; - } - weston_view_set_position(drag->icon, fx, fy); -} - -static int -pointer_drag_surface_get_label(struct weston_surface *surface, - char *buf, size_t len) -{ - return snprintf(buf, len, "pointer drag icon"); -} - -static void -pointer_drag_surface_committed(struct weston_surface *es, - int32_t sx, int32_t sy) -{ - struct weston_pointer_drag *drag = es->committed_private; - struct weston_pointer *pointer = drag->grab.pointer; - - assert(es->committed == pointer_drag_surface_committed); - - drag_surface_configure(&drag->base, pointer, NULL, es, sx, sy); -} - -static int -touch_drag_surface_get_label(struct weston_surface *surface, - char *buf, size_t len) -{ - return snprintf(buf, len, "touch drag icon"); -} - -static void -touch_drag_surface_committed(struct weston_surface *es, int32_t sx, int32_t sy) -{ - struct weston_touch_drag *drag = es->committed_private; - struct weston_touch *touch = drag->grab.touch; - - assert(es->committed == touch_drag_surface_committed); - - drag_surface_configure(&drag->base, NULL, touch, es, sx, sy); -} - -static void -destroy_drag_focus(struct wl_listener *listener, void *data) -{ - struct weston_drag *drag = - container_of(listener, struct weston_drag, focus_listener); - - drag->focus_resource = NULL; -} - -static void -weston_drag_set_focus(struct weston_drag *drag, - struct weston_seat *seat, - struct weston_view *view, - wl_fixed_t sx, wl_fixed_t sy) -{ - struct wl_resource *resource, *offer_resource = NULL; - struct wl_display *display = seat->compositor->wl_display; - struct weston_data_offer *offer; - uint32_t serial; - - if (drag->focus && view && drag->focus->surface == view->surface) { - drag->focus = view; - return; - } - - if (drag->focus_resource) { - wl_data_device_send_leave(drag->focus_resource); - wl_list_remove(&drag->focus_listener.link); - drag->focus_resource = NULL; - drag->focus = NULL; - } - - if (!view || !view->surface->resource) - return; - - if (!drag->data_source && - wl_resource_get_client(view->surface->resource) != drag->client) - return; - - if (drag->data_source && - drag->data_source->offer) { - /* Unlink the offer from the source */ - offer = drag->data_source->offer; - offer->source = NULL; - drag->data_source->offer = NULL; - wl_list_remove(&offer->source_destroy_listener.link); - } - - resource = wl_resource_find_for_client(&seat->drag_resource_list, - wl_resource_get_client(view->surface->resource)); - if (!resource) - return; - - serial = wl_display_next_serial(display); - - if (drag->data_source) { - drag->data_source->accepted = false; - offer = weston_data_source_send_offer(drag->data_source, resource); - if (offer == NULL) - return; - - data_offer_update_action(offer); - - offer_resource = offer->resource; - if (wl_resource_get_version (offer_resource) >= - WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) { - wl_data_offer_send_source_actions (offer_resource, - drag->data_source->dnd_actions); - } - } - - wl_data_device_send_enter(resource, serial, view->surface->resource, - sx, sy, offer_resource); - - drag->focus = view; - drag->focus_listener.notify = destroy_drag_focus; - wl_resource_add_destroy_listener(resource, &drag->focus_listener); - drag->focus_resource = resource; -} - -static void -drag_grab_focus(struct weston_pointer_grab *grab) -{ - struct weston_pointer_drag *drag = - container_of(grab, struct weston_pointer_drag, grab); - struct weston_pointer *pointer = grab->pointer; - struct weston_view *view; - wl_fixed_t sx, sy; - - view = weston_compositor_pick_view(pointer->seat->compositor, - pointer->x, pointer->y, - &sx, &sy); - if (drag->base.focus != view) - weston_drag_set_focus(&drag->base, pointer->seat, view, sx, sy); -} - -static void -drag_grab_motion(struct weston_pointer_grab *grab, - const struct timespec *time, - struct weston_pointer_motion_event *event) -{ - struct weston_pointer_drag *drag = - container_of(grab, struct weston_pointer_drag, grab); - struct weston_pointer *pointer = drag->grab.pointer; - float fx, fy; - wl_fixed_t sx, sy; - uint32_t msecs; - - weston_pointer_move(pointer, event); - - if (drag->base.icon) { - fx = wl_fixed_to_double(pointer->x) + drag->base.dx; - fy = wl_fixed_to_double(pointer->y) + drag->base.dy; - weston_view_set_position(drag->base.icon, fx, fy); - weston_view_schedule_repaint(drag->base.icon); - } - - if (drag->base.focus_resource) { - msecs = timespec_to_msec(time); - weston_view_from_global_fixed(drag->base.focus, - pointer->x, pointer->y, - &sx, &sy); - - wl_data_device_send_motion(drag->base.focus_resource, msecs, sx, sy); - } -} - -static void -data_device_end_drag_grab(struct weston_drag *drag, - struct weston_seat *seat) -{ - if (drag->icon) { - if (weston_view_is_mapped(drag->icon)) - weston_view_unmap(drag->icon); - - drag->icon->surface->committed = NULL; - weston_surface_set_label_func(drag->icon->surface, NULL); - pixman_region32_clear(&drag->icon->surface->pending.input); - wl_list_remove(&drag->icon_destroy_listener.link); - weston_view_destroy(drag->icon); - } - - weston_drag_set_focus(drag, seat, NULL, 0, 0); -} - -static void -data_device_end_pointer_drag_grab(struct weston_pointer_drag *drag) -{ - struct weston_pointer *pointer = drag->grab.pointer; - struct weston_keyboard *keyboard = drag->base.keyboard_grab.keyboard; - - data_device_end_drag_grab(&drag->base, pointer->seat); - weston_pointer_end_grab(pointer); - weston_keyboard_end_grab(keyboard); - free(drag); -} - -static void -drag_grab_button(struct weston_pointer_grab *grab, - const struct timespec *time, - uint32_t button, uint32_t state_w) -{ - struct weston_pointer_drag *drag = - container_of(grab, struct weston_pointer_drag, grab); - struct weston_pointer *pointer = drag->grab.pointer; - enum wl_pointer_button_state state = state_w; - struct weston_data_source *data_source = drag->base.data_source; - - if (data_source && - pointer->grab_button == button && - state == WL_POINTER_BUTTON_STATE_RELEASED) { - if (drag->base.focus_resource && - data_source->accepted && - data_source->current_dnd_action) { - wl_data_device_send_drop(drag->base.focus_resource); - - if (wl_resource_get_version(data_source->resource) >= - WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) - wl_data_source_send_dnd_drop_performed(data_source->resource); - - data_source->offer->in_ask = - data_source->current_dnd_action == - WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; - - data_source->seat = NULL; - } else if (wl_resource_get_version(data_source->resource) >= - WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { - wl_data_source_send_cancelled(data_source->resource); - } - } - - if (pointer->button_count == 0 && - state == WL_POINTER_BUTTON_STATE_RELEASED) { - if (drag->base.data_source) - wl_list_remove(&drag->base.data_source_listener.link); - data_device_end_pointer_drag_grab(drag); - } -} - -static void -drag_grab_axis(struct weston_pointer_grab *grab, - const struct timespec *time, - struct weston_pointer_axis_event *event) -{ -} - -static void -drag_grab_axis_source(struct weston_pointer_grab *grab, uint32_t source) -{ -} - -static void -drag_grab_frame(struct weston_pointer_grab *grab) -{ -} - -static void -drag_grab_cancel(struct weston_pointer_grab *grab) -{ - struct weston_pointer_drag *drag = - container_of(grab, struct weston_pointer_drag, grab); - - if (drag->base.data_source) - wl_list_remove(&drag->base.data_source_listener.link); - - data_device_end_pointer_drag_grab(drag); -} - -static const struct weston_pointer_grab_interface pointer_drag_grab_interface = { - drag_grab_focus, - drag_grab_motion, - drag_grab_button, - drag_grab_axis, - drag_grab_axis_source, - drag_grab_frame, - drag_grab_cancel, -}; - -static void -drag_grab_touch_down(struct weston_touch_grab *grab, - const struct timespec *time, int touch_id, - wl_fixed_t sx, wl_fixed_t sy) -{ -} - -static void -data_device_end_touch_drag_grab(struct weston_touch_drag *drag) -{ - struct weston_touch *touch = drag->grab.touch; - struct weston_keyboard *keyboard = drag->base.keyboard_grab.keyboard; - - data_device_end_drag_grab(&drag->base, touch->seat); - weston_touch_end_grab(touch); - weston_keyboard_end_grab(keyboard); - free(drag); -} - -static void -drag_grab_touch_up(struct weston_touch_grab *grab, - const struct timespec *time, int touch_id) -{ - struct weston_touch_drag *touch_drag = - container_of(grab, struct weston_touch_drag, grab); - struct weston_touch *touch = grab->touch; - - if (touch_id != touch->grab_touch_id) - return; - - if (touch_drag->base.focus_resource) - wl_data_device_send_drop(touch_drag->base.focus_resource); - if (touch_drag->base.data_source) - wl_list_remove(&touch_drag->base.data_source_listener.link); - data_device_end_touch_drag_grab(touch_drag); -} - -static void -drag_grab_touch_focus(struct weston_touch_drag *drag) -{ - struct weston_touch *touch = drag->grab.touch; - struct weston_view *view; - wl_fixed_t view_x, view_y; - - view = weston_compositor_pick_view(touch->seat->compositor, - touch->grab_x, touch->grab_y, - &view_x, &view_y); - if (drag->base.focus != view) - weston_drag_set_focus(&drag->base, touch->seat, - view, view_x, view_y); -} - -static void -drag_grab_touch_motion(struct weston_touch_grab *grab, - const struct timespec *time, - int touch_id, wl_fixed_t x, wl_fixed_t y) -{ - struct weston_touch_drag *touch_drag = - container_of(grab, struct weston_touch_drag, grab); - struct weston_touch *touch = grab->touch; - wl_fixed_t view_x, view_y; - float fx, fy; - uint32_t msecs; - - if (touch_id != touch->grab_touch_id) - return; - - drag_grab_touch_focus(touch_drag); - if (touch_drag->base.icon) { - fx = wl_fixed_to_double(touch->grab_x) + touch_drag->base.dx; - fy = wl_fixed_to_double(touch->grab_y) + touch_drag->base.dy; - weston_view_set_position(touch_drag->base.icon, fx, fy); - weston_view_schedule_repaint(touch_drag->base.icon); - } - - if (touch_drag->base.focus_resource) { - msecs = timespec_to_msec(time); - weston_view_from_global_fixed(touch_drag->base.focus, - touch->grab_x, touch->grab_y, - &view_x, &view_y); - wl_data_device_send_motion(touch_drag->base.focus_resource, - msecs, view_x, view_y); - } -} - -static void -drag_grab_touch_frame(struct weston_touch_grab *grab) -{ -} - -static void -drag_grab_touch_cancel(struct weston_touch_grab *grab) -{ - struct weston_touch_drag *touch_drag = - container_of(grab, struct weston_touch_drag, grab); - - if (touch_drag->base.data_source) - wl_list_remove(&touch_drag->base.data_source_listener.link); - data_device_end_touch_drag_grab(touch_drag); -} - -static const struct weston_touch_grab_interface touch_drag_grab_interface = { - drag_grab_touch_down, - drag_grab_touch_up, - drag_grab_touch_motion, - drag_grab_touch_frame, - drag_grab_touch_cancel -}; - -static void -drag_grab_keyboard_key(struct weston_keyboard_grab *grab, - const struct timespec *time, uint32_t key, uint32_t state) -{ -} - -static void -drag_grab_keyboard_modifiers(struct weston_keyboard_grab *grab, - uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) -{ - struct weston_keyboard *keyboard = grab->keyboard; - struct weston_drag *drag = - container_of(grab, struct weston_drag, keyboard_grab); - uint32_t compositor_action; - - if (mods_depressed & (1 << keyboard->xkb_info->shift_mod)) - compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; - else if (mods_depressed & (1 << keyboard->xkb_info->ctrl_mod)) - compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - else - compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - - drag->data_source->compositor_action = compositor_action; - - if (drag->data_source->offer) - data_offer_update_action(drag->data_source->offer); -} - -static void -drag_grab_keyboard_cancel(struct weston_keyboard_grab *grab) -{ - struct weston_drag *drag = - container_of(grab, struct weston_drag, keyboard_grab); - struct weston_pointer *pointer = grab->keyboard->seat->pointer_state; - struct weston_touch *touch = grab->keyboard->seat->touch_state; - - if (pointer && pointer->grab->interface == &pointer_drag_grab_interface) { - struct weston_touch_drag *touch_drag = - (struct weston_touch_drag *) drag; - drag_grab_touch_cancel(&touch_drag->grab); - } else if (touch && touch->grab->interface == &touch_drag_grab_interface) { - struct weston_pointer_drag *pointer_drag = - (struct weston_pointer_drag *) drag; - drag_grab_cancel(&pointer_drag->grab); - } -} - -static const struct weston_keyboard_grab_interface keyboard_drag_grab_interface = { - drag_grab_keyboard_key, - drag_grab_keyboard_modifiers, - drag_grab_keyboard_cancel -}; - -static void -destroy_pointer_data_device_source(struct wl_listener *listener, void *data) -{ - struct weston_pointer_drag *drag = container_of(listener, - struct weston_pointer_drag, base.data_source_listener); - - data_device_end_pointer_drag_grab(drag); -} - -static void -handle_drag_icon_destroy(struct wl_listener *listener, void *data) -{ - struct weston_drag *drag = container_of(listener, struct weston_drag, - icon_destroy_listener); - - drag->icon = NULL; -} - -WL_EXPORT int -weston_pointer_start_drag(struct weston_pointer *pointer, - struct weston_data_source *source, - struct weston_surface *icon, - struct wl_client *client) -{ - struct weston_pointer_drag *drag; - struct weston_keyboard *keyboard = - weston_seat_get_keyboard(pointer->seat); - - drag = zalloc(sizeof *drag); - if (drag == NULL) - return -1; - - drag->grab.interface = &pointer_drag_grab_interface; - drag->base.keyboard_grab.interface = &keyboard_drag_grab_interface; - drag->base.client = client; - drag->base.data_source = source; - - if (icon) { - drag->base.icon = weston_view_create(icon); - if (drag->base.icon == NULL) { - free(drag); - return -1; - } - - drag->base.icon_destroy_listener.notify = handle_drag_icon_destroy; - wl_signal_add(&icon->destroy_signal, - &drag->base.icon_destroy_listener); - - icon->committed = pointer_drag_surface_committed; - icon->committed_private = drag; - weston_surface_set_label_func(icon, - pointer_drag_surface_get_label); - } else { - drag->base.icon = NULL; - } - - if (source) { - drag->base.data_source_listener.notify = destroy_pointer_data_device_source; - wl_signal_add(&source->destroy_signal, - &drag->base.data_source_listener); - } - - weston_pointer_clear_focus(pointer); - weston_keyboard_set_focus(keyboard, NULL); - - weston_pointer_start_grab(pointer, &drag->grab); - weston_keyboard_start_grab(keyboard, &drag->base.keyboard_grab); - - return 0; -} - -static void -destroy_touch_data_device_source(struct wl_listener *listener, void *data) -{ - struct weston_touch_drag *drag = container_of(listener, - struct weston_touch_drag, base.data_source_listener); - - data_device_end_touch_drag_grab(drag); -} - -WL_EXPORT int -weston_touch_start_drag(struct weston_touch *touch, - struct weston_data_source *source, - struct weston_surface *icon, - struct wl_client *client) -{ - struct weston_touch_drag *drag; - struct weston_keyboard *keyboard = - weston_seat_get_keyboard(touch->seat); - - drag = zalloc(sizeof *drag); - if (drag == NULL) - return -1; - - drag->grab.interface = &touch_drag_grab_interface; - drag->base.client = client; - drag->base.data_source = source; - - if (icon) { - drag->base.icon = weston_view_create(icon); - if (drag->base.icon == NULL) { - free(drag); - return -1; - } - - drag->base.icon_destroy_listener.notify = handle_drag_icon_destroy; - wl_signal_add(&icon->destroy_signal, - &drag->base.icon_destroy_listener); - - icon->committed = touch_drag_surface_committed; - icon->committed_private = drag; - weston_surface_set_label_func(icon, - touch_drag_surface_get_label); - } else { - drag->base.icon = NULL; - } - - if (source) { - drag->base.data_source_listener.notify = destroy_touch_data_device_source; - wl_signal_add(&source->destroy_signal, - &drag->base.data_source_listener); - } - - weston_keyboard_set_focus(keyboard, NULL); - - weston_touch_start_grab(touch, &drag->grab); - weston_keyboard_start_grab(keyboard, &drag->base.keyboard_grab); - - drag_grab_touch_focus(drag); - - return 0; -} - -static void -data_device_start_drag(struct wl_client *client, struct wl_resource *resource, - struct wl_resource *source_resource, - struct wl_resource *origin_resource, - struct wl_resource *icon_resource, uint32_t serial) -{ - struct weston_seat *seat = wl_resource_get_user_data(resource); - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - struct weston_touch *touch = weston_seat_get_touch(seat); - struct weston_surface *origin = wl_resource_get_user_data(origin_resource); - struct weston_data_source *source = NULL; - struct weston_surface *icon = NULL; - int is_pointer_grab, is_touch_grab; - int32_t ret = 0; - - is_pointer_grab = pointer && - pointer->button_count == 1 && - pointer->grab_serial == serial && - pointer->focus && - pointer->focus->surface == origin; - - is_touch_grab = touch && - touch->num_tp == 1 && - touch->grab_serial == serial && - touch->focus && - touch->focus->surface == origin; - - if (!is_pointer_grab && !is_touch_grab) - return; - - /* FIXME: Check that the data source type array isn't empty. */ - - if (source_resource) - source = wl_resource_get_user_data(source_resource); - if (icon_resource) - icon = wl_resource_get_user_data(icon_resource); - - if (icon) { - if (weston_surface_set_role(icon, "wl_data_device-icon", - resource, - WL_DATA_DEVICE_ERROR_ROLE) < 0) - return; - } - - if (is_pointer_grab) - ret = weston_pointer_start_drag(pointer, source, icon, client); - else if (is_touch_grab) - ret = weston_touch_start_drag(touch, source, icon, client); - - if (ret < 0) - wl_resource_post_no_memory(resource); - else - source->seat = seat; -} - -static void -destroy_selection_data_source(struct wl_listener *listener, void *data) -{ - struct weston_seat *seat = container_of(listener, struct weston_seat, - selection_data_source_listener); - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct wl_resource *data_device; - struct weston_surface *focus = NULL; - - seat->selection_data_source = NULL; - - if (keyboard) - focus = keyboard->focus; - if (focus && focus->resource) { - data_device = wl_resource_find_for_client(&seat->drag_resource_list, - wl_resource_get_client(focus->resource)); - if (data_device) - wl_data_device_send_selection(data_device, NULL); - } - - wl_signal_emit(&seat->selection_signal, seat); -} - -/** \brief Send the selection to the specified client - * - * This function creates a new wl_data_offer if there is a wl_data_source - * currently set as the selection and sends it to the specified client, - * followed by the wl_data_device.selection() event. - * If there is no current selection the wl_data_device.selection() event - * will carry a NULL wl_data_offer. - * - * If the client does not have a wl_data_device for the specified seat - * nothing will be done. - * - * \param seat The seat owning the wl_data_device used to send the events. - * \param client The client to which to send the selection. - */ -WL_EXPORT void -weston_seat_send_selection(struct weston_seat *seat, struct wl_client *client) -{ - struct weston_data_offer *offer; - struct wl_resource *data_device; - - wl_resource_for_each(data_device, &seat->drag_resource_list) { - if (wl_resource_get_client(data_device) != client) - continue; - - if (seat->selection_data_source) { - offer = weston_data_source_send_offer(seat->selection_data_source, - data_device); - wl_data_device_send_selection(data_device, offer->resource); - } else { - wl_data_device_send_selection(data_device, NULL); - } - } -} - -WL_EXPORT void -weston_seat_set_selection(struct weston_seat *seat, - struct weston_data_source *source, uint32_t serial) -{ - struct weston_surface *focus = NULL; - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - - if (seat->selection_data_source && - seat->selection_serial - serial < UINT32_MAX / 2) - return; - - if (seat->selection_data_source) { - seat->selection_data_source->cancel(seat->selection_data_source); - wl_list_remove(&seat->selection_data_source_listener.link); - seat->selection_data_source = NULL; - } - - seat->selection_data_source = source; - seat->selection_serial = serial; - - if (source) - source->set_selection = true; - - if (keyboard) - focus = keyboard->focus; - if (focus && focus->resource) { - weston_seat_send_selection(seat, wl_resource_get_client(focus->resource)); - } - - wl_signal_emit(&seat->selection_signal, seat); - - if (source) { - seat->selection_data_source_listener.notify = - destroy_selection_data_source; - wl_signal_add(&source->destroy_signal, - &seat->selection_data_source_listener); - } -} - -static void -data_device_set_selection(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *source_resource, uint32_t serial) -{ - struct weston_seat *seat = wl_resource_get_user_data(resource); - struct weston_data_source *source; - - if (!seat || !source_resource) - return; - - source = wl_resource_get_user_data(source_resource); - - if (source->actions_set) { - wl_resource_post_error(source_resource, - WL_DATA_SOURCE_ERROR_INVALID_SOURCE, - "cannot set drag-and-drop source as selection"); - return; - } - - /* FIXME: Store serial and check against incoming serial here. */ - weston_seat_set_selection(seat, source, serial); -} -static void -data_device_release(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct wl_data_device_interface data_device_interface = { - data_device_start_drag, - data_device_set_selection, - data_device_release -}; - -static void -destroy_data_source(struct wl_resource *resource) -{ - struct weston_data_source *source = - wl_resource_get_user_data(resource); - char **p; - - wl_signal_emit(&source->destroy_signal, source); - - wl_array_for_each(p, &source->mime_types) - free(*p); - - wl_array_release(&source->mime_types); - - free(source); -} - -static void -client_source_accept(struct weston_data_source *source, - uint32_t time, const char *mime_type) -{ - wl_data_source_send_target(source->resource, mime_type); -} - -static void -client_source_send(struct weston_data_source *source, - const char *mime_type, int32_t fd) -{ - wl_data_source_send_send(source->resource, mime_type, fd); - close(fd); -} - -static void -client_source_cancel(struct weston_data_source *source) -{ - wl_data_source_send_cancelled(source->resource); -} - -static void -create_data_source(struct wl_client *client, - struct wl_resource *resource, uint32_t id) -{ - struct weston_data_source *source; - - source = malloc(sizeof *source); - if (source == NULL) { - wl_resource_post_no_memory(resource); - return; - } - - source->resource = - wl_resource_create(client, &wl_data_source_interface, - wl_resource_get_version(resource), id); - if (source->resource == NULL) { - free(source); - wl_resource_post_no_memory(resource); - return; - } - - wl_signal_init(&source->destroy_signal); - source->accept = client_source_accept; - source->send = client_source_send; - source->cancel = client_source_cancel; - source->offer = NULL; - source->accepted = false; - source->seat = NULL; - source->actions_set = false; - source->dnd_actions = 0; - source->current_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - source->compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - source->set_selection = false; - - wl_array_init(&source->mime_types); - - wl_resource_set_implementation(source->resource, &data_source_interface, - source, destroy_data_source); -} - -static void unbind_data_device(struct wl_resource *resource) -{ - wl_list_remove(wl_resource_get_link(resource)); -} - -static void -get_data_device(struct wl_client *client, - struct wl_resource *manager_resource, - uint32_t id, struct wl_resource *seat_resource) -{ - struct weston_seat *seat = wl_resource_get_user_data(seat_resource); - struct wl_resource *resource; - - resource = wl_resource_create(client, - &wl_data_device_interface, - wl_resource_get_version(manager_resource), - id); - if (resource == NULL) { - wl_resource_post_no_memory(manager_resource); - return; - } - - if (seat) { - wl_list_insert(&seat->drag_resource_list, - wl_resource_get_link(resource)); - } else { - wl_list_init(wl_resource_get_link(resource)); - } - - wl_resource_set_implementation(resource, &data_device_interface, - seat, unbind_data_device); -} - -static const struct wl_data_device_manager_interface manager_interface = { - create_data_source, - get_data_device -}; - -static void -bind_manager(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct wl_resource *resource; - - resource = wl_resource_create(client, - &wl_data_device_manager_interface, - version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, &manager_interface, - NULL, NULL); -} - -WL_EXPORT void -wl_data_device_set_keyboard_focus(struct weston_seat *seat) -{ - struct weston_surface *focus; - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - - if (!keyboard) - return; - - focus = keyboard->focus; - if (!focus || !focus->resource) - return; - - weston_seat_send_selection(seat, wl_resource_get_client(focus->resource)); -} - -WL_EXPORT int -wl_data_device_manager_init(struct wl_display *display) -{ - if (wl_global_create(display, - &wl_data_device_manager_interface, 3, - NULL, bind_manager) == NULL) - return -1; - - return 0; -} diff --git a/libweston/dbus.c b/libweston/dbus.c deleted file mode 100644 index 91f2be7e..00000000 --- a/libweston/dbus.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright © 2013 David Herrmann <dh.herrmann@gmail.com> - * - * 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. - */ - -/* - * DBus Helpers - * This file contains the dbus mainloop integration and several helpers to - * make lowlevel libdbus access easier. - */ - -#include "config.h" - -#include <dbus/dbus.h> -#include <errno.h> -#include <fcntl.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/eventfd.h> -#include <sys/timerfd.h> -#include <unistd.h> -#include <wayland-server.h> - -#include <libweston/libweston.h> -#include "dbus.h" - -/* - * DBus Mainloop Integration - * weston_dbus_bind() and weston_dbus_unbind() allow to bind an existing - * DBusConnection to an existing wl_event_loop object. All dbus dispatching - * is then nicely integrated into the wayland event loop. - * Note that this only provides basic watch and timeout dispatching. No - * remote thread wakeup, signal handling or other dbus insanity is supported. - * This is fine as long as you don't use any of the deprecated libdbus - * interfaces (like waking up remote threads..). There is really no rational - * reason to support these. - */ - -static int weston_dbus_dispatch_watch(int fd, uint32_t mask, void *data) -{ - DBusWatch *watch = data; - uint32_t flags = 0; - - if (dbus_watch_get_enabled(watch)) { - if (mask & WL_EVENT_READABLE) - flags |= DBUS_WATCH_READABLE; - if (mask & WL_EVENT_WRITABLE) - flags |= DBUS_WATCH_WRITABLE; - if (mask & WL_EVENT_HANGUP) - flags |= DBUS_WATCH_HANGUP; - if (mask & WL_EVENT_ERROR) - flags |= DBUS_WATCH_ERROR; - - dbus_watch_handle(watch, flags); - } - - return 0; -} - -static dbus_bool_t weston_dbus_add_watch(DBusWatch *watch, void *data) -{ - struct wl_event_loop *loop = data; - struct wl_event_source *s; - int fd; - uint32_t mask = 0, flags; - - if (dbus_watch_get_enabled(watch)) { - flags = dbus_watch_get_flags(watch); - if (flags & DBUS_WATCH_READABLE) - mask |= WL_EVENT_READABLE; - if (flags & DBUS_WATCH_WRITABLE) - mask |= WL_EVENT_WRITABLE; - } - - fd = dbus_watch_get_unix_fd(watch); - s = wl_event_loop_add_fd(loop, fd, mask, weston_dbus_dispatch_watch, - watch); - if (!s) - return FALSE; - - dbus_watch_set_data(watch, s, NULL); - return TRUE; -} - -static void weston_dbus_remove_watch(DBusWatch *watch, void *data) -{ - struct wl_event_source *s; - - s = dbus_watch_get_data(watch); - if (!s) - return; - - wl_event_source_remove(s); -} - -static void weston_dbus_toggle_watch(DBusWatch *watch, void *data) -{ - struct wl_event_source *s; - uint32_t mask = 0, flags; - - s = dbus_watch_get_data(watch); - if (!s) - return; - - if (dbus_watch_get_enabled(watch)) { - flags = dbus_watch_get_flags(watch); - if (flags & DBUS_WATCH_READABLE) - mask |= WL_EVENT_READABLE; - if (flags & DBUS_WATCH_WRITABLE) - mask |= WL_EVENT_WRITABLE; - } - - wl_event_source_fd_update(s, mask); -} - -static int weston_dbus_dispatch_timeout(void *data) -{ - DBusTimeout *timeout = data; - - if (dbus_timeout_get_enabled(timeout)) - dbus_timeout_handle(timeout); - - return 0; -} - -static int weston_dbus_adjust_timeout(DBusTimeout *timeout, - struct wl_event_source *s) -{ - int64_t t = 0; - - if (dbus_timeout_get_enabled(timeout)) - t = dbus_timeout_get_interval(timeout); - - return wl_event_source_timer_update(s, t); -} - -static dbus_bool_t weston_dbus_add_timeout(DBusTimeout *timeout, void *data) -{ - struct wl_event_loop *loop = data; - struct wl_event_source *s; - int r; - - s = wl_event_loop_add_timer(loop, weston_dbus_dispatch_timeout, - timeout); - if (!s) - return FALSE; - - r = weston_dbus_adjust_timeout(timeout, s); - if (r < 0) { - wl_event_source_remove(s); - return FALSE; - } - - dbus_timeout_set_data(timeout, s, NULL); - return TRUE; -} - -static void weston_dbus_remove_timeout(DBusTimeout *timeout, void *data) -{ - struct wl_event_source *s; - - s = dbus_timeout_get_data(timeout); - if (!s) - return; - - wl_event_source_remove(s); -} - -static void weston_dbus_toggle_timeout(DBusTimeout *timeout, void *data) -{ - struct wl_event_source *s; - - s = dbus_timeout_get_data(timeout); - if (!s) - return; - - weston_dbus_adjust_timeout(timeout, s); -} - -static int weston_dbus_dispatch(int fd, uint32_t mask, void *data) -{ - DBusConnection *c = data; - int r; - - do { - r = dbus_connection_dispatch(c); - if (r == DBUS_DISPATCH_COMPLETE) - r = 0; - else if (r == DBUS_DISPATCH_DATA_REMAINS) - r = -EAGAIN; - else if (r == DBUS_DISPATCH_NEED_MEMORY) - r = -ENOMEM; - else - r = -EIO; - } while (r == -EAGAIN); - - if (r) - weston_log("cannot dispatch dbus events: %d\n", r); - - return 0; -} - -static int weston_dbus_bind(struct wl_event_loop *loop, DBusConnection *c, - struct wl_event_source **ctx_out) -{ - bool b; - int r, fd; - - /* Idle events cannot reschedule themselves, therefore we use a dummy - * event-fd and mark it for post-dispatch. Hence, the dbus - * dispatcher is called after every dispatch-round. - * This is required as dbus doesn't allow dispatching events from - * within its own event sources. */ - fd = eventfd(0, EFD_CLOEXEC); - if (fd < 0) - return -errno; - - *ctx_out = wl_event_loop_add_fd(loop, fd, 0, weston_dbus_dispatch, c); - close(fd); - - if (!*ctx_out) - return -ENOMEM; - - wl_event_source_check(*ctx_out); - - b = dbus_connection_set_watch_functions(c, - weston_dbus_add_watch, - weston_dbus_remove_watch, - weston_dbus_toggle_watch, - loop, - NULL); - if (!b) { - r = -ENOMEM; - goto error; - } - - b = dbus_connection_set_timeout_functions(c, - weston_dbus_add_timeout, - weston_dbus_remove_timeout, - weston_dbus_toggle_timeout, - loop, - NULL); - if (!b) { - r = -ENOMEM; - goto error; - } - - dbus_connection_ref(c); - return 0; - -error: - dbus_connection_set_timeout_functions(c, NULL, NULL, NULL, - NULL, NULL); - dbus_connection_set_watch_functions(c, NULL, NULL, NULL, - NULL, NULL); - wl_event_source_remove(*ctx_out); - *ctx_out = NULL; - return r; -} - -static void weston_dbus_unbind(DBusConnection *c, struct wl_event_source *ctx) -{ - dbus_connection_set_timeout_functions(c, NULL, NULL, NULL, - NULL, NULL); - dbus_connection_set_watch_functions(c, NULL, NULL, NULL, - NULL, NULL); - dbus_connection_unref(c); - wl_event_source_remove(ctx); -} - -/* - * Convenience Helpers - * Several convenience helpers are provided to make using dbus in weston - * easier. We don't use any of the gdbus or qdbus helpers as they pull in - * huge dependencies and actually are quite awful to use. Instead, we only - * use the basic low-level libdbus library. - */ - -int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus, - DBusConnection **out, struct wl_event_source **ctx_out) -{ - DBusConnection *c; - int r; - - /* Ihhh, global state.. stupid dbus. */ - dbus_connection_set_change_sigpipe(FALSE); - - /* This is actually synchronous. It blocks for some authentication and - * setup. We just trust the dbus-server here and accept this blocking - * call. There is no real reason to complicate things further and make - * this asynchronous/non-blocking. A context should be created during - * thead/process/app setup, so blocking calls should be fine. */ - c = dbus_bus_get_private(bus, NULL); - if (!c) - return -EIO; - - dbus_connection_set_exit_on_disconnect(c, FALSE); - - r = weston_dbus_bind(loop, c, ctx_out); - if (r < 0) - goto error; - - *out = c; - return r; - -error: - dbus_connection_close(c); - dbus_connection_unref(c); - return r; -} - -void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx) -{ - weston_dbus_unbind(c, ctx); - dbus_connection_close(c); - dbus_connection_unref(c); -} - -int weston_dbus_add_match(DBusConnection *c, const char *format, ...) -{ - DBusError err; - int r; - va_list list; - char *str; - - va_start(list, format); - r = vasprintf(&str, format, list); - va_end(list); - - if (r < 0) - return -ENOMEM; - - dbus_error_init(&err); - dbus_bus_add_match(c, str, &err); - free(str); - if (dbus_error_is_set(&err)) { - dbus_error_free(&err); - return -EIO; - } - - return 0; -} - -int weston_dbus_add_match_signal(DBusConnection *c, const char *sender, - const char *iface, const char *member, - const char *path) -{ - return weston_dbus_add_match(c, - "type='signal'," - "sender='%s'," - "interface='%s'," - "member='%s'," - "path='%s'", - sender, iface, member, path); -} - -void weston_dbus_remove_match(DBusConnection *c, const char *format, ...) -{ - int r; - va_list list; - char *str; - - va_start(list, format); - r = vasprintf(&str, format, list); - va_end(list); - - if (r < 0) - return; - - dbus_bus_remove_match(c, str, NULL); - free(str); -} - -void weston_dbus_remove_match_signal(DBusConnection *c, const char *sender, - const char *iface, const char *member, - const char *path) -{ - weston_dbus_remove_match(c, - "type='signal'," - "sender='%s'," - "interface='%s'," - "member='%s'," - "path='%s'", - sender, iface, member, path); -} diff --git a/libweston/dbus.h b/libweston/dbus.h deleted file mode 100644 index 639946ce..00000000 --- a/libweston/dbus.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright © 2013 David Herrmann <dh.herrmann@gmail.com> - * - * 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. - */ - -#ifndef _WESTON_DBUS_H_ -#define _WESTON_DBUS_H_ - -#include "config.h" - -#include <errno.h> -#include <wayland-server.h> - -#include <libweston/libweston.h> - -#ifdef HAVE_DBUS - -#include <dbus/dbus.h> - -/* - * weston_dbus_open() - Open new dbus connection - * - * Opens a new dbus connection to the bus given as @bus. It automatically - * integrates the new connection into the main-loop @loop. The connection - * itself is returned in @out. - * This also returns a context source used for dbus dispatching. It is - * returned on success in @ctx_out and must be passed to weston_dbus_close() - * unchanged. You must not access it from outside of a dbus helper! - * - * Returns 0 on success, negative error code on failure. - */ -int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus, - DBusConnection **out, struct wl_event_source **ctx_out); - -/* - * weston_dbus_close() - Close dbus connection - * - * Closes a dbus connection that was previously opened via weston_dbus_open(). - * It unbinds the connection from the main-loop it was previously bound to, - * closes the dbus connection and frees all resources. If you want to access - * @c after this call returns, you must hold a dbus-reference to it. But - * notice that the connection is closed after this returns so it cannot be - * used to spawn new dbus requests. - * You must pass the context source returns by weston_dbus_open() as @ctx. - */ -void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx); - -/* - * weston_dbus_add_match() - Add dbus match - * - * Configure a dbus-match on the given dbus-connection. This match is saved - * on the dbus-server as long as the connection is open. See dbus-manual - * for information. Compared to the dbus_bus_add_match() this allows a - * var-arg formatted match-string. - */ -int weston_dbus_add_match(DBusConnection *c, const char *format, ...); - -/* - * weston_dbus_add_match_signal() - Add dbus signal match - * - * Same as weston_dbus_add_match() but does the dbus-match formatting for - * signals internally. - */ -int weston_dbus_add_match_signal(DBusConnection *c, const char *sender, - const char *iface, const char *member, - const char *path); - -/* - * weston_dbus_remove_match() - Remove dbus match - * - * Remove a previously configured dbus-match from the dbus server. There is - * no need to remove dbus-matches if you close the connection, anyway. - * Compared to dbus_bus_remove_match() this allows a var-arg formatted - * match string. - */ -void weston_dbus_remove_match(DBusConnection *c, const char *format, ...); - -/* - * weston_dbus_remove_match_signal() - Remove dbus signal match - * - * Same as weston_dbus_remove_match() but does the dbus-match formatting for - * signals internally. - */ -void weston_dbus_remove_match_signal(DBusConnection *c, const char *sender, - const char *iface, const char *member, - const char *path); - -#endif /* HAVE_DBUS */ - -#endif // _WESTON_DBUS_H_ diff --git a/libweston/git-version.h.meson b/libweston/git-version.h.meson deleted file mode 100644 index d91f19c4..00000000 --- a/libweston/git-version.h.meson +++ /dev/null @@ -1 +0,0 @@ -#define BUILD_ID "@VCS_TAG@" diff --git a/libweston/input.c b/libweston/input.c deleted file mode 100644 index 28dcb0b9..00000000 --- a/libweston/input.c +++ /dev/null @@ -1,5086 +0,0 @@ -/* - * Copyright © 2013 Intel Corporation - * Copyright 2017-2018 Collabora, Ltd. - * Copyright 2017-2018 General Electric Company - * - * 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 <stdbool.h> -#include <stdlib.h> -#include <stdint.h> -#include <string.h> -#include <sys/mman.h> -#include <assert.h> -#include <unistd.h> -#include <values.h> -#include <fcntl.h> -#include <limits.h> -#include <errno.h> - -#include "shared/helpers.h" -#include "shared/os-compatibility.h" -#include "shared/timespec-util.h" -#include <libweston/libweston.h> -#include "backend.h" -#include "libweston-internal.h" -#include "relative-pointer-unstable-v1-server-protocol.h" -#include "pointer-constraints-unstable-v1-server-protocol.h" -#include "input-timestamps-unstable-v1-server-protocol.h" - -enum pointer_constraint_type { - POINTER_CONSTRAINT_TYPE_LOCK, - POINTER_CONSTRAINT_TYPE_CONFINE, -}; - -enum motion_direction { - MOTION_DIRECTION_POSITIVE_X = 1 << 0, - MOTION_DIRECTION_NEGATIVE_X = 1 << 1, - MOTION_DIRECTION_POSITIVE_Y = 1 << 2, - MOTION_DIRECTION_NEGATIVE_Y = 1 << 3, -}; - -struct vec2d { - double x, y; -}; - -struct line { - struct vec2d a; - struct vec2d b; -}; - -struct border { - struct line line; - enum motion_direction blocking_dir; -}; - -static void -maybe_warp_confined_pointer(struct weston_pointer_constraint *constraint); - -static void -empty_region(pixman_region32_t *region) -{ - pixman_region32_fini(region); - pixman_region32_init(region); -} - -static void -region_init_infinite(pixman_region32_t *region) -{ - pixman_region32_init_rect(region, INT32_MIN, INT32_MIN, - UINT32_MAX, UINT32_MAX); -} - -static void -send_timestamp(struct wl_resource *resource, - const struct timespec *time) -{ - uint32_t tv_sec_hi, tv_sec_lo, tv_nsec; - - timespec_to_proto(time, &tv_sec_hi, &tv_sec_lo, &tv_nsec); - zwp_input_timestamps_v1_send_timestamp(resource, tv_sec_hi, tv_sec_lo, - tv_nsec); -} - -static void -send_timestamps_for_input_resource(struct wl_resource *input_resource, - struct wl_list *list, - const struct timespec *time) -{ - struct wl_resource *resource; - - wl_resource_for_each(resource, list) { - if (wl_resource_get_user_data(resource) == input_resource) - send_timestamp(resource, time); - } -} - -static void -remove_input_resource_from_timestamps(struct wl_resource *input_resource, - struct wl_list *list) -{ - struct wl_resource *resource; - - wl_resource_for_each(resource, list) { - if (wl_resource_get_user_data(resource) == input_resource) - wl_resource_set_user_data(resource, NULL); - } -} - -/** Register a touchscreen input device - * - * \param touch The parent weston_touch that identifies the seat. - * \param syspath Unique device name. - * \param backend_data Backend private data if necessary. - * \param ops Calibration operations, or NULL for not able to run calibration. - * \return New touch device, or NULL on failure. - */ -WL_EXPORT struct weston_touch_device * -weston_touch_create_touch_device(struct weston_touch *touch, - const char *syspath, - void *backend_data, - const struct weston_touch_device_ops *ops) -{ - struct weston_touch_device *device; - - assert(syspath); - if (ops) { - assert(ops->get_output); - assert(ops->get_calibration_head_name); - assert(ops->get_calibration); - assert(ops->set_calibration); - } - - device = zalloc(sizeof *device); - if (!device) - return NULL; - - wl_signal_init(&device->destroy_signal); - - device->syspath = strdup(syspath); - if (!device->syspath) { - free(device); - return NULL; - } - - device->backend_data = backend_data; - device->ops = ops; - - device->aggregate = touch; - wl_list_insert(touch->device_list.prev, &device->link); - - return device; -} - -/** Destroy the touch device. */ -WL_EXPORT void -weston_touch_device_destroy(struct weston_touch_device *device) -{ - wl_list_remove(&device->link); - wl_signal_emit(&device->destroy_signal, device); - free(device->syspath); - free(device); -} - -/** Is it possible to run calibration on this touch device? */ -WL_EXPORT bool -weston_touch_device_can_calibrate(struct weston_touch_device *device) -{ - return !!device->ops; -} - -static enum weston_touch_mode -weston_touch_device_get_mode(struct weston_touch_device *device) -{ - return device->aggregate->seat->compositor->touch_mode; -} - -static struct weston_pointer_client * -weston_pointer_client_create(struct wl_client *client) -{ - struct weston_pointer_client *pointer_client; - - pointer_client = zalloc(sizeof *pointer_client); - if (!pointer_client) - return NULL; - - pointer_client->client = client; - wl_list_init(&pointer_client->pointer_resources); - wl_list_init(&pointer_client->relative_pointer_resources); - - return pointer_client; -} - -static void -weston_pointer_client_destroy(struct weston_pointer_client *pointer_client) -{ - struct wl_resource *resource; - - wl_resource_for_each(resource, &pointer_client->pointer_resources) { - wl_resource_set_user_data(resource, NULL); - } - - wl_resource_for_each(resource, - &pointer_client->relative_pointer_resources) { - wl_resource_set_user_data(resource, NULL); - } - - wl_list_remove(&pointer_client->pointer_resources); - wl_list_remove(&pointer_client->relative_pointer_resources); - free(pointer_client); -} - -static bool -weston_pointer_client_is_empty(struct weston_pointer_client *pointer_client) -{ - return (wl_list_empty(&pointer_client->pointer_resources) && - wl_list_empty(&pointer_client->relative_pointer_resources)); -} - -static struct weston_pointer_client * -weston_pointer_get_pointer_client(struct weston_pointer *pointer, - struct wl_client *client) -{ - struct weston_pointer_client *pointer_client; - - wl_list_for_each(pointer_client, &pointer->pointer_clients, link) { - if (pointer_client->client == client) - return pointer_client; - } - - return NULL; -} - -static struct weston_pointer_client * -weston_pointer_ensure_pointer_client(struct weston_pointer *pointer, - struct wl_client *client) -{ - struct weston_pointer_client *pointer_client; - - pointer_client = weston_pointer_get_pointer_client(pointer, client); - if (pointer_client) - return pointer_client; - - pointer_client = weston_pointer_client_create(client); - wl_list_insert(&pointer->pointer_clients, &pointer_client->link); - - if (pointer->focus && - pointer->focus->surface->resource && - wl_resource_get_client(pointer->focus->surface->resource) == client) { - pointer->focus_client = pointer_client; - } - - return pointer_client; -} - -static void -weston_pointer_cleanup_pointer_client(struct weston_pointer *pointer, - struct weston_pointer_client *pointer_client) -{ - if (weston_pointer_client_is_empty(pointer_client)) { - if (pointer->focus_client == pointer_client) - pointer->focus_client = NULL; - wl_list_remove(&pointer_client->link); - weston_pointer_client_destroy(pointer_client); - } -} - -static void -unbind_pointer_client_resource(struct wl_resource *resource) -{ - struct weston_pointer *pointer = wl_resource_get_user_data(resource); - struct wl_client *client = wl_resource_get_client(resource); - struct weston_pointer_client *pointer_client; - - wl_list_remove(wl_resource_get_link(resource)); - - if (pointer) { - pointer_client = weston_pointer_get_pointer_client(pointer, - client); - assert(pointer_client); - remove_input_resource_from_timestamps(resource, - &pointer->timestamps_list); - weston_pointer_cleanup_pointer_client(pointer, pointer_client); - } -} - -static void unbind_resource(struct wl_resource *resource) -{ - wl_list_remove(wl_resource_get_link(resource)); -} - -WL_EXPORT void -weston_pointer_motion_to_abs(struct weston_pointer *pointer, - struct weston_pointer_motion_event *event, - wl_fixed_t *x, wl_fixed_t *y) -{ - if (event->mask & WESTON_POINTER_MOTION_ABS) { - *x = wl_fixed_from_double(event->x); - *y = wl_fixed_from_double(event->y); - } else if (event->mask & WESTON_POINTER_MOTION_REL) { - *x = pointer->x + wl_fixed_from_double(event->dx); - *y = pointer->y + wl_fixed_from_double(event->dy); - } else { - assert(!"invalid motion event"); - *x = *y = 0; - } -} - -static bool -weston_pointer_motion_to_rel(struct weston_pointer *pointer, - struct weston_pointer_motion_event *event, - double *dx, double *dy, - double *dx_unaccel, double *dy_unaccel) -{ - if (event->mask & WESTON_POINTER_MOTION_REL && - event->mask & WESTON_POINTER_MOTION_REL_UNACCEL) { - *dx = event->dx; - *dy = event->dy; - *dx_unaccel = event->dx_unaccel; - *dy_unaccel = event->dy_unaccel; - return true; - } else if (event->mask & WESTON_POINTER_MOTION_REL) { - *dx_unaccel = *dx = event->dx; - *dy_unaccel = *dy = event->dy; - return true; - } else if (event->mask & WESTON_POINTER_MOTION_REL_UNACCEL) { - *dx_unaccel = *dx = event->dx_unaccel; - *dy_unaccel = *dy = event->dy_unaccel; - return true; - } else { - return false; - } -} - -WL_EXPORT void -weston_seat_repick(struct weston_seat *seat) -{ - const struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - if (!pointer) - return; - - pointer->grab->interface->focus(pointer->grab); -} - -static void -weston_compositor_idle_inhibit(struct weston_compositor *compositor) -{ - weston_compositor_wake(compositor); - compositor->idle_inhibit++; -} - -static void -weston_compositor_idle_release(struct weston_compositor *compositor) -{ - compositor->idle_inhibit--; - weston_compositor_wake(compositor); -} - -static void -pointer_focus_view_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_pointer *pointer = - container_of(listener, struct weston_pointer, - focus_view_listener); - - weston_pointer_clear_focus(pointer); -} - -static void -pointer_focus_resource_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_pointer *pointer = - container_of(listener, struct weston_pointer, - focus_resource_listener); - - weston_pointer_clear_focus(pointer); -} - -static void -keyboard_focus_resource_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_keyboard *keyboard = - container_of(listener, struct weston_keyboard, - focus_resource_listener); - - weston_keyboard_set_focus(keyboard, NULL); -} - -static void -touch_focus_view_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_touch *touch = - container_of(listener, struct weston_touch, - focus_view_listener); - - weston_touch_set_focus(touch, NULL); -} - -static void -touch_focus_resource_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_touch *touch = - container_of(listener, struct weston_touch, - focus_resource_listener); - - weston_touch_set_focus(touch, NULL); -} - -static void -move_resources(struct wl_list *destination, struct wl_list *source) -{ - wl_list_insert_list(destination, source); - wl_list_init(source); -} - -static void -move_resources_for_client(struct wl_list *destination, - struct wl_list *source, - struct wl_client *client) -{ - struct wl_resource *resource, *tmp; - wl_resource_for_each_safe(resource, tmp, source) { - if (wl_resource_get_client(resource) == client) { - wl_list_remove(wl_resource_get_link(resource)); - wl_list_insert(destination, - wl_resource_get_link(resource)); - } - } -} - -static void -default_grab_pointer_focus(struct weston_pointer_grab *grab) -{ - struct weston_pointer *pointer = grab->pointer; - struct weston_view *view; - wl_fixed_t sx, sy; - - if (pointer->button_count > 0) - return; - - view = weston_compositor_pick_view(pointer->seat->compositor, - pointer->x, pointer->y, - &sx, &sy); - - if (pointer->focus != view || pointer->sx != sx || pointer->sy != sy) - weston_pointer_set_focus(pointer, view, sx, sy); -} - -static void -pointer_send_relative_motion(struct weston_pointer *pointer, - const struct timespec *time, - struct weston_pointer_motion_event *event) -{ - uint64_t time_usec; - double dx, dy, dx_unaccel, dy_unaccel; - wl_fixed_t dxf, dyf, dxf_unaccel, dyf_unaccel; - struct wl_list *resource_list; - struct wl_resource *resource; - - if (!pointer->focus_client) - return; - - if (!weston_pointer_motion_to_rel(pointer, event, - &dx, &dy, - &dx_unaccel, &dy_unaccel)) - return; - - resource_list = &pointer->focus_client->relative_pointer_resources; - time_usec = timespec_to_usec(&event->time); - if (time_usec == 0) - time_usec = timespec_to_usec(time); - - dxf = wl_fixed_from_double(dx); - dyf = wl_fixed_from_double(dy); - dxf_unaccel = wl_fixed_from_double(dx_unaccel); - dyf_unaccel = wl_fixed_from_double(dy_unaccel); - - wl_resource_for_each(resource, resource_list) { - zwp_relative_pointer_v1_send_relative_motion( - resource, - (uint32_t) (time_usec >> 32), - (uint32_t) time_usec, - dxf, dyf, - dxf_unaccel, dyf_unaccel); - } -} - -static void -pointer_send_motion(struct weston_pointer *pointer, - const struct timespec *time, - wl_fixed_t sx, wl_fixed_t sy) -{ - struct wl_list *resource_list; - struct wl_resource *resource; - uint32_t msecs; - - if (!pointer->focus_client) - return; - - resource_list = &pointer->focus_client->pointer_resources; - msecs = timespec_to_msec(time); - wl_resource_for_each(resource, resource_list) { - send_timestamps_for_input_resource(resource, - &pointer->timestamps_list, - time); - wl_pointer_send_motion(resource, msecs, sx, sy); - } -} - -WL_EXPORT void -weston_pointer_send_motion(struct weston_pointer *pointer, - const struct timespec *time, - struct weston_pointer_motion_event *event) -{ - wl_fixed_t x, y; - wl_fixed_t old_sx = pointer->sx; - wl_fixed_t old_sy = pointer->sy; - - if (pointer->focus) { - weston_pointer_motion_to_abs(pointer, event, &x, &y); - weston_view_from_global_fixed(pointer->focus, x, y, - &pointer->sx, &pointer->sy); - } - - weston_pointer_move(pointer, event); - - if (old_sx != pointer->sx || old_sy != pointer->sy) { - pointer_send_motion(pointer, time, - pointer->sx, pointer->sy); - } - - pointer_send_relative_motion(pointer, time, event); -} - -static void -default_grab_pointer_motion(struct weston_pointer_grab *grab, - const struct timespec *time, - struct weston_pointer_motion_event *event) -{ - weston_pointer_send_motion(grab->pointer, time, event); -} - -/** Check if the pointer has focused resources. - * - * \param pointer The pointer to check for focused resources. - * \return Whether or not this pointer has focused resources - */ -WL_EXPORT bool -weston_pointer_has_focus_resource(struct weston_pointer *pointer) -{ - if (!pointer->focus_client) - return false; - - if (wl_list_empty(&pointer->focus_client->pointer_resources)) - return false; - - return true; -} - -/** Send wl_pointer.button events to focused resources. - * - * \param pointer The pointer where the button events originates from. - * \param time The timestamp of the event - * \param button The button value of the event - * \param state The state enum value of the event - * - * For every resource that is currently in focus, send a wl_pointer.button event - * with the passed parameters. The focused resources are the wl_pointer - * resources of the client which currently has the surface with pointer focus. - */ -WL_EXPORT void -weston_pointer_send_button(struct weston_pointer *pointer, - const struct timespec *time, uint32_t button, - enum wl_pointer_button_state state) -{ - struct wl_display *display = pointer->seat->compositor->wl_display; - struct wl_list *resource_list; - struct wl_resource *resource; - uint32_t serial; - uint32_t msecs; - - if (!weston_pointer_has_focus_resource(pointer)) - return; - - resource_list = &pointer->focus_client->pointer_resources; - serial = wl_display_next_serial(display); - msecs = timespec_to_msec(time); - wl_resource_for_each(resource, resource_list) { - send_timestamps_for_input_resource(resource, - &pointer->timestamps_list, - time); - wl_pointer_send_button(resource, serial, msecs, button, state); - } -} - -static void -default_grab_pointer_button(struct weston_pointer_grab *grab, - const struct timespec *time, uint32_t button, - enum wl_pointer_button_state state) -{ - struct weston_pointer *pointer = grab->pointer; - struct weston_compositor *compositor = pointer->seat->compositor; - struct weston_view *view; - wl_fixed_t sx, sy; - - weston_pointer_send_button(pointer, time, button, state); - - if (pointer->button_count == 0 && - state == WL_POINTER_BUTTON_STATE_RELEASED) { - view = weston_compositor_pick_view(compositor, - pointer->x, pointer->y, - &sx, &sy); - - weston_pointer_set_focus(pointer, view, sx, sy); - } -} - -/** Send wl_pointer.axis events to focused resources. - * - * \param pointer The pointer where the axis events originates from. - * \param time The timestamp of the event - * \param event The axis value of the event - * - * For every resource that is currently in focus, send a wl_pointer.axis event - * with the passed parameters. The focused resources are the wl_pointer - * resources of the client which currently has the surface with pointer focus. - */ -WL_EXPORT void -weston_pointer_send_axis(struct weston_pointer *pointer, - const struct timespec *time, - struct weston_pointer_axis_event *event) -{ - struct wl_resource *resource; - struct wl_list *resource_list; - uint32_t msecs; - - if (!weston_pointer_has_focus_resource(pointer)) - return; - - resource_list = &pointer->focus_client->pointer_resources; - msecs = timespec_to_msec(time); - wl_resource_for_each(resource, resource_list) { - if (event->has_discrete && - wl_resource_get_version(resource) >= - WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) - wl_pointer_send_axis_discrete(resource, event->axis, - event->discrete); - - if (event->value) { - send_timestamps_for_input_resource(resource, - &pointer->timestamps_list, - time); - wl_pointer_send_axis(resource, msecs, - event->axis, - wl_fixed_from_double(event->value)); - } else if (wl_resource_get_version(resource) >= - WL_POINTER_AXIS_STOP_SINCE_VERSION) { - send_timestamps_for_input_resource(resource, - &pointer->timestamps_list, - time); - wl_pointer_send_axis_stop(resource, msecs, - event->axis); - } - } -} - -/** Send wl_pointer.axis_source events to focused resources. - * - * \param pointer The pointer where the axis_source events originates from. - * \param source The axis_source enum value of the event - * - * For every resource that is currently in focus, send a wl_pointer.axis_source - * event with the passed parameter. The focused resources are the wl_pointer - * resources of the client which currently has the surface with pointer focus. - */ -WL_EXPORT void -weston_pointer_send_axis_source(struct weston_pointer *pointer, - enum wl_pointer_axis_source source) -{ - struct wl_resource *resource; - struct wl_list *resource_list; - - if (!weston_pointer_has_focus_resource(pointer)) - return; - - resource_list = &pointer->focus_client->pointer_resources; - wl_resource_for_each(resource, resource_list) { - if (wl_resource_get_version(resource) >= - WL_POINTER_AXIS_SOURCE_SINCE_VERSION) { - wl_pointer_send_axis_source(resource, source); - } - } -} - -static void -pointer_send_frame(struct wl_resource *resource) -{ - if (wl_resource_get_version(resource) >= - WL_POINTER_FRAME_SINCE_VERSION) { - wl_pointer_send_frame(resource); - } -} - -/** Send wl_pointer.frame events to focused resources. - * - * \param pointer The pointer where the frame events originates from. - * - * For every resource that is currently in focus, send a wl_pointer.frame event. - * The focused resources are the wl_pointer resources of the client which - * currently has the surface with pointer focus. - */ -WL_EXPORT void -weston_pointer_send_frame(struct weston_pointer *pointer) -{ - struct wl_resource *resource; - struct wl_list *resource_list; - - if (!weston_pointer_has_focus_resource(pointer)) - return; - - resource_list = &pointer->focus_client->pointer_resources; - wl_resource_for_each(resource, resource_list) - pointer_send_frame(resource); -} - -static void -default_grab_pointer_axis(struct weston_pointer_grab *grab, - const struct timespec *time, - struct weston_pointer_axis_event *event) -{ - weston_pointer_send_axis(grab->pointer, time, event); -} - -static void -default_grab_pointer_axis_source(struct weston_pointer_grab *grab, - enum wl_pointer_axis_source source) -{ - weston_pointer_send_axis_source(grab->pointer, source); -} - -static void -default_grab_pointer_frame(struct weston_pointer_grab *grab) -{ - weston_pointer_send_frame(grab->pointer); -} - -static void -default_grab_pointer_cancel(struct weston_pointer_grab *grab) -{ -} - -static const struct weston_pointer_grab_interface - default_pointer_grab_interface = { - default_grab_pointer_focus, - default_grab_pointer_motion, - default_grab_pointer_button, - default_grab_pointer_axis, - default_grab_pointer_axis_source, - default_grab_pointer_frame, - default_grab_pointer_cancel, -}; - -/** Check if the touch has focused resources. - * - * \param touch The touch to check for focused resources. - * \return Whether or not this touch has focused resources - */ -WL_EXPORT bool -weston_touch_has_focus_resource(struct weston_touch *touch) -{ - if (!touch->focus) - return false; - - if (wl_list_empty(&touch->focus_resource_list)) - return false; - - return true; -} - -/** Send wl_touch.down events to focused resources. - * - * \param touch The touch where the down events originates from. - * \param time The timestamp of the event - * \param touch_id The touch_id value of the event - * \param x The x value of the event - * \param y The y value of the event - * - * For every resource that is currently in focus, send a wl_touch.down event - * with the passed parameters. The focused resources are the wl_touch - * resources of the client which currently has the surface with touch focus. - */ -WL_EXPORT void -weston_touch_send_down(struct weston_touch *touch, const struct timespec *time, - int touch_id, wl_fixed_t x, wl_fixed_t y) -{ - struct wl_display *display = touch->seat->compositor->wl_display; - uint32_t serial; - struct wl_resource *resource; - struct wl_list *resource_list; - wl_fixed_t sx, sy; - uint32_t msecs; - - if (!weston_touch_has_focus_resource(touch)) - return; - - weston_view_from_global_fixed(touch->focus, x, y, &sx, &sy); - - resource_list = &touch->focus_resource_list; - serial = wl_display_next_serial(display); - msecs = timespec_to_msec(time); - wl_resource_for_each(resource, resource_list) { - send_timestamps_for_input_resource(resource, - &touch->timestamps_list, - time); - wl_touch_send_down(resource, serial, msecs, - touch->focus->surface->resource, - touch_id, sx, sy); - } -} - -static void -default_grab_touch_down(struct weston_touch_grab *grab, - const struct timespec *time, int touch_id, - wl_fixed_t x, wl_fixed_t y) -{ - weston_touch_send_down(grab->touch, time, touch_id, x, y); -} - -/** Send wl_touch.up events to focused resources. - * - * \param touch The touch where the up events originates from. - * \param time The timestamp of the event - * \param touch_id The touch_id value of the event - * - * For every resource that is currently in focus, send a wl_touch.up event - * with the passed parameters. The focused resources are the wl_touch - * resources of the client which currently has the surface with touch focus. - */ -WL_EXPORT void -weston_touch_send_up(struct weston_touch *touch, const struct timespec *time, - int touch_id) -{ - struct wl_display *display = touch->seat->compositor->wl_display; - uint32_t serial; - struct wl_resource *resource; - struct wl_list *resource_list; - uint32_t msecs; - - if (!weston_touch_has_focus_resource(touch)) - return; - - resource_list = &touch->focus_resource_list; - serial = wl_display_next_serial(display); - msecs = timespec_to_msec(time); - wl_resource_for_each(resource, resource_list) { - send_timestamps_for_input_resource(resource, - &touch->timestamps_list, - time); - wl_touch_send_up(resource, serial, msecs, touch_id); - } -} - -static void -default_grab_touch_up(struct weston_touch_grab *grab, - const struct timespec *time, int touch_id) -{ - weston_touch_send_up(grab->touch, time, touch_id); -} - -/** Send wl_touch.motion events to focused resources. - * - * \param touch The touch where the motion events originates from. - * \param time The timestamp of the event - * \param touch_id The touch_id value of the event - * \param x The x value of the event - * \param y The y value of the event - * - * For every resource that is currently in focus, send a wl_touch.motion event - * with the passed parameters. The focused resources are the wl_touch - * resources of the client which currently has the surface with touch focus. - */ -WL_EXPORT void -weston_touch_send_motion(struct weston_touch *touch, - const struct timespec *time, int touch_id, - wl_fixed_t x, wl_fixed_t y) -{ - struct wl_resource *resource; - struct wl_list *resource_list; - wl_fixed_t sx, sy; - uint32_t msecs; - - if (!weston_touch_has_focus_resource(touch)) - return; - - weston_view_from_global_fixed(touch->focus, x, y, &sx, &sy); - - resource_list = &touch->focus_resource_list; - msecs = timespec_to_msec(time); - wl_resource_for_each(resource, resource_list) { - send_timestamps_for_input_resource(resource, - &touch->timestamps_list, - time); - wl_touch_send_motion(resource, msecs, - touch_id, sx, sy); - } -} - -static void -default_grab_touch_motion(struct weston_touch_grab *grab, - const struct timespec *time, int touch_id, - wl_fixed_t x, wl_fixed_t y) -{ - weston_touch_send_motion(grab->touch, time, touch_id, x, y); -} - - -/** Send wl_touch.frame events to focused resources. - * - * \param touch The touch where the frame events originates from. - * - * For every resource that is currently in focus, send a wl_touch.frame event. - * The focused resources are the wl_touch resources of the client which - * currently has the surface with touch focus. - */ -WL_EXPORT void -weston_touch_send_frame(struct weston_touch *touch) -{ - struct wl_resource *resource; - - if (!weston_touch_has_focus_resource(touch)) - return; - - wl_resource_for_each(resource, &touch->focus_resource_list) - wl_touch_send_frame(resource); -} - -static void -default_grab_touch_frame(struct weston_touch_grab *grab) -{ - weston_touch_send_frame(grab->touch); -} - -static void -default_grab_touch_cancel(struct weston_touch_grab *grab) -{ -} - -static const struct weston_touch_grab_interface default_touch_grab_interface = { - default_grab_touch_down, - default_grab_touch_up, - default_grab_touch_motion, - default_grab_touch_frame, - default_grab_touch_cancel, -}; - -/** Check if the keyboard has focused resources. - * - * \param keyboard The keyboard to check for focused resources. - * \return Whether or not this keyboard has focused resources - */ -WL_EXPORT bool -weston_keyboard_has_focus_resource(struct weston_keyboard *keyboard) -{ - if (!keyboard->focus) - return false; - - if (wl_list_empty(&keyboard->focus_resource_list)) - return false; - - return true; -} - -/** Send wl_keyboard.key events to focused resources. - * - * \param keyboard The keyboard where the key events originates from. - * \param time The timestamp of the event - * \param key The key value of the event - * \param state The state enum value of the event - * - * For every resource that is currently in focus, send a wl_keyboard.key event - * with the passed parameters. The focused resources are the wl_keyboard - * resources of the client which currently has the surface with keyboard focus. - */ -WL_EXPORT void -weston_keyboard_send_key(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, - enum wl_keyboard_key_state state) -{ - struct wl_resource *resource; - struct wl_display *display = keyboard->seat->compositor->wl_display; - uint32_t serial; - struct wl_list *resource_list; - uint32_t msecs; - - if (!weston_keyboard_has_focus_resource(keyboard)) - return; - - resource_list = &keyboard->focus_resource_list; - serial = wl_display_next_serial(display); - msecs = timespec_to_msec(time); - wl_resource_for_each(resource, resource_list) { - send_timestamps_for_input_resource(resource, - &keyboard->timestamps_list, - time); - wl_keyboard_send_key(resource, serial, msecs, key, state); - } -}; - -static void -default_grab_keyboard_key(struct weston_keyboard_grab *grab, - const struct timespec *time, uint32_t key, - uint32_t state) -{ - weston_keyboard_send_key(grab->keyboard, time, key, state); -} - -static void -send_modifiers_to_resource(struct weston_keyboard *keyboard, - struct wl_resource *resource, - uint32_t serial) -{ - wl_keyboard_send_modifiers(resource, - serial, - keyboard->modifiers.mods_depressed, - keyboard->modifiers.mods_latched, - keyboard->modifiers.mods_locked, - keyboard->modifiers.group); -} - -static void -send_modifiers_to_client_in_list(struct wl_client *client, - struct wl_list *list, - uint32_t serial, - struct weston_keyboard *keyboard) -{ - struct wl_resource *resource; - - wl_resource_for_each(resource, list) { - if (wl_resource_get_client(resource) == client) - send_modifiers_to_resource(keyboard, - resource, - serial); - } -} - -static struct weston_pointer_client * -find_pointer_client_for_surface(struct weston_pointer *pointer, - struct weston_surface *surface) -{ - struct wl_client *client; - - if (!surface) - return NULL; - - if (!surface->resource) - return NULL; - - client = wl_resource_get_client(surface->resource); - return weston_pointer_get_pointer_client(pointer, client); -} - -static struct weston_pointer_client * -find_pointer_client_for_view(struct weston_pointer *pointer, struct weston_view *view) -{ - if (!view) - return NULL; - - return find_pointer_client_for_surface(pointer, view->surface); -} - -static struct wl_resource * -find_resource_for_surface(struct wl_list *list, struct weston_surface *surface) -{ - if (!surface) - return NULL; - - if (!surface->resource) - return NULL; - - return wl_resource_find_for_client(list, wl_resource_get_client(surface->resource)); -} - -/** Send wl_keyboard.modifiers events to focused resources and pointer - * focused resources. - * - * \param keyboard The keyboard where the modifiers events originates from. - * \param serial The serial of the event - * \param mods_depressed The mods_depressed value of the event - * \param mods_latched The mods_latched value of the event - * \param mods_locked The mods_locked value of the event - * \param group The group value of the event - * - * For every resource that is currently in focus, send a wl_keyboard.modifiers - * event with the passed parameters. The focused resources are the wl_keyboard - * resources of the client which currently has the surface with keyboard focus. - * This also sends wl_keyboard.modifiers events to the wl_keyboard resources of - * the client having pointer focus (if different from the keyboard focus client). - */ -WL_EXPORT void -weston_keyboard_send_modifiers(struct weston_keyboard *keyboard, - uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) -{ - struct weston_pointer *pointer = - weston_seat_get_pointer(keyboard->seat); - - if (weston_keyboard_has_focus_resource(keyboard)) { - struct wl_list *resource_list; - struct wl_resource *resource; - - resource_list = &keyboard->focus_resource_list; - wl_resource_for_each(resource, resource_list) { - wl_keyboard_send_modifiers(resource, serial, - mods_depressed, mods_latched, - mods_locked, group); - } - } - - if (pointer && pointer->focus && pointer->focus->surface->resource && - pointer->focus->surface != keyboard->focus) { - struct wl_client *pointer_client = - wl_resource_get_client(pointer->focus->surface->resource); - - send_modifiers_to_client_in_list(pointer_client, - &keyboard->resource_list, - serial, - keyboard); - } -} - -static void -default_grab_keyboard_modifiers(struct weston_keyboard_grab *grab, - uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) -{ - weston_keyboard_send_modifiers(grab->keyboard, serial, mods_depressed, - mods_latched, mods_locked, group); -} - -static void -default_grab_keyboard_cancel(struct weston_keyboard_grab *grab) -{ -} - -static const struct weston_keyboard_grab_interface - default_keyboard_grab_interface = { - default_grab_keyboard_key, - default_grab_keyboard_modifiers, - default_grab_keyboard_cancel, -}; - -static void -pointer_unmap_sprite(struct weston_pointer *pointer) -{ - struct weston_surface *surface = pointer->sprite->surface; - - if (weston_surface_is_mapped(surface)) - weston_surface_unmap(surface); - - wl_list_remove(&pointer->sprite_destroy_listener.link); - surface->committed = NULL; - surface->committed_private = NULL; - weston_surface_set_label_func(surface, NULL); - weston_view_destroy(pointer->sprite); - pointer->sprite = NULL; -} - -static void -pointer_handle_sprite_destroy(struct wl_listener *listener, void *data) -{ - struct weston_pointer *pointer = - container_of(listener, struct weston_pointer, - sprite_destroy_listener); - - pointer->sprite = NULL; -} - -static void -weston_pointer_reset_state(struct weston_pointer *pointer) -{ - pointer->button_count = 0; -} - -static void -weston_pointer_handle_output_destroy(struct wl_listener *listener, void *data); - -static struct weston_pointer * -weston_pointer_create(struct weston_seat *seat) -{ - struct weston_pointer *pointer; - - pointer = zalloc(sizeof *pointer); - if (pointer == NULL) - return NULL; - - wl_list_init(&pointer->pointer_clients); - weston_pointer_set_default_grab(pointer, - seat->compositor->default_pointer_grab); - wl_list_init(&pointer->focus_resource_listener.link); - pointer->focus_resource_listener.notify = pointer_focus_resource_destroyed; - pointer->default_grab.pointer = pointer; - pointer->grab = &pointer->default_grab; - wl_signal_init(&pointer->motion_signal); - wl_signal_init(&pointer->focus_signal); - wl_list_init(&pointer->focus_view_listener.link); - wl_signal_init(&pointer->destroy_signal); - wl_list_init(&pointer->timestamps_list); - - pointer->sprite_destroy_listener.notify = pointer_handle_sprite_destroy; - - /* FIXME: Pick better co-ords. */ - pointer->x = wl_fixed_from_int(100); - pointer->y = wl_fixed_from_int(100); - - pointer->output_destroy_listener.notify = - weston_pointer_handle_output_destroy; - wl_signal_add(&seat->compositor->output_destroyed_signal, - &pointer->output_destroy_listener); - - pointer->sx = wl_fixed_from_int(-1000000); - pointer->sy = wl_fixed_from_int(-1000000); - - return pointer; -} - -static void -weston_pointer_destroy(struct weston_pointer *pointer) -{ - struct weston_pointer_client *pointer_client, *tmp; - - wl_signal_emit(&pointer->destroy_signal, pointer); - - if (pointer->sprite) - pointer_unmap_sprite(pointer); - - wl_list_for_each_safe(pointer_client, tmp, &pointer->pointer_clients, - link) { - wl_list_remove(&pointer_client->link); - weston_pointer_client_destroy(pointer_client); - } - - wl_list_remove(&pointer->focus_resource_listener.link); - wl_list_remove(&pointer->focus_view_listener.link); - wl_list_remove(&pointer->output_destroy_listener.link); - wl_list_remove(&pointer->timestamps_list); - free(pointer); -} - -void -weston_pointer_set_default_grab(struct weston_pointer *pointer, - const struct weston_pointer_grab_interface *interface) -{ - if (interface) - pointer->default_grab.interface = interface; - else - pointer->default_grab.interface = - &default_pointer_grab_interface; -} - -static struct weston_keyboard * -weston_keyboard_create(void) -{ - struct weston_keyboard *keyboard; - - keyboard = zalloc(sizeof *keyboard); - if (keyboard == NULL) - return NULL; - - wl_list_init(&keyboard->resource_list); - wl_list_init(&keyboard->focus_resource_list); - wl_list_init(&keyboard->focus_resource_listener.link); - keyboard->focus_resource_listener.notify = keyboard_focus_resource_destroyed; - wl_array_init(&keyboard->keys); - keyboard->default_grab.interface = &default_keyboard_grab_interface; - keyboard->default_grab.keyboard = keyboard; - keyboard->grab = &keyboard->default_grab; - wl_signal_init(&keyboard->focus_signal); - wl_list_init(&keyboard->timestamps_list); - - return keyboard; -} - -static void -weston_xkb_info_destroy(struct weston_xkb_info *xkb_info); - -static void -weston_keyboard_destroy(struct weston_keyboard *keyboard) -{ - struct wl_resource *resource; - - wl_resource_for_each(resource, &keyboard->resource_list) { - wl_resource_set_user_data(resource, NULL); - } - - wl_resource_for_each(resource, &keyboard->focus_resource_list) { - wl_resource_set_user_data(resource, NULL); - } - - wl_list_remove(&keyboard->resource_list); - wl_list_remove(&keyboard->focus_resource_list); - - xkb_state_unref(keyboard->xkb_state.state); - if (keyboard->xkb_info) - weston_xkb_info_destroy(keyboard->xkb_info); - xkb_keymap_unref(keyboard->pending_keymap); - - wl_array_release(&keyboard->keys); - wl_list_remove(&keyboard->focus_resource_listener.link); - wl_list_remove(&keyboard->timestamps_list); - free(keyboard); -} - -static void -weston_touch_reset_state(struct weston_touch *touch) -{ - touch->num_tp = 0; -} - -static struct weston_touch * -weston_touch_create(void) -{ - struct weston_touch *touch; - - touch = zalloc(sizeof *touch); - if (touch == NULL) - return NULL; - - wl_list_init(&touch->device_list); - wl_list_init(&touch->resource_list); - wl_list_init(&touch->focus_resource_list); - wl_list_init(&touch->focus_view_listener.link); - touch->focus_view_listener.notify = touch_focus_view_destroyed; - wl_list_init(&touch->focus_resource_listener.link); - touch->focus_resource_listener.notify = touch_focus_resource_destroyed; - touch->default_grab.interface = &default_touch_grab_interface; - touch->default_grab.touch = touch; - touch->grab = &touch->default_grab; - wl_signal_init(&touch->focus_signal); - wl_list_init(&touch->timestamps_list); - - return touch; -} - -static void -weston_touch_destroy(struct weston_touch *touch) -{ - struct wl_resource *resource; - - assert(wl_list_empty(&touch->device_list)); - - wl_resource_for_each(resource, &touch->resource_list) { - wl_resource_set_user_data(resource, NULL); - } - - wl_resource_for_each(resource, &touch->focus_resource_list) { - wl_resource_set_user_data(resource, NULL); - } - - wl_list_remove(&touch->resource_list); - wl_list_remove(&touch->focus_resource_list); - wl_list_remove(&touch->focus_view_listener.link); - wl_list_remove(&touch->focus_resource_listener.link); - wl_list_remove(&touch->timestamps_list); - free(touch); -} - -static void -seat_send_updated_caps(struct weston_seat *seat) -{ - enum wl_seat_capability caps = 0; - struct wl_resource *resource; - - if (seat->pointer_device_count > 0) - caps |= WL_SEAT_CAPABILITY_POINTER; - if (seat->keyboard_device_count > 0) - caps |= WL_SEAT_CAPABILITY_KEYBOARD; - if (seat->touch_device_count > 0) - caps |= WL_SEAT_CAPABILITY_TOUCH; - - wl_resource_for_each(resource, &seat->base_resource_list) { - wl_seat_send_capabilities(resource, caps); - } - wl_signal_emit(&seat->updated_caps_signal, seat); -} - - -/** Clear the pointer focus - * - * \param pointer the pointer to clear focus for. - * - * This can be used to unset pointer focus and set the co-ordinates to the - * arbitrary values we use for the no focus case. - * - * There's no requirement to use this function. For example, passing the - * results of a weston_compositor_pick_view() directly to - * weston_pointer_set_focus() will do the right thing when no view is found. - */ -WL_EXPORT void -weston_pointer_clear_focus(struct weston_pointer *pointer) -{ - weston_pointer_set_focus(pointer, NULL, - wl_fixed_from_int(-1000000), - wl_fixed_from_int(-1000000)); -} - -WL_EXPORT void -weston_pointer_set_focus(struct weston_pointer *pointer, - struct weston_view *view, - wl_fixed_t sx, wl_fixed_t sy) -{ - struct weston_pointer_client *pointer_client; - struct weston_keyboard *kbd = weston_seat_get_keyboard(pointer->seat); - struct wl_resource *resource; - struct wl_resource *surface_resource; - struct wl_display *display = pointer->seat->compositor->wl_display; - uint32_t serial; - struct wl_list *focus_resource_list; - int refocus = 0; - - if ((!pointer->focus && view) || - (pointer->focus && !view) || - (pointer->focus && pointer->focus->surface != view->surface) || - pointer->sx != sx || pointer->sy != sy) - refocus = 1; - - if (pointer->focus_client && refocus) { - focus_resource_list = &pointer->focus_client->pointer_resources; - if (!wl_list_empty(focus_resource_list)) { - serial = wl_display_next_serial(display); - surface_resource = pointer->focus->surface->resource; - wl_resource_for_each(resource, focus_resource_list) { - wl_pointer_send_leave(resource, serial, - surface_resource); - pointer_send_frame(resource); - } - } - - pointer->focus_client = NULL; - } - - pointer_client = find_pointer_client_for_view(pointer, view); - if (pointer_client && refocus) { - struct wl_client *surface_client = pointer_client->client; - - serial = wl_display_next_serial(display); - - if (kbd && kbd->focus != view->surface) - send_modifiers_to_client_in_list(surface_client, - &kbd->resource_list, - serial, - kbd); - - pointer->focus_client = pointer_client; - - focus_resource_list = &pointer->focus_client->pointer_resources; - wl_resource_for_each(resource, focus_resource_list) { - wl_pointer_send_enter(resource, - serial, - view->surface->resource, - sx, sy); - pointer_send_frame(resource); - } - - pointer->focus_serial = serial; - } - - wl_list_remove(&pointer->focus_view_listener.link); - wl_list_init(&pointer->focus_view_listener.link); - wl_list_remove(&pointer->focus_resource_listener.link); - wl_list_init(&pointer->focus_resource_listener.link); - if (view) - wl_signal_add(&view->destroy_signal, &pointer->focus_view_listener); - if (view && view->surface->resource) - wl_resource_add_destroy_listener(view->surface->resource, - &pointer->focus_resource_listener); - - pointer->focus = view; - pointer->focus_view_listener.notify = pointer_focus_view_destroyed; - pointer->sx = sx; - pointer->sy = sy; - - assert(view || sx == wl_fixed_from_int(-1000000)); - assert(view || sy == wl_fixed_from_int(-1000000)); - - wl_signal_emit(&pointer->focus_signal, pointer); -} - -static void -send_enter_to_resource_list(struct wl_list *list, - struct weston_keyboard *keyboard, - struct weston_surface *surface, - uint32_t serial) -{ - struct wl_resource *resource; - - wl_resource_for_each(resource, list) { - send_modifiers_to_resource(keyboard, resource, serial); - wl_keyboard_send_enter(resource, serial, - surface->resource, - &keyboard->keys); - } -} - -WL_EXPORT void -weston_keyboard_set_focus(struct weston_keyboard *keyboard, - struct weston_surface *surface) -{ - struct weston_seat *seat = keyboard->seat; - struct wl_resource *resource; - struct wl_display *display = keyboard->seat->compositor->wl_display; - uint32_t serial; - struct wl_list *focus_resource_list; - - /* Keyboard focus on a surface without a client is equivalent to NULL - * focus as nothing would react to the keyboard events anyway. - * Just set focus to NULL instead - the destroy listener hangs on the - * wl_resource anyway. - */ - if (surface && !surface->resource) - surface = NULL; - - focus_resource_list = &keyboard->focus_resource_list; - - if (!wl_list_empty(focus_resource_list) && keyboard->focus != surface) { - serial = wl_display_next_serial(display); - wl_resource_for_each(resource, focus_resource_list) { - wl_keyboard_send_leave(resource, serial, - keyboard->focus->resource); - } - move_resources(&keyboard->resource_list, focus_resource_list); - } - - if (find_resource_for_surface(&keyboard->resource_list, surface) && - keyboard->focus != surface) { - struct wl_client *surface_client = - wl_resource_get_client(surface->resource); - - serial = wl_display_next_serial(display); - - move_resources_for_client(focus_resource_list, - &keyboard->resource_list, - surface_client); - send_enter_to_resource_list(focus_resource_list, - keyboard, - surface, - serial); - keyboard->focus_serial = serial; - } - - if (seat->saved_kbd_focus) { - wl_list_remove(&seat->saved_kbd_focus_listener.link); - seat->saved_kbd_focus = NULL; - } - - wl_list_remove(&keyboard->focus_resource_listener.link); - wl_list_init(&keyboard->focus_resource_listener.link); - if (surface) - wl_resource_add_destroy_listener(surface->resource, - &keyboard->focus_resource_listener); - - keyboard->focus = surface; - wl_signal_emit(&keyboard->focus_signal, keyboard); -} - -/* Users of this function must manually manage the keyboard focus */ -WL_EXPORT void -weston_keyboard_start_grab(struct weston_keyboard *keyboard, - struct weston_keyboard_grab *grab) -{ - keyboard->grab = grab; - grab->keyboard = keyboard; -} - -WL_EXPORT void -weston_keyboard_end_grab(struct weston_keyboard *keyboard) -{ - keyboard->grab = &keyboard->default_grab; -} - -static void -weston_keyboard_cancel_grab(struct weston_keyboard *keyboard) -{ - keyboard->grab->interface->cancel(keyboard->grab); -} - -WL_EXPORT void -weston_pointer_start_grab(struct weston_pointer *pointer, - struct weston_pointer_grab *grab) -{ - pointer->grab = grab; - grab->pointer = pointer; - pointer->grab->interface->focus(pointer->grab); -} - -WL_EXPORT void -weston_pointer_end_grab(struct weston_pointer *pointer) -{ - pointer->grab = &pointer->default_grab; - pointer->grab->interface->focus(pointer->grab); -} - -static void -weston_pointer_cancel_grab(struct weston_pointer *pointer) -{ - pointer->grab->interface->cancel(pointer->grab); -} - -WL_EXPORT void -weston_touch_start_grab(struct weston_touch *touch, struct weston_touch_grab *grab) -{ - touch->grab = grab; - grab->touch = touch; -} - -WL_EXPORT void -weston_touch_end_grab(struct weston_touch *touch) -{ - touch->grab = &touch->default_grab; -} - -static void -weston_touch_cancel_grab(struct weston_touch *touch) -{ - touch->grab->interface->cancel(touch->grab); -} - -static void -weston_pointer_clamp_for_output(struct weston_pointer *pointer, - struct weston_output *output, - wl_fixed_t *fx, wl_fixed_t *fy) -{ - int x, y; - - x = wl_fixed_to_int(*fx); - y = wl_fixed_to_int(*fy); - - if (x < output->x) - *fx = wl_fixed_from_int(output->x); - else if (x >= output->x + output->width) - *fx = wl_fixed_from_int(output->x + - output->width - 1); - if (y < output->y) - *fy = wl_fixed_from_int(output->y); - else if (y >= output->y + output->height) - *fy = wl_fixed_from_int(output->y + - output->height - 1); -} - -WL_EXPORT void -weston_pointer_clamp(struct weston_pointer *pointer, wl_fixed_t *fx, wl_fixed_t *fy) -{ - struct weston_compositor *ec = pointer->seat->compositor; - struct weston_output *output, *prev = NULL; - int x, y, old_x, old_y, valid = 0; - - x = wl_fixed_to_int(*fx); - y = wl_fixed_to_int(*fy); - old_x = wl_fixed_to_int(pointer->x); - old_y = wl_fixed_to_int(pointer->y); - - wl_list_for_each(output, &ec->output_list, link) { - if (pointer->seat->output && pointer->seat->output != output) - continue; - if (pixman_region32_contains_point(&output->region, - x, y, NULL)) - valid = 1; - if (pixman_region32_contains_point(&output->region, - old_x, old_y, NULL)) - prev = output; - } - - if (!prev) - prev = pointer->seat->output; - - if (prev && !valid) - weston_pointer_clamp_for_output(pointer, prev, fx, fy); -} - -static void -weston_pointer_move_to(struct weston_pointer *pointer, - wl_fixed_t x, wl_fixed_t y) -{ - int32_t ix, iy; - - weston_pointer_clamp (pointer, &x, &y); - - pointer->x = x; - pointer->y = y; - - ix = wl_fixed_to_int(x); - iy = wl_fixed_to_int(y); - - if (pointer->sprite) { - weston_view_set_position(pointer->sprite, - ix - pointer->hotspot_x, - iy - pointer->hotspot_y); - weston_view_schedule_repaint(pointer->sprite); - } - - pointer->grab->interface->focus(pointer->grab); - wl_signal_emit(&pointer->motion_signal, pointer); -} - -WL_EXPORT void -weston_pointer_move(struct weston_pointer *pointer, - struct weston_pointer_motion_event *event) -{ - wl_fixed_t x, y; - - weston_pointer_motion_to_abs(pointer, event, &x, &y); - weston_pointer_move_to(pointer, x, y); -} - -/** Verify if the pointer is in a valid position and move it if it isn't. - */ -static void -weston_pointer_handle_output_destroy(struct wl_listener *listener, void *data) -{ - struct weston_pointer *pointer; - struct weston_compositor *ec; - struct weston_output *output, *closest = NULL; - int x, y, distance, min = INT_MAX; - wl_fixed_t fx, fy; - - pointer = container_of(listener, struct weston_pointer, - output_destroy_listener); - ec = pointer->seat->compositor; - - x = wl_fixed_to_int(pointer->x); - y = wl_fixed_to_int(pointer->y); - - wl_list_for_each(output, &ec->output_list, link) { - if (pixman_region32_contains_point(&output->region, - x, y, NULL)) - return; - - /* Aproximante the distance from the pointer to the center of - * the output. */ - distance = abs(output->x + output->width / 2 - x) + - abs(output->y + output->height / 2 - y); - if (distance < min) { - min = distance; - closest = output; - } - } - - /* Nothing to do if there's no output left. */ - if (!closest) - return; - - fx = pointer->x; - fy = pointer->y; - - weston_pointer_clamp_for_output(pointer, closest, &fx, &fy); - weston_pointer_move_to(pointer, fx, fy); -} - -WL_EXPORT void -notify_motion(struct weston_seat *seat, - const struct timespec *time, - struct weston_pointer_motion_event *event) -{ - struct weston_compositor *ec = seat->compositor; - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - weston_compositor_wake(ec); - pointer->grab->interface->motion(pointer->grab, time, event); -} - -static void -run_modifier_bindings(struct weston_seat *seat, uint32_t old, uint32_t new) -{ - struct weston_compositor *compositor = seat->compositor; - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - uint32_t diff; - unsigned int i; - struct { - uint32_t xkb; - enum weston_keyboard_modifier weston; - } mods[] = { - { keyboard->xkb_info->ctrl_mod, MODIFIER_CTRL }, - { keyboard->xkb_info->alt_mod, MODIFIER_ALT }, - { keyboard->xkb_info->super_mod, MODIFIER_SUPER }, - { keyboard->xkb_info->shift_mod, MODIFIER_SHIFT }, - }; - - diff = new & ~old; - for (i = 0; i < ARRAY_LENGTH(mods); i++) { - if (diff & (1 << mods[i].xkb)) - weston_compositor_run_modifier_binding(compositor, - keyboard, - mods[i].weston, - WL_KEYBOARD_KEY_STATE_PRESSED); - } - - diff = old & ~new; - for (i = 0; i < ARRAY_LENGTH(mods); i++) { - if (diff & (1 << mods[i].xkb)) - weston_compositor_run_modifier_binding(compositor, - keyboard, - mods[i].weston, - WL_KEYBOARD_KEY_STATE_RELEASED); - } -} - -WL_EXPORT void -notify_motion_absolute(struct weston_seat *seat, const struct timespec *time, - double x, double y) -{ - struct weston_compositor *ec = seat->compositor; - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - struct weston_pointer_motion_event event = { 0 }; - - weston_compositor_wake(ec); - - event = (struct weston_pointer_motion_event) { - .mask = WESTON_POINTER_MOTION_ABS, - .x = x, - .y = y, - }; - - pointer->grab->interface->motion(pointer->grab, time, &event); -} - -static unsigned int -peek_next_activate_serial(struct weston_compositor *c) -{ - unsigned serial = c->activate_serial + 1; - - return serial == 0 ? 1 : serial; -} - -static void -inc_activate_serial(struct weston_compositor *c) -{ - c->activate_serial = peek_next_activate_serial (c); -} - -WL_EXPORT void -weston_view_activate(struct weston_view *view, - struct weston_seat *seat, - uint32_t flags) -{ - struct weston_compositor *compositor = seat->compositor; - - if (flags & WESTON_ACTIVATE_FLAG_CLICKED) { - view->click_to_activate_serial = - peek_next_activate_serial(compositor); - } - - weston_seat_set_keyboard_focus(seat, view->surface); -} - -WL_EXPORT void -notify_button(struct weston_seat *seat, const struct timespec *time, - int32_t button, enum wl_pointer_button_state state) -{ - struct weston_compositor *compositor = seat->compositor; - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - weston_compositor_idle_inhibit(compositor); - if (pointer->button_count == 0) { - pointer->grab_button = button; - pointer->grab_time = *time; - pointer->grab_x = pointer->x; - pointer->grab_y = pointer->y; - } - pointer->button_count++; - } else { - weston_compositor_idle_release(compositor); - pointer->button_count--; - } - - weston_compositor_run_button_binding(compositor, pointer, time, button, - state); - - pointer->grab->interface->button(pointer->grab, time, button, state); - - if (pointer->button_count == 1) - pointer->grab_serial = - wl_display_get_serial(compositor->wl_display); -} - -WL_EXPORT void -notify_axis(struct weston_seat *seat, const struct timespec *time, - struct weston_pointer_axis_event *event) -{ - struct weston_compositor *compositor = seat->compositor; - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - weston_compositor_wake(compositor); - - if (weston_compositor_run_axis_binding(compositor, pointer, - time, event)) - return; - - pointer->grab->interface->axis(pointer->grab, time, event); -} - -WL_EXPORT void -notify_axis_source(struct weston_seat *seat, uint32_t source) -{ - struct weston_compositor *compositor = seat->compositor; - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - weston_compositor_wake(compositor); - - pointer->grab->interface->axis_source(pointer->grab, source); -} - -WL_EXPORT void -notify_pointer_frame(struct weston_seat *seat) -{ - struct weston_compositor *compositor = seat->compositor; - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - weston_compositor_wake(compositor); - - pointer->grab->interface->frame(pointer->grab); -} - -WL_EXPORT int -weston_keyboard_set_locks(struct weston_keyboard *keyboard, - uint32_t mask, uint32_t value) -{ - uint32_t serial; - xkb_mod_mask_t mods_depressed, mods_latched, mods_locked, group; - xkb_mod_mask_t num, caps; - - /* We don't want the leds to go out of sync with the actual state - * so if the backend has no way to change the leds don't try to - * change the state */ - if (!keyboard->seat->led_update) - return -1; - - mods_depressed = xkb_state_serialize_mods(keyboard->xkb_state.state, - XKB_STATE_DEPRESSED); - mods_latched = xkb_state_serialize_mods(keyboard->xkb_state.state, - XKB_STATE_LATCHED); - mods_locked = xkb_state_serialize_mods(keyboard->xkb_state.state, - XKB_STATE_LOCKED); - group = xkb_state_serialize_group(keyboard->xkb_state.state, - XKB_STATE_EFFECTIVE); - - num = (1 << keyboard->xkb_info->mod2_mod); - caps = (1 << keyboard->xkb_info->caps_mod); - if (mask & WESTON_NUM_LOCK) { - if (value & WESTON_NUM_LOCK) - mods_locked |= num; - else - mods_locked &= ~num; - } - if (mask & WESTON_CAPS_LOCK) { - if (value & WESTON_CAPS_LOCK) - mods_locked |= caps; - else - mods_locked &= ~caps; - } - - xkb_state_update_mask(keyboard->xkb_state.state, mods_depressed, - mods_latched, mods_locked, 0, 0, group); - - serial = wl_display_next_serial( - keyboard->seat->compositor->wl_display); - notify_modifiers(keyboard->seat, serial); - - return 0; -} - -WL_EXPORT void -notify_modifiers(struct weston_seat *seat, uint32_t serial) -{ - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct weston_keyboard_grab *grab = keyboard->grab; - uint32_t mods_depressed, mods_latched, mods_locked, group; - uint32_t mods_lookup; - enum weston_led leds = 0; - int changed = 0; - - /* Serialize and update our internal state, checking to see if it's - * different to the previous state. */ - mods_depressed = xkb_state_serialize_mods(keyboard->xkb_state.state, - XKB_STATE_MODS_DEPRESSED); - mods_latched = xkb_state_serialize_mods(keyboard->xkb_state.state, - XKB_STATE_MODS_LATCHED); - mods_locked = xkb_state_serialize_mods(keyboard->xkb_state.state, - XKB_STATE_MODS_LOCKED); - group = xkb_state_serialize_layout(keyboard->xkb_state.state, - XKB_STATE_LAYOUT_EFFECTIVE); - - if (mods_depressed != keyboard->modifiers.mods_depressed || - mods_latched != keyboard->modifiers.mods_latched || - mods_locked != keyboard->modifiers.mods_locked || - group != keyboard->modifiers.group) - changed = 1; - - run_modifier_bindings(seat, keyboard->modifiers.mods_depressed, - mods_depressed); - - keyboard->modifiers.mods_depressed = mods_depressed; - keyboard->modifiers.mods_latched = mods_latched; - keyboard->modifiers.mods_locked = mods_locked; - keyboard->modifiers.group = group; - - /* And update the modifier_state for bindings. */ - mods_lookup = mods_depressed | mods_latched; - seat->modifier_state = 0; - if (mods_lookup & (1 << keyboard->xkb_info->ctrl_mod)) - seat->modifier_state |= MODIFIER_CTRL; - if (mods_lookup & (1 << keyboard->xkb_info->alt_mod)) - seat->modifier_state |= MODIFIER_ALT; - if (mods_lookup & (1 << keyboard->xkb_info->super_mod)) - seat->modifier_state |= MODIFIER_SUPER; - if (mods_lookup & (1 << keyboard->xkb_info->shift_mod)) - seat->modifier_state |= MODIFIER_SHIFT; - - /* Finally, notify the compositor that LEDs have changed. */ - if (xkb_state_led_index_is_active(keyboard->xkb_state.state, - keyboard->xkb_info->num_led)) - leds |= LED_NUM_LOCK; - if (xkb_state_led_index_is_active(keyboard->xkb_state.state, - keyboard->xkb_info->caps_led)) - leds |= LED_CAPS_LOCK; - if (xkb_state_led_index_is_active(keyboard->xkb_state.state, - keyboard->xkb_info->scroll_led)) - leds |= LED_SCROLL_LOCK; - if (leds != keyboard->xkb_state.leds && seat->led_update) - seat->led_update(seat, leds); - keyboard->xkb_state.leds = leds; - - if (changed) { - grab->interface->modifiers(grab, - serial, - keyboard->modifiers.mods_depressed, - keyboard->modifiers.mods_latched, - keyboard->modifiers.mods_locked, - keyboard->modifiers.group); - } -} - -static void -update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key, - enum wl_keyboard_key_state state) -{ - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - enum xkb_key_direction direction; - - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) - direction = XKB_KEY_DOWN; - else - direction = XKB_KEY_UP; - - /* Offset the keycode by 8, as the evdev XKB rules reflect X's - * broken keycode system, which starts at 8. */ - xkb_state_update_key(keyboard->xkb_state.state, key + 8, direction); - - notify_modifiers(seat, serial); -} - -WL_EXPORT void -weston_keyboard_send_keymap(struct weston_keyboard *kbd, struct wl_resource *resource) -{ - struct weston_xkb_info *xkb_info = kbd->xkb_info; - int fd; - size_t size; - enum ro_anonymous_file_mapmode mapmode; - - if (wl_resource_get_version(resource) < 7) - mapmode = RO_ANONYMOUS_FILE_MAPMODE_SHARED; - else - mapmode = RO_ANONYMOUS_FILE_MAPMODE_PRIVATE; - - fd = os_ro_anonymous_file_get_fd(xkb_info->keymap_rofile, mapmode); - size = os_ro_anonymous_file_size(xkb_info->keymap_rofile); - - if (fd == -1) { - weston_log("creating a keymap file failed: %s\n", - strerror(errno)); - return; - } - - wl_keyboard_send_keymap(resource, - WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, - fd, - size); - - os_ro_anonymous_file_put_fd(fd); -} - -static void -send_modifiers(struct wl_resource *resource, uint32_t serial, struct weston_keyboard *keyboard) -{ - wl_keyboard_send_modifiers(resource, serial, - keyboard->modifiers.mods_depressed, - keyboard->modifiers.mods_latched, - keyboard->modifiers.mods_locked, - keyboard->modifiers.group); -} - -static struct weston_xkb_info * -weston_xkb_info_create(struct xkb_keymap *keymap); - -static void -update_keymap(struct weston_seat *seat) -{ - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct wl_resource *resource; - struct weston_xkb_info *xkb_info; - struct xkb_state *state; - xkb_mod_mask_t latched_mods; - xkb_mod_mask_t locked_mods; - - xkb_info = weston_xkb_info_create(keyboard->pending_keymap); - - xkb_keymap_unref(keyboard->pending_keymap); - keyboard->pending_keymap = NULL; - - if (!xkb_info) { - weston_log("failed to create XKB info\n"); - return; - } - - state = xkb_state_new(xkb_info->keymap); - if (!state) { - weston_log("failed to initialise XKB state\n"); - weston_xkb_info_destroy(xkb_info); - return; - } - - latched_mods = xkb_state_serialize_mods(keyboard->xkb_state.state, - XKB_STATE_MODS_LATCHED); - locked_mods = xkb_state_serialize_mods(keyboard->xkb_state.state, - XKB_STATE_MODS_LOCKED); - xkb_state_update_mask(state, - 0, /* depressed */ - latched_mods, - locked_mods, - 0, 0, 0); - - weston_xkb_info_destroy(keyboard->xkb_info); - keyboard->xkb_info = xkb_info; - - xkb_state_unref(keyboard->xkb_state.state); - keyboard->xkb_state.state = state; - - wl_resource_for_each(resource, &keyboard->resource_list) - weston_keyboard_send_keymap(keyboard, resource); - wl_resource_for_each(resource, &keyboard->focus_resource_list) - weston_keyboard_send_keymap(keyboard, resource); - - notify_modifiers(seat, wl_display_next_serial(seat->compositor->wl_display)); - - if (!latched_mods && !locked_mods) - return; - - wl_resource_for_each(resource, &keyboard->resource_list) - send_modifiers(resource, wl_display_get_serial(seat->compositor->wl_display), keyboard); - wl_resource_for_each(resource, &keyboard->focus_resource_list) - send_modifiers(resource, wl_display_get_serial(seat->compositor->wl_display), keyboard); -} - -WL_EXPORT void -notify_key(struct weston_seat *seat, const struct timespec *time, uint32_t key, - enum wl_keyboard_key_state state, - enum weston_key_state_update update_state) -{ - struct weston_compositor *compositor = seat->compositor; - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct weston_keyboard_grab *grab = keyboard->grab; - uint32_t *k, *end; - - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - weston_compositor_idle_inhibit(compositor); - } else { - weston_compositor_idle_release(compositor); - } - - end = keyboard->keys.data + keyboard->keys.size; - for (k = keyboard->keys.data; k < end; k++) { - if (*k == key) { - /* Ignore server-generated repeats. */ - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) - return; - *k = *--end; - } - } - keyboard->keys.size = (void *) end - keyboard->keys.data; - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - k = wl_array_add(&keyboard->keys, sizeof *k); - *k = key; - } - - if (grab == &keyboard->default_grab || - grab == &keyboard->input_method_grab) { - weston_compositor_run_key_binding(compositor, keyboard, time, - key, state); - grab = keyboard->grab; - } - - grab->interface->key(grab, time, key, state); - - if (keyboard->pending_keymap && - keyboard->keys.size == 0) - update_keymap(seat); - - if (update_state == STATE_UPDATE_AUTOMATIC) { - update_modifier_state(seat, - wl_display_get_serial(compositor->wl_display), - key, - state); - } - - keyboard->grab_serial = wl_display_get_serial(compositor->wl_display); - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - keyboard->grab_time = *time; - keyboard->grab_key = key; - } -} - -WL_EXPORT void -notify_pointer_focus(struct weston_seat *seat, struct weston_output *output, - double x, double y) -{ - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - if (output) { - weston_pointer_move_to(pointer, - wl_fixed_from_double(x), - wl_fixed_from_double(y)); - } else { - /* FIXME: We should call weston_pointer_set_focus(seat, - * NULL) here, but somehow that breaks re-entry... */ - } -} - -static void -destroy_device_saved_kbd_focus(struct wl_listener *listener, void *data) -{ - struct weston_seat *ws; - - ws = container_of(listener, struct weston_seat, - saved_kbd_focus_listener); - - ws->saved_kbd_focus = NULL; -} - -WL_EXPORT void -notify_keyboard_focus_in(struct weston_seat *seat, struct wl_array *keys, - enum weston_key_state_update update_state) -{ - struct weston_compositor *compositor = seat->compositor; - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct weston_surface *surface; - uint32_t *k, serial; - - serial = wl_display_next_serial(compositor->wl_display); - wl_array_copy(&keyboard->keys, keys); - wl_array_for_each(k, &keyboard->keys) { - weston_compositor_idle_inhibit(compositor); - if (update_state == STATE_UPDATE_AUTOMATIC) - update_modifier_state(seat, serial, *k, - WL_KEYBOARD_KEY_STATE_PRESSED); - } - - surface = seat->saved_kbd_focus; - if (surface) { - weston_keyboard_set_focus(keyboard, surface); - } -} - -WL_EXPORT void -notify_keyboard_focus_out(struct weston_seat *seat) -{ - struct weston_compositor *compositor = seat->compositor; - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - struct weston_surface *focus = keyboard->focus; - uint32_t *k, serial; - - serial = wl_display_next_serial(compositor->wl_display); - wl_array_for_each(k, &keyboard->keys) { - weston_compositor_idle_release(compositor); - update_modifier_state(seat, serial, *k, - WL_KEYBOARD_KEY_STATE_RELEASED); - } - - seat->modifier_state = 0; - - weston_keyboard_set_focus(keyboard, NULL); - weston_keyboard_cancel_grab(keyboard); - if (pointer) - weston_pointer_cancel_grab(pointer); - - if (focus) { - seat->saved_kbd_focus = focus; - seat->saved_kbd_focus_listener.notify = - destroy_device_saved_kbd_focus; - wl_signal_add(&focus->destroy_signal, - &seat->saved_kbd_focus_listener); - } -} - -WL_EXPORT void -weston_touch_set_focus(struct weston_touch *touch, struct weston_view *view) -{ - struct wl_list *focus_resource_list; - - focus_resource_list = &touch->focus_resource_list; - - if (view && touch->focus && - touch->focus->surface == view->surface) { - touch->focus = view; - return; - } - - wl_list_remove(&touch->focus_resource_listener.link); - wl_list_init(&touch->focus_resource_listener.link); - wl_list_remove(&touch->focus_view_listener.link); - wl_list_init(&touch->focus_view_listener.link); - - if (!wl_list_empty(focus_resource_list)) { - move_resources(&touch->resource_list, - focus_resource_list); - } - - if (view) { - struct wl_client *surface_client; - - if (!view->surface->resource) { - touch->focus = NULL; - return; - } - - surface_client = wl_resource_get_client(view->surface->resource); - move_resources_for_client(focus_resource_list, - &touch->resource_list, - surface_client); - wl_resource_add_destroy_listener(view->surface->resource, - &touch->focus_resource_listener); - wl_signal_add(&view->destroy_signal, &touch->focus_view_listener); - } - touch->focus = view; -} - -static void -process_touch_normal(struct weston_touch_device *device, - const struct timespec *time, int touch_id, - double double_x, double double_y, int touch_type) -{ - struct weston_touch *touch = device->aggregate; - struct weston_touch_grab *grab = device->aggregate->grab; - struct weston_compositor *ec = device->aggregate->seat->compositor; - struct weston_view *ev; - wl_fixed_t sx, sy; - wl_fixed_t x = wl_fixed_from_double(double_x); - wl_fixed_t y = wl_fixed_from_double(double_y); - - /* Update grab's global coordinates. */ - if (touch_id == touch->grab_touch_id && touch_type != WL_TOUCH_UP) { - touch->grab_x = x; - touch->grab_y = y; - } - - switch (touch_type) { - case WL_TOUCH_DOWN: - /* the first finger down picks the view, and all further go - * to that view for the remainder of the touch session i.e. - * until all touch points are up again. */ - if (touch->num_tp == 1) { - ev = weston_compositor_pick_view(ec, x, y, &sx, &sy); - weston_touch_set_focus(touch, ev); - } else if (!touch->focus) { - /* Unexpected condition: We have non-initial touch but - * there is no focused surface. - */ - weston_log("touch event received with %d points down " - "but no surface focused\n", touch->num_tp); - return; - } - - weston_compositor_run_touch_binding(ec, touch, - time, touch_type); - - grab->interface->down(grab, time, touch_id, x, y); - if (touch->num_tp == 1) { - touch->grab_serial = - wl_display_get_serial(ec->wl_display); - touch->grab_touch_id = touch_id; - touch->grab_time = *time; - touch->grab_x = x; - touch->grab_y = y; - } - - break; - case WL_TOUCH_MOTION: - ev = touch->focus; - if (!ev) - break; - - grab->interface->motion(grab, time, touch_id, x, y); - break; - case WL_TOUCH_UP: - grab->interface->up(grab, time, touch_id); - if (touch->num_tp == 0) - weston_touch_set_focus(touch, NULL); - break; - } -} - -static enum weston_touch_mode -get_next_touch_mode(enum weston_touch_mode from) -{ - switch (from) { - case WESTON_TOUCH_MODE_PREP_NORMAL: - return WESTON_TOUCH_MODE_NORMAL; - - case WESTON_TOUCH_MODE_PREP_CALIB: - return WESTON_TOUCH_MODE_CALIB; - - case WESTON_TOUCH_MODE_NORMAL: - case WESTON_TOUCH_MODE_CALIB: - return from; - } - - return WESTON_TOUCH_MODE_NORMAL; -} - -/** Global touch mode update - * - * If no seat has a touch down and the compositor is in a PREP touch mode, - * set the compositor to the goal touch mode. - * - * Calls calibrator if touch mode changed. - */ -static void -weston_compositor_update_touch_mode(struct weston_compositor *compositor) -{ - struct weston_seat *seat; - struct weston_touch *touch; - enum weston_touch_mode goal; - - wl_list_for_each(seat, &compositor->seat_list, link) { - touch = weston_seat_get_touch(seat); - if (!touch) - continue; - - if (touch->num_tp > 0) - return; - } - - goal = get_next_touch_mode(compositor->touch_mode); - if (compositor->touch_mode != goal) { - compositor->touch_mode = goal; - touch_calibrator_mode_changed(compositor); - } -} - -/** Start transition to normal touch event handling - * - * The touch event mode changes when all touches on all touch devices have - * been lifted. If no touches are currently down, the transition is immediate. - * - * \sa weston_touch_mode - */ -void -weston_compositor_set_touch_mode_normal(struct weston_compositor *compositor) -{ - switch (compositor->touch_mode) { - case WESTON_TOUCH_MODE_PREP_NORMAL: - case WESTON_TOUCH_MODE_NORMAL: - return; - case WESTON_TOUCH_MODE_PREP_CALIB: - compositor->touch_mode = WESTON_TOUCH_MODE_NORMAL; - touch_calibrator_mode_changed(compositor); - return; - case WESTON_TOUCH_MODE_CALIB: - compositor->touch_mode = WESTON_TOUCH_MODE_PREP_NORMAL; - } - - weston_compositor_update_touch_mode(compositor); -} - -/** Start transition to calibrator touch event handling - * - * The touch event mode changes when all touches on all touch devices have - * been lifted. If no touches are currently down, the transition is immediate. - * - * \sa weston_touch_mode - */ -void -weston_compositor_set_touch_mode_calib(struct weston_compositor *compositor) -{ - switch (compositor->touch_mode) { - case WESTON_TOUCH_MODE_PREP_CALIB: - case WESTON_TOUCH_MODE_CALIB: - assert(0); - return; - case WESTON_TOUCH_MODE_PREP_NORMAL: - compositor->touch_mode = WESTON_TOUCH_MODE_CALIB; - touch_calibrator_mode_changed(compositor); - return; - case WESTON_TOUCH_MODE_NORMAL: - compositor->touch_mode = WESTON_TOUCH_MODE_PREP_CALIB; - } - - weston_compositor_update_touch_mode(compositor); -} - -/** Feed in touch down, motion, and up events, calibratable device. - * - * It assumes always the correct cycle sequence until it gets here: touch_down - * → touch_update → ... → touch_update → touch_end. The driver is responsible - * for sending along such order. - * - * \param device The physical device that generated the event. - * \param time The event timestamp. - * \param touch_id ID for the touch point of this event (multi-touch). - * \param x X coordinate in compositor global space. - * \param y Y coordinate in compositor global space. - * \param norm Normalized device X, Y coordinates in calibration space, or NULL. - * \param touch_type Either WL_TOUCH_DOWN, WL_TOUCH_UP, or WL_TOUCH_MOTION. - * - * Coordinates double_x and double_y are used for normal operation. - * - * Coordinates norm are only used for touch device calibration. If and only if - * the weston_touch_device does not support calibrating, norm must be NULL. - * - * The calibration space is the normalized coordinate space - * [0.0, 1.0]×[0.0, 1.0] of the weston_touch_device. This is assumed to - * map to the similar normalized coordinate space of the associated - * weston_output. - */ -WL_EXPORT void -notify_touch_normalized(struct weston_touch_device *device, - const struct timespec *time, - int touch_id, - double x, double y, - const struct weston_point2d_device_normalized *norm, - int touch_type) -{ - struct weston_seat *seat = device->aggregate->seat; - struct weston_touch *touch = device->aggregate; - - if (touch_type != WL_TOUCH_UP) { - if (weston_touch_device_can_calibrate(device)) - assert(norm != NULL); - else - assert(norm == NULL); - } - - /* Update touchpoints count regardless of the current mode. */ - switch (touch_type) { - case WL_TOUCH_DOWN: - weston_compositor_idle_inhibit(seat->compositor); - - touch->num_tp++; - break; - case WL_TOUCH_UP: - if (touch->num_tp == 0) { - /* This can happen if we start out with one or - * more fingers on the touch screen, in which - * case we didn't get the corresponding down - * event. */ - weston_log("Unmatched touch up event on seat %s, device %s\n", - seat->seat_name, device->syspath); - return; - } - weston_compositor_idle_release(seat->compositor); - - touch->num_tp--; - break; - default: - break; - } - - /* Properly forward the touch event */ - switch (weston_touch_device_get_mode(device)) { - case WESTON_TOUCH_MODE_NORMAL: - case WESTON_TOUCH_MODE_PREP_CALIB: - process_touch_normal(device, time, touch_id, x, y, touch_type); - break; - case WESTON_TOUCH_MODE_CALIB: - case WESTON_TOUCH_MODE_PREP_NORMAL: - notify_touch_calibrator(device, time, touch_id, - norm, touch_type); - break; - } -} - -WL_EXPORT void -notify_touch_frame(struct weston_touch_device *device) -{ - struct weston_touch_grab *grab; - - switch (weston_touch_device_get_mode(device)) { - case WESTON_TOUCH_MODE_NORMAL: - case WESTON_TOUCH_MODE_PREP_CALIB: - grab = device->aggregate->grab; - grab->interface->frame(grab); - break; - case WESTON_TOUCH_MODE_CALIB: - case WESTON_TOUCH_MODE_PREP_NORMAL: - notify_touch_calibrator_frame(device); - break; - } - - weston_compositor_update_touch_mode(device->aggregate->seat->compositor); -} - -WL_EXPORT void -notify_touch_cancel(struct weston_touch_device *device) -{ - struct weston_touch_grab *grab; - - switch (weston_touch_device_get_mode(device)) { - case WESTON_TOUCH_MODE_NORMAL: - case WESTON_TOUCH_MODE_PREP_CALIB: - grab = device->aggregate->grab; - grab->interface->cancel(grab); - break; - case WESTON_TOUCH_MODE_CALIB: - case WESTON_TOUCH_MODE_PREP_NORMAL: - notify_touch_calibrator_cancel(device); - break; - } - - weston_compositor_update_touch_mode(device->aggregate->seat->compositor); -} - -static int -pointer_cursor_surface_get_label(struct weston_surface *surface, - char *buf, size_t len) -{ - return snprintf(buf, len, "cursor"); -} - -static void -pointer_cursor_surface_committed(struct weston_surface *es, - int32_t dx, int32_t dy) -{ - struct weston_pointer *pointer = es->committed_private; - int x, y; - - if (es->width == 0) - return; - - assert(es == pointer->sprite->surface); - - pointer->hotspot_x -= dx; - pointer->hotspot_y -= dy; - - x = wl_fixed_to_int(pointer->x) - pointer->hotspot_x; - y = wl_fixed_to_int(pointer->y) - pointer->hotspot_y; - - weston_view_set_position(pointer->sprite, x, y); - - empty_region(&es->pending.input); - empty_region(&es->input); - - if (!weston_surface_is_mapped(es)) { - weston_layer_entry_insert(&es->compositor->cursor_layer.view_list, - &pointer->sprite->layer_link); - weston_view_update_transform(pointer->sprite); - es->is_mapped = true; - pointer->sprite->is_mapped = true; - } -} - -static void -pointer_set_cursor(struct wl_client *client, struct wl_resource *resource, - uint32_t serial, struct wl_resource *surface_resource, - int32_t x, int32_t y) -{ - struct weston_pointer *pointer = wl_resource_get_user_data(resource); - struct weston_surface *surface = NULL; - - if (!pointer) - return; - - if (surface_resource) - surface = wl_resource_get_user_data(surface_resource); - - if (pointer->focus == NULL) - return; - /* pointer->focus->surface->resource can be NULL. Surfaces like the - black_surface used in shell.c for fullscreen don't have - a resource, but can still have focus */ - if (pointer->focus->surface->resource == NULL) - return; - if (wl_resource_get_client(pointer->focus->surface->resource) != client) - return; - if (pointer->focus_serial - serial > UINT32_MAX / 2) - return; - - if (!surface) { - if (pointer->sprite) - pointer_unmap_sprite(pointer); - return; - } - - if (pointer->sprite && pointer->sprite->surface == surface && - pointer->hotspot_x == x && pointer->hotspot_y == y) - return; - - if (!pointer->sprite || pointer->sprite->surface != surface) { - if (weston_surface_set_role(surface, "wl_pointer-cursor", - resource, - WL_POINTER_ERROR_ROLE) < 0) - return; - - if (pointer->sprite) - pointer_unmap_sprite(pointer); - - wl_signal_add(&surface->destroy_signal, - &pointer->sprite_destroy_listener); - - surface->committed = pointer_cursor_surface_committed; - surface->committed_private = pointer; - weston_surface_set_label_func(surface, - pointer_cursor_surface_get_label); - pointer->sprite = weston_view_create(surface); - } - - pointer->hotspot_x = x; - pointer->hotspot_y = y; - - if (surface->buffer_ref.buffer) { - pointer_cursor_surface_committed(surface, 0, 0); - weston_view_schedule_repaint(pointer->sprite); - } -} - -static void -pointer_release(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct wl_pointer_interface pointer_interface = { - pointer_set_cursor, - pointer_release -}; - -static void -seat_get_pointer(struct wl_client *client, struct wl_resource *resource, - uint32_t id) -{ - struct weston_seat *seat = wl_resource_get_user_data(resource); - /* We use the pointer_state directly, which means we'll - * give a wl_pointer if the seat has ever had one - even though - * the spec explicitly states that this request only takes effect - * if the seat has the pointer capability. - * - * This prevents a race between the compositor sending new - * capabilities and the client trying to use the old ones. - */ - struct weston_pointer *pointer = seat ? seat->pointer_state : NULL; - struct wl_resource *cr; - struct weston_pointer_client *pointer_client; - - cr = wl_resource_create(client, &wl_pointer_interface, - wl_resource_get_version(resource), id); - if (cr == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_list_init(wl_resource_get_link(cr)); - wl_resource_set_implementation(cr, &pointer_interface, pointer, - unbind_pointer_client_resource); - - /* If we don't have a pointer_state, the resource is inert, so there - * is nothing more to set up */ - if (!pointer) - return; - - pointer_client = weston_pointer_ensure_pointer_client(pointer, client); - if (!pointer_client) { - wl_client_post_no_memory(client); - return; - } - - wl_list_insert(&pointer_client->pointer_resources, - wl_resource_get_link(cr)); - - if (pointer->focus && pointer->focus->surface->resource && - wl_resource_get_client(pointer->focus->surface->resource) == client) { - wl_fixed_t sx, sy; - - weston_view_from_global_fixed(pointer->focus, - pointer->x, - pointer->y, - &sx, &sy); - - wl_pointer_send_enter(cr, - pointer->focus_serial, - pointer->focus->surface->resource, - sx, sy); - pointer_send_frame(cr); - } -} - -static void -destroy_keyboard_resource(struct wl_resource *resource) -{ - struct weston_keyboard *keyboard = wl_resource_get_user_data(resource); - - wl_list_remove(wl_resource_get_link(resource)); - - if (keyboard) { - remove_input_resource_from_timestamps(resource, - &keyboard->timestamps_list); - } -} - -static void -keyboard_release(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct wl_keyboard_interface keyboard_interface = { - keyboard_release -}; - -static bool -should_send_modifiers_to_client(struct weston_seat *seat, - struct wl_client *client) -{ - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - if (keyboard && - keyboard->focus && - keyboard->focus->resource && - wl_resource_get_client(keyboard->focus->resource) == client) - return true; - - if (pointer && - pointer->focus && - pointer->focus->surface->resource && - wl_resource_get_client(pointer->focus->surface->resource) == client) - return true; - - return false; -} - -static void -seat_get_keyboard(struct wl_client *client, struct wl_resource *resource, - uint32_t id) -{ - struct weston_seat *seat = wl_resource_get_user_data(resource); - /* We use the keyboard_state directly, which means we'll - * give a wl_keyboard if the seat has ever had one - even though - * the spec explicitly states that this request only takes effect - * if the seat has the keyboard capability. - * - * This prevents a race between the compositor sending new - * capabilities and the client trying to use the old ones. - */ - struct weston_keyboard *keyboard = seat ? seat->keyboard_state : NULL; - struct wl_resource *cr; - - cr = wl_resource_create(client, &wl_keyboard_interface, - wl_resource_get_version(resource), id); - if (cr == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_list_init(wl_resource_get_link(cr)); - wl_resource_set_implementation(cr, &keyboard_interface, - keyboard, destroy_keyboard_resource); - - /* If we don't have a keyboard_state, the resource is inert, so there - * is nothing more to set up */ - if (!keyboard) - return; - - /* May be moved to focused list later by either - * weston_keyboard_set_focus or directly if this client is already - * focused */ - wl_list_insert(&keyboard->resource_list, wl_resource_get_link(cr)); - - if (wl_resource_get_version(cr) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) { - wl_keyboard_send_repeat_info(cr, - seat->compositor->kb_repeat_rate, - seat->compositor->kb_repeat_delay); - } - - weston_keyboard_send_keymap(keyboard, cr); - - if (should_send_modifiers_to_client(seat, client)) { - send_modifiers_to_resource(keyboard, - cr, - keyboard->focus_serial); - } - - if (keyboard->focus && keyboard->focus->resource && - wl_resource_get_client(keyboard->focus->resource) == client) { - struct weston_surface *surface = - (struct weston_surface *)keyboard->focus; - - wl_list_remove(wl_resource_get_link(cr)); - wl_list_insert(&keyboard->focus_resource_list, - wl_resource_get_link(cr)); - wl_keyboard_send_enter(cr, - keyboard->focus_serial, - surface->resource, - &keyboard->keys); - - /* If this is the first keyboard resource for this - * client... */ - if (keyboard->focus_resource_list.prev == - wl_resource_get_link(cr)) - wl_data_device_set_keyboard_focus(seat); - } -} - -static void -destroy_touch_resource(struct wl_resource *resource) -{ - struct weston_touch *touch = wl_resource_get_user_data(resource); - - wl_list_remove(wl_resource_get_link(resource)); - - if (touch) { - remove_input_resource_from_timestamps(resource, - &touch->timestamps_list); - } -} - -static void -touch_release(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct wl_touch_interface touch_interface = { - touch_release -}; - -static void -seat_get_touch(struct wl_client *client, struct wl_resource *resource, - uint32_t id) -{ - struct weston_seat *seat = wl_resource_get_user_data(resource); - /* We use the touch_state directly, which means we'll - * give a wl_touch if the seat has ever had one - even though - * the spec explicitly states that this request only takes effect - * if the seat has the touch capability. - * - * This prevents a race between the compositor sending new - * capabilities and the client trying to use the old ones. - */ - struct weston_touch *touch = seat ? seat->touch_state : NULL; - struct wl_resource *cr; - - cr = wl_resource_create(client, &wl_touch_interface, - wl_resource_get_version(resource), id); - if (cr == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_list_init(wl_resource_get_link(cr)); - wl_resource_set_implementation(cr, &touch_interface, - touch, destroy_touch_resource); - - /* If we don't have a touch_state, the resource is inert, so there - * is nothing more to set up */ - if (!touch) - return; - - if (touch->focus && - wl_resource_get_client(touch->focus->surface->resource) == client) { - wl_list_insert(&touch->focus_resource_list, - wl_resource_get_link(cr)); - } else { - wl_list_insert(&touch->resource_list, - wl_resource_get_link(cr)); - } -} - -static void -seat_release(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct wl_seat_interface seat_interface = { - seat_get_pointer, - seat_get_keyboard, - seat_get_touch, - seat_release, -}; - -static void -bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id) -{ - struct weston_seat *seat = data; - struct wl_resource *resource; - enum wl_seat_capability caps = 0; - - resource = wl_resource_create(client, - &wl_seat_interface, version, id); - wl_list_insert(&seat->base_resource_list, wl_resource_get_link(resource)); - wl_resource_set_implementation(resource, &seat_interface, data, - unbind_resource); - - if (weston_seat_get_pointer(seat)) - caps |= WL_SEAT_CAPABILITY_POINTER; - if (weston_seat_get_keyboard(seat)) - caps |= WL_SEAT_CAPABILITY_KEYBOARD; - if (weston_seat_get_touch(seat)) - caps |= WL_SEAT_CAPABILITY_TOUCH; - - wl_seat_send_capabilities(resource, caps); - if (version >= WL_SEAT_NAME_SINCE_VERSION) - wl_seat_send_name(resource, seat->seat_name); -} - -static void -relative_pointer_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct zwp_relative_pointer_v1_interface relative_pointer_interface = { - relative_pointer_destroy -}; - -static void -relative_pointer_manager_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -relative_pointer_manager_get_relative_pointer(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, - struct wl_resource *pointer_resource) -{ - struct weston_pointer *pointer = - wl_resource_get_user_data(pointer_resource); - struct weston_pointer_client *pointer_client; - struct wl_resource *cr; - - cr = wl_resource_create(client, &zwp_relative_pointer_v1_interface, - wl_resource_get_version(resource), id); - if (cr == NULL) { - wl_client_post_no_memory(client); - return; - } - - pointer_client = weston_pointer_ensure_pointer_client(pointer, client); - if (!pointer_client) { - wl_client_post_no_memory(client); - return; - } - - wl_list_insert(&pointer_client->relative_pointer_resources, - wl_resource_get_link(cr)); - wl_resource_set_implementation(cr, &relative_pointer_interface, - pointer, - unbind_pointer_client_resource); -} - -static const struct zwp_relative_pointer_manager_v1_interface relative_pointer_manager = { - relative_pointer_manager_destroy, - relative_pointer_manager_get_relative_pointer, -}; - -static void -bind_relative_pointer_manager(struct wl_client *client, void *data, - uint32_t version, uint32_t id) -{ - struct weston_compositor *compositor = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, - &zwp_relative_pointer_manager_v1_interface, - 1, id); - - wl_resource_set_implementation(resource, &relative_pointer_manager, - compositor, - NULL); -} - -WL_EXPORT int -weston_compositor_set_xkb_rule_names(struct weston_compositor *ec, - struct xkb_rule_names *names) -{ - if (ec->xkb_context == NULL) { - ec->xkb_context = xkb_context_new(0); - if (ec->xkb_context == NULL) { - weston_log("failed to create XKB context\n"); - return -1; - } - } - - if (names) - ec->xkb_names = *names; - if (!ec->xkb_names.rules) - ec->xkb_names.rules = strdup("evdev"); - if (!ec->xkb_names.model) - ec->xkb_names.model = strdup("pc105"); - if (!ec->xkb_names.layout) - ec->xkb_names.layout = strdup("us"); - - return 0; -} - -static void -weston_xkb_info_destroy(struct weston_xkb_info *xkb_info) -{ - if (--xkb_info->ref_count > 0) - return; - - xkb_keymap_unref(xkb_info->keymap); - - os_ro_anonymous_file_destroy(xkb_info->keymap_rofile); - free(xkb_info); -} - -void -weston_compositor_xkb_destroy(struct weston_compositor *ec) -{ - free((char *) ec->xkb_names.rules); - free((char *) ec->xkb_names.model); - free((char *) ec->xkb_names.layout); - free((char *) ec->xkb_names.variant); - free((char *) ec->xkb_names.options); - - if (ec->xkb_info) - weston_xkb_info_destroy(ec->xkb_info); - xkb_context_unref(ec->xkb_context); -} - -static struct weston_xkb_info * -weston_xkb_info_create(struct xkb_keymap *keymap) -{ - char *keymap_string; - size_t keymap_size; - struct weston_xkb_info *xkb_info = zalloc(sizeof *xkb_info); - if (xkb_info == NULL) - return NULL; - - xkb_info->keymap = xkb_keymap_ref(keymap); - xkb_info->ref_count = 1; - - xkb_info->shift_mod = xkb_keymap_mod_get_index(xkb_info->keymap, - XKB_MOD_NAME_SHIFT); - xkb_info->caps_mod = xkb_keymap_mod_get_index(xkb_info->keymap, - XKB_MOD_NAME_CAPS); - xkb_info->ctrl_mod = xkb_keymap_mod_get_index(xkb_info->keymap, - XKB_MOD_NAME_CTRL); - xkb_info->alt_mod = xkb_keymap_mod_get_index(xkb_info->keymap, - XKB_MOD_NAME_ALT); - xkb_info->mod2_mod = xkb_keymap_mod_get_index(xkb_info->keymap, - "Mod2"); - xkb_info->mod3_mod = xkb_keymap_mod_get_index(xkb_info->keymap, - "Mod3"); - xkb_info->super_mod = xkb_keymap_mod_get_index(xkb_info->keymap, - XKB_MOD_NAME_LOGO); - xkb_info->mod5_mod = xkb_keymap_mod_get_index(xkb_info->keymap, - "Mod5"); - - xkb_info->num_led = xkb_keymap_led_get_index(xkb_info->keymap, - XKB_LED_NAME_NUM); - xkb_info->caps_led = xkb_keymap_led_get_index(xkb_info->keymap, - XKB_LED_NAME_CAPS); - xkb_info->scroll_led = xkb_keymap_led_get_index(xkb_info->keymap, - XKB_LED_NAME_SCROLL); - - keymap_string = xkb_keymap_get_as_string(xkb_info->keymap, - XKB_KEYMAP_FORMAT_TEXT_V1); - if (keymap_string == NULL) { - weston_log("failed to get string version of keymap\n"); - goto err_keymap; - } - keymap_size = strlen(keymap_string) + 1; - - xkb_info->keymap_rofile = os_ro_anonymous_file_create(keymap_size, - keymap_string); - free(keymap_string); - - if (!xkb_info->keymap_rofile) { - weston_log("failed to create anonymous file for keymap\n"); - goto err_keymap; - } - - return xkb_info; - -err_keymap: - xkb_keymap_unref(xkb_info->keymap); - free(xkb_info); - return NULL; -} - -static int -weston_compositor_build_global_keymap(struct weston_compositor *ec) -{ - struct xkb_keymap *keymap; - - if (ec->xkb_info != NULL) - return 0; - - keymap = xkb_keymap_new_from_names(ec->xkb_context, - &ec->xkb_names, - 0); - if (keymap == NULL) { - weston_log("failed to compile global XKB keymap\n"); - weston_log(" tried rules %s, model %s, layout %s, variant %s, " - "options %s\n", - ec->xkb_names.rules, ec->xkb_names.model, - ec->xkb_names.layout, ec->xkb_names.variant, - ec->xkb_names.options); - return -1; - } - - ec->xkb_info = weston_xkb_info_create(keymap); - xkb_keymap_unref(keymap); - if (ec->xkb_info == NULL) - return -1; - - return 0; -} - -WL_EXPORT void -weston_seat_update_keymap(struct weston_seat *seat, struct xkb_keymap *keymap) -{ - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - - if (!keyboard || !keymap) - return; - - xkb_keymap_unref(keyboard->pending_keymap); - keyboard->pending_keymap = xkb_keymap_ref(keymap); - - if (keyboard->keys.size == 0) - update_keymap(seat); -} - -WL_EXPORT int -weston_seat_init_keyboard(struct weston_seat *seat, struct xkb_keymap *keymap) -{ - struct weston_keyboard *keyboard; - - if (seat->keyboard_state) { - seat->keyboard_device_count += 1; - if (seat->keyboard_device_count == 1) - seat_send_updated_caps(seat); - return 0; - } - - keyboard = weston_keyboard_create(); - if (keyboard == NULL) { - weston_log("failed to allocate weston keyboard struct\n"); - return -1; - } - - if (keymap != NULL) { - keyboard->xkb_info = weston_xkb_info_create(keymap); - if (keyboard->xkb_info == NULL) - goto err; - } else { - if (weston_compositor_build_global_keymap(seat->compositor) < 0) - goto err; - keyboard->xkb_info = seat->compositor->xkb_info; - keyboard->xkb_info->ref_count++; - } - - keyboard->xkb_state.state = xkb_state_new(keyboard->xkb_info->keymap); - if (keyboard->xkb_state.state == NULL) { - weston_log("failed to initialise XKB state\n"); - goto err; - } - - keyboard->xkb_state.leds = 0; - - seat->keyboard_state = keyboard; - seat->keyboard_device_count = 1; - keyboard->seat = seat; - - seat_send_updated_caps(seat); - - return 0; - -err: - if (keyboard->xkb_info) - weston_xkb_info_destroy(keyboard->xkb_info); - free(keyboard); - - return -1; -} - -static void -weston_keyboard_reset_state(struct weston_keyboard *keyboard) -{ - struct weston_seat *seat = keyboard->seat; - struct xkb_state *state; - - state = xkb_state_new(keyboard->xkb_info->keymap); - if (!state) { - weston_log("failed to reset XKB state\n"); - return; - } - xkb_state_unref(keyboard->xkb_state.state); - keyboard->xkb_state.state = state; - - keyboard->xkb_state.leds = 0; - - seat->modifier_state = 0; -} - -WL_EXPORT void -weston_seat_release_keyboard(struct weston_seat *seat) -{ - seat->keyboard_device_count--; - assert(seat->keyboard_device_count >= 0); - if (seat->keyboard_device_count == 0) { - weston_keyboard_set_focus(seat->keyboard_state, NULL); - weston_keyboard_cancel_grab(seat->keyboard_state); - weston_keyboard_reset_state(seat->keyboard_state); - seat_send_updated_caps(seat); - } -} - -WL_EXPORT void -weston_seat_init_pointer(struct weston_seat *seat) -{ - struct weston_pointer *pointer; - - if (seat->pointer_state) { - seat->pointer_device_count += 1; - if (seat->pointer_device_count == 1) - seat_send_updated_caps(seat); - return; - } - - pointer = weston_pointer_create(seat); - if (pointer == NULL) - return; - - seat->pointer_state = pointer; - seat->pointer_device_count = 1; - pointer->seat = seat; - - seat_send_updated_caps(seat); -} - -WL_EXPORT void -weston_seat_release_pointer(struct weston_seat *seat) -{ - struct weston_pointer *pointer = seat->pointer_state; - - seat->pointer_device_count--; - if (seat->pointer_device_count == 0) { - weston_pointer_clear_focus(pointer); - weston_pointer_cancel_grab(pointer); - - if (pointer->sprite) - pointer_unmap_sprite(pointer); - - weston_pointer_reset_state(pointer); - seat_send_updated_caps(seat); - - /* seat->pointer is intentionally not destroyed so that - * a newly attached pointer on this seat will retain - * the previous cursor co-ordinates. - */ - } -} - -WL_EXPORT void -weston_seat_init_touch(struct weston_seat *seat) -{ - struct weston_touch *touch; - - if (seat->touch_state) { - seat->touch_device_count += 1; - if (seat->touch_device_count == 1) - seat_send_updated_caps(seat); - return; - } - - touch = weston_touch_create(); - if (touch == NULL) - return; - - seat->touch_state = touch; - seat->touch_device_count = 1; - touch->seat = seat; - - seat_send_updated_caps(seat); -} - -WL_EXPORT void -weston_seat_release_touch(struct weston_seat *seat) -{ - seat->touch_device_count--; - if (seat->touch_device_count == 0) { - weston_touch_set_focus(seat->touch_state, NULL); - weston_touch_cancel_grab(seat->touch_state); - weston_touch_reset_state(seat->touch_state); - seat_send_updated_caps(seat); - } -} - -WL_EXPORT void -weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec, - const char *seat_name) -{ - memset(seat, 0, sizeof *seat); - - seat->selection_data_source = NULL; - wl_list_init(&seat->base_resource_list); - wl_signal_init(&seat->selection_signal); - wl_list_init(&seat->drag_resource_list); - wl_signal_init(&seat->destroy_signal); - wl_signal_init(&seat->updated_caps_signal); - - seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, - MIN(wl_seat_interface.version, 7), - seat, bind_seat); - - seat->compositor = ec; - seat->modifier_state = 0; - seat->seat_name = strdup(seat_name); - - wl_list_insert(ec->seat_list.prev, &seat->link); - - clipboard_create(seat); - - wl_signal_emit(&ec->seat_created_signal, seat); -} - -WL_EXPORT void -weston_seat_release(struct weston_seat *seat) -{ - struct wl_resource *resource; - - wl_resource_for_each(resource, &seat->base_resource_list) { - wl_resource_set_user_data(resource, NULL); - } - - wl_resource_for_each(resource, &seat->drag_resource_list) { - wl_resource_set_user_data(resource, NULL); - } - - wl_list_remove(&seat->base_resource_list); - wl_list_remove(&seat->drag_resource_list); - - wl_list_remove(&seat->link); - - if (seat->saved_kbd_focus) - wl_list_remove(&seat->saved_kbd_focus_listener.link); - - if (seat->pointer_state) - weston_pointer_destroy(seat->pointer_state); - if (seat->keyboard_state) - weston_keyboard_destroy(seat->keyboard_state); - if (seat->touch_state) - weston_touch_destroy(seat->touch_state); - - free (seat->seat_name); - - wl_global_destroy(seat->global); - - wl_signal_emit(&seat->destroy_signal, seat); -} - -/** Get a seat's keyboard pointer - * - * \param seat The seat to query - * \return The seat's keyboard pointer, or NULL if no keyboard is present - * - * The keyboard pointer for a seat isn't freed when all keyboards are removed, - * so it should only be used when the seat's keyboard_device_count is greater - * than zero. This function does that test and only returns a pointer - * when a keyboard is present. - */ -WL_EXPORT struct weston_keyboard * -weston_seat_get_keyboard(struct weston_seat *seat) -{ - if (!seat) - return NULL; - - if (seat->keyboard_device_count) - return seat->keyboard_state; - - return NULL; -} - -/** Get a seat's pointer pointer - * - * \param seat The seat to query - * \return The seat's pointer pointer, or NULL if no pointer device is present - * - * The pointer pointer for a seat isn't freed when all mice are removed, - * so it should only be used when the seat's pointer_device_count is greater - * than zero. This function does that test and only returns a pointer - * when a pointing device is present. - */ -WL_EXPORT struct weston_pointer * -weston_seat_get_pointer(struct weston_seat *seat) -{ - if (!seat) - return NULL; - - if (seat->pointer_device_count) - return seat->pointer_state; - - return NULL; -} - -static const struct zwp_locked_pointer_v1_interface locked_pointer_interface; -static const struct zwp_confined_pointer_v1_interface confined_pointer_interface; - -static enum pointer_constraint_type -pointer_constraint_get_type(struct weston_pointer_constraint *constraint) -{ - if (wl_resource_instance_of(constraint->resource, - &zwp_locked_pointer_v1_interface, - &locked_pointer_interface)) { - return POINTER_CONSTRAINT_TYPE_LOCK; - } else if (wl_resource_instance_of(constraint->resource, - &zwp_confined_pointer_v1_interface, - &confined_pointer_interface)) { - return POINTER_CONSTRAINT_TYPE_CONFINE; - } - - abort(); - return 0; -} - -static void -pointer_constraint_notify_activated(struct weston_pointer_constraint *constraint) -{ - struct wl_resource *resource = constraint->resource; - - switch (pointer_constraint_get_type(constraint)) { - case POINTER_CONSTRAINT_TYPE_LOCK: - zwp_locked_pointer_v1_send_locked(resource); - break; - case POINTER_CONSTRAINT_TYPE_CONFINE: - zwp_confined_pointer_v1_send_confined(resource); - break; - } -} - -static void -pointer_constraint_notify_deactivated(struct weston_pointer_constraint *constraint) -{ - struct wl_resource *resource = constraint->resource; - - switch (pointer_constraint_get_type(constraint)) { - case POINTER_CONSTRAINT_TYPE_LOCK: - zwp_locked_pointer_v1_send_unlocked(resource); - break; - case POINTER_CONSTRAINT_TYPE_CONFINE: - zwp_confined_pointer_v1_send_unconfined(resource); - break; - } -} - -static struct weston_pointer_constraint * -get_pointer_constraint_for_pointer(struct weston_surface *surface, - struct weston_pointer *pointer) -{ - struct weston_pointer_constraint *constraint; - - wl_list_for_each(constraint, &surface->pointer_constraints, link) { - if (constraint->pointer == pointer) - return constraint; - } - - return NULL; -} - -/** Get a seat's touch pointer - * - * \param seat The seat to query - * \return The seat's touch pointer, or NULL if no touch device is present - * - * The touch pointer for a seat isn't freed when all touch devices are removed, - * so it should only be used when the seat's touch_device_count is greater - * than zero. This function does that test and only returns a pointer - * when a touch device is present. - */ -WL_EXPORT struct weston_touch * -weston_seat_get_touch(struct weston_seat *seat) -{ - if (!seat) - return NULL; - - if (seat->touch_device_count) - return seat->touch_state; - - return NULL; -} - -/** Sets the keyboard focus to the given surface - * - * \param surface the surface to focus on - * \param seat The seat to query - */ -WL_EXPORT void -weston_seat_set_keyboard_focus(struct weston_seat *seat, - struct weston_surface *surface) -{ - struct weston_compositor *compositor = seat->compositor; - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct weston_surface_activation_data activation_data; - - if (keyboard && keyboard->focus != surface) { - weston_keyboard_set_focus(keyboard, surface); - wl_data_device_set_keyboard_focus(seat); - } - - inc_activate_serial(compositor); - - activation_data = (struct weston_surface_activation_data) { - .surface = surface, - .seat = seat, - }; - wl_signal_emit(&compositor->activate_signal, &activation_data); -} - -static void -enable_pointer_constraint(struct weston_pointer_constraint *constraint, - struct weston_view *view) -{ - assert(constraint->view == NULL); - constraint->view = view; - pointer_constraint_notify_activated(constraint); - weston_pointer_start_grab(constraint->pointer, &constraint->grab); - wl_list_remove(&constraint->surface_destroy_listener.link); - wl_list_init(&constraint->surface_destroy_listener.link); -} - -static bool -is_pointer_constraint_enabled(struct weston_pointer_constraint *constraint) -{ - return constraint->view != NULL; -} - -static void -weston_pointer_constraint_disable(struct weston_pointer_constraint *constraint) -{ - constraint->view = NULL; - pointer_constraint_notify_deactivated(constraint); - weston_pointer_end_grab(constraint->grab.pointer); -} - -void -weston_pointer_constraint_destroy(struct weston_pointer_constraint *constraint) -{ - if (is_pointer_constraint_enabled(constraint)) - weston_pointer_constraint_disable(constraint); - - wl_list_remove(&constraint->pointer_destroy_listener.link); - wl_list_remove(&constraint->surface_destroy_listener.link); - wl_list_remove(&constraint->surface_commit_listener.link); - wl_list_remove(&constraint->surface_activate_listener.link); - - wl_resource_set_user_data(constraint->resource, NULL); - pixman_region32_fini(&constraint->region); - wl_list_remove(&constraint->link); - free(constraint); -} - -static void -disable_pointer_constraint(struct weston_pointer_constraint *constraint) -{ - switch (constraint->lifetime) { - case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT: - weston_pointer_constraint_destroy(constraint); - break; - case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT: - weston_pointer_constraint_disable(constraint); - break; - } -} - -static bool -is_within_constraint_region(struct weston_pointer_constraint *constraint, - wl_fixed_t sx, wl_fixed_t sy) -{ - struct weston_surface *surface = constraint->surface; - pixman_region32_t constraint_region; - bool result; - - pixman_region32_init(&constraint_region); - pixman_region32_intersect(&constraint_region, - &surface->input, - &constraint->region); - result = pixman_region32_contains_point(&constraint_region, - wl_fixed_to_int(sx), - wl_fixed_to_int(sy), - NULL); - pixman_region32_fini(&constraint_region); - - return result; -} - -static void -maybe_enable_pointer_constraint(struct weston_pointer_constraint *constraint) -{ - struct weston_surface *surface = constraint->surface; - struct weston_view *vit; - struct weston_view *view = NULL; - struct weston_pointer *pointer = constraint->pointer; - struct weston_keyboard *keyboard; - struct weston_seat *seat = pointer->seat; - int32_t x, y; - - /* Postpone if no view of the surface was most recently clicked. */ - wl_list_for_each(vit, &surface->views, surface_link) { - if (vit->click_to_activate_serial == - surface->compositor->activate_serial) { - view = vit; - } - } - if (view == NULL) - return; - - /* Postpone if surface doesn't have keyboard focus. */ - keyboard = weston_seat_get_keyboard(seat); - if (!keyboard || keyboard->focus != surface) - return; - - /* Postpone constraint if the pointer is not within the - * constraint region. - */ - weston_view_from_global(view, - wl_fixed_to_int(pointer->x), - wl_fixed_to_int(pointer->y), - &x, &y); - if (!is_within_constraint_region(constraint, - wl_fixed_from_int(x), - wl_fixed_from_int(y))) - return; - - enable_pointer_constraint(constraint, view); -} - -static void -locked_pointer_grab_pointer_focus(struct weston_pointer_grab *grab) -{ -} - -static void -locked_pointer_grab_pointer_motion(struct weston_pointer_grab *grab, - const struct timespec *time, - struct weston_pointer_motion_event *event) -{ - pointer_send_relative_motion(grab->pointer, time, event); -} - -static void -locked_pointer_grab_pointer_button(struct weston_pointer_grab *grab, - const struct timespec *time, - uint32_t button, - uint32_t state_w) -{ - weston_pointer_send_button(grab->pointer, time, button, state_w); -} - -static void -locked_pointer_grab_pointer_axis(struct weston_pointer_grab *grab, - const struct timespec *time, - struct weston_pointer_axis_event *event) -{ - weston_pointer_send_axis(grab->pointer, time, event); -} - -static void -locked_pointer_grab_pointer_axis_source(struct weston_pointer_grab *grab, - uint32_t source) -{ - weston_pointer_send_axis_source(grab->pointer, source); -} - -static void -locked_pointer_grab_pointer_frame(struct weston_pointer_grab *grab) -{ - weston_pointer_send_frame(grab->pointer); -} - -static void -locked_pointer_grab_pointer_cancel(struct weston_pointer_grab *grab) -{ - struct weston_pointer_constraint *constraint = - container_of(grab, struct weston_pointer_constraint, grab); - - disable_pointer_constraint(constraint); -} - -static const struct weston_pointer_grab_interface - locked_pointer_grab_interface = { - locked_pointer_grab_pointer_focus, - locked_pointer_grab_pointer_motion, - locked_pointer_grab_pointer_button, - locked_pointer_grab_pointer_axis, - locked_pointer_grab_pointer_axis_source, - locked_pointer_grab_pointer_frame, - locked_pointer_grab_pointer_cancel, -}; - -static void -pointer_constraint_constrain_resource_destroyed(struct wl_resource *resource) -{ - struct weston_pointer_constraint *constraint = - wl_resource_get_user_data(resource); - - if (!constraint) - return; - - weston_pointer_constraint_destroy(constraint); -} - -static void -pointer_constraint_surface_activate(struct wl_listener *listener, void *data) -{ - struct weston_surface_activation_data *activation = data; - struct weston_pointer *pointer; - struct weston_surface *focus = activation->surface; - struct weston_pointer_constraint *constraint = - container_of(listener, struct weston_pointer_constraint, - surface_activate_listener); - bool is_constraint_surface; - - pointer = weston_seat_get_pointer(activation->seat); - if (!pointer) - return; - - is_constraint_surface = - get_pointer_constraint_for_pointer(focus, pointer) == constraint; - - if (is_constraint_surface && - !is_pointer_constraint_enabled(constraint)) - maybe_enable_pointer_constraint(constraint); - else if (!is_constraint_surface && - is_pointer_constraint_enabled(constraint)) - disable_pointer_constraint(constraint); -} - -static void -pointer_constraint_pointer_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_pointer_constraint *constraint = - container_of(listener, struct weston_pointer_constraint, - pointer_destroy_listener); - - weston_pointer_constraint_destroy(constraint); -} - -static void -pointer_constraint_surface_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_pointer_constraint *constraint = - container_of(listener, struct weston_pointer_constraint, - surface_destroy_listener); - - weston_pointer_constraint_destroy(constraint); -} - -static void -pointer_constraint_surface_committed(struct wl_listener *listener, void *data) -{ - struct weston_pointer_constraint *constraint = - container_of(listener, struct weston_pointer_constraint, - surface_commit_listener); - - if (constraint->region_is_pending) { - constraint->region_is_pending = false; - pixman_region32_copy(&constraint->region, - &constraint->region_pending); - pixman_region32_fini(&constraint->region_pending); - pixman_region32_init(&constraint->region_pending); - } - - if (constraint->hint_is_pending) { - constraint->hint_is_pending = false; - - constraint->hint_is_pending = true; - constraint->hint_x = constraint->hint_x_pending; - constraint->hint_y = constraint->hint_y_pending; - } - - if (pointer_constraint_get_type(constraint) == - POINTER_CONSTRAINT_TYPE_CONFINE && - is_pointer_constraint_enabled(constraint)) - maybe_warp_confined_pointer(constraint); -} - -static struct weston_pointer_constraint * -weston_pointer_constraint_create(struct weston_surface *surface, - struct weston_pointer *pointer, - struct weston_region *region, - enum zwp_pointer_constraints_v1_lifetime lifetime, - struct wl_resource *cr, - const struct weston_pointer_grab_interface *grab_interface) -{ - struct weston_pointer_constraint *constraint; - - constraint = zalloc(sizeof *constraint); - if (!constraint) - return NULL; - - constraint->lifetime = lifetime; - pixman_region32_init(&constraint->region); - pixman_region32_init(&constraint->region_pending); - wl_list_insert(&surface->pointer_constraints, &constraint->link); - constraint->surface = surface; - constraint->pointer = pointer; - constraint->resource = cr; - constraint->grab.interface = grab_interface; - if (region) { - pixman_region32_copy(&constraint->region, - ®ion->region); - } else { - pixman_region32_fini(&constraint->region); - region_init_infinite(&constraint->region); - } - - constraint->surface_activate_listener.notify = - pointer_constraint_surface_activate; - constraint->surface_destroy_listener.notify = - pointer_constraint_surface_destroyed; - constraint->surface_commit_listener.notify = - pointer_constraint_surface_committed; - constraint->pointer_destroy_listener.notify = - pointer_constraint_pointer_destroyed; - - wl_signal_add(&surface->compositor->activate_signal, - &constraint->surface_activate_listener); - wl_signal_add(&pointer->destroy_signal, - &constraint->pointer_destroy_listener); - wl_signal_add(&surface->destroy_signal, - &constraint->surface_destroy_listener); - wl_signal_add(&surface->commit_signal, - &constraint->surface_commit_listener); - - return constraint; -} - -static void -init_pointer_constraint(struct wl_resource *pointer_constraints_resource, - uint32_t id, - struct weston_surface *surface, - struct weston_pointer *pointer, - struct weston_region *region, - enum zwp_pointer_constraints_v1_lifetime lifetime, - const struct wl_interface *interface, - const void *implementation, - const struct weston_pointer_grab_interface *grab_interface) -{ - struct wl_client *client = - wl_resource_get_client(pointer_constraints_resource); - struct wl_resource *cr; - struct weston_pointer_constraint *constraint; - - if (pointer && get_pointer_constraint_for_pointer(surface, pointer)) { - wl_resource_post_error(pointer_constraints_resource, - ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED, - "the pointer has a lock/confine request on this surface"); - return; - } - - cr = wl_resource_create(client, interface, - wl_resource_get_version(pointer_constraints_resource), - id); - if (cr == NULL) { - wl_client_post_no_memory(client); - return; - } - - if (pointer) { - constraint = weston_pointer_constraint_create(surface, pointer, - region, lifetime, - cr, grab_interface); - if (constraint == NULL) { - wl_client_post_no_memory(client); - return; - } - } else { - constraint = NULL; - } - - wl_resource_set_implementation(cr, implementation, constraint, - pointer_constraint_constrain_resource_destroyed); - - if (constraint) - maybe_enable_pointer_constraint(constraint); -} - -static void -pointer_constraints_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -locked_pointer_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - struct weston_pointer_constraint *constraint = - wl_resource_get_user_data(resource); - wl_fixed_t x, y; - - if (constraint && constraint->view && constraint->hint_is_pending && - is_within_constraint_region(constraint, - constraint->hint_x, - constraint->hint_y)) { - weston_view_to_global_fixed(constraint->view, - constraint->hint_x, - constraint->hint_y, - &x, &y); - weston_pointer_move_to(constraint->pointer, x, y); - } - wl_resource_destroy(resource); -} - -static void -locked_pointer_set_cursor_position_hint(struct wl_client *client, - struct wl_resource *resource, - wl_fixed_t surface_x, - wl_fixed_t surface_y) -{ - struct weston_pointer_constraint *constraint = - wl_resource_get_user_data(resource); - - /* Ignore a set cursor hint that was sent after the lock was cancelled. - */ - if (!constraint || - !constraint->resource || - constraint->resource != resource) - return; - - constraint->hint_is_pending = true; - constraint->hint_x_pending = surface_x; - constraint->hint_y_pending = surface_y; -} - -static void -locked_pointer_set_region(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *region_resource) -{ - struct weston_pointer_constraint *constraint = - wl_resource_get_user_data(resource); - struct weston_region *region = region_resource ? - wl_resource_get_user_data(region_resource) : NULL; - - if (!constraint) - return; - - if (region) { - pixman_region32_copy(&constraint->region_pending, - ®ion->region); - } else { - pixman_region32_fini(&constraint->region_pending); - region_init_infinite(&constraint->region_pending); - } - constraint->region_is_pending = true; -} - - -static const struct zwp_locked_pointer_v1_interface locked_pointer_interface = { - locked_pointer_destroy, - locked_pointer_set_cursor_position_hint, - locked_pointer_set_region, -}; - -static void -pointer_constraints_lock_pointer(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, - struct wl_resource *surface_resource, - struct wl_resource *pointer_resource, - struct wl_resource *region_resource, - uint32_t lifetime) -{ - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - struct weston_pointer *pointer = wl_resource_get_user_data(pointer_resource); - struct weston_region *region = region_resource ? - wl_resource_get_user_data(region_resource) : NULL; - - init_pointer_constraint(resource, id, surface, pointer, region, lifetime, - &zwp_locked_pointer_v1_interface, - &locked_pointer_interface, - &locked_pointer_grab_interface); -} - -static void -confined_pointer_grab_pointer_focus(struct weston_pointer_grab *grab) -{ -} - -static double -vec2d_cross_product(struct vec2d a, struct vec2d b) -{ - return a.x * b.y - a.y * b.x; -} - -static struct vec2d -vec2d_add(struct vec2d a, struct vec2d b) -{ - return (struct vec2d) { - .x = a.x + b.x, - .y = a.y + b.y, - }; -} - -static struct vec2d -vec2d_subtract(struct vec2d a, struct vec2d b) -{ - return (struct vec2d) { - .x = a.x - b.x, - .y = a.y - b.y, - }; -} - -static struct vec2d -vec2d_multiply_constant(double c, struct vec2d a) -{ - return (struct vec2d) { - .x = c * a.x, - .y = c * a.y, - }; -} - -static bool -lines_intersect(struct line *line1, struct line *line2, - struct vec2d *intersection) -{ - struct vec2d p = line1->a; - struct vec2d r = vec2d_subtract(line1->b, line1->a); - struct vec2d q = line2->a; - struct vec2d s = vec2d_subtract(line2->b, line2->a); - double rxs; - double sxr; - double t; - double u; - - /* - * The line (p, r) and (q, s) intersects where - * - * p + t r = q + u s - * - * Calculate t: - * - * (p + t r) × s = (q + u s) × s - * p × s + t (r × s) = q × s + u (s × s) - * p × s + t (r × s) = q × s - * t (r × s) = q × s - p × s - * t (r × s) = (q - p) × s - * t = ((q - p) × s) / (r × s) - * - * Using the same method, for u we get: - * - * u = ((p - q) × r) / (s × r) - */ - - rxs = vec2d_cross_product(r, s); - sxr = vec2d_cross_product(s, r); - - /* If r × s = 0 then the lines are either parallel or collinear. */ - if (fabs(rxs) < DBL_MIN) - return false; - - t = vec2d_cross_product(vec2d_subtract(q, p), s) / rxs; - u = vec2d_cross_product(vec2d_subtract(p, q), r) / sxr; - - /* The lines only intersect if 0 ≤ t ≤ 1 and 0 ≤ u ≤ 1. */ - if (t < 0.0 || t > 1.0 || u < 0.0 || u > 1.0) - return false; - - *intersection = vec2d_add(p, vec2d_multiply_constant(t, r)); - return true; -} - -static struct border * -add_border(struct wl_array *array, - double x1, double y1, - double x2, double y2, - enum motion_direction blocking_dir) -{ - struct border *border = wl_array_add(array, sizeof *border); - - *border = (struct border) { - .line = (struct line) { - .a = (struct vec2d) { - .x = x1, - .y = y1, - }, - .b = (struct vec2d) { - .x = x2, - .y = y2, - }, - }, - .blocking_dir = blocking_dir, - }; - - return border; -} - -static int -compare_lines_x(const void *a, const void *b) -{ - const struct border *border_a = a; - const struct border *border_b = b; - - - if (border_a->line.a.x == border_b->line.a.x) - return border_a->line.b.x < border_b->line.b.x; - else - return border_a->line.a.x > border_b->line.a.x; -} - -static void -add_non_overlapping_edges(pixman_box32_t *boxes, - int band_above_start, - int band_below_start, - int band_below_end, - struct wl_array *borders) -{ - int i; - struct wl_array band_merge; - struct border *border; - struct border *prev_border; - struct border *new_border; - - wl_array_init(&band_merge); - - /* Add bottom band of previous row, and top band of current row, and - * sort them so lower left x coordinate comes first. If there are two - * borders with the same left x coordinate, the wider one comes first. - */ - for (i = band_above_start; i < band_below_start; i++) { - pixman_box32_t *box = &boxes[i]; - add_border(&band_merge, box->x1, box->y2, box->x2, box->y2, - MOTION_DIRECTION_POSITIVE_Y); - } - for (i = band_below_start; i < band_below_end; i++) { - pixman_box32_t *box= &boxes[i]; - add_border(&band_merge, box->x1, box->y1, box->x2, box->y1, - MOTION_DIRECTION_NEGATIVE_Y); - } - qsort(band_merge.data, - band_merge.size / sizeof *border, - sizeof *border, - compare_lines_x); - - /* Combine the two combined bands so that any overlapping border is - * eliminated. */ - prev_border = NULL; - wl_array_for_each(border, &band_merge) { - assert(border->line.a.y == border->line.b.y); - assert(!prev_border || - prev_border->line.a.y == border->line.a.y); - assert(!prev_border || - (prev_border->line.a.x != border->line.a.x || - prev_border->line.b.x != border->line.b.x)); - assert(!prev_border || - prev_border->line.a.x <= border->line.a.x); - - if (prev_border && - prev_border->line.a.x == border->line.a.x) { - /* - * ------------ + - * ------- = - * [ ]----- - */ - prev_border->line.a.x = border->line.b.x; - } else if (prev_border && - prev_border->line.b.x == border->line.b.x) { - /* - * ------------ + - * ------ = - * ------[ ] - */ - prev_border->line.b.x = border->line.a.x; - } else if (prev_border && - prev_border->line.b.x == border->line.a.x) { - /* - * -------- + - * ------ = - * -------------- - */ - prev_border->line.b.x = border->line.b.x; - } else if (prev_border && - prev_border->line.b.x >= border->line.a.x) { - /* - * --------------- + - * ------ = - * -----[ ]---- - */ - new_border = add_border(borders, - border->line.b.x, - border->line.b.y, - prev_border->line.b.x, - prev_border->line.b.y, - prev_border->blocking_dir); - prev_border->line.b.x = border->line.a.x; - prev_border = new_border; - } else { - assert(!prev_border || - prev_border->line.b.x < border->line.a.x); - /* - * First border or non-overlapping. - * - * ----- + - * ----- = - * ----- ----- - */ - new_border = wl_array_add(borders, sizeof *border); - *new_border = *border; - prev_border = new_border; - } - } - - wl_array_release(&band_merge); -} - -static void -add_band_bottom_edges(pixman_box32_t *boxes, - int band_start, - int band_end, - struct wl_array *borders) -{ - int i; - - for (i = band_start; i < band_end; i++) { - add_border(borders, - boxes[i].x1, boxes[i].y2, - boxes[i].x2, boxes[i].y2, - MOTION_DIRECTION_POSITIVE_Y); - } -} - -static void -region_to_outline(pixman_region32_t *region, struct wl_array *borders) -{ - pixman_box32_t *boxes; - int num_boxes; - int i; - int top_most, bottom_most; - int current_roof; - int prev_top; - int band_start, prev_band_start; - - /* - * Remove any overlapping lines from the set of rectangles. Note that - * pixman regions are grouped as rows of rectangles, where rectangles - * in one row never touch or overlap and are all of the same height. - * - * -------- --- -------- --- - * | | | | | | | | - * ----------====---- --- ----------- ----- --- - * | | => | | - * ----==========--------- ----- ---------- - * | | | | - * ------------------- ------------------- - * - */ - - boxes = pixman_region32_rectangles(region, &num_boxes); - prev_top = 0; - top_most = boxes[0].y1; - current_roof = top_most; - bottom_most = boxes[num_boxes - 1].y2; - band_start = 0; - prev_band_start = 0; - for (i = 0; i < num_boxes; i++) { - /* Detect if there is a vertical empty space, and add the lower - * level of the previous band if so was the case. */ - if (i > 0 && - boxes[i].y1 != prev_top && - boxes[i].y1 != boxes[i - 1].y2) { - current_roof = boxes[i].y1; - add_band_bottom_edges(boxes, - band_start, - i, - borders); - } - - /* Special case adding the last band, since it won't be handled - * by the band change detection below. */ - if (boxes[i].y1 != current_roof && i == num_boxes - 1) { - if (boxes[i].y1 != prev_top) { - /* The last band is a single box, so we don't - * have a prev_band_start to tell us when the - * previous band started. */ - add_non_overlapping_edges(boxes, - band_start, - i, - i + 1, - borders); - } else { - add_non_overlapping_edges(boxes, - prev_band_start, - band_start, - i + 1, - borders); - } - } - - /* Detect when passing a band and combine the top border of the - * just passed band with the bottom band of the previous band. - */ - if (boxes[i].y1 != top_most && boxes[i].y1 != prev_top) { - /* Combine the two passed bands. */ - if (prev_top != current_roof) { - add_non_overlapping_edges(boxes, - prev_band_start, - band_start, - i, - borders); - } - - prev_band_start = band_start; - band_start = i; - } - - /* Add the top border if the box is part of the current roof. */ - if (boxes[i].y1 == current_roof) { - add_border(borders, - boxes[i].x1, boxes[i].y1, - boxes[i].x2, boxes[i].y1, - MOTION_DIRECTION_NEGATIVE_Y); - } - - /* Add the bottom border of the last band. */ - if (boxes[i].y2 == bottom_most) { - add_border(borders, - boxes[i].x1, boxes[i].y2, - boxes[i].x2, boxes[i].y2, - MOTION_DIRECTION_POSITIVE_Y); - } - - /* Always add the left border. */ - add_border(borders, - boxes[i].x1, boxes[i].y1, - boxes[i].x1, boxes[i].y2, - MOTION_DIRECTION_NEGATIVE_X); - - /* Always add the right border. */ - add_border(borders, - boxes[i].x2, boxes[i].y1, - boxes[i].x2, boxes[i].y2, - MOTION_DIRECTION_POSITIVE_X); - - prev_top = boxes[i].y1; - } -} - -static bool -is_border_horizontal (struct border *border) -{ - return border->line.a.y == border->line.b.y; -} - -static bool -is_border_blocking_directions(struct border *border, - uint32_t directions) -{ - /* Don't block parallel motions. */ - if (is_border_horizontal(border)) { - if ((directions & (MOTION_DIRECTION_POSITIVE_Y | - MOTION_DIRECTION_NEGATIVE_Y)) == 0) - return false; - } else { - if ((directions & (MOTION_DIRECTION_POSITIVE_X | - MOTION_DIRECTION_NEGATIVE_X)) == 0) - return false; - } - - return (~border->blocking_dir & directions) != directions; -} - -static struct border * -get_closest_border(struct wl_array *borders, - struct line *motion, - uint32_t directions) -{ - struct border *border; - struct vec2d intersection; - struct vec2d delta; - double distance_2; - struct border *closest_border = NULL; - double closest_distance_2 = DBL_MAX; - - wl_array_for_each(border, borders) { - if (!is_border_blocking_directions(border, directions)) - continue; - - if (!lines_intersect(&border->line, motion, &intersection)) - continue; - - delta = vec2d_subtract(intersection, motion->a); - distance_2 = delta.x*delta.x + delta.y*delta.y; - if (distance_2 < closest_distance_2) { - closest_border = border; - closest_distance_2 = distance_2; - } - } - - return closest_border; -} - -static void -clamp_to_border(struct border *border, - struct line *motion, - uint32_t *motion_dir) -{ - /* - * When clamping either rightward or downward motions, the motion needs - * to be clamped so that the destination coordinate does not end up on - * the border (see weston_pointer_clamp_event_to_region). Do this by - * clamping such motions to the border minus the smallest possible - * wl_fixed_t value. - */ - if (is_border_horizontal(border)) { - if (*motion_dir & MOTION_DIRECTION_POSITIVE_Y) - motion->b.y = border->line.a.y - wl_fixed_to_double(1); - else - motion->b.y = border->line.a.y; - *motion_dir &= ~(MOTION_DIRECTION_POSITIVE_Y | - MOTION_DIRECTION_NEGATIVE_Y); - } else { - if (*motion_dir & MOTION_DIRECTION_POSITIVE_X) - motion->b.x = border->line.a.x - wl_fixed_to_double(1); - else - motion->b.x = border->line.a.x; - *motion_dir &= ~(MOTION_DIRECTION_POSITIVE_X | - MOTION_DIRECTION_NEGATIVE_X); - } -} - -static uint32_t -get_motion_directions(struct line *motion) -{ - uint32_t directions = 0; - - if (motion->a.x < motion->b.x) - directions |= MOTION_DIRECTION_POSITIVE_X; - else if (motion->a.x > motion->b.x) - directions |= MOTION_DIRECTION_NEGATIVE_X; - if (motion->a.y < motion->b.y) - directions |= MOTION_DIRECTION_POSITIVE_Y; - else if (motion->a.y > motion->b.y) - directions |= MOTION_DIRECTION_NEGATIVE_Y; - - return directions; -} - -static void -weston_pointer_clamp_event_to_region(struct weston_pointer *pointer, - struct weston_pointer_motion_event *event, - pixman_region32_t *region, - wl_fixed_t *clamped_x, - wl_fixed_t *clamped_y) -{ - wl_fixed_t x, y; - wl_fixed_t sx, sy; - wl_fixed_t old_sx = pointer->sx; - wl_fixed_t old_sy = pointer->sy; - struct wl_array borders; - struct line motion; - struct border *closest_border; - float new_x_f, new_y_f; - uint32_t directions; - - weston_pointer_motion_to_abs(pointer, event, &x, &y); - weston_view_from_global_fixed(pointer->focus, x, y, &sx, &sy); - - wl_array_init(&borders); - - /* - * Generate borders given the confine region we are to use. The borders - * are defined to be the outer region of the allowed area. This means - * top/left borders are "within" the allowed area, while bottom/right - * borders are outside. This needs to be considered when clamping - * confined motion vectors. - */ - region_to_outline(region, &borders); - - motion = (struct line) { - .a = (struct vec2d) { - .x = wl_fixed_to_double(old_sx), - .y = wl_fixed_to_double(old_sy), - }, - .b = (struct vec2d) { - .x = wl_fixed_to_double(sx), - .y = wl_fixed_to_double(sy), - }, - }; - directions = get_motion_directions(&motion); - - while (directions) { - closest_border = get_closest_border(&borders, - &motion, - directions); - if (closest_border) - clamp_to_border(closest_border, &motion, &directions); - else - break; - } - - weston_view_to_global_float(pointer->focus, - (float) motion.b.x, (float) motion.b.y, - &new_x_f, &new_y_f); - *clamped_x = wl_fixed_from_double(new_x_f); - *clamped_y = wl_fixed_from_double(new_y_f); - - wl_array_release(&borders); -} - -static double -point_to_border_distance_2(struct border *border, double x, double y) -{ - double orig_x, orig_y; - double dx, dy; - - if (is_border_horizontal(border)) { - if (x < border->line.a.x) - orig_x = border->line.a.x; - else if (x > border->line.b.x) - orig_x = border->line.b.x; - else - orig_x = x; - orig_y = border->line.a.y; - } else { - if (y < border->line.a.y) - orig_y = border->line.a.y; - else if (y > border->line.b.y) - orig_y = border->line.b.y; - else - orig_y = y; - orig_x = border->line.a.x; - } - - - dx = fabs(orig_x - x); - dy = fabs(orig_y - y); - return dx*dx + dy*dy; -} - -static void -warp_to_behind_border(struct border *border, wl_fixed_t *sx, wl_fixed_t *sy) -{ - switch (border->blocking_dir) { - case MOTION_DIRECTION_POSITIVE_X: - case MOTION_DIRECTION_NEGATIVE_X: - if (border->blocking_dir == MOTION_DIRECTION_POSITIVE_X) - *sx = wl_fixed_from_double(border->line.a.x) - 1; - else - *sx = wl_fixed_from_double(border->line.a.x) + 1; - if (*sy < wl_fixed_from_double(border->line.a.y)) - *sy = wl_fixed_from_double(border->line.a.y) + 1; - else if (*sy > wl_fixed_from_double(border->line.b.y)) - *sy = wl_fixed_from_double(border->line.b.y) - 1; - break; - case MOTION_DIRECTION_POSITIVE_Y: - case MOTION_DIRECTION_NEGATIVE_Y: - if (border->blocking_dir == MOTION_DIRECTION_POSITIVE_Y) - *sy = wl_fixed_from_double(border->line.a.y) - 1; - else - *sy = wl_fixed_from_double(border->line.a.y) + 1; - if (*sx < wl_fixed_from_double(border->line.a.x)) - *sx = wl_fixed_from_double(border->line.a.x) + 1; - else if (*sx > wl_fixed_from_double(border->line.b.x)) - *sx = wl_fixed_from_double(border->line.b.x) - 1; - break; - } -} - -static void -maybe_warp_confined_pointer(struct weston_pointer_constraint *constraint) -{ - wl_fixed_t x; - wl_fixed_t y; - wl_fixed_t sx; - wl_fixed_t sy; - - weston_view_from_global_fixed(constraint->view, - constraint->pointer->x, - constraint->pointer->y, - &sx, - &sy); - - if (!is_within_constraint_region(constraint, sx, sy)) { - double xf = wl_fixed_to_double(sx); - double yf = wl_fixed_to_double(sy); - pixman_region32_t confine_region; - struct wl_array borders; - struct border *border; - double closest_distance_2 = DBL_MAX; - struct border *closest_border = NULL; - - wl_array_init(&borders); - - pixman_region32_init(&confine_region); - pixman_region32_intersect(&confine_region, - &constraint->view->surface->input, - &constraint->region); - region_to_outline(&confine_region, &borders); - pixman_region32_fini(&confine_region); - - wl_array_for_each(border, &borders) { - double distance_2; - - distance_2 = point_to_border_distance_2(border, xf, yf); - if (distance_2 < closest_distance_2) { - closest_border = border; - closest_distance_2 = distance_2; - } - } - assert(closest_border); - - warp_to_behind_border(closest_border, &sx, &sy); - - wl_array_release(&borders); - - weston_view_to_global_fixed(constraint->view, sx, sy, &x, &y); - weston_pointer_move_to(constraint->pointer, x, y); - } -} - -static void -confined_pointer_grab_pointer_motion(struct weston_pointer_grab *grab, - const struct timespec *time, - struct weston_pointer_motion_event *event) -{ - struct weston_pointer_constraint *constraint = - container_of(grab, struct weston_pointer_constraint, grab); - struct weston_pointer *pointer = grab->pointer; - struct weston_surface *surface; - wl_fixed_t x, y; - wl_fixed_t old_sx = pointer->sx; - wl_fixed_t old_sy = pointer->sy; - pixman_region32_t confine_region; - - assert(pointer->focus); - assert(pointer->focus->surface == constraint->surface); - - surface = pointer->focus->surface; - - pixman_region32_init(&confine_region); - pixman_region32_intersect(&confine_region, - &surface->input, - &constraint->region); - weston_pointer_clamp_event_to_region(pointer, event, - &confine_region, &x, &y); - weston_pointer_move_to(pointer, x, y); - pixman_region32_fini(&confine_region); - - weston_view_from_global_fixed(pointer->focus, x, y, - &pointer->sx, &pointer->sy); - - if (old_sx != pointer->sx || old_sy != pointer->sy) { - pointer_send_motion(pointer, time, - pointer->sx, pointer->sy); - } - - pointer_send_relative_motion(pointer, time, event); -} - -static void -confined_pointer_grab_pointer_button(struct weston_pointer_grab *grab, - const struct timespec *time, - uint32_t button, - uint32_t state_w) -{ - weston_pointer_send_button(grab->pointer, time, button, state_w); -} - -static void -confined_pointer_grab_pointer_axis(struct weston_pointer_grab *grab, - const struct timespec *time, - struct weston_pointer_axis_event *event) -{ - weston_pointer_send_axis(grab->pointer, time, event); -} - -static void -confined_pointer_grab_pointer_axis_source(struct weston_pointer_grab *grab, - uint32_t source) -{ - weston_pointer_send_axis_source(grab->pointer, source); -} - -static void -confined_pointer_grab_pointer_frame(struct weston_pointer_grab *grab) -{ - weston_pointer_send_frame(grab->pointer); -} - -static void -confined_pointer_grab_pointer_cancel(struct weston_pointer_grab *grab) -{ - struct weston_pointer_constraint *constraint = - container_of(grab, struct weston_pointer_constraint, grab); - - disable_pointer_constraint(constraint); -} - -static const struct weston_pointer_grab_interface - confined_pointer_grab_interface = { - confined_pointer_grab_pointer_focus, - confined_pointer_grab_pointer_motion, - confined_pointer_grab_pointer_button, - confined_pointer_grab_pointer_axis, - confined_pointer_grab_pointer_axis_source, - confined_pointer_grab_pointer_frame, - confined_pointer_grab_pointer_cancel, -}; - -static void -confined_pointer_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -confined_pointer_set_region(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *region_resource) -{ - struct weston_pointer_constraint *constraint = - wl_resource_get_user_data(resource); - struct weston_region *region = region_resource ? - wl_resource_get_user_data(region_resource) : NULL; - - if (!constraint) - return; - - if (region) { - pixman_region32_copy(&constraint->region_pending, - ®ion->region); - } else { - pixman_region32_fini(&constraint->region_pending); - region_init_infinite(&constraint->region_pending); - } - constraint->region_is_pending = true; -} - -static const struct zwp_confined_pointer_v1_interface confined_pointer_interface = { - confined_pointer_destroy, - confined_pointer_set_region, -}; - -static void -pointer_constraints_confine_pointer(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, - struct wl_resource *surface_resource, - struct wl_resource *pointer_resource, - struct wl_resource *region_resource, - uint32_t lifetime) -{ - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - struct weston_pointer *pointer = wl_resource_get_user_data(pointer_resource); - struct weston_region *region = region_resource ? - wl_resource_get_user_data(region_resource) : NULL; - - init_pointer_constraint(resource, id, surface, pointer, region, lifetime, - &zwp_confined_pointer_v1_interface, - &confined_pointer_interface, - &confined_pointer_grab_interface); -} - -static const struct zwp_pointer_constraints_v1_interface pointer_constraints_interface = { - pointer_constraints_destroy, - pointer_constraints_lock_pointer, - pointer_constraints_confine_pointer, -}; - -static void -bind_pointer_constraints(struct wl_client *client, void *data, - uint32_t version, uint32_t id) -{ - struct wl_resource *resource; - - resource = wl_resource_create(client, - &zwp_pointer_constraints_v1_interface, - 1, id); - - wl_resource_set_implementation(resource, &pointer_constraints_interface, - NULL, NULL); -} - -static void -input_timestamps_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct zwp_input_timestamps_v1_interface - input_timestamps_interface = { - input_timestamps_destroy, -}; - -static void -input_timestamps_manager_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -input_timestamps_manager_get_keyboard_timestamps(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, - struct wl_resource *keyboard_resource) -{ - struct weston_keyboard *keyboard = - wl_resource_get_user_data(keyboard_resource); - struct wl_resource *input_ts; - - input_ts = wl_resource_create(client, - &zwp_input_timestamps_v1_interface, - 1, id); - if (!input_ts) { - wl_client_post_no_memory(client); - return; - } - - if (keyboard) { - wl_list_insert(&keyboard->timestamps_list, - wl_resource_get_link(input_ts)); - } else { - wl_list_init(wl_resource_get_link(input_ts)); - } - - wl_resource_set_implementation(input_ts, - &input_timestamps_interface, - keyboard_resource, - unbind_resource); -} - -static void -input_timestamps_manager_get_pointer_timestamps(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, - struct wl_resource *pointer_resource) -{ - struct weston_pointer *pointer = - wl_resource_get_user_data(pointer_resource); - struct wl_resource *input_ts; - - input_ts = wl_resource_create(client, - &zwp_input_timestamps_v1_interface, - 1, id); - if (!input_ts) { - wl_client_post_no_memory(client); - return; - } - - if (pointer) { - wl_list_insert(&pointer->timestamps_list, - wl_resource_get_link(input_ts)); - } else { - wl_list_init(wl_resource_get_link(input_ts)); - } - - wl_resource_set_implementation(input_ts, - &input_timestamps_interface, - pointer_resource, - unbind_resource); -} - -static void -input_timestamps_manager_get_touch_timestamps(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, - struct wl_resource *touch_resource) -{ - struct weston_touch *touch = wl_resource_get_user_data(touch_resource); - struct wl_resource *input_ts; - - input_ts = wl_resource_create(client, - &zwp_input_timestamps_v1_interface, - 1, id); - if (!input_ts) { - wl_client_post_no_memory(client); - return; - } - - if (touch) { - wl_list_insert(&touch->timestamps_list, - wl_resource_get_link(input_ts)); - } else { - wl_list_init(wl_resource_get_link(input_ts)); - } - - wl_resource_set_implementation(input_ts, - &input_timestamps_interface, - touch_resource, - unbind_resource); -} - -static const struct zwp_input_timestamps_manager_v1_interface - input_timestamps_manager_interface = { - input_timestamps_manager_destroy, - input_timestamps_manager_get_keyboard_timestamps, - input_timestamps_manager_get_pointer_timestamps, - input_timestamps_manager_get_touch_timestamps, -}; - -static void -bind_input_timestamps_manager(struct wl_client *client, void *data, - uint32_t version, uint32_t id) -{ - struct wl_resource *resource = - wl_resource_create(client, - &zwp_input_timestamps_manager_v1_interface, - 1, id); - - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, - &input_timestamps_manager_interface, - NULL, NULL); -} - -int -weston_input_init(struct weston_compositor *compositor) -{ - if (!wl_global_create(compositor->wl_display, - &zwp_relative_pointer_manager_v1_interface, 1, - compositor, bind_relative_pointer_manager)) - return -1; - - if (!wl_global_create(compositor->wl_display, - &zwp_pointer_constraints_v1_interface, 1, - NULL, bind_pointer_constraints)) - return -1; - - if (!wl_global_create(compositor->wl_display, - &zwp_input_timestamps_manager_v1_interface, 1, - NULL, bind_input_timestamps_manager)) - return -1; - - return 0; -} diff --git a/libweston/launcher-direct.c b/libweston/launcher-direct.c deleted file mode 100644 index 9fa329b6..00000000 --- a/libweston/launcher-direct.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright © 2012 Benjamin Franzke - * Copyright © 2013 Intel Corporation - * - * 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 <libweston/libweston.h> - -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> -#include <signal.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <linux/vt.h> -#include <linux/kd.h> -#include <linux/major.h> - -#include "launcher-impl.h" - -#define DRM_MAJOR 226 - -#ifndef KDSKBMUTE -#define KDSKBMUTE 0x4B51 -#endif - -/* major()/minor() */ -#ifdef MAJOR_IN_MKDEV -#include <sys/mkdev.h> -#endif -#ifdef MAJOR_IN_SYSMACROS -#include <sys/sysmacros.h> -#endif - -#ifdef BUILD_DRM_COMPOSITOR - -#include <xf86drm.h> - -static inline int -is_drm_master(int drm_fd) -{ - drm_magic_t magic; - - return drmGetMagic(drm_fd, &magic) == 0 && - drmAuthMagic(drm_fd, magic) == 0; -} - -#else - -static inline int -drmDropMaster(int drm_fd) -{ - return 0; -} - -static inline int -drmSetMaster(int drm_fd) -{ - return 0; -} - -static inline int -is_drm_master(int drm_fd) -{ - return 0; -} - -#endif - -struct launcher_direct { - struct weston_launcher base; - struct weston_compositor *compositor; - int kb_mode, tty, drm_fd; - struct wl_event_source *vt_source; -}; - -static int -vt_handler(int signal_number, void *data) -{ - struct launcher_direct *launcher = data; - struct weston_compositor *compositor = launcher->compositor; - - if (compositor->session_active) { - compositor->session_active = false; - wl_signal_emit(&compositor->session_signal, compositor); - drmDropMaster(launcher->drm_fd); - ioctl(launcher->tty, VT_RELDISP, 1); - } else { - ioctl(launcher->tty, VT_RELDISP, VT_ACKACQ); - drmSetMaster(launcher->drm_fd); - compositor->session_active = true; - wl_signal_emit(&compositor->session_signal, compositor); - } - - return 1; -} - -static int -setup_tty(struct launcher_direct *launcher, int tty) -{ - struct wl_event_loop *loop; - struct vt_mode mode = { 0 }; - struct stat buf; - char tty_device[32] ="<stdin>"; - int ret, kd_mode; - - if (tty == 0) { - launcher->tty = dup(tty); - if (launcher->tty == -1) { - weston_log("couldn't dup stdin: %s\n", - strerror(errno)); - return -1; - } - } else { - snprintf(tty_device, sizeof tty_device, "/dev/tty%d", tty); - launcher->tty = open(tty_device, O_RDWR | O_CLOEXEC); - if (launcher->tty == -1) { - weston_log("couldn't open tty %s: %s\n", tty_device, - strerror(errno)); - return -1; - } - } - - if (fstat(launcher->tty, &buf) == -1 || - major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0) { - weston_log("%s not a vt\n", tty_device); - weston_log("if running weston from ssh, " - "use --tty to specify a tty\n"); - goto err_close; - } - - ret = ioctl(launcher->tty, KDGETMODE, &kd_mode); - if (ret) { - weston_log("failed to get VT mode: %s\n", strerror(errno)); - return -1; - } - if (kd_mode != KD_TEXT) { - weston_log("%s is already in graphics mode, " - "is another display server running?\n", tty_device); - goto err_close; - } - - ioctl(launcher->tty, VT_ACTIVATE, minor(buf.st_rdev)); - ioctl(launcher->tty, VT_WAITACTIVE, minor(buf.st_rdev)); - - if (ioctl(launcher->tty, KDGKBMODE, &launcher->kb_mode)) { - weston_log("failed to read keyboard mode: %s\n", - strerror(errno)); - goto err_close; - } - - if (ioctl(launcher->tty, KDSKBMUTE, 1) && - ioctl(launcher->tty, KDSKBMODE, K_OFF)) { - weston_log("failed to set K_OFF keyboard mode: %s\n", - strerror(errno)); - goto err_close; - } - - ret = ioctl(launcher->tty, KDSETMODE, KD_GRAPHICS); - if (ret) { - weston_log("failed to set KD_GRAPHICS mode on tty: %s\n", - strerror(errno)); - goto err_close; - } - - /* - * SIGRTMIN is used as global VT-acquire+release signal. Note that - * SIGRT* must be tested on runtime, as their exact values are not - * known at compile-time. POSIX requires 32 of them to be available. - */ - if (SIGRTMIN > SIGRTMAX) { - weston_log("not enough RT signals available: %u-%u\n", - SIGRTMIN, SIGRTMAX); - ret = -EINVAL; - goto err_close; - } - - mode.mode = VT_PROCESS; - mode.relsig = SIGRTMIN; - mode.acqsig = SIGRTMIN; - if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) { - weston_log("failed to take control of vt handling\n"); - goto err_close; - } - - loop = wl_display_get_event_loop(launcher->compositor->wl_display); - launcher->vt_source = - wl_event_loop_add_signal(loop, SIGRTMIN, vt_handler, launcher); - if (!launcher->vt_source) - goto err_close; - - return 0; - - err_close: - close(launcher->tty); - return -1; -} - -static int -launcher_direct_open(struct weston_launcher *launcher_base, const char *path, int flags) -{ - struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base); - struct stat s; - int fd; - - fd = open(path, flags | O_CLOEXEC); - if (fd == -1) - return -1; - - if (fstat(fd, &s) == -1) { - close(fd); - return -1; - } - - if (major(s.st_rdev) == DRM_MAJOR) { - launcher->drm_fd = fd; - if (!is_drm_master(fd)) { - weston_log("drm fd not master\n"); - close(fd); - return -1; - } - } - - return fd; -} - -static void -launcher_direct_close(struct weston_launcher *launcher_base, int fd) -{ - close(fd); -} - -static void -launcher_direct_restore(struct weston_launcher *launcher_base) -{ - struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base); - struct vt_mode mode = { 0 }; - - if (ioctl(launcher->tty, KDSKBMUTE, 0) && - ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode)) - weston_log("failed to restore kb mode: %s\n", - strerror(errno)); - - if (ioctl(launcher->tty, KDSETMODE, KD_TEXT)) - weston_log("failed to set KD_TEXT mode on tty: %s\n", - strerror(errno)); - - /* We have to drop master before we switch the VT back in - * VT_AUTO, so we don't risk switching to a VT with another - * display server, that will then fail to set drm master. */ - drmDropMaster(launcher->drm_fd); - - mode.mode = VT_AUTO; - if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) - weston_log("could not reset vt handling\n"); -} - -static int -launcher_direct_activate_vt(struct weston_launcher *launcher_base, int vt) -{ - struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base); - return ioctl(launcher->tty, VT_ACTIVATE, vt); -} - -static int -launcher_direct_connect(struct weston_launcher **out, struct weston_compositor *compositor, - int tty, const char *seat_id, bool sync_drm) -{ - struct launcher_direct *launcher; - - if (geteuid() != 0) - return -EINVAL; - - launcher = zalloc(sizeof(*launcher)); - if (launcher == NULL) - return -ENOMEM; - - launcher->base.iface = &launcher_direct_iface; - launcher->compositor = compositor; - - if (setup_tty(launcher, tty) == -1) { - free(launcher); - return -1; - } - - * (struct launcher_direct **) out = launcher; - return 0; -} - -static void -launcher_direct_destroy(struct weston_launcher *launcher_base) -{ - struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base); - - launcher_direct_restore(&launcher->base); - wl_event_source_remove(launcher->vt_source); - - if (launcher->tty >= 0) - close(launcher->tty); - - free(launcher); -} - -static int -launcher_direct_get_vt(struct weston_launcher *base) -{ - struct launcher_direct *launcher = wl_container_of(base, launcher, base); - struct stat s; - if (fstat(launcher->tty, &s) < 0) - return -1; - - return minor(s.st_rdev); -} - -const struct launcher_interface launcher_direct_iface = { - .connect = launcher_direct_connect, - .destroy = launcher_direct_destroy, - .open = launcher_direct_open, - .close = launcher_direct_close, - .activate_vt = launcher_direct_activate_vt, - .get_vt = launcher_direct_get_vt, -}; diff --git a/libweston/launcher-impl.h b/libweston/launcher-impl.h deleted file mode 100644 index 4161caff..00000000 --- a/libweston/launcher-impl.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2015 Jasper St. Pierre - * - * 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 <libweston/libweston.h> - -struct weston_launcher; - -struct launcher_interface { - int (* connect) (struct weston_launcher **launcher_out, struct weston_compositor *compositor, - int tty, const char *seat_id, bool sync_drm); - void (* destroy) (struct weston_launcher *launcher); - int (* open) (struct weston_launcher *launcher, const char *path, int flags); - void (* close) (struct weston_launcher *launcher, int fd); - int (* activate_vt) (struct weston_launcher *launcher, int vt); - /* Get the number of the VT weston is running in */ - int (* get_vt) (struct weston_launcher *launcher); -}; - -struct weston_launcher { - const struct launcher_interface *iface; -}; - -extern const struct launcher_interface launcher_logind_iface; -extern const struct launcher_interface launcher_weston_launch_iface; -extern const struct launcher_interface launcher_direct_iface; diff --git a/libweston/launcher-logind.c b/libweston/launcher-logind.c deleted file mode 100644 index 9b3c52fa..00000000 --- a/libweston/launcher-logind.c +++ /dev/null @@ -1,856 +0,0 @@ -/* - * Copyright © 2013 David Herrmann - * - * 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 <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <systemd/sd-login.h> -#include <unistd.h> - -#include <libweston/libweston.h> -#include "backend.h" -#include "dbus.h" -#include "launcher-impl.h" - -#define DRM_MAJOR 226 - -/* major()/minor() */ -#ifdef MAJOR_IN_MKDEV -#include <sys/mkdev.h> -#endif -#ifdef MAJOR_IN_SYSMACROS -#include <sys/sysmacros.h> -#endif - -struct launcher_logind { - struct weston_launcher base; - struct weston_compositor *compositor; - bool sync_drm; - char *seat; - char *sid; - unsigned int vtnr; - int vt; - int kb_mode; - - DBusConnection *dbus; - struct wl_event_source *dbus_ctx; - char *spath; - DBusPendingCall *pending_active; -}; - -static int -launcher_logind_take_device(struct launcher_logind *wl, uint32_t major, - uint32_t minor, bool *paused_out) -{ - DBusMessage *m, *reply; - bool b; - int r, fd; - dbus_bool_t paused; - - m = dbus_message_new_method_call("org.freedesktop.login1", - wl->spath, - "org.freedesktop.login1.Session", - "TakeDevice"); - if (!m) - return -ENOMEM; - - b = dbus_message_append_args(m, - DBUS_TYPE_UINT32, &major, - DBUS_TYPE_UINT32, &minor, - DBUS_TYPE_INVALID); - if (!b) { - r = -ENOMEM; - goto err_unref; - } - - reply = dbus_connection_send_with_reply_and_block(wl->dbus, m, - -1, NULL); - if (!reply) { - r = -ENODEV; - goto err_unref; - } - - b = dbus_message_get_args(reply, NULL, - DBUS_TYPE_UNIX_FD, &fd, - DBUS_TYPE_BOOLEAN, &paused, - DBUS_TYPE_INVALID); - if (!b) { - r = -ENODEV; - goto err_reply; - } - - r = fd; - if (paused_out) - *paused_out = paused; - -err_reply: - dbus_message_unref(reply); -err_unref: - dbus_message_unref(m); - return r; -} - -static void -launcher_logind_release_device(struct launcher_logind *wl, uint32_t major, - uint32_t minor) -{ - DBusMessage *m; - bool b; - - m = dbus_message_new_method_call("org.freedesktop.login1", - wl->spath, - "org.freedesktop.login1.Session", - "ReleaseDevice"); - if (m) { - b = dbus_message_append_args(m, - DBUS_TYPE_UINT32, &major, - DBUS_TYPE_UINT32, &minor, - DBUS_TYPE_INVALID); - if (b) - dbus_connection_send(wl->dbus, m, NULL); - dbus_message_unref(m); - } -} - -static void -launcher_logind_pause_device_complete(struct launcher_logind *wl, uint32_t major, - uint32_t minor) -{ - DBusMessage *m; - bool b; - - m = dbus_message_new_method_call("org.freedesktop.login1", - wl->spath, - "org.freedesktop.login1.Session", - "PauseDeviceComplete"); - if (m) { - b = dbus_message_append_args(m, - DBUS_TYPE_UINT32, &major, - DBUS_TYPE_UINT32, &minor, - DBUS_TYPE_INVALID); - if (b) - dbus_connection_send(wl->dbus, m, NULL); - dbus_message_unref(m); - } -} - -static int -launcher_logind_open(struct weston_launcher *launcher, const char *path, int flags) -{ - struct launcher_logind *wl = wl_container_of(launcher, wl, base); - struct stat st; - int fl, r, fd; - - r = stat(path, &st); - if (r < 0) - return -1; - if (!S_ISCHR(st.st_mode)) { - errno = ENODEV; - return -1; - } - - fd = launcher_logind_take_device(wl, major(st.st_rdev), - minor(st.st_rdev), NULL); - if (fd < 0) - return fd; - - /* Compared to weston_launcher_open() we cannot specify the open-mode - * directly. Instead, logind passes us an fd with sane default modes. - * For DRM and evdev this means O_RDWR | O_CLOEXEC. If we want - * something else, we need to change it afterwards. We currently - * only support setting O_NONBLOCK. Changing access-modes is not - * possible so accept whatever logind passes us. */ - - fl = fcntl(fd, F_GETFL); - if (fl < 0) { - r = -errno; - goto err_close; - } - - if (flags & O_NONBLOCK) - fl |= O_NONBLOCK; - - r = fcntl(fd, F_SETFL, fl); - if (r < 0) { - r = -errno; - goto err_close; - } - return fd; - -err_close: - close(fd); - launcher_logind_release_device(wl, major(st.st_rdev), - minor(st.st_rdev)); - errno = -r; - return -1; -} - -static void -launcher_logind_close(struct weston_launcher *launcher, int fd) -{ - struct launcher_logind *wl = wl_container_of(launcher, wl, base); - struct stat st; - int r; - - r = fstat(fd, &st); - close(fd); - if (r < 0) { - weston_log("logind: cannot fstat fd: %s\n", strerror(errno)); - return; - } - - if (!S_ISCHR(st.st_mode)) { - weston_log("logind: invalid device passed\n"); - return; - } - - launcher_logind_release_device(wl, major(st.st_rdev), - minor(st.st_rdev)); -} - -static int -launcher_logind_activate_vt(struct weston_launcher *launcher, int vt) -{ - struct launcher_logind *wl = wl_container_of(launcher, wl, base); - DBusMessage *m; - bool b; - int r; - - m = dbus_message_new_method_call("org.freedesktop.login1", - "/org/freedesktop/login1/seat/self", - "org.freedesktop.login1.Seat", - "SwitchTo"); - if (!m) - return -ENOMEM; - - b = dbus_message_append_args(m, - DBUS_TYPE_UINT32, &vt, - DBUS_TYPE_INVALID); - if (!b) { - r = -ENOMEM; - goto err_unref; - } - - dbus_connection_send(wl->dbus, m, NULL); - r = 0; - - err_unref: - dbus_message_unref(m); - return r; -} - -static void -launcher_logind_set_active(struct launcher_logind *wl, bool active) -{ - if (wl->compositor->session_active == active) - return; - - wl->compositor->session_active = active; - - wl_signal_emit(&wl->compositor->session_signal, - wl->compositor); -} - -static void -parse_active(struct launcher_logind *wl, DBusMessage *m, DBusMessageIter *iter) -{ - DBusMessageIter sub; - dbus_bool_t b; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) - return; - - dbus_message_iter_recurse(iter, &sub); - - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN) - return; - - dbus_message_iter_get_basic(&sub, &b); - - /* If the backend requested DRM master-device synchronization, we only - * wake-up the compositor once the master-device is up and running. For - * other backends, we immediately forward the Active-change event. */ - if (!wl->sync_drm || !b) - launcher_logind_set_active(wl, b); -} - -static void -get_active_cb(DBusPendingCall *pending, void *data) -{ - struct launcher_logind *wl = data; - DBusMessageIter iter; - DBusMessage *m; - int type; - - dbus_pending_call_unref(wl->pending_active); - wl->pending_active = NULL; - - m = dbus_pending_call_steal_reply(pending); - if (!m) - return; - - type = dbus_message_get_type(m); - if (type == DBUS_MESSAGE_TYPE_METHOD_RETURN && - dbus_message_iter_init(m, &iter)) - parse_active(wl, m, &iter); - - dbus_message_unref(m); -} - -static void -launcher_logind_get_active(struct launcher_logind *wl) -{ - DBusPendingCall *pending; - DBusMessage *m; - bool b; - const char *iface, *name; - - m = dbus_message_new_method_call("org.freedesktop.login1", - wl->spath, - "org.freedesktop.DBus.Properties", - "Get"); - if (!m) - return; - - iface = "org.freedesktop.login1.Session"; - name = "Active"; - b = dbus_message_append_args(m, - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - if (!b) - goto err_unref; - - b = dbus_connection_send_with_reply(wl->dbus, m, &pending, -1); - if (!b) - goto err_unref; - - b = dbus_pending_call_set_notify(pending, get_active_cb, wl, NULL); - if (!b) { - dbus_pending_call_cancel(pending); - dbus_pending_call_unref(pending); - goto err_unref; - } - - if (wl->pending_active) { - dbus_pending_call_cancel(wl->pending_active); - dbus_pending_call_unref(wl->pending_active); - } - wl->pending_active = pending; - return; - -err_unref: - dbus_message_unref(m); -} - -static void -disconnected_dbus(struct launcher_logind *wl) -{ - weston_log("logind: dbus connection lost, exiting..\n"); - exit(-1); -} - -static void -session_removed(struct launcher_logind *wl, DBusMessage *m) -{ - const char *name, *obj; - bool r; - - r = dbus_message_get_args(m, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_OBJECT_PATH, &obj, - DBUS_TYPE_INVALID); - if (!r) { - weston_log("logind: cannot parse SessionRemoved dbus signal\n"); - return; - } - - if (!strcmp(name, wl->sid)) { - weston_log("logind: our session got closed, exiting..\n"); - exit(-1); - } -} - -static void -property_changed(struct launcher_logind *wl, DBusMessage *m) -{ - DBusMessageIter iter, sub, entry; - const char *interface, *name; - - if (!dbus_message_iter_init(m, &iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - goto error; - - dbus_message_iter_get_basic(&iter, &interface); - - if (!dbus_message_iter_next(&iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) - goto error; - - dbus_message_iter_recurse(&iter, &sub); - - while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY) { - dbus_message_iter_recurse(&sub, &entry); - - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) - goto error; - - dbus_message_iter_get_basic(&entry, &name); - if (!dbus_message_iter_next(&entry)) - goto error; - - if (!strcmp(name, "Active")) { - parse_active(wl, m, &entry); - return; - } - - dbus_message_iter_next(&sub); - } - - if (!dbus_message_iter_next(&iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) - goto error; - - dbus_message_iter_recurse(&iter, &sub); - - while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) { - dbus_message_iter_get_basic(&sub, &name); - - if (!strcmp(name, "Active")) { - launcher_logind_get_active(wl); - return; - } - - dbus_message_iter_next(&sub); - } - - return; - -error: - weston_log("logind: cannot parse PropertiesChanged dbus signal\n"); -} - -static void -device_paused(struct launcher_logind *wl, DBusMessage *m) -{ - bool r; - const char *type; - uint32_t major, minor; - - r = dbus_message_get_args(m, NULL, - DBUS_TYPE_UINT32, &major, - DBUS_TYPE_UINT32, &minor, - DBUS_TYPE_STRING, &type, - DBUS_TYPE_INVALID); - if (!r) { - weston_log("logind: cannot parse PauseDevice dbus signal\n"); - return; - } - - /* "pause" means synchronous pausing. Acknowledge it unconditionally - * as we support asynchronous device shutdowns, anyway. - * "force" means asynchronous pausing. - * "gone" means the device is gone. We handle it the same as "force" as - * a following udev event will be caught, too. - * - * If it's our main DRM device, tell the compositor to go asleep. */ - - if (!strcmp(type, "pause")) - launcher_logind_pause_device_complete(wl, major, minor); - - if (wl->sync_drm && wl->compositor->backend->device_changed) - wl->compositor->backend->device_changed(wl->compositor, - makedev(major,minor), - false); -} - -static void -device_resumed(struct launcher_logind *wl, DBusMessage *m) -{ - bool r; - uint32_t major, minor; - - r = dbus_message_get_args(m, NULL, - DBUS_TYPE_UINT32, &major, - DBUS_TYPE_UINT32, &minor, - DBUS_TYPE_INVALID); - if (!r) { - weston_log("logind: cannot parse ResumeDevice dbus signal\n"); - return; - } - - /* DeviceResumed messages provide us a new file-descriptor for - * resumed devices. For DRM devices it's the same as before, for evdev - * devices it's a new open-file. As we reopen evdev devices, anyway, - * there is no need for us to handle this event for evdev. For DRM, we - * notify the compositor to wake up. */ - - if (wl->sync_drm && wl->compositor->backend->device_changed) - wl->compositor->backend->device_changed(wl->compositor, - makedev(major,minor), - true); -} - -static DBusHandlerResult -filter_dbus(DBusConnection *c, DBusMessage *m, void *data) -{ - struct launcher_logind *wl = data; - - if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) { - disconnected_dbus(wl); - } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Manager", - "SessionRemoved")) { - session_removed(wl, m); - } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties", - "PropertiesChanged")) { - property_changed(wl, m); - } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session", - "PauseDevice")) { - device_paused(wl, m); - } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session", - "ResumeDevice")) { - device_resumed(wl, m); - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static int -launcher_logind_setup_dbus(struct launcher_logind *wl) -{ - bool b; - int r; - - r = asprintf(&wl->spath, "/org/freedesktop/login1/session/%s", - wl->sid); - if (r < 0) - return -ENOMEM; - - b = dbus_connection_add_filter(wl->dbus, filter_dbus, wl, NULL); - if (!b) { - weston_log("logind: cannot add dbus filter\n"); - r = -ENOMEM; - goto err_spath; - } - - r = weston_dbus_add_match_signal(wl->dbus, - "org.freedesktop.login1", - "org.freedesktop.login1.Manager", - "SessionRemoved", - "/org/freedesktop/login1"); - if (r < 0) { - weston_log("logind: cannot add dbus match\n"); - goto err_spath; - } - - r = weston_dbus_add_match_signal(wl->dbus, - "org.freedesktop.login1", - "org.freedesktop.login1.Session", - "PauseDevice", - wl->spath); - if (r < 0) { - weston_log("logind: cannot add dbus match\n"); - goto err_spath; - } - - r = weston_dbus_add_match_signal(wl->dbus, - "org.freedesktop.login1", - "org.freedesktop.login1.Session", - "ResumeDevice", - wl->spath); - if (r < 0) { - weston_log("logind: cannot add dbus match\n"); - goto err_spath; - } - - r = weston_dbus_add_match_signal(wl->dbus, - "org.freedesktop.login1", - "org.freedesktop.DBus.Properties", - "PropertiesChanged", - wl->spath); - if (r < 0) { - weston_log("logind: cannot add dbus match\n"); - goto err_spath; - } - - return 0; - -err_spath: - /* don't remove any dbus-match as the connection is closed, anyway */ - free(wl->spath); - return r; -} - -static void -launcher_logind_destroy_dbus(struct launcher_logind *wl) -{ - /* don't remove any dbus-match as the connection is closed, anyway */ - free(wl->spath); -} - -static int -launcher_logind_take_control(struct launcher_logind *wl) -{ - DBusError err; - DBusMessage *m, *reply; - dbus_bool_t force; - bool b; - int r; - - dbus_error_init(&err); - - m = dbus_message_new_method_call("org.freedesktop.login1", - wl->spath, - "org.freedesktop.login1.Session", - "TakeControl"); - if (!m) - return -ENOMEM; - - force = false; - b = dbus_message_append_args(m, - DBUS_TYPE_BOOLEAN, &force, - DBUS_TYPE_INVALID); - if (!b) { - r = -ENOMEM; - goto err_unref; - } - - reply = dbus_connection_send_with_reply_and_block(wl->dbus, - m, -1, &err); - if (!reply) { - if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD)) - weston_log("logind: old systemd version detected\n"); - else - weston_log("logind: cannot take control over session %s\n", wl->sid); - - dbus_error_free(&err); - r = -EIO; - goto err_unref; - } - - dbus_message_unref(reply); - dbus_message_unref(m); - return 0; - -err_unref: - dbus_message_unref(m); - return r; -} - -static void -launcher_logind_release_control(struct launcher_logind *wl) -{ - DBusMessage *m; - - m = dbus_message_new_method_call("org.freedesktop.login1", - wl->spath, - "org.freedesktop.login1.Session", - "ReleaseControl"); - if (m) { - dbus_connection_send(wl->dbus, m, NULL); - dbus_message_unref(m); - } -} - -static int -weston_sd_session_get_vt(const char *sid, unsigned int *out) -{ -#ifdef HAVE_SYSTEMD_LOGIN_209 - return sd_session_get_vt(sid, out); -#else - int r; - char *tty; - - r = sd_session_get_tty(sid, &tty); - if (r < 0) - return r; - - r = sscanf(tty, "tty%u", out); - free(tty); - - if (r != 1) - return -EINVAL; - - return 0; -#endif -} - -static int -launcher_logind_activate(struct launcher_logind *wl) -{ - DBusMessage *m; - - m = dbus_message_new_method_call("org.freedesktop.login1", - wl->spath, - "org.freedesktop.login1.Session", - "Activate"); - if (!m) - return -ENOMEM; - - dbus_connection_send(wl->dbus, m, NULL); - return 0; -} - -static int -launcher_logind_connect(struct weston_launcher **out, struct weston_compositor *compositor, - int tty, const char *seat_id, bool sync_drm) -{ - struct launcher_logind *wl; - struct wl_event_loop *loop; - char *t; - int r; - - wl = zalloc(sizeof(*wl)); - if (wl == NULL) { - r = -ENOMEM; - goto err_out; - } - - wl->base.iface = &launcher_logind_iface; - wl->compositor = compositor; - wl->sync_drm = sync_drm; - - wl->seat = strdup(seat_id); - if (!wl->seat) { - r = -ENOMEM; - goto err_wl; - } - - r = sd_pid_get_session(getpid(), &wl->sid); - if (r < 0) { - weston_log("logind: not running in a systemd session\n"); - goto err_seat; - } - - t = NULL; - r = sd_session_get_seat(wl->sid, &t); - if (r < 0) { - weston_log("logind: failed to get session seat\n"); - free(t); - goto err_session; - } else if (strcmp(seat_id, t)) { - weston_log("logind: weston's seat '%s' differs from session-seat '%s'\n", - seat_id, t); - r = -EINVAL; - free(t); - goto err_session; - } - - r = strcmp(t, "seat0"); - free(t); - if (r == 0) { - r = weston_sd_session_get_vt(wl->sid, &wl->vtnr); - if (r < 0) { - weston_log("logind: session not running on a VT\n"); - goto err_session; - } else if (tty > 0 && wl->vtnr != (unsigned int )tty) { - weston_log("logind: requested VT --tty=%d differs from real session VT %u\n", - tty, wl->vtnr); - r = -EINVAL; - goto err_session; - } - } - - loop = wl_display_get_event_loop(compositor->wl_display); - r = weston_dbus_open(loop, DBUS_BUS_SYSTEM, &wl->dbus, &wl->dbus_ctx); - if (r < 0) { - weston_log("logind: cannot connect to system dbus\n"); - goto err_session; - } - - r = launcher_logind_setup_dbus(wl); - if (r < 0) - goto err_dbus; - - r = launcher_logind_take_control(wl); - if (r < 0) - goto err_dbus_cleanup; - - r = launcher_logind_activate(wl); - if (r < 0) - goto err_dbus_cleanup; - - weston_log("logind: session control granted\n"); - * (struct launcher_logind **) out = wl; - return 0; - -err_dbus_cleanup: - launcher_logind_destroy_dbus(wl); -err_dbus: - weston_dbus_close(wl->dbus, wl->dbus_ctx); -err_session: - free(wl->sid); -err_seat: - free(wl->seat); -err_wl: - free(wl); -err_out: - weston_log("logind: cannot setup systemd-logind helper (%d), using legacy fallback\n", r); - errno = -r; - return -1; -} - -static void -launcher_logind_destroy(struct weston_launcher *launcher) -{ - struct launcher_logind *wl = wl_container_of(launcher, wl, base); - - if (wl->pending_active) { - dbus_pending_call_cancel(wl->pending_active); - dbus_pending_call_unref(wl->pending_active); - } - - launcher_logind_release_control(wl); - launcher_logind_destroy_dbus(wl); - weston_dbus_close(wl->dbus, wl->dbus_ctx); - free(wl->sid); - free(wl->seat); - free(wl); -} - -static int -launcher_logind_get_vt(struct weston_launcher *launcher) -{ - struct launcher_logind *wl = wl_container_of(launcher, wl, base); - return wl->vtnr; -} - -const struct launcher_interface launcher_logind_iface = { - .connect = launcher_logind_connect, - .destroy = launcher_logind_destroy, - .open = launcher_logind_open, - .close = launcher_logind_close, - .activate_vt = launcher_logind_activate_vt, - .get_vt = launcher_logind_get_vt, -}; diff --git a/libweston/launcher-util.c b/libweston/launcher-util.c deleted file mode 100644 index 5cbb0abb..00000000 --- a/libweston/launcher-util.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright © 2012 Benjamin Franzke - * Copyright © 2013 Intel Corporation - * - * 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 <libweston/libweston.h> - -#include "launcher-util.h" -#include "launcher-impl.h" - -#include <stdint.h> -#include <unistd.h> -#include <linux/input.h> - -static const struct launcher_interface *ifaces[] = { -#ifdef HAVE_SYSTEMD_LOGIN - &launcher_logind_iface, -#endif - &launcher_weston_launch_iface, - &launcher_direct_iface, - NULL, -}; - -WL_EXPORT struct weston_launcher * -weston_launcher_connect(struct weston_compositor *compositor, int tty, - const char *seat_id, bool sync_drm) -{ - const struct launcher_interface **it; - - for (it = ifaces; *it != NULL; it++) { - const struct launcher_interface *iface = *it; - struct weston_launcher *launcher; - - if (iface->connect(&launcher, compositor, tty, seat_id, sync_drm) == 0) - return launcher; - } - - return NULL; -} - -WL_EXPORT void -weston_launcher_destroy(struct weston_launcher *launcher) -{ - launcher->iface->destroy(launcher); -} - -WL_EXPORT int -weston_launcher_open(struct weston_launcher *launcher, - const char *path, int flags) -{ - return launcher->iface->open(launcher, path, flags); -} - -WL_EXPORT void -weston_launcher_close(struct weston_launcher *launcher, int fd) -{ - launcher->iface->close(launcher, fd); -} - -WL_EXPORT int -weston_launcher_activate_vt(struct weston_launcher *launcher, int vt) -{ - return launcher->iface->activate_vt(launcher, vt); -} - -static void -switch_vt_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, void *data) -{ - struct weston_compositor *compositor = data; - struct weston_launcher *launcher = compositor->launcher; - int vt = key - KEY_F1 + 1; - - if (vt == launcher->iface->get_vt(launcher)) - return; - - weston_launcher_activate_vt(launcher, vt); -} - -WL_EXPORT void -weston_setup_vt_switch_bindings(struct weston_compositor *compositor) -{ - uint32_t key; - struct weston_launcher *launcher = compositor->launcher; - - if (launcher->iface->get_vt(launcher) <= 0) - return; - - if (compositor->vt_switching == false) - return; - - for (key = KEY_F1; key < KEY_F9; key++) - weston_compositor_add_key_binding(compositor, key, - MODIFIER_CTRL | MODIFIER_ALT, - switch_vt_binding, - compositor); -} diff --git a/libweston/launcher-util.h b/libweston/launcher-util.h deleted file mode 100644 index dd7b7702..00000000 --- a/libweston/launcher-util.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © 2012 Benjamin Franzke - * - * 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. - */ - -#ifndef _WESTON_LAUNCHER_UTIL_H_ -#define _WESTON_LAUNCHER_UTIL_H_ - -#include "config.h" - -#include <libweston/libweston.h> - -struct weston_launcher; - -struct weston_launcher * -weston_launcher_connect(struct weston_compositor *compositor, int tty, - const char *seat_id, bool sync_drm); - -void -weston_launcher_destroy(struct weston_launcher *launcher); - -int -weston_launcher_open(struct weston_launcher *launcher, - const char *path, int flags); - -void -weston_launcher_close(struct weston_launcher *launcher, int fd); - -int -weston_launcher_activate_vt(struct weston_launcher *launcher, int vt); - -void -weston_setup_vt_switch_bindings(struct weston_compositor *compositor); - -#endif diff --git a/libweston/launcher-weston-launch.c b/libweston/launcher-weston-launch.c deleted file mode 100644 index 7bac0a30..00000000 --- a/libweston/launcher-weston-launch.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright © 2012 Benjamin Franzke - * Copyright © 2013 Intel Corporation - * - * 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 <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <errno.h> -#include <signal.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/uio.h> -#include <sys/ioctl.h> -#include <fcntl.h> -#include <unistd.h> -#include <linux/vt.h> -#include <linux/kd.h> -#include <linux/major.h> - -#include <libweston/libweston.h> -#include "weston-launch.h" -#include "launcher-impl.h" - -#define DRM_MAJOR 226 - -#ifndef KDSKBMUTE -#define KDSKBMUTE 0x4B51 -#endif - -#ifdef BUILD_DRM_COMPOSITOR - -#include <xf86drm.h> - -#else - -static inline int -drmDropMaster(int drm_fd) -{ - return 0; -} - -static inline int -drmSetMaster(int drm_fd) -{ - return 0; -} - -#endif - -/* major()/minor() */ -#ifdef MAJOR_IN_MKDEV -#include <sys/mkdev.h> -#endif -#ifdef MAJOR_IN_SYSMACROS -#include <sys/sysmacros.h> -#endif - -union cmsg_data { unsigned char b[4]; int fd; }; - -struct launcher_weston_launch { - struct weston_launcher base; - struct weston_compositor *compositor; - struct wl_event_loop *loop; - int fd; - struct wl_event_source *source; - - int kb_mode, tty, drm_fd; -}; - -static int -launcher_weston_launch_open(struct weston_launcher *launcher_base, - const char *path, int flags) -{ - struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base); - int n, ret; - struct msghdr msg; - struct cmsghdr *cmsg; - struct iovec iov; - union cmsg_data *data; - char control[CMSG_SPACE(sizeof data->fd)]; - ssize_t len; - struct weston_launcher_open *message; - - n = sizeof(*message) + strlen(path) + 1; - message = malloc(n); - if (!message) - return -1; - - message->header.opcode = WESTON_LAUNCHER_OPEN; - message->flags = flags; - strcpy(message->path, path); - - do { - len = send(launcher->fd, message, n, 0); - } while (len < 0 && errno == EINTR); - free(message); - - memset(&msg, 0, sizeof msg); - iov.iov_base = &ret; - iov.iov_len = sizeof ret; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = control; - msg.msg_controllen = sizeof control; - - do { - len = recvmsg(launcher->fd, &msg, MSG_CMSG_CLOEXEC); - } while (len < 0 && errno == EINTR); - - if (len != sizeof ret || - ret < 0) - return -1; - - cmsg = CMSG_FIRSTHDR(&msg); - if (!cmsg || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS) { - fprintf(stderr, "invalid control message\n"); - return -1; - } - - data = (union cmsg_data *) CMSG_DATA(cmsg); - if (data->fd == -1) { - fprintf(stderr, "missing drm fd in socket request\n"); - return -1; - } - - return data->fd; -} - -static void -launcher_weston_launch_close(struct weston_launcher *launcher_base, int fd) -{ - close(fd); -} - -static void -launcher_weston_launch_restore(struct weston_launcher *launcher_base) -{ - struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base); - struct vt_mode mode = { 0 }; - - if (ioctl(launcher->tty, KDSKBMUTE, 0) && - ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode)) - weston_log("failed to restore kb mode: %s\n", - strerror(errno)); - - if (ioctl(launcher->tty, KDSETMODE, KD_TEXT)) - weston_log("failed to set KD_TEXT mode on tty: %s\n", - strerror(errno)); - - /* We have to drop master before we switch the VT back in - * VT_AUTO, so we don't risk switching to a VT with another - * display server, that will then fail to set drm master. */ - drmDropMaster(launcher->drm_fd); - - mode.mode = VT_AUTO; - if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) - weston_log("could not reset vt handling\n"); -} - -static int -launcher_weston_launch_data(int fd, uint32_t mask, void *data) -{ - struct launcher_weston_launch *launcher = data; - int len, ret; - - if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { - weston_log("launcher socket closed, exiting\n"); - /* Normally the weston-launch will reset the tty, but - * in this case it died or something, so do it here so - * we don't end up with a stuck vt. */ - launcher_weston_launch_restore(&launcher->base); - exit(-1); - } - - do { - len = recv(launcher->fd, &ret, sizeof ret, 0); - } while (len < 0 && errno == EINTR); - - switch (ret) { - case WESTON_LAUNCHER_ACTIVATE: - launcher->compositor->session_active = true; - wl_signal_emit(&launcher->compositor->session_signal, - launcher->compositor); - break; - case WESTON_LAUNCHER_DEACTIVATE: - launcher->compositor->session_active = false; - wl_signal_emit(&launcher->compositor->session_signal, - launcher->compositor); - break; - default: - weston_log("unexpected event from weston-launch\n"); - break; - } - - return 1; -} - -static int -launcher_weston_launch_activate_vt(struct weston_launcher *launcher_base, int vt) -{ - struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base); - return ioctl(launcher->tty, VT_ACTIVATE, vt); -} - -static int -launcher_weston_launch_connect(struct weston_launcher **out, struct weston_compositor *compositor, - int tty, const char *seat_id, bool sync_drm) -{ - struct launcher_weston_launch *launcher; - struct wl_event_loop *loop; - - launcher = malloc(sizeof *launcher); - if (launcher == NULL) - return -ENOMEM; - - launcher->base.iface = &launcher_weston_launch_iface; - * (struct launcher_weston_launch **) out = launcher; - launcher->compositor = compositor; - launcher->drm_fd = -1; - launcher->fd = weston_environment_get_fd("WESTON_LAUNCHER_SOCK"); - if (launcher->fd != -1) { - launcher->tty = weston_environment_get_fd("WESTON_TTY_FD"); - /* We don't get a chance to read out the original kb - * mode for the tty, so just hard code K_UNICODE here - * in case we have to clean if weston-launch dies. */ - launcher->kb_mode = K_UNICODE; - - loop = wl_display_get_event_loop(compositor->wl_display); - launcher->source = wl_event_loop_add_fd(loop, launcher->fd, - WL_EVENT_READABLE, - launcher_weston_launch_data, - launcher); - if (launcher->source == NULL) { - free(launcher); - return -ENOMEM; - } - - return 0; - } else { - return -1; - } -} - -static void -launcher_weston_launch_destroy(struct weston_launcher *launcher_base) -{ - struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base); - - if (launcher->fd != -1) { - close(launcher->fd); - wl_event_source_remove(launcher->source); - } else { - launcher_weston_launch_restore(&launcher->base); - } - - if (launcher->tty >= 0) - close(launcher->tty); - - free(launcher); -} - -static int -launcher_weston_launch_get_vt(struct weston_launcher *base) -{ - struct launcher_weston_launch *launcher = wl_container_of(base, launcher, base); - struct stat s; - if (fstat(launcher->tty, &s) < 0) - return -1; - - return minor(s.st_rdev); -} - -const struct launcher_interface launcher_weston_launch_iface = { - .connect = launcher_weston_launch_connect, - .destroy = launcher_weston_launch_destroy, - .open = launcher_weston_launch_open, - .close = launcher_weston_launch_close, - .activate_vt = launcher_weston_launch_activate_vt, - .get_vt = launcher_weston_launch_get_vt, -}; diff --git a/libweston/libinput-device.c b/libweston/libinput-device.c deleted file mode 100644 index 9a084cbf..00000000 --- a/libweston/libinput-device.c +++ /dev/null @@ -1,761 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * Copyright © 2013 Jonas Ådahl - * Copyright 2017-2018 Collabora, Ltd. - * Copyright 2017-2018 General Electric Company - * - * 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 <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <linux/input.h> -#include <unistd.h> -#include <fcntl.h> -#include <assert.h> -#include <libinput.h> - -#include <libweston/libweston.h> -#include "backend.h" -#include "libweston-internal.h" -#include "libinput-device.h" -#include "shared/helpers.h" -#include "shared/timespec-util.h" - -void -evdev_led_update(struct evdev_device *device, enum weston_led weston_leds) -{ - enum libinput_led leds = 0; - - if (weston_leds & LED_NUM_LOCK) - leds |= LIBINPUT_LED_NUM_LOCK; - if (weston_leds & LED_CAPS_LOCK) - leds |= LIBINPUT_LED_CAPS_LOCK; - if (weston_leds & LED_SCROLL_LOCK) - leds |= LIBINPUT_LED_SCROLL_LOCK; - - libinput_device_led_update(device->device, leds); -} - -static void -handle_keyboard_key(struct libinput_device *libinput_device, - struct libinput_event_keyboard *keyboard_event) -{ - struct evdev_device *device = - libinput_device_get_user_data(libinput_device); - int key_state = - libinput_event_keyboard_get_key_state(keyboard_event); - int seat_key_count = - libinput_event_keyboard_get_seat_key_count(keyboard_event); - struct timespec time; - - /* Ignore key events that are not seat wide state changes. */ - if ((key_state == LIBINPUT_KEY_STATE_PRESSED && - seat_key_count != 1) || - (key_state == LIBINPUT_KEY_STATE_RELEASED && - seat_key_count != 0)) - return; - - timespec_from_usec(&time, - libinput_event_keyboard_get_time_usec(keyboard_event)); - - notify_key(device->seat, &time, - libinput_event_keyboard_get_key(keyboard_event), - key_state, STATE_UPDATE_AUTOMATIC); -} - -static bool -handle_pointer_motion(struct libinput_device *libinput_device, - struct libinput_event_pointer *pointer_event) -{ - struct evdev_device *device = - libinput_device_get_user_data(libinput_device); - struct weston_pointer_motion_event event = { 0 }; - struct timespec time; - double dx_unaccel, dy_unaccel; - - timespec_from_usec(&time, - libinput_event_pointer_get_time_usec(pointer_event)); - dx_unaccel = libinput_event_pointer_get_dx_unaccelerated(pointer_event); - dy_unaccel = libinput_event_pointer_get_dy_unaccelerated(pointer_event); - - event = (struct weston_pointer_motion_event) { - .mask = WESTON_POINTER_MOTION_REL | - WESTON_POINTER_MOTION_REL_UNACCEL, - .time = time, - .dx = libinput_event_pointer_get_dx(pointer_event), - .dy = libinput_event_pointer_get_dy(pointer_event), - .dx_unaccel = dx_unaccel, - .dy_unaccel = dy_unaccel, - }; - - notify_motion(device->seat, &time, &event); - - return true; -} - -static bool -handle_pointer_motion_absolute( - struct libinput_device *libinput_device, - struct libinput_event_pointer *pointer_event) -{ - struct evdev_device *device = - libinput_device_get_user_data(libinput_device); - struct weston_output *output = device->output; - struct timespec time; - double x, y; - uint32_t width, height; - - if (!output) - return false; - - timespec_from_usec(&time, - libinput_event_pointer_get_time_usec(pointer_event)); - width = device->output->current_mode->width; - height = device->output->current_mode->height; - - x = libinput_event_pointer_get_absolute_x_transformed(pointer_event, - width); - y = libinput_event_pointer_get_absolute_y_transformed(pointer_event, - height); - - weston_output_transform_coordinate(device->output, x, y, &x, &y); - notify_motion_absolute(device->seat, &time, x, y); - - return true; -} - -static bool -handle_pointer_button(struct libinput_device *libinput_device, - struct libinput_event_pointer *pointer_event) -{ - struct evdev_device *device = - libinput_device_get_user_data(libinput_device); - int button_state = - libinput_event_pointer_get_button_state(pointer_event); - int seat_button_count = - libinput_event_pointer_get_seat_button_count(pointer_event); - struct timespec time; - - /* Ignore button events that are not seat wide state changes. */ - if ((button_state == LIBINPUT_BUTTON_STATE_PRESSED && - seat_button_count != 1) || - (button_state == LIBINPUT_BUTTON_STATE_RELEASED && - seat_button_count != 0)) - return false; - - timespec_from_usec(&time, - libinput_event_pointer_get_time_usec(pointer_event)); - - notify_button(device->seat, &time, - libinput_event_pointer_get_button(pointer_event), - button_state); - - return true; -} - -static double -normalize_scroll(struct libinput_event_pointer *pointer_event, - enum libinput_pointer_axis axis) -{ - enum libinput_pointer_axis_source source; - double value = 0.0; - - source = libinput_event_pointer_get_axis_source(pointer_event); - /* libinput < 0.8 sent wheel click events with value 10. Since 0.8 - the value is the angle of the click in degrees. To keep - backwards-compat with existing clients, we just send multiples of - the click count. - */ - switch (source) { - case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: - value = 10 * libinput_event_pointer_get_axis_value_discrete( - pointer_event, - axis); - break; - case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: - case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: - value = libinput_event_pointer_get_axis_value(pointer_event, - axis); - break; - default: - assert(!"unhandled event source in normalize_scroll"); - } - - return value; -} - -static int32_t -get_axis_discrete(struct libinput_event_pointer *pointer_event, - enum libinput_pointer_axis axis) -{ - enum libinput_pointer_axis_source source; - - source = libinput_event_pointer_get_axis_source(pointer_event); - - if (source != LIBINPUT_POINTER_AXIS_SOURCE_WHEEL) - return 0; - - return libinput_event_pointer_get_axis_value_discrete(pointer_event, - axis); -} - -static bool -handle_pointer_axis(struct libinput_device *libinput_device, - struct libinput_event_pointer *pointer_event) -{ - static int warned; - struct evdev_device *device = - libinput_device_get_user_data(libinput_device); - double vert, horiz; - int32_t vert_discrete, horiz_discrete; - enum libinput_pointer_axis axis; - struct weston_pointer_axis_event weston_event; - enum libinput_pointer_axis_source source; - uint32_t wl_axis_source; - bool has_vert, has_horiz; - struct timespec time; - - has_vert = libinput_event_pointer_has_axis(pointer_event, - LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); - has_horiz = libinput_event_pointer_has_axis(pointer_event, - LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); - - if (!has_vert && !has_horiz) - return false; - - source = libinput_event_pointer_get_axis_source(pointer_event); - switch (source) { - case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: - wl_axis_source = WL_POINTER_AXIS_SOURCE_WHEEL; - break; - case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: - wl_axis_source = WL_POINTER_AXIS_SOURCE_FINGER; - break; - case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: - wl_axis_source = WL_POINTER_AXIS_SOURCE_CONTINUOUS; - break; - default: - if (warned < 5) { - weston_log("Unknown scroll source %d.\n", source); - warned++; - } - return false; - } - - notify_axis_source(device->seat, wl_axis_source); - - timespec_from_usec(&time, - libinput_event_pointer_get_time_usec(pointer_event)); - - if (has_vert) { - axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL; - vert_discrete = get_axis_discrete(pointer_event, axis); - vert = normalize_scroll(pointer_event, axis); - - weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL; - weston_event.value = vert; - weston_event.discrete = vert_discrete; - weston_event.has_discrete = (vert_discrete != 0); - - notify_axis(device->seat, &time, &weston_event); - } - - if (has_horiz) { - axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL; - horiz_discrete = get_axis_discrete(pointer_event, axis); - horiz = normalize_scroll(pointer_event, axis); - - weston_event.axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL; - weston_event.value = horiz; - weston_event.discrete = horiz_discrete; - weston_event.has_discrete = (horiz_discrete != 0); - - notify_axis(device->seat, &time, &weston_event); - } - - return true; -} - -static struct weston_output * -touch_get_output(struct weston_touch_device *device) -{ - struct evdev_device *evdev_device = device->backend_data; - - return evdev_device->output; -} - -static const char * -touch_get_calibration_head_name(struct weston_touch_device *device) -{ - struct evdev_device *evdev_device = device->backend_data; - struct weston_output *output = evdev_device->output; - struct weston_head *head; - - if (!output) - return NULL; - - assert(output->enabled); - if (evdev_device->output_name) - return evdev_device->output_name; - - /* No specific head was configured, so the association was made by - * the default rule. Just grab whatever head's name. - */ - wl_list_for_each(head, &output->head_list, output_link) - return head->name; - - assert(0); - return NULL; -} - -static void -touch_get_calibration(struct weston_touch_device *device, - struct weston_touch_device_matrix *cal) -{ - struct evdev_device *evdev_device = device->backend_data; - - libinput_device_config_calibration_get_matrix(evdev_device->device, - cal->m); -} - -static void -do_set_calibration(struct evdev_device *evdev_device, - const struct weston_touch_device_matrix *cal) -{ - enum libinput_config_status status; - - weston_log("input device %s: applying calibration:\n", - libinput_device_get_sysname(evdev_device->device)); - weston_log_continue(STAMP_SPACE " %f %f %f\n", - cal->m[0], cal->m[1], cal->m[2]); - weston_log_continue(STAMP_SPACE " %f %f %f\n", - cal->m[3], cal->m[4], cal->m[5]); - - status = libinput_device_config_calibration_set_matrix(evdev_device->device, - cal->m); - if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) - weston_log("Error: Failed to apply calibration.\n"); -} - -static void -touch_set_calibration(struct weston_touch_device *device, - const struct weston_touch_device_matrix *cal) -{ - struct evdev_device *evdev_device = device->backend_data; - - /* Stop output hotplug from reloading the WL_CALIBRATION values. - * libinput will maintain the latest calibration for us. - */ - evdev_device->override_wl_calibration = true; - - do_set_calibration(evdev_device, cal); -} - -static const struct weston_touch_device_ops touch_calibration_ops = { - .get_output = touch_get_output, - .get_calibration_head_name = touch_get_calibration_head_name, - .get_calibration = touch_get_calibration, - .set_calibration = touch_set_calibration -}; - -static struct weston_touch_device * -create_touch_device(struct evdev_device *device) -{ - const struct weston_touch_device_ops *ops = NULL; - struct weston_touch_device *touch_device; - struct udev_device *udev_device; - - if (libinput_device_config_calibration_has_matrix(device->device)) - ops = &touch_calibration_ops; - - udev_device = libinput_device_get_udev_device(device->device); - if (!udev_device) - return NULL; - - touch_device = weston_touch_create_touch_device(device->seat->touch_state, - udev_device_get_syspath(udev_device), - device, ops); - - udev_device_unref(udev_device); - - if (!touch_device) - return NULL; - - weston_log("Touchscreen - %s - %s\n", - libinput_device_get_name(device->device), - touch_device->syspath); - - return touch_device; -} - -static void -handle_touch_with_coords(struct libinput_device *libinput_device, - struct libinput_event_touch *touch_event, - int touch_type) -{ - struct evdev_device *device = - libinput_device_get_user_data(libinput_device); - double x; - double y; - struct weston_point2d_device_normalized norm; - uint32_t width, height; - struct timespec time; - int32_t slot; - - if (!device->output) - return; - - timespec_from_usec(&time, - libinput_event_touch_get_time_usec(touch_event)); - slot = libinput_event_touch_get_seat_slot(touch_event); - - width = device->output->current_mode->width; - height = device->output->current_mode->height; - x = libinput_event_touch_get_x_transformed(touch_event, width); - y = libinput_event_touch_get_y_transformed(touch_event, height); - - weston_output_transform_coordinate(device->output, - x, y, &x, &y); - - if (weston_touch_device_can_calibrate(device->touch_device)) { - norm.x = libinput_event_touch_get_x_transformed(touch_event, 1); - norm.y = libinput_event_touch_get_y_transformed(touch_event, 1); - notify_touch_normalized(device->touch_device, &time, slot, - x, y, &norm, touch_type); - } else { - notify_touch(device->touch_device, &time, slot, x, y, touch_type); - } -} - -static void -handle_touch_down(struct libinput_device *device, - struct libinput_event_touch *touch_event) -{ - handle_touch_with_coords(device, touch_event, WL_TOUCH_DOWN); -} - -static void -handle_touch_motion(struct libinput_device *device, - struct libinput_event_touch *touch_event) -{ - handle_touch_with_coords(device, touch_event, WL_TOUCH_MOTION); -} - -static void -handle_touch_up(struct libinput_device *libinput_device, - struct libinput_event_touch *touch_event) -{ - struct evdev_device *device = - libinput_device_get_user_data(libinput_device); - struct timespec time; - int32_t slot = libinput_event_touch_get_seat_slot(touch_event); - - timespec_from_usec(&time, - libinput_event_touch_get_time_usec(touch_event)); - - notify_touch(device->touch_device, &time, slot, 0, 0, WL_TOUCH_UP); -} - -static void -handle_touch_frame(struct libinput_device *libinput_device, - struct libinput_event_touch *touch_event) -{ - struct evdev_device *device = - libinput_device_get_user_data(libinput_device); - - notify_touch_frame(device->touch_device); -} - -int -evdev_device_process_event(struct libinput_event *event) -{ - struct libinput_device *libinput_device = - libinput_event_get_device(event); - struct evdev_device *device = - libinput_device_get_user_data(libinput_device); - int handled = 1; - bool need_frame = false; - - switch (libinput_event_get_type(event)) { - case LIBINPUT_EVENT_KEYBOARD_KEY: - handle_keyboard_key(libinput_device, - libinput_event_get_keyboard_event(event)); - break; - case LIBINPUT_EVENT_POINTER_MOTION: - need_frame = handle_pointer_motion(libinput_device, - libinput_event_get_pointer_event(event)); - break; - case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: - need_frame = handle_pointer_motion_absolute( - libinput_device, - libinput_event_get_pointer_event(event)); - break; - case LIBINPUT_EVENT_POINTER_BUTTON: - need_frame = handle_pointer_button(libinput_device, - libinput_event_get_pointer_event(event)); - break; - case LIBINPUT_EVENT_POINTER_AXIS: - need_frame = handle_pointer_axis( - libinput_device, - libinput_event_get_pointer_event(event)); - break; - case LIBINPUT_EVENT_TOUCH_DOWN: - handle_touch_down(libinput_device, - libinput_event_get_touch_event(event)); - break; - case LIBINPUT_EVENT_TOUCH_MOTION: - handle_touch_motion(libinput_device, - libinput_event_get_touch_event(event)); - break; - case LIBINPUT_EVENT_TOUCH_UP: - handle_touch_up(libinput_device, - libinput_event_get_touch_event(event)); - break; - case LIBINPUT_EVENT_TOUCH_FRAME: - handle_touch_frame(libinput_device, - libinput_event_get_touch_event(event)); - break; - default: - handled = 0; - weston_log("unknown libinput event %d\n", - libinput_event_get_type(event)); - } - - if (need_frame) - notify_pointer_frame(device->seat); - - return handled; -} - -static void -notify_output_destroy(struct wl_listener *listener, void *data) -{ - struct evdev_device *device = - container_of(listener, - struct evdev_device, output_destroy_listener); - - evdev_device_set_output(device, NULL); -} - -/** - * The WL_CALIBRATION property requires a pixel-specific matrix to be - * applied after scaling device coordinates to screen coordinates. libinput - * can't do that, so we need to convert the calibration to the normalized - * format libinput expects. - */ -void -evdev_device_set_calibration(struct evdev_device *device) -{ - struct udev *udev; - struct udev_device *udev_device = NULL; - const char *sysname = libinput_device_get_sysname(device->device); - const char *calibration_values; - uint32_t width, height; - struct weston_touch_device_matrix calibration; - - if (!libinput_device_config_calibration_has_matrix(device->device)) - return; - - /* If LIBINPUT_CALIBRATION_MATRIX was set to non-identity, we will not - * override it with WL_CALIBRATION. It also means we don't need an - * output to load a calibration. */ - if (libinput_device_config_calibration_get_default_matrix( - device->device, - calibration.m) != 0) - return; - - /* touch_set_calibration() has updated the values, do not load old - * values from WL_CALIBRATION. - */ - if (device->override_wl_calibration) - return; - - if (!device->output) { - weston_log("input device %s has no enabled output associated " - "(%s named), skipping calibration for now.\n", - sysname, device->output_name ?: "none"); - return; - } - - width = device->output->width; - height = device->output->height; - if (width == 0 || height == 0) - return; - - udev = udev_new(); - if (!udev) - return; - - udev_device = udev_device_new_from_subsystem_sysname(udev, - "input", - sysname); - if (!udev_device) - goto out; - - calibration_values = - udev_device_get_property_value(udev_device, - "WL_CALIBRATION"); - - if (calibration_values) { - weston_log("Warning: input device %s has WL_CALIBRATION property set. " - "Support for it will be removed in the future. " - "Please use LIBINPUT_CALIBRATION_MATRIX instead.\n", - sysname); - } - - if (!calibration_values || sscanf(calibration_values, - "%f %f %f %f %f %f", - &calibration.m[0], - &calibration.m[1], - &calibration.m[2], - &calibration.m[3], - &calibration.m[4], - &calibration.m[5]) != 6) - goto out; - - /* normalize to a format libinput can use. There is a chance of - this being wrong if the width/height don't match the device - width/height but I'm not sure how to fix that */ - calibration.m[2] /= width; - calibration.m[5] /= height; - - do_set_calibration(device, &calibration); - - weston_log_continue(STAMP_SPACE " raw translation %f %f for output %s\n", - calibration.m[2] * width, - calibration.m[5] * height, - device->output->name); - -out: - if (udev_device) - udev_device_unref(udev_device); - udev_unref(udev); -} - -void -evdev_device_set_output(struct evdev_device *device, - struct weston_output *output) -{ - if (device->output == output) - return; - - if (device->output_destroy_listener.notify) { - wl_list_remove(&device->output_destroy_listener.link); - device->output_destroy_listener.notify = NULL; - } - - if (!output) { - weston_log("output for input device %s removed\n", - libinput_device_get_sysname(device->device)); - - device->output = NULL; - return; - } - - weston_log("associating input device %s with output %s " - "(%s by udev)\n", - libinput_device_get_sysname(device->device), - output->name, - device->output_name ?: "none"); - - device->output = output; - device->output_destroy_listener.notify = notify_output_destroy; - wl_signal_add(&output->destroy_signal, - &device->output_destroy_listener); - evdev_device_set_calibration(device); -} - -struct evdev_device * -evdev_device_create(struct libinput_device *libinput_device, - struct weston_seat *seat) -{ - struct evdev_device *device; - - device = zalloc(sizeof *device); - if (device == NULL) - return NULL; - - device->seat = seat; - wl_list_init(&device->link); - device->device = libinput_device; - - if (libinput_device_has_capability(libinput_device, - LIBINPUT_DEVICE_CAP_KEYBOARD)) { - weston_seat_init_keyboard(seat, NULL); - device->seat_caps |= EVDEV_SEAT_KEYBOARD; - } - if (libinput_device_has_capability(libinput_device, - LIBINPUT_DEVICE_CAP_POINTER)) { - weston_seat_init_pointer(seat); - device->seat_caps |= EVDEV_SEAT_POINTER; - } - if (libinput_device_has_capability(libinput_device, - LIBINPUT_DEVICE_CAP_TOUCH)) { - weston_seat_init_touch(seat); - device->seat_caps |= EVDEV_SEAT_TOUCH; - device->touch_device = create_touch_device(device); - } - - libinput_device_set_user_data(libinput_device, device); - libinput_device_ref(libinput_device); - - return device; -} - -void -evdev_device_destroy(struct evdev_device *device) -{ - if (device->seat_caps & EVDEV_SEAT_POINTER) - weston_seat_release_pointer(device->seat); - if (device->seat_caps & EVDEV_SEAT_KEYBOARD) - weston_seat_release_keyboard(device->seat); - if (device->seat_caps & EVDEV_SEAT_TOUCH) { - weston_touch_device_destroy(device->touch_device); - weston_seat_release_touch(device->seat); - } - - if (device->output) - wl_list_remove(&device->output_destroy_listener.link); - wl_list_remove(&device->link); - libinput_device_unref(device->device); - free(device->output_name); - free(device); -} - -void -evdev_notify_keyboard_focus(struct weston_seat *seat, - struct wl_list *evdev_devices) -{ - struct wl_array keys; - - if (seat->keyboard_device_count == 0) - return; - - wl_array_init(&keys); - notify_keyboard_focus_in(seat, &keys, STATE_UPDATE_AUTOMATIC); - wl_array_release(&keys); -} diff --git a/libweston/libinput-device.h b/libweston/libinput-device.h deleted file mode 100644 index d3fc645d..00000000 --- a/libweston/libinput-device.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright © 2011, 2012 Intel Corporation - * - * 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. - */ - -#ifndef _LIBINPUT_DEVICE_H_ -#define _LIBINPUT_DEVICE_H_ - -#include "config.h" - -#include <linux/input.h> -#include <wayland-util.h> -#include <libinput.h> -#include <stdbool.h> - -#include <libweston/libweston.h> - -enum evdev_device_seat_capability { - EVDEV_SEAT_POINTER = (1 << 0), - EVDEV_SEAT_KEYBOARD = (1 << 1), - EVDEV_SEAT_TOUCH = (1 << 2) -}; - -struct evdev_device { - struct weston_seat *seat; - enum evdev_device_seat_capability seat_caps; - struct libinput_device *device; - struct weston_touch_device *touch_device; - struct wl_list link; - struct weston_output *output; - struct wl_listener output_destroy_listener; - char *output_name; - int fd; - bool override_wl_calibration; -}; - -void -evdev_led_update(struct evdev_device *device, enum weston_led leds); - -struct evdev_device * -evdev_device_create(struct libinput_device *libinput_device, - struct weston_seat *seat); - -int -evdev_device_process_event(struct libinput_event *event); - -void -evdev_device_set_output(struct evdev_device *device, - struct weston_output *output); -void -evdev_device_destroy(struct evdev_device *device); - -void -evdev_notify_keyboard_focus(struct weston_seat *seat, - struct wl_list *evdev_devices); -void -evdev_device_set_calibration(struct evdev_device *device); - -int -dispatch_libinput(struct libinput *libinput); - -#endif /* _LIBINPUT_DEVICE_H_ */ diff --git a/libweston/libinput-seat.c b/libweston/libinput-seat.c deleted file mode 100644 index 98015518..00000000 --- a/libweston/libinput-seat.c +++ /dev/null @@ -1,489 +0,0 @@ -/* - * Copyright © 2013 Intel Corporation - * Copyright © 2013 Jonas Ådahl - * - * 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 <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <libinput.h> -#include <libudev.h> - -#include <libweston/libweston.h> -#include "backend.h" -#include "libweston-internal.h" -#include "weston-log-internal.h" -#include "launcher-util.h" -#include "libinput-seat.h" -#include "libinput-device.h" -#include "shared/helpers.h" - -static void -process_events(struct udev_input *input); -static struct udev_seat * -udev_seat_create(struct udev_input *input, const char *seat_name); -static void -udev_seat_destroy(struct udev_seat *seat); - -static struct udev_seat * -get_udev_seat(struct udev_input *input, struct libinput_device *device) -{ - struct libinput_seat *libinput_seat; - const char *seat_name; - - libinput_seat = libinput_device_get_seat(device); - seat_name = libinput_seat_get_logical_name(libinput_seat); - return udev_seat_get_named(input, seat_name); -} - -static struct weston_output * -output_find_by_head_name(struct weston_compositor *compositor, - const char *head_name) -{ - struct weston_output *output; - struct weston_head *head; - - if (!head_name) - return NULL; - - /* Only enabled outputs with connected heads. - * This means force-enabled outputs but with disconnected heads - * will be ignored; if the touchscreen doesn't have a video signal, - * touching it is meaningless. - */ - wl_list_for_each(output, &compositor->output_list, link) { - wl_list_for_each(head, &output->head_list, output_link) { - if (!weston_head_is_connected(head)) - continue; - - if (strcmp(head_name, head->name) == 0) - return output; - } - } - - return NULL; -} - -static void -device_added(struct udev_input *input, struct libinput_device *libinput_device) -{ - struct weston_compositor *c; - struct evdev_device *device; - struct weston_output *output; - const char *output_name; - struct weston_seat *seat; - struct udev_seat *udev_seat; - struct weston_pointer *pointer; - - c = input->compositor; - - udev_seat = get_udev_seat(input, libinput_device); - if (!udev_seat) - return; - - seat = &udev_seat->base; - device = evdev_device_create(libinput_device, seat); - if (device == NULL) - return; - - if (input->configure_device != NULL) - input->configure_device(c, device->device); - evdev_device_set_calibration(device); - udev_seat = (struct udev_seat *) seat; - wl_list_insert(udev_seat->devices_list.prev, &device->link); - - pointer = weston_seat_get_pointer(seat); - if (seat->output && pointer) - weston_pointer_clamp(pointer, - &pointer->x, - &pointer->y); - - output_name = libinput_device_get_output_name(libinput_device); - if (output_name) { - device->output_name = strdup(output_name); - output = output_find_by_head_name(c, output_name); - evdev_device_set_output(device, output); - } else if (!wl_list_empty(&c->output_list)) { - /* default assignment to an arbitrary output */ - output = container_of(c->output_list.next, - struct weston_output, link); - evdev_device_set_output(device, output); - } - - if (!input->suspended) - weston_seat_repick(seat); -} - -static void -device_removed(struct udev_input *input, struct libinput_device *libinput_device) -{ - struct evdev_device *device; - - device = libinput_device_get_user_data(libinput_device); - evdev_device_destroy(device); -} - -static void -udev_seat_remove_devices(struct udev_seat *seat) -{ - struct evdev_device *device, *next; - - wl_list_for_each_safe(device, next, &seat->devices_list, link) { - evdev_device_destroy(device); - } -} - -void -udev_input_disable(struct udev_input *input) -{ - if (input->suspended) - return; - - wl_event_source_remove(input->libinput_source); - input->libinput_source = NULL; - libinput_suspend(input->libinput); - process_events(input); - input->suspended = 1; -} - -static int -udev_input_process_event(struct libinput_event *event) -{ - struct libinput *libinput = libinput_event_get_context(event); - struct libinput_device *libinput_device = - libinput_event_get_device(event); - struct udev_input *input = libinput_get_user_data(libinput); - int handled = 1; - - switch (libinput_event_get_type(event)) { - case LIBINPUT_EVENT_DEVICE_ADDED: - device_added(input, libinput_device); - break; - case LIBINPUT_EVENT_DEVICE_REMOVED: - device_removed(input, libinput_device); - break; - default: - handled = 0; - } - - return handled; -} - -static void -process_event(struct libinput_event *event) -{ - if (udev_input_process_event(event)) - return; - if (evdev_device_process_event(event)) - return; -} - -static void -process_events(struct udev_input *input) -{ - struct libinput_event *event; - - while ((event = libinput_get_event(input->libinput))) { - process_event(event); - libinput_event_destroy(event); - } -} - -static int -udev_input_dispatch(struct udev_input *input) -{ - if (libinput_dispatch(input->libinput) != 0) - weston_log("libinput: Failed to dispatch libinput\n"); - - process_events(input); - - return 0; -} - -static int -libinput_source_dispatch(int fd, uint32_t mask, void *data) -{ - struct udev_input *input = data; - - return udev_input_dispatch(input) != 0; -} - -static int -open_restricted(const char *path, int flags, void *user_data) -{ - struct udev_input *input = user_data; - struct weston_launcher *launcher = input->compositor->launcher; - - return weston_launcher_open(launcher, path, flags); -} - -static void -close_restricted(int fd, void *user_data) -{ - struct udev_input *input = user_data; - struct weston_launcher *launcher = input->compositor->launcher; - - weston_launcher_close(launcher, fd); -} - -const struct libinput_interface libinput_interface = { - open_restricted, - close_restricted, -}; - -int -udev_input_enable(struct udev_input *input) -{ - struct wl_event_loop *loop; - struct weston_compositor *c = input->compositor; - int fd; - struct udev_seat *seat; - int devices_found = 0; - - loop = wl_display_get_event_loop(c->wl_display); - fd = libinput_get_fd(input->libinput); - input->libinput_source = - wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, - libinput_source_dispatch, input); - if (!input->libinput_source) { - return -1; - } - - if (input->suspended) { - if (libinput_resume(input->libinput) != 0) { - wl_event_source_remove(input->libinput_source); - input->libinput_source = NULL; - return -1; - } - input->suspended = 0; - process_events(input); - } - - wl_list_for_each(seat, &input->compositor->seat_list, base.link) { - evdev_notify_keyboard_focus(&seat->base, &seat->devices_list); - - if (!wl_list_empty(&seat->devices_list)) - devices_found = 1; - } - - if (devices_found == 0 && !c->require_input) { - weston_log("warning: no input devices found, but none required " - "as per configuration.\n"); - return 0; - } - - if (devices_found == 0) { - weston_log( - "warning: no input devices on entering Weston. " - "Possible causes:\n" - "\t- no permissions to read /dev/input/event*\n" - "\t- seats misconfigured " - "(Weston backend option 'seat', " - "udev device property ID_SEAT)\n"); - return -1; - } - - return 0; -} - -static void -libinput_log_func(struct libinput *libinput, - enum libinput_log_priority priority, - const char *format, va_list args) -{ - weston_vlog(format, args); -} - -int -udev_input_init(struct udev_input *input, struct weston_compositor *c, - struct udev *udev, const char *seat_id, - udev_configure_device_t configure_device) -{ - enum libinput_log_priority priority = LIBINPUT_LOG_PRIORITY_INFO; - const char *log_priority = NULL; - - memset(input, 0, sizeof *input); - - input->compositor = c; - input->configure_device = configure_device; - - log_priority = getenv("WESTON_LIBINPUT_LOG_PRIORITY"); - - input->libinput = libinput_udev_create_context(&libinput_interface, - input, udev); - if (!input->libinput) { - return -1; - } - - libinput_log_set_handler(input->libinput, &libinput_log_func); - - if (log_priority) { - if (strcmp(log_priority, "debug") == 0) { - priority = LIBINPUT_LOG_PRIORITY_DEBUG; - } else if (strcmp(log_priority, "info") == 0) { - priority = LIBINPUT_LOG_PRIORITY_INFO; - } else if (strcmp(log_priority, "error") == 0) { - priority = LIBINPUT_LOG_PRIORITY_ERROR; - } - } - - libinput_log_set_priority(input->libinput, priority); - - if (libinput_udev_assign_seat(input->libinput, seat_id) != 0) { - libinput_unref(input->libinput); - return -1; - } - - process_events(input); - - return udev_input_enable(input); -} - -void -udev_input_destroy(struct udev_input *input) -{ - struct udev_seat *seat, *next; - - if (input->libinput_source) - wl_event_source_remove(input->libinput_source); - wl_list_for_each_safe(seat, next, &input->compositor->seat_list, base.link) - udev_seat_destroy(seat); - libinput_unref(input->libinput); -} - -static void -udev_seat_led_update(struct weston_seat *seat_base, enum weston_led leds) -{ - struct udev_seat *seat = (struct udev_seat *) seat_base; - struct evdev_device *device; - - wl_list_for_each(device, &seat->devices_list, link) - evdev_led_update(device, leds); -} - -static void -udev_seat_output_changed(struct udev_seat *seat, struct weston_output *output) -{ - struct evdev_device *device; - struct weston_output *found; - - wl_list_for_each(device, &seat->devices_list, link) { - /* If we find any input device without an associated output - * or an output name to associate with, just tie it with the - * output we got here - the default assignment. - */ - if (!device->output_name) { - if (!device->output) - evdev_device_set_output(device, output); - - continue; - } - - /* Update all devices' output associations, may they gain or - * lose it. - */ - found = output_find_by_head_name(output->compositor, - device->output_name); - evdev_device_set_output(device, found); - } -} - -static void -notify_output_create(struct wl_listener *listener, void *data) -{ - struct udev_seat *seat = container_of(listener, struct udev_seat, - output_create_listener); - struct weston_output *output = data; - - udev_seat_output_changed(seat, output); -} - -static void -notify_output_heads_changed(struct wl_listener *listener, void *data) -{ - struct udev_seat *seat = container_of(listener, struct udev_seat, - output_heads_listener); - struct weston_output *output = data; - - udev_seat_output_changed(seat, output); -} - -static struct udev_seat * -udev_seat_create(struct udev_input *input, const char *seat_name) -{ - struct weston_compositor *c = input->compositor; - struct udev_seat *seat; - - seat = zalloc(sizeof *seat); - if (!seat) - return NULL; - - weston_seat_init(&seat->base, c, seat_name); - seat->base.led_update = udev_seat_led_update; - - seat->output_create_listener.notify = notify_output_create; - wl_signal_add(&c->output_created_signal, - &seat->output_create_listener); - - seat->output_heads_listener.notify = notify_output_heads_changed; - wl_signal_add(&c->output_heads_changed_signal, - &seat->output_heads_listener); - - wl_list_init(&seat->devices_list); - - return seat; -} - -static void -udev_seat_destroy(struct udev_seat *seat) -{ - struct weston_keyboard *keyboard = - weston_seat_get_keyboard(&seat->base); - - if (keyboard) - notify_keyboard_focus_out(&seat->base); - - udev_seat_remove_devices(seat); - weston_seat_release(&seat->base); - wl_list_remove(&seat->output_create_listener.link); - wl_list_remove(&seat->output_heads_listener.link); - free(seat); -} - -struct udev_seat * -udev_seat_get_named(struct udev_input *input, const char *seat_name) -{ - struct udev_seat *seat; - - wl_list_for_each(seat, &input->compositor->seat_list, base.link) { - if (strcmp(seat->base.seat_name, seat_name) == 0) - return seat; - } - - return udev_seat_create(input, seat_name); -} diff --git a/libweston/libinput-seat.h b/libweston/libinput-seat.h deleted file mode 100644 index 315980dc..00000000 --- a/libweston/libinput-seat.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright © 2013 Intel Corporation - * Copyright © 2013 Jonas Ådahl - * - * 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. - */ - -#ifndef _LIBINPUT_SEAT_H_ -#define _LIBINPUT_SEAT_H_ - -#include "config.h" - -#include <libudev.h> - -#include <libweston/libweston.h> - -struct libinput_device; - -struct udev_seat { - struct weston_seat base; - struct wl_list devices_list; - struct wl_listener output_create_listener; - struct wl_listener output_heads_listener; -}; - -typedef void (*udev_configure_device_t)(struct weston_compositor *compositor, - struct libinput_device *device); - -struct udev_input { - struct libinput *libinput; - struct wl_event_source *libinput_source; - struct weston_compositor *compositor; - int suspended; - udev_configure_device_t configure_device; -}; - -int -udev_input_enable(struct udev_input *input); -void -udev_input_disable(struct udev_input *input); -int -udev_input_init(struct udev_input *input, - struct weston_compositor *c, - struct udev *udev, - const char *seat_id, - udev_configure_device_t configure_device); -void -udev_input_destroy(struct udev_input *input); - -struct udev_seat * -udev_seat_get_named(struct udev_input *u, - const char *seat_name); - -#endif diff --git a/libweston/libweston-internal.h b/libweston/libweston-internal.h deleted file mode 100644 index 66c38e86..00000000 --- a/libweston/libweston-internal.h +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2017, 2018 General Electric Company - * Copyright © 2012, 2017-2019 Collabora, Ltd. - * - * 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. - */ - -#ifndef LIBWESTON_INTERNAL_H -#define LIBWESTON_INTERNAL_H - -/* - * This is the internal (private) part of libweston. All symbols found here - * are, and should be only (with a few exceptions) used within the internal - * parts of libweston. Notable exception(s) include a few files in tests/ that - * need access to these functions, screen-share file from compositor/ and those - * remoting/. Those will require some further fixing as to avoid including this - * private header. - * - * Eventually, these symbols should reside naturally into their own scope. New - * features should either provide their own (internal) header or use this one. - */ - - -/* weston_buffer */ - -void -weston_buffer_send_server_error(struct weston_buffer *buffer, - const char *msg); -void -weston_buffer_reference(struct weston_buffer_reference *ref, - struct weston_buffer *buffer); - -void -weston_buffer_release_move(struct weston_buffer_release_reference *dest, - struct weston_buffer_release_reference *src); - -void -weston_buffer_release_reference(struct weston_buffer_release_reference *ref, - struct weston_buffer_release *buf_release); - -/* weston_bindings */ -void -weston_binding_list_destroy_all(struct wl_list *list); - -/* weston_compositor */ - -void -touch_calibrator_mode_changed(struct weston_compositor *compositor); - -int -noop_renderer_init(struct weston_compositor *ec); - -void -weston_compositor_add_head(struct weston_compositor *compositor, - struct weston_head *head); -void -weston_compositor_add_pending_output(struct weston_output *output, - struct weston_compositor *compositor); -bool -weston_compositor_import_dmabuf(struct weston_compositor *compositor, - struct linux_dmabuf_buffer *buffer); -bool -weston_compositor_dmabuf_can_scanout(struct weston_compositor *compositor, - struct linux_dmabuf_buffer *buffer); -void -weston_compositor_offscreen(struct weston_compositor *compositor); - -char * -weston_compositor_print_scene_graph(struct weston_compositor *ec); - -void -weston_compositor_read_presentation_clock( - const struct weston_compositor *compositor, - struct timespec *ts); - -int -weston_compositor_run_axis_binding(struct weston_compositor *compositor, - struct weston_pointer *pointer, - const struct timespec *time, - struct weston_pointer_axis_event *event); -void -weston_compositor_run_button_binding(struct weston_compositor *compositor, - struct weston_pointer *pointer, - const struct timespec *time, - uint32_t button, - enum wl_pointer_button_state value); -int -weston_compositor_run_debug_binding(struct weston_compositor *compositor, - struct weston_keyboard *keyboard, - const struct timespec *time, - uint32_t key, - enum wl_keyboard_key_state state); -void -weston_compositor_run_key_binding(struct weston_compositor *compositor, - struct weston_keyboard *keyboard, - const struct timespec *time, - uint32_t key, - enum wl_keyboard_key_state state); -void -weston_compositor_run_modifier_binding(struct weston_compositor *compositor, - struct weston_keyboard *keyboard, - enum weston_keyboard_modifier modifier, - enum wl_keyboard_key_state state); -void -weston_compositor_run_touch_binding(struct weston_compositor *compositor, - struct weston_touch *touch, - const struct timespec *time, - int touch_type); -void -weston_compositor_stack_plane(struct weston_compositor *ec, - struct weston_plane *plane, - struct weston_plane *above); -void -weston_compositor_set_touch_mode_normal(struct weston_compositor *compositor); - -void -weston_compositor_set_touch_mode_calib(struct weston_compositor *compositor); - -int -weston_compositor_set_presentation_clock(struct weston_compositor *compositor, - clockid_t clk_id); -int -weston_compositor_set_presentation_clock_software( - struct weston_compositor *compositor); -void -weston_compositor_shutdown(struct weston_compositor *ec); - -void -weston_compositor_xkb_destroy(struct weston_compositor *ec); - -int -weston_input_init(struct weston_compositor *compositor); - -/* weston_output */ - -void -weston_output_disable_planes_incr(struct weston_output *output); - -void -weston_output_disable_planes_decr(struct weston_output *output); - -/* weston_plane */ - -void -weston_plane_init(struct weston_plane *plane, - struct weston_compositor *ec, - int32_t x, int32_t y); -void -weston_plane_release(struct weston_plane *plane); - -/* weston_seat */ - -struct clipboard * -clipboard_create(struct weston_seat *seat); - -void -weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec, - const char *seat_name); - -void -weston_seat_repick(struct weston_seat *seat); - -void -weston_seat_release(struct weston_seat *seat); - -void -weston_seat_send_selection(struct weston_seat *seat, struct wl_client *client); - -void -weston_seat_init_pointer(struct weston_seat *seat); - -int -weston_seat_init_keyboard(struct weston_seat *seat, struct xkb_keymap *keymap); - -void -weston_seat_init_touch(struct weston_seat *seat); - -void -weston_seat_release_keyboard(struct weston_seat *seat); - -void -weston_seat_release_pointer(struct weston_seat *seat); - -void -weston_seat_release_touch(struct weston_seat *seat); - -void -weston_seat_update_keymap(struct weston_seat *seat, struct xkb_keymap *keymap); - -void -wl_data_device_set_keyboard_focus(struct weston_seat *seat); - -/* weston_pointer */ - -void -weston_pointer_clamp(struct weston_pointer *pointer, - wl_fixed_t *fx, wl_fixed_t *fy); -void -weston_pointer_set_default_grab(struct weston_pointer *pointer, - const struct weston_pointer_grab_interface *interface); - -void -weston_pointer_constraint_destroy(struct weston_pointer_constraint *constraint); - -/* weston_keyboard */ -bool -weston_keyboard_has_focus_resource(struct weston_keyboard *keyboard); - -/* weston_touch */ - -struct weston_touch_device * -weston_touch_create_touch_device(struct weston_touch *touch, - const char *syspath, - void *backend_data, - const struct weston_touch_device_ops *ops); - -void -weston_touch_device_destroy(struct weston_touch_device *device); - -bool -weston_touch_has_focus_resource(struct weston_touch *touch); - -int -weston_touch_start_drag(struct weston_touch *touch, - struct weston_data_source *source, - struct weston_surface *icon, - struct wl_client *client); - - -/* weston_touch_device */ - -bool -weston_touch_device_can_calibrate(struct weston_touch_device *device); - -/* weston_surface */ -void -weston_surface_to_buffer_float(struct weston_surface *surface, - float x, float y, float *bx, float *by); -pixman_box32_t -weston_surface_to_buffer_rect(struct weston_surface *surface, - pixman_box32_t rect); - -void -weston_surface_to_buffer_region(struct weston_surface *surface, - pixman_region32_t *surface_region, - pixman_region32_t *buffer_region); -void -weston_surface_schedule_repaint(struct weston_surface *surface); - -/* weston_spring */ - -void -weston_spring_init(struct weston_spring *spring, - double k, double current, double target); -int -weston_spring_done(struct weston_spring *spring); - -void -weston_spring_update(struct weston_spring *spring, const struct timespec *time); - -/* weston_view */ - -void -weston_view_to_global_fixed(struct weston_view *view, - wl_fixed_t sx, wl_fixed_t sy, - wl_fixed_t *x, wl_fixed_t *y); -void -weston_view_from_global_float(struct weston_view *view, - float x, float y, float *vx, float *vy); -bool -weston_view_is_opaque(struct weston_view *ev, pixman_region32_t *region); - -bool -weston_view_has_valid_buffer(struct weston_view *ev); - -bool -weston_view_matches_output_entirely(struct weston_view *ev, - struct weston_output *output); -void -weston_view_move_to_plane(struct weston_view *view, - struct weston_plane *plane); - -void -weston_transformed_coord(int width, int height, - enum wl_output_transform transform, - int32_t scale, - float sx, float sy, float *bx, float *by); -pixman_box32_t -weston_transformed_rect(int width, int height, - enum wl_output_transform transform, - int32_t scale, - pixman_box32_t rect); -void -weston_transformed_region(int width, int height, - enum wl_output_transform transform, - int32_t scale, - pixman_region32_t *src, pixman_region32_t *dest); -void -weston_matrix_transform_region(pixman_region32_t *dest, - struct weston_matrix *matrix, - pixman_region32_t *src); - -/* protected_surface */ -void -weston_protected_surface_send_event(struct protected_surface *psurface, - enum weston_hdcp_protection protection); - -/* others */ -int -wl_data_device_manager_init(struct wl_display *display); - -#endif diff --git a/libweston/linux-dmabuf.c b/libweston/linux-dmabuf.c deleted file mode 100644 index 796e9826..00000000 --- a/libweston/linux-dmabuf.c +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright © 2014, 2015 Collabora, Ltd. - * - * 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 <assert.h> -#include <stdint.h> -#include <unistd.h> -#include <sys/types.h> - -#include <libweston/libweston.h> -#include "linux-dmabuf.h" -#include "linux-dmabuf-unstable-v1-server-protocol.h" -#include "libweston-internal.h" - -static void -linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer *buffer) -{ - int i; - - for (i = 0; i < buffer->attributes.n_planes; i++) { - close(buffer->attributes.fd[i]); - buffer->attributes.fd[i] = -1; - } - - buffer->attributes.n_planes = 0; - free(buffer); -} - -static void -destroy_params(struct wl_resource *params_resource) -{ - struct linux_dmabuf_buffer *buffer; - - buffer = wl_resource_get_user_data(params_resource); - - if (!buffer) - return; - - linux_dmabuf_buffer_destroy(buffer); -} - -static void -params_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -params_add(struct wl_client *client, - struct wl_resource *params_resource, - int32_t name_fd, - uint32_t plane_idx, - uint32_t offset, - uint32_t stride, - uint32_t modifier_hi, - uint32_t modifier_lo) -{ - struct linux_dmabuf_buffer *buffer; - - buffer = wl_resource_get_user_data(params_resource); - if (!buffer) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, - "params was already used to create a wl_buffer"); - close(name_fd); - return; - } - - assert(buffer->params_resource == params_resource); - assert(!buffer->buffer_resource); - - if (plane_idx >= MAX_DMABUF_PLANES) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, - "plane index %u is too high", plane_idx); - close(name_fd); - return; - } - - if (buffer->attributes.fd[plane_idx] != -1) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET, - "a dmabuf has already been added for plane %u", - plane_idx); - close(name_fd); - return; - } - - buffer->attributes.fd[plane_idx] = name_fd; - buffer->attributes.offset[plane_idx] = offset; - buffer->attributes.stride[plane_idx] = stride; - - if (wl_resource_get_version(params_resource) < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) - buffer->attributes.modifier[plane_idx] = DRM_FORMAT_MOD_INVALID; - else - buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) | - modifier_lo; - - buffer->attributes.n_planes++; -} - -static void -linux_dmabuf_wl_buffer_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct wl_buffer_interface linux_dmabuf_buffer_implementation = { - linux_dmabuf_wl_buffer_destroy -}; - -static void -destroy_linux_dmabuf_wl_buffer(struct wl_resource *resource) -{ - struct linux_dmabuf_buffer *buffer; - - buffer = wl_resource_get_user_data(resource); - assert(buffer->buffer_resource == resource); - assert(!buffer->params_resource); - - if (buffer->user_data_destroy_func) - buffer->user_data_destroy_func(buffer); - - linux_dmabuf_buffer_destroy(buffer); -} - -static void -params_create_common(struct wl_client *client, - struct wl_resource *params_resource, - uint32_t buffer_id, - int32_t width, - int32_t height, - uint32_t format, - uint32_t flags) -{ - struct linux_dmabuf_buffer *buffer; - int i; - - buffer = wl_resource_get_user_data(params_resource); - - if (!buffer) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, - "params was already used to create a wl_buffer"); - return; - } - - assert(buffer->params_resource == params_resource); - assert(!buffer->buffer_resource); - - /* Switch the linux_dmabuf_buffer object from params resource to - * eventually wl_buffer resource. - */ - wl_resource_set_user_data(buffer->params_resource, NULL); - buffer->params_resource = NULL; - - if (!buffer->attributes.n_planes) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, - "no dmabuf has been added to the params"); - goto err_out; - } - - /* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */ - for (i = 0; i < buffer->attributes.n_planes; i++) { - if (buffer->attributes.fd[i] == -1) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, - "no dmabuf has been added for plane %i", i); - goto err_out; - } - } - - buffer->attributes.width = width; - buffer->attributes.height = height; - buffer->attributes.format = format; - buffer->attributes.flags = flags; - - if (width < 1 || height < 1) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS, - "invalid width %d or height %d", width, height); - goto err_out; - } - - for (i = 0; i < buffer->attributes.n_planes; i++) { - off_t size; - - if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, - "size overflow for plane %i", i); - goto err_out; - } - - if (i == 0 && - (uint64_t) buffer->attributes.offset[i] + - (uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, - "size overflow for plane %i", i); - goto err_out; - } - - /* Don't report an error as it might be caused - * by the kernel not supporting seeking on dmabuf */ - size = lseek(buffer->attributes.fd[i], 0, SEEK_END); - if (size == -1) - continue; - - if (buffer->attributes.offset[i] >= size) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, - "invalid offset %i for plane %i", - buffer->attributes.offset[i], i); - goto err_out; - } - - if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, - "invalid stride %i for plane %i", - buffer->attributes.stride[i], i); - goto err_out; - } - - /* Only valid for first plane as other planes might be - * sub-sampled according to fourcc format */ - if (i == 0 && - buffer->attributes.offset[i] + buffer->attributes.stride[i] * height > size) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, - "invalid buffer stride or height for plane %i", i); - goto err_out; - } - } - - if (buffer->direct_display) { - if (!weston_compositor_dmabuf_can_scanout(buffer->compositor, - buffer)) - goto err_failed; - - goto avoid_gpu_import; - } - - if (!weston_compositor_import_dmabuf(buffer->compositor, buffer)) - goto err_failed; - -avoid_gpu_import: - buffer->buffer_resource = wl_resource_create(client, - &wl_buffer_interface, - 1, buffer_id); - if (!buffer->buffer_resource) { - wl_resource_post_no_memory(params_resource); - goto err_buffer; - } - - wl_resource_set_implementation(buffer->buffer_resource, - &linux_dmabuf_buffer_implementation, - buffer, destroy_linux_dmabuf_wl_buffer); - - /* send 'created' event when the request is not for an immediate - * import, ie buffer_id is zero */ - if (buffer_id == 0) - zwp_linux_buffer_params_v1_send_created(params_resource, - buffer->buffer_resource); - - return; - -err_buffer: - if (buffer->user_data_destroy_func) - buffer->user_data_destroy_func(buffer); - -err_failed: - if (buffer_id == 0) - zwp_linux_buffer_params_v1_send_failed(params_resource); - else - /* since the behavior is left implementation defined by the - * protocol in case of create_immed failure due to an unknown cause, - * we choose to treat it as a fatal error and immediately kill the - * client instead of creating an invalid handle and waiting for it - * to be used. - */ - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER, - "importing the supplied dmabufs failed"); - -err_out: - linux_dmabuf_buffer_destroy(buffer); -} - -static void -params_create(struct wl_client *client, - struct wl_resource *params_resource, - int32_t width, - int32_t height, - uint32_t format, - uint32_t flags) -{ - params_create_common(client, params_resource, 0, width, height, format, - flags); -} - -static void -params_create_immed(struct wl_client *client, - struct wl_resource *params_resource, - uint32_t buffer_id, - int32_t width, - int32_t height, - uint32_t format, - uint32_t flags) -{ - params_create_common(client, params_resource, buffer_id, width, height, - format, flags); -} - -static const struct zwp_linux_buffer_params_v1_interface -zwp_linux_buffer_params_implementation = { - params_destroy, - params_add, - params_create, - params_create_immed -}; - -static void -linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -linux_dmabuf_create_params(struct wl_client *client, - struct wl_resource *linux_dmabuf_resource, - uint32_t params_id) -{ - struct weston_compositor *compositor; - struct linux_dmabuf_buffer *buffer; - uint32_t version; - int i; - - version = wl_resource_get_version(linux_dmabuf_resource); - compositor = wl_resource_get_user_data(linux_dmabuf_resource); - - buffer = zalloc(sizeof *buffer); - if (!buffer) - goto err_out; - - for (i = 0; i < MAX_DMABUF_PLANES; i++) - buffer->attributes.fd[i] = -1; - - buffer->compositor = compositor; - buffer->params_resource = - wl_resource_create(client, - &zwp_linux_buffer_params_v1_interface, - version, params_id); - buffer->direct_display = false; - if (!buffer->params_resource) - goto err_dealloc; - - wl_resource_set_implementation(buffer->params_resource, - &zwp_linux_buffer_params_implementation, - buffer, destroy_params); - - return; - -err_dealloc: - free(buffer); - -err_out: - wl_resource_post_no_memory(linux_dmabuf_resource); -} - -/** Get the linux_dmabuf_buffer from a wl_buffer resource - * - * If the given wl_buffer resource was created through the linux_dmabuf - * protocol interface, returns the linux_dmabuf_buffer object. This can - * be used as a type check for a wl_buffer. - * - * \param resource A wl_buffer resource. - * \return The linux_dmabuf_buffer if it exists, or NULL otherwise. - */ -WL_EXPORT struct linux_dmabuf_buffer * -linux_dmabuf_buffer_get(struct wl_resource *resource) -{ - struct linux_dmabuf_buffer *buffer; - - if (!resource) - return NULL; - - if (!wl_resource_instance_of(resource, &wl_buffer_interface, - &linux_dmabuf_buffer_implementation)) - return NULL; - - buffer = wl_resource_get_user_data(resource); - assert(buffer); - assert(!buffer->params_resource); - assert(buffer->buffer_resource == resource); - - return buffer; -} - -/** Set renderer-private data - * - * Set the user data for the linux_dmabuf_buffer. It is invalid to overwrite - * a non-NULL user data with a new non-NULL pointer. This is meant to - * protect against renderers fighting over linux_dmabuf_buffer user data - * ownership. - * - * The renderer-private data is usually set from the - * weston_renderer::import_dmabuf hook. - * - * \param buffer The linux_dmabuf_buffer object to set for. - * \param data The new renderer-private data pointer. - * \param func Destructor function to be called for the renderer-private - * data when the linux_dmabuf_buffer gets destroyed. - * - * \sa weston_compositor_import_dmabuf - */ -WL_EXPORT void -linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer, - void *data, - dmabuf_user_data_destroy_func func) -{ - assert(data == NULL || buffer->user_data == NULL); - - buffer->user_data = data; - buffer->user_data_destroy_func = func; -} - -/** Get renderer-private data - * - * Get the user data from the linux_dmabuf_buffer. - * - * \param buffer The linux_dmabuf_buffer to query. - * \return Renderer-private data pointer. - * - * \sa linux_dmabuf_buffer_set_user_data - */ -WL_EXPORT void * -linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer) -{ - return buffer->user_data; -} - -static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = { - linux_dmabuf_destroy, - linux_dmabuf_create_params -}; - -static void -bind_linux_dmabuf(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct weston_compositor *compositor = data; - struct wl_resource *resource; - int *formats = NULL; - uint64_t *modifiers = NULL; - int num_formats, num_modifiers; - uint64_t modifier_invalid = DRM_FORMAT_MOD_INVALID; - int i, j; - - resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface, - version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, &linux_dmabuf_implementation, - compositor, NULL); - - /* - * Use EGL_EXT_image_dma_buf_import_modifiers to query and advertise - * format/modifier codes. - */ - compositor->renderer->query_dmabuf_formats(compositor, &formats, - &num_formats); - - for (i = 0; i < num_formats; i++) { - compositor->renderer->query_dmabuf_modifiers(compositor, - formats[i], - &modifiers, - &num_modifiers); - - /* send DRM_FORMAT_MOD_INVALID token when no modifiers are supported - * for this format */ - if (num_modifiers == 0) { - num_modifiers = 1; - modifiers = &modifier_invalid; - } - for (j = 0; j < num_modifiers; j++) { - if (version >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) { - uint32_t modifier_lo = modifiers[j] & 0xFFFFFFFF; - uint32_t modifier_hi = modifiers[j] >> 32; - zwp_linux_dmabuf_v1_send_modifier(resource, - formats[i], - modifier_hi, - modifier_lo); - } else if (modifiers[j] == DRM_FORMAT_MOD_LINEAR || - modifiers == &modifier_invalid) { - zwp_linux_dmabuf_v1_send_format(resource, - formats[i]); - } - } - if (modifiers != &modifier_invalid) - free(modifiers); - } - free(formats); -} - -/** Advertise linux_dmabuf support - * - * Calling this initializes the zwp_linux_dmabuf protocol support, so that - * the interface will be advertised to clients. Essentially it creates a - * global. Do not call this function multiple times in the compositor's - * lifetime. There is no way to deinit explicitly, globals will be reaped - * when the wl_display gets destroyed. - * - * \param compositor The compositor to init for. - * \return Zero on success, -1 on failure. - */ -WL_EXPORT int -linux_dmabuf_setup(struct weston_compositor *compositor) -{ - if (!wl_global_create(compositor->wl_display, - &zwp_linux_dmabuf_v1_interface, 3, - compositor, bind_linux_dmabuf)) - return -1; - - return 0; -} - -/** Resolve an internal compositor error by disconnecting the client. - * - * This function is used in cases when the dmabuf-based wl_buffer - * turns out unusable and there is no fallback path. This is used by - * renderers which are the fallback path in the first place. - * - * It is possible the fault is caused by a compositor bug, the underlying - * graphics stack bug or normal behaviour, or perhaps a client mistake. - * In any case, the options are to either composite garbage or nothing, - * or disconnect the client. This is a helper function for the latter. - * - * The error is sent as an INVALID_OBJECT error on the client's wl_display. - * - * \param buffer The linux_dmabuf_buffer that is unusable. - * \param msg A custom error message attached to the protocol error. - */ -WL_EXPORT void -linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer, - const char *msg) -{ - struct wl_client *client; - struct wl_resource *display_resource; - uint32_t id; - - assert(buffer->buffer_resource); - id = wl_resource_get_id(buffer->buffer_resource); - client = wl_resource_get_client(buffer->buffer_resource); - display_resource = wl_client_get_object(client, 1); - - assert(display_resource); - wl_resource_post_error(display_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "linux_dmabuf server error with " - "wl_buffer@%u: %s", id, msg); -} diff --git a/libweston/linux-dmabuf.h b/libweston/linux-dmabuf.h deleted file mode 100644 index 926dd9e0..00000000 --- a/libweston/linux-dmabuf.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright © 2014, 2015 Collabora, Ltd. - * - * 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. - */ - -#ifndef WESTON_LINUX_DMABUF_H -#define WESTON_LINUX_DMABUF_H - -#include <stdint.h> - -#define MAX_DMABUF_PLANES 4 -#ifndef DRM_FORMAT_MOD_INVALID -#define DRM_FORMAT_MOD_INVALID ((1ULL<<56) - 1) -#endif -#ifndef DRM_FORMAT_MOD_LINEAR -#define DRM_FORMAT_MOD_LINEAR 0 -#endif - -struct linux_dmabuf_buffer; -typedef void (*dmabuf_user_data_destroy_func)( - struct linux_dmabuf_buffer *buffer); - -struct dmabuf_attributes { - int32_t width; - int32_t height; - uint32_t format; - uint32_t flags; /* enum zlinux_buffer_params_flags */ - int n_planes; - int fd[MAX_DMABUF_PLANES]; - uint32_t offset[MAX_DMABUF_PLANES]; - uint32_t stride[MAX_DMABUF_PLANES]; - uint64_t modifier[MAX_DMABUF_PLANES]; -}; - -struct linux_dmabuf_buffer { - struct wl_resource *buffer_resource; - struct wl_resource *params_resource; - struct weston_compositor *compositor; - struct dmabuf_attributes attributes; - - void *user_data; - dmabuf_user_data_destroy_func user_data_destroy_func; - - /* XXX: - * - * Add backend private data. This would be for the backend - * to do all additional imports it might ever use in advance. - * The basic principle, even if not implemented in drivers today, - * is that dmabufs are first attached, but the actual allocation - * is deferred to first use. This would allow the exporter and all - * attachers to agree on how to allocate. - * - * The DRM backend would use this to create drmFBs for each - * dmabuf_buffer, just in case at some point it would become - * feasible to scan it out directly. This would improve the - * possibilities to successfully scan out, avoiding compositing. - */ - - /**< marked as scan-out capable, avoids any composition */ - bool direct_display; -}; - -int -linux_dmabuf_setup(struct weston_compositor *compositor); - -int -weston_direct_display_setup(struct weston_compositor *compositor); - -struct linux_dmabuf_buffer * -linux_dmabuf_buffer_get(struct wl_resource *resource); - -void -linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer, - void *data, - dmabuf_user_data_destroy_func func); -void * -linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer); - -void -linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer, - const char *msg); - -#endif /* WESTON_LINUX_DMABUF_H */ diff --git a/libweston/linux-explicit-synchronization.c b/libweston/linux-explicit-synchronization.c deleted file mode 100644 index 4b473839..00000000 --- a/libweston/linux-explicit-synchronization.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright © 2018 Collabora, Ltd. - * - * 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 <assert.h> -#include <inttypes.h> - -#include <libweston/libweston.h> -#include "linux-explicit-synchronization.h" -#include "linux-explicit-synchronization-unstable-v1-server-protocol.h" -#include "linux-sync-file.h" -#include "shared/fd-util.h" -#include "libweston-internal.h" - -static void -destroy_linux_buffer_release(struct wl_resource *resource) -{ - struct weston_buffer_release *buffer_release = - wl_resource_get_user_data(resource); - - fd_clear(&buffer_release->fence_fd); - free(buffer_release); -} - -static void -destroy_linux_surface_synchronization(struct wl_resource *resource) -{ - struct weston_surface *surface = - wl_resource_get_user_data(resource); - - if (surface) { - fd_clear(&surface->pending.acquire_fence_fd); - surface->synchronization_resource = NULL; - } -} - -static void -linux_surface_synchronization_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -linux_surface_synchronization_set_acquire_fence(struct wl_client *client, - struct wl_resource *resource, - int32_t fd) -{ - struct weston_surface *surface = wl_resource_get_user_data(resource); - - if (!surface) { - wl_resource_post_error( - resource, - ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_SURFACE, - "surface no longer exists"); - goto err; - } - - if (!linux_sync_file_is_valid(fd)) { - wl_resource_post_error( - resource, - ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_INVALID_FENCE, - "invalid fence fd"); - goto err; - } - - if (surface->pending.acquire_fence_fd != -1) { - wl_resource_post_error( - resource, - ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_DUPLICATE_FENCE, - "already have a fence fd"); - goto err; - } - - fd_update(&surface->pending.acquire_fence_fd, fd); - - return; - -err: - close(fd); -} - -static void -linux_surface_synchronization_get_release(struct wl_client *client, - struct wl_resource *resource, - uint32_t id) -{ - struct weston_surface *surface = - wl_resource_get_user_data(resource); - struct weston_buffer_release *buffer_release; - - if (!surface) { - wl_resource_post_error( - resource, - ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_SURFACE, - "surface no longer exists"); - return; - } - - if (surface->pending.buffer_release_ref.buffer_release) { - wl_resource_post_error( - resource, - ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_DUPLICATE_RELEASE, - "already has a buffer release"); - return; - } - - buffer_release = zalloc(sizeof *buffer_release); - if (buffer_release == NULL) - goto err_alloc; - - buffer_release->fence_fd = -1; - buffer_release->resource = - wl_resource_create(client, - &zwp_linux_buffer_release_v1_interface, - wl_resource_get_version(resource), id); - if (!buffer_release->resource) - goto err_create; - - wl_resource_set_implementation(buffer_release->resource, NULL, - buffer_release, - destroy_linux_buffer_release); - - weston_buffer_release_reference(&surface->pending.buffer_release_ref, - buffer_release); - - return; - -err_create: - free(buffer_release); - -err_alloc: - wl_client_post_no_memory(client); - -} - -const struct zwp_linux_surface_synchronization_v1_interface -linux_surface_synchronization_implementation = { - linux_surface_synchronization_destroy, - linux_surface_synchronization_set_acquire_fence, - linux_surface_synchronization_get_release, -}; - -static void -linux_explicit_synchronization_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -linux_explicit_synchronization_get_synchronization(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, - struct wl_resource *surface_resource) -{ - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - - if (surface->synchronization_resource) { - wl_resource_post_error( - resource, - ZWP_LINUX_EXPLICIT_SYNCHRONIZATION_V1_ERROR_SYNCHRONIZATION_EXISTS, - "wl_surface@%"PRIu32" already has a synchronization object", - wl_resource_get_id(surface_resource)); - return; - } - - surface->synchronization_resource = - wl_resource_create(client, - &zwp_linux_surface_synchronization_v1_interface, - wl_resource_get_version(resource), id); - if (!surface->synchronization_resource) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(surface->synchronization_resource, - &linux_surface_synchronization_implementation, - surface, - destroy_linux_surface_synchronization); -} - -static const struct zwp_linux_explicit_synchronization_v1_interface -linux_explicit_synchronization_implementation = { - linux_explicit_synchronization_destroy, - linux_explicit_synchronization_get_synchronization -}; - -static void -bind_linux_explicit_synchronization(struct wl_client *client, - void *data, uint32_t version, - uint32_t id) -{ - struct weston_compositor *compositor = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, - &zwp_linux_explicit_synchronization_v1_interface, - version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, - &linux_explicit_synchronization_implementation, - compositor, NULL); -} - -/** Advertise linux_explicit_synchronization support - * - * Calling this initializes the zwp_linux_explicit_synchronization_v1 - * protocol support, so that the interface will be advertised to clients. - * Essentially it creates a global. Do not call this function multiple times - * in the compositor's lifetime. There is no way to deinit explicitly, globals - * will be reaped when the wl_display gets destroyed. - * - * \param compositor The compositor to init for. - * \return Zero on success, -1 on failure. - */ -WL_EXPORT int -linux_explicit_synchronization_setup(struct weston_compositor *compositor) -{ - if (!wl_global_create(compositor->wl_display, - &zwp_linux_explicit_synchronization_v1_interface, - 2, compositor, - bind_linux_explicit_synchronization)) - return -1; - - return 0; -} - -/** Resolve an internal compositor error by disconnecting the client. - * - * This function is used in cases when explicit synchronization - * turns out to be unusable and there is no fallback path. - * - * It is possible the fault is caused by a compositor bug, the underlying - * graphics stack bug or normal behaviour, or perhaps a client mistake. - * In any case, the options are to either composite garbage or nothing, - * or disconnect the client. This is a helper function for the latter. - * - * The error is sent as an INVALID_OBJECT error on the client's wl_display. - * - * \param resource The explicit synchronization related resource that is unusable. - * \param msg A custom error message attached to the protocol error. - */ -WL_EXPORT void -linux_explicit_synchronization_send_server_error(struct wl_resource *resource, - const char *msg) -{ - uint32_t id = wl_resource_get_id(resource); - const char *class = wl_resource_get_class(resource); - struct wl_client *client = wl_resource_get_client(resource); - struct wl_resource *display_resource = wl_client_get_object(client, 1); - - assert(display_resource); - wl_resource_post_error(display_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "linux_explicit_synchronization server error " - "with %s@%"PRIu32": %s", - class, id, msg); -} diff --git a/libweston/linux-explicit-synchronization.h b/libweston/linux-explicit-synchronization.h deleted file mode 100644 index 55022879..00000000 --- a/libweston/linux-explicit-synchronization.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2018 Collabora, Ltd. - * - * 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. - */ - -#ifndef WESTON_LINUX_EXPLICIT_SYNCHRONIZATION_H -#define WESTON_LINUX_EXPLICIT_SYNCHRONIZATION_H - -struct weston_compositor; -struct wl_resource; - -int -linux_explicit_synchronization_setup(struct weston_compositor *compositor); - -void -linux_explicit_synchronization_send_server_error(struct wl_resource *resource, - const char *msg); - -#endif /* WESTON_LINUX_EXPLICIT_SYNCHRONIZATION */ diff --git a/libweston/linux-sync-file-uapi.h b/libweston/linux-sync-file-uapi.h deleted file mode 100644 index cd30665f..00000000 --- a/libweston/linux-sync-file-uapi.h +++ /dev/null @@ -1,30 +0,0 @@ -/* Sync file Linux kernel UAPI */ - -#ifndef WESTON_LINUX_SYNC_FILE_UAPI_H -#define WESTON_LINUX_SYNC_FILE_UAPI_H - -#include <linux/ioctl.h> -#include <linux/types.h> - -struct sync_fence_info { - char obj_name[32]; - char driver_name[32]; - __s32 status; - __u32 flags; - __u64 timestamp_ns; -}; - -struct sync_file_info { - char name[32]; - __s32 status; - __u32 flags; - __u32 num_fences; - __u32 pad; - - __u64 sync_fence_info; -}; - -#define SYNC_IOC_MAGIC '>' -#define SYNC_IOC_FILE_INFO _IOWR(SYNC_IOC_MAGIC, 4, struct sync_file_info) - -#endif /* WESTON_LINUX_SYNC_FILE_UAPI_H */ diff --git a/libweston/linux-sync-file.c b/libweston/linux-sync-file.c deleted file mode 100644 index 9f5313cc..00000000 --- a/libweston/linux-sync-file.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright © 2018 Collabora, Ltd - * - * 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 <assert.h> -#include <errno.h> -#include <poll.h> -#include <stddef.h> -#include <sys/ioctl.h> -#include <wayland-server-core.h> - -#ifdef HAVE_LINUX_SYNC_FILE_H -#include <linux/sync_file.h> -#else -#include "linux-sync-file-uapi.h" -#endif - -#include "linux-sync-file.h" -#include "shared/timespec-util.h" - -/* Check that a file descriptor represents a valid sync file - * - * \param fd[in] a file descriptor - * \return true if fd is a valid sync file, false otherwise - */ -bool -linux_sync_file_is_valid(int fd) -{ - struct sync_file_info file_info = { { 0 } }; - - if (ioctl(fd, SYNC_IOC_FILE_INFO, &file_info) < 0) - return false; - - return file_info.num_fences > 0; -} - -/* Read the timestamp stored in a sync file - * - * \param fd[in] fd a file descriptor for a sync file - * \param ts[out] the timespec struct to fill with the timestamp - * \return 0 if a timestamp was read, -1 on error - */ -WL_EXPORT int -weston_linux_sync_file_read_timestamp(int fd, struct timespec *ts) -{ - struct sync_file_info file_info = { { 0 } }; - struct sync_fence_info fence_info = { { 0 } }; - - assert(ts != NULL); - - file_info.sync_fence_info = (uint64_t)(uintptr_t)&fence_info; - file_info.num_fences = 1; - - if (ioctl(fd, SYNC_IOC_FILE_INFO, &file_info) < 0) - return -1; - - timespec_from_nsec(ts, fence_info.timestamp_ns); - - return 0; -} diff --git a/libweston/linux-sync-file.h b/libweston/linux-sync-file.h deleted file mode 100644 index 9746d7ba..00000000 --- a/libweston/linux-sync-file.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © 2018 Collabora, Ltd - * - * 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. - */ - -#ifndef WESTON_LINUX_SYNC_FILE_H -#define WESTON_LINUX_SYNC_FILE_H - -#include <sys/time.h> -#include <stdbool.h> - -bool -linux_sync_file_is_valid(int fd); - -int -weston_linux_sync_file_read_timestamp(int fd, struct timespec *ts); - -#endif /* WESTON_LINUX_SYNC_FILE_H */ diff --git a/libweston/log.c b/libweston/log.c deleted file mode 100644 index 6ce9454b..00000000 --- a/libweston/log.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright © 2012 Martin Minarik - * - * 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 <stdio.h> -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> -#include <sys/time.h> -#include <time.h> - -#include <wayland-util.h> - -#include <libweston/libweston.h> - -/** - * \defgroup wlog weston-logging - */ - -static int -default_log_handler(const char *fmt, va_list ap); - -/** Needs to be set, defaults to default_log_handler - * - * \ingroup wlog - */ -static log_func_t log_handler = default_log_handler; - -/** Needs to be set, defaults to default_log_handler - * - * \ingroup wlog - */ -static log_func_t log_continue_handler = default_log_handler; - -/** Sentinel log message handler - * - * This function is used as the default handler for log messages. It - * exists only to issue a noisy reminder to the user that a real handler - * must be installed prior to issuing logging calls. The process is - * immediately aborted after the reminder is printed. - * - * \param fmt The format string. Ignored. - * \param ap The variadic argument list. Ignored. - * - * \ingroup wlog - */ -static int -default_log_handler(const char *fmt, va_list ap) -{ - fprintf(stderr, "weston_log_set_handler() must be called before using of weston_log().\n"); - abort(); -} - -/** Install the log handler - * - * The given functions will be called to output text as passed to the - * \a weston_log and \a weston_log_continue functions. - * - * \param log The log function. This function will be called when - * \a weston_log is called, and should begin a new line, - * with user defined line headers, if any. - * \param cont The continue log function. This function will be called - * when \a weston_log_continue is called, and should append - * its output to the current line, without any header or - * other content in between. - * - * \ingroup wlog - */ -WL_EXPORT void -weston_log_set_handler(log_func_t log, log_func_t cont) -{ - log_handler = log; - log_continue_handler = cont; -} - -/** weston_vlog calls log_handler - * \ingroup wlog - */ -WL_EXPORT int -weston_vlog(const char *fmt, va_list ap) -{ - return log_handler(fmt, ap); -} - -/** printf() equivalent in weston compositor. - * - * \rststar - * .. note:: - * - * Needs :var:`log_handler` to be set-up! - * \endrststar - * - * \ingroup wlog - */ -WL_EXPORT int -weston_log(const char *fmt, ...) -{ - int l; - va_list argp; - - va_start(argp, fmt); - l = weston_vlog(fmt, argp); - va_end(argp); - - return l; -} - -/** weston_vlog_continue calls log_continue_handler - * - * \ingroup wlog - */ -WL_EXPORT int -weston_vlog_continue(const char *fmt, va_list argp) -{ - return log_continue_handler(fmt, argp); -} - -/** weston_log_continue - * - * \ingroup wlog - */ -WL_EXPORT int -weston_log_continue(const char *fmt, ...) -{ - int l; - va_list argp; - - va_start(argp, fmt); - l = weston_vlog_continue(fmt, argp); - va_end(argp); - - return l; -} diff --git a/libweston/meson.build b/libweston/meson.build deleted file mode 100644 index d989b287..00000000 --- a/libweston/meson.build +++ /dev/null @@ -1,243 +0,0 @@ -deps_libweston = [ - dep_wayland_server, - dep_pixman, - dep_libm, - dep_libdl, - dep_libdrm_headers, - dep_xkbcommon, - dep_matrix_c -] -srcs_libweston = [ - git_version_h, - 'animation.c', - 'bindings.c', - 'clipboard.c', - 'compositor.c', - 'content-protection.c', - 'data-device.c', - 'input.c', - 'linux-dmabuf.c', - 'linux-explicit-synchronization.c', - 'linux-sync-file.c', - 'log.c', - 'noop-renderer.c', - 'pixel-formats.c', - 'pixman-renderer.c', - 'plugin-registry.c', - 'screenshooter.c', - 'timeline.c', - 'touch-calibration.c', - 'weston-log-wayland.c', - 'weston-log-file.c', - 'weston-log-flight-rec.c', - 'weston-log.c', - 'weston-direct-display.c', - 'zoom.c', - linux_dmabuf_unstable_v1_protocol_c, - linux_dmabuf_unstable_v1_server_protocol_h, - linux_explicit_synchronization_unstable_v1_protocol_c, - linux_explicit_synchronization_unstable_v1_server_protocol_h, - input_method_unstable_v1_protocol_c, - input_method_unstable_v1_server_protocol_h, - input_timestamps_unstable_v1_protocol_c, - input_timestamps_unstable_v1_server_protocol_h, - presentation_time_protocol_c, - presentation_time_server_protocol_h, - pointer_constraints_unstable_v1_protocol_c, - pointer_constraints_unstable_v1_server_protocol_h, - relative_pointer_unstable_v1_protocol_c, - relative_pointer_unstable_v1_server_protocol_h, - weston_screenshooter_protocol_c, - weston_screenshooter_server_protocol_h, - text_cursor_position_protocol_c, - text_cursor_position_server_protocol_h, - text_input_unstable_v1_protocol_c, - text_input_unstable_v1_server_protocol_h, - weston_touch_calibration_protocol_c, - weston_touch_calibration_server_protocol_h, - weston_content_protection_protocol_c, - weston_content_protection_server_protocol_h, - viewporter_protocol_c, - viewporter_server_protocol_h, - xdg_output_unstable_v1_protocol_c, - xdg_output_unstable_v1_server_protocol_h, - weston_debug_protocol_c, - weston_debug_server_protocol_h, - weston_direct_display_protocol_c, - weston_direct_display_server_protocol_h, -] - -if get_option('renderer-gl') - dep_egl = dependency('egl', required: false) - if not dep_egl.found() - error('libweston + gl-renderer requires egl which was not found. Or, you can use \'-Drenderer-gl=false\'.') - endif - deps_libweston += dep_egl -endif - -lib_weston = shared_library( - 'weston-@0@'.format(libweston_major), - srcs_libweston, - include_directories: common_inc, - install: true, - version: '0.0.@0@'.format(libweston_revision), - link_whole: lib_libshared, - dependencies: deps_libweston -) - -deps_for_libweston_users = [ - dep_wayland_server, - dep_pixman, - dep_xkbcommon, -] - -# For external users, like Weston. -dep_libweston_public = declare_dependency( - link_with: lib_weston, - include_directories: public_inc, - dependencies: deps_for_libweston_users -) - -# For internal users, like the backends. -dep_libweston_private = declare_dependency( - link_with: lib_weston, - include_directories: [ include_directories('.'), public_inc ], - dependencies: deps_for_libweston_users -) - -# XXX: We should be able to use dep_libweston_private.partial_dependency() instead -# of this, but a Meson bug makes it not work. It will be fixed with -# https://github.com/mesonbuild/meson/pull/5167 -# in hopefully Meson 0.51. -dep_libweston_private_h_deps = [] -foreach d : deps_for_libweston_users - dep_libweston_private_h_deps += d.partial_dependency(compile_args: true) -endforeach -dep_libweston_private_h = declare_dependency( - include_directories: [ include_directories('.'), public_inc ], - dependencies: dep_libweston_private_h_deps -) - -pkgconfig.generate( - lib_weston, - filebase: 'libweston-@0@'.format(libweston_major), - name: 'libweston API', - version: version_weston, - description: 'Header files for libweston compositors development', - requires_private: deps_for_libweston_users, - subdirs: dir_include_libweston -) - -pkgconfig.generate( - filebase: 'libweston-@0@-protocols'.format(libweston_major), - name: 'libWeston Protocols', - version: version_weston, - description: 'libWeston protocol files', - variables: [ - 'datarootdir=' + join_paths('${prefix}', get_option('datadir')), - 'pkgdatadir=' + join_paths('${pc_sysrootdir}${datarootdir}', dir_protocol_libweston) - ], - install_dir: dir_data_pc -) - -srcs_session_helper = [ - 'launcher-direct.c', - 'launcher-util.c', - 'launcher-weston-launch.c', -] -deps_session_helper = [ dep_libweston_private_h ] - -if get_option('backend-drm') - deps_session_helper += dep_libdrm -endif - -systemd_dep = dependency('', required: false) -if get_option('launcher-logind') - systemd_dep = dependency('libsystemd', version: '>= 209', required: false) - if systemd_dep.found() - config_h.set('HAVE_SYSTEMD_LOGIN_209', '1') - else - systemd_dep = dependency('libsystemd-login', version: '>= 198', required: false) - if not systemd_dep.found() - error('logind support requires libsystemd or libsystemd-login but neither was found. Or, you can use \'-Dlauncher-logind=false\'') - endif - endif - - dbus_dep = dependency('dbus-1', version: '>= 1.6', required: false) - if not dbus_dep.found() - error('logind support requires dbus-1 >= 1.6 which was not found. Or, you can use \'-Dlauncher-logind=false\'') - endif - - config_h.set('HAVE_DBUS', '1') - config_h.set('HAVE_SYSTEMD_LOGIN', '1') - - srcs_session_helper += [ - 'dbus.c', - 'launcher-logind.c', - ] - deps_session_helper += [ - dbus_dep, - systemd_dep, - ] -endif - -lib_session_helper = static_library( - 'session-helper', - srcs_session_helper, - include_directories: common_inc, - dependencies: deps_session_helper, - install: false -) -dep_session_helper = declare_dependency(link_with: lib_session_helper) - - -lib_libinput_backend = static_library( - 'libinput-backend', - [ - 'libinput-device.c', - 'libinput-seat.c' - ], - dependencies: [ - dep_libweston_private, - dep_libinput, - dependency('libudev', version: '>= 136') - ], - include_directories: common_inc, - install: false -) -dep_libinput_backend = declare_dependency( - link_with: lib_libinput_backend, - include_directories: include_directories('.') -) - -dep_vertex_clipping = declare_dependency( - sources: 'vertex-clipping.c', - include_directories: include_directories('.') -) - -if get_option('weston-launch') - dep_pam = cc.find_library('pam') - - if not cc.has_function('pam_open_session', dependencies: dep_pam) - error('pam_open_session not found for weston-launch') - endif - - executable( - 'weston-launch', - 'weston-launch.c', - dependencies: [dep_pam, systemd_dep, dep_libdrm], - include_directories: common_inc, - install: true - ) - - meson.add_install_script('echo', 'REMINDER: You are installing weston-launch, please make it setuid-root.') -endif - -subdir('renderer-gl') -subdir('backend-drm') -subdir('backend-fbdev') -subdir('backend-headless') -subdir('backend-rdp') -subdir('backend-wayland') -subdir('backend-x11') -subdir('backend-tdm') diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c deleted file mode 100644 index d4bd2efe..00000000 --- a/libweston/noop-renderer.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright © 2012 Intel Corporation - * - * 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 <stdint.h> -#include <stdlib.h> - -#include <libweston/libweston.h> - -static int -noop_renderer_read_pixels(struct weston_output *output, - pixman_format_code_t format, void *pixels, - uint32_t x, uint32_t y, - uint32_t width, uint32_t height) -{ - return 0; -} - -static void -noop_renderer_repaint_output(struct weston_output *output, - pixman_region32_t *output_damage) -{ -} - -static void -noop_renderer_flush_damage(struct weston_surface *surface) -{ -} - -static void -noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) -{ - struct wl_shm_buffer *shm_buffer; - uint8_t *data; - uint32_t size, i, width, height, stride; - volatile unsigned char unused = 0; /* volatile so it's not optimized out */ - - if (!buffer) - return; - - shm_buffer = wl_shm_buffer_get(buffer->resource); - - if (!shm_buffer) { - weston_log("No-op renderer supports only SHM buffers\n"); - return; - } - - data = wl_shm_buffer_get_data(shm_buffer); - stride = wl_shm_buffer_get_stride(shm_buffer); - width = wl_shm_buffer_get_width(shm_buffer); - height = wl_shm_buffer_get_height(shm_buffer); - size = stride * height; - - /* Access the buffer data to make sure the buffer's client gets killed - * if the buffer size is invalid. This makes the bad_buffer test pass. - * This can be removed if we start reading the buffer contents - * somewhere else, e.g. in repaint_output(). */ - wl_shm_buffer_begin_access(shm_buffer); - for (i = 0; i < size; i++) - unused ^= data[i]; - wl_shm_buffer_end_access(shm_buffer); - - buffer->shm_buffer = shm_buffer; - buffer->width = width; - buffer->height = height; -} - -static void -noop_renderer_surface_set_color(struct weston_surface *surface, - float red, float green, float blue, float alpha) -{ -} - -static void -noop_renderer_destroy(struct weston_compositor *ec) -{ - free(ec->renderer); - ec->renderer = NULL; -} - -WL_EXPORT int -noop_renderer_init(struct weston_compositor *ec) -{ - struct weston_renderer *renderer; - - renderer = zalloc(sizeof *renderer); - if (renderer == NULL) - return -1; - - renderer->read_pixels = noop_renderer_read_pixels; - renderer->repaint_output = noop_renderer_repaint_output; - renderer->flush_damage = noop_renderer_flush_damage; - renderer->attach = noop_renderer_attach; - renderer->surface_set_color = noop_renderer_surface_set_color; - renderer->destroy = noop_renderer_destroy; - ec->renderer = renderer; - - return 0; -} diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c deleted file mode 100644 index 79dc709c..00000000 --- a/libweston/pixel-formats.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright © 2016, 2019 Collabora, Ltd. - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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. - * - * Author: Daniel Stone <daniels@collabora.com> - */ - -#include "config.h" - -#include <endian.h> -#include <inttypes.h> -#include <stdbool.h> -#include <unistd.h> -#include <string.h> -#include <drm_fourcc.h> -#include <wayland-client-protocol.h> - -#include "shared/helpers.h" -#include "wayland-util.h" -#include "pixel-formats.h" - -#if ENABLE_EGL -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#define GL_FORMAT(fmt) .gl_format = (fmt) -#define GL_TYPE(type) .gl_type = (type) -#define SAMPLER_TYPE(type) .sampler_type = (type) -#else -#define GL_FORMAT(fmt) .gl_format = 0 -#define GL_TYPE(type) .gl_type = 0 -#define SAMPLER_TYPE(type) .sampler_type = 0 -#endif - -#define DRM_FORMAT(f) .format = DRM_FORMAT_ ## f, .drm_format_name = #f -#define BITS_RGBA_FIXED(r_, g_, b_, a_) \ - .bits.r = r_, \ - .bits.g = g_, \ - .bits.b = b_, \ - .bits.a = a_, \ - .component_type = PIXEL_COMPONENT_TYPE_FIXED - -#include "shared/weston-egl-ext.h" - -/** - * Table of DRM formats supported by Weston; RGB, ARGB and YUV formats are - * supported. Indexed/greyscale formats, and formats not containing complete - * colour channels, are not supported. - */ -static const struct pixel_format_info pixel_format_table[] = { - { - DRM_FORMAT(XRGB4444), - BITS_RGBA_FIXED(4, 4, 4, 0), - }, - { - DRM_FORMAT(ARGB4444), - BITS_RGBA_FIXED(4, 4, 4, 4), - .opaque_substitute = DRM_FORMAT_XRGB4444, - }, - { - DRM_FORMAT(XBGR4444), - BITS_RGBA_FIXED(4, 4, 4, 0), - }, - { - DRM_FORMAT(ABGR4444), - BITS_RGBA_FIXED(4, 4, 4, 4), - .opaque_substitute = DRM_FORMAT_XBGR4444, - }, - { - DRM_FORMAT(RGBX4444), - BITS_RGBA_FIXED(4, 4, 4, 0), -# if __BYTE_ORDER == __LITTLE_ENDIAN - GL_FORMAT(GL_RGBA), - GL_TYPE(GL_UNSIGNED_SHORT_4_4_4_4), -#endif - }, - { - DRM_FORMAT(RGBA4444), - BITS_RGBA_FIXED(4, 4, 4, 4), - .opaque_substitute = DRM_FORMAT_RGBX4444, -# if __BYTE_ORDER == __LITTLE_ENDIAN - GL_FORMAT(GL_RGBA), - GL_TYPE(GL_UNSIGNED_SHORT_4_4_4_4), -#endif - }, - { - DRM_FORMAT(BGRX4444), - BITS_RGBA_FIXED(4, 4, 4, 0), - }, - { - DRM_FORMAT(BGRA4444), - BITS_RGBA_FIXED(4, 4, 4, 4), - .opaque_substitute = DRM_FORMAT_BGRX4444, - }, - { - DRM_FORMAT(XRGB1555), - BITS_RGBA_FIXED(5, 5, 5, 0), - .depth = 15, - .bpp = 16, - }, - { - DRM_FORMAT(ARGB1555), - BITS_RGBA_FIXED(5, 5, 5, 1), - .opaque_substitute = DRM_FORMAT_XRGB1555, - }, - { - DRM_FORMAT(XBGR1555), - BITS_RGBA_FIXED(5, 5, 5, 0), - }, - { - DRM_FORMAT(ABGR1555), - BITS_RGBA_FIXED(5, 5, 5, 1), - .opaque_substitute = DRM_FORMAT_XBGR1555, - }, - { - DRM_FORMAT(RGBX5551), - BITS_RGBA_FIXED(5, 5, 5, 0), -# if __BYTE_ORDER == __LITTLE_ENDIAN - GL_FORMAT(GL_RGBA), - GL_TYPE(GL_UNSIGNED_SHORT_5_5_5_1), -#endif - }, - { - DRM_FORMAT(RGBA5551), - BITS_RGBA_FIXED(5, 5, 5, 1), - .opaque_substitute = DRM_FORMAT_RGBX5551, -# if __BYTE_ORDER == __LITTLE_ENDIAN - GL_FORMAT(GL_RGBA), - GL_TYPE(GL_UNSIGNED_SHORT_5_5_5_1), -#endif - }, - { - DRM_FORMAT(BGRX5551), - BITS_RGBA_FIXED(5, 5, 5, 0), - }, - { - DRM_FORMAT(BGRA5551), - BITS_RGBA_FIXED(5, 5, 5, 1), - .opaque_substitute = DRM_FORMAT_BGRX5551, - }, - { - DRM_FORMAT(RGB565), - BITS_RGBA_FIXED(5, 6, 5, 0), - .depth = 16, - .bpp = 16, -# if __BYTE_ORDER == __LITTLE_ENDIAN - GL_FORMAT(GL_RGB), - GL_TYPE(GL_UNSIGNED_SHORT_5_6_5), -#endif - }, - { - DRM_FORMAT(BGR565), - BITS_RGBA_FIXED(5, 6, 5, 0), - }, - { - DRM_FORMAT(RGB888), - BITS_RGBA_FIXED(8, 8, 8, 0), - }, - { - DRM_FORMAT(BGR888), - BITS_RGBA_FIXED(8, 8, 8, 0), - GL_FORMAT(GL_RGB), - GL_TYPE(GL_UNSIGNED_BYTE), - }, - { - DRM_FORMAT(XRGB8888), - BITS_RGBA_FIXED(8, 8, 8, 0), - .depth = 24, - .bpp = 32, - GL_FORMAT(GL_BGRA_EXT), - GL_TYPE(GL_UNSIGNED_BYTE), - }, - { - DRM_FORMAT(ARGB8888), - BITS_RGBA_FIXED(8, 8, 8, 8), - .opaque_substitute = DRM_FORMAT_XRGB8888, - .depth = 32, - .bpp = 32, - GL_FORMAT(GL_BGRA_EXT), - GL_TYPE(GL_UNSIGNED_BYTE), - }, - { - DRM_FORMAT(XBGR8888), - BITS_RGBA_FIXED(8, 8, 8, 0), - GL_FORMAT(GL_RGBA), - GL_TYPE(GL_UNSIGNED_BYTE), - }, - { - DRM_FORMAT(ABGR8888), - BITS_RGBA_FIXED(8, 8, 8, 8), - .opaque_substitute = DRM_FORMAT_XBGR8888, - GL_FORMAT(GL_RGBA), - GL_TYPE(GL_UNSIGNED_BYTE), - }, - { - DRM_FORMAT(RGBX8888), - BITS_RGBA_FIXED(8, 8, 8, 0), - }, - { - DRM_FORMAT(RGBA8888), - BITS_RGBA_FIXED(8, 8, 8, 8), - .opaque_substitute = DRM_FORMAT_RGBX8888, - }, - { - DRM_FORMAT(BGRX8888), - BITS_RGBA_FIXED(8, 8, 8, 0), - }, - { - DRM_FORMAT(BGRA8888), - BITS_RGBA_FIXED(8, 8, 8, 8), - .opaque_substitute = DRM_FORMAT_BGRX8888, - }, - { - DRM_FORMAT(XRGB2101010), - BITS_RGBA_FIXED(10, 10, 10, 0), - .depth = 30, - .bpp = 32, - }, - { - DRM_FORMAT(ARGB2101010), - BITS_RGBA_FIXED(10, 10, 10, 2), - .opaque_substitute = DRM_FORMAT_XRGB2101010, - }, - { - DRM_FORMAT(XBGR2101010), - BITS_RGBA_FIXED(10, 10, 10, 0), -# if __BYTE_ORDER == __LITTLE_ENDIAN - GL_FORMAT(GL_RGBA), - GL_TYPE(GL_UNSIGNED_INT_2_10_10_10_REV_EXT), -#endif - }, - { - DRM_FORMAT(ABGR2101010), - BITS_RGBA_FIXED(10, 10, 10, 2), - .opaque_substitute = DRM_FORMAT_XBGR2101010, -# if __BYTE_ORDER == __LITTLE_ENDIAN - GL_FORMAT(GL_RGBA), - GL_TYPE(GL_UNSIGNED_INT_2_10_10_10_REV_EXT), -#endif - }, - { - DRM_FORMAT(RGBX1010102), - BITS_RGBA_FIXED(10, 10, 10, 0), - }, - { - DRM_FORMAT(RGBA1010102), - BITS_RGBA_FIXED(10, 10, 10, 2), - .opaque_substitute = DRM_FORMAT_RGBX1010102, - }, - { - DRM_FORMAT(BGRX1010102), - BITS_RGBA_FIXED(10, 10, 10, 0), - }, - { - DRM_FORMAT(BGRA1010102), - BITS_RGBA_FIXED(10, 10, 10, 2), - .opaque_substitute = DRM_FORMAT_BGRX1010102, - }, - { - DRM_FORMAT(YUYV), - SAMPLER_TYPE(EGL_TEXTURE_Y_XUXV_WL), - .num_planes = 1, - .hsub = 2, - }, - { - DRM_FORMAT(YVYU), - SAMPLER_TYPE(EGL_TEXTURE_Y_XUXV_WL), - .num_planes = 1, - .chroma_order = ORDER_VU, - .hsub = 2, - }, - { - DRM_FORMAT(UYVY), - SAMPLER_TYPE(EGL_TEXTURE_Y_XUXV_WL), - .num_planes = 1, - .luma_chroma_order = ORDER_CHROMA_LUMA, - .hsub = 2, - }, - { - DRM_FORMAT(VYUY), - SAMPLER_TYPE(EGL_TEXTURE_Y_XUXV_WL), - .num_planes = 1, - .luma_chroma_order = ORDER_CHROMA_LUMA, - .chroma_order = ORDER_VU, - .hsub = 2, - }, - { - DRM_FORMAT(NV12), - SAMPLER_TYPE(EGL_TEXTURE_Y_UV_WL), - .num_planes = 2, - .hsub = 2, - .vsub = 2, - }, - { - DRM_FORMAT(NV21), - SAMPLER_TYPE(EGL_TEXTURE_Y_UV_WL), - .num_planes = 2, - .chroma_order = ORDER_VU, - .hsub = 2, - .vsub = 2, - }, - { - DRM_FORMAT(NV16), - SAMPLER_TYPE(EGL_TEXTURE_Y_UV_WL), - .num_planes = 2, - .hsub = 2, - .vsub = 1, - }, - { - DRM_FORMAT(NV61), - SAMPLER_TYPE(EGL_TEXTURE_Y_UV_WL), - .num_planes = 2, - .chroma_order = ORDER_VU, - .hsub = 2, - .vsub = 1, - }, - { - DRM_FORMAT(NV24), - SAMPLER_TYPE(EGL_TEXTURE_Y_UV_WL), - .num_planes = 2, - }, - { - DRM_FORMAT(NV42), - SAMPLER_TYPE(EGL_TEXTURE_Y_UV_WL), - .num_planes = 2, - .chroma_order = ORDER_VU, - }, - { - DRM_FORMAT(YUV410), - SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), - .num_planes = 3, - .hsub = 4, - .vsub = 4, - }, - { - DRM_FORMAT(YVU410), - SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), - .num_planes = 3, - .chroma_order = ORDER_VU, - .hsub = 4, - .vsub = 4, - }, - { - DRM_FORMAT(YUV411), - SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), - .num_planes = 3, - .hsub = 4, - .vsub = 1, - }, - { - DRM_FORMAT(YVU411), - SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), - .num_planes = 3, - .chroma_order = ORDER_VU, - .hsub = 4, - .vsub = 1, - }, - { - DRM_FORMAT(YUV420), - SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), - .num_planes = 3, - .hsub = 2, - .vsub = 2, - }, - { - DRM_FORMAT(YVU420), - SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), - .num_planes = 3, - .chroma_order = ORDER_VU, - .hsub = 2, - .vsub = 2, - }, - { - DRM_FORMAT(YUV422), - SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), - .num_planes = 3, - .hsub = 2, - .vsub = 1, - }, - { - DRM_FORMAT(YVU422), - SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), - .num_planes = 3, - .chroma_order = ORDER_VU, - .hsub = 2, - .vsub = 1, - }, - { - DRM_FORMAT(YUV444), - SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), - .num_planes = 3, - }, - { - DRM_FORMAT(YVU444), - SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), - .num_planes = 3, - .chroma_order = ORDER_VU, - }, -}; - -WL_EXPORT const struct pixel_format_info * -pixel_format_get_info_shm(uint32_t format) -{ - if (format == WL_SHM_FORMAT_XRGB8888) - return pixel_format_get_info(DRM_FORMAT_XRGB8888); - else if (format == WL_SHM_FORMAT_ARGB8888) - return pixel_format_get_info(DRM_FORMAT_ARGB8888); - else - return pixel_format_get_info(format); -} - -WL_EXPORT const struct pixel_format_info * -pixel_format_get_info(uint32_t format) -{ - unsigned int i; - - for (i = 0; i < ARRAY_LENGTH(pixel_format_table); i++) { - if (pixel_format_table[i].format == format) - return &pixel_format_table[i]; - } - - return NULL; -} - -WL_EXPORT const struct pixel_format_info * -pixel_format_get_info_by_drm_name(const char *drm_format_name) -{ - const struct pixel_format_info *info; - unsigned int i; - - for (i = 0; i < ARRAY_LENGTH(pixel_format_table); i++) { - info = &pixel_format_table[i]; - if (strcasecmp(info->drm_format_name, drm_format_name) == 0) - return info; - } - - return NULL; -} - -WL_EXPORT unsigned int -pixel_format_get_plane_count(const struct pixel_format_info *info) -{ - return info->num_planes ? info->num_planes : 1; -} - -WL_EXPORT bool -pixel_format_is_opaque(const struct pixel_format_info *info) -{ - return !info->opaque_substitute; -} - -WL_EXPORT const struct pixel_format_info * -pixel_format_get_opaque_substitute(const struct pixel_format_info *info) -{ - if (!info->opaque_substitute) - return info; - else - return pixel_format_get_info(info->opaque_substitute); -} - -WL_EXPORT const struct pixel_format_info * -pixel_format_get_info_by_opaque_substitute(uint32_t format) -{ - unsigned int i; - - for (i = 0; i < ARRAY_LENGTH(pixel_format_table); i++) { - if (pixel_format_table[i].opaque_substitute == format) - return &pixel_format_table[i]; - } - - return NULL; -} - -WL_EXPORT unsigned int -pixel_format_width_for_plane(const struct pixel_format_info *info, - unsigned int plane, - unsigned int width) -{ - /* We don't support any formats where the first plane is subsampled. */ - if (plane == 0 || !info->hsub) - return width; - - return width / info->hsub; -} - -WL_EXPORT unsigned int -pixel_format_height_for_plane(const struct pixel_format_info *info, - unsigned int plane, - unsigned int height) -{ - /* We don't support any formats where the first plane is subsampled. */ - if (plane == 0 || !info->vsub) - return height; - - return height / info->vsub; -} diff --git a/libweston/pixel-formats.h b/libweston/pixel-formats.h deleted file mode 100644 index 3e125260..00000000 --- a/libweston/pixel-formats.h +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright © 2016, 2019 Collabora, Ltd. - * Copyright (c) 2018 DisplayLink (UK) Ltd. - * - * 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. - * - * Author: Daniel Stone <daniels@collabora.com> - */ - -#include <inttypes.h> -#include <stdbool.h> - -/** - * Contains information about pixel formats, mapping format codes from - * wl_shm and drm_fourcc.h (which are deliberately identical, but for the - * special cases of WL_SHM_ARGB8888 and WL_SHM_XRGB8888) into various - * sets of information. Helper functions are provided for dealing with these - * raw structures. - */ -struct pixel_format_info { - /** DRM/wl_shm format code */ - uint32_t format; - - /** The DRM format name without the DRM_FORMAT_ prefix. */ - const char *drm_format_name; - - /** If non-zero, number of planes in base (non-modified) format. */ - int num_planes; - - /** If format contains alpha channel, opaque equivalent of format, - * i.e. alpha channel replaced with X. */ - uint32_t opaque_substitute; - - /** How the format should be sampled, expressed in terms of tokens - * from the EGL_WL_bind_wayland_display extension. If not set, - * assumed to be either RGB or RGBA, depending on whether or not - * the format contains an alpha channel. The samplers may still - * return alpha even for opaque formats; users must manually set - * the alpha channel to 1.0 (or ignore it) if the format is - * opaque. */ - uint32_t sampler_type; - - /** GL format, if data can be natively/directly uploaded. Note that - * whilst DRM formats are little-endian unless explicitly specified, - * (i.e. DRM_FORMAT_ARGB8888 is stored BGRA as sequential bytes in - * memory), GL uses the sequential byte order, so that format maps to - * GL_BGRA_EXT plus GL_UNSIGNED_BYTE. To add to the confusion, the - * explicitly-sized types (e.g. GL_UNSIGNED_SHORT_5_5_5_1) read in - * machine-endian order, so for these types, the correspondence - * depends on endianness. */ - int gl_format; - - /** GL data type, if data can be natively/directly uploaded. */ - int gl_type; - - /** If set, this format can be used with the legacy drmModeAddFB() - * function (not AddFB2), using this and the bpp member. */ - int depth; - - /** See 'depth' member above. */ - int bpp; - - /** Horizontal subsampling; if non-zero, divide the width by this - * member to obtain the number of columns in the source buffer for - * secondary planes only. Stride is not affected by horizontal - * subsampling. */ - int hsub; - - /** Vertical subsampling; if non-zero, divide the height by this - * member to obtain the number of rows in the source buffer for - * secondary planes only. */ - int vsub; - - /* Ordering of chroma components. */ - enum { - ORDER_UV = 0, - ORDER_VU, - } chroma_order; - - /* If packed YUV (num_planes == 1), ordering of luma/chroma - * components. */ - enum { - ORDER_LUMA_CHROMA = 0, - ORDER_CHROMA_LUMA, - } luma_chroma_order; - - /** How many significant bits each channel has, or zero if N/A. */ - struct { - int r; - int g; - int b; - int a; - } bits; - - /** How channel bits are interpreted, fixed (uint) or floating-point */ - enum { - PIXEL_COMPONENT_TYPE_FIXED = 0, - PIXEL_COMPONENT_TYPE_FLOAT, - } component_type; -}; - -/** - * Get pixel format information for a DRM format code - * - * Given a DRM format code, return a pixel format info structure describing - * the properties of that format. - * - * @param format DRM format code to get info for - * @returns A pixel format structure (must not be freed), or NULL if the - * format could not be found - */ -const struct pixel_format_info * -pixel_format_get_info(uint32_t format); - -/** - * Get pixel format information for a SHM format code - * - * Given a SHM format code, return a DRM pixel format info structure describing - * the properties of that format. - * - * @param format SHM format code to get info for. - * @returns A pixel format structure (must not be freed), or NULL if the - * format could not be found. - */ -const struct pixel_format_info * -pixel_format_get_info_shm(uint32_t format); - -/** - * Get pixel format information for a named DRM format - * - * Given a DRM format name, return a pixel format info structure describing - * the properties of that format. - * - * The DRM format name is the preprocessor token name from drm_fourcc.h - * without the DRM_FORMAT_ prefix. The search is also case-insensitive. - * Both "xrgb8888" and "XRGB8888" searches will find DRM_FORMAT_XRGB8888 - * for example. - * - * @param drm_format_name DRM format name to get info for (not NULL) - * @returns A pixel format structure (must not be freed), or NULL if the - * name could not be found - */ -const struct pixel_format_info * -pixel_format_get_info_by_drm_name(const char *drm_format_name); - -/** - * Get number of planes used by a pixel format - * - * Given a pixel format info structure, return the number of planes - * required for a buffer. Note that this is not necessarily identical to - * the number of samplers required to be bound, as two views into a single - * plane are sometimes required. - * - * @param format Pixel format info structure - * @returns Number of planes required for the format - */ -unsigned int -pixel_format_get_plane_count(const struct pixel_format_info *format); - -/** - * Determine if a pixel format is opaque or contains alpha - * - * Returns whether or not the pixel format is opaque, or contains a - * significant alpha channel. Note that the suggested EGL sampler type may - * still sample undefined data into the alpha channel; users must consider - * alpha as 1.0 if the format is opaque, and not rely on the sampler to - * return this when sampling from the alpha channel. - * - * @param format Pixel format info structure - * @returns True if the format is opaque, or false if it has significant alpha - */ -bool -pixel_format_is_opaque(const struct pixel_format_info *format); - -/** - * Get compatible opaque equivalent for a format - * - * Given a pixel format info structure, return a format which is wholly - * compatible with the input format, but opaque, ignoring the alpha channel. - * If an alpha format is provided, but the content is known to all be opaque, - * then this can be used as a substitute to avoid blending. - * - * If the input format is opaque, this function will return the input format. - * - * @param format Pixel format info structure - * @returns A pixel format info structure for the compatible opaque substitute - */ -const struct pixel_format_info * -pixel_format_get_opaque_substitute(const struct pixel_format_info *format); - -/** - * For an opaque format, get the equivalent format with alpha instead of an - * ignored channel - * - * This is the opposite lookup from pixel_format_get_opaque_substitute(). - * Finds the format whose opaque substitute is the given format. - * - * If the input format is not opaque or does not have ignored (X) bits, then - * the search cannot find a match. - * - * @param format DRM format code to search for - * @returns A pixel format info structure for the pixel format whose opaque - * substitute is the argument, or NULL if no match. - */ -const struct pixel_format_info * -pixel_format_get_info_by_opaque_substitute(uint32_t format); - -/** - * Return the effective sampling width for a given plane - * - * When horizontal subsampling is effective, a sampler bound to a secondary - * plane must bind the sampler with a smaller effective width. This function - * returns the effective width to use for the sampler, i.e. dividing by hsub. - * - * If horizontal subsampling is not in effect, this will be equal to the - * width. - * - * @param format Pixel format info structure - * @param plane Zero-indexed plane number - * @param width Width of the buffer - * @returns Effective width for sampling - */ -unsigned int -pixel_format_width_for_plane(const struct pixel_format_info *format, - unsigned int plane, - unsigned int width); - -/** - * Return the effective sampling height for a given plane - * - * When vertical subsampling is in effect, a sampler bound to a secondary - * plane must bind the sampler with a smaller effective height. This function - * returns the effective height to use for the sampler, i.e. dividing by vsub. - * - * If vertical subsampling is not in effect, this will be equal to the height. - * - * @param format Pixel format info structure - * @param plane Zero-indexed plane number - * @param height Height of the buffer - * @returns Effective width for sampling - */ -unsigned int -pixel_format_height_for_plane(const struct pixel_format_info *format, - unsigned int plane, - unsigned int height); diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c deleted file mode 100644 index cae89741..00000000 --- a/libweston/pixman-renderer.c +++ /dev/null @@ -1,968 +0,0 @@ -/* - * Copyright © 2012 Intel Corporation - * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com> - * Copyright © 2015 Collabora, Ltd. - * - * 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 <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <assert.h> - -#include "pixman-renderer.h" -#include "shared/helpers.h" - -#include <linux/input.h> - -struct pixman_output_state { - void *shadow_buffer; - pixman_image_t *shadow_image; - pixman_image_t *hw_buffer; - pixman_region32_t *hw_extra_damage; -}; - -struct pixman_surface_state { - struct weston_surface *surface; - - pixman_image_t *image; - struct weston_buffer_reference buffer_ref; - struct weston_buffer_release_reference buffer_release_ref; - - struct wl_listener buffer_destroy_listener; - struct wl_listener surface_destroy_listener; - struct wl_listener renderer_destroy_listener; -}; - -struct pixman_renderer { - struct weston_renderer base; - - int repaint_debug; - pixman_image_t *debug_color; - struct weston_binding *debug_binding; - - struct wl_signal destroy_signal; -}; - -static inline struct pixman_output_state * -get_output_state(struct weston_output *output) -{ - return (struct pixman_output_state *)output->renderer_state; -} - -static int -pixman_renderer_create_surface(struct weston_surface *surface); - -static inline struct pixman_surface_state * -get_surface_state(struct weston_surface *surface) -{ - if (!surface->renderer_state) - pixman_renderer_create_surface(surface); - - return (struct pixman_surface_state *)surface->renderer_state; -} - -static inline struct pixman_renderer * -get_renderer(struct weston_compositor *ec) -{ - return (struct pixman_renderer *)ec->renderer; -} - -static int -pixman_renderer_read_pixels(struct weston_output *output, - pixman_format_code_t format, void *pixels, - uint32_t x, uint32_t y, - uint32_t width, uint32_t height) -{ - struct pixman_output_state *po = get_output_state(output); - pixman_image_t *out_buf; - - if (!po->hw_buffer) { - errno = ENODEV; - return -1; - } - - out_buf = pixman_image_create_bits(format, - width, - height, - pixels, - (PIXMAN_FORMAT_BPP(format) / 8) * width); - - pixman_image_composite32(PIXMAN_OP_SRC, - po->hw_buffer, /* src */ - NULL /* mask */, - out_buf, /* dest */ - x, y, /* src_x, src_y */ - 0, 0, /* mask_x, mask_y */ - 0, 0, /* dest_x, dest_y */ - pixman_image_get_width (po->hw_buffer), /* width */ - pixman_image_get_height (po->hw_buffer) /* height */); - - pixman_image_unref(out_buf); - - return 0; -} - -static void -region_global_to_output(struct weston_output *output, pixman_region32_t *region) -{ - if (output->zoom.active) { - weston_matrix_transform_region(region, &output->matrix, region); - } else { - pixman_region32_translate(region, -output->x, -output->y); - weston_transformed_region(output->width, output->height, - output->transform, - output->current_scale, - region, region); - } -} - -#define D2F(v) pixman_double_to_fixed((double)v) - -static void -weston_matrix_to_pixman_transform(pixman_transform_t *pt, - const struct weston_matrix *wm) -{ - /* Pixman supports only 2D transform matrix, but Weston uses 3D, * - * so we're omitting Z coordinate here. */ - pt->matrix[0][0] = pixman_double_to_fixed(wm->d[0]); - pt->matrix[0][1] = pixman_double_to_fixed(wm->d[4]); - pt->matrix[0][2] = pixman_double_to_fixed(wm->d[12]); - pt->matrix[1][0] = pixman_double_to_fixed(wm->d[1]); - pt->matrix[1][1] = pixman_double_to_fixed(wm->d[5]); - pt->matrix[1][2] = pixman_double_to_fixed(wm->d[13]); - pt->matrix[2][0] = pixman_double_to_fixed(wm->d[3]); - pt->matrix[2][1] = pixman_double_to_fixed(wm->d[7]); - pt->matrix[2][2] = pixman_double_to_fixed(wm->d[15]); -} - -static void -pixman_renderer_compute_transform(pixman_transform_t *transform_out, - struct weston_view *ev, - struct weston_output *output) -{ - struct weston_matrix matrix; - - /* Set up the source transformation based on the surface - position, the output position/transform/scale and the client - specified buffer transform/scale */ - matrix = output->inverse_matrix; - - if (ev->transform.enabled) { - weston_matrix_multiply(&matrix, &ev->transform.inverse); - } else { - weston_matrix_translate(&matrix, - -ev->geometry.x, -ev->geometry.y, 0); - } - - weston_matrix_multiply(&matrix, &ev->surface->surface_to_buffer_matrix); - - weston_matrix_to_pixman_transform(transform_out, &matrix); -} - -static bool -view_transformation_is_translation(struct weston_view *view) -{ - if (!view->transform.enabled) - return true; - - if (view->transform.matrix.type <= WESTON_MATRIX_TRANSFORM_TRANSLATE) - return true; - - return false; -} - -static void -region_intersect_only_translation(pixman_region32_t *result_global, - pixman_region32_t *global, - pixman_region32_t *surf, - struct weston_view *view) -{ - float view_x, view_y; - - assert(view_transformation_is_translation(view)); - - /* Convert from surface to global coordinates */ - pixman_region32_copy(result_global, surf); - weston_view_to_global_float(view, 0, 0, &view_x, &view_y); - pixman_region32_translate(result_global, (int)view_x, (int)view_y); - - pixman_region32_intersect(result_global, result_global, global); -} - -static void -composite_whole(pixman_op_t op, - pixman_image_t *src, - pixman_image_t *mask, - pixman_image_t *dest, - const pixman_transform_t *transform, - pixman_filter_t filter) -{ - int32_t dest_width; - int32_t dest_height; - - dest_width = pixman_image_get_width(dest); - dest_height = pixman_image_get_height(dest); - - pixman_image_set_transform(src, transform); - pixman_image_set_filter(src, filter, NULL, 0); - - pixman_image_composite32(op, src, mask, dest, - 0, 0, /* src_x, src_y */ - 0, 0, /* mask_x, mask_y */ - 0, 0, /* dest_x, dest_y */ - dest_width, dest_height); -} - -static void -composite_clipped(pixman_image_t *src, - pixman_image_t *mask, - pixman_image_t *dest, - const pixman_transform_t *transform, - pixman_filter_t filter, - pixman_region32_t *src_clip) -{ - int n_box; - pixman_box32_t *boxes; - int32_t dest_width; - int32_t dest_height; - int src_stride; - int bitspp; - pixman_format_code_t src_format; - void *src_data; - int i; - - /* Hardcoded to use PIXMAN_OP_OVER, because sampling outside of - * a Pixman image produces (0,0,0,0) instead of discarding the - * fragment. - */ - - dest_width = pixman_image_get_width(dest); - dest_height = pixman_image_get_height(dest); - src_format = pixman_image_get_format(src); - src_stride = pixman_image_get_stride(src); - bitspp = PIXMAN_FORMAT_BPP(src_format); - src_data = pixman_image_get_data(src); - - assert(src_format); - - /* This would be massive overdraw, except when n_box is 1. */ - boxes = pixman_region32_rectangles(src_clip, &n_box); - for (i = 0; i < n_box; i++) { - uint8_t *ptr = src_data; - pixman_image_t *boximg; - pixman_transform_t adj = *transform; - - ptr += boxes[i].y1 * src_stride; - ptr += boxes[i].x1 * bitspp / 8; - boximg = pixman_image_create_bits_no_clear(src_format, - boxes[i].x2 - boxes[i].x1, - boxes[i].y2 - boxes[i].y1, - (uint32_t *)ptr, src_stride); - - pixman_transform_translate(&adj, NULL, - pixman_int_to_fixed(-boxes[i].x1), - pixman_int_to_fixed(-boxes[i].y1)); - pixman_image_set_transform(boximg, &adj); - - pixman_image_set_filter(boximg, filter, NULL, 0); - pixman_image_composite32(PIXMAN_OP_OVER, boximg, mask, dest, - 0, 0, /* src_x, src_y */ - 0, 0, /* mask_x, mask_y */ - 0, 0, /* dest_x, dest_y */ - dest_width, dest_height); - - pixman_image_unref(boximg); - } - - if (n_box > 1) { - static bool warned = false; - - if (!warned) - weston_log("Pixman-renderer warning: %dx overdraw\n", - n_box); - warned = true; - } -} - -/** Paint an intersected region - * - * \param ev The view to be painted. - * \param output The output being painted. - * \param repaint_output The region to be painted in output coordinates. - * \param source_clip The region of the source image to use, in source image - * coordinates. If NULL, use the whole source image. - * \param pixman_op Compositing operator, either SRC or OVER. - */ -static void -repaint_region(struct weston_view *ev, struct weston_output *output, - pixman_region32_t *repaint_output, - pixman_region32_t *source_clip, - pixman_op_t pixman_op) -{ - struct pixman_renderer *pr = - (struct pixman_renderer *) output->compositor->renderer; - struct pixman_surface_state *ps = get_surface_state(ev->surface); - struct pixman_output_state *po = get_output_state(output); - struct weston_buffer_viewport *vp = &ev->surface->buffer_viewport; - pixman_image_t *target_image; - pixman_transform_t transform; - pixman_filter_t filter; - pixman_image_t *mask_image; - pixman_color_t mask = { 0, }; - - if (po->shadow_image) - target_image = po->shadow_image; - else - target_image = po->hw_buffer; - - /* Clip rendering to the damaged output region */ - pixman_image_set_clip_region32(target_image, repaint_output); - - pixman_renderer_compute_transform(&transform, ev, output); - - if (ev->transform.enabled || output->current_scale != vp->buffer.scale) - filter = PIXMAN_FILTER_BILINEAR; - else - filter = PIXMAN_FILTER_NEAREST; - - if (ps->buffer_ref.buffer) - wl_shm_buffer_begin_access(ps->buffer_ref.buffer->shm_buffer); - - if (ev->alpha < 1.0) { - mask.alpha = 0xffff * ev->alpha; - mask_image = pixman_image_create_solid_fill(&mask); - } else { - mask_image = NULL; - } - - if (source_clip) - composite_clipped(ps->image, mask_image, target_image, - &transform, filter, source_clip); - else - composite_whole(pixman_op, ps->image, mask_image, - target_image, &transform, filter); - - if (mask_image) - pixman_image_unref(mask_image); - - if (ps->buffer_ref.buffer) - wl_shm_buffer_end_access(ps->buffer_ref.buffer->shm_buffer); - - if (pr->repaint_debug) - pixman_image_composite32(PIXMAN_OP_OVER, - pr->debug_color, /* src */ - NULL /* mask */, - target_image, /* dest */ - 0, 0, /* src_x, src_y */ - 0, 0, /* mask_x, mask_y */ - 0, 0, /* dest_x, dest_y */ - pixman_image_get_width (target_image), /* width */ - pixman_image_get_height (target_image) /* height */); - - pixman_image_set_clip_region32(target_image, NULL); -} - -static void -draw_view_translated(struct weston_view *view, struct weston_output *output, - pixman_region32_t *repaint_global) -{ - struct weston_surface *surface = view->surface; - /* non-opaque region in surface coordinates: */ - pixman_region32_t surface_blend; - /* region to be painted in output coordinates: */ - pixman_region32_t repaint_output; - - pixman_region32_init(&repaint_output); - - /* Blended region is whole surface minus opaque region, - * unless surface alpha forces us to blend all. - */ - pixman_region32_init_rect(&surface_blend, 0, 0, - surface->width, surface->height); - - if (!(view->alpha < 1.0)) { - pixman_region32_subtract(&surface_blend, &surface_blend, - &surface->opaque); - - if (pixman_region32_not_empty(&surface->opaque)) { - region_intersect_only_translation(&repaint_output, - repaint_global, - &surface->opaque, - view); - region_global_to_output(output, &repaint_output); - - repaint_region(view, output, &repaint_output, NULL, - PIXMAN_OP_SRC); - } - } - - if (pixman_region32_not_empty(&surface_blend)) { - region_intersect_only_translation(&repaint_output, - repaint_global, - &surface_blend, view); - region_global_to_output(output, &repaint_output); - - repaint_region(view, output, &repaint_output, NULL, - PIXMAN_OP_OVER); - } - - pixman_region32_fini(&surface_blend); - pixman_region32_fini(&repaint_output); -} - -static void -draw_view_source_clipped(struct weston_view *view, - struct weston_output *output, - pixman_region32_t *repaint_global) -{ - struct weston_surface *surface = view->surface; - pixman_region32_t surf_region; - pixman_region32_t buffer_region; - pixman_region32_t repaint_output; - - /* Do not bother separating the opaque region from non-opaque. - * Source clipping requires PIXMAN_OP_OVER in all cases, so painting - * opaque separately has no benefit. - */ - - pixman_region32_init_rect(&surf_region, 0, 0, - surface->width, surface->height); - if (view->geometry.scissor_enabled) - pixman_region32_intersect(&surf_region, &surf_region, - &view->geometry.scissor); - - pixman_region32_init(&buffer_region); - weston_surface_to_buffer_region(surface, &surf_region, &buffer_region); - - pixman_region32_init(&repaint_output); - pixman_region32_copy(&repaint_output, repaint_global); - region_global_to_output(output, &repaint_output); - - repaint_region(view, output, &repaint_output, &buffer_region, - PIXMAN_OP_OVER); - - pixman_region32_fini(&repaint_output); - pixman_region32_fini(&buffer_region); - pixman_region32_fini(&surf_region); -} - -static void -draw_view(struct weston_view *ev, struct weston_output *output, - pixman_region32_t *damage) /* in global coordinates */ -{ - struct pixman_surface_state *ps = get_surface_state(ev->surface); - /* repaint bounding region in global coordinates: */ - pixman_region32_t repaint; - - /* No buffer attached */ - if (!ps->image) - return; - - pixman_region32_init(&repaint); - pixman_region32_intersect(&repaint, - &ev->transform.boundingbox, damage); - pixman_region32_subtract(&repaint, &repaint, &ev->clip); - - if (!pixman_region32_not_empty(&repaint)) - goto out; - - if (view_transformation_is_translation(ev)) { - /* The simple case: The surface regions opaque, non-opaque, - * etc. are convertible to global coordinate space. - * There is no need to use a source clip region. - * It is possible to paint opaque region as PIXMAN_OP_SRC. - * Also the boundingbox is accurate rather than an - * approximation. - */ - draw_view_translated(ev, output, &repaint); - } else { - /* The complex case: the view transformation does not allow - * converting opaque etc. regions into global coordinate space. - * Therefore we need source clipping to avoid sampling from - * unwanted source image areas, unless the source image is - * to be used whole. Source clipping does not work with - * PIXMAN_OP_SRC. - */ - draw_view_source_clipped(ev, output, &repaint); - } - -out: - pixman_region32_fini(&repaint); -} -static void -repaint_surfaces(struct weston_output *output, pixman_region32_t *damage) -{ - struct weston_compositor *compositor = output->compositor; - struct weston_view *view; - - wl_list_for_each_reverse(view, &compositor->view_list, link) - if (view->plane == &compositor->primary_plane) - draw_view(view, output, damage); -} - -static void -copy_to_hw_buffer(struct weston_output *output, pixman_region32_t *region) -{ - struct pixman_output_state *po = get_output_state(output); - pixman_region32_t output_region; - - pixman_region32_init(&output_region); - pixman_region32_copy(&output_region, region); - - region_global_to_output(output, &output_region); - - pixman_image_set_clip_region32 (po->hw_buffer, &output_region); - pixman_region32_fini(&output_region); - - pixman_image_composite32(PIXMAN_OP_SRC, - po->shadow_image, /* src */ - NULL /* mask */, - po->hw_buffer, /* dest */ - 0, 0, /* src_x, src_y */ - 0, 0, /* mask_x, mask_y */ - 0, 0, /* dest_x, dest_y */ - pixman_image_get_width (po->hw_buffer), /* width */ - pixman_image_get_height (po->hw_buffer) /* height */); - - pixman_image_set_clip_region32 (po->hw_buffer, NULL); -} - -static void -pixman_renderer_repaint_output(struct weston_output *output, - pixman_region32_t *output_damage) -{ - struct pixman_output_state *po = get_output_state(output); - pixman_region32_t hw_damage; - - if (!po->hw_buffer) { - po->hw_extra_damage = NULL; - return; - } - - pixman_region32_init(&hw_damage); - if (po->hw_extra_damage) { - pixman_region32_union(&hw_damage, - po->hw_extra_damage, output_damage); - po->hw_extra_damage = NULL; - } else { - pixman_region32_copy(&hw_damage, output_damage); - } - - if (po->shadow_image) { - repaint_surfaces(output, output_damage); - copy_to_hw_buffer(output, &hw_damage); - } else { - repaint_surfaces(output, &hw_damage); - } - pixman_region32_fini(&hw_damage); - - wl_signal_emit(&output->frame_signal, output_damage); - - /* Actual flip should be done by caller */ -} - -static void -pixman_renderer_flush_damage(struct weston_surface *surface) -{ - /* No-op for pixman renderer */ -} - -static void -buffer_state_handle_buffer_destroy(struct wl_listener *listener, void *data) -{ - struct pixman_surface_state *ps; - - ps = container_of(listener, struct pixman_surface_state, - buffer_destroy_listener); - - if (ps->image) { - pixman_image_unref(ps->image); - ps->image = NULL; - } - - ps->buffer_destroy_listener.notify = NULL; -} - -static void -pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) -{ - struct pixman_surface_state *ps = get_surface_state(es); - struct wl_shm_buffer *shm_buffer; - pixman_format_code_t pixman_format; - - weston_buffer_reference(&ps->buffer_ref, buffer); - weston_buffer_release_reference(&ps->buffer_release_ref, - es->buffer_release_ref.buffer_release); - - if (ps->buffer_destroy_listener.notify) { - wl_list_remove(&ps->buffer_destroy_listener.link); - ps->buffer_destroy_listener.notify = NULL; - } - - if (ps->image) { - pixman_image_unref(ps->image); - ps->image = NULL; - } - - if (!buffer) - return; - - shm_buffer = wl_shm_buffer_get(buffer->resource); - - if (! shm_buffer) { - weston_log("Pixman renderer supports only SHM buffers\n"); - weston_buffer_reference(&ps->buffer_ref, NULL); - weston_buffer_release_reference(&ps->buffer_release_ref, NULL); - return; - } - - switch (wl_shm_buffer_get_format(shm_buffer)) { - case WL_SHM_FORMAT_XRGB8888: - pixman_format = PIXMAN_x8r8g8b8; - es->is_opaque = true; - break; - case WL_SHM_FORMAT_ARGB8888: - pixman_format = PIXMAN_a8r8g8b8; - es->is_opaque = false; - break; - case WL_SHM_FORMAT_RGB565: - pixman_format = PIXMAN_r5g6b5; - es->is_opaque = true; - break; - default: - weston_log("Unsupported SHM buffer format 0x%x\n", - wl_shm_buffer_get_format(shm_buffer)); - weston_buffer_reference(&ps->buffer_ref, NULL); - weston_buffer_release_reference(&ps->buffer_release_ref, NULL); - weston_buffer_send_server_error(buffer, - "disconnecting due to unhandled buffer type"); - return; - break; - } - - buffer->shm_buffer = shm_buffer; - buffer->width = wl_shm_buffer_get_width(shm_buffer); - buffer->height = wl_shm_buffer_get_height(shm_buffer); - - ps->image = pixman_image_create_bits(pixman_format, - buffer->width, buffer->height, - wl_shm_buffer_get_data(shm_buffer), - wl_shm_buffer_get_stride(shm_buffer)); - - ps->buffer_destroy_listener.notify = - buffer_state_handle_buffer_destroy; - wl_signal_add(&buffer->destroy_signal, - &ps->buffer_destroy_listener); -} - -static void -pixman_renderer_surface_state_destroy(struct pixman_surface_state *ps) -{ - wl_list_remove(&ps->surface_destroy_listener.link); - wl_list_remove(&ps->renderer_destroy_listener.link); - if (ps->buffer_destroy_listener.notify) { - wl_list_remove(&ps->buffer_destroy_listener.link); - ps->buffer_destroy_listener.notify = NULL; - } - - ps->surface->renderer_state = NULL; - - if (ps->image) { - pixman_image_unref(ps->image); - ps->image = NULL; - } - weston_buffer_reference(&ps->buffer_ref, NULL); - weston_buffer_release_reference(&ps->buffer_release_ref, NULL); - free(ps); -} - -static void -surface_state_handle_surface_destroy(struct wl_listener *listener, void *data) -{ - struct pixman_surface_state *ps; - - ps = container_of(listener, struct pixman_surface_state, - surface_destroy_listener); - - pixman_renderer_surface_state_destroy(ps); -} - -static void -surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data) -{ - struct pixman_surface_state *ps; - - ps = container_of(listener, struct pixman_surface_state, - renderer_destroy_listener); - - pixman_renderer_surface_state_destroy(ps); -} - -static int -pixman_renderer_create_surface(struct weston_surface *surface) -{ - struct pixman_surface_state *ps; - struct pixman_renderer *pr = get_renderer(surface->compositor); - - ps = zalloc(sizeof *ps); - if (ps == NULL) - return -1; - - surface->renderer_state = ps; - - ps->surface = surface; - - ps->surface_destroy_listener.notify = - surface_state_handle_surface_destroy; - wl_signal_add(&surface->destroy_signal, - &ps->surface_destroy_listener); - - ps->renderer_destroy_listener.notify = - surface_state_handle_renderer_destroy; - wl_signal_add(&pr->destroy_signal, - &ps->renderer_destroy_listener); - - return 0; -} - -static void -pixman_renderer_surface_set_color(struct weston_surface *es, - float red, float green, float blue, float alpha) -{ - struct pixman_surface_state *ps = get_surface_state(es); - pixman_color_t color; - - color.red = red * 0xffff; - color.green = green * 0xffff; - color.blue = blue * 0xffff; - color.alpha = alpha * 0xffff; - - if (ps->image) { - pixman_image_unref(ps->image); - ps->image = NULL; - } - - ps->image = pixman_image_create_solid_fill(&color); -} - -static void -pixman_renderer_destroy(struct weston_compositor *ec) -{ - struct pixman_renderer *pr = get_renderer(ec); - - wl_signal_emit(&pr->destroy_signal, pr); - weston_binding_destroy(pr->debug_binding); - free(pr); - - ec->renderer = NULL; -} - -static void -pixman_renderer_surface_get_content_size(struct weston_surface *surface, - int *width, int *height) -{ - struct pixman_surface_state *ps = get_surface_state(surface); - - if (ps->image) { - *width = pixman_image_get_width(ps->image); - *height = pixman_image_get_height(ps->image); - } else { - *width = 0; - *height = 0; - } -} - -static int -pixman_renderer_surface_copy_content(struct weston_surface *surface, - void *target, size_t size, - int src_x, int src_y, - int width, int height) -{ - const pixman_format_code_t format = PIXMAN_a8b8g8r8; - const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */ - struct pixman_surface_state *ps = get_surface_state(surface); - pixman_image_t *out_buf; - - if (!ps->image) - return -1; - - out_buf = pixman_image_create_bits(format, width, height, - target, width * bytespp); - - pixman_image_set_transform(ps->image, NULL); - pixman_image_composite32(PIXMAN_OP_SRC, - ps->image, /* src */ - NULL, /* mask */ - out_buf, /* dest */ - src_x, src_y, /* src_x, src_y */ - 0, 0, /* mask_x, mask_y */ - 0, 0, /* dest_x, dest_y */ - width, height); - - pixman_image_unref(out_buf); - - return 0; -} - -static void -debug_binding(struct weston_keyboard *keyboard, const struct timespec *time, - uint32_t key, void *data) -{ - struct weston_compositor *ec = data; - struct pixman_renderer *pr = (struct pixman_renderer *) ec->renderer; - - pr->repaint_debug ^= 1; - - if (pr->repaint_debug) { - pixman_color_t red = { - 0x3fff, 0x0000, 0x0000, 0x3fff - }; - - pr->debug_color = pixman_image_create_solid_fill(&red); - } else { - pixman_image_unref(pr->debug_color); - weston_compositor_damage_all(ec); - } -} - -WL_EXPORT int -pixman_renderer_init(struct weston_compositor *ec) -{ - struct pixman_renderer *renderer; - - renderer = zalloc(sizeof *renderer); - if (renderer == NULL) - return -1; - - renderer->repaint_debug = 0; - renderer->debug_color = NULL; - renderer->base.read_pixels = pixman_renderer_read_pixels; - renderer->base.repaint_output = pixman_renderer_repaint_output; - renderer->base.flush_damage = pixman_renderer_flush_damage; - renderer->base.attach = pixman_renderer_attach; - renderer->base.surface_set_color = pixman_renderer_surface_set_color; - renderer->base.destroy = pixman_renderer_destroy; - renderer->base.surface_get_content_size = - pixman_renderer_surface_get_content_size; - renderer->base.surface_copy_content = - pixman_renderer_surface_copy_content; - ec->renderer = &renderer->base; - ec->capabilities |= WESTON_CAP_ROTATION_ANY; - ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK; - - renderer->debug_binding = - weston_compositor_add_debug_binding(ec, KEY_R, - debug_binding, ec); - - wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565); - - wl_signal_init(&renderer->destroy_signal); - - return 0; -} - -WL_EXPORT void -pixman_renderer_output_set_buffer(struct weston_output *output, - pixman_image_t *buffer) -{ - struct pixman_output_state *po = get_output_state(output); - - if (po->hw_buffer) - pixman_image_unref(po->hw_buffer); - po->hw_buffer = buffer; - - if (po->hw_buffer) { - output->compositor->read_format = pixman_image_get_format(po->hw_buffer); - pixman_image_ref(po->hw_buffer); - } -} - -WL_EXPORT void -pixman_renderer_output_set_hw_extra_damage(struct weston_output *output, - pixman_region32_t *extra_damage) -{ - struct pixman_output_state *po = get_output_state(output); - - po->hw_extra_damage = extra_damage; -} - -WL_EXPORT int -pixman_renderer_output_create(struct weston_output *output, uint32_t flags) -{ - struct pixman_output_state *po; - int w, h; - - po = zalloc(sizeof *po); - if (po == NULL) - return -1; - - if (flags & PIXMAN_RENDERER_OUTPUT_USE_SHADOW) { - /* set shadow image transformation */ - w = output->current_mode->width; - h = output->current_mode->height; - - po->shadow_buffer = malloc(w * h * 4); - - if (!po->shadow_buffer) { - free(po); - return -1; - } - - po->shadow_image = - pixman_image_create_bits(PIXMAN_x8r8g8b8, w, h, - po->shadow_buffer, w * 4); - - if (!po->shadow_image) { - free(po->shadow_buffer); - free(po); - return -1; - } - } - - output->renderer_state = po; - - return 0; -} - -WL_EXPORT void -pixman_renderer_output_destroy(struct weston_output *output) -{ - struct pixman_output_state *po = get_output_state(output); - - if (po->shadow_image) - pixman_image_unref(po->shadow_image); - - if (po->hw_buffer) - pixman_image_unref(po->hw_buffer); - - free(po->shadow_buffer); - - po->shadow_buffer = NULL; - po->shadow_image = NULL; - po->hw_buffer = NULL; - - free(po); -} diff --git a/libweston/pixman-renderer.h b/libweston/pixman-renderer.h deleted file mode 100644 index f53ae2a3..00000000 --- a/libweston/pixman-renderer.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com> - * - * 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 <libweston/libweston.h> -#include "backend.h" -#include "libweston-internal.h" - -int -pixman_renderer_init(struct weston_compositor *ec); - -enum pixman_renderer_output_flags { - PIXMAN_RENDERER_OUTPUT_USE_SHADOW = (1 << 0), -}; - -int -pixman_renderer_output_create(struct weston_output *output, uint32_t flags); - -void -pixman_renderer_output_set_buffer(struct weston_output *output, - pixman_image_t *buffer); - -void -pixman_renderer_output_set_hw_extra_damage(struct weston_output *output, - pixman_region32_t *extra_damage); - -void -pixman_renderer_output_destroy(struct weston_output *output); diff --git a/libweston/plugin-registry.c b/libweston/plugin-registry.c deleted file mode 100644 index f5ec6fa5..00000000 --- a/libweston/plugin-registry.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2016 DENSO CORPORATION - * - * 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 <assert.h> -#include <string.h> -#include <stdlib.h> - -#include <libweston/libweston.h> -#include <libweston/plugin-registry.h> - -struct weston_plugin_api { - struct wl_list link; /**< in weston_compositor::plugin_api_list */ - - char *api_name; /**< The search key */ - const void *vtable; /**< The function table */ - size_t vtable_size; /**< Size of the function table in bytes */ -}; - -/** Register an implementation of an API - * - * \param compositor The compositor instance. - * \param api_name The API name which other plugins use to find the - * implementation. - * \param vtable Pointer to the function table of the API. - * \param vtable_size Size of the function table in bytes. - * \return 0 on success, -1 on error, -2 if api_name already registered - * - * This call makes the given vtable to be reachable via - * weston_plugin_api_get(). Calls through the vtable may start happening - * as soon as the caller returns after success. Argument vtable must not be - * NULL. Argument api_name must be non-NULL and non-zero length. - * - * You can increase the function table size without breaking the ABI. - * To cater for ABI breaks, it is recommended to have api_name include a - * version number. - * - * A registered API cannot be unregistered. However, calls through a - * registered API must not be made from the compositor destroy signal handlers. - */ -WL_EXPORT int -weston_plugin_api_register(struct weston_compositor *compositor, - const char *api_name, - const void *vtable, - size_t vtable_size) -{ - struct weston_plugin_api *wpa; - - assert(api_name); - assert(strlen(api_name) > 0); - assert(vtable); - - if (!api_name || !vtable || strlen(api_name) == 0) - return -1; - - wl_list_for_each(wpa, &compositor->plugin_api_list, link) - if (strcmp(wpa->api_name, api_name) == 0) - return -2; - - wpa = zalloc(sizeof(*wpa)); - if (!wpa) - return -1; - - wpa->api_name = strdup(api_name); - wpa->vtable = vtable; - wpa->vtable_size = vtable_size; - - if (!wpa->api_name) { - free(wpa); - - return -1; - } - - wl_list_insert(&compositor->plugin_api_list, &wpa->link); - weston_log("Registered plugin API '%s' of size %zd\n", - wpa->api_name, wpa->vtable_size); - - return 0; -} - -/** Internal function to free registered APIs - * - * \param compositor The compositor instance. - */ -void -weston_plugin_api_destroy_list(struct weston_compositor *compositor) -{ - struct weston_plugin_api *wpa, *tmp; - - wl_list_for_each_safe(wpa, tmp, &compositor->plugin_api_list, link) { - free(wpa->api_name); - wl_list_remove(&wpa->link); - free(wpa); - } -} - -/** Fetch the implementation of an API - * - * \param compositor The compositor instance. - * \param api_name The name of the API to search for. - * \param vtable_size The expected function table size in bytes. - * \return Pointer to the function table, or NULL on error. - * - * Find the function table corresponding to api_name. The vtable_size here - * must be less or equal to the vtable_size given in the corresponding - * weston_plugin_api_register() call made by the implementing plugin. - * - * Calls can be made through the function table immediately. However, calls - * must not be made from or after the compositor destroy signal handler. - */ -WL_EXPORT const void * -weston_plugin_api_get(struct weston_compositor *compositor, - const char *api_name, - size_t vtable_size) -{ - struct weston_plugin_api *wpa; - - assert(api_name); - if (!api_name) - return NULL; - - wl_list_for_each(wpa, &compositor->plugin_api_list, link) { - if (strcmp(wpa->api_name, api_name) != 0) - continue; - - if (vtable_size <= wpa->vtable_size) - return wpa->vtable; - - return NULL; - } - - return NULL; -} diff --git a/libweston/renderer-gl/egl-glue.c b/libweston/renderer-gl/egl-glue.c deleted file mode 100644 index d96efeae..00000000 --- a/libweston/renderer-gl/egl-glue.c +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright © 2012 Intel Corporation - * Copyright © 2015, 2019 Collabora, Ltd. - * Copyright © 2016 NVIDIA Corporation - * - * 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 <assert.h> - -#include "shared/helpers.h" -#include "shared/platform.h" - -#include "gl-renderer.h" -#include "gl-renderer-internal.h" -#include "pixel-formats.h" -#include "shared/weston-egl-ext.h" - -#include <assert.h> - -struct egl_config_print_info { - const EGLint *attrs; - unsigned attrs_count; - const char *prefix; - const char *separator; - int field_width; -}; - -static const char * -egl_error_string(EGLint code) -{ -#define MYERRCODE(x) case x: return #x; - switch (code) { - MYERRCODE(EGL_SUCCESS) - MYERRCODE(EGL_NOT_INITIALIZED) - MYERRCODE(EGL_BAD_ACCESS) - MYERRCODE(EGL_BAD_ALLOC) - MYERRCODE(EGL_BAD_ATTRIBUTE) - MYERRCODE(EGL_BAD_CONTEXT) - MYERRCODE(EGL_BAD_CONFIG) - MYERRCODE(EGL_BAD_CURRENT_SURFACE) - MYERRCODE(EGL_BAD_DISPLAY) - MYERRCODE(EGL_BAD_SURFACE) - MYERRCODE(EGL_BAD_MATCH) - MYERRCODE(EGL_BAD_PARAMETER) - MYERRCODE(EGL_BAD_NATIVE_PIXMAP) - MYERRCODE(EGL_BAD_NATIVE_WINDOW) - MYERRCODE(EGL_CONTEXT_LOST) - default: - return "unknown"; - } -#undef MYERRCODE -} - -void -gl_renderer_print_egl_error_state(void) -{ - EGLint code; - - code = eglGetError(); - weston_log("EGL error state: %s (0x%04lx)\n", - egl_error_string(code), (long)code); -} - -static void -print_egl_surface_type_bits(FILE *fp, EGLint egl_surface_type) -{ - const char *sep = ""; - unsigned i; - - static const struct { - EGLint bit; - const char *str; - } egl_surf_bits[] = { - { EGL_WINDOW_BIT, "win" }, - { EGL_PIXMAP_BIT, "pix" }, - { EGL_PBUFFER_BIT, "pbf" }, - { EGL_MULTISAMPLE_RESOLVE_BOX_BIT, "ms_resolve_box" }, - { EGL_SWAP_BEHAVIOR_PRESERVED_BIT, "swap_preserved" }, - }; - - for (i = 0; i < ARRAY_LENGTH(egl_surf_bits); i++) { - if (egl_surface_type & egl_surf_bits[i].bit) { - fprintf(fp, "%s%s", sep, egl_surf_bits[i].str); - sep = "|"; - } - } -} - -static const struct egl_config_print_info config_info_ints[] = { -#define ARRAY(...) ((const EGLint[]) { __VA_ARGS__ }) - - { ARRAY(EGL_CONFIG_ID), 1, "id: ", "", 3 }, - { ARRAY(EGL_RED_SIZE, EGL_GREEN_SIZE, EGL_BLUE_SIZE, EGL_ALPHA_SIZE), 4, - "rgba: ", " ", 1 }, - { ARRAY(EGL_BUFFER_SIZE), 1, "buf: ", "", 2 }, - { ARRAY(EGL_DEPTH_SIZE), 1, "dep: ", "", 2 }, - { ARRAY(EGL_STENCIL_SIZE), 1, "stcl: ", "", 1 }, - { ARRAY(EGL_MIN_SWAP_INTERVAL, EGL_MAX_SWAP_INTERVAL), 2, - "int: ", "-", 1 }, - -#undef ARRAY -}; - -static void -print_egl_config_ints(FILE *fp, EGLDisplay egldpy, EGLConfig eglconfig) -{ - unsigned i; - - for (i = 0; i < ARRAY_LENGTH(config_info_ints); i++) { - const struct egl_config_print_info *info = &config_info_ints[i]; - unsigned j; - const char *sep = ""; - - fputs(info->prefix, fp); - for (j = 0; j < info->attrs_count; j++) { - EGLint value; - - if (eglGetConfigAttrib(egldpy, eglconfig, - info->attrs[j], &value)) { - fprintf(fp, "%s%*d", - sep, info->field_width, value); - } else { - fprintf(fp, "%s!", sep); - } - sep = info->separator; - } - - fputs(" ", fp); - } -} - -static void -print_egl_config_info(FILE *fp, EGLDisplay egldpy, EGLConfig eglconfig) -{ - EGLint value; - - print_egl_config_ints(fp, egldpy, eglconfig); - - fputs("type: ", fp); - if (eglGetConfigAttrib(egldpy, eglconfig, EGL_SURFACE_TYPE, &value)) - print_egl_surface_type_bits(fp, value); - else - fputs("-", fp); - - fputs(" vis_id: ", fp); - if (eglGetConfigAttrib(egldpy, eglconfig, EGL_NATIVE_VISUAL_ID, &value)) { - if (value != 0) { - const struct pixel_format_info *p; - - p = pixel_format_get_info(value); - if (p) { - fprintf(fp, "%s (0x%x)", - p->drm_format_name, (unsigned)value); - } else { - fprintf(fp, "0x%x", (unsigned)value); - } - } else { - fputs("0", fp); - } - } else { - fputs("-", fp); - } -} - -static void -log_all_egl_configs(EGLDisplay egldpy) -{ - EGLint count = 0; - EGLConfig *configs; - int i; - char *strbuf = NULL; - size_t strsize = 0; - FILE *fp; - - weston_log("All available EGLConfigs:\n"); - - if (!eglGetConfigs(egldpy, NULL, 0, &count) || count < 1) - return; - - configs = calloc(count, sizeof *configs); - if (!configs) - return; - - if (!eglGetConfigs(egldpy, configs, count, &count)) - return; - - fp = open_memstream(&strbuf, &strsize); - if (!fp) - goto out; - - for (i = 0; i < count; i++) { - print_egl_config_info(fp, egldpy, configs[i]); - fputc(0, fp); - fflush(fp); - weston_log_continue(STAMP_SPACE "%s\n", strbuf); - rewind(fp); - } - - fclose(fp); - free(strbuf); - -out: - free(configs); -} - -void -log_egl_config_info(EGLDisplay egldpy, EGLConfig eglconfig) -{ - char *strbuf = NULL; - size_t strsize = 0; - FILE *fp; - - fp = open_memstream(&strbuf, &strsize); - if (fp) { - print_egl_config_info(fp, egldpy, eglconfig); - fclose(fp); - } - - weston_log("Chosen EGL config details: %s\n", strbuf ? strbuf : "?"); - free(strbuf); -} - -static bool -egl_config_pixel_format_matches(struct gl_renderer *gr, - EGLConfig config, - const struct pixel_format_info *pinfo) -{ - static const EGLint attribs[4] = { - EGL_ALPHA_SIZE, EGL_RED_SIZE, EGL_GREEN_SIZE, EGL_BLUE_SIZE - }; - const int *argb[4] = { - &pinfo->bits.a, &pinfo->bits.r, &pinfo->bits.g, &pinfo->bits.b - }; - unsigned i; - EGLint value; - - if (gr->platform == EGL_PLATFORM_GBM_KHR) { - if (!eglGetConfigAttrib(gr->egl_display, config, - EGL_NATIVE_VISUAL_ID, &value)) - return false; - - return ((uint32_t)value) == pinfo->format; - } - - for (i = 0; i < 4; i++) { - if (!eglGetConfigAttrib(gr->egl_display, config, - attribs[i], &value)) - return false; - if (value != *argb[i]) - return false; - } - - return true; -} - -static int -egl_choose_config(struct gl_renderer *gr, - const EGLint *attribs, - const struct pixel_format_info *const *pinfo, - unsigned pinfo_count, - EGLConfig *config_out) -{ - EGLint count = 0; - EGLint matched = 0; - EGLConfig *configs; - unsigned i; - EGLint j; - int config_index = -1; - - if (!eglGetConfigs(gr->egl_display, NULL, 0, &count) || count < 1) { - weston_log("No EGL configs to choose from.\n"); - return -1; - } - configs = calloc(count, sizeof *configs); - if (!configs) - return -1; - - if (!eglChooseConfig(gr->egl_display, attribs, configs, - count, &matched) || !matched) { - weston_log("No EGL configs with appropriate attributes.\n"); - goto out; - } - - if (pinfo_count == 0) - config_index = 0; - - for (i = 0; config_index == -1 && i < pinfo_count; i++) - for (j = 0; config_index == -1 && j < matched; j++) - if (egl_config_pixel_format_matches(gr, configs[j], - pinfo[i])) - config_index = j; - - if (config_index != -1) - *config_out = configs[config_index]; - -out: - free(configs); - if (config_index == -1) - return -1; - - if (i > 1) - weston_log("Unable to use first choice EGL config with" - " %s, succeeded with alternate %s.\n", - pinfo[0]->drm_format_name, - pinfo[i - 1]->drm_format_name); - return 0; -} - -static bool -egl_config_is_compatible(struct gl_renderer *gr, - EGLConfig config, - EGLint egl_surface_type, - const struct pixel_format_info *const *pinfo, - unsigned pinfo_count) -{ - EGLint value; - unsigned i; - - if (config == EGL_NO_CONFIG_KHR) - return false; - - if (!eglGetConfigAttrib(gr->egl_display, config, - EGL_SURFACE_TYPE, &value)) - return false; - if ((value & egl_surface_type) != egl_surface_type) - return false; - - for (i = 0; i < pinfo_count; i++) { - if (egl_config_pixel_format_matches(gr, config, pinfo[i])) - return true; - } - return false; -} - -/* The caller must free() the string */ -static char * -explain_egl_config_criteria(EGLint egl_surface_type, - const struct pixel_format_info *const *pinfo, - unsigned pinfo_count) -{ - FILE *fp; - char *str = NULL; - size_t size = 0; - const char *sep; - unsigned i; - - fp = open_memstream(&str, &size); - if (!fp) - return NULL; - - fputs("{ ", fp); - - print_egl_surface_type_bits(fp, egl_surface_type); - fputs("; ", fp); - - sep = ""; - for (i = 0; i < pinfo_count; i++) { - fprintf(fp, "%s%s", sep, pinfo[i]->drm_format_name); - sep = ", "; - } - - fputs(" }", fp); - - fclose(fp); - - return str; -} - -EGLConfig -gl_renderer_get_egl_config(struct gl_renderer *gr, - EGLint egl_surface_type, - const uint32_t *drm_formats, - unsigned drm_formats_count) -{ - EGLConfig egl_config; - const struct pixel_format_info *pinfo[16]; - unsigned pinfo_count; - unsigned i; - char *what; - EGLint config_attribs[] = { - EGL_SURFACE_TYPE, egl_surface_type, - EGL_RED_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_BLUE_SIZE, 1, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_NONE - }; - - assert(drm_formats_count < ARRAY_LENGTH(pinfo)); - drm_formats_count = MIN(drm_formats_count, ARRAY_LENGTH(pinfo)); - - for (pinfo_count = 0, i = 0; i < drm_formats_count; i++) { - pinfo[pinfo_count] = pixel_format_get_info(drm_formats[i]); - if (!pinfo[pinfo_count]) { - weston_log("Bad/unknown DRM format code 0x%08x.\n", - drm_formats[i]); - continue; - } - pinfo_count++; - } - - if (egl_config_is_compatible(gr, gr->egl_config, egl_surface_type, - pinfo, pinfo_count)) - return gr->egl_config; - - if (egl_choose_config(gr, config_attribs, pinfo, pinfo_count, - &egl_config) < 0) { - what = explain_egl_config_criteria(egl_surface_type, - pinfo, pinfo_count); - weston_log("No EGLConfig matches %s.\n", what); - free(what); - log_all_egl_configs(gr->egl_display); - return EGL_NO_CONFIG_KHR; - } - - /* - * If we do not have configless context support, all EGLConfigs must - * be the one and the same, because we use just one GL context for - * everything. - */ - if (gr->egl_config != EGL_NO_CONFIG_KHR && - egl_config != gr->egl_config) { - what = explain_egl_config_criteria(egl_surface_type, - pinfo, pinfo_count); - weston_log("Found an EGLConfig matching %s but it is not usable" - " because neither EGL_KHR_no_config_context nor " - "EGL_MESA_configless_context are supported by EGL.\n", - what); - free(what); - return EGL_NO_CONFIG_KHR; - } - - return egl_config; -} - -static void -renderer_setup_egl_client_extensions(struct gl_renderer *gr) -{ - const char *extensions; - - extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); - if (!extensions) { - weston_log("Retrieving EGL client extension string failed.\n"); - return; - } - - if (weston_check_egl_extension(extensions, "EGL_EXT_platform_base")) - gr->create_platform_window = - (void *) eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"); - else - weston_log("warning: EGL_EXT_platform_base not supported.\n"); -} - -int -gl_renderer_setup_egl_extensions(struct weston_compositor *ec) -{ - static const struct { - char *extension, *entrypoint; - } swap_damage_ext_to_entrypoint[] = { - { - .extension = "EGL_EXT_swap_buffers_with_damage", - .entrypoint = "eglSwapBuffersWithDamageEXT", - }, - { - .extension = "EGL_KHR_swap_buffers_with_damage", - .entrypoint = "eglSwapBuffersWithDamageKHR", - }, - }; - struct gl_renderer *gr = get_renderer(ec); - const char *extensions; - EGLBoolean ret; - unsigned i; - - gr->create_image = (void *) eglGetProcAddress("eglCreateImageKHR"); - gr->destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR"); - - gr->bind_display = - (void *) eglGetProcAddress("eglBindWaylandDisplayWL"); - gr->unbind_display = - (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL"); - gr->query_buffer = - (void *) eglGetProcAddress("eglQueryWaylandBufferWL"); - gr->set_damage_region = - (void *) eglGetProcAddress("eglSetDamageRegionKHR"); - - extensions = - (const char *) eglQueryString(gr->egl_display, EGL_EXTENSIONS); - if (!extensions) { - weston_log("Retrieving EGL extension string failed.\n"); - return -1; - } - - if (weston_check_egl_extension(extensions, "EGL_IMG_context_priority")) - gr->has_context_priority = true; - - if (weston_check_egl_extension(extensions, "EGL_WL_bind_wayland_display")) - gr->has_bind_display = true; - if (gr->has_bind_display) { - assert(gr->bind_display); - assert(gr->unbind_display); - assert(gr->query_buffer); - ret = gr->bind_display(gr->egl_display, ec->wl_display); - if (!ret) - gr->has_bind_display = false; - } - - if (weston_check_egl_extension(extensions, "EGL_EXT_buffer_age")) - gr->has_egl_buffer_age = true; - - if (weston_check_egl_extension(extensions, "EGL_KHR_partial_update")) { - assert(gr->set_damage_region); - gr->has_egl_partial_update = true; - } - - for (i = 0; i < ARRAY_LENGTH(swap_damage_ext_to_entrypoint); i++) { - if (weston_check_egl_extension(extensions, - swap_damage_ext_to_entrypoint[i].extension)) { - gr->swap_buffers_with_damage = - (void *) eglGetProcAddress( - swap_damage_ext_to_entrypoint[i].entrypoint); - assert(gr->swap_buffers_with_damage); - break; - } - } - - if (weston_check_egl_extension(extensions, "EGL_KHR_no_config_context") || - weston_check_egl_extension(extensions, "EGL_MESA_configless_context")) - gr->has_configless_context = true; - - if (weston_check_egl_extension(extensions, "EGL_KHR_surfaceless_context")) - gr->has_surfaceless_context = true; - - if (weston_check_egl_extension(extensions, "EGL_EXT_image_dma_buf_import")) - gr->has_dmabuf_import = true; - - if (weston_check_egl_extension(extensions, - "EGL_EXT_image_dma_buf_import_modifiers")) { - gr->query_dmabuf_formats = - (void *) eglGetProcAddress("eglQueryDmaBufFormatsEXT"); - gr->query_dmabuf_modifiers = - (void *) eglGetProcAddress("eglQueryDmaBufModifiersEXT"); - assert(gr->query_dmabuf_formats); - assert(gr->query_dmabuf_modifiers); - gr->has_dmabuf_import_modifiers = true; - } - - if (weston_check_egl_extension(extensions, "EGL_KHR_fence_sync") && - weston_check_egl_extension(extensions, "EGL_ANDROID_native_fence_sync")) { - gr->create_sync = - (void *) eglGetProcAddress("eglCreateSyncKHR"); - gr->destroy_sync = - (void *) eglGetProcAddress("eglDestroySyncKHR"); - gr->dup_native_fence_fd = - (void *) eglGetProcAddress("eglDupNativeFenceFDANDROID"); - assert(gr->create_sync); - assert(gr->destroy_sync); - assert(gr->dup_native_fence_fd); - gr->has_native_fence_sync = true; - } else { - weston_log("warning: Disabling render GPU timeline and explicit " - "synchronization due to missing " - "EGL_ANDROID_native_fence_sync extension\n"); - } - - if (weston_check_egl_extension(extensions, "EGL_KHR_wait_sync")) { - gr->wait_sync = (void *) eglGetProcAddress("eglWaitSyncKHR"); - assert(gr->wait_sync); - gr->has_wait_sync = true; - } else { - weston_log("warning: Disabling explicit synchronization due" - "to missing EGL_KHR_wait_sync extension\n"); - } - - renderer_setup_egl_client_extensions(gr); - - return 0; -} diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h deleted file mode 100644 index 5f8bac10..00000000 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright © 2019 Collabora, Ltd. - * - * 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. - */ - -#ifndef GL_RENDERER_INTERNAL_H -#define GL_RENDERER_INTERNAL_H - -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include "shared/weston-egl-ext.h" /* for PFN* stuff */ - -struct gl_shader { - GLuint program; - GLuint vertex_shader, fragment_shader; - GLint proj_uniform; - GLint tex_uniforms[3]; - GLint alpha_uniform; - GLint color_uniform; - const char *vertex_source, *fragment_source; -}; - -struct gl_renderer { - struct weston_renderer base; - bool fragment_shader_debug; - bool fan_debug; - struct weston_binding *fragment_binding; - struct weston_binding *fan_binding; - - EGLenum platform; - EGLDisplay egl_display; - EGLContext egl_context; - EGLConfig egl_config; - - EGLSurface dummy_surface; - - uint32_t gl_version; - - struct wl_array vertices; - struct wl_array vtxcnt; - - PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d; - PFNEGLCREATEIMAGEKHRPROC create_image; - PFNEGLDESTROYIMAGEKHRPROC destroy_image; - PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage; - PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC create_platform_window; - - bool has_unpack_subimage; - - PFNEGLBINDWAYLANDDISPLAYWL bind_display; - PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display; - PFNEGLQUERYWAYLANDBUFFERWL query_buffer; - bool has_bind_display; - - bool has_context_priority; - - bool has_egl_image_external; - - bool has_egl_buffer_age; - bool has_egl_partial_update; - PFNEGLSETDAMAGEREGIONKHRPROC set_damage_region; - - bool has_configless_context; - - bool has_surfaceless_context; - - bool has_dmabuf_import; - struct wl_list dmabuf_images; - - bool has_gl_texture_rg; - - struct gl_shader texture_shader_rgba; - struct gl_shader texture_shader_rgbx; - struct gl_shader texture_shader_egl_external; - struct gl_shader texture_shader_y_uv; - struct gl_shader texture_shader_y_u_v; - struct gl_shader texture_shader_y_xuxv; - struct gl_shader texture_shader_xyuv; - struct gl_shader invert_color_shader; - struct gl_shader solid_shader; - struct gl_shader *current_shader; - - struct wl_signal destroy_signal; - - struct wl_listener output_destroy_listener; - - bool has_dmabuf_import_modifiers; - PFNEGLQUERYDMABUFFORMATSEXTPROC query_dmabuf_formats; - PFNEGLQUERYDMABUFMODIFIERSEXTPROC query_dmabuf_modifiers; - - bool has_native_fence_sync; - PFNEGLCREATESYNCKHRPROC create_sync; - PFNEGLDESTROYSYNCKHRPROC destroy_sync; - PFNEGLDUPNATIVEFENCEFDANDROIDPROC dup_native_fence_fd; - - bool has_wait_sync; - PFNEGLWAITSYNCKHRPROC wait_sync; - - void *wl_tbm_server; -}; - -static inline struct gl_renderer * -get_renderer(struct weston_compositor *ec) -{ - return (struct gl_renderer *)ec->renderer; -} - -void -gl_renderer_print_egl_error_state(void); - -void -log_egl_config_info(EGLDisplay egldpy, EGLConfig eglconfig); - -EGLConfig -gl_renderer_get_egl_config(struct gl_renderer *gr, - EGLint egl_surface_type, - const uint32_t *drm_formats, - unsigned drm_formats_count); - -int -gl_renderer_setup_egl_extensions(struct weston_compositor *ec); - -#endif /* GL_RENDERER_INTERNAL_H */ diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c deleted file mode 100644 index 967827e6..00000000 --- a/libweston/renderer-gl/gl-renderer.c +++ /dev/null @@ -1,3943 +0,0 @@ -/* - * Copyright © 2012 Intel Corporation - * Copyright © 2015,2019 Collabora, Ltd. - * Copyright © 2016 NVIDIA Corporation - * - * 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 <GLES2/gl2.h> -#include <GLES2/gl2ext.h> - -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <float.h> -#include <assert.h> -#include <linux/input.h> -#include <drm_fourcc.h> -#include <unistd.h> - -#include "linux-sync-file.h" -#include "timeline.h" - -#include "gl-renderer.h" -#include "gl-renderer-internal.h" -#include "vertex-clipping.h" -#include "linux-dmabuf.h" -#include "linux-dmabuf-unstable-v1-server-protocol.h" -#include "linux-explicit-synchronization.h" -#include "pixel-formats.h" - -#include "shared/fd-util.h" -#include "shared/helpers.h" -#include "shared/platform.h" -#include "shared/timespec-util.h" -#include "shared/weston-egl-ext.h" - -#ifdef HAVE_TBM -#include <tbm_bufmgr.h> -#include <wayland-tbm-server.h> -#endif - -#define GR_GL_VERSION(major, minor) \ - (((uint32_t)(major) << 16) | (uint32_t)(minor)) - -#define GR_GL_VERSION_INVALID \ - GR_GL_VERSION(0, 0) - -#define BUFFER_DAMAGE_COUNT 2 - -enum gl_border_status { - BORDER_STATUS_CLEAN = 0, - BORDER_TOP_DIRTY = 1 << GL_RENDERER_BORDER_TOP, - BORDER_LEFT_DIRTY = 1 << GL_RENDERER_BORDER_LEFT, - BORDER_RIGHT_DIRTY = 1 << GL_RENDERER_BORDER_RIGHT, - BORDER_BOTTOM_DIRTY = 1 << GL_RENDERER_BORDER_BOTTOM, - BORDER_ALL_DIRTY = 0xf, - BORDER_SIZE_CHANGED = 0x10 -}; - -struct gl_border_image { - GLuint tex; - int32_t width, height; - int32_t tex_width; - void *data; -}; - -struct gl_output_state { - EGLSurface egl_surface; - pixman_region32_t buffer_damage[BUFFER_DAMAGE_COUNT]; - int buffer_damage_index; - enum gl_border_status border_damage[BUFFER_DAMAGE_COUNT]; - struct gl_border_image borders[4]; - enum gl_border_status border_status; - - struct weston_matrix output_matrix; - - EGLSyncKHR begin_render_sync, end_render_sync; - - /* struct timeline_render_point::link */ - struct wl_list timeline_render_point_list; -}; - -enum buffer_type { - BUFFER_TYPE_NULL, - BUFFER_TYPE_SOLID, /* internal solid color surfaces without a buffer */ - BUFFER_TYPE_SHM, - BUFFER_TYPE_EGL -}; - -struct gl_renderer; - -struct egl_image { - struct gl_renderer *renderer; - EGLImageKHR image; - int refcount; -}; - -enum import_type { - IMPORT_TYPE_INVALID, - IMPORT_TYPE_DIRECT, - IMPORT_TYPE_GL_CONVERSION -}; - -struct dmabuf_image { - struct linux_dmabuf_buffer *dmabuf; - int num_images; - struct egl_image *images[3]; - struct wl_list link; - - enum import_type import_type; - GLenum target; - struct gl_shader *shader; -}; - -struct yuv_plane_descriptor { - int width_divisor; - int height_divisor; - uint32_t format; - int plane_index; -}; - -enum texture_type { - TEXTURE_Y_XUXV_WL, - TEXTURE_Y_UV_WL, - TEXTURE_Y_U_V_WL, - TEXTURE_XYUV_WL -}; - -struct yuv_format_descriptor { - uint32_t format; - int input_planes; - int output_planes; - enum texture_type texture_type; - struct yuv_plane_descriptor plane[4]; -}; - -struct gl_surface_state { - GLfloat color[4]; - struct gl_shader *shader; - - GLuint textures[3]; - int num_textures; - bool needs_full_upload; - pixman_region32_t texture_damage; - - /* These are only used by SHM surfaces to detect when we need - * to do a full upload to specify a new internal texture - * format */ - GLenum gl_format[3]; - GLenum gl_pixel_type; - - struct egl_image* images[3]; - GLenum target; - int num_images; - - struct weston_buffer_reference buffer_ref; - struct weston_buffer_release_reference buffer_release_ref; - enum buffer_type buffer_type; - int pitch; /* in pixels */ - int height; /* in pixels */ - bool y_inverted; - bool direct_display; - - /* Extension needed for SHM YUV texture */ - int offset[3]; /* offset per plane */ - int hsub[3]; /* horizontal subsampling per plane */ - int vsub[3]; /* vertical subsampling per plane */ - - struct weston_surface *surface; - - /* Whether this surface was used in the current output repaint. - Used only in the context of a gl_renderer_repaint_output call. */ - bool used_in_output_repaint; - - struct wl_listener surface_destroy_listener; - struct wl_listener renderer_destroy_listener; -}; - -enum timeline_render_point_type { - TIMELINE_RENDER_POINT_TYPE_BEGIN, - TIMELINE_RENDER_POINT_TYPE_END -}; - -struct timeline_render_point { - struct wl_list link; /* gl_output_state::timeline_render_point_list */ - - enum timeline_render_point_type type; - int fd; - struct weston_output *output; - struct wl_event_source *event_source; -}; - -static PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL; - -static inline const char * -dump_format(uint32_t format, char out[4]) -{ -#if BYTE_ORDER == BIG_ENDIAN - format = __builtin_bswap32(format); -#endif - memcpy(out, &format, 4); - return out; -} - -static inline struct gl_output_state * -get_output_state(struct weston_output *output) -{ - return (struct gl_output_state *)output->renderer_state; -} - -static int -gl_renderer_create_surface(struct weston_surface *surface); - -static inline struct gl_surface_state * -get_surface_state(struct weston_surface *surface) -{ - if (!surface->renderer_state) - gl_renderer_create_surface(surface); - - return (struct gl_surface_state *)surface->renderer_state; -} - -static void -timeline_render_point_destroy(struct timeline_render_point *trp) -{ - wl_list_remove(&trp->link); - wl_event_source_remove(trp->event_source); - close(trp->fd); - free(trp); -} - -static int -timeline_render_point_handler(int fd, uint32_t mask, void *data) -{ - struct timeline_render_point *trp = data; - const char *tp_name = trp->type == TIMELINE_RENDER_POINT_TYPE_BEGIN ? - "renderer_gpu_begin" : "renderer_gpu_end"; - - if (mask & WL_EVENT_READABLE) { - struct timespec tspec = { 0 }; - - if (weston_linux_sync_file_read_timestamp(trp->fd, - &tspec) == 0) { - TL_POINT(trp->output->compositor, tp_name, TLP_GPU(&tspec), - TLP_OUTPUT(trp->output), TLP_END); - } - } - - timeline_render_point_destroy(trp); - - return 0; -} - -static EGLSyncKHR -create_render_sync(struct gl_renderer *gr) -{ - static const EGLint attribs[] = { EGL_NONE }; - - if (!gr->has_native_fence_sync) - return EGL_NO_SYNC_KHR; - - return gr->create_sync(gr->egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, - attribs); -} - -static void -timeline_submit_render_sync(struct gl_renderer *gr, - struct weston_compositor *ec, - struct weston_output *output, - EGLSyncKHR sync, - enum timeline_render_point_type type) -{ - struct gl_output_state *go; - struct wl_event_loop *loop; - int fd; - struct timeline_render_point *trp; - - if (!weston_log_scope_is_enabled(ec->timeline) || - !gr->has_native_fence_sync || - sync == EGL_NO_SYNC_KHR) - return; - - go = get_output_state(output); - loop = wl_display_get_event_loop(ec->wl_display); - - fd = gr->dup_native_fence_fd(gr->egl_display, sync); - if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) - return; - - trp = zalloc(sizeof *trp); - if (trp == NULL) { - close(fd); - return; - } - - trp->type = type; - trp->fd = fd; - trp->output = output; - trp->event_source = wl_event_loop_add_fd(loop, fd, - WL_EVENT_READABLE, - timeline_render_point_handler, - trp); - - wl_list_insert(&go->timeline_render_point_list, &trp->link); -} - -static struct egl_image* -egl_image_create(struct gl_renderer *gr, EGLenum target, - EGLClientBuffer buffer, const EGLint *attribs) -{ - struct egl_image *img; - - img = zalloc(sizeof *img); - img->renderer = gr; - img->refcount = 1; - img->image = gr->create_image(gr->egl_display, EGL_NO_CONTEXT, - target, buffer, attribs); - - if (img->image == EGL_NO_IMAGE_KHR) { - free(img); - return NULL; - } - - return img; -} - -static struct egl_image* -egl_image_ref(struct egl_image *image) -{ - image->refcount++; - - return image; -} - -static int -egl_image_unref(struct egl_image *image) -{ - struct gl_renderer *gr = image->renderer; - - assert(image->refcount > 0); - - image->refcount--; - if (image->refcount > 0) - return image->refcount; - - gr->destroy_image(gr->egl_display, image->image); - free(image); - - return 0; -} - -static struct dmabuf_image* -dmabuf_image_create(void) -{ - struct dmabuf_image *img; - - img = zalloc(sizeof *img); - wl_list_init(&img->link); - - return img; -} - -static void -dmabuf_image_destroy(struct dmabuf_image *image) -{ - int i; - - for (i = 0; i < image->num_images; ++i) - egl_image_unref(image->images[i]); - - if (image->dmabuf) - linux_dmabuf_buffer_set_user_data(image->dmabuf, NULL, NULL); - - wl_list_remove(&image->link); - free(image); -} - -#define max(a, b) (((a) > (b)) ? (a) : (b)) -#define min(a, b) (((a) > (b)) ? (b) : (a)) - -/* - * Compute the boundary vertices of the intersection of the global coordinate - * aligned rectangle 'rect', and an arbitrary quadrilateral produced from - * 'surf_rect' when transformed from surface coordinates into global coordinates. - * The vertices are written to 'ex' and 'ey', and the return value is the - * number of vertices. Vertices are produced in clockwise winding order. - * Guarantees to produce either zero vertices, or 3-8 vertices with non-zero - * polygon area. - */ -static int -calculate_edges(struct weston_view *ev, pixman_box32_t *rect, - pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey) -{ - - struct clip_context ctx; - int i, n; - GLfloat min_x, max_x, min_y, max_y; - struct polygon8 surf = { - { surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 }, - { surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 }, - 4 - }; - - ctx.clip.x1 = rect->x1; - ctx.clip.y1 = rect->y1; - ctx.clip.x2 = rect->x2; - ctx.clip.y2 = rect->y2; - - /* transform surface to screen space: */ - for (i = 0; i < surf.n; i++) - weston_view_to_global_float(ev, surf.x[i], surf.y[i], - &surf.x[i], &surf.y[i]); - - /* find bounding box: */ - min_x = max_x = surf.x[0]; - min_y = max_y = surf.y[0]; - - for (i = 1; i < surf.n; i++) { - min_x = min(min_x, surf.x[i]); - max_x = max(max_x, surf.x[i]); - min_y = min(min_y, surf.y[i]); - max_y = max(max_y, surf.y[i]); - } - - /* First, simple bounding box check to discard early transformed - * surface rects that do not intersect with the clip region: - */ - if ((min_x >= ctx.clip.x2) || (max_x <= ctx.clip.x1) || - (min_y >= ctx.clip.y2) || (max_y <= ctx.clip.y1)) - return 0; - - /* Simple case, bounding box edges are parallel to surface edges, - * there will be only four edges. We just need to clip the surface - * vertices to the clip rect bounds: - */ - if (!ev->transform.enabled) - return clip_simple(&ctx, &surf, ex, ey); - - /* Transformed case: use a general polygon clipping algorithm to - * clip the surface rectangle with each side of 'rect'. - * The algorithm is Sutherland-Hodgman, as explained in - * http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm - * but without looking at any of that code. - */ - n = clip_transformed(&ctx, &surf, ex, ey); - - if (n < 3) - return 0; - - return n; -} - -static bool -merge_down(pixman_box32_t *a, pixman_box32_t *b, pixman_box32_t *merge) -{ - if (a->x1 == b->x1 && a->x2 == b->x2 && a->y1 == b->y2) { - merge->x1 = a->x1; - merge->x2 = a->x2; - merge->y1 = b->y1; - merge->y2 = a->y2; - return true; - } - return false; -} - -static int -compress_bands(pixman_box32_t *inrects, int nrects, - pixman_box32_t **outrects) -{ - bool merged = false; - pixman_box32_t *out, merge_rect; - int i, j, nout; - - if (!nrects) { - *outrects = NULL; - return 0; - } - - /* nrects is an upper bound - we're not too worried about - * allocating a little extra - */ - out = malloc(sizeof(pixman_box32_t) * nrects); - out[0] = inrects[0]; - nout = 1; - for (i = 1; i < nrects; i++) { - for (j = 0; j < nout; j++) { - merged = merge_down(&inrects[i], &out[j], &merge_rect); - if (merged) { - out[j] = merge_rect; - break; - } - } - if (!merged) { - out[nout] = inrects[i]; - nout++; - } - } - *outrects = out; - return nout; -} - -static int -texture_region(struct weston_view *ev, pixman_region32_t *region, - pixman_region32_t *surf_region) -{ - struct gl_surface_state *gs = get_surface_state(ev->surface); - struct weston_compositor *ec = ev->surface->compositor; - struct gl_renderer *gr = get_renderer(ec); - GLfloat *v, inv_width, inv_height; - unsigned int *vtxcnt, nvtx = 0; - pixman_box32_t *rects, *surf_rects; - pixman_box32_t *raw_rects; - int i, j, k, nrects, nsurf, raw_nrects; - bool used_band_compression; - raw_rects = pixman_region32_rectangles(region, &raw_nrects); - surf_rects = pixman_region32_rectangles(surf_region, &nsurf); - - if (raw_nrects < 4) { - used_band_compression = false; - nrects = raw_nrects; - rects = raw_rects; - } else { - nrects = compress_bands(raw_rects, raw_nrects, &rects); - used_band_compression = true; - } - /* worst case we can have 8 vertices per rect (ie. clipped into - * an octagon): - */ - v = wl_array_add(&gr->vertices, nrects * nsurf * 8 * 4 * sizeof *v); - vtxcnt = wl_array_add(&gr->vtxcnt, nrects * nsurf * sizeof *vtxcnt); - - inv_width = 1.0 / gs->pitch; - inv_height = 1.0 / gs->height; - - for (i = 0; i < nrects; i++) { - pixman_box32_t *rect = &rects[i]; - for (j = 0; j < nsurf; j++) { - pixman_box32_t *surf_rect = &surf_rects[j]; - GLfloat sx, sy, bx, by; - GLfloat ex[8], ey[8]; /* edge points in screen space */ - int n; - - /* The transformed surface, after clipping to the clip region, - * can have as many as eight sides, emitted as a triangle-fan. - * The first vertex in the triangle fan can be chosen arbitrarily, - * since the area is guaranteed to be convex. - * - * If a corner of the transformed surface falls outside of the - * clip region, instead of emitting one vertex for the corner - * of the surface, up to two are emitted for two corresponding - * intersection point(s) between the surface and the clip region. - * - * To do this, we first calculate the (up to eight) points that - * form the intersection of the clip rect and the transformed - * surface. - */ - n = calculate_edges(ev, rect, surf_rect, ex, ey); - if (n < 3) - continue; - - /* emit edge points: */ - for (k = 0; k < n; k++) { - weston_view_from_global_float(ev, ex[k], ey[k], - &sx, &sy); - /* position: */ - *(v++) = ex[k]; - *(v++) = ey[k]; - /* texcoord: */ - weston_surface_to_buffer_float(ev->surface, - sx, sy, - &bx, &by); - *(v++) = bx * inv_width; - if (gs->y_inverted) { - *(v++) = by * inv_height; - } else { - *(v++) = (gs->height - by) * inv_height; - } - } - - vtxcnt[nvtx++] = n; - } - } - - if (used_band_compression) - free(rects); - return nvtx; -} - -static void -triangle_fan_debug(struct weston_view *view, int first, int count) -{ - struct weston_compositor *compositor = view->surface->compositor; - struct gl_renderer *gr = get_renderer(compositor); - int i; - GLushort *buffer; - GLushort *index; - int nelems; - static int color_idx = 0; - static const GLfloat color[][4] = { - { 1.0, 0.0, 0.0, 1.0 }, - { 0.0, 1.0, 0.0, 1.0 }, - { 0.0, 0.0, 1.0, 1.0 }, - { 1.0, 1.0, 1.0, 1.0 }, - }; - - nelems = (count - 1 + count - 2) * 2; - - buffer = malloc(sizeof(GLushort) * nelems); - index = buffer; - - for (i = 1; i < count; i++) { - *index++ = first; - *index++ = first + i; - } - - for (i = 2; i < count; i++) { - *index++ = first + i - 1; - *index++ = first + i; - } - - glUseProgram(gr->solid_shader.program); - glUniform4fv(gr->solid_shader.color_uniform, 1, - color[color_idx++ % ARRAY_LENGTH(color)]); - glDrawElements(GL_LINES, nelems, GL_UNSIGNED_SHORT, buffer); - glUseProgram(gr->current_shader->program); - free(buffer); -} - -static void -repaint_region(struct weston_view *ev, pixman_region32_t *region, - pixman_region32_t *surf_region) -{ - struct weston_compositor *ec = ev->surface->compositor; - struct gl_renderer *gr = get_renderer(ec); - GLfloat *v; - unsigned int *vtxcnt; - int i, first, nfans; - - /* The final region to be painted is the intersection of - * 'region' and 'surf_region'. However, 'region' is in the global - * coordinates, and 'surf_region' is in the surface-local - * coordinates. texture_region() will iterate over all pairs of - * rectangles from both regions, compute the intersection - * polygon for each pair, and store it as a triangle fan if - * it has a non-zero area (at least 3 vertices, actually). - */ - nfans = texture_region(ev, region, surf_region); - - v = gr->vertices.data; - vtxcnt = gr->vtxcnt.data; - - /* position: */ - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[0]); - glEnableVertexAttribArray(0); - - /* texcoord: */ - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]); - glEnableVertexAttribArray(1); - - for (i = 0, first = 0; i < nfans; i++) { - glDrawArrays(GL_TRIANGLE_FAN, first, vtxcnt[i]); - if (gr->fan_debug) - triangle_fan_debug(ev, first, vtxcnt[i]); - first += vtxcnt[i]; - } - - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(0); - - gr->vertices.size = 0; - gr->vtxcnt.size = 0; -} - -static int -use_output(struct weston_output *output) -{ - static int errored; - struct gl_output_state *go = get_output_state(output); - struct gl_renderer *gr = get_renderer(output->compositor); - EGLBoolean ret; - - ret = eglMakeCurrent(gr->egl_display, go->egl_surface, - go->egl_surface, gr->egl_context); - - if (ret == EGL_FALSE) { - if (errored) - return -1; - errored = 1; - weston_log("Failed to make EGL context current.\n"); - gl_renderer_print_egl_error_state(); - return -1; - } - - return 0; -} - -static int -shader_init(struct gl_shader *shader, struct gl_renderer *gr, - const char *vertex_source, const char *fragment_source); - -static void -use_shader(struct gl_renderer *gr, struct gl_shader *shader) -{ - if (!shader->program) { - int ret; - - ret = shader_init(shader, gr, - shader->vertex_source, - shader->fragment_source); - - if (ret < 0) - weston_log("warning: failed to compile shader\n"); - } - - if (gr->current_shader == shader) - return; - glUseProgram(shader->program); - gr->current_shader = shader; -} - -static void -shader_uniforms(struct gl_shader *shader, - struct weston_view *view, - struct weston_output *output) -{ - int i; - struct gl_surface_state *gs = get_surface_state(view->surface); - struct gl_output_state *go = get_output_state(output); - - glUniformMatrix4fv(shader->proj_uniform, - 1, GL_FALSE, go->output_matrix.d); - glUniform4fv(shader->color_uniform, 1, gs->color); - glUniform1f(shader->alpha_uniform, view->alpha); - - for (i = 0; i < gs->num_textures; i++) - glUniform1i(shader->tex_uniforms[i], i); -} - -static int -ensure_surface_buffer_is_ready(struct gl_renderer *gr, - struct gl_surface_state *gs) -{ - EGLint attribs[] = { - EGL_SYNC_NATIVE_FENCE_FD_ANDROID, - -1, - EGL_NONE - }; - struct weston_surface *surface = gs->surface; - struct weston_buffer *buffer = gs->buffer_ref.buffer; - EGLSyncKHR sync; - EGLint wait_ret; - EGLint destroy_ret; - - if (!buffer) - return 0; - - if (surface->acquire_fence_fd < 0) - return 0; - - /* We should only get a fence if we support EGLSyncKHR, since - * we don't advertise the explicit sync protocol otherwise. */ - assert(gr->has_native_fence_sync); - /* We should only get a fence for non-SHM buffers, since surface - * commit would have failed otherwise. */ - assert(wl_shm_buffer_get(buffer->resource) == NULL); - - attribs[1] = dup(surface->acquire_fence_fd); - if (attribs[1] == -1) { - linux_explicit_synchronization_send_server_error( - gs->surface->synchronization_resource, - "Failed to dup acquire fence"); - return -1; - } - - sync = gr->create_sync(gr->egl_display, - EGL_SYNC_NATIVE_FENCE_ANDROID, - attribs); - if (sync == EGL_NO_SYNC_KHR) { - linux_explicit_synchronization_send_server_error( - gs->surface->synchronization_resource, - "Failed to create EGLSyncKHR object"); - close(attribs[1]); - return -1; - } - - wait_ret = gr->wait_sync(gr->egl_display, sync, 0); - if (wait_ret == EGL_FALSE) { - linux_explicit_synchronization_send_server_error( - gs->surface->synchronization_resource, - "Failed to wait on EGLSyncKHR object"); - /* Continue to try to destroy the sync object. */ - } - - - destroy_ret = gr->destroy_sync(gr->egl_display, sync); - if (destroy_ret == EGL_FALSE) { - linux_explicit_synchronization_send_server_error( - gs->surface->synchronization_resource, - "Failed to destroy on EGLSyncKHR object"); - } - - return (wait_ret == EGL_TRUE && destroy_ret == EGL_TRUE) ? 0 : -1; -} - - - /* Checks if a view needs to be censored on an output - * Checks for 2 types of censor requirements - * - recording_censor: Censor protected view when a - * protected view is captured. - * - unprotected_censor: Censor regions of protected views - * when displayed on an output which has lower protection capability. - * Returns the originally stored gl_shader if content censoring is required, - * NULL otherwise. - */ -static struct gl_shader * -setup_censor_overrides(struct weston_output *output, - struct weston_view *ev) -{ - struct gl_shader *replaced_shader = NULL; - struct weston_compositor *ec = ev->surface->compositor; - struct gl_renderer *gr = get_renderer(ec); - struct gl_surface_state *gs = get_surface_state(ev->surface); - bool recording_censor = - (output->disable_planes > 0) && - (ev->surface->desired_protection > WESTON_HDCP_DISABLE); - - bool unprotected_censor = - (ev->surface->desired_protection > output->current_protection); - - if (gs->direct_display) { - gs->color[0] = 0.40; - gs->color[1] = 0.0; - gs->color[2] = 0.0; - gs->color[3] = 1.0; - gs->shader = &gr->solid_shader; - return gs->shader; - } - - /* When not in enforced mode, the client is notified of the protection */ - /* change, so content censoring is not required */ - if (ev->surface->protection_mode != - WESTON_SURFACE_PROTECTION_MODE_ENFORCED) - return NULL; - - if (recording_censor || unprotected_censor) { - replaced_shader = gs->shader; - gs->color[0] = 0.40; - gs->color[1] = 0.0; - gs->color[2] = 0.0; - gs->color[3] = 1.0; - gs->shader = &gr->solid_shader; - } - - return replaced_shader; -} - -static void -draw_view(struct weston_view *ev, struct weston_output *output, - pixman_region32_t *damage) /* in global coordinates */ -{ - struct weston_compositor *ec = ev->surface->compositor; - struct gl_renderer *gr = get_renderer(ec); - struct gl_surface_state *gs = get_surface_state(ev->surface); - /* repaint bounding region in global coordinates: */ - pixman_region32_t repaint; - /* opaque region in surface coordinates: */ - pixman_region32_t surface_opaque; - /* non-opaque region in surface coordinates: */ - pixman_region32_t surface_blend; - GLint filter; - int i; - struct gl_shader *replaced_shader = NULL; - - /* In case of a runtime switch of renderers, we may not have received - * an attach for this surface since the switch. In that case we don't - * have a valid buffer or a proper shader set up so skip rendering. */ - if (!gs->shader && !gs->direct_display) - return; - - pixman_region32_init(&repaint); - pixman_region32_intersect(&repaint, - &ev->transform.boundingbox, damage); - pixman_region32_subtract(&repaint, &repaint, &ev->clip); - - if (!pixman_region32_not_empty(&repaint)) - goto out; - - if (ensure_surface_buffer_is_ready(gr, gs) < 0) - goto out; - - replaced_shader = setup_censor_overrides(output, ev); - - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - if (gr->fan_debug) { - use_shader(gr, &gr->solid_shader); - shader_uniforms(&gr->solid_shader, ev, output); - } - - use_shader(gr, gs->shader); - shader_uniforms(gs->shader, ev, output); - - if (ev->transform.enabled || output->zoom.active || - output->current_scale != ev->surface->buffer_viewport.buffer.scale) - filter = GL_LINEAR; - else - filter = GL_NEAREST; - - for (i = 0; i < gs->num_textures; i++) { - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(gs->target, gs->textures[i]); - glTexParameteri(gs->target, GL_TEXTURE_MIN_FILTER, filter); - glTexParameteri(gs->target, GL_TEXTURE_MAG_FILTER, filter); - } - - /* blended region is whole surface minus opaque region: */ - pixman_region32_init_rect(&surface_blend, 0, 0, - ev->surface->width, ev->surface->height); - if (ev->geometry.scissor_enabled) - pixman_region32_intersect(&surface_blend, &surface_blend, - &ev->geometry.scissor); - pixman_region32_subtract(&surface_blend, &surface_blend, - &ev->surface->opaque); - - /* XXX: Should we be using ev->transform.opaque here? */ - pixman_region32_init(&surface_opaque); - if (ev->geometry.scissor_enabled) - pixman_region32_intersect(&surface_opaque, - &ev->surface->opaque, - &ev->geometry.scissor); - else - pixman_region32_copy(&surface_opaque, &ev->surface->opaque); - - if (pixman_region32_not_empty(&surface_opaque)) { - if (gs->shader == &gr->texture_shader_rgba) { - /* Special case for RGBA textures with possibly - * bad data in alpha channel: use the shader - * that forces texture alpha = 1.0. - * Xwayland surfaces need this. - */ - use_shader(gr, &gr->texture_shader_rgbx); - shader_uniforms(&gr->texture_shader_rgbx, ev, output); - } - - if (ev->alpha < 1.0) - glEnable(GL_BLEND); - else - glDisable(GL_BLEND); - - repaint_region(ev, &repaint, &surface_opaque); - gs->used_in_output_repaint = true; - } - - if (pixman_region32_not_empty(&surface_blend)) { - use_shader(gr, gs->shader); - glEnable(GL_BLEND); - repaint_region(ev, &repaint, &surface_blend); - gs->used_in_output_repaint = true; - } - - pixman_region32_fini(&surface_blend); - pixman_region32_fini(&surface_opaque); - -out: - pixman_region32_fini(&repaint); - - if (replaced_shader) - gs->shader = replaced_shader; -} - -static void -repaint_views(struct weston_output *output, pixman_region32_t *damage) -{ - struct weston_compositor *compositor = output->compositor; - struct weston_view *view; - - wl_list_for_each_reverse(view, &compositor->view_list, link) - if (view->plane == &compositor->primary_plane) - draw_view(view, output, damage); -} - -static int -gl_renderer_create_fence_fd(struct weston_output *output); - -/* Updates the release fences of surfaces that were used in the current output - * repaint. Should only be used from gl_renderer_repaint_output, so that the - * information in gl_surface_state.used_in_output_repaint is accurate. - */ -static void -update_buffer_release_fences(struct weston_compositor *compositor, - struct weston_output *output) -{ - struct weston_view *view; - - wl_list_for_each_reverse(view, &compositor->view_list, link) { - struct gl_surface_state *gs; - struct weston_buffer_release *buffer_release; - int fence_fd; - - if (view->plane != &compositor->primary_plane) - continue; - - gs = get_surface_state(view->surface); - buffer_release = gs->buffer_release_ref.buffer_release; - - if (!gs->used_in_output_repaint || !buffer_release) - continue; - - fence_fd = gl_renderer_create_fence_fd(output); - - /* If we have a buffer_release then it means we support fences, - * and we should be able to create the release fence. If we - * can't, something has gone horribly wrong, so disconnect the - * client. - */ - if (fence_fd == -1) { - linux_explicit_synchronization_send_server_error( - buffer_release->resource, - "Failed to create release fence"); - fd_clear(&buffer_release->fence_fd); - continue; - } - - /* At the moment it is safe to just replace the fence_fd, - * discarding the previous one: - * - * 1. If the previous fence fd represents a sync fence from - * a previous repaint cycle, that fence fd is now not - * sufficient to provide the release guarantee and should - * be replaced. - * - * 2. If the fence fd represents a sync fence from another - * output in the same repaint cycle, it's fine to replace - * it since we are rendering to all outputs using the same - * EGL context, so a fence issued for a later output rendering - * is guaranteed to signal after fences for previous output - * renderings. - * - * Note that the above is only valid if the buffer_release - * fences only originate from the GL renderer, which guarantees - * a total order of operations and fences. If we introduce - * fences from other sources (e.g., plane out-fences), we will - * need to merge fences instead. - */ - fd_update(&buffer_release->fence_fd, fence_fd); - } -} - -static void -draw_output_border_texture(struct gl_output_state *go, - enum gl_renderer_border_side side, - int32_t x, int32_t y, - int32_t width, int32_t height) -{ - struct gl_border_image *img = &go->borders[side]; - static GLushort indices [] = { 0, 1, 3, 3, 1, 2 }; - - if (!img->data) { - if (img->tex) { - glDeleteTextures(1, &img->tex); - img->tex = 0; - } - - return; - } - - if (!img->tex) { - glGenTextures(1, &img->tex); - glBindTexture(GL_TEXTURE_2D, img->tex); - - glTexParameteri(GL_TEXTURE_2D, - GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, - GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, - GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, - GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } else { - glBindTexture(GL_TEXTURE_2D, img->tex); - } - - if (go->border_status & (1 << side)) { - glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0); - glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); - glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, - img->tex_width, img->height, 0, - GL_BGRA_EXT, GL_UNSIGNED_BYTE, img->data); - } - - GLfloat texcoord[] = { - 0.0f, 0.0f, - (GLfloat)img->width / (GLfloat)img->tex_width, 0.0f, - (GLfloat)img->width / (GLfloat)img->tex_width, 1.0f, - 0.0f, 1.0f, - }; - - GLfloat verts[] = { - x, y, - x + width, y, - x + width, y + height, - x, y + height - }; - - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texcoord); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); - - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(0); -} - -static int -output_has_borders(struct weston_output *output) -{ - struct gl_output_state *go = get_output_state(output); - - return go->borders[GL_RENDERER_BORDER_TOP].data || - go->borders[GL_RENDERER_BORDER_RIGHT].data || - go->borders[GL_RENDERER_BORDER_BOTTOM].data || - go->borders[GL_RENDERER_BORDER_LEFT].data; -} - -static void -draw_output_borders(struct weston_output *output, - enum gl_border_status border_status) -{ - struct gl_output_state *go = get_output_state(output); - struct gl_renderer *gr = get_renderer(output->compositor); - struct gl_shader *shader = &gr->texture_shader_rgba; - struct gl_border_image *top, *bottom, *left, *right; - struct weston_matrix matrix; - int full_width, full_height; - - if (border_status == BORDER_STATUS_CLEAN) - return; /* Clean. Nothing to do. */ - - top = &go->borders[GL_RENDERER_BORDER_TOP]; - bottom = &go->borders[GL_RENDERER_BORDER_BOTTOM]; - left = &go->borders[GL_RENDERER_BORDER_LEFT]; - right = &go->borders[GL_RENDERER_BORDER_RIGHT]; - - full_width = output->current_mode->width + left->width + right->width; - full_height = output->current_mode->height + top->height + bottom->height; - - glDisable(GL_BLEND); - use_shader(gr, shader); - - glViewport(0, 0, full_width, full_height); - - weston_matrix_init(&matrix); - weston_matrix_translate(&matrix, -full_width/2.0, -full_height/2.0, 0); - weston_matrix_scale(&matrix, 2.0/full_width, -2.0/full_height, 1); - glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, matrix.d); - - glUniform1i(shader->tex_uniforms[0], 0); - glUniform1f(shader->alpha_uniform, 1); - glActiveTexture(GL_TEXTURE0); - - if (border_status & BORDER_TOP_DIRTY) - draw_output_border_texture(go, GL_RENDERER_BORDER_TOP, - 0, 0, - full_width, top->height); - if (border_status & BORDER_LEFT_DIRTY) - draw_output_border_texture(go, GL_RENDERER_BORDER_LEFT, - 0, top->height, - left->width, output->current_mode->height); - if (border_status & BORDER_RIGHT_DIRTY) - draw_output_border_texture(go, GL_RENDERER_BORDER_RIGHT, - full_width - right->width, top->height, - right->width, output->current_mode->height); - if (border_status & BORDER_BOTTOM_DIRTY) - draw_output_border_texture(go, GL_RENDERER_BORDER_BOTTOM, - 0, full_height - bottom->height, - full_width, bottom->height); -} - -static void -output_get_border_damage(struct weston_output *output, - enum gl_border_status border_status, - pixman_region32_t *damage) -{ - struct gl_output_state *go = get_output_state(output); - struct gl_border_image *top, *bottom, *left, *right; - int full_width, full_height; - - if (border_status == BORDER_STATUS_CLEAN) - return; /* Clean. Nothing to do. */ - - top = &go->borders[GL_RENDERER_BORDER_TOP]; - bottom = &go->borders[GL_RENDERER_BORDER_BOTTOM]; - left = &go->borders[GL_RENDERER_BORDER_LEFT]; - right = &go->borders[GL_RENDERER_BORDER_RIGHT]; - - full_width = output->current_mode->width + left->width + right->width; - full_height = output->current_mode->height + top->height + bottom->height; - if (border_status & BORDER_TOP_DIRTY) - pixman_region32_union_rect(damage, damage, - 0, 0, - full_width, top->height); - if (border_status & BORDER_LEFT_DIRTY) - pixman_region32_union_rect(damage, damage, - 0, top->height, - left->width, output->current_mode->height); - if (border_status & BORDER_RIGHT_DIRTY) - pixman_region32_union_rect(damage, damage, - full_width - right->width, top->height, - right->width, output->current_mode->height); - if (border_status & BORDER_BOTTOM_DIRTY) - pixman_region32_union_rect(damage, damage, - 0, full_height - bottom->height, - full_width, bottom->height); -} - -static void -output_get_damage(struct weston_output *output, - pixman_region32_t *buffer_damage, uint32_t *border_damage) -{ - struct gl_output_state *go = get_output_state(output); - struct gl_renderer *gr = get_renderer(output->compositor); - EGLint buffer_age = 0; - EGLBoolean ret; - int i; - - if (gr->has_egl_buffer_age) { - ret = eglQuerySurface(gr->egl_display, go->egl_surface, - EGL_BUFFER_AGE_EXT, &buffer_age); - if (ret == EGL_FALSE) { - weston_log("buffer age query failed.\n"); - gl_renderer_print_egl_error_state(); - } - } - - if (buffer_age == 0 || buffer_age - 1 > BUFFER_DAMAGE_COUNT) { - pixman_region32_copy(buffer_damage, &output->region); - *border_damage = BORDER_ALL_DIRTY; - } else { - for (i = 0; i < buffer_age - 1; i++) - *border_damage |= go->border_damage[(go->buffer_damage_index + i) % BUFFER_DAMAGE_COUNT]; - - if (*border_damage & BORDER_SIZE_CHANGED) { - /* If we've had a resize, we have to do a full - * repaint. */ - *border_damage |= BORDER_ALL_DIRTY; - pixman_region32_copy(buffer_damage, &output->region); - } else { - for (i = 0; i < buffer_age - 1; i++) - pixman_region32_union(buffer_damage, - buffer_damage, - &go->buffer_damage[(go->buffer_damage_index + i) % BUFFER_DAMAGE_COUNT]); - } - } -} - -static void -output_rotate_damage(struct weston_output *output, - pixman_region32_t *output_damage, - enum gl_border_status border_status) -{ - struct gl_output_state *go = get_output_state(output); - struct gl_renderer *gr = get_renderer(output->compositor); - - if (!gr->has_egl_buffer_age) - return; - - go->buffer_damage_index += BUFFER_DAMAGE_COUNT - 1; - go->buffer_damage_index %= BUFFER_DAMAGE_COUNT; - - pixman_region32_copy(&go->buffer_damage[go->buffer_damage_index], output_damage); - go->border_damage[go->buffer_damage_index] = border_status; -} - -/** - * Given a region in Weston's (top-left-origin) global co-ordinate space, - * translate it to the co-ordinate space used by GL for our output - * rendering. This requires shifting it into output co-ordinate space: - * translating for output offset within the global co-ordinate space, - * multiplying by output scale to get buffer rather than logical size. - * - * Finally, if borders are drawn around the output, we translate the area - * to account for the border region around the outside, and add any - * damage if the borders have been redrawn. - * - * @param output The output whose co-ordinate space we are after - * @param global_region The affected region in global co-ordinate space - * @param[out] rects Y-inverted quads in {x,y,w,h} order; caller must free - * @param[out] nrects Number of quads (4x number of co-ordinates) - */ -static void -pixman_region_to_egl_y_invert(struct weston_output *output, - struct pixman_region32 *global_region, - EGLint **rects, - EGLint *nrects) -{ - struct gl_output_state *go = get_output_state(output); - pixman_region32_t transformed; - struct pixman_box32 *box; - int buffer_height; - EGLint *d; - int i; - - /* Translate from global to output co-ordinate space. */ - pixman_region32_init(&transformed); - pixman_region32_copy(&transformed, global_region); - pixman_region32_translate(&transformed, -output->x, -output->y); - weston_transformed_region(output->width, output->height, - output->transform, - output->current_scale, - &transformed, &transformed); - - /* If we have borders drawn around the output, shift our output damage - * to account for borders being drawn around the outside, adding any - * damage resulting from borders being redrawn. */ - if (output_has_borders(output)) { - pixman_region32_translate(&transformed, - go->borders[GL_RENDERER_BORDER_LEFT].width, - go->borders[GL_RENDERER_BORDER_TOP].height); - output_get_border_damage(output, go->border_status, - &transformed); - } - - /* Convert from a Pixman region into {x,y,w,h} quads, flipping in the - * Y axis to account for GL's lower-left-origin co-ordinate space. */ - box = pixman_region32_rectangles(&transformed, nrects); - *rects = malloc(*nrects * 4 * sizeof(EGLint)); - - buffer_height = go->borders[GL_RENDERER_BORDER_TOP].height + - output->current_mode->height + - go->borders[GL_RENDERER_BORDER_BOTTOM].height; - - d = *rects; - for (i = 0; i < *nrects; ++i) { - *d++ = box[i].x1; - *d++ = buffer_height - box[i].y2; - *d++ = box[i].x2 - box[i].x1; - *d++ = box[i].y2 - box[i].y1; - } - - pixman_region32_fini(&transformed); -} - -/* NOTE: We now allow falling back to ARGB gl visuals when XRGB is - * unavailable, so we're assuming the background has no transparency - * and that everything with a blend, like drop shadows, will have something - * opaque (like the background) drawn underneath it. - * - * Depending on the underlying hardware, violating that assumption could - * result in seeing through to another display plane. - */ -static void -gl_renderer_repaint_output(struct weston_output *output, - pixman_region32_t *output_damage) -{ - struct gl_output_state *go = get_output_state(output); - struct weston_compositor *compositor = output->compositor; - struct gl_renderer *gr = get_renderer(compositor); - EGLBoolean ret; - static int errored; - /* areas we've damaged since we last used this buffer */ - pixman_region32_t previous_damage; - /* total area we need to repaint this time */ - pixman_region32_t total_damage; - enum gl_border_status border_status = BORDER_STATUS_CLEAN; - struct weston_view *view; - - if (use_output(output) < 0) - return; - - /* Clear the used_in_output_repaint flag, so that we can properly track - * which surfaces were used in this output repaint. */ - wl_list_for_each_reverse(view, &compositor->view_list, link) { - if (view->plane == &compositor->primary_plane) { - struct gl_surface_state *gs = - get_surface_state(view->surface); - gs->used_in_output_repaint = false; - } - } - - if (go->begin_render_sync != EGL_NO_SYNC_KHR) - gr->destroy_sync(gr->egl_display, go->begin_render_sync); - if (go->end_render_sync != EGL_NO_SYNC_KHR) - gr->destroy_sync(gr->egl_display, go->end_render_sync); - - go->begin_render_sync = create_render_sync(gr); - - /* Calculate the viewport */ - glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width, - go->borders[GL_RENDERER_BORDER_BOTTOM].height, - output->current_mode->width, - output->current_mode->height); - - /* Calculate the global GL matrix */ - go->output_matrix = output->matrix; - weston_matrix_translate(&go->output_matrix, - -(output->current_mode->width / 2.0), - -(output->current_mode->height / 2.0), 0); - weston_matrix_scale(&go->output_matrix, - 2.0 / output->current_mode->width, - -2.0 / output->current_mode->height, 1); - - /* In fan debug mode, redraw everything to make sure that we clear any - * fans left over from previous draws on this buffer. - * This precludes the use of EGL_EXT_swap_buffers_with_damage and - * EGL_KHR_partial_update, since we damage the whole area. */ - if (gr->fan_debug) { - pixman_region32_t undamaged; - pixman_region32_init(&undamaged); - pixman_region32_subtract(&undamaged, &output->region, - output_damage); - gr->fan_debug = false; - repaint_views(output, &undamaged); - gr->fan_debug = true; - pixman_region32_fini(&undamaged); - } - - /* previous_damage covers regions damaged in previous paints since we - * last used this buffer */ - pixman_region32_init(&previous_damage); - pixman_region32_init(&total_damage); /* total area to redraw */ - - /* Update previous_damage using buffer_age (if available), and store - * current damaged region for future use. */ - output_get_damage(output, &previous_damage, &border_status); - output_rotate_damage(output, output_damage, go->border_status); - - /* Redraw both areas which have changed since we last used this buffer, - * as well as the areas we now want to repaint, to make sure the - * buffer is up to date. */ - pixman_region32_union(&total_damage, &previous_damage, output_damage); - border_status |= go->border_status; - - if (gr->has_egl_partial_update && !gr->fan_debug) { - int n_egl_rects; - EGLint *egl_rects; - - /* For partial_update, we need to pass the region which has - * changed since we last rendered into this specific buffer; - * this is total_damage. */ - pixman_region_to_egl_y_invert(output, &total_damage, - &egl_rects, &n_egl_rects); - gr->set_damage_region(gr->egl_display, go->egl_surface, - egl_rects, n_egl_rects); - free(egl_rects); - } - - repaint_views(output, &total_damage); - - pixman_region32_fini(&total_damage); - pixman_region32_fini(&previous_damage); - - draw_output_borders(output, border_status); - - wl_signal_emit(&output->frame_signal, output_damage); - - go->end_render_sync = create_render_sync(gr); - - if (gr->swap_buffers_with_damage && !gr->fan_debug) { - int n_egl_rects; - EGLint *egl_rects; - - /* For swap_buffers_with_damage, we need to pass the region - * which has changed since the previous SwapBuffers on this - * surface - this is output_damage. */ - pixman_region_to_egl_y_invert(output, output_damage, - &egl_rects, &n_egl_rects); - ret = gr->swap_buffers_with_damage(gr->egl_display, - go->egl_surface, - egl_rects, n_egl_rects); - free(egl_rects); - } else { - ret = eglSwapBuffers(gr->egl_display, go->egl_surface); - } - - if (ret == EGL_FALSE && !errored) { - errored = 1; - weston_log("Failed in eglSwapBuffers.\n"); - gl_renderer_print_egl_error_state(); - } - - go->border_status = BORDER_STATUS_CLEAN; - - /* We have to submit the render sync objects after swap buffers, since - * the objects get assigned a valid sync file fd only after a gl flush. - */ - timeline_submit_render_sync(gr, compositor, output, - go->begin_render_sync, - TIMELINE_RENDER_POINT_TYPE_BEGIN); - timeline_submit_render_sync(gr, compositor, output, go->end_render_sync, - TIMELINE_RENDER_POINT_TYPE_END); - - update_buffer_release_fences(compositor, output); -} - -static int -gl_renderer_read_pixels(struct weston_output *output, - pixman_format_code_t format, void *pixels, - uint32_t x, uint32_t y, - uint32_t width, uint32_t height) -{ - GLenum gl_format; - struct gl_output_state *go = get_output_state(output); - - x += go->borders[GL_RENDERER_BORDER_LEFT].width; - y += go->borders[GL_RENDERER_BORDER_BOTTOM].height; - - switch (format) { - case PIXMAN_a8r8g8b8: - gl_format = GL_BGRA_EXT; - break; - case PIXMAN_a8b8g8r8: - gl_format = GL_RGBA; - break; - default: - return -1; - } - - if (use_output(output) < 0) - return -1; - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glReadPixels(x, y, width, height, gl_format, - GL_UNSIGNED_BYTE, pixels); - - return 0; -} - -static GLenum gl_format_from_internal(GLenum internal_format) -{ - switch (internal_format) { - case GL_R8_EXT: - return GL_RED_EXT; - case GL_RG8_EXT: - return GL_RG_EXT; - default: - return internal_format; - } -} - -static void -gl_renderer_flush_damage(struct weston_surface *surface) -{ - struct gl_renderer *gr = get_renderer(surface->compositor); - struct gl_surface_state *gs = get_surface_state(surface); - struct weston_buffer *buffer = gs->buffer_ref.buffer; - struct weston_view *view; - bool texture_used; - pixman_box32_t *rectangles; - uint8_t *data; - int i, j, n; - - pixman_region32_union(&gs->texture_damage, - &gs->texture_damage, &surface->damage); - - if (!buffer) - return; - - /* Avoid upload, if the texture won't be used this time. - * We still accumulate the damage in texture_damage, and - * hold the reference to the buffer, in case the surface - * migrates back to the primary plane. - */ - texture_used = false; - wl_list_for_each(view, &surface->views, surface_link) { - if (view->plane == &surface->compositor->primary_plane) { - texture_used = true; - break; - } - } - if (!texture_used) - return; - - if (!pixman_region32_not_empty(&gs->texture_damage) && - !gs->needs_full_upload) - goto done; - - data = wl_shm_buffer_get_data(buffer->shm_buffer); - - if (!gr->has_unpack_subimage) { - wl_shm_buffer_begin_access(buffer->shm_buffer); - for (j = 0; j < gs->num_textures; j++) { - glBindTexture(GL_TEXTURE_2D, gs->textures[j]); - glTexImage2D(GL_TEXTURE_2D, 0, - gs->gl_format[j], - gs->pitch / gs->hsub[j], - buffer->height / gs->vsub[j], - 0, - gl_format_from_internal(gs->gl_format[j]), - gs->gl_pixel_type, - data + gs->offset[j]); - } - wl_shm_buffer_end_access(buffer->shm_buffer); - - goto done; - } - - if (gs->needs_full_upload) { - glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); - glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); - wl_shm_buffer_begin_access(buffer->shm_buffer); - for (j = 0; j < gs->num_textures; j++) { - glBindTexture(GL_TEXTURE_2D, gs->textures[j]); - glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, - gs->pitch / gs->hsub[j]); - glTexImage2D(GL_TEXTURE_2D, 0, - gs->gl_format[j], - gs->pitch / gs->hsub[j], - buffer->height / gs->vsub[j], - 0, - gl_format_from_internal(gs->gl_format[j]), - gs->gl_pixel_type, - data + gs->offset[j]); - } - wl_shm_buffer_end_access(buffer->shm_buffer); - goto done; - } - - rectangles = pixman_region32_rectangles(&gs->texture_damage, &n); - wl_shm_buffer_begin_access(buffer->shm_buffer); - for (i = 0; i < n; i++) { - pixman_box32_t r; - - r = weston_surface_to_buffer_rect(surface, rectangles[i]); - - for (j = 0; j < gs->num_textures; j++) { - glBindTexture(GL_TEXTURE_2D, gs->textures[j]); - glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, - gs->pitch / gs->hsub[j]); - glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, - r.x1 / gs->hsub[j]); - glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, - r.y1 / gs->hsub[j]); - glTexSubImage2D(GL_TEXTURE_2D, 0, - r.x1 / gs->hsub[j], - r.y1 / gs->vsub[j], - (r.x2 - r.x1) / gs->hsub[j], - (r.y2 - r.y1) / gs->vsub[j], - gl_format_from_internal(gs->gl_format[j]), - gs->gl_pixel_type, - data + gs->offset[j]); - } - } - wl_shm_buffer_end_access(buffer->shm_buffer); - -done: - pixman_region32_fini(&gs->texture_damage); - pixman_region32_init(&gs->texture_damage); - gs->needs_full_upload = false; - - weston_buffer_reference(&gs->buffer_ref, NULL); - weston_buffer_release_reference(&gs->buffer_release_ref, NULL); -} - -static void -ensure_textures(struct gl_surface_state *gs, int num_textures) -{ - int i; - - if (num_textures <= gs->num_textures) - return; - - for (i = gs->num_textures; i < num_textures; i++) { - glGenTextures(1, &gs->textures[i]); - glBindTexture(gs->target, gs->textures[i]); - glTexParameteri(gs->target, - GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(gs->target, - GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - gs->num_textures = num_textures; - glBindTexture(gs->target, 0); -} - -static void -gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, - struct wl_shm_buffer *shm_buffer) -{ - struct weston_compositor *ec = es->compositor; - struct gl_renderer *gr = get_renderer(ec); - struct gl_surface_state *gs = get_surface_state(es); - GLenum gl_format[3] = {0, 0, 0}; - GLenum gl_pixel_type; - int pitch; - int num_planes; - - buffer->shm_buffer = shm_buffer; - buffer->width = wl_shm_buffer_get_width(shm_buffer); - buffer->height = wl_shm_buffer_get_height(shm_buffer); - - num_planes = 1; - gs->offset[0] = 0; - gs->hsub[0] = 1; - gs->vsub[0] = 1; - - switch (wl_shm_buffer_get_format(shm_buffer)) { - case WL_SHM_FORMAT_XRGB8888: - gs->shader = &gr->texture_shader_rgbx; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - gl_format[0] = GL_BGRA_EXT; - gl_pixel_type = GL_UNSIGNED_BYTE; - es->is_opaque = true; - break; - case WL_SHM_FORMAT_ARGB8888: - gs->shader = &gr->texture_shader_rgba; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - gl_format[0] = GL_BGRA_EXT; - gl_pixel_type = GL_UNSIGNED_BYTE; - es->is_opaque = false; - break; - case WL_SHM_FORMAT_RGB565: - gs->shader = &gr->texture_shader_rgbx; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; - gl_format[0] = GL_RGB; - gl_pixel_type = GL_UNSIGNED_SHORT_5_6_5; - es->is_opaque = true; - break; - case WL_SHM_FORMAT_YUV420: - gs->shader = &gr->texture_shader_y_u_v; - pitch = wl_shm_buffer_get_stride(shm_buffer); - gl_pixel_type = GL_UNSIGNED_BYTE; - num_planes = 3; - gs->offset[1] = gs->offset[0] + (pitch / gs->hsub[0]) * - (buffer->height / gs->vsub[0]); - gs->hsub[1] = 2; - gs->vsub[1] = 2; - gs->offset[2] = gs->offset[1] + (pitch / gs->hsub[1]) * - (buffer->height / gs->vsub[1]); - gs->hsub[2] = 2; - gs->vsub[2] = 2; - if (gr->has_gl_texture_rg) { - gl_format[0] = GL_R8_EXT; - gl_format[1] = GL_R8_EXT; - gl_format[2] = GL_R8_EXT; - } else { - gl_format[0] = GL_LUMINANCE; - gl_format[1] = GL_LUMINANCE; - gl_format[2] = GL_LUMINANCE; - } - es->is_opaque = true; - break; - case WL_SHM_FORMAT_NV12: - pitch = wl_shm_buffer_get_stride(shm_buffer); - gl_pixel_type = GL_UNSIGNED_BYTE; - num_planes = 2; - gs->offset[1] = gs->offset[0] + (pitch / gs->hsub[0]) * - (buffer->height / gs->vsub[0]); - gs->hsub[1] = 2; - gs->vsub[1] = 2; - if (gr->has_gl_texture_rg) { - gs->shader = &gr->texture_shader_y_uv; - gl_format[0] = GL_R8_EXT; - gl_format[1] = GL_RG8_EXT; - } else { - gs->shader = &gr->texture_shader_y_xuxv; - gl_format[0] = GL_LUMINANCE; - gl_format[1] = GL_LUMINANCE_ALPHA; - } - es->is_opaque = true; - break; - case WL_SHM_FORMAT_YUYV: - gs->shader = &gr->texture_shader_y_xuxv; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; - gl_pixel_type = GL_UNSIGNED_BYTE; - num_planes = 2; - gs->offset[1] = 0; - gs->hsub[1] = 2; - gs->vsub[1] = 1; - if (gr->has_gl_texture_rg) - gl_format[0] = GL_RG8_EXT; - else - gl_format[0] = GL_LUMINANCE_ALPHA; - gl_format[1] = GL_BGRA_EXT; - es->is_opaque = true; - break; - default: - weston_log("warning: unknown shm buffer format: %08x\n", - wl_shm_buffer_get_format(shm_buffer)); - return; - } - - /* Only allocate a texture if it doesn't match existing one. - * If a switch from DRM allocated buffer to a SHM buffer is - * happening, we need to allocate a new texture buffer. */ - if (pitch != gs->pitch || - buffer->height != gs->height || - gl_format[0] != gs->gl_format[0] || - gl_format[1] != gs->gl_format[1] || - gl_format[2] != gs->gl_format[2] || - gl_pixel_type != gs->gl_pixel_type || - gs->buffer_type != BUFFER_TYPE_SHM) { - gs->pitch = pitch; - gs->height = buffer->height; - gs->target = GL_TEXTURE_2D; - gs->gl_format[0] = gl_format[0]; - gs->gl_format[1] = gl_format[1]; - gs->gl_format[2] = gl_format[2]; - gs->gl_pixel_type = gl_pixel_type; - gs->buffer_type = BUFFER_TYPE_SHM; - gs->needs_full_upload = true; - gs->y_inverted = true; - gs->direct_display = false; - - gs->surface = es; - - ensure_textures(gs, num_planes); - } -} - -static void -gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer, - uint32_t format) -{ - struct weston_compositor *ec = es->compositor; - struct gl_renderer *gr = get_renderer(ec); - struct gl_surface_state *gs = get_surface_state(es); - EGLint attribs[3]; - int i, num_planes; - - buffer->legacy_buffer = (struct wl_buffer *)buffer->resource; - gr->query_buffer(gr->egl_display, buffer->legacy_buffer, - EGL_WIDTH, &buffer->width); - gr->query_buffer(gr->egl_display, buffer->legacy_buffer, - EGL_HEIGHT, &buffer->height); - gr->query_buffer(gr->egl_display, buffer->legacy_buffer, - EGL_WAYLAND_Y_INVERTED_WL, &buffer->y_inverted); - - for (i = 0; i < gs->num_images; i++) { - egl_image_unref(gs->images[i]); - gs->images[i] = NULL; - } - gs->num_images = 0; - gs->target = GL_TEXTURE_2D; - es->is_opaque = false; - switch (format) { - case EGL_TEXTURE_RGB: - es->is_opaque = true; - /* fallthrough */ - case EGL_TEXTURE_RGBA: - default: - num_planes = 1; - gs->shader = &gr->texture_shader_rgba; - break; - case EGL_TEXTURE_EXTERNAL_WL: - num_planes = 1; - gs->target = GL_TEXTURE_EXTERNAL_OES; - gs->shader = &gr->texture_shader_egl_external; - break; - case EGL_TEXTURE_Y_UV_WL: - num_planes = 2; - gs->shader = &gr->texture_shader_y_uv; - es->is_opaque = true; - break; - case EGL_TEXTURE_Y_U_V_WL: - num_planes = 3; - gs->shader = &gr->texture_shader_y_u_v; - es->is_opaque = true; - break; - case EGL_TEXTURE_Y_XUXV_WL: - num_planes = 2; - gs->shader = &gr->texture_shader_y_xuxv; - es->is_opaque = true; - break; - } - - ensure_textures(gs, num_planes); - for (i = 0; i < num_planes; i++) { - attribs[0] = EGL_WAYLAND_PLANE_WL; - attribs[1] = i; - attribs[2] = EGL_NONE; - gs->images[i] = egl_image_create(gr, - EGL_WAYLAND_BUFFER_WL, - buffer->legacy_buffer, - attribs); - if (!gs->images[i]) { - weston_log("failed to create img for plane %d\n", i); - continue; - } - gs->num_images++; - - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(gs->target, gs->textures[i]); - gr->image_target_texture_2d(gs->target, - gs->images[i]->image); - } - - gs->pitch = buffer->width; - gs->height = buffer->height; - gs->buffer_type = BUFFER_TYPE_EGL; - gs->y_inverted = buffer->y_inverted; -} - -static void -gl_renderer_destroy_dmabuf(struct linux_dmabuf_buffer *dmabuf) -{ - struct dmabuf_image *image = linux_dmabuf_buffer_get_user_data(dmabuf); - - dmabuf_image_destroy(image); -} - -static struct egl_image * -import_simple_dmabuf(struct gl_renderer *gr, - struct dmabuf_attributes *attributes) -{ - struct egl_image *image; - EGLint attribs[50]; - int atti = 0; - bool has_modifier; - - /* This requires the Mesa commit in - * Mesa 10.3 (08264e5dad4df448e7718e782ad9077902089a07) or - * Mesa 10.2.7 (55d28925e6109a4afd61f109e845a8a51bd17652). - * Otherwise Mesa closes the fd behind our back and re-importing - * will fail. - * https://bugs.freedesktop.org/show_bug.cgi?id=76188 - */ - - attribs[atti++] = EGL_WIDTH; - attribs[atti++] = attributes->width; - attribs[atti++] = EGL_HEIGHT; - attribs[atti++] = attributes->height; - attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; - attribs[atti++] = attributes->format; - - if (attributes->modifier[0] != DRM_FORMAT_MOD_INVALID) { - if (!gr->has_dmabuf_import_modifiers) - return NULL; - has_modifier = true; - } else { - has_modifier = false; - } - - if (attributes->n_planes > 0) { - attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; - attribs[atti++] = attributes->fd[0]; - attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; - attribs[atti++] = attributes->offset[0]; - attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; - attribs[atti++] = attributes->stride[0]; - if (has_modifier) { - attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; - attribs[atti++] = attributes->modifier[0] & 0xFFFFFFFF; - attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; - attribs[atti++] = attributes->modifier[0] >> 32; - } - } - - if (attributes->n_planes > 1) { - attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT; - attribs[atti++] = attributes->fd[1]; - attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; - attribs[atti++] = attributes->offset[1]; - attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; - attribs[atti++] = attributes->stride[1]; - if (has_modifier) { - attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; - attribs[atti++] = attributes->modifier[1] & 0xFFFFFFFF; - attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; - attribs[atti++] = attributes->modifier[1] >> 32; - } - } - - if (attributes->n_planes > 2) { - attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT; - attribs[atti++] = attributes->fd[2]; - attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; - attribs[atti++] = attributes->offset[2]; - attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; - attribs[atti++] = attributes->stride[2]; - if (has_modifier) { - attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; - attribs[atti++] = attributes->modifier[2] & 0xFFFFFFFF; - attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; - attribs[atti++] = attributes->modifier[2] >> 32; - } - } - - if (gr->has_dmabuf_import_modifiers) { - if (attributes->n_planes > 3) { - attribs[atti++] = EGL_DMA_BUF_PLANE3_FD_EXT; - attribs[atti++] = attributes->fd[3]; - attribs[atti++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT; - attribs[atti++] = attributes->offset[3]; - attribs[atti++] = EGL_DMA_BUF_PLANE3_PITCH_EXT; - attribs[atti++] = attributes->stride[3]; - attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT; - attribs[atti++] = attributes->modifier[3] & 0xFFFFFFFF; - attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT; - attribs[atti++] = attributes->modifier[3] >> 32; - } - } - - attribs[atti++] = EGL_NONE; - - image = egl_image_create(gr, EGL_LINUX_DMA_BUF_EXT, NULL, - attribs); - - return image; -} - -/* The kernel header drm_fourcc.h defines the DRM formats below. We duplicate - * some of the definitions here so that building Weston won't require - * bleeding-edge kernel headers. - */ -#ifndef DRM_FORMAT_R8 -#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ -#endif - -#ifndef DRM_FORMAT_GR88 -#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ -#endif - -#ifndef DRM_FORMAT_XYUV8888 -#define DRM_FORMAT_XYUV8888 fourcc_code('X', 'Y', 'U', 'V') /* [31:0] X:Y:Cb:Cr 8:8:8:8 little endian */ -#endif - -struct yuv_format_descriptor yuv_formats[] = { - { - .format = DRM_FORMAT_YUYV, - .input_planes = 1, - .output_planes = 2, - .texture_type = TEXTURE_Y_XUXV_WL, - {{ - .width_divisor = 1, - .height_divisor = 1, - .format = DRM_FORMAT_GR88, - .plane_index = 0 - }, { - .width_divisor = 2, - .height_divisor = 1, - .format = DRM_FORMAT_ARGB8888, - .plane_index = 0 - }} - }, { - .format = DRM_FORMAT_NV12, - .input_planes = 2, - .output_planes = 2, - .texture_type = TEXTURE_Y_UV_WL, - {{ - .width_divisor = 1, - .height_divisor = 1, - .format = DRM_FORMAT_R8, - .plane_index = 0 - }, { - .width_divisor = 2, - .height_divisor = 2, - .format = DRM_FORMAT_GR88, - .plane_index = 1 - }} - }, { - .format = DRM_FORMAT_YUV420, - .input_planes = 3, - .output_planes = 3, - .texture_type = TEXTURE_Y_U_V_WL, - {{ - .width_divisor = 1, - .height_divisor = 1, - .format = DRM_FORMAT_R8, - .plane_index = 0 - }, { - .width_divisor = 2, - .height_divisor = 2, - .format = DRM_FORMAT_R8, - .plane_index = 1 - }, { - .width_divisor = 2, - .height_divisor = 2, - .format = DRM_FORMAT_R8, - .plane_index = 2 - }} - }, { - .format = DRM_FORMAT_YUV444, - .input_planes = 3, - .output_planes = 3, - .texture_type = TEXTURE_Y_U_V_WL, - {{ - .width_divisor = 1, - .height_divisor = 1, - .format = DRM_FORMAT_R8, - .plane_index = 0 - }, { - .width_divisor = 1, - .height_divisor = 1, - .format = DRM_FORMAT_R8, - .plane_index = 1 - }, { - .width_divisor = 1, - .height_divisor = 1, - .format = DRM_FORMAT_R8, - .plane_index = 2 - }} - }, { - .format = DRM_FORMAT_XYUV8888, - .input_planes = 1, - .output_planes = 1, - .texture_type = TEXTURE_XYUV_WL, - {{ - .width_divisor = 1, - .height_divisor = 1, - .format = DRM_FORMAT_XBGR8888, - .plane_index = 0 - }} - } -}; - -static struct egl_image * -import_dmabuf_single_plane(struct gl_renderer *gr, - const struct dmabuf_attributes *attributes, - struct yuv_plane_descriptor *descriptor) -{ - struct dmabuf_attributes plane; - struct egl_image *image; - char fmt[4]; - - plane.width = attributes->width / descriptor->width_divisor; - plane.height = attributes->height / descriptor->height_divisor; - plane.format = descriptor->format; - plane.n_planes = 1; - plane.fd[0] = attributes->fd[descriptor->plane_index]; - plane.offset[0] = attributes->offset[descriptor->plane_index]; - plane.stride[0] = attributes->stride[descriptor->plane_index]; - plane.modifier[0] = attributes->modifier[descriptor->plane_index]; - - image = import_simple_dmabuf(gr, &plane); - if (!image) { - weston_log("Failed to import plane %d as %.4s\n", - descriptor->plane_index, - dump_format(descriptor->format, fmt)); - return NULL; - } - - return image; -} - -static bool -import_yuv_dmabuf(struct gl_renderer *gr, - struct dmabuf_image *image) -{ - unsigned i; - int j; - int ret; - struct yuv_format_descriptor *format = NULL; - struct dmabuf_attributes *attributes = &image->dmabuf->attributes; - char fmt[4]; - - for (i = 0; i < ARRAY_LENGTH(yuv_formats); ++i) { - if (yuv_formats[i].format == attributes->format) { - format = &yuv_formats[i]; - break; - } - } - - if (!format) { - weston_log("Error during import, and no known conversion for format " - "%.4s in the renderer\n", - dump_format(attributes->format, fmt)); - return false; - } - - if (attributes->n_planes != format->input_planes) { - weston_log("%.4s dmabuf must contain %d plane%s (%d provided)\n", - dump_format(format->format, fmt), - format->input_planes, - (format->input_planes > 1) ? "s" : "", - attributes->n_planes); - return false; - } - - for (j = 0; j < format->output_planes; ++j) { - image->images[j] = import_dmabuf_single_plane(gr, attributes, - &format->plane[j]); - if (!image->images[j]) { - while (j) { - ret = egl_image_unref(image->images[--j]); - assert(ret == 0); - } - return false; - } - } - - image->num_images = format->output_planes; - - switch (format->texture_type) { - case TEXTURE_Y_XUXV_WL: - image->shader = &gr->texture_shader_y_xuxv; - break; - case TEXTURE_Y_UV_WL: - image->shader = &gr->texture_shader_y_uv; - break; - case TEXTURE_Y_U_V_WL: - image->shader = &gr->texture_shader_y_u_v; - break; - case TEXTURE_XYUV_WL: - image->shader = &gr->texture_shader_xyuv; - break; - default: - assert(false); - } - - return true; -} - -static GLenum -choose_texture_target(struct dmabuf_attributes *attributes) -{ - if (attributes->n_planes > 1) - return GL_TEXTURE_EXTERNAL_OES; - - switch (attributes->format & ~DRM_FORMAT_BIG_ENDIAN) { - case DRM_FORMAT_YUYV: - case DRM_FORMAT_YVYU: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_VYUY: - case DRM_FORMAT_AYUV: - case DRM_FORMAT_XYUV8888: - return GL_TEXTURE_EXTERNAL_OES; - default: - return GL_TEXTURE_2D; - } -} - -static struct dmabuf_image * -import_dmabuf(struct gl_renderer *gr, - struct linux_dmabuf_buffer *dmabuf) -{ - struct egl_image *egl_image; - struct dmabuf_image *image; - - image = dmabuf_image_create(); - image->dmabuf = dmabuf; - - egl_image = import_simple_dmabuf(gr, &dmabuf->attributes); - if (egl_image) { - image->num_images = 1; - image->images[0] = egl_image; - image->import_type = IMPORT_TYPE_DIRECT; - image->target = choose_texture_target(&dmabuf->attributes); - - switch (image->target) { - case GL_TEXTURE_2D: - image->shader = &gr->texture_shader_rgba; - break; - default: - image->shader = &gr->texture_shader_egl_external; - } - } else { - if (!import_yuv_dmabuf(gr, image)) { - dmabuf_image_destroy(image); - return NULL; - } - image->import_type = IMPORT_TYPE_GL_CONVERSION; - image->target = GL_TEXTURE_2D; - } - - return image; -} - -static void -gl_renderer_query_dmabuf_formats(struct weston_compositor *wc, - int **formats, int *num_formats) -{ - struct gl_renderer *gr = get_renderer(wc); - static const int fallback_formats[] = { - DRM_FORMAT_ARGB8888, - DRM_FORMAT_XRGB8888, - DRM_FORMAT_YUYV, - DRM_FORMAT_NV12, - DRM_FORMAT_YUV420, - DRM_FORMAT_YUV444, - DRM_FORMAT_XYUV8888, - }; - bool fallback = false; - EGLint num; - - assert(gr->has_dmabuf_import); - - if (!gr->has_dmabuf_import_modifiers || - !gr->query_dmabuf_formats(gr->egl_display, 0, NULL, &num)) { - num = gr->has_gl_texture_rg ? ARRAY_LENGTH(fallback_formats) : 2; - fallback = true; - } - - *formats = calloc(num, sizeof(int)); - if (*formats == NULL) { - *num_formats = 0; - return; - } - - if (fallback) { - memcpy(*formats, fallback_formats, num * sizeof(int)); - *num_formats = num; - return; - } - - if (!gr->query_dmabuf_formats(gr->egl_display, num, *formats, &num)) { - *num_formats = 0; - free(*formats); - return; - } - - *num_formats = num; -} - -static void -gl_renderer_query_dmabuf_modifiers(struct weston_compositor *wc, int format, - uint64_t **modifiers, - int *num_modifiers) -{ - struct gl_renderer *gr = get_renderer(wc); - int num; - - assert(gr->has_dmabuf_import); - - if (!gr->has_dmabuf_import_modifiers || - !gr->query_dmabuf_modifiers(gr->egl_display, format, 0, NULL, - NULL, &num) || - num == 0) { - *num_modifiers = 0; - return; - } - - *modifiers = calloc(num, sizeof(uint64_t)); - if (*modifiers == NULL) { - *num_modifiers = 0; - return; - } - if (!gr->query_dmabuf_modifiers(gr->egl_display, format, - num, *modifiers, NULL, &num)) { - *num_modifiers = 0; - free(*modifiers); - return; - } - - *num_modifiers = num; -} - -static bool -gl_renderer_import_dmabuf(struct weston_compositor *ec, - struct linux_dmabuf_buffer *dmabuf) -{ - struct gl_renderer *gr = get_renderer(ec); - struct dmabuf_image *image; - int i; - - assert(gr->has_dmabuf_import); - - for (i = 0; i < dmabuf->attributes.n_planes; i++) { - /* return if EGL doesn't support import modifiers */ - if (dmabuf->attributes.modifier[i] != DRM_FORMAT_MOD_INVALID) - if (!gr->has_dmabuf_import_modifiers) - return false; - - /* return if modifiers passed are unequal */ - if (dmabuf->attributes.modifier[i] != - dmabuf->attributes.modifier[0]) - return false; - } - - /* reject all flags we do not recognize or handle */ - if (dmabuf->attributes.flags & ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT) - return false; - - image = import_dmabuf(gr, dmabuf); - if (!image) - return false; - - wl_list_insert(&gr->dmabuf_images, &image->link); - linux_dmabuf_buffer_set_user_data(dmabuf, image, - gl_renderer_destroy_dmabuf); - - return true; -} - -static bool -import_known_dmabuf(struct gl_renderer *gr, - struct dmabuf_image *image) -{ - switch (image->import_type) { - case IMPORT_TYPE_DIRECT: - image->images[0] = import_simple_dmabuf(gr, &image->dmabuf->attributes); - if (!image->images[0]) - return false; - image->num_images = 1; - break; - - case IMPORT_TYPE_GL_CONVERSION: - if (!import_yuv_dmabuf(gr, image)) - return false; - break; - - default: - weston_log("Invalid import type for dmabuf\n"); - return false; - } - - return true; -} - -static bool -dmabuf_is_opaque(struct linux_dmabuf_buffer *dmabuf) -{ - const struct pixel_format_info *info; - - info = pixel_format_get_info(dmabuf->attributes.format & - ~DRM_FORMAT_BIG_ENDIAN); - if (!info) - return false; - - return pixel_format_is_opaque(info); -} - -#ifdef HAVE_TBM -static bool -tbm_surface_is_opaque(tbm_surface_h tsurface) -{ - const struct pixel_format_info *info; - - /* format of tbm surface use fourcc */ - info = pixel_format_get_info(tbm_surface_get_format(tsurface) & - ~DRM_FORMAT_BIG_ENDIAN); - if (!info) - return false; - - return pixel_format_is_opaque(info); -} - -static void -gl_renderer_attach_tbm_surface(struct weston_surface *surface, - struct weston_buffer *buffer, - tbm_surface_h tsurface) -{ - struct gl_renderer *gr = get_renderer(surface->compositor); - struct gl_surface_state *gs = get_surface_state(surface); - tbm_format format; - int num_planes = 1; - int i; - - buffer->width = tbm_surface_get_width(tsurface); - buffer->height = tbm_surface_get_height(tsurface); - /* how to know it? */ - buffer->y_inverted = true; - - for (i = 0; i < gs->num_images; i++) - egl_image_unref(gs->images[i]); - gs->num_images = 0; - - gs->target = GL_TEXTURE_2D; - gs->pitch = buffer->width; - gs->height = buffer->height; - gs->buffer_type = BUFFER_TYPE_EGL; - gs->y_inverted = buffer->y_inverted; - surface->is_opaque = tbm_surface_is_opaque(tsurface); - - format = tbm_surface_get_format(tsurface); - switch(format) { - case TBM_FORMAT_ARGB8888: - case TBM_FORMAT_XRGB8888: - gs->shader = &gr->texture_shader_rgba; - break; - default: - gs->shader = &gr->texture_shader_egl_external; - break; - } - -#ifndef EGL_NATIVE_SURFACE_TIZEN -#define EGL_NATIVE_SURFACE_TIZEN 0x32A1 -#endif - - ensure_textures(gs, num_planes); - for (i = 0; i < num_planes; i++) { - gs->images[i] = egl_image_create(gr, - EGL_NATIVE_SURFACE_TIZEN, - (void *)tsurface, - NULL); - if (!gs->images[i]) { - weston_log("failed to create img for plane %d\n", i); - continue; - } - gs->num_images++; - - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(gs->target, gs->textures[i]); - gr->image_target_texture_2d(gs->target, - gs->images[i]->image); - } -} -#endif - -static void -gl_renderer_attach_dmabuf(struct weston_surface *surface, - struct weston_buffer *buffer, - struct linux_dmabuf_buffer *dmabuf) -{ - struct gl_renderer *gr = get_renderer(surface->compositor); - struct gl_surface_state *gs = get_surface_state(surface); - struct dmabuf_image *image; - int i; - int ret; - - if (!gr->has_dmabuf_import) { - linux_dmabuf_buffer_send_server_error(dmabuf, - "EGL dmabuf import not supported"); - return; - } - - buffer->width = dmabuf->attributes.width; - buffer->height = dmabuf->attributes.height; - - /* - * GL-renderer uses the OpenGL convention of texture coordinates, where - * the origin is at bottom-left. Because dmabuf buffers have the origin - * at top-left, we must invert the Y_INVERT flag to get the image right. - */ - buffer->y_inverted = - !(dmabuf->attributes.flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT); - - for (i = 0; i < gs->num_images; i++) - egl_image_unref(gs->images[i]); - gs->num_images = 0; - - gs->pitch = buffer->width; - gs->height = buffer->height; - gs->buffer_type = BUFFER_TYPE_EGL; - gs->y_inverted = buffer->y_inverted; - gs->direct_display = dmabuf->direct_display; - surface->is_opaque = dmabuf_is_opaque(dmabuf); - - /* - * We try to always hold an imported EGLImage from the dmabuf - * to prevent the client from preventing re-imports. But, we also - * need to re-import every time the contents may change because - * GL driver's caching may need flushing. - * - * Here we release the cache reference which has to be final. - */ - if (dmabuf->direct_display) - return; - - image = linux_dmabuf_buffer_get_user_data(dmabuf); - - /* The dmabuf_image should have been created during the import */ - assert(image != NULL); - - for (i = 0; i < image->num_images; ++i) { - ret = egl_image_unref(image->images[i]); - assert(ret == 0); - } - - if (!import_known_dmabuf(gr, image)) { - linux_dmabuf_buffer_send_server_error(dmabuf, "EGL dmabuf import failed"); - return; - } - - gs->num_images = image->num_images; - for (i = 0; i < gs->num_images; ++i) - gs->images[i] = egl_image_ref(image->images[i]); - - gs->target = image->target; - ensure_textures(gs, gs->num_images); - for (i = 0; i < gs->num_images; ++i) { - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(gs->target, gs->textures[i]); - gr->image_target_texture_2d(gs->target, gs->images[i]->image); - } - - gs->shader = image->shader; -} - -static void -gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) -{ - struct weston_compositor *ec = es->compositor; - struct gl_renderer *gr = get_renderer(ec); - struct gl_surface_state *gs = get_surface_state(es); - struct wl_shm_buffer *shm_buffer; - struct linux_dmabuf_buffer *dmabuf; - EGLint format; - int i; -#ifdef HAVE_TBM - tbm_surface_h tsurface; -#endif - - weston_buffer_reference(&gs->buffer_ref, buffer); - weston_buffer_release_reference(&gs->buffer_release_ref, - es->buffer_release_ref.buffer_release); - - if (!buffer) { - for (i = 0; i < gs->num_images; i++) { - egl_image_unref(gs->images[i]); - gs->images[i] = NULL; - } - gs->num_images = 0; - glDeleteTextures(gs->num_textures, gs->textures); - gs->num_textures = 0; - gs->buffer_type = BUFFER_TYPE_NULL; - gs->y_inverted = true; - gs->direct_display = false; - es->is_opaque = false; - return; - } - - shm_buffer = wl_shm_buffer_get(buffer->resource); - - if (shm_buffer) - gl_renderer_attach_shm(es, buffer, shm_buffer); -#ifdef HAVE_TBM - else if ((tsurface = wayland_tbm_server_get_surface(gr->wl_tbm_server, buffer->resource))) - gl_renderer_attach_tbm_surface(es, buffer, tsurface); -#endif - else if (gr->has_bind_display && - gr->query_buffer(gr->egl_display, (void *)buffer->resource, - EGL_TEXTURE_FORMAT, &format)) - gl_renderer_attach_egl(es, buffer, format); - else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource))) - gl_renderer_attach_dmabuf(es, buffer, dmabuf); - else { - weston_log("unhandled buffer type!\n"); - if (gr->has_bind_display) { - weston_log("eglQueryWaylandBufferWL failed\n"); - gl_renderer_print_egl_error_state(); - } - weston_buffer_reference(&gs->buffer_ref, NULL); - weston_buffer_release_reference(&gs->buffer_release_ref, NULL); - gs->buffer_type = BUFFER_TYPE_NULL; - gs->y_inverted = true; - es->is_opaque = false; - weston_buffer_send_server_error(buffer, - "disconnecting due to unhandled buffer type"); - } -} - -static void -gl_renderer_surface_set_color(struct weston_surface *surface, - float red, float green, float blue, float alpha) -{ - struct gl_surface_state *gs = get_surface_state(surface); - struct gl_renderer *gr = get_renderer(surface->compositor); - - gs->color[0] = red; - gs->color[1] = green; - gs->color[2] = blue; - gs->color[3] = alpha; - gs->buffer_type = BUFFER_TYPE_SOLID; - gs->pitch = 1; - gs->height = 1; - - gs->shader = &gr->solid_shader; -} - -static void -gl_renderer_surface_get_content_size(struct weston_surface *surface, - int *width, int *height) -{ - struct gl_surface_state *gs = get_surface_state(surface); - - if (gs->buffer_type == BUFFER_TYPE_NULL) { - *width = 0; - *height = 0; - } else { - *width = gs->pitch; - *height = gs->height; - } -} - -static uint32_t -pack_color(pixman_format_code_t format, float *c) -{ - uint8_t r = round(c[0] * 255.0f); - uint8_t g = round(c[1] * 255.0f); - uint8_t b = round(c[2] * 255.0f); - uint8_t a = round(c[3] * 255.0f); - - switch (format) { - case PIXMAN_a8b8g8r8: - return (a << 24) | (b << 16) | (g << 8) | r; - default: - assert(0); - return 0; - } -} - -static int -gl_renderer_surface_copy_content(struct weston_surface *surface, - void *target, size_t size, - int src_x, int src_y, - int width, int height) -{ - static const GLfloat verts[4 * 2] = { - 0.0f, 0.0f, - 1.0f, 0.0f, - 1.0f, 1.0f, - 0.0f, 1.0f - }; - static const GLfloat projmat_normal[16] = { /* transpose */ - 2.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 2.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, 1.0f - }; - static const GLfloat projmat_yinvert[16] = { /* transpose */ - 2.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -2.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - -1.0f, 1.0f, 0.0f, 1.0f - }; - const pixman_format_code_t format = PIXMAN_a8b8g8r8; - const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */ - const GLenum gl_format = GL_RGBA; /* PIXMAN_a8b8g8r8 little-endian */ - struct gl_renderer *gr = get_renderer(surface->compositor); - struct gl_surface_state *gs = get_surface_state(surface); - int cw, ch; - GLuint fbo; - GLuint tex; - GLenum status; - const GLfloat *proj; - int i; - - gl_renderer_surface_get_content_size(surface, &cw, &ch); - - switch (gs->buffer_type) { - case BUFFER_TYPE_NULL: - return -1; - case BUFFER_TYPE_SOLID: - *(uint32_t *)target = pack_color(format, gs->color); - return 0; - case BUFFER_TYPE_SHM: - gl_renderer_flush_damage(surface); - /* fall through */ - case BUFFER_TYPE_EGL: - break; - } - - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cw, ch, - 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - glBindTexture(GL_TEXTURE_2D, 0); - - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, tex, 0); - - status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - weston_log("%s: fbo error: %#x\n", __func__, status); - glDeleteFramebuffers(1, &fbo); - glDeleteTextures(1, &tex); - return -1; - } - - glViewport(0, 0, cw, ch); - glDisable(GL_BLEND); - use_shader(gr, gs->shader); - if (gs->y_inverted) - proj = projmat_normal; - else - proj = projmat_yinvert; - - glUniformMatrix4fv(gs->shader->proj_uniform, 1, GL_FALSE, proj); - glUniform1f(gs->shader->alpha_uniform, 1.0f); - - for (i = 0; i < gs->num_textures; i++) { - glUniform1i(gs->shader->tex_uniforms[i], i); - - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(gs->target, gs->textures[i]); - glTexParameteri(gs->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(gs->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } - - /* position: */ - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); - glEnableVertexAttribArray(0); - - /* texcoord: */ - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, verts); - glEnableVertexAttribArray(1); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(0); - - glPixelStorei(GL_PACK_ALIGNMENT, bytespp); - glReadPixels(src_x, src_y, width, height, gl_format, - GL_UNSIGNED_BYTE, target); - - glDeleteFramebuffers(1, &fbo); - glDeleteTextures(1, &tex); - - return 0; -} - -static void -surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr) -{ - int i; - - wl_list_remove(&gs->surface_destroy_listener.link); - wl_list_remove(&gs->renderer_destroy_listener.link); - - gs->surface->renderer_state = NULL; - - glDeleteTextures(gs->num_textures, gs->textures); - - for (i = 0; i < gs->num_images; i++) - egl_image_unref(gs->images[i]); - - weston_buffer_reference(&gs->buffer_ref, NULL); - weston_buffer_release_reference(&gs->buffer_release_ref, NULL); - pixman_region32_fini(&gs->texture_damage); - free(gs); -} - -static void -surface_state_handle_surface_destroy(struct wl_listener *listener, void *data) -{ - struct gl_surface_state *gs; - struct gl_renderer *gr; - - gs = container_of(listener, struct gl_surface_state, - surface_destroy_listener); - - gr = get_renderer(gs->surface->compositor); - - surface_state_destroy(gs, gr); -} - -static void -surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data) -{ - struct gl_surface_state *gs; - struct gl_renderer *gr; - - gr = data; - - gs = container_of(listener, struct gl_surface_state, - renderer_destroy_listener); - - surface_state_destroy(gs, gr); -} - -static int -gl_renderer_create_surface(struct weston_surface *surface) -{ - struct gl_surface_state *gs; - struct gl_renderer *gr = get_renderer(surface->compositor); - - gs = zalloc(sizeof *gs); - if (gs == NULL) - return -1; - - /* A buffer is never attached to solid color surfaces, yet - * they still go through texcoord computations. Do not divide - * by zero there. - */ - gs->pitch = 1; - gs->y_inverted = true; - gs->direct_display = false; - - gs->surface = surface; - - pixman_region32_init(&gs->texture_damage); - surface->renderer_state = gs; - - gs->surface_destroy_listener.notify = - surface_state_handle_surface_destroy; - wl_signal_add(&surface->destroy_signal, - &gs->surface_destroy_listener); - - gs->renderer_destroy_listener.notify = - surface_state_handle_renderer_destroy; - wl_signal_add(&gr->destroy_signal, - &gs->renderer_destroy_listener); - - if (surface->buffer_ref.buffer) { - gl_renderer_attach(surface, surface->buffer_ref.buffer); - gl_renderer_flush_damage(surface); - } - - return 0; -} - -static const char vertex_shader[] = - "uniform mat4 proj;\n" - "attribute vec2 position;\n" - "attribute vec2 texcoord;\n" - "varying vec2 v_texcoord;\n" - "void main()\n" - "{\n" - " gl_Position = proj * vec4(position, 0.0, 1.0);\n" - " v_texcoord = texcoord;\n" - "}\n"; - -/* Declare common fragment shader uniforms */ -#define FRAGMENT_CONVERT_YUV \ - " y *= alpha;\n" \ - " u *= alpha;\n" \ - " v *= alpha;\n" \ - " gl_FragColor.r = y + 1.59602678 * v;\n" \ - " gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v;\n" \ - " gl_FragColor.b = y + 2.01723214 * u;\n" \ - " gl_FragColor.a = alpha;\n" - -static const char fragment_debug[] = - " gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + gl_FragColor * 0.8;\n"; - -static const char fragment_brace[] = - "}\n"; - -static const char texture_fragment_shader_rgba[] = - "precision mediump float;\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D tex;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;" - ; - -static const char texture_fragment_shader_rgbx[] = - "precision mediump float;\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D tex;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor.rgb = alpha * texture2D(tex, v_texcoord).rgb\n;" - " gl_FragColor.a = alpha;\n" - ; - -static const char texture_fragment_shader_egl_external[] = - "#extension GL_OES_EGL_image_external : require\n" - "precision mediump float;\n" - "varying vec2 v_texcoord;\n" - "uniform samplerExternalOES tex;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;" - ; - -static const char texture_fragment_shader_y_uv[] = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "uniform sampler2D tex1;\n" - "varying vec2 v_texcoord;\n" - "uniform float alpha;\n" - "void main() {\n" - " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" - " float u = texture2D(tex1, v_texcoord).r - 0.5;\n" - " float v = texture2D(tex1, v_texcoord).g - 0.5;\n" - FRAGMENT_CONVERT_YUV - ; - -static const char texture_fragment_shader_y_u_v[] = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "uniform sampler2D tex1;\n" - "uniform sampler2D tex2;\n" - "varying vec2 v_texcoord;\n" - "uniform float alpha;\n" - "void main() {\n" - " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" - " float u = texture2D(tex1, v_texcoord).x - 0.5;\n" - " float v = texture2D(tex2, v_texcoord).x - 0.5;\n" - FRAGMENT_CONVERT_YUV - ; - -static const char texture_fragment_shader_y_xuxv[] = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "uniform sampler2D tex1;\n" - "varying vec2 v_texcoord;\n" - "uniform float alpha;\n" - "void main() {\n" - " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" - " float u = texture2D(tex1, v_texcoord).g - 0.5;\n" - " float v = texture2D(tex1, v_texcoord).a - 0.5;\n" - FRAGMENT_CONVERT_YUV - ; - -static const char texture_fragment_shader_xyuv[] = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "varying vec2 v_texcoord;\n" - "uniform float alpha;\n" - "void main() {\n" - " float y = 1.16438356 * (texture2D(tex, v_texcoord).b - 0.0625);\n" - " float u = texture2D(tex, v_texcoord).g - 0.5;\n" - " float v = texture2D(tex, v_texcoord).r - 0.5;\n" - FRAGMENT_CONVERT_YUV - ; - -static const char solid_fragment_shader[] = - "precision mediump float;\n" - "uniform vec4 color;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor = alpha * color\n;" - ; - -static int -compile_shader(GLenum type, int count, const char **sources) -{ - GLuint s; - char msg[512]; - GLint status; - - s = glCreateShader(type); - glShaderSource(s, count, sources, NULL); - glCompileShader(s); - glGetShaderiv(s, GL_COMPILE_STATUS, &status); - if (!status) { - glGetShaderInfoLog(s, sizeof msg, NULL, msg); - weston_log("shader info: %s\n", msg); - return GL_NONE; - } - - return s; -} - -static int -shader_init(struct gl_shader *shader, struct gl_renderer *renderer, - const char *vertex_source, const char *fragment_source) -{ - char msg[512]; - GLint status; - int count; - const char *sources[3]; - - shader->vertex_shader = - compile_shader(GL_VERTEX_SHADER, 1, &vertex_source); - - if (renderer->fragment_shader_debug) { - sources[0] = fragment_source; - sources[1] = fragment_debug; - sources[2] = fragment_brace; - count = 3; - } else { - sources[0] = fragment_source; - sources[1] = fragment_brace; - count = 2; - } - - shader->fragment_shader = - compile_shader(GL_FRAGMENT_SHADER, count, sources); - - shader->program = glCreateProgram(); - glAttachShader(shader->program, shader->vertex_shader); - glAttachShader(shader->program, shader->fragment_shader); - glBindAttribLocation(shader->program, 0, "position"); - glBindAttribLocation(shader->program, 1, "texcoord"); - - glLinkProgram(shader->program); - glGetProgramiv(shader->program, GL_LINK_STATUS, &status); - if (!status) { - glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg); - weston_log("link info: %s\n", msg); - return -1; - } - - shader->proj_uniform = glGetUniformLocation(shader->program, "proj"); - shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex"); - shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1"); - shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2"); - shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha"); - shader->color_uniform = glGetUniformLocation(shader->program, "color"); - - return 0; -} - -static void -shader_release(struct gl_shader *shader) -{ - glDeleteShader(shader->vertex_shader); - glDeleteShader(shader->fragment_shader); - glDeleteProgram(shader->program); - - shader->vertex_shader = 0; - shader->fragment_shader = 0; - shader->program = 0; -} - -static void -log_extensions(const char *name, const char *extensions) -{ - const char *p, *end; - int l; - int len; - - l = weston_log("%s:", name); - p = extensions; - while (*p) { - end = strchrnul(p, ' '); - len = end - p; - if (l + len > 78) - l = weston_log_continue("\n" STAMP_SPACE "%.*s", - len, p); - else - l += weston_log_continue(" %.*s", len, p); - for (p = end; isspace(*p); p++) - ; - } - weston_log_continue("\n"); -} - -static void -log_egl_info(EGLDisplay egldpy) -{ - const char *str; - - str = eglQueryString(egldpy, EGL_VERSION); - weston_log("EGL version: %s\n", str ? str : "(null)"); - - str = eglQueryString(egldpy, EGL_VENDOR); - weston_log("EGL vendor: %s\n", str ? str : "(null)"); - - str = eglQueryString(egldpy, EGL_CLIENT_APIS); - weston_log("EGL client APIs: %s\n", str ? str : "(null)"); - - str = eglQueryString(egldpy, EGL_EXTENSIONS); - log_extensions("EGL extensions", str ? str : "(null)"); -} - -static void -log_gl_info(void) -{ - const char *str; - - str = (char *)glGetString(GL_VERSION); - weston_log("GL version: %s\n", str ? str : "(null)"); - - str = (char *)glGetString(GL_SHADING_LANGUAGE_VERSION); - weston_log("GLSL version: %s\n", str ? str : "(null)"); - - str = (char *)glGetString(GL_VENDOR); - weston_log("GL vendor: %s\n", str ? str : "(null)"); - - str = (char *)glGetString(GL_RENDERER); - weston_log("GL renderer: %s\n", str ? str : "(null)"); - - str = (char *)glGetString(GL_EXTENSIONS); - log_extensions("GL extensions", str ? str : "(null)"); -} - -static void -gl_renderer_output_set_border(struct weston_output *output, - enum gl_renderer_border_side side, - int32_t width, int32_t height, - int32_t tex_width, unsigned char *data) -{ - struct gl_output_state *go = get_output_state(output); - - if (go->borders[side].width != width || - go->borders[side].height != height) - /* In this case, we have to blow everything and do a full - * repaint. */ - go->border_status |= BORDER_SIZE_CHANGED | BORDER_ALL_DIRTY; - - if (data == NULL) { - width = 0; - height = 0; - } - - go->borders[side].width = width; - go->borders[side].height = height; - go->borders[side].tex_width = tex_width; - go->borders[side].data = data; - go->border_status |= 1 << side; -} - -static int -gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface); - -static EGLSurface -gl_renderer_create_window_surface(struct gl_renderer *gr, - EGLNativeWindowType window_for_legacy, - void *window_for_platform, - const uint32_t *drm_formats, - unsigned drm_formats_count) -{ - EGLSurface egl_surface = EGL_NO_SURFACE; - EGLConfig egl_config; - - egl_config = gl_renderer_get_egl_config(gr, EGL_WINDOW_BIT, - drm_formats, drm_formats_count); - if (egl_config == EGL_NO_CONFIG_KHR) - return EGL_NO_SURFACE; - - log_egl_config_info(gr->egl_display, egl_config); - - if (gr->create_platform_window) - egl_surface = gr->create_platform_window(gr->egl_display, - egl_config, - window_for_platform, - NULL); - else - egl_surface = eglCreateWindowSurface(gr->egl_display, - egl_config, - window_for_legacy, NULL); - - return egl_surface; -} - -static int -gl_renderer_output_create(struct weston_output *output, - EGLSurface surface) -{ - struct gl_output_state *go; - int i; - - go = zalloc(sizeof *go); - if (go == NULL) - return -1; - - go->egl_surface = surface; - - for (i = 0; i < BUFFER_DAMAGE_COUNT; i++) - pixman_region32_init(&go->buffer_damage[i]); - - wl_list_init(&go->timeline_render_point_list); - - go->begin_render_sync = EGL_NO_SYNC_KHR; - go->end_render_sync = EGL_NO_SYNC_KHR; - - output->renderer_state = go; - - return 0; -} - -static int -gl_renderer_output_window_create(struct weston_output *output, - EGLNativeWindowType window_for_legacy, - void *window_for_platform, - const uint32_t *drm_formats, - unsigned drm_formats_count) -{ - struct weston_compositor *ec = output->compositor; - struct gl_renderer *gr = get_renderer(ec); - EGLSurface egl_surface = EGL_NO_SURFACE; - int ret = 0; - - egl_surface = gl_renderer_create_window_surface(gr, - window_for_legacy, - window_for_platform, - drm_formats, - drm_formats_count); - if (egl_surface == EGL_NO_SURFACE) { - weston_log("failed to create egl surface\n"); - return -1; - } - - ret = gl_renderer_output_create(output, egl_surface); - if (ret < 0) - weston_platform_destroy_egl_surface(gr->egl_display, egl_surface); - - return ret; -} - -static int -gl_renderer_output_pbuffer_create(struct weston_output *output, - int width, - int height, - const uint32_t *drm_formats, - unsigned drm_formats_count) -{ - struct gl_renderer *gr = get_renderer(output->compositor); - EGLConfig pbuffer_config; - EGLSurface egl_surface; - int ret; - EGLint pbuffer_attribs[] = { - EGL_WIDTH, width, - EGL_HEIGHT, height, - EGL_NONE - }; - - pbuffer_config = gl_renderer_get_egl_config(gr, EGL_PBUFFER_BIT, - drm_formats, - drm_formats_count); - if (pbuffer_config == EGL_NO_CONFIG_KHR) { - weston_log("failed to choose EGL config for PbufferSurface\n"); - return -1; - } - - log_egl_config_info(gr->egl_display, pbuffer_config); - - egl_surface = eglCreatePbufferSurface(gr->egl_display, pbuffer_config, - pbuffer_attribs); - if (egl_surface == EGL_NO_SURFACE) { - weston_log("failed to create egl surface\n"); - gl_renderer_print_egl_error_state(); - return -1; - } - - ret = gl_renderer_output_create(output, egl_surface); - if (ret < 0) - eglDestroySurface(gr->egl_display, egl_surface); - - return ret; -} - -static void -gl_renderer_output_destroy(struct weston_output *output) -{ - struct gl_renderer *gr = get_renderer(output->compositor); - struct gl_output_state *go = get_output_state(output); - struct timeline_render_point *trp, *tmp; - int i; - - for (i = 0; i < 2; i++) - pixman_region32_fini(&go->buffer_damage[i]); - - eglMakeCurrent(gr->egl_display, - EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT); - - weston_platform_destroy_egl_surface(gr->egl_display, go->egl_surface); - - if (!wl_list_empty(&go->timeline_render_point_list)) - weston_log("warning: discarding pending timeline render" - "objects at output destruction"); - - wl_list_for_each_safe(trp, tmp, &go->timeline_render_point_list, link) - timeline_render_point_destroy(trp); - - if (go->begin_render_sync != EGL_NO_SYNC_KHR) - gr->destroy_sync(gr->egl_display, go->begin_render_sync); - if (go->end_render_sync != EGL_NO_SYNC_KHR) - gr->destroy_sync(gr->egl_display, go->end_render_sync); - - free(go); -} - -static int -gl_renderer_create_fence_fd(struct weston_output *output) -{ - struct gl_output_state *go = get_output_state(output); - struct gl_renderer *gr = get_renderer(output->compositor); - int fd; - - if (go->end_render_sync == EGL_NO_SYNC_KHR) - return -1; - - fd = gr->dup_native_fence_fd(gr->egl_display, go->end_render_sync); - if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) - return -1; - - return fd; -} - -static void -gl_renderer_destroy(struct weston_compositor *ec) -{ - struct gl_renderer *gr = get_renderer(ec); - struct dmabuf_image *image, *next; - - wl_signal_emit(&gr->destroy_signal, gr); - - if (gr->has_bind_display) - gr->unbind_display(gr->egl_display, ec->wl_display); - - /* Work around crash in egl_dri2.c's dri2_make_current() - when does this apply? */ - eglMakeCurrent(gr->egl_display, - EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT); - - - wl_list_for_each_safe(image, next, &gr->dmabuf_images, link) - dmabuf_image_destroy(image); - - if (gr->dummy_surface != EGL_NO_SURFACE) - weston_platform_destroy_egl_surface(gr->egl_display, - gr->dummy_surface); - - eglTerminate(gr->egl_display); - eglReleaseThread(); - - wl_list_remove(&gr->output_destroy_listener.link); - - wl_array_release(&gr->vertices); - wl_array_release(&gr->vtxcnt); - - if (gr->fragment_binding) - weston_binding_destroy(gr->fragment_binding); - if (gr->fan_binding) - weston_binding_destroy(gr->fan_binding); - - free(gr); -} - -/** Checks whether a platform EGL client extension is supported - * - * \param ec The weston compositor - * \param extension_suffix The EGL client extension suffix - * \return 1 if supported, 0 if using fallbacks, -1 unsupported - * - * This function checks whether a specific platform_* extension is supported - * by EGL. - * - * The extension suffix should be the suffix of the platform extension (that - * specifies a platform argument as defined in EGL_EXT_platform_base). For - * example, passing "foo" will check whether either "EGL_KHR_platform_foo", - * "EGL_EXT_platform_foo", or "EGL_MESA_platform_foo" is supported. - * - * The return value is 1: - * - if the supplied EGL client extension is supported. - * The return value is 0: - * - if the platform_base client extension isn't supported so will - * fallback to eglGetDisplay and friends. - * The return value is -1: - * - if the supplied EGL client extension is not supported. - */ -static int -gl_renderer_supports(struct weston_compositor *ec, - const char *extension_suffix) -{ - static const char *extensions = NULL; - char s[64]; - - if (!extensions) { - extensions = (const char *) eglQueryString( - EGL_NO_DISPLAY, EGL_EXTENSIONS); - - if (!extensions) - return 0; - - log_extensions("EGL client extensions", - extensions); - } - - if (!weston_check_egl_extension(extensions, "EGL_EXT_platform_base")) - return 0; - - snprintf(s, sizeof s, "EGL_KHR_platform_%s", extension_suffix); - if (weston_check_egl_extension(extensions, s)) - return 1; - - snprintf(s, sizeof s, "EGL_EXT_platform_%s", extension_suffix); - if (weston_check_egl_extension(extensions, s)) - return 1; - - snprintf(s, sizeof s, "EGL_MESA_platform_%s", extension_suffix); - if (weston_check_egl_extension(extensions, s)) - return 1; - - /* at this point we definitely have some platform extensions but - * haven't found the supplied platform, so chances are it's - * not supported. */ - - return -1; -} - -static const char * -platform_to_extension(EGLenum platform) -{ - switch (platform) { - case EGL_PLATFORM_GBM_KHR: - return "gbm"; - case EGL_PLATFORM_WAYLAND_KHR: - return "wayland"; - case EGL_PLATFORM_X11_KHR: - return "x11"; - case EGL_PLATFORM_SURFACELESS_MESA: - return "surfaceless"; - default: - assert(0 && "bad EGL platform enum"); - } -} - -static void -output_handle_destroy(struct wl_listener *listener, void *data) -{ - struct gl_renderer *gr; - struct weston_output *output = data; - - gr = container_of(listener, struct gl_renderer, - output_destroy_listener); - - if (wl_list_empty(&output->compositor->output_list)) - eglMakeCurrent(gr->egl_display, gr->dummy_surface, - gr->dummy_surface, gr->egl_context); -} - -static int -gl_renderer_create_pbuffer_surface(struct gl_renderer *gr) { - EGLConfig pbuffer_config; - static const EGLint pbuffer_attribs[] = { - EGL_WIDTH, 10, - EGL_HEIGHT, 10, - EGL_NONE - }; - - pbuffer_config = gl_renderer_get_egl_config(gr, EGL_PBUFFER_BIT, - NULL, 0); - if (pbuffer_config == EGL_NO_CONFIG_KHR) { - weston_log("failed to choose EGL config for PbufferSurface\n"); - return -1; - } - - gr->dummy_surface = eglCreatePbufferSurface(gr->egl_display, - pbuffer_config, - pbuffer_attribs); - - if (gr->dummy_surface == EGL_NO_SURFACE) { - weston_log("failed to create PbufferSurface\n"); - return -1; - } - - return 0; -} - -static int -gl_renderer_display_create(struct weston_compositor *ec, - EGLenum platform, - void *native_display, - EGLint egl_surface_type, - const uint32_t *drm_formats, - unsigned drm_formats_count) -{ - struct gl_renderer *gr; - EGLint major, minor; - int supports = 0; - - if (platform) { - supports = gl_renderer_supports( - ec, platform_to_extension(platform)); - if (supports < 0) - return -1; - } - - /* Surfaceless is unusable without platform_base extension */ - if (supports == 0 && platform == EGL_PLATFORM_SURFACELESS_MESA) - return -1; - - gr = zalloc(sizeof *gr); - if (gr == NULL) - return -1; - - gr->base.read_pixels = gl_renderer_read_pixels; - gr->base.repaint_output = gl_renderer_repaint_output; - gr->base.flush_damage = gl_renderer_flush_damage; - gr->base.attach = gl_renderer_attach; - gr->base.surface_set_color = gl_renderer_surface_set_color; - gr->base.destroy = gl_renderer_destroy; - gr->base.surface_get_content_size = - gl_renderer_surface_get_content_size; - gr->base.surface_copy_content = gl_renderer_surface_copy_content; - gr->platform = platform; - gr->egl_display = NULL; - - /* extension_suffix is supported */ - if (supports) { - if (!get_platform_display) { - get_platform_display = (void *) eglGetProcAddress( - "eglGetPlatformDisplayEXT"); - } - - /* also wrap this in the supports check because - * eglGetProcAddress can return non-NULL and still not - * support the feature at runtime, so ensure the - * appropriate extension checks have been done. */ - if (get_platform_display && platform) { - gr->egl_display = get_platform_display(platform, - native_display, - NULL); - } - } - - if (!gr->egl_display) { - weston_log("warning: either no EGL_EXT_platform_base " - "support or specific platform support; " - "falling back to eglGetDisplay.\n"); - gr->egl_display = eglGetDisplay(native_display); - } - - if (gr->egl_display == EGL_NO_DISPLAY) { - weston_log("failed to create display\n"); - goto fail; - } - - if (!eglInitialize(gr->egl_display, &major, &minor)) { - weston_log("failed to initialize display\n"); - goto fail_with_error; - } - - log_egl_info(gr->egl_display); - - ec->renderer = &gr->base; - - if (gl_renderer_setup_egl_extensions(ec) < 0) - goto fail_with_error; - - if (!gr->has_configless_context) { - if (!gr->has_surfaceless_context) - egl_surface_type |= EGL_PBUFFER_BIT; - - gr->egl_config = gl_renderer_get_egl_config(gr, - egl_surface_type, - drm_formats, - drm_formats_count); - if (gr->egl_config == EGL_NO_CONFIG_KHR) { - weston_log("failed to choose EGL config\n"); - goto fail_terminate; - } - } - - ec->capabilities |= WESTON_CAP_ROTATION_ANY; - ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP; - ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK; - if (gr->has_native_fence_sync && gr->has_wait_sync) - ec->capabilities |= WESTON_CAP_EXPLICIT_SYNC; - - wl_list_init(&gr->dmabuf_images); - if (gr->has_dmabuf_import) { - gr->base.import_dmabuf = gl_renderer_import_dmabuf; - gr->base.query_dmabuf_formats = - gl_renderer_query_dmabuf_formats; - gr->base.query_dmabuf_modifiers = - gl_renderer_query_dmabuf_modifiers; - } - - if (gr->has_surfaceless_context) { - weston_log("EGL_KHR_surfaceless_context available\n"); - gr->dummy_surface = EGL_NO_SURFACE; - } else { - weston_log("EGL_KHR_surfaceless_context unavailable. " - "Trying PbufferSurface\n"); - - if (gl_renderer_create_pbuffer_surface(gr) < 0) - goto fail_with_error; - } - - wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565); - wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_YUV420); - wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_NV12); - wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_YUYV); - - wl_signal_init(&gr->destroy_signal); - - if (gl_renderer_setup(ec, gr->dummy_surface) < 0) { - if (gr->dummy_surface != EGL_NO_SURFACE) - weston_platform_destroy_egl_surface(gr->egl_display, - gr->dummy_surface); - goto fail_with_error; - } - -#ifdef HAVE_TBM - tbm_bufmgr bufmgr; - - gr->wl_tbm_server = wayland_tbm_server_init(ec->wl_display, NULL, -1, -1); - if (!gr->wl_tbm_server) { - weston_log("Fail to init wayland_tbm_server\n"); - goto fail_with_error; - } - - bufmgr = wayland_tbm_server_get_bufmgr(gr->wl_tbm_server); - if (!bufmgr) { - weston_log("Fail to get bufmgr\n"); - wayland_tbm_server_deinit(gr->wl_tbm_server); - goto fail_with_error; - } - - tbm_bufmgr_bind_native_display(bufmgr, (void *)ec->wl_display); -#endif - return 0; - -fail_with_error: - gl_renderer_print_egl_error_state(); -fail_terminate: - eglTerminate(gr->egl_display); -fail: - free(gr); - return -1; -} - -static int -compile_shaders(struct weston_compositor *ec) -{ - struct gl_renderer *gr = get_renderer(ec); - - gr->texture_shader_rgba.vertex_source = vertex_shader; - gr->texture_shader_rgba.fragment_source = texture_fragment_shader_rgba; - - gr->texture_shader_rgbx.vertex_source = vertex_shader; - gr->texture_shader_rgbx.fragment_source = texture_fragment_shader_rgbx; - - gr->texture_shader_egl_external.vertex_source = vertex_shader; - gr->texture_shader_egl_external.fragment_source = - texture_fragment_shader_egl_external; - - gr->texture_shader_y_uv.vertex_source = vertex_shader; - gr->texture_shader_y_uv.fragment_source = texture_fragment_shader_y_uv; - - gr->texture_shader_y_u_v.vertex_source = vertex_shader; - gr->texture_shader_y_u_v.fragment_source = - texture_fragment_shader_y_u_v; - - gr->texture_shader_y_xuxv.vertex_source = vertex_shader; - gr->texture_shader_y_xuxv.fragment_source = - texture_fragment_shader_y_xuxv; - - gr->texture_shader_xyuv.vertex_source = vertex_shader; - gr->texture_shader_xyuv.fragment_source = texture_fragment_shader_xyuv; - - gr->solid_shader.vertex_source = vertex_shader; - gr->solid_shader.fragment_source = solid_fragment_shader; - - return 0; -} - -static void -fragment_debug_binding(struct weston_keyboard *keyboard, - const struct timespec *time, - uint32_t key, void *data) -{ - struct weston_compositor *ec = data; - struct gl_renderer *gr = get_renderer(ec); - struct weston_output *output; - - gr->fragment_shader_debug = !gr->fragment_shader_debug; - - shader_release(&gr->texture_shader_rgba); - shader_release(&gr->texture_shader_rgbx); - shader_release(&gr->texture_shader_egl_external); - shader_release(&gr->texture_shader_y_uv); - shader_release(&gr->texture_shader_y_u_v); - shader_release(&gr->texture_shader_y_xuxv); - shader_release(&gr->texture_shader_xyuv); - shader_release(&gr->solid_shader); - - /* Force use_shader() to call glUseProgram(), since we need to use - * the recompiled version of the shader. */ - gr->current_shader = NULL; - - wl_list_for_each(output, &ec->output_list, link) - weston_output_damage(output); -} - -static void -fan_debug_repaint_binding(struct weston_keyboard *keyboard, - const struct timespec *time, - uint32_t key, void *data) -{ - struct weston_compositor *compositor = data; - struct gl_renderer *gr = get_renderer(compositor); - - gr->fan_debug = !gr->fan_debug; - weston_compositor_damage_all(compositor); -} - -static uint32_t -get_gl_version(void) -{ - const char *version; - int major, minor; - - version = (const char *) glGetString(GL_VERSION); - if (version && - (sscanf(version, "%d.%d", &major, &minor) == 2 || - sscanf(version, "OpenGL ES %d.%d", &major, &minor) == 2)) { - return GR_GL_VERSION(major, minor); - } - - return GR_GL_VERSION_INVALID; -} - -static int -gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) -{ - struct gl_renderer *gr = get_renderer(ec); - const char *extensions; - EGLBoolean ret; - - EGLint context_attribs[16] = { - EGL_CONTEXT_CLIENT_VERSION, 0, - }; - unsigned int nattr = 2; - - if (!eglBindAPI(EGL_OPENGL_ES_API)) { - weston_log("failed to bind EGL_OPENGL_ES_API\n"); - gl_renderer_print_egl_error_state(); - return -1; - } - - /* - * Being the compositor we require minimum output latency, - * so request a high priority context for ourselves - that should - * reschedule all of our rendering and its dependencies to be completed - * first. If the driver doesn't permit us to create a high priority - * context, it will fallback to the default priority (MEDIUM). - */ - if (gr->has_context_priority) { - context_attribs[nattr++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG; - context_attribs[nattr++] = EGL_CONTEXT_PRIORITY_HIGH_IMG; - } - - assert(nattr < ARRAY_LENGTH(context_attribs)); - context_attribs[nattr] = EGL_NONE; - - /* try to create an OpenGLES 3 context first */ - context_attribs[1] = 3; - gr->egl_context = eglCreateContext(gr->egl_display, gr->egl_config, - EGL_NO_CONTEXT, context_attribs); - if (gr->egl_context == NULL) { - /* and then fallback to OpenGLES 2 */ - context_attribs[1] = 2; - gr->egl_context = eglCreateContext(gr->egl_display, - gr->egl_config, - EGL_NO_CONTEXT, - context_attribs); - if (gr->egl_context == NULL) { - weston_log("failed to create context\n"); - gl_renderer_print_egl_error_state(); - return -1; - } - } - - if (gr->has_context_priority) { - EGLint value = EGL_CONTEXT_PRIORITY_MEDIUM_IMG; - - eglQueryContext(gr->egl_display, gr->egl_context, - EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value); - - if (value != EGL_CONTEXT_PRIORITY_HIGH_IMG) { - weston_log("Failed to obtain a high priority context.\n"); - /* Not an error, continue on as normal */ - } - } - - ret = eglMakeCurrent(gr->egl_display, egl_surface, - egl_surface, gr->egl_context); - if (ret == EGL_FALSE) { - weston_log("Failed to make EGL context current.\n"); - gl_renderer_print_egl_error_state(); - return -1; - } - - gr->gl_version = get_gl_version(); - if (gr->gl_version == GR_GL_VERSION_INVALID) { - weston_log("warning: failed to detect GLES version, " - "defaulting to 2.0.\n"); - gr->gl_version = GR_GL_VERSION(2, 0); - } - - log_gl_info(); - - gr->image_target_texture_2d = - (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES"); - - extensions = (const char *) glGetString(GL_EXTENSIONS); - if (!extensions) { - weston_log("Retrieving GL extension string failed.\n"); - return -1; - } - - if (!weston_check_egl_extension(extensions, "GL_EXT_texture_format_BGRA8888")) { - weston_log("GL_EXT_texture_format_BGRA8888 not available\n"); - return -1; - } - - if (weston_check_egl_extension(extensions, "GL_EXT_read_format_bgra")) - ec->read_format = PIXMAN_a8r8g8b8; - else - ec->read_format = PIXMAN_a8b8g8r8; - - if (gr->gl_version >= GR_GL_VERSION(3, 0) || - weston_check_egl_extension(extensions, "GL_EXT_unpack_subimage")) - gr->has_unpack_subimage = true; - - if (gr->gl_version >= GR_GL_VERSION(3, 0) || - weston_check_egl_extension(extensions, "GL_EXT_texture_rg")) - gr->has_gl_texture_rg = true; - - if (weston_check_egl_extension(extensions, "GL_OES_EGL_image_external")) - gr->has_egl_image_external = true; - - glActiveTexture(GL_TEXTURE0); - - if (compile_shaders(ec)) - return -1; - - gr->fragment_binding = - weston_compositor_add_debug_binding(ec, KEY_S, - fragment_debug_binding, - ec); - gr->fan_binding = - weston_compositor_add_debug_binding(ec, KEY_F, - fan_debug_repaint_binding, - ec); - - gr->output_destroy_listener.notify = output_handle_destroy; - wl_signal_add(&ec->output_destroyed_signal, - &gr->output_destroy_listener); - - weston_log("GL ES 2 renderer features:\n"); - weston_log_continue(STAMP_SPACE "read-back format: %s\n", - ec->read_format == PIXMAN_a8r8g8b8 ? "BGRA" : "RGBA"); - weston_log_continue(STAMP_SPACE "wl_shm sub-image to texture: %s\n", - gr->has_unpack_subimage ? "yes" : "no"); - weston_log_continue(STAMP_SPACE "EGL Wayland extension: %s\n", - gr->has_bind_display ? "yes" : "no"); - - - return 0; -} - -WL_EXPORT struct gl_renderer_interface gl_renderer_interface = { - .display_create = gl_renderer_display_create, - .output_window_create = gl_renderer_output_window_create, - .output_pbuffer_create = gl_renderer_output_pbuffer_create, - .output_destroy = gl_renderer_output_destroy, - .output_set_border = gl_renderer_output_set_border, - .create_fence_fd = gl_renderer_create_fence_fd, -}; diff --git a/libweston/renderer-gl/gl-renderer.h b/libweston/renderer-gl/gl-renderer.h deleted file mode 100644 index 2bca2d00..00000000 --- a/libweston/renderer-gl/gl-renderer.h +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright © 2012 John Kåre Alsaker - * - * 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 <stdint.h> - -#include <libweston/libweston.h> -#include "backend.h" -#include "libweston-internal.h" - -#ifdef ENABLE_EGL - -#include <EGL/egl.h> -#include <EGL/eglext.h> - -#else - -typedef int EGLint; -typedef int EGLenum; -typedef void *EGLDisplay; -typedef void *EGLSurface; -typedef void *EGLConfig; -typedef intptr_t EGLNativeDisplayType; -typedef intptr_t EGLNativeWindowType; -#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0) -#define EGL_PBUFFER_BIT 0x0001 -#define EGL_WINDOW_BIT 0x0004 - -#endif /* ENABLE_EGL */ - -enum gl_renderer_border_side { - GL_RENDERER_BORDER_TOP = 0, - GL_RENDERER_BORDER_LEFT = 1, - GL_RENDERER_BORDER_RIGHT = 2, - GL_RENDERER_BORDER_BOTTOM = 3, -}; - -struct gl_renderer_interface { - /** - * Initialize GL-renderer with the given EGL platform and native display - * - * \param ec The weston_compositor where to initialize. - * \param platform The EGL platform identifier. - * \param native_display The native display corresponding to the given - * EGL platform. - * \param egl_surface_type EGL_SURFACE_TYPE bits for the base EGLConfig. - * \param drm_formats Array of DRM pixel formats that are acceptable - * for the base EGLConfig. - * \param drm_formats_count The drm_formats array length. - * \return 0 on success, -1 on failure. - * - * This function creates an EGLDisplay and initializes it. It also - * creates the GL ES context and sets it up. It attempts GL ES 3.0 - * and falls back to GL ES 2.0 if 3.0 is not supported. - * - * If \c platform is zero or EGL_EXT_platform_base is not supported, - * choosing the platform is left for the EGL implementation. Otherwise - * the given platform is used explicitly if the EGL implementation - * advertises it. Without the advertisement this function fails. - * - * If neither EGL_KHR_no_config_context or EGL_MESA_configless_context - * are supported, the arguments egl_surface_type, drm_formats, and - * drm_formats_count are used to find a so called base EGLConfig. The - * GL context is created with the base EGLConfig, and outputs will be - * required to use the same config as well. If one or both of the - * extensions are supported, these arguments are unused, and each - * output can use a different EGLConfig (pixel format). - * - * The first format in drm_formats that matches any EGLConfig - * determines which EGLConfig is chosen. On EGL GBM platform, the - * pixel format must match exactly. On other platforms, it is enough - * that each R, G, B, A channel has the same number of bits as in the - * DRM format. - */ - int (*display_create)(struct weston_compositor *ec, - EGLenum platform, - void *native_display, - EGLint egl_surface_type, - const uint32_t *drm_formats, - unsigned drm_formats_count); - - /** - * Attach GL-renderer to the output with a native window - * - * \param output The output to create a rendering surface for. - * \param window_for_legacy Native window handle for - * eglCreateWindowSurface(). - * \param window_for_platform Native window handle for - * eglCreatePlatformWindowSurface(). - * \param drm_formats Array of DRM pixel formats that are acceptable. - * \param drm_formats_count The drm_formats array length. - * \return 0 on success, -1 on failure. - * - * This function creates the renderer data structures needed to repaint - * the output. The repaint results will be directed to the given native - * window. - * - * If EGL_EXT_platform_base is supported then \c window_for_platform is - * used, otherwise \c window_for_legacy is used. This is because the - * handle on X11 platform is different between the two. - * - * The first format in drm_formats that matches any EGLConfig - * determines which EGLConfig is chosen. See \c display_create about - * how the matching works and the possible limitations. - * - * This function should be used only if \c display_create was called - * with \c EGL_WINDOW_BIT in \c egl_surface_type. - */ - int (*output_window_create)(struct weston_output *output, - EGLNativeWindowType window_for_legacy, - void *window_for_platform, - const uint32_t *drm_formats, - unsigned drm_formats_count); - - /** - * Attach GL-renderer to the output with internal pixel storage - * - * \param output The output to create a rendering surface for. - * \param width Width of the rendering surface in pixels. - * \param height Height of the rendering surface in pixels. - * \param drm_formats Array of DRM pixel formats that are acceptable. - * \param drm_formats_count The drm_formats array length. - * \return 0 on success, -1 on failure. - * - * This function creates the renderer data structures needed to repaint - * the output. The repaint results will be kept internal and can only - * be accessed through e.g. screen capture. - * - * The first format in drm_formats that matches any EGLConfig - * determines which EGLConfig is chosen. See \c display_create about - * how the matching works and the possible limitations. - * - * This function should be used only if \c display_create was called - * with \c EGL_PBUFFER_BIT in \c egl_surface_type. - */ - int (*output_pbuffer_create)(struct weston_output *output, - int width, - int height, - const uint32_t *drm_formats, - unsigned drm_formats_count); - - void (*output_destroy)(struct weston_output *output); - - /* Sets the output border. - * - * The side specifies the side for which we are setting the border. - * The width and height are the width and height of the border. - * The tex_width patemeter specifies the width of the actual - * texture; this may be larger than width if the data is not - * tightly packed. - * - * The top and bottom textures will extend over the sides to the - * full width of the bordered window. The right and left edges, - * however, will extend only to the top and bottom of the - * compositor surface. This is demonstrated by the picture below: - * - * +-----------------------+ - * | TOP | - * +-+-------------------+-+ - * | | | | - * |L| |R| - * |E| |I| - * |F| |G| - * |T| |H| - * | | |T| - * | | | | - * +-+-------------------+-+ - * | BOTTOM | - * +-----------------------+ - */ - void (*output_set_border)(struct weston_output *output, - enum gl_renderer_border_side side, - int32_t width, int32_t height, - int32_t tex_width, unsigned char *data); - - /* Create fence sync FD to wait for GPU rendering. - * - * Return FD on success, -1 on failure or unsupported - * EGL_ANDROID_native_fence_sync extension. - */ - int (*create_fence_fd)(struct weston_output *output); -}; diff --git a/libweston/renderer-gl/meson.build b/libweston/renderer-gl/meson.build deleted file mode 100644 index 2d0d3938..00000000 --- a/libweston/renderer-gl/meson.build +++ /dev/null @@ -1,51 +0,0 @@ -if not get_option('renderer-gl') - subdir_done() -endif - -config_h.set('ENABLE_EGL', '1') - -srcs_renderer_gl = [ - 'egl-glue.c', - 'gl-renderer.c', - linux_dmabuf_unstable_v1_protocol_c, - linux_dmabuf_unstable_v1_server_protocol_h, -] - -deps_renderer_gl = [ - dep_libm, - dep_pixman, - dep_libweston_private, - dep_libdrm_headers, - dep_vertex_clipping -] - -foreach name : [ 'egl', 'glesv2' ] - d = dependency(name, required: false) - if not d.found() - error('gl-renderer requires @0@ which was not found. Or, you can use \'-Drenderer-gl=false\'.'.format(name)) - endif - deps_renderer_gl += d -endforeach - -if get_option('backend-tdm') - foreach name : [ 'libtbm', 'wayland-tbm-server' ] - d = dependency(name, required: false) - if not d.found() - error('backend-tdm of gl-renderer requires @0@ which was not found. Or, you can use \'-Dbackend-tdm=false\'.'.format(name)) - endif - deps_renderer_gl += d - endforeach - - config_h.set('HAVE_TBM', '1') -endif - -plugin_gl = shared_library( - 'gl-renderer', - srcs_renderer_gl, - include_directories: common_inc, - dependencies: deps_renderer_gl, - name_prefix: '', - install: true, - install_dir: dir_module_libweston -) -env_modmap += 'gl-renderer.so=@0@;'.format(plugin_gl.full_path()) diff --git a/libweston/screenshooter.c b/libweston/screenshooter.c deleted file mode 100644 index 4ea519bd..00000000 --- a/libweston/screenshooter.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright © 2008-2011 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 <stdlib.h> -#include <stdint.h> -#include <stdio.h> -#include <string.h> -#include <linux/input.h> -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> -#include <sys/uio.h> - -#include <libweston/libweston.h> -#include "shared/helpers.h" -#include "shared/timespec-util.h" -#include "backend.h" -#include "libweston-internal.h" - -#include "wcap/wcap-decode.h" - -struct screenshooter_frame_listener { - struct wl_listener listener; - struct weston_buffer *buffer; - struct weston_output *output; - weston_screenshooter_done_func_t done; - void *data; -}; - -static void -copy_bgra_yflip(uint8_t *dst, uint8_t *src, int height, int stride) -{ - uint8_t *end; - - end = dst + height * stride; - while (dst < end) { - memcpy(dst, src, stride); - dst += stride; - src -= stride; - } -} - -static void -copy_bgra(uint8_t *dst, uint8_t *src, int height, int stride) -{ - /* TODO: optimize this out */ - memcpy(dst, src, height * stride); -} - -static void -copy_row_swap_RB(void *vdst, void *vsrc, int bytes) -{ - uint32_t *dst = vdst; - uint32_t *src = vsrc; - uint32_t *end = dst + bytes / 4; - - while (dst < end) { - uint32_t v = *src++; - /* A R G B */ - uint32_t tmp = v & 0xff00ff00; - tmp |= (v >> 16) & 0x000000ff; - tmp |= (v << 16) & 0x00ff0000; - *dst++ = tmp; - } -} - -static void -copy_rgba_yflip(uint8_t *dst, uint8_t *src, int height, int stride) -{ - uint8_t *end; - - end = dst + height * stride; - while (dst < end) { - copy_row_swap_RB(dst, src, stride); - dst += stride; - src -= stride; - } -} - -static void -copy_rgba(uint8_t *dst, uint8_t *src, int height, int stride) -{ - uint8_t *end; - - end = dst + height * stride; - while (dst < end) { - copy_row_swap_RB(dst, src, stride); - dst += stride; - src += stride; - } -} - -static void -screenshooter_frame_notify(struct wl_listener *listener, void *data) -{ - struct screenshooter_frame_listener *l = - container_of(listener, - struct screenshooter_frame_listener, listener); - struct weston_output *output = l->output; - struct weston_compositor *compositor = output->compositor; - int32_t stride; - uint8_t *pixels, *d, *s; - - weston_output_disable_planes_decr(output); - wl_list_remove(&listener->link); - stride = l->buffer->width * (PIXMAN_FORMAT_BPP(compositor->read_format) / 8); - pixels = malloc(stride * l->buffer->height); - - if (pixels == NULL) { - l->done(l->data, WESTON_SCREENSHOOTER_NO_MEMORY); - free(l); - return; - } - - compositor->renderer->read_pixels(output, - compositor->read_format, pixels, - 0, 0, output->current_mode->width, - output->current_mode->height); - - stride = wl_shm_buffer_get_stride(l->buffer->shm_buffer); - - d = wl_shm_buffer_get_data(l->buffer->shm_buffer); - s = pixels + stride * (l->buffer->height - 1); - - wl_shm_buffer_begin_access(l->buffer->shm_buffer); - - switch (compositor->read_format) { - case PIXMAN_a8r8g8b8: - case PIXMAN_x8r8g8b8: - if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP) - copy_bgra_yflip(d, s, output->current_mode->height, stride); - else - copy_bgra(d, pixels, output->current_mode->height, stride); - break; - case PIXMAN_x8b8g8r8: - case PIXMAN_a8b8g8r8: - if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP) - copy_rgba_yflip(d, s, output->current_mode->height, stride); - else - copy_rgba(d, pixels, output->current_mode->height, stride); - break; - default: - break; - } - - wl_shm_buffer_end_access(l->buffer->shm_buffer); - - l->done(l->data, WESTON_SCREENSHOOTER_SUCCESS); - free(pixels); - free(l); -} - -WL_EXPORT int -weston_screenshooter_shoot(struct weston_output *output, - struct weston_buffer *buffer, - weston_screenshooter_done_func_t done, void *data) -{ - struct screenshooter_frame_listener *l; - - if (!wl_shm_buffer_get(buffer->resource)) { - done(data, WESTON_SCREENSHOOTER_BAD_BUFFER); - return -1; - } - - buffer->shm_buffer = wl_shm_buffer_get(buffer->resource); - buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer); - buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer); - - if (buffer->width < output->current_mode->width || - buffer->height < output->current_mode->height) { - done(data, WESTON_SCREENSHOOTER_BAD_BUFFER); - return -1; - } - - l = malloc(sizeof *l); - if (l == NULL) { - done(data, WESTON_SCREENSHOOTER_NO_MEMORY); - return -1; - } - - l->buffer = buffer; - l->output = output; - l->done = done; - l->data = data; - l->listener.notify = screenshooter_frame_notify; - wl_signal_add(&output->frame_signal, &l->listener); - weston_output_disable_planes_incr(output); - weston_output_damage(output); - - return 0; -} - -struct weston_recorder { - struct weston_output *output; - uint32_t *frame, *rect; - uint32_t *tmpbuf; - uint32_t total; - int fd; - struct wl_listener frame_listener; - int count, destroying; -}; - -static uint32_t * -output_run(uint32_t *p, uint32_t delta, int run) -{ - int i; - - while (run > 0) { - if (run <= 0xe0) { - *p++ = delta | ((run - 1) << 24); - break; - } - - i = 24 - __builtin_clz(run); - *p++ = delta | ((i + 0xe0) << 24); - run -= 1 << (7 + i); - } - - return p; -} - -static uint32_t -component_delta(uint32_t next, uint32_t prev) -{ - unsigned char dr, dg, db; - - dr = (next >> 16) - (prev >> 16); - dg = (next >> 8) - (prev >> 8); - db = (next >> 0) - (prev >> 0); - - return (dr << 16) | (dg << 8) | (db << 0); -} - -static void -weston_recorder_destroy(struct weston_recorder *recorder); - -static void -weston_recorder_frame_notify(struct wl_listener *listener, void *data) -{ - struct weston_recorder *recorder = - container_of(listener, struct weston_recorder, frame_listener); - struct weston_output *output = recorder->output; - struct weston_compositor *compositor = output->compositor; - uint32_t msecs = timespec_to_msec(&output->frame_time); - pixman_box32_t *r; - pixman_region32_t damage, transformed_damage; - int i, j, k, n, width, height, run, stride; - uint32_t delta, prev, *d, *s, *p, next; - struct { - uint32_t msecs; - uint32_t nrects; - } header; - struct iovec v[2]; - int do_yflip; - int y_orig; - uint32_t *outbuf; - - do_yflip = !!(compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP); - if (do_yflip) - outbuf = recorder->rect; - else - outbuf = recorder->tmpbuf; - - pixman_region32_init(&damage); - pixman_region32_init(&transformed_damage); - pixman_region32_intersect(&damage, &output->region, data); - pixman_region32_translate(&damage, -output->x, -output->y); - weston_transformed_region(output->width, output->height, - output->transform, output->current_scale, - &damage, &transformed_damage); - pixman_region32_fini(&damage); - - r = pixman_region32_rectangles(&transformed_damage, &n); - if (n == 0) { - pixman_region32_fini(&transformed_damage); - return; - } - - header.msecs = msecs; - header.nrects = n; - v[0].iov_base = &header; - v[0].iov_len = sizeof header; - v[1].iov_base = r; - v[1].iov_len = n * sizeof *r; - recorder->total += writev(recorder->fd, v, 2); - stride = output->current_mode->width; - - for (i = 0; i < n; i++) { - width = r[i].x2 - r[i].x1; - height = r[i].y2 - r[i].y1; - - if (do_yflip) - y_orig = output->current_mode->height - r[i].y2; - else - y_orig = r[i].y1; - - compositor->renderer->read_pixels(output, - compositor->read_format, recorder->rect, - r[i].x1, y_orig, width, height); - - p = outbuf; - run = prev = 0; /* quiet gcc */ - for (j = 0; j < height; j++) { - if (do_yflip) - s = recorder->rect + width * j; - else - s = recorder->rect + width * (height - j - 1); - y_orig = r[i].y2 - j - 1; - d = recorder->frame + stride * y_orig + r[i].x1; - - for (k = 0; k < width; k++) { - next = *s++; - delta = component_delta(next, *d); - *d++ = next; - if (run == 0 || delta == prev) { - run++; - } else { - p = output_run(p, prev, run); - run = 1; - } - prev = delta; - } - } - - p = output_run(p, prev, run); - - recorder->total += write(recorder->fd, - outbuf, (p - outbuf) * 4); - -#if 0 - fprintf(stderr, - "%dx%d at %d,%d rle from %d to %d bytes (%f) total %dM\n", - width, height, r[i].x1, r[i].y1, - width * height * 4, (int) (p - outbuf) * 4, - (float) (p - outbuf) / (width * height), - recorder->total / 1024 / 1024); -#endif - } - - pixman_region32_fini(&transformed_damage); - recorder->count++; - - if (recorder->destroying) - weston_recorder_destroy(recorder); -} - -static void -weston_recorder_free(struct weston_recorder *recorder) -{ - if (recorder == NULL) - return; - - free(recorder->tmpbuf); - free(recorder->rect); - free(recorder->frame); - free(recorder); -} - -static struct weston_recorder * -weston_recorder_create(struct weston_output *output, const char *filename) -{ - struct weston_compositor *compositor = output->compositor; - struct weston_recorder *recorder; - int stride, size; - struct { uint32_t magic, format, width, height; } header; - int do_yflip; - - do_yflip = !!(compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP); - - recorder = zalloc(sizeof *recorder); - if (recorder == NULL) { - weston_log("%s: out of memory\n", __func__); - return NULL; - } - - stride = output->current_mode->width; - size = stride * 4 * output->current_mode->height; - recorder->frame = zalloc(size); - recorder->rect = malloc(size); - recorder->output = output; - - if ((recorder->frame == NULL) || (recorder->rect == NULL)) { - weston_log("%s: out of memory\n", __func__); - goto err_recorder; - } - - if (!do_yflip) { - recorder->tmpbuf = malloc(size); - if (recorder->tmpbuf == NULL) { - weston_log("%s: out of memory\n", __func__); - goto err_recorder; - } - } - - header.magic = WCAP_HEADER_MAGIC; - - switch (compositor->read_format) { - case PIXMAN_x8r8g8b8: - case PIXMAN_a8r8g8b8: - header.format = WCAP_FORMAT_XRGB8888; - break; - case PIXMAN_a8b8g8r8: - header.format = WCAP_FORMAT_XBGR8888; - break; - default: - weston_log("unknown recorder format\n"); - goto err_recorder; - } - - recorder->fd = open(filename, - O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); - - if (recorder->fd < 0) { - weston_log("problem opening output file %s: %s\n", filename, - strerror(errno)); - goto err_recorder; - } - - header.width = output->current_mode->width; - header.height = output->current_mode->height; - recorder->total += write(recorder->fd, &header, sizeof header); - - recorder->frame_listener.notify = weston_recorder_frame_notify; - wl_signal_add(&output->frame_signal, &recorder->frame_listener); - weston_output_disable_planes_incr(output); - weston_output_damage(output); - - return recorder; - -err_recorder: - weston_recorder_free(recorder); - return NULL; -} - -static void -weston_recorder_destroy(struct weston_recorder *recorder) -{ - wl_list_remove(&recorder->frame_listener.link); - close(recorder->fd); - weston_output_disable_planes_decr(recorder->output); - weston_recorder_free(recorder); -} - -WL_EXPORT struct weston_recorder * -weston_recorder_start(struct weston_output *output, const char *filename) -{ - struct wl_listener *listener; - - listener = wl_signal_get(&output->frame_signal, - weston_recorder_frame_notify); - if (listener) { - weston_log("a recorder on output %s is already running\n", - output->name); - return NULL; - } - - weston_log("starting recorder for output %s, file %s\n", - output->name, filename); - return weston_recorder_create(output, filename); -} - -WL_EXPORT void -weston_recorder_stop(struct weston_recorder *recorder) -{ - weston_log("stopping recorder, total file size %dM, %d frames\n", - recorder->total / (1024 * 1024), recorder->count); - - recorder->destroying = 1; - weston_output_schedule_repaint(recorder->output); -} diff --git a/libweston/spring-tool.c b/libweston/spring-tool.c deleted file mode 100644 index b032d737..00000000 --- a/libweston/spring-tool.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright © 2011 Intel Corporation - * - * 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 <stdint.h> -#include <inttypes.h> - -#include "config.h" - -#include <libweston/libweston.h> -#include "shared/timespec-util.h" - -WL_EXPORT void -weston_view_geometry_dirty(struct weston_view *view) -{ -} - -WL_EXPORT int -weston_log(const char *fmt, ...) -{ - return 0; -} - -WL_EXPORT void -weston_view_schedule_repaint(struct weston_view *view) -{ -} - -WL_EXPORT void -weston_compositor_schedule_repaint(struct weston_compositor *compositor) -{ -} - -int -main(int argc, char *argv[]) -{ - const double k = 300.0; - const double current = 0.5; - const double target = 1.0; - const double friction = 1400; - - struct weston_spring spring; - struct timespec time = { 0 }; - - weston_spring_init(&spring, k, current, target); - spring.friction = friction; - spring.previous = 0.48; - spring.timestamp = (struct timespec) { 0 }; - - while (!weston_spring_done(&spring)) { - printf("\t%" PRId64 "\t%f\n", - timespec_to_msec(&time), spring.current); - weston_spring_update(&spring, &time); - timespec_add_msec(&time, &time, 16); - } - - return 0; -} diff --git a/libweston/timeline.c b/libweston/timeline.c deleted file mode 100644 index 9da8b5e3..00000000 --- a/libweston/timeline.c +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright © 2014 Pekka Paalanen <pq@iki.fi> - * Copyright © 2014, 2019 Collabora, Ltd. - * - * 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 <stdio.h> -#include <errno.h> -#include <string.h> -#include <time.h> -#include <assert.h> - -#include <libweston/libweston.h> -#include <libweston/weston-log.h> -#include "timeline.h" -#include "weston-log-internal.h" - -/** - * Timeline itself is not a subscriber but a scope (a producer of data), and it - * re-routes the data it produces to all the subscriptions (and implicitly - * to the subscribers) using a subscription iteration to go through all of them. - * - * Public API: - * * weston_timeline_refresh_subscription_objects() - allows outside parts of - * libweston notify/signal timeline code about the fact that underlying object - * has suffered some modifications and needs to re-emit the object ID. - * * weston_log_timeline_point() - which will disseminate data to all - * subscriptions - * - * Do note that only weston_timeline_refresh_subscription_objects() - * is exported in libweston. - * - * Destruction of the objects assigned to each underlying objects happens in - * two places: one in the logging framework callback of the log scope - * ('destroy_subscription'), and secondly, when the object itself gets - * destroyed. - * - * timeline_emit_context - For each subscription this object will be created to - * store a buffer when the object itself will be written and a subscription, - * which will be used to force the object ID if there is a need to do so (the - * underlying object has been refreshed, or better said has suffered some - * modification). Data written to a subscription will be flushed before the - * data written to the FILE *. - * - * @param cur a FILE * - * @param subscription a pointer to an already created subscription - * - * @ingroup internal-log - * @sa weston_timeline_point - */ -struct timeline_emit_context { - FILE *cur; - struct weston_log_subscription *subscription; -}; - -/** Create a timeline subscription and hang it off the subscription - * - * Called when the subscription is created. - * - * @ingroup internal-log - */ -void -weston_timeline_create_subscription(struct weston_log_subscription *sub, - void *user_data) -{ - struct weston_timeline_subscription *tl_sub = zalloc(sizeof(*tl_sub)); - if (!tl_sub) - return; - - wl_list_init(&tl_sub->objects); - - /* attach this timeline_subscription to it */ - weston_log_subscription_set_data(sub, tl_sub); -} - -static void -weston_timeline_destroy_subscription_object(struct weston_timeline_subscription_object *sub_obj) -{ - /* remove the notify listener */ - wl_list_remove(&sub_obj->destroy_listener.link); - sub_obj->destroy_listener.notify = NULL; - - wl_list_remove(&sub_obj->subscription_link); - free(sub_obj); -} - -/** Destroy the timeline subscription and all timeline subscription objects - * associated with it. - * - * Called when (before) the subscription is destroyed. - * - * @ingroup internal-log - */ -void -weston_timeline_destroy_subscription(struct weston_log_subscription *sub, - void *user_data) -{ - struct weston_timeline_subscription *tl_sub = - weston_log_subscription_get_data(sub); - struct weston_timeline_subscription_object *sub_obj, *tmp_sub_obj; - - if (!tl_sub) - return; - - wl_list_for_each_safe(sub_obj, tmp_sub_obj, - &tl_sub->objects, subscription_link) - weston_timeline_destroy_subscription_object(sub_obj); - - free(tl_sub); -} - -static bool -weston_timeline_check_object_refresh(struct weston_timeline_subscription_object *obj) -{ - if (obj->force_refresh == true) { - obj->force_refresh = false; - return true; - } - return false; -} - -static struct weston_timeline_subscription_object * -weston_timeline_subscription_search(struct weston_timeline_subscription *tl_sub, - void *object) -{ - struct weston_timeline_subscription_object *sub_obj; - - wl_list_for_each(sub_obj, &tl_sub->objects, subscription_link) - if (sub_obj->object == object) - return sub_obj; - - return NULL; -} - -static struct weston_timeline_subscription_object * -weston_timeline_subscription_object_create(void *object, - struct weston_timeline_subscription *tm_sub) -{ - struct weston_timeline_subscription_object *sub_obj; - - sub_obj = zalloc(sizeof(*sub_obj)); - sub_obj->id = ++tm_sub->next_id; - sub_obj->object = object; - - /* when the object is created so that it has the chance to display the - * object ID, we set the refresh status; it will only be re-freshed by - * the backend (or part parts) when the underlying objects has suffered - * modifications */ - sub_obj->force_refresh = true; - - wl_list_insert(&tm_sub->objects, &sub_obj->subscription_link); - - return sub_obj; -} - -static void -weston_timeline_destroy_subscription_object_notify(struct wl_listener *listener, void *data) -{ - struct weston_timeline_subscription_object *sub_obj; - - sub_obj = wl_container_of(listener, sub_obj, destroy_listener); - weston_timeline_destroy_subscription_object(sub_obj); -} - -static struct weston_timeline_subscription_object * -weston_timeline_subscription_output_ensure(struct weston_timeline_subscription *tl_sub, - struct weston_output *output) -{ - struct weston_timeline_subscription_object *sub_obj; - - sub_obj = weston_timeline_subscription_search(tl_sub, output); - if (!sub_obj) { - sub_obj = weston_timeline_subscription_object_create(output, tl_sub); - - sub_obj->destroy_listener.notify = - weston_timeline_destroy_subscription_object_notify; - wl_signal_add(&output->destroy_signal, - &sub_obj->destroy_listener); - } - return sub_obj; -} - -static struct weston_timeline_subscription_object * -weston_timeline_subscription_surface_ensure(struct weston_timeline_subscription *tl_sub, - struct weston_surface *surface) -{ - struct weston_timeline_subscription_object *sub_obj; - - sub_obj = weston_timeline_subscription_search(tl_sub, surface); - if (!sub_obj) { - sub_obj = weston_timeline_subscription_object_create(surface, tl_sub); - - sub_obj->destroy_listener.notify = - weston_timeline_destroy_subscription_object_notify; - wl_signal_add(&surface->destroy_signal, - &sub_obj->destroy_listener); - } - - return sub_obj; -} - -static void -fprint_quoted_string(struct weston_log_subscription *sub, const char *str) -{ - if (!str) { - weston_log_subscription_printf(sub, "null"); - return; - } - - weston_log_subscription_printf(sub, "\"%s\"", str); -} - -static void -emit_weston_output_print_id(struct weston_log_subscription *sub, - struct weston_timeline_subscription_object *sub_obj, - const char *name) -{ - if (!weston_timeline_check_object_refresh(sub_obj)) - return; - - weston_log_subscription_printf(sub, "{ \"id\":%u, " - "\"type\":\"weston_output\", \"name\":", sub_obj->id); - fprint_quoted_string(sub, name); - weston_log_subscription_printf(sub, " }\n"); -} - -static int -emit_weston_output(struct timeline_emit_context *ctx, void *obj) -{ - struct weston_log_subscription *sub = ctx->subscription; - struct weston_output *output = obj; - struct weston_timeline_subscription_object *sub_obj; - struct weston_timeline_subscription *tl_sub; - - tl_sub = weston_log_subscription_get_data(sub); - sub_obj = weston_timeline_subscription_output_ensure(tl_sub, output); - emit_weston_output_print_id(sub, sub_obj, output->name); - - assert(sub_obj->id != 0); - fprintf(ctx->cur, "\"wo\":%u", sub_obj->id); - - return 1; -} - - -static void -check_weston_surface_description(struct weston_log_subscription *sub, - struct weston_surface *s, - struct weston_timeline_subscription *tm_sub, - struct weston_timeline_subscription_object *sub_obj) -{ - struct weston_surface *mains; - char d[512]; - char mainstr[32]; - - if (!weston_timeline_check_object_refresh(sub_obj)) - return; - - mains = weston_surface_get_main_surface(s); - if (mains != s) { - struct weston_timeline_subscription_object *new_sub_obj; - - new_sub_obj = weston_timeline_subscription_surface_ensure(tm_sub, mains); - check_weston_surface_description(sub, mains, tm_sub, new_sub_obj); - if (snprintf(mainstr, sizeof(mainstr), ", \"main_surface\":%u", - new_sub_obj->id) < 0) - mainstr[0] = '\0'; - } else { - mainstr[0] = '\0'; - } - - if (!s->get_label || s->get_label(s, d, sizeof(d)) < 0) - d[0] = '\0'; - - weston_log_subscription_printf(sub, "{ \"id\":%u, " - "\"type\":\"weston_surface\", \"desc\":", - sub_obj->id); - fprint_quoted_string(sub, d[0] ? d : NULL); - weston_log_subscription_printf(sub, "%s }\n", mainstr); -} - -static int -emit_weston_surface(struct timeline_emit_context *ctx, void *obj) -{ - struct weston_log_subscription *sub = ctx->subscription; - struct weston_surface *surface = obj; - struct weston_timeline_subscription_object *sub_obj; - struct weston_timeline_subscription *tl_sub; - - tl_sub = weston_log_subscription_get_data(sub); - sub_obj = weston_timeline_subscription_surface_ensure(tl_sub, surface); - check_weston_surface_description(sub, surface, tl_sub, sub_obj); - - assert(sub_obj->id != 0); - fprintf(ctx->cur, "\"ws\":%u", sub_obj->id); - - return 1; -} - -static int -emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj) -{ - struct timespec *ts = obj; - - fprintf(ctx->cur, "\"vblank\":[%" PRId64 ", %ld]", - (int64_t)ts->tv_sec, ts->tv_nsec); - - return 1; -} - -static int -emit_gpu_timestamp(struct timeline_emit_context *ctx, void *obj) -{ - struct timespec *ts = obj; - - fprintf(ctx->cur, "\"gpu\":[%" PRId64 ", %ld]", - (int64_t)ts->tv_sec, ts->tv_nsec); - - return 1; -} - -static struct weston_timeline_subscription_object * -weston_timeline_get_subscription_object(struct weston_log_subscription *sub, - void *object) -{ - struct weston_timeline_subscription *tl_sub; - - tl_sub = weston_log_subscription_get_data(sub); - if (!tl_sub) - return NULL; - - return weston_timeline_subscription_search(tl_sub, object); -} - -/** Sets (on) the timeline subscription object refresh status. - * - * This function 'notifies' timeline to print the object ID. The timeline code - * will reset it back, so there's no need for users to do anything about it. - * - * Can be used from outside libweston. - * - * @param wc a weston_compositor instance - * @param object the underyling object - * - * @ingroup log - */ -WL_EXPORT void -weston_timeline_refresh_subscription_objects(struct weston_compositor *wc, - void *object) -{ - struct weston_log_subscription *sub = NULL; - - while ((sub = weston_log_subscription_iterate(wc->timeline, sub))) { - struct weston_timeline_subscription_object *sub_obj; - - sub_obj = weston_timeline_get_subscription_object(sub, object); - if (sub_obj) - sub_obj->force_refresh = true; - } -} - -typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj); - -static const type_func type_dispatch[] = { - [TLT_OUTPUT] = emit_weston_output, - [TLT_SURFACE] = emit_weston_surface, - [TLT_VBLANK] = emit_vblank_timestamp, - [TLT_GPU] = emit_gpu_timestamp, -}; - -/** Disseminates the message to all subscriptions of the scope \c - * timeline_scope - * - * The TL_POINT() is a wrapper over this function, but it uses the weston_compositor - * instance to pass the timeline scope. - * - * @param timeline_scope the timeline scope - * @param name the name of the timeline point. Interpretable by the tool reading - * the output (wesgr). - * - * @ingroup log - */ -WL_EXPORT void -weston_timeline_point(struct weston_log_scope *timeline_scope, - const char *name, ...) -{ - struct timespec ts; - enum timeline_type otype; - void *obj; - char buf[512]; - struct weston_log_subscription *sub = NULL; - - if (!weston_log_scope_is_enabled(timeline_scope)) - return; - - clock_gettime(CLOCK_MONOTONIC, &ts); - - while ((sub = weston_log_subscription_iterate(timeline_scope, sub))) { - va_list argp; - struct timeline_emit_context ctx = {}; - - memset(buf, 0, sizeof(buf)); - ctx.cur = fmemopen(buf, sizeof(buf), "w"); - ctx.subscription = sub; - - if (!ctx.cur) { - weston_log("Timeline error in fmemopen, closing.\n"); - return; - } - - fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"", - (int64_t)ts.tv_sec, ts.tv_nsec, name); - - va_start(argp, name); - while (1) { - otype = va_arg(argp, enum timeline_type); - if (otype == TLT_END) - break; - - obj = va_arg(argp, void *); - if (type_dispatch[otype]) { - fprintf(ctx.cur, ", "); - type_dispatch[otype](&ctx, obj); - } - } - va_end(argp); - - fprintf(ctx.cur, " }\n"); - fflush(ctx.cur); - if (ferror(ctx.cur)) { - weston_log("Timeline error in constructing entry, closing.\n"); - } else { - weston_log_subscription_printf(ctx.subscription, "%s", buf); - } - - fclose(ctx.cur); - - } -} diff --git a/libweston/timeline.h b/libweston/timeline.h deleted file mode 100644 index aaed7431..00000000 --- a/libweston/timeline.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright © 2014 Pekka Paalanen <pq@iki.fi> - * Copyright © 2014, 2019 Collabora, Ltd. - * - * 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. - */ - -#ifndef WESTON_TIMELINE_H -#define WESTON_TIMELINE_H - -#include <wayland-util.h> -#include <stdbool.h> - -#include <libweston/weston-log.h> -#include <wayland-server-core.h> - -enum timeline_type { - TLT_END = 0, - TLT_OUTPUT, - TLT_SURFACE, - TLT_VBLANK, - TLT_GPU, -}; - -/** Timeline subscription created for each subscription - * - * Created automatically by weston_log_scope::new_subscription and - * destroyed by weston_log_scope::destroy_subscription. - * - * @ingroup internal-log - */ -struct weston_timeline_subscription { - unsigned int next_id; - struct wl_list objects; /**< weston_timeline_subscription_object::subscription_link */ -}; - -/** - * Created when object is first seen for a particular timeline subscription - * Destroyed when the subscription got destroyed or object was destroyed - * - * @ingroup internal-log - */ -struct weston_timeline_subscription_object { - void *object; /**< points to the object */ - unsigned int id; - bool force_refresh; - struct wl_list subscription_link; /**< weston_timeline_subscription::objects */ - struct wl_listener destroy_listener; -}; - -#define TYPEVERIFY(type, arg) ({ \ - typeof(arg) tmp___ = (arg); \ - (void)((type)0 == tmp___); \ - tmp___; }) - -/** - * Should be used as the last argument when using TL_POINT macro - * - * @ingroup log - */ -#define TLP_END TLT_END, NULL - -#define TLP_OUTPUT(o) TLT_OUTPUT, TYPEVERIFY(struct weston_output *, (o)) -#define TLP_SURFACE(s) TLT_SURFACE, TYPEVERIFY(struct weston_surface *, (s)) -#define TLP_VBLANK(t) TLT_VBLANK, TYPEVERIFY(const struct timespec *, (t)) -#define TLP_GPU(t) TLT_GPU, TYPEVERIFY(const struct timespec *, (t)) - -/** This macro is used to add timeline points. - * - * Use TLP_END when done for the vargs. - * - * @param ec weston_compositor instance - * - * @ingroup log - */ -#define TL_POINT(ec, ...) do { \ - weston_timeline_point(ec->timeline, __VA_ARGS__); \ -} while (0) - -void -weston_timeline_point(struct weston_log_scope *timeline_scope, - const char *name, ...); - -#endif /* WESTON_TIMELINE_H */ diff --git a/libweston/touch-calibration.c b/libweston/touch-calibration.c deleted file mode 100644 index 187c8987..00000000 --- a/libweston/touch-calibration.c +++ /dev/null @@ -1,717 +0,0 @@ -/* - * Copyright 2017-2018 Collabora, Ltd. - * Copyright 2017-2018 General Electric Company - * - * 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 <string.h> -#include <wayland-server.h> - -#include "shared/helpers.h" -#include "shared/string-helpers.h" -#include <libweston/zalloc.h> -#include "shared/timespec-util.h" -#include <libweston/libweston.h> -#include "libweston-internal.h" - -#include "weston-touch-calibration-server-protocol.h" - -struct weston_touch_calibrator { - struct wl_resource *resource; - - struct weston_compositor *compositor; - - struct weston_surface *surface; - struct wl_listener surface_destroy_listener; - struct wl_listener surface_commit_listener; - - struct weston_touch_device *device; - struct wl_listener device_destroy_listener; - - struct weston_output *output; - struct wl_listener output_destroy_listener; - - struct weston_view *view; - - /** The calibration procedure has been cancelled. */ - bool calibration_cancelled; - - /** The current touch sequence has been cancelled. */ - bool touch_cancelled; -}; - -static struct weston_touch_calibrator * -calibrator_from_device(struct weston_touch_device *device) -{ - return device->aggregate->seat->compositor->touch_calibrator; -} - -static uint32_t -wire_uint_from_double(double c) -{ - assert(c >= 0.0); - assert(c <= 1.0); - - return round(c * 0xffffffff); -} - -static bool -normalized_is_valid(const struct weston_point2d_device_normalized *p) -{ - return p->x >= 0.0 && p->x <= 1.0 && - p->y >= 0.0 && p->y <= 1.0; -} - -WL_EXPORT void -notify_touch_calibrator(struct weston_touch_device *device, - const struct timespec *time, int32_t slot, - const struct weston_point2d_device_normalized *norm, - int touch_type) -{ - struct weston_touch_calibrator *calibrator; - struct wl_resource *res; - uint32_t msecs; - uint32_t x = 0; - uint32_t y = 0; - - calibrator = calibrator_from_device(device); - if (!calibrator) - return; - - res = calibrator->resource; - - /* Ignore any touch events coming from another device */ - if (device != calibrator->device) { - if (touch_type == WL_TOUCH_DOWN) - weston_touch_calibrator_send_invalid_touch(res); - return; - } - - /* Ignore all events if we have sent 'cancel' event until all - * touches (on the seat) are up. - */ - if (calibrator->touch_cancelled) { - if (calibrator->device->aggregate->num_tp == 0) { - assert(touch_type == WL_TOUCH_UP); - calibrator->touch_cancelled = false; - } - return; - } - - msecs = timespec_to_msec(time); - if (touch_type != WL_TOUCH_UP) { - if (normalized_is_valid(norm)) { - x = wire_uint_from_double(norm->x); - y = wire_uint_from_double(norm->y); - } else { - /* Coordinates are out of bounds */ - if (touch_type == WL_TOUCH_MOTION) { - weston_touch_calibrator_send_cancel(res); - calibrator->touch_cancelled = true; - } - weston_touch_calibrator_send_invalid_touch(res); - return; - } - } - - switch (touch_type) { - case WL_TOUCH_UP: - weston_touch_calibrator_send_up(res, msecs, slot); - break; - case WL_TOUCH_DOWN: - weston_touch_calibrator_send_down(res, msecs, slot, x, y); - break; - case WL_TOUCH_MOTION: - weston_touch_calibrator_send_motion(res, msecs, slot, x, y); - break; - default: - return; - } -} - -WL_EXPORT void -notify_touch_calibrator_frame(struct weston_touch_device *device) -{ - struct weston_touch_calibrator *calibrator; - - calibrator = calibrator_from_device(device); - if (!calibrator) - return; - - weston_touch_calibrator_send_frame(calibrator->resource); -} - -WL_EXPORT void -notify_touch_calibrator_cancel(struct weston_touch_device *device) -{ - struct weston_touch_calibrator *calibrator; - - calibrator = calibrator_from_device(device); - if (!calibrator) - return; - - weston_touch_calibrator_send_cancel(calibrator->resource); -} - -static void -map_calibrator(struct weston_touch_calibrator *calibrator) -{ - struct weston_compositor *c = calibrator->compositor; - struct weston_touch_device *device = calibrator->device; - static const struct weston_touch_device_matrix identity = { - .m = { 1, 0, 0, 0, 1, 0} - }; - - assert(!calibrator->view); - assert(calibrator->output); - assert(calibrator->surface); - assert(calibrator->surface->resource); - - calibrator->view = weston_view_create(calibrator->surface); - if (!calibrator->view) { - wl_resource_post_no_memory(calibrator->surface->resource); - return; - } - - weston_layer_entry_insert(&c->calibrator_layer.view_list, - &calibrator->view->layer_link); - - weston_view_set_position(calibrator->view, - calibrator->output->x, - calibrator->output->y); - calibrator->view->output = calibrator->surface->output; - calibrator->view->is_mapped = true; - - calibrator->surface->output = calibrator->output; - calibrator->surface->is_mapped = true; - - weston_output_schedule_repaint(calibrator->output); - - device->ops->get_calibration(device, &device->saved_calibration); - device->ops->set_calibration(device, &identity); -} - -static void -unmap_calibrator(struct weston_touch_calibrator *calibrator) -{ - struct weston_touch_device *device = calibrator->device; - - wl_list_remove(&calibrator->surface_commit_listener.link); - wl_list_init(&calibrator->surface_commit_listener.link); - - if (!calibrator->view) - return; - - weston_view_destroy(calibrator->view); - calibrator->view = NULL; - weston_surface_unmap(calibrator->surface); - - /* Reload saved calibration */ - if (device) - device->ops->set_calibration(device, - &device->saved_calibration); -} - -void -touch_calibrator_mode_changed(struct weston_compositor *compositor) -{ - struct weston_touch_calibrator *calibrator; - - calibrator = compositor->touch_calibrator; - if (!calibrator) - return; - - if (calibrator->calibration_cancelled) - return; - - if (compositor->touch_mode == WESTON_TOUCH_MODE_CALIB) - map_calibrator(calibrator); -} - -static void -touch_calibrator_surface_committed(struct wl_listener *listener, void *data) -{ - struct weston_touch_calibrator *calibrator = - container_of(listener, struct weston_touch_calibrator, - surface_commit_listener); - struct weston_surface *surface = calibrator->surface; - - wl_list_remove(&calibrator->surface_commit_listener.link); - wl_list_init(&calibrator->surface_commit_listener.link); - - if (surface->width != calibrator->output->width || - surface->height != calibrator->output->height) { - wl_resource_post_error(calibrator->resource, - WESTON_TOUCH_CALIBRATOR_ERROR_BAD_SIZE, - "calibrator surface size does not match"); - return; - } - - weston_compositor_set_touch_mode_calib(calibrator->compositor); - /* results in call to touch_calibrator_mode_changed() */ -} - -static void -touch_calibrator_convert(struct wl_client *client, - struct wl_resource *resource, - int32_t x, - int32_t y, - uint32_t coordinate_id) -{ - struct weston_touch_calibrator *calibrator; - struct wl_resource *coordinate_resource; - struct weston_output *output; - struct weston_surface *surface; - uint32_t version; - struct weston_vector p = { { 0.0, 0.0, 0.0, 1.0 } }; - struct weston_point2d_device_normalized norm; - - version = wl_resource_get_version(resource); - calibrator = wl_resource_get_user_data(resource); - surface = calibrator->surface; - output = calibrator->output; - - coordinate_resource = - wl_resource_create(client, &weston_touch_coordinate_interface, - version, coordinate_id); - if (!coordinate_resource) { - wl_client_post_no_memory(client); - return; - } - - if (calibrator->calibration_cancelled) { - weston_touch_coordinate_send_result(coordinate_resource, 0, 0); - wl_resource_destroy(coordinate_resource); - return; - } - - if (!surface || !surface->is_mapped) { - wl_resource_post_error(resource, - WESTON_TOUCH_CALIBRATOR_ERROR_NOT_MAPPED, - "calibrator surface is not mapped"); - return; - } - assert(calibrator->view); - assert(output); - - if (x < 0 || y < 0 || x >= surface->width || y >= surface->height) { - wl_resource_post_error(resource, - WESTON_TOUCH_CALIBRATOR_ERROR_BAD_COORDINATES, - "convert(%d, %d) input is out of bounds", - x, y); - return; - } - - /* Convert from surface-local coordinates into global, from global - * into output-raw, do perspective division and normalize. - */ - weston_view_to_global_float(calibrator->view, x, y, &p.f[0], &p.f[1]); - weston_matrix_transform(&output->matrix, &p); - norm.x = p.f[0] / (p.f[3] * output->current_mode->width); - norm.y = p.f[1] / (p.f[3] * output->current_mode->height); - - if (!normalized_is_valid(&norm)) { - wl_resource_post_error(resource, - WESTON_TOUCH_CALIBRATOR_ERROR_BAD_COORDINATES, - "convert(%d, %d) output is out of bounds", - x, y); - return; - } - - weston_touch_coordinate_send_result(coordinate_resource, - wire_uint_from_double(norm.x), - wire_uint_from_double(norm.y)); - wl_resource_destroy(coordinate_resource); -} - -static void -destroy_touch_calibrator(struct wl_resource *resource) -{ - struct weston_touch_calibrator *calibrator; - - calibrator = wl_resource_get_user_data(resource); - - calibrator->compositor->touch_calibrator = NULL; - - weston_compositor_set_touch_mode_normal(calibrator->compositor); - - if (calibrator->surface) { - unmap_calibrator(calibrator); - weston_surface_set_role(calibrator->surface, NULL, - calibrator->surface->resource, 0); - wl_list_remove(&calibrator->surface_destroy_listener.link); - wl_list_remove(&calibrator->surface_commit_listener.link); - } - - if (calibrator->device) - wl_list_remove(&calibrator->device_destroy_listener.link); - - if (calibrator->output) - wl_list_remove(&calibrator->output_destroy_listener.link); - - free(calibrator); -} - -static void -touch_calibrator_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct weston_touch_calibrator_interface -touch_calibrator_implementation = { - touch_calibrator_destroy, - touch_calibrator_convert -}; - -static void -touch_calibrator_cancel_calibration(struct weston_touch_calibrator *calibrator) -{ - weston_touch_calibrator_send_cancel_calibration(calibrator->resource); - calibrator->calibration_cancelled = true; - - if (calibrator->surface) - unmap_calibrator(calibrator); -} - -static void -touch_calibrator_output_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_touch_calibrator *calibrator = - container_of(listener, struct weston_touch_calibrator, - output_destroy_listener); - - assert(calibrator->output == data); - calibrator->output = NULL; - - touch_calibrator_cancel_calibration(calibrator); -} - -static void -touch_calibrator_device_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_touch_calibrator *calibrator = - container_of(listener, struct weston_touch_calibrator, - device_destroy_listener); - - assert(calibrator->device == data); - calibrator->device = NULL; - - touch_calibrator_cancel_calibration(calibrator); -} - -static void -touch_calibrator_surface_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_touch_calibrator *calibrator = - container_of(listener, struct weston_touch_calibrator, - surface_destroy_listener); - - assert(calibrator->surface->resource == data); - - unmap_calibrator(calibrator); - calibrator->surface = NULL; -} - -static void -touch_calibration_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static struct weston_touch_device * -weston_compositor_find_touch_device_by_syspath( - struct weston_compositor *compositor, - const char *syspath) -{ - struct weston_seat *seat; - struct weston_touch *touch; - struct weston_touch_device *device; - - if (!syspath) - return NULL; - - wl_list_for_each(seat, &compositor->seat_list, link) { - touch = weston_seat_get_touch(seat); - if (!touch) - continue; - - wl_list_for_each(device, &touch->device_list, link) { - if (strcmp(device->syspath, syspath) == 0) - return device; - } - } - - return NULL; -} - -static void -touch_calibration_create_calibrator( - struct wl_client *client, - struct wl_resource *touch_calibration_resource, - struct wl_resource *surface_resource, - const char *syspath, - uint32_t calibrator_id) -{ - struct weston_compositor *compositor; - struct weston_touch_calibrator *calibrator; - struct weston_touch_device *device; - struct weston_output *output = NULL; - struct weston_surface *surface; - uint32_t version; - int ret; - - version = wl_resource_get_version(touch_calibration_resource); - compositor = wl_resource_get_user_data(touch_calibration_resource); - - if (compositor->touch_calibrator != NULL) { - wl_resource_post_error(touch_calibration_resource, - WESTON_TOUCH_CALIBRATION_ERROR_ALREADY_EXISTS, - "a calibrator has already been created"); - return; - } - - calibrator = zalloc(sizeof *calibrator); - if (!calibrator) { - wl_client_post_no_memory(client); - return; - } - - calibrator->compositor = compositor; - calibrator->resource = wl_resource_create(client, - &weston_touch_calibrator_interface, - version, calibrator_id); - if (!calibrator->resource) { - wl_client_post_no_memory(client); - goto err_dealloc; - } - - surface = wl_resource_get_user_data(surface_resource); - assert(surface); - ret = weston_surface_set_role(surface, "weston_touch_calibrator", - touch_calibration_resource, - WESTON_TOUCH_CALIBRATION_ERROR_INVALID_SURFACE); - if (ret < 0) - goto err_destroy_resource; - - calibrator->surface_destroy_listener.notify = - touch_calibrator_surface_destroyed; - wl_resource_add_destroy_listener(surface->resource, - &calibrator->surface_destroy_listener); - calibrator->surface = surface; - - calibrator->surface_commit_listener.notify = - touch_calibrator_surface_committed; - wl_signal_add(&surface->commit_signal, - &calibrator->surface_commit_listener); - - device = weston_compositor_find_touch_device_by_syspath(compositor, - syspath); - if (device) { - output = device->ops->get_output(device); - if (weston_touch_device_can_calibrate(device) && output) - calibrator->device = device; - } - - if (!calibrator->device) { - wl_resource_post_error(touch_calibration_resource, - WESTON_TOUCH_CALIBRATION_ERROR_INVALID_DEVICE, - "the given touch device '%s' is not valid", - syspath ?: ""); - goto err_unlink_surface; - } - - calibrator->device_destroy_listener.notify = - touch_calibrator_device_destroyed; - wl_signal_add(&calibrator->device->destroy_signal, - &calibrator->device_destroy_listener); - - wl_resource_set_implementation(calibrator->resource, - &touch_calibrator_implementation, - calibrator, destroy_touch_calibrator); - - assert(output); - calibrator->output_destroy_listener.notify = - touch_calibrator_output_destroyed; - wl_signal_add(&output->destroy_signal, - &calibrator->output_destroy_listener); - calibrator->output = output; - - weston_touch_calibrator_send_configure(calibrator->resource, - output->width, - output->height); - - compositor->touch_calibrator = calibrator; - - return; - -err_unlink_surface: - wl_list_remove(&calibrator->surface_commit_listener.link); - wl_list_remove(&calibrator->surface_destroy_listener.link); - -err_destroy_resource: - wl_resource_destroy(calibrator->resource); - -err_dealloc: - free(calibrator); -} - -static void -touch_calibration_save(struct wl_client *client, - struct wl_resource *touch_calibration_resource, - const char *device_name, - struct wl_array *matrix_data) -{ - struct weston_touch_device *device; - struct weston_compositor *compositor; - struct weston_touch_device_matrix calibration; - struct weston_touch_calibrator *calibrator; - int i = 0; - float *c; - - compositor = wl_resource_get_user_data(touch_calibration_resource); - - device = weston_compositor_find_touch_device_by_syspath(compositor, - device_name); - if (!device || !weston_touch_device_can_calibrate(device)) { - wl_resource_post_error(touch_calibration_resource, - WESTON_TOUCH_CALIBRATION_ERROR_INVALID_DEVICE, - "the given device is not valid"); - return; - } - - wl_array_for_each(c, matrix_data) { - calibration.m[i++] = *c; - } - - /* If calibration can't be saved, don't set it as current */ - if (compositor->touch_calibration_save && - compositor->touch_calibration_save(compositor, device, - &calibration) < 0) - return; - - /* If calibrator is still mapped, the compositor will use - * saved_calibration when going back to normal touch handling. - * Continuing calibrating after save request is undefined. */ - calibrator = compositor->touch_calibrator; - if (calibrator && - calibrator->surface && - weston_surface_is_mapped(calibrator->surface)) - device->saved_calibration = calibration; - else - device->ops->set_calibration(device, &calibration); -} - -static const struct weston_touch_calibration_interface -touch_calibration_implementation = { - touch_calibration_destroy, - touch_calibration_create_calibrator, - touch_calibration_save -}; - -static void -bind_touch_calibration(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct weston_compositor *compositor = data; - struct wl_resource *resource; - struct weston_touch_device *device; - struct weston_seat *seat; - struct weston_touch *touch; - const char *name; - - resource = wl_resource_create(client, - &weston_touch_calibration_interface, - version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, - &touch_calibration_implementation, - compositor, NULL); - - wl_list_for_each(seat, &compositor->seat_list, link) { - touch = weston_seat_get_touch(seat); - if (!touch) - continue; - - wl_list_for_each(device, &touch->device_list, link) { - if (!weston_touch_device_can_calibrate(device)) - continue; - - name = device->ops->get_calibration_head_name(device); - if (!name) - continue; - - weston_touch_calibration_send_touch_device(resource, - device->syspath, name); - } - } -} - -/** Advertise touch_calibration support - * - * \param compositor The compositor to init for. - * \param save The callback function for saving a new calibration, or NULL. - * \return Zero on success, -1 on failure or if already enabled. - * - * Calling this initializes the weston_touch_calibration protocol support, - * so that the interface will be advertised to clients. It is recommended - * to use some mechanism, e.g. wl_display_set_global_filter(), to restrict - * access to the interface. - * - * There is no way to disable this once enabled. - * - * If the save callback is NULL, a new calibration provided by a client will - * always be accepted. If the save callback is not NULL, it must return - * success for the new calibration to be accepted. - */ -WL_EXPORT int -weston_compositor_enable_touch_calibrator(struct weston_compositor *compositor, - weston_touch_calibration_save_func save) -{ - if (compositor->touch_calibration) - return -1; - - compositor->touch_calibration = wl_global_create(compositor->wl_display, - &weston_touch_calibration_interface, 1, - compositor, bind_touch_calibration); - if (!compositor->touch_calibration) - return -1; - - compositor->touch_calibration_save = save; - weston_layer_init(&compositor->calibrator_layer, compositor); - - /* needs to be stacked above everything except lock screen and cursor, - * otherwise the position value is arbitrary */ - weston_layer_set_position(&compositor->calibrator_layer, - WESTON_LAYER_POSITION_TOP_UI + 120); - - return 0; -} diff --git a/libweston/vertex-clipping.c b/libweston/vertex-clipping.c deleted file mode 100644 index a71e7336..00000000 --- a/libweston/vertex-clipping.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright © 2012 Intel Corporation - * - * 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 <assert.h> -#include <float.h> -#include <math.h> - -#include "vertex-clipping.h" - -float -float_difference(float a, float b) -{ - /* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */ - static const float max_diff = 4.0f * FLT_MIN; - static const float max_rel_diff = 4.0e-5; - float diff = a - b; - float adiff = fabsf(diff); - - if (adiff <= max_diff) - return 0.0f; - - a = fabsf(a); - b = fabsf(b); - if (adiff <= (a > b ? a : b) * max_rel_diff) - return 0.0f; - - return diff; -} - -/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line x = x_arg. - * Compute the y coordinate of the intersection. - */ -static float -clip_intersect_y(float p1x, float p1y, float p2x, float p2y, - float x_arg) -{ - float a; - float diff = float_difference(p1x, p2x); - - /* Practically vertical line segment, yet the end points have already - * been determined to be on different sides of the line. Therefore - * the line segment is part of the line and intersects everywhere. - * Return the end point, so we use the whole line segment. - */ - if (diff == 0.0f) - return p2y; - - a = (x_arg - p2x) / diff; - return p2y + (p1y - p2y) * a; -} - -/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line y = y_arg. - * Compute the x coordinate of the intersection. - */ -static float -clip_intersect_x(float p1x, float p1y, float p2x, float p2y, - float y_arg) -{ - float a; - float diff = float_difference(p1y, p2y); - - /* Practically horizontal line segment, yet the end points have already - * been determined to be on different sides of the line. Therefore - * the line segment is part of the line and intersects everywhere. - * Return the end point, so we use the whole line segment. - */ - if (diff == 0.0f) - return p2x; - - a = (y_arg - p2y) / diff; - return p2x + (p1x - p2x) * a; -} - -enum path_transition { - PATH_TRANSITION_OUT_TO_OUT = 0, - PATH_TRANSITION_OUT_TO_IN = 1, - PATH_TRANSITION_IN_TO_OUT = 2, - PATH_TRANSITION_IN_TO_IN = 3, -}; - -static void -clip_append_vertex(struct clip_context *ctx, float x, float y) -{ - *ctx->vertices.x++ = x; - *ctx->vertices.y++ = y; -} - -static enum path_transition -path_transition_left_edge(struct clip_context *ctx, float x, float y) -{ - return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1); -} - -static enum path_transition -path_transition_right_edge(struct clip_context *ctx, float x, float y) -{ - return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2); -} - -static enum path_transition -path_transition_top_edge(struct clip_context *ctx, float x, float y) -{ - return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1); -} - -static enum path_transition -path_transition_bottom_edge(struct clip_context *ctx, float x, float y) -{ - return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2); -} - -static void -clip_polygon_leftright(struct clip_context *ctx, - enum path_transition transition, - float x, float y, float clip_x) -{ - float yi; - - switch (transition) { - case PATH_TRANSITION_IN_TO_IN: - clip_append_vertex(ctx, x, y); - break; - case PATH_TRANSITION_IN_TO_OUT: - yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x); - clip_append_vertex(ctx, clip_x, yi); - break; - case PATH_TRANSITION_OUT_TO_IN: - yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x); - clip_append_vertex(ctx, clip_x, yi); - clip_append_vertex(ctx, x, y); - break; - case PATH_TRANSITION_OUT_TO_OUT: - /* nothing */ - break; - default: - assert(0 && "bad enum path_transition"); - } - - ctx->prev.x = x; - ctx->prev.y = y; -} - -static void -clip_polygon_topbottom(struct clip_context *ctx, - enum path_transition transition, - float x, float y, float clip_y) -{ - float xi; - - switch (transition) { - case PATH_TRANSITION_IN_TO_IN: - clip_append_vertex(ctx, x, y); - break; - case PATH_TRANSITION_IN_TO_OUT: - xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y); - clip_append_vertex(ctx, xi, clip_y); - break; - case PATH_TRANSITION_OUT_TO_IN: - xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y); - clip_append_vertex(ctx, xi, clip_y); - clip_append_vertex(ctx, x, y); - break; - case PATH_TRANSITION_OUT_TO_OUT: - /* nothing */ - break; - default: - assert(0 && "bad enum path_transition"); - } - - ctx->prev.x = x; - ctx->prev.y = y; -} - -static void -clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src, - float *dst_x, float *dst_y) -{ - ctx->prev.x = src->x[src->n - 1]; - ctx->prev.y = src->y[src->n - 1]; - ctx->vertices.x = dst_x; - ctx->vertices.y = dst_y; -} - -static int -clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src, - float *dst_x, float *dst_y) -{ - enum path_transition trans; - int i; - - if (src->n < 2) - return 0; - - clip_context_prepare(ctx, src, dst_x, dst_y); - for (i = 0; i < src->n; i++) { - trans = path_transition_left_edge(ctx, src->x[i], src->y[i]); - clip_polygon_leftright(ctx, trans, src->x[i], src->y[i], - ctx->clip.x1); - } - return ctx->vertices.x - dst_x; -} - -static int -clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src, - float *dst_x, float *dst_y) -{ - enum path_transition trans; - int i; - - if (src->n < 2) - return 0; - - clip_context_prepare(ctx, src, dst_x, dst_y); - for (i = 0; i < src->n; i++) { - trans = path_transition_right_edge(ctx, src->x[i], src->y[i]); - clip_polygon_leftright(ctx, trans, src->x[i], src->y[i], - ctx->clip.x2); - } - return ctx->vertices.x - dst_x; -} - -static int -clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src, - float *dst_x, float *dst_y) -{ - enum path_transition trans; - int i; - - if (src->n < 2) - return 0; - - clip_context_prepare(ctx, src, dst_x, dst_y); - for (i = 0; i < src->n; i++) { - trans = path_transition_top_edge(ctx, src->x[i], src->y[i]); - clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i], - ctx->clip.y1); - } - return ctx->vertices.x - dst_x; -} - -static int -clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src, - float *dst_x, float *dst_y) -{ - enum path_transition trans; - int i; - - if (src->n < 2) - return 0; - - clip_context_prepare(ctx, src, dst_x, dst_y); - for (i = 0; i < src->n; i++) { - trans = path_transition_bottom_edge(ctx, src->x[i], src->y[i]); - clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i], - ctx->clip.y2); - } - return ctx->vertices.x - dst_x; -} - -#define max(a, b) (((a) > (b)) ? (a) : (b)) -#define min(a, b) (((a) > (b)) ? (b) : (a)) -#define clip(x, a, b) min(max(x, a), b) - -int -clip_simple(struct clip_context *ctx, - struct polygon8 *surf, - float *ex, - float *ey) -{ - int i; - for (i = 0; i < surf->n; i++) { - ex[i] = clip(surf->x[i], ctx->clip.x1, ctx->clip.x2); - ey[i] = clip(surf->y[i], ctx->clip.y1, ctx->clip.y2); - } - return surf->n; -} - -int -clip_transformed(struct clip_context *ctx, - struct polygon8 *surf, - float *ex, - float *ey) -{ - struct polygon8 polygon; - int i, n; - - polygon.n = clip_polygon_left(ctx, surf, polygon.x, polygon.y); - surf->n = clip_polygon_right(ctx, &polygon, surf->x, surf->y); - polygon.n = clip_polygon_top(ctx, surf, polygon.x, polygon.y); - surf->n = clip_polygon_bottom(ctx, &polygon, surf->x, surf->y); - - /* Get rid of duplicate vertices */ - ex[0] = surf->x[0]; - ey[0] = surf->y[0]; - n = 1; - for (i = 1; i < surf->n; i++) { - if (float_difference(ex[n - 1], surf->x[i]) == 0.0f && - float_difference(ey[n - 1], surf->y[i]) == 0.0f) - continue; - ex[n] = surf->x[i]; - ey[n] = surf->y[i]; - n++; - } - if (float_difference(ex[n - 1], surf->x[0]) == 0.0f && - float_difference(ey[n - 1], surf->y[0]) == 0.0f) - n--; - - return n; -} diff --git a/libweston/vertex-clipping.h b/libweston/vertex-clipping.h deleted file mode 100644 index 0c699021..00000000 --- a/libweston/vertex-clipping.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright © 2012 Intel Corporation - * - * 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. - */ -#ifndef _WESTON_VERTEX_CLIPPING_H -#define _WESTON_VERTEX_CLIPPING_H - -struct polygon8 { - float x[8]; - float y[8]; - int n; -}; - -struct clip_context { - struct { - float x; - float y; - } prev; - - struct { - float x1, y1; - float x2, y2; - } clip; - - struct { - float *x; - float *y; - } vertices; -}; - -float -float_difference(float a, float b); - -int -clip_simple(struct clip_context *ctx, - struct polygon8 *surf, - float *ex, - float *ey); - -int -clip_transformed(struct clip_context *ctx, - struct polygon8 *surf, - float *ex, - float *ey);\ - -#endif diff --git a/libweston/weston-direct-display.c b/libweston/weston-direct-display.c deleted file mode 100644 index bf25b5a3..00000000 --- a/libweston/weston-direct-display.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright © 2019 Collabora, Ltd. - * - * 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 <assert.h> -#include <stdint.h> -#include <unistd.h> -#include <sys/types.h> - -#include <libweston/libweston.h> -#include "linux-dmabuf.h" -#include "weston-direct-display-server-protocol.h" -#include "libweston-internal.h" - -static void -direct_display_enable(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *dmabuf_res) -{ - struct linux_dmabuf_buffer *dmabuf; - - dmabuf = wl_resource_get_user_data(dmabuf_res); - assert(dmabuf); - dmabuf->direct_display = true; -} - -static void -direct_display_destroy(struct wl_client *client, - struct wl_resource *global_resource) -{ - wl_resource_destroy(global_resource); -} - -static const struct weston_direct_display_v1_interface - weston_direct_display_interface_v1 = { - direct_display_enable, - direct_display_destroy, -}; - -static void -bind_direct_display(struct wl_client *client, void *data, - uint32_t version, uint32_t id) -{ - struct wl_resource *resource; - struct weston_compositor *ec = data; - - resource = wl_resource_create(client, - &weston_direct_display_v1_interface, - version, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } - - wl_resource_set_implementation(resource, - &weston_direct_display_interface_v1, - ec, NULL); -} - -WL_EXPORT int -weston_direct_display_setup(struct weston_compositor *ec) -{ - if (!wl_global_create(ec->wl_display, - &weston_direct_display_v1_interface, 1, - ec, bind_direct_display)) - return -1; - - return 0; -} diff --git a/libweston/weston-launch.c b/libweston/weston-launch.c deleted file mode 100644 index 8a711b4a..00000000 --- a/libweston/weston-launch.c +++ /dev/null @@ -1,874 +0,0 @@ -/* - * Copyright © 2012 Benjamin Franzke - * - * 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 <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <poll.h> -#include <errno.h> - -#include <getopt.h> - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <sys/socket.h> -#include <sys/signalfd.h> -#include <signal.h> -#include <unistd.h> -#include <fcntl.h> - -#include <linux/vt.h> -#include <linux/major.h> -#include <linux/kd.h> - -#include <pwd.h> -#include <grp.h> -#include <security/pam_appl.h> - -#ifdef HAVE_SYSTEMD_LOGIN -#include <systemd/sd-login.h> -#endif - -#include "weston-launch.h" - -#define DRM_MAJOR 226 - -#ifndef KDSKBMUTE -#define KDSKBMUTE 0x4B51 -#endif - -#ifndef EVIOCREVOKE -#define EVIOCREVOKE _IOW('E', 0x91, int) -#endif - -#define MAX_ARGV_SIZE 256 - -#ifdef BUILD_DRM_COMPOSITOR - -#include <xf86drm.h> - -#else - -static inline int -drmDropMaster(int drm_fd) -{ - return 0; -} - -static inline int -drmSetMaster(int drm_fd) -{ - return 0; -} - -#endif - -/* major()/minor() */ -#ifdef MAJOR_IN_MKDEV -# include <sys/mkdev.h> -#endif -#ifdef MAJOR_IN_SYSMACROS -# include <sys/sysmacros.h> -#endif - -struct weston_launch { - struct pam_conv pc; - pam_handle_t *ph; - int tty; - int ttynr; - int sock[2]; - int drm_fd; - int last_input_fd; - int kb_mode; - struct passwd *pw; - - int signalfd; - - pid_t child; - int verbose; - char *new_user; -}; - -union cmsg_data { unsigned char b[4]; int fd; }; - -static gid_t * -read_groups(int *ngroups) -{ - int n; - gid_t *groups; - - n = getgroups(0, NULL); - - if (n < 0) { - fprintf(stderr, "Unable to retrieve groups: %s\n", - strerror(errno)); - return NULL; - } - - groups = malloc(n * sizeof(gid_t)); - if (!groups) - return NULL; - - if (getgroups(n, groups) < 0) { - fprintf(stderr, "Unable to retrieve groups: %s\n", - strerror(errno)); - free(groups); - return NULL; - } - - *ngroups = n; - return groups; -} - -static bool -weston_launch_allowed(struct weston_launch *wl) -{ - struct group *gr; - gid_t *groups; - int ngroups; -#ifdef HAVE_SYSTEMD_LOGIN - char *session, *seat; - int err; -#endif - - if (getuid() == 0) - return true; - - gr = getgrnam("weston-launch"); - if (gr) { - groups = read_groups(&ngroups); - if (groups && ngroups > 0) { - while (ngroups--) { - if (groups[ngroups] == gr->gr_gid) { - free(groups); - return true; - } - } - free(groups); - } - } - -#ifdef HAVE_SYSTEMD_LOGIN - err = sd_pid_get_session(getpid(), &session); - if (err == 0 && session) { - if (sd_session_is_active(session) && - sd_session_get_seat(session, &seat) == 0) { - free(seat); - free(session); - return true; - } - free(session); - } -#endif - - return false; -} - -static int -pam_conversation_fn(int msg_count, - const struct pam_message **messages, - struct pam_response **responses, - void *user_data) -{ - return PAM_SUCCESS; -} - -static int -setup_pam(struct weston_launch *wl) -{ - int err; - - wl->pc.conv = pam_conversation_fn; - wl->pc.appdata_ptr = wl; - - err = pam_start("login", wl->pw->pw_name, &wl->pc, &wl->ph); - if (err != PAM_SUCCESS) { - fprintf(stderr, "failed to start pam transaction: %d: %s\n", - err, pam_strerror(wl->ph, err)); - return -1; - } - - err = pam_set_item(wl->ph, PAM_TTY, ttyname(wl->tty)); - if (err != PAM_SUCCESS) { - fprintf(stderr, "failed to set PAM_TTY item: %d: %s\n", - err, pam_strerror(wl->ph, err)); - return -1; - } - - err = pam_open_session(wl->ph, 0); - if (err != PAM_SUCCESS) { - fprintf(stderr, "failed to open pam session: %d: %s\n", - err, pam_strerror(wl->ph, err)); - return -1; - } - - return 0; -} - -static int -setup_launcher_socket(struct weston_launch *wl) -{ - if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, wl->sock) < 0) { - fprintf(stderr, "weston: socketpair failed: %s\n", - strerror(errno)); - return -1; - } - - if (fcntl(wl->sock[0], F_SETFD, FD_CLOEXEC) < 0) { - fprintf(stderr, "weston: fcntl failed: %s\n", - strerror(errno)); - return -1; - } - - return 0; -} - -static int -setup_signals(struct weston_launch *wl) -{ - int ret; - sigset_t mask; - struct sigaction sa; - - memset(&sa, 0, sizeof sa); - sa.sa_handler = SIG_DFL; - sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; - ret = sigaction(SIGCHLD, &sa, NULL); - assert(ret == 0); - - sa.sa_handler = SIG_IGN; - sa.sa_flags = 0; - sigaction(SIGHUP, &sa, NULL); - - ret = sigemptyset(&mask); - assert(ret == 0); - sigaddset(&mask, SIGCHLD); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGUSR1); - sigaddset(&mask, SIGUSR2); - ret = sigprocmask(SIG_BLOCK, &mask, NULL); - assert(ret == 0); - - wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); - if (wl->signalfd < 0) - return -errno; - - return 0; -} - -static void -setenv_fd(const char *env, int fd) -{ - char buf[32]; - - snprintf(buf, sizeof buf, "%d", fd); - setenv(env, buf, 1); -} - -static int -send_reply(struct weston_launch *wl, int reply) -{ - int len; - - do { - len = send(wl->sock[0], &reply, sizeof reply, 0); - } while (len < 0 && errno == EINTR); - - return len; -} - -static int -handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len) -{ - int fd = -1, ret = -1; - char control[CMSG_SPACE(sizeof(fd))]; - struct cmsghdr *cmsg; - struct stat s; - struct msghdr nmsg; - struct iovec iov; - struct weston_launcher_open *message; - union cmsg_data *data; - - message = msg->msg_iov->iov_base; - if ((size_t)len < sizeof(*message)) - goto err0; - - /* Ensure path is null-terminated */ - ((char *) message)[len-1] = '\0'; - - fd = open(message->path, message->flags); - if (fd < 0) { - fprintf(stderr, "Error opening device %s: %s\n", - message->path, strerror(errno)); - goto err0; - } - - if (fstat(fd, &s) < 0) { - close(fd); - fd = -1; - fprintf(stderr, "Failed to stat %s\n", message->path); - goto err0; - } - - if (major(s.st_rdev) != INPUT_MAJOR && - major(s.st_rdev) != DRM_MAJOR) { - close(fd); - fd = -1; - fprintf(stderr, "Device %s is not an input or drm device\n", - message->path); - goto err0; - } - -err0: - memset(&nmsg, 0, sizeof nmsg); - nmsg.msg_iov = &iov; - nmsg.msg_iovlen = 1; - if (fd != -1) { - nmsg.msg_control = control; - nmsg.msg_controllen = sizeof control; - cmsg = CMSG_FIRSTHDR(&nmsg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); - data = (union cmsg_data *) CMSG_DATA(cmsg); - data->fd = fd; - nmsg.msg_controllen = cmsg->cmsg_len; - ret = 0; - } - iov.iov_base = &ret; - iov.iov_len = sizeof ret; - - if (wl->verbose) - fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n", - message->path, ret, fd); - do { - len = sendmsg(wl->sock[0], &nmsg, 0); - } while (len < 0 && errno == EINTR); - - if (len < 0) - return -1; - - if (fd != -1 && major(s.st_rdev) == DRM_MAJOR) - wl->drm_fd = fd; - if (fd != -1 && major(s.st_rdev) == INPUT_MAJOR && - wl->last_input_fd < fd) - wl->last_input_fd = fd; - - return 0; -} - -static int -handle_socket_msg(struct weston_launch *wl) -{ - char control[CMSG_SPACE(sizeof(int))]; - char buf[BUFSIZ]; - struct msghdr msg; - struct iovec iov; - int ret = -1; - ssize_t len; - struct weston_launcher_message *message; - - memset(&msg, 0, sizeof(msg)); - iov.iov_base = buf; - iov.iov_len = sizeof buf; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = control; - msg.msg_controllen = sizeof control; - - do { - len = recvmsg(wl->sock[0], &msg, 0); - } while (len < 0 && errno == EINTR); - - if (len < 1) - return -1; - - message = (void *) buf; - switch (message->opcode) { - case WESTON_LAUNCHER_OPEN: - ret = handle_open(wl, &msg, len); - break; - } - - return ret; -} - -static void -quit(struct weston_launch *wl, int status) -{ - struct vt_mode mode = { 0 }; - int err; - - close(wl->signalfd); - close(wl->sock[0]); - - if (wl->new_user) { - err = pam_close_session(wl->ph, 0); - if (err) - fprintf(stderr, "pam_close_session failed: %d: %s\n", - err, pam_strerror(wl->ph, err)); - pam_end(wl->ph, err); - } - - if (ioctl(wl->tty, KDSKBMUTE, 0) && - ioctl(wl->tty, KDSKBMODE, wl->kb_mode)) - fprintf(stderr, "failed to restore keyboard mode: %s\n", - strerror(errno)); - - if (ioctl(wl->tty, KDSETMODE, KD_TEXT)) - fprintf(stderr, "failed to set KD_TEXT mode on tty: %s\n", - strerror(errno)); - - /* We have to drop master before we switch the VT back in - * VT_AUTO, so we don't risk switching to a VT with another - * display server, that will then fail to set drm master. */ - drmDropMaster(wl->drm_fd); - - mode.mode = VT_AUTO; - if (ioctl(wl->tty, VT_SETMODE, &mode) < 0) - fprintf(stderr, "could not reset vt handling\n"); - - exit(status); -} - -static void -close_input_fds(struct weston_launch *wl) -{ - struct stat s; - int fd; - - for (fd = 3; fd <= wl->last_input_fd; fd++) { - if (fstat(fd, &s) == 0 && major(s.st_rdev) == INPUT_MAJOR) { - /* EVIOCREVOKE may fail if the kernel doesn't - * support it, but all we can do is ignore it. */ - ioctl(fd, EVIOCREVOKE, 0); - close(fd); - } - } -} - -static int -handle_signal(struct weston_launch *wl) -{ - struct signalfd_siginfo sig; - int pid, status, ret; - - if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) { - fprintf(stderr, "weston: reading signalfd failed: %s\n", - strerror(errno)); - return -1; - } - - switch (sig.ssi_signo) { - case SIGCHLD: - pid = waitpid(-1, &status, 0); - if (pid == wl->child) { - wl->child = 0; - if (WIFEXITED(status)) - ret = WEXITSTATUS(status); - else if (WIFSIGNALED(status)) - /* - * If weston dies because of signal N, we - * return 10+N. This is distinct from - * weston-launch dying because of a signal - * (128+N). - */ - ret = 10 + WTERMSIG(status); - else - ret = 0; - quit(wl, ret); - } - break; - case SIGTERM: - case SIGINT: - if (!wl->child) - break; - - if (wl->verbose) - fprintf(stderr, "weston-launch: sending %s to pid %d\n", - strsignal(sig.ssi_signo), wl->child); - - kill(wl->child, sig.ssi_signo); - break; - case SIGUSR1: - send_reply(wl, WESTON_LAUNCHER_DEACTIVATE); - close_input_fds(wl); - drmDropMaster(wl->drm_fd); - ioctl(wl->tty, VT_RELDISP, 1); - break; - case SIGUSR2: - ioctl(wl->tty, VT_RELDISP, VT_ACKACQ); - drmSetMaster(wl->drm_fd); - send_reply(wl, WESTON_LAUNCHER_ACTIVATE); - break; - default: - return -1; - } - - return 0; -} - -static int -setup_tty(struct weston_launch *wl, const char *tty) -{ - struct stat buf; - struct vt_mode mode = { 0 }; - char *t; - - if (!wl->new_user) { - wl->tty = STDIN_FILENO; - } else if (tty) { - t = ttyname(STDIN_FILENO); - if (t && strcmp(t, tty) == 0) - wl->tty = STDIN_FILENO; - else - wl->tty = open(tty, O_RDWR | O_NOCTTY); - } else { - int tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC); - char filename[16]; - - if (tty0 < 0) { - fprintf(stderr, "weston: could not open tty0: %s\n", - strerror(errno)); - return -1; - } - - if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1) - { - fprintf(stderr, "weston: failed to find non-opened console: %s\n", - strerror(errno)); - return -1; - } - - snprintf(filename, sizeof filename, "/dev/tty%d", wl->ttynr); - wl->tty = open(filename, O_RDWR | O_NOCTTY); - close(tty0); - } - - if (wl->tty < 0) { - fprintf(stderr, "weston: failed to open tty: %s\n", - strerror(errno)); - return -1; - } - - if (fstat(wl->tty, &buf) == -1 || - major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0) { - fprintf(stderr, "weston: weston-launch must be run from a virtual terminal\n"); - return -1; - } - - if (tty) { - if (fstat(wl->tty, &buf) < 0) { - fprintf(stderr, "weston: stat %s failed: %s\n", tty, - strerror(errno)); - return -1; - } - - if (major(buf.st_rdev) != TTY_MAJOR) { - fprintf(stderr, - "weston: invalid tty device: %s\n", tty); - return -1; - } - - wl->ttynr = minor(buf.st_rdev); - } - - if (ioctl(wl->tty, KDGKBMODE, &wl->kb_mode)) { - fprintf(stderr, - "weston: failed to get current keyboard mode: %s", - strerror(errno)); - return -1; - } - - if (ioctl(wl->tty, KDSKBMUTE, 1) && - ioctl(wl->tty, KDSKBMODE, K_OFF)) { - fprintf(stderr, - "weston: failed to set K_OFF keyboard mode: %s\n", - strerror(errno)); - return -1; - } - - if (ioctl(wl->tty, KDSETMODE, KD_GRAPHICS)) { - fprintf(stderr, - "weston: failed to set KD_GRAPHICS mode on tty: %s\n", - strerror(errno)); - return -1; - } - - mode.mode = VT_PROCESS; - mode.relsig = SIGUSR1; - mode.acqsig = SIGUSR2; - if (ioctl(wl->tty, VT_SETMODE, &mode) < 0) { - fprintf(stderr, - "weston: failed to take control of vt handling %s\n", - strerror(errno)); - return -1; - } - - return 0; -} - -static int -setup_session(struct weston_launch *wl, char **child_argv) -{ - char **env; - char *term; - int i; - - if (wl->tty != STDIN_FILENO) { - if (setsid() < 0) { - fprintf(stderr, "weston: setsid failed %s\n", - strerror(errno)); - exit(EXIT_FAILURE); - } - if (ioctl(wl->tty, TIOCSCTTY, 0) < 0) { - fprintf(stderr, "TIOCSCTTY failed - tty is in use %s\n", - strerror(errno)); - exit(EXIT_FAILURE); - } - } - - term = getenv("TERM"); - clearenv(); - if (term) - setenv("TERM", term, 1); - setenv("USER", wl->pw->pw_name, 1); - setenv("LOGNAME", wl->pw->pw_name, 1); - setenv("HOME", wl->pw->pw_dir, 1); - setenv("SHELL", wl->pw->pw_shell, 1); - - env = pam_getenvlist(wl->ph); - if (env) { - for (i = 0; env[i]; ++i) { - if (putenv(env[i]) != 0) - fprintf(stderr, "putenv %s failed\n", env[i]); - } - free(env); - } - - /* - * We open a new session, so it makes sense - * to run a new login shell - */ - child_argv[0] = "/bin/sh"; - child_argv[1] = "-l"; - child_argv[2] = "-c"; - child_argv[3] = "exec " BINDIR "/weston \"$@\""; - child_argv[4] = "weston"; - return 5; -} - -static void -drop_privileges(struct weston_launch *wl) -{ - if (setgid(wl->pw->pw_gid) < 0 || -#ifdef HAVE_INITGROUPS - initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 || -#endif - setuid(wl->pw->pw_uid) < 0) { - fprintf(stderr, "weston: dropping privileges failed %s\n", - strerror(errno)); - exit(EXIT_FAILURE); - } -} - -static void -launch_compositor(struct weston_launch *wl, int argc, char *argv[]) -{ - char *child_argv[MAX_ARGV_SIZE]; - sigset_t mask; - int o, i; - - if (wl->verbose) - printf("weston-launch: spawned weston with pid: %d\n", getpid()); - if (wl->new_user) { - o = setup_session(wl, child_argv); - } else { - child_argv[0] = BINDIR "/weston"; - o = 1; - } - for (i = 0; i < argc; ++i) - child_argv[o + i] = argv[i]; - child_argv[o + i] = NULL; - - if (geteuid() == 0) - drop_privileges(wl); - - setenv_fd("WESTON_TTY_FD", wl->tty); - setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]); - - unsetenv("DISPLAY"); - - /* Do not give our signal mask to the new process. */ - sigemptyset(&mask); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGCHLD); - sigaddset(&mask, SIGINT); - sigprocmask(SIG_UNBLOCK, &mask, NULL); - - - execv(child_argv[0], child_argv); - fprintf(stderr, "weston: exec failed: %s\n", strerror(errno)); - exit(EXIT_FAILURE); -} - -static void -help(const char *name) -{ - fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name); - fprintf(stderr, " -u, --user Start session as specified username,\n" - " e.g. -u joe, requires root.\n"); - fprintf(stderr, " -t, --tty Start session on alternative tty,\n" - " e.g. -t /dev/tty4, requires -u option.\n"); - fprintf(stderr, " -v, --verbose Be verbose\n"); - fprintf(stderr, " -h, --help Display this help message\n"); -} - -int -main(int argc, char *argv[]) -{ - struct weston_launch wl; - int i, c; - char *tty = NULL; - struct option opts[] = { - { "user", required_argument, NULL, 'u' }, - { "tty", required_argument, NULL, 't' }, - { "verbose", no_argument, NULL, 'v' }, - { "help", no_argument, NULL, 'h' }, - { 0, 0, NULL, 0 } - }; - - memset(&wl, 0, sizeof wl); - - while ((c = getopt_long(argc, argv, "u:t:vh", opts, &i)) != -1) { - switch (c) { - case 'u': - wl.new_user = optarg; - if (getuid() != 0) { - fprintf(stderr, "weston: Permission denied. -u allowed for root only\n"); - exit(EXIT_FAILURE); - } - break; - case 't': - tty = optarg; - break; - case 'v': - wl.verbose = 1; - break; - case 'h': - help("weston-launch"); - exit(EXIT_FAILURE); - default: - exit(EXIT_FAILURE); - } - } - - if ((argc - optind) > (MAX_ARGV_SIZE - 6)) { - fprintf(stderr, - "weston: Too many arguments to pass to weston: %s\n", - strerror(E2BIG)); - exit(EXIT_FAILURE); - } - - if (tty && !wl.new_user) { - fprintf(stderr, "weston: -t/--tty option requires -u/--user option as well\n"); - exit(EXIT_FAILURE); - } - - if (wl.new_user) - wl.pw = getpwnam(wl.new_user); - else - wl.pw = getpwuid(getuid()); - if (wl.pw == NULL) { - fprintf(stderr, "weston: failed to get username: %s\n", - strerror(errno)); - exit(EXIT_FAILURE); - } - - if (!weston_launch_allowed(&wl)) { - fprintf(stderr, "Permission denied. You should either:\n" -#ifdef HAVE_SYSTEMD_LOGIN - " - run from an active and local (systemd) session.\n" -#else - " - enable systemd session support for weston-launch.\n" -#endif - " - or add yourself to the 'weston-launch' group.\n"); - exit(EXIT_FAILURE); - } - - if (setup_tty(&wl, tty) < 0) - exit(EXIT_FAILURE); - - if (wl.new_user && setup_pam(&wl) < 0) - exit(EXIT_FAILURE); - - if (setup_launcher_socket(&wl) < 0) - exit(EXIT_FAILURE); - - if (setup_signals(&wl) < 0) - exit(EXIT_FAILURE); - - wl.child = fork(); - if (wl.child == -1) { - fprintf(stderr, "weston: fork failed %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - if (wl.child == 0) - launch_compositor(&wl, argc - optind, argv + optind); - - close(wl.sock[1]); - if (wl.tty != STDIN_FILENO) - close(wl.tty); - - while (1) { - struct pollfd fds[2]; - int n; - - fds[0].fd = wl.sock[0]; - fds[0].events = POLLIN; - fds[1].fd = wl.signalfd; - fds[1].events = POLLIN; - - n = poll(fds, 2, -1); - if (n < 0) { - fprintf(stderr, "poll failed: %s\n", strerror(errno)); - return -1; - } - if (fds[0].revents & POLLIN) - handle_socket_msg(&wl); - if (fds[1].revents) - handle_signal(&wl); - } - - return 0; -} diff --git a/libweston/weston-launch.h b/libweston/weston-launch.h deleted file mode 100644 index 819321c8..00000000 --- a/libweston/weston-launch.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © 2012 Benjamin Franzke - * - * 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. - */ - -#ifndef _WESTON_LAUNCH_H_ -#define _WESTON_LAUNCH_H_ - -enum weston_launcher_opcode { - WESTON_LAUNCHER_OPEN, -}; - -enum weston_launcher_event { - WESTON_LAUNCHER_SUCCESS, - WESTON_LAUNCHER_ACTIVATE, - WESTON_LAUNCHER_DEACTIVATE -}; - -struct weston_launcher_message { - int opcode; -}; - -struct weston_launcher_open { - struct weston_launcher_message header; - int flags; - char path[0]; -}; - -int -weston_environment_get_fd(const char *env); - -#endif diff --git a/libweston/weston-log-file.c b/libweston/weston-log-file.c deleted file mode 100644 index 2cdb247d..00000000 --- a/libweston/weston-log-file.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright © 2019 Collabora Ltd. - * - * 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 <libweston/weston-log.h> -#include "shared/helpers.h" -#include <libweston/libweston.h> - -#include "weston-log-internal.h" - -#include <stdio.h> - -/** File type of stream - */ -struct weston_debug_log_file { - struct weston_log_subscriber base; - FILE *file; -}; - -static struct weston_debug_log_file * -to_weston_debug_log_file(struct weston_log_subscriber *sub) -{ - return container_of(sub, struct weston_debug_log_file, base); -} - -static void -weston_log_file_write(struct weston_log_subscriber *sub, - const char *data, size_t len) -{ - struct weston_debug_log_file *stream = to_weston_debug_log_file(sub); - fwrite(data, len, 1, stream->file); -} - -/** Creates a file type of subscriber - * - * Should be destroyed using weston_log_subscriber_destroy_log() - * - * @param dump_to if specified, used for writing data to - * @returns a weston_log_subscriber object or NULL in case of failure - * - * @sa weston_log_subscriber_destroy_log - * - */ -WL_EXPORT struct weston_log_subscriber * -weston_log_subscriber_create_log(FILE *dump_to) -{ - struct weston_debug_log_file *file = zalloc(sizeof(*file)); - - if (!file) - return NULL; - - if (dump_to) - file->file = dump_to; - else - file->file = stderr; - - - file->base.write = weston_log_file_write; - file->base.destroy = NULL; - file->base.complete = NULL; - - wl_list_init(&file->base.subscription_list); - - return &file->base; -} - -/** Destroy the subscriber created with weston_log_subscriber_create_log - * - * @param subscriber the weston_log_subscriber object to destroy - * - */ -WL_EXPORT void -weston_log_subscriber_destroy_log(struct weston_log_subscriber *subscriber) -{ - struct weston_debug_log_file *file = to_weston_debug_log_file(subscriber); - free(file); -} diff --git a/libweston/weston-log-flight-rec.c b/libweston/weston-log-flight-rec.c deleted file mode 100644 index 31ea3045..00000000 --- a/libweston/weston-log-flight-rec.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright © 2019 Collabora Ltd. - * - * 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 <libweston/weston-log.h> -#include "shared/helpers.h" -#include <libweston/libweston.h> - -#include "weston-log-internal.h" - -#include <assert.h> -#include <unistd.h> -#include <stdarg.h> -#include <string.h> -#include <errno.h> -#include <sys/time.h> - -struct weston_ring_buffer { - uint32_t append_pos; /**< where in the buffer we are */ - uint32_t size; /**< max length of the ring buffer */ - char *buf; /**< the buffer itself */ - FILE *file; /**< where to write in case we need to dump the buf */ - bool overlap; /**< in case buff overlaps, hint from where to print buf contents */ -}; - -/** allows easy access to the ring buffer in case of a core dump - */ -WL_EXPORT struct weston_ring_buffer *weston_primary_flight_recorder_ring_buffer = NULL; - -/** A black box type of stream, used to aggregate data continuously, and - * when needed, to dump its contents for inspection. - */ -struct weston_debug_log_flight_recorder { - struct weston_log_subscriber base; - struct weston_ring_buffer rb; -}; - -static void -weston_ring_buffer_init(struct weston_ring_buffer *rb, size_t size, char *buf) -{ - rb->append_pos = 0; - rb->size = size - 1; - rb->buf = buf; - rb->overlap = false; - rb->file = stderr; -} - -static struct weston_debug_log_flight_recorder * -to_flight_recorder(struct weston_log_subscriber *sub) -{ - return container_of(sub, struct weston_debug_log_flight_recorder, base); -} - -static void -weston_log_flight_recorder_adjust_end(struct weston_ring_buffer *rb, - size_t bytes_to_advance) -{ - if (rb->append_pos == rb->size - bytes_to_advance) - rb->append_pos = 0; - else - rb->append_pos += bytes_to_advance; -} - -static void -weston_log_flight_recorder_write_chunks(struct weston_ring_buffer *rb, - const char *data, size_t len) -{ - /* no of chunks that matches our buffer size */ - size_t nr_chunks = len / rb->size; - - /* bytes left that do not fill entire buffer */ - size_t bytes_left_last_chunk = len % rb->size; - const char *c_data = data; - - /* might overlap multiple times, memcpy is redundant, - * that's why we don't even modify append_pos */ - while (nr_chunks-- > 0) { - memcpy(&rb->buf[rb->append_pos], c_data, rb->size); - c_data += rb->size; - } - - if (bytes_left_last_chunk) - memcpy(&rb->buf[rb->append_pos], c_data, bytes_left_last_chunk); - - /* adjust append_pos */ - weston_log_flight_recorder_adjust_end(rb, bytes_left_last_chunk); -} - -static void -weston_log_flight_recorder_write_chunks_overlap(struct weston_ring_buffer *rb, - const char *data, size_t len) -{ - size_t transfer_remains = - (rb->append_pos + len) - rb->size; - size_t transfer_to_end = len - transfer_remains; - const char *c_data = data; - - /* transfer what remains until the end of the buffer */ - memcpy(&rb->buf[rb->append_pos], c_data, transfer_to_end); - c_data += transfer_to_end; - - /* reset append_pos as we filled up the buffer */ - rb->append_pos = 0; - - /* transfer what remains */ - weston_log_flight_recorder_write_chunks(rb, c_data, transfer_remains); - rb->overlap = true; -} - -static void -weston_log_flight_recorder_write_data(struct weston_ring_buffer *rb, - const char *data, size_t len) -{ - /* - * If append_pos is at the beginning of the buffer, we determine if we - * should do it in chunks, and if there are any bytes left we transfer - * those as well. - * - * If the append_pos is somewhere inside the buffer we determine how - * many bytes we need to transfer between we reach the end and overlap, - * then we proceed as in the first step. - */ - if (rb->append_pos == 0) - weston_log_flight_recorder_write_chunks(rb, data, len); - else - weston_log_flight_recorder_write_chunks_overlap(rb, data, len); -} - -static void -weston_log_flight_recorder_write(struct weston_log_subscriber *sub, - const char *data, size_t len) -{ - struct weston_debug_log_flight_recorder *flight_rec = - to_flight_recorder(sub); - struct weston_ring_buffer *rb = &flight_rec->rb; - - /* in case the data is bigger than the size of the buf */ - if (rb->size < len) { - weston_log_flight_recorder_write_data(rb, data, len); - } else { - /* if we can transfer it without wrapping it do it at once */ - if (rb->append_pos <= rb->size - len) { - memcpy(&rb->buf[rb->append_pos], data, len); - - /* - * adjust append_pos, take care of the situation were - * to fill up the entire buf - */ - weston_log_flight_recorder_adjust_end(rb, len); - } else { - weston_log_flight_recorder_write_data(rb, data, len); - } - } - -} - -static void -weston_log_flight_recorder_map_memory(struct weston_debug_log_flight_recorder *flight_rec) -{ - size_t i = 0; - - for (i = 0; i < flight_rec->rb.size; i++) - flight_rec->rb.buf[i] = 0xff; -} - -static void -weston_log_subscriber_display_flight_rec_data(struct weston_ring_buffer *rb, - FILE *file) -{ - FILE *file_d = stderr; - if (file) - file_d = file; - - if (!rb->overlap) { - if (rb->append_pos) - fwrite(rb->buf, sizeof(char), rb->append_pos, file_d); - else - fwrite(rb->buf, sizeof(char), rb->size, file_d); - } else { - /* from append_pos to size */ - fwrite(&rb->buf[rb->append_pos], sizeof(char), - rb->size - rb->append_pos, file_d); - /* from 0 to append_pos */ - fwrite(rb->buf, sizeof(char), rb->append_pos, file_d); - } -} - -WL_EXPORT void -weston_log_subscriber_display_flight_rec(struct weston_log_subscriber *sub) -{ - struct weston_debug_log_flight_recorder *flight_rec = - to_flight_recorder(sub); - struct weston_ring_buffer *rb = &flight_rec->rb; - - weston_log_subscriber_display_flight_rec_data(rb, rb->file); -} - -/** Create a flight recorder type of subscriber - * - * Allocates both the flight recorder and the underlying ring buffer. Use - * weston_log_subscriber_destroy_flight_rec() to clean-up. - * - * @param size specify the maximum size (in bytes) of the backing storage - * for the flight recorder - * @returns a weston_log_subscriber object or NULL in case of failure - */ -WL_EXPORT struct weston_log_subscriber * -weston_log_subscriber_create_flight_rec(size_t size) -{ - struct weston_debug_log_flight_recorder *flight_rec; - char *weston_rb; - - assert("Can't create more than one flight recorder." && - !weston_primary_flight_recorder_ring_buffer); - - flight_rec = zalloc(sizeof(*flight_rec)); - if (!flight_rec) - return NULL; - - flight_rec->base.write = weston_log_flight_recorder_write; - flight_rec->base.destroy = NULL; - flight_rec->base.complete = NULL; - wl_list_init(&flight_rec->base.subscription_list); - - weston_rb = zalloc(sizeof(char) * size); - if (!weston_rb) { - free(flight_rec); - return NULL; - } - - weston_ring_buffer_init(&flight_rec->rb, size, weston_rb); - weston_primary_flight_recorder_ring_buffer = &flight_rec->rb; - - /* write some data to the rb such that the memory gets mapped */ - weston_log_flight_recorder_map_memory(flight_rec); - - return &flight_rec->base; -} - -/** Destroys the weston_log_subscriber object created with - * weston_log_subscriber_create_flight_rec() - * - * @param sub the weston_log_subscriber object - * - */ -WL_EXPORT void -weston_log_subscriber_destroy_flight_rec(struct weston_log_subscriber *sub) -{ - struct weston_debug_log_flight_recorder *flight_rec = to_flight_recorder(sub); - free(flight_rec->rb.buf); - free(flight_rec); -} - -/** Retrieve flight recorder ring buffer contents, could be useful when - * implementing an assert()-like wrapper. - * - * @param file a FILE type already opened. Can also pass stderr/stdout under gdb - * if the program is loaded into memory. - * - * Uses the global exposed weston_primary_flight_recorder_ring_buffer. - * - */ -WL_EXPORT void -weston_log_flight_recorder_display_buffer(FILE *file) -{ - if (!weston_primary_flight_recorder_ring_buffer) - return; - - weston_log_subscriber_display_flight_rec_data(weston_primary_flight_recorder_ring_buffer, - file); -} diff --git a/libweston/weston-log-internal.h b/libweston/weston-log-internal.h deleted file mode 100644 index ba5b51ce..00000000 --- a/libweston/weston-log-internal.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright © 2019 Collabora Ltd - * - * 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. - */ -#ifndef WESTON_LOG_INTERNAL_H -#define WESTON_LOG_INTERNAL_H - -#include "wayland-util.h" - -struct weston_log_subscription; - -/** Subscriber allows each type of stream to customize or to provide its own - * methods to manipulate the underlying storage. It follows also an - * object-oriented approach, contains the ops callbacks and a list of - * subcriptions of type weston_log_subscription. Each subscription created will - * be both added to this subscription list and that of the weston_log_scope. - * - * A kind of stream can inherit the subscriber class and provide its own callbacks: - * @code - * struct weston_log_data_stream { - * struct weston_log_subscriber base; - * struct weston_data_stream opaque; - * }; - * @endcode - * - * Passing the base class will require container retrieval type of methods - * to be allowed to reach the opaque type (i.e., container_of()). - * - * @ingroup internal-log - * - */ -struct weston_log_subscriber { - /** write the data pointed by @param data */ - void (*write)(struct weston_log_subscriber *sub, const char *data, size_t len); - /** For the type of streams that required additional destroy operation - * for destroying the stream */ - void (*destroy)(struct weston_log_subscriber *sub); - /** For the type of streams that can inform the 'consumer' part that - * write operation has been terminated/finished and should close the - * stream. - */ - void (*complete)(struct weston_log_subscriber *sub); - struct wl_list subscription_list; /**< weston_log_subscription::owner_link */ -}; - -void -weston_log_subscription_create(struct weston_log_subscriber *owner, - struct weston_log_scope *scope); - -void -weston_log_subscription_destroy(struct weston_log_subscription *sub); - -struct weston_log_subscription * -weston_log_subscriber_get_only_subscription(struct weston_log_subscriber *subscriber); - -void -weston_log_subscription_add(struct weston_log_scope *scope, - struct weston_log_subscription *sub); -void -weston_log_subscription_remove(struct weston_log_subscription *sub); - - -void -weston_log_bind_weston_debug(struct wl_client *client, - void *data, uint32_t version, uint32_t id); - -struct weston_log_scope * -weston_log_get_scope(struct weston_log_context *log_ctx, const char *name); - -void -weston_log_run_cb_new_subscription(struct weston_log_subscription *sub); - -void -weston_debug_protocol_advertise_scopes(struct weston_log_context *log_ctx, - struct wl_resource *res); -int -weston_log_ctx_compositor_setup(struct weston_compositor *compositor, - struct weston_log_context *log_ctx); - -int -weston_vlog(const char *fmt, va_list ap); -int -weston_vlog_continue(const char *fmt, va_list ap); - -void * -weston_log_subscription_get_data(struct weston_log_subscription *sub); - -void -weston_log_subscription_set_data(struct weston_log_subscription *sub, void *data); - -void -weston_timeline_create_subscription(struct weston_log_subscription *sub, - void *user_data); - -void -weston_timeline_destroy_subscription(struct weston_log_subscription *sub, - void *user_data); - -#endif /* WESTON_LOG_INTERNAL_H */ diff --git a/libweston/weston-log-wayland.c b/libweston/weston-log-wayland.c deleted file mode 100644 index 43fc2885..00000000 --- a/libweston/weston-log-wayland.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright © 2019 Collabora Ltd - * - * 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 <libweston/weston-log.h> -#include "shared/helpers.h" -#include <libweston/libweston.h> - -#include "weston-log-internal.h" -#include "weston-debug-server-protocol.h" - -#include <assert.h> -#include <unistd.h> -#include <stdarg.h> -#include <string.h> -#include <errno.h> -#include <sys/time.h> - -/** A debug stream created by a client - * - * A client provides a file descriptor for the server to write debug messages - * into. A weston_log_debug_wayland is associated to one weston_log_scope via the - * scope name, and the scope provides the messages. There can be several - * streams for the same scope, all streams getting the same messages. - * - * The following is specific to weston-debug protocol. - * Subscription/unsubscription takes place in the stream_create(), respectively - * in stream_destroy(). - */ -struct weston_log_debug_wayland { - struct weston_log_subscriber base; - int fd; /**< client provided fd */ - struct wl_resource *resource; /**< weston_debug_stream_v1 object */ -}; - -static struct weston_log_debug_wayland * -to_weston_log_debug_wayland(struct weston_log_subscriber *sub) -{ - return container_of(sub, struct weston_log_debug_wayland, base); -} - -static void -stream_close_unlink(struct weston_log_debug_wayland *stream) -{ - if (stream->fd != -1) - close(stream->fd); - stream->fd = -1; -} - -static void WL_PRINTF(2, 3) -stream_close_on_failure(struct weston_log_debug_wayland *stream, - const char *fmt, ...) -{ - char *msg; - va_list ap; - int ret; - - stream_close_unlink(stream); - - va_start(ap, fmt); - ret = vasprintf(&msg, fmt, ap); - va_end(ap); - - if (ret > 0) { - weston_debug_stream_v1_send_failure(stream->resource, msg); - free(msg); - } else { - weston_debug_stream_v1_send_failure(stream->resource, - "MEMFAIL"); - } -} - -/** Write data into a specific debug stream - * - * \param sub The subscriber's stream to write into; must not be NULL. - * \param[in] data Pointer to the data to write. - * \param len Number of bytes to write. - * - * Writes the given data (binary verbatim) into the debug stream. - * If \c len is zero or negative, the write is silently dropped. - * - * Writing is continued until all data has been written or - * a write fails. If the write fails due to a signal, it is re-tried. - * Otherwise on failure, the stream is closed and - * \c weston_debug_stream_v1.failure event is sent to the client. - * - * \memberof weston_log_debug_wayland - */ -static void -weston_log_debug_wayland_write(struct weston_log_subscriber *sub, - const char *data, size_t len) -{ - ssize_t len_ = len; - ssize_t ret; - int e; - struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub); - - if (stream->fd == -1) - return; - - while (len_ > 0) { - ret = write(stream->fd, data, len_); - e = errno; - if (ret < 0) { - if (e == EINTR) - continue; - - stream_close_on_failure(stream, - "Error writing %zd bytes: %s (%d)", - len_, strerror(e), e); - break; - } - - len_ -= ret; - data += ret; - } -} - -/** Close the debug stream and send success event - * - * \param sub Subscriber's stream to close. - * - * Closes the debug stream and sends \c weston_debug_stream_v1.complete - * event to the client. This tells the client the debug information dump - * is complete. - * - * \memberof weston_log_debug_wayland - */ -static void -weston_log_debug_wayland_complete(struct weston_log_subscriber *sub) -{ - struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub); - - stream_close_unlink(stream); - weston_debug_stream_v1_send_complete(stream->resource); -} - -static void -weston_log_debug_wayland_to_destroy(struct weston_log_subscriber *sub) -{ - struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub); - stream_close_on_failure(stream, "debug name removed"); -} - -static struct weston_log_debug_wayland * -stream_create(struct weston_log_context *log_ctx, const char *name, - int32_t streamfd, struct wl_resource *stream_resource) -{ - struct weston_log_debug_wayland *stream; - struct weston_log_scope *scope; - - stream = zalloc(sizeof *stream); - if (!stream) - return NULL; - - stream->fd = streamfd; - stream->resource = stream_resource; - - stream->base.write = weston_log_debug_wayland_write; - stream->base.destroy = weston_log_debug_wayland_to_destroy; - stream->base.complete = weston_log_debug_wayland_complete; - wl_list_init(&stream->base.subscription_list); - - scope = weston_log_get_scope(log_ctx, name); - if (scope) { - weston_log_subscription_create(&stream->base, scope); - } else { - stream_close_on_failure(stream, - "Debug stream name '%s' is unknown.", - name); - } - - return stream; -} - -static void -stream_destroy(struct wl_resource *stream_resource) -{ - struct weston_log_debug_wayland *stream; - struct weston_log_subscription *sub = NULL; - - stream = wl_resource_get_user_data(stream_resource); - - if (stream->fd != -1) - close(stream->fd); - - sub = weston_log_subscriber_get_only_subscription(&stream->base); - - /* we can have a zero subscription if clients tried to subscribe - * to a non-existent scope */ - if (sub) - weston_log_subscription_destroy(sub); - - free(stream); -} - -static void -weston_debug_stream_destroy(struct wl_client *client, - struct wl_resource *stream_resource) -{ - wl_resource_destroy(stream_resource); -} - -static const struct weston_debug_stream_v1_interface - weston_debug_stream_impl = { - weston_debug_stream_destroy -}; - -static void -weston_debug_destroy(struct wl_client *client, - struct wl_resource *global_resource) -{ - wl_resource_destroy(global_resource); -} - -static void -weston_debug_subscribe(struct wl_client *client, - struct wl_resource *global_resource, - const char *name, - int32_t streamfd, - uint32_t new_stream_id) -{ - struct weston_log_context *log_ctx; - struct wl_resource *stream_resource; - uint32_t version; - struct weston_log_debug_wayland *stream; - - log_ctx = wl_resource_get_user_data(global_resource); - version = wl_resource_get_version(global_resource); - - stream_resource = wl_resource_create(client, - &weston_debug_stream_v1_interface, - version, new_stream_id); - if (!stream_resource) - goto fail; - - stream = stream_create(log_ctx, name, streamfd, stream_resource); - if (!stream) - goto fail; - - wl_resource_set_implementation(stream_resource, - &weston_debug_stream_impl, - stream, stream_destroy); - return; - -fail: - close(streamfd); - wl_client_post_no_memory(client); -} - -static const struct weston_debug_v1_interface weston_debug_impl = { - weston_debug_destroy, - weston_debug_subscribe -}; - -void -weston_log_bind_weston_debug(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct weston_log_context *log_ctx = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, - &weston_debug_v1_interface, - version, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(resource, &weston_debug_impl, - log_ctx, NULL); - - weston_debug_protocol_advertise_scopes(log_ctx, resource); -} diff --git a/libweston/weston-log.c b/libweston/weston-log.c deleted file mode 100644 index b2c4597c..00000000 --- a/libweston/weston-log.c +++ /dev/null @@ -1,962 +0,0 @@ -/* - * Copyright © 2017 Pekka Paalanen <pq@iki.fi> - * Copyright © 2018 Zodiac Inflight Innovations - * - * 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 <libweston/weston-log.h> -#include "shared/helpers.h" -#include <libweston/libweston.h> - -#include "weston-log-internal.h" -#include "weston-debug-server-protocol.h" - -#include <assert.h> -#include <unistd.h> -#include <stdarg.h> -#include <string.h> -#include <errno.h> -#include <sys/time.h> - -/** - * @defgroup log Public Logging/Debugging API - * @defgroup internal-log Private/Internal Logging/Debugging API - * @defgroup debug-protocol weston-debug protocol specific - */ - -/** Main weston-log context - * - * One per weston_compositor. Stores list of scopes created and a list pending - * subscriptions. - * - * A pending subscription is a subscription to a scope which hasn't been - * created. When the scope is finally created the pending subscription will be - * removed from the pending subscription list, but not before was added in the - * scope's subscription list and that of the subscriber list. - * - * Pending subscriptions only make sense for other types of streams, other than - * those created by weston-debug protocol. In the case of the weston-debug - * protocol, the subscription processes is done automatically whenever a client - * connects and subscribes to a scope which was previously advertised by the - * compositor. - * - * @ingroup internal-log - */ -struct weston_log_context { - struct wl_global *global; - struct wl_list scope_list; /**< weston_log_scope::compositor_link */ - struct wl_list pending_subscription_list; /**< weston_log_subscription::source_link */ -}; - -/** weston-log message scope - * - * This is used for scoping logging/debugging messages. Clients can subscribe - * to only the scopes they are interested in. A scope is identified by its name - * (also referred to as debug stream name). - * - * @ingroup log - */ -struct weston_log_scope { - char *name; - char *desc; - weston_log_scope_cb new_subscription; - weston_log_scope_cb destroy_subscription; - void *user_data; - struct wl_list compositor_link; - struct wl_list subscription_list; /**< weston_log_subscription::source_link */ -}; - -/** Ties a subscriber to a scope - * - * A subscription is created each time we'd want to subscribe to a scope. From - * the stream type we can retrieve the subscriber and from the subscriber we - * reach each of the streams callbacks. See also weston_log_subscriber object. - * - * When a subscription has been created we store it in the scope's subscription - * list and in the subscriber's subscription list. The subscription might be a - * pending subscription until the scope for which there's was a subscribe has - * been created. The scope creation will take of looking through the pending - * subscription list. - * - * A subscription can reached from a subscriber subscription list by using the - * streams base class. - * - * @ingroup internal-log - */ -struct weston_log_subscription { - struct weston_log_subscriber *owner; - struct wl_list owner_link; /**< weston_log_subscriber::subscription_list */ - - char *scope_name; - struct weston_log_scope *source; - struct wl_list source_link; /**< weston_log_scope::subscription_list or - weston_log_context::pending_subscription_list */ - - void *data; -}; - -static struct weston_log_subscription * -find_pending_subscription(struct weston_log_context *log_ctx, - const char *scope_name) -{ - struct weston_log_subscription *sub; - - wl_list_for_each(sub, &log_ctx->pending_subscription_list, source_link) - if (!strcmp(sub->scope_name, scope_name)) - return sub; - - return NULL; -} - -/** Create a pending subscription and add it the list of pending subscriptions - * - * @param owner a subscriber represented by weston_log_subscriber object - * @param scope_name the name of the scope (which we don't have in the list of scopes) - * @param log_ctx the logging context used to add the pending subscription - * - * @memberof weston_log_subscription - */ -static void -weston_log_subscription_create_pending(struct weston_log_subscriber *owner, - const char *scope_name, - struct weston_log_context *log_ctx) -{ - assert(owner); - assert(scope_name); - struct weston_log_subscription *sub = zalloc(sizeof(*sub)); - - if (!sub) - return; - - sub->scope_name = strdup(scope_name); - sub->owner = owner; - - wl_list_insert(&log_ctx->pending_subscription_list, &sub->source_link); -} - -/** Destroys the pending subscription created previously with - * weston_log_subscription_create_pending() - * - * @param sub the weston_log_subscription object to remove from the list - * of subscriptions and to destroy the subscription - * - * @memberof weston_log_subscription - */ -static void -weston_log_subscription_destroy_pending(struct weston_log_subscription *sub) -{ - assert(sub); - /* pending subsriptions do not have a source */ - wl_list_remove(&sub->source_link); - free(sub->scope_name); - free(sub); -} - -/** Write to the stream's subscription - * - * @memberof weston_log_subscription - */ -static void -weston_log_subscription_write(struct weston_log_subscription *sub, - const char *data, size_t len) -{ - if (sub->owner && sub->owner->write) - sub->owner->write(sub->owner, data, len); -} - -/** Write a formatted string to the stream's subscription - * - * @memberof weston_log_subscription - */ -static void -weston_log_subscription_vprintf(struct weston_log_subscription *sub, - const char *fmt, va_list ap) -{ - static const char oom[] = "Out of memory"; - char *str; - int len; - - if (!weston_log_scope_is_enabled(sub->source)) - return; - - len = vasprintf(&str, fmt, ap); - if (len >= 0) { - weston_log_subscription_write(sub, str, len); - free(str); - } else { - weston_log_subscription_write(sub, oom, sizeof oom - 1); - } -} - -void -weston_log_subscription_set_data(struct weston_log_subscription *sub, void *data) -{ - /* don't allow data to already be set */ - assert(!sub->data); - sub->data = data; -} - -void * -weston_log_subscription_get_data(struct weston_log_subscription *sub) -{ - return sub->data; -} - -/** Creates a new subscription using the subscriber by \c owner. - * - * The subscription created is added to the \c owner subscription list. - * Destroying the subscription using weston_log_subscription_destroy() will - * remove the link from the subscription list and free storage alloc'ed. - * - * Note that this adds the subscription to the scope's subscription list - * hence the \c scope required argument. - * - * @param owner the subscriber owner, must be created before creating a - * subscription - * @param scope the scope in order to add the subscription to the scope's - * subscription list - * @returns a weston_log_subscription object in case of success, or NULL - * otherwise - * - * @sa weston_log_subscription_destroy, weston_log_subscription_remove, - * weston_log_subscription_add - * @memberof weston_log_subscription - */ -void -weston_log_subscription_create(struct weston_log_subscriber *owner, - struct weston_log_scope *scope) -{ - struct weston_log_subscription *sub; - assert(owner); - assert(scope); - assert(scope->name); - - sub = zalloc(sizeof(*sub)); - if (!sub) - return; - - sub->owner = owner; - sub->scope_name = strdup(scope->name); - - wl_list_insert(&sub->owner->subscription_list, &sub->owner_link); - - weston_log_subscription_add(scope, sub); - weston_log_run_cb_new_subscription(sub); -} - -/** Destroys the subscription - * - * Removes the subscription from the scopes subscription list and from - * subscriber's subscription list. It destroys the subscription afterwads. - * - * @memberof weston_log_subscription - */ -void -weston_log_subscription_destroy(struct weston_log_subscription *sub) -{ - assert(sub); - - if (sub->source->destroy_subscription) - sub->source->destroy_subscription(sub, sub->source->user_data); - - if (sub->owner) - wl_list_remove(&sub->owner_link); - - weston_log_subscription_remove(sub); - free(sub->scope_name); - free(sub); -} - -/** Retrieve a subscription by using the subscriber - * - * This is useful when trying to find a subscription from the subscriber by - * having only access to the stream. - * - * @param subscriber the subscriber in question - * @returns a weston_log_subscription object - * - * @memberof weston_log_subscription - */ -struct weston_log_subscription * -weston_log_subscriber_get_only_subscription(struct weston_log_subscriber *subscriber) -{ - struct weston_log_subscription *sub; - /* unlikely, but can happen */ - if (wl_list_length(&subscriber->subscription_list) == 0) - return NULL; - - assert(wl_list_length(&subscriber->subscription_list) == 1); - - return wl_container_of(subscriber->subscription_list.prev, - sub, owner_link); -} - -/** Adds the subscription \c sub to the subscription list of the - * scope. - * - * This should used when the scope has been created, and the subscription \c - * sub has be created before calling this function. - * - * @param scope the scope - * @param sub the subscription, it must be created before, see - * weston_log_subscription_create() - * - * @memberof weston_log_subscription - */ -void -weston_log_subscription_add(struct weston_log_scope *scope, - struct weston_log_subscription *sub) -{ - assert(scope); - assert(sub); - /* don't allow subscriptions to have a source already! */ - assert(!sub->source); - - sub->source = scope; - wl_list_insert(&scope->subscription_list, &sub->source_link); -} - -/** Removes the subscription from the scope's subscription list - * - * @memberof weston_log_subscription - */ -void -weston_log_subscription_remove(struct weston_log_subscription *sub) -{ - assert(sub); - if (sub->source) - wl_list_remove(&sub->source_link); - sub->source = NULL; -} - -/** Look-up the scope from the scope list stored in the log context, by - * matching against the \c name. - * - * @param log_ctx - * @param name the scope name, see weston_compositor_add_log_scope() - * @returns NULL if none found, or a pointer to a weston_log_scope - * - * @ingroup internal-log - */ -struct weston_log_scope * -weston_log_get_scope(struct weston_log_context *log_ctx, const char *name) -{ - struct weston_log_scope *scope; - wl_list_for_each(scope, &log_ctx->scope_list, compositor_link) - if (strcmp(name, scope->name) == 0) - return scope; - return NULL; -} - -/** Wrapper to invoke the weston_log_scope_cb. Allows to call the cb - * new_subscription of a log scope. - * - * @ingroup internal-log - */ -void -weston_log_run_cb_new_subscription(struct weston_log_subscription *sub) -{ - if (sub->source->new_subscription) - sub->source->new_subscription(sub, sub->source->user_data); -} - -/** Advertise the log scope name and the log scope description - * - * This is only used by the weston-debug protocol! - * - * @ingroup internal-log - */ -void -weston_debug_protocol_advertise_scopes(struct weston_log_context *log_ctx, - struct wl_resource *res) -{ - struct weston_log_scope *scope; - wl_list_for_each(scope, &log_ctx->scope_list, compositor_link) - weston_debug_v1_send_available(res, scope->name, scope->desc); -} - -/** - * Connect weston_compositor structure to weston_log_context structure. - * - * \param compositor - * \param log_ctx - * \return 0 on success, -1 on failure - * - * Sets weston_compositor::weston_log_ctx. - * - * @ingroup log - */ -int -weston_log_ctx_compositor_setup(struct weston_compositor *compositor, - struct weston_log_context *log_ctx) -{ - assert(!compositor->weston_log_ctx); - assert(log_ctx); - - compositor->weston_log_ctx = log_ctx; - return 0; -} - -/** Creates weston_log_context structure - * - * \return NULL in case of failure, or a weston_log_context object in case of - * success - * - * weston_log_context is a singleton for each weston_compositor. - * @ingroup log - * - */ -WL_EXPORT struct weston_log_context * -weston_log_ctx_compositor_create(void) -{ - struct weston_log_context *log_ctx; - - log_ctx = zalloc(sizeof *log_ctx); - if (!log_ctx) - return NULL; - - wl_list_init(&log_ctx->scope_list); - wl_list_init(&log_ctx->pending_subscription_list); - - return log_ctx; -} - -/** Destroy weston_log_context structure - * - * \param compositor The libweston compositor whose weston-debug to tear down. - * - * Clears weston_compositor::weston_log_ctx. - * @ingroup log - * - */ -WL_EXPORT void -weston_log_ctx_compositor_destroy(struct weston_compositor *compositor) -{ - struct weston_log_context *log_ctx = compositor->weston_log_ctx; - struct weston_log_scope *scope; - struct weston_log_subscription *pending_sub, *pending_sub_tmp; - - if (log_ctx->global) - wl_global_destroy(log_ctx->global); - - wl_list_for_each(scope, &log_ctx->scope_list, compositor_link) - fprintf(stderr, "Internal warning: debug scope '%s' has not been destroyed.\n", - scope->name); - - /* Remove head to not crash if scope removed later. */ - wl_list_remove(&log_ctx->scope_list); - - /* Remove any pending subscription(s) which nobody subscribed to */ - wl_list_for_each_safe(pending_sub, pending_sub_tmp, - &log_ctx->pending_subscription_list, source_link) { - weston_log_subscription_destroy_pending(pending_sub); - } - - /* pending_subscription_list should be empty at this point */ - - free(log_ctx); - - compositor->weston_log_ctx = NULL; -} - -/** Enable weston-debug protocol extension - * - * \param compositor The libweston compositor where to enable. - * - * This enables the weston_debug_v1 Wayland protocol extension which any client - * can use to get debug messages from the compositor. - * - * WARNING: This feature should not be used in production. If a client - * provides a file descriptor that blocks writes, it will block the whole - * compositor indefinitely. - * - * There is no control on which client is allowed to subscribe to debug - * messages. Any and all clients are allowed. - * - * The debug extension is disabled by default, and once enabled, cannot be - * disabled again. - * - * @ingroup debug-protocol - */ -WL_EXPORT void -weston_compositor_enable_debug_protocol(struct weston_compositor *compositor) -{ - struct weston_log_context *log_ctx = compositor->weston_log_ctx; - assert(log_ctx); - if (log_ctx->global) - return; - - log_ctx->global = wl_global_create(compositor->wl_display, - &weston_debug_v1_interface, 1, - log_ctx, weston_log_bind_weston_debug); - if (!log_ctx->global) - return; - - fprintf(stderr, "WARNING: debug protocol has been enabled. " - "This is a potential denial-of-service attack vector and " - "information leak.\n"); -} - -/** Determine if the debug protocol has been enabled - * - * \param wc The libweston compositor to verify if debug protocol has been - * enabled - * - * @ingroup debug-protocol - */ -WL_EXPORT bool -weston_compositor_is_debug_protocol_enabled(struct weston_compositor *wc) -{ - return wc->weston_log_ctx->global != NULL; -} - -/** Register a new stream name, creating a log scope. - * - * @param log_ctx The weston_log_context where to add. - * @param name The debug stream/scope name; must not be NULL. - * @param description The log scope description for humans; must not be NULL. - * @param new_subscription Optional callback when a client subscribes to this - * scope. - * @param destroy_subscription Optional callback when a client destroys the - * subscription. - * @param user_data Optional user data pointer for the callback. - * @returns A valid pointer on success, NULL on failure. - * - * This function is used to create a log scope. All debug message printing - * happens for a scope, which allows clients to subscribe to the kind of debug - * messages they want by \c name. For the weston-debug protocol, - * subscription for the scope will happen automatically but for other types of - * streams, weston_log_subscribe() should be called as to create a subscription - * and tie it to the scope created by this function. - * - * \p name must be unique in the weston_compositor instance. \p name - * and \p description must both be provided. In case of the weston-debug - * protocol, the description is printed when a client asks for a list of - * supported log scopes. - * - * \p new_subscription, if not NULL, is called when a client subscribes to the log - * scope creating a debug stream. This is for log scopes that need to print - * messages as a response to a client appearing, e.g. printing a list of - * windows on demand or a static preamble. The argument \p user_data is - * passed in to the callback and is otherwise unused. - * - * For one-shot debug streams, \c new_subscription should finally call - * weston_log_subscription_complete() to close the stream and tell the client - * the printing is complete. Otherwise the client expects more data to be - * written. The complete callback in weston_log_subscriber should be installed - * to trigger it and it is set-up automatically for the weston-debug protocol. - * - * As subscription can take place before creating the scope, any pending - * subscriptions to scope added by weston_log_subscribe(), will be checked - * against the scope being created and if found will be added to the scope's - * subscription list. - * - * The log scope must be destroyed using weston_compositor_log_scope_destroy() - * before destroying the weston_compositor. - * - * @memberof weston_log_scope - * @sa weston_log_scope_cb, weston_log_subscribe - */ -WL_EXPORT struct weston_log_scope * -weston_compositor_add_log_scope(struct weston_log_context *log_ctx, - const char *name, - const char *description, - weston_log_scope_cb new_subscription, - weston_log_scope_cb destroy_subscription, - void *user_data) -{ - struct weston_log_scope *scope; - struct weston_log_subscription *pending_sub = NULL; - - if (!name || !description) { - fprintf(stderr, "Error: cannot add a debug scope without name or description.\n"); - return NULL; - } - - if (!log_ctx) { - fprintf(stderr, "Error: cannot add debug scope '%s', infra not initialized.\n", - name); - return NULL; - } - - if (weston_log_get_scope(log_ctx, name)){ - fprintf(stderr, "Error: debug scope named '%s' is already registered.\n", - name); - return NULL; - } - - scope = zalloc(sizeof *scope); - if (!scope) { - fprintf(stderr, "Error adding debug scope '%s': out of memory.\n", - name); - return NULL; - } - - scope->name = strdup(name); - scope->desc = strdup(description); - scope->new_subscription = new_subscription; - scope->destroy_subscription = destroy_subscription; - scope->user_data = user_data; - wl_list_init(&scope->subscription_list); - - if (!scope->name || !scope->desc) { - fprintf(stderr, "Error adding debug scope '%s': out of memory.\n", - name); - free(scope->name); - free(scope->desc); - free(scope); - return NULL; - } - - wl_list_insert(log_ctx->scope_list.prev, &scope->compositor_link); - - /* check if there are any pending subscriptions to this scope */ - while ((pending_sub = find_pending_subscription(log_ctx, scope->name)) != NULL) { - weston_log_subscription_create(pending_sub->owner, scope); - - /* remove it from pending */ - weston_log_subscription_destroy_pending(pending_sub); - } - - - return scope; -} - -/** Destroy a log scope - * - * @param scope The log scope to destroy; may be NULL. - * - * Destroys the log scope, calling each stream's destroy callback if one was - * installed/created. - * - * @memberof weston_log_scope - */ -WL_EXPORT void -weston_compositor_log_scope_destroy(struct weston_log_scope *scope) -{ - struct weston_log_subscription *sub, *sub_tmp; - - if (!scope) - return; - - wl_list_for_each_safe(sub, sub_tmp, &scope->subscription_list, source_link) { - /* destroy each subscription */ - if (sub->owner->destroy) - sub->owner->destroy(sub->owner); - - weston_log_subscription_destroy(sub); - } - - wl_list_remove(&scope->compositor_link); - free(scope->name); - free(scope->desc); - free(scope); -} - -/** Are there any active subscriptions to the scope? - * - * \param scope The log scope to check; may be NULL. - * \return True if any streams are open for this scope, false otherwise. - * - * As printing some debugging messages may be relatively expensive, one - * can use this function to determine if there is a need to gather the - * debugging information at all. If this function returns false, all - * printing for this scope is dropped, so gathering the information is - * pointless. - * - * The return value of this function should not be stored, as new clients - * may subscribe to the debug scope later. - * - * If the given scope is NULL, this function will always return false, - * making it safe to use in teardown or destroy code, provided the - * scope is initialized to NULL before creation and set to NULL after - * destruction. - * - * \memberof weston_log_scope - */ -WL_EXPORT bool -weston_log_scope_is_enabled(struct weston_log_scope *scope) -{ - if (!scope) - return false; - - return !wl_list_empty(&scope->subscription_list); -} - -/** Close the stream's complete callback if one was installed/created. - * - * @ingroup log - */ -WL_EXPORT void -weston_log_subscription_complete(struct weston_log_subscription *sub) -{ - if (sub->owner && sub->owner->complete) - sub->owner->complete(sub->owner); -} - -/** Close the log scope. - * - * @param scope The log scope to complete; may be NULL. - * - * Complete the log scope, calling each stream's complete callback if one was - * installed/created. This can be useful to signal the reading end that the - * data has been transmited and should no longer expect that written over the - * stream. Particularly useful for the weston-debug protocol. - * - * @memberof weston_log_scope - * @sa weston_compositor_add_log_scope, weston_compositor_log_scope_destroy - */ -WL_EXPORT void -weston_log_scope_complete(struct weston_log_scope *scope) -{ - struct weston_log_subscription *sub; - - if (!scope) - return; - - wl_list_for_each(sub, &scope->subscription_list, source_link) - weston_log_subscription_complete(sub); -} - -/** Write log data for a scope - * - * \param scope The debug scope to write for; may be NULL, in which case - * nothing will be written. - * \param[in] data Pointer to the data to write. - * \param len Number of bytes to write. - * - * Writes the given data to all subscribed clients' streams. - * - * \memberof weston_log_scope - */ -WL_EXPORT void -weston_log_scope_write(struct weston_log_scope *scope, - const char *data, size_t len) -{ - struct weston_log_subscription *sub; - - if (!scope) - return; - - wl_list_for_each(sub, &scope->subscription_list, source_link) - weston_log_subscription_write(sub, data, len); -} - -/** Write a formatted string for a scope (varargs) - * - * \param scope The log scope to write for; may be NULL, in which case - * nothing will be written. - * \param fmt Printf-style format string. - * \param ap Formatting arguments. - * - * Writes to formatted string to all subscribed clients' streams. - * - * The behavioral details for each stream are the same as for - * weston_debug_stream_write(). - * - * \memberof weston_log_scope - */ -WL_EXPORT int -weston_log_scope_vprintf(struct weston_log_scope *scope, - const char *fmt, va_list ap) -{ - static const char oom[] = "Out of memory"; - char *str; - int len = 0; - - if (!weston_log_scope_is_enabled(scope)) - return len; - - len = vasprintf(&str, fmt, ap); - if (len >= 0) { - weston_log_scope_write(scope, str, len); - free(str); - } else { - weston_log_scope_write(scope, oom, sizeof oom - 1); - } - - return len; -} - -/** Write a formatted string for a scope - * - * \param scope The log scope to write for; may be NULL, in which case - * nothing will be written. - * \param fmt Printf-style format string and arguments. - * - * Writes to formatted string to all subscribed clients' streams. - * - * The behavioral details for each stream are the same as for - * weston_debug_stream_write(). - * - * \memberof weston_log_scope - */ -WL_EXPORT int -weston_log_scope_printf(struct weston_log_scope *scope, - const char *fmt, ...) -{ - va_list ap; - int len; - - va_start(ap, fmt); - len = weston_log_scope_vprintf(scope, fmt, ap); - va_end(ap); - - return len; -} - -/** Write a formatted string for a subscription - * - * \param sub The subscription to write for; may be NULL, in which case - * nothing will be written. - * \param fmt Printf-style format string and arguments. - * - * Writes to formatted string to the stream that created the subscription. - * - * @ingroup log - */ -WL_EXPORT void -weston_log_subscription_printf(struct weston_log_subscription *sub, - const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - weston_log_subscription_vprintf(sub, fmt, ap); - va_end(ap); -} - -/** Write debug scope name and current time into string - * - * \param[in] scope debug scope; may be NULL - * \param[out] buf Buffer to store the string. - * \param len Available size in the buffer in bytes. - * \return \c buf - * - * Reads the current local wall-clock time and formats it into a string. - * and append the debug scope name to it, if a scope is available. - * The string is NUL-terminated, even if truncated. - * - * @memberof weston_log_scope - */ -WL_EXPORT char * -weston_log_scope_timestamp(struct weston_log_scope *scope, - char *buf, size_t len) -{ - struct timeval tv; - struct tm *bdt; - char string[128]; - size_t ret = 0; - - gettimeofday(&tv, NULL); - - bdt = localtime(&tv.tv_sec); - if (bdt) - ret = strftime(string, sizeof string, - "%Y-%m-%d %H:%M:%S", bdt); - - if (ret > 0) { - snprintf(buf, len, "[%s.%03ld][%s]", string, - tv.tv_usec / 1000, - (scope) ? scope->name : "no scope"); - } else { - snprintf(buf, len, "[?][%s]", - (scope) ? scope->name : "no scope"); - } - - return buf; -} - -/** Subscribe to a scope - * - * Creates a subscription which is used to subscribe the \p subscriber - * to the scope \c scope_name. - * - * If \c scope_name has already been created (using - * weston_compositor_add_log_scope) the subscription will take place - * immediately, otherwise we store the subscription into a pending list. See - * also weston_compositor_add_log_scope(). - * - * @param log_ctx the log context, used for accessing pending list - * @param subscriber the subscriber, which has to be created before - * @param scope_name the scope name. In case the scope is not created - * we temporarily store the subscription in the pending list. - * - * @ingroup log - */ -WL_EXPORT void -weston_log_subscribe(struct weston_log_context *log_ctx, - struct weston_log_subscriber *subscriber, - const char *scope_name) -{ - assert(log_ctx); - assert(subscriber); - assert(scope_name); - - struct weston_log_scope *scope; - - scope = weston_log_get_scope(log_ctx, scope_name); - if (scope) - weston_log_subscription_create(subscriber, scope); - else - /* - * if we don't have already as scope for it, add it to pending - * subscription list - */ - weston_log_subscription_create_pending(subscriber, scope_name, log_ctx); -} - -/** Iterate over all subscriptions in a scope - * - * @param scope the scope for which you want to iterate - * @param sub_iter the iterator, use NULL to start from the 'head' - * @returns the next subscription from the log scope - * - * This is (quite) useful when 'log_scope' and 'log_subscription' are opaque. Do note - * that \c sub_iter needs to be NULL-initialized before calling this function. - * - */ -WL_EXPORT struct weston_log_subscription * -weston_log_subscription_iterate(struct weston_log_scope *scope, - struct weston_log_subscription *sub_iter) -{ - struct wl_list *list = &scope->subscription_list; - struct wl_list *node; - - /* go to the next item in the list or if not set starts with the head */ - if (sub_iter) - node = sub_iter->source_link.next; - else - node = list->next; - - assert(node); - assert(!sub_iter || node != &sub_iter->source_link); - - /* if we're at the end */ - if (node == list) - return NULL; - - return container_of(node, struct weston_log_subscription, source_link); -} diff --git a/libweston/zoom.c b/libweston/zoom.c deleted file mode 100644 index 064d6a84..00000000 --- a/libweston/zoom.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright © 2012 Scott Moreau - * - * 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 <assert.h> -#include <stdint.h> -#include <stdlib.h> -#include <stdbool.h> - -#include <libweston/libweston.h> -#include "backend.h" -#include "libweston-internal.h" -#include "text-cursor-position-server-protocol.h" -#include "shared/helpers.h" - -static void -weston_zoom_frame_z(struct weston_animation *animation, - struct weston_output *output, - const struct timespec *time) -{ - if (animation->frame_counter <= 1) - output->zoom.spring_z.timestamp = *time; - - weston_spring_update(&output->zoom.spring_z, time); - - if (output->zoom.spring_z.current > output->zoom.max_level) - output->zoom.spring_z.current = output->zoom.max_level; - else if (output->zoom.spring_z.current < 0.0) - output->zoom.spring_z.current = 0.0; - - if (weston_spring_done(&output->zoom.spring_z)) { - if (output->zoom.active && output->zoom.level <= 0.0) { - output->zoom.active = false; - output->zoom.seat = NULL; - weston_output_disable_planes_decr(output); - wl_list_remove(&output->zoom.motion_listener.link); - } - output->zoom.spring_z.current = output->zoom.level; - wl_list_remove(&animation->link); - wl_list_init(&animation->link); - } - - output->dirty = 1; - weston_output_damage(output); -} - -static void -zoom_area_center_from_point(struct weston_output *output, - double *x, double *y) -{ - float level = output->zoom.spring_z.current; - - *x = (*x - output->x) * level + output->width / 2.; - *y = (*y - output->y) * level + output->height / 2.; -} - -static void -weston_output_update_zoom_transform(struct weston_output *output) -{ - double x = output->zoom.current.x; /* global pointer coords */ - double y = output->zoom.current.y; - float level; - - level = output->zoom.spring_z.current; - - if (!output->zoom.active || level > output->zoom.max_level || - level == 0.0f) - return; - - zoom_area_center_from_point(output, &x, &y); - - output->zoom.trans_x = x - output->width / 2; - output->zoom.trans_y = y - output->height / 2; - - if (output->zoom.trans_x < 0) - output->zoom.trans_x = 0; - if (output->zoom.trans_y < 0) - output->zoom.trans_y = 0; - if (output->zoom.trans_x > level * output->width) - output->zoom.trans_x = level * output->width; - if (output->zoom.trans_y > level * output->height) - output->zoom.trans_y = level * output->height; -} - -static void -weston_zoom_transition(struct weston_output *output) -{ - if (output->zoom.level != output->zoom.spring_z.current) { - output->zoom.spring_z.target = output->zoom.level; - if (wl_list_empty(&output->zoom.animation_z.link)) { - output->zoom.animation_z.frame_counter = 0; - wl_list_insert(output->animation_list.prev, - &output->zoom.animation_z.link); - } - } - - output->dirty = 1; - weston_output_damage(output); -} - -WL_EXPORT void -weston_output_update_zoom(struct weston_output *output) -{ - struct weston_seat *seat = output->zoom.seat; - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - if (!pointer) - return; - - assert(output->zoom.active); - - output->zoom.current.x = wl_fixed_to_double(pointer->x); - output->zoom.current.y = wl_fixed_to_double(pointer->y); - - weston_zoom_transition(output); - weston_output_update_zoom_transform(output); -} - -static void -motion(struct wl_listener *listener, void *data) -{ - struct weston_output_zoom *zoom = - container_of(listener, struct weston_output_zoom, motion_listener); - struct weston_output *output = - container_of(zoom, struct weston_output, zoom); - - weston_output_update_zoom(output); -} - -WL_EXPORT void -weston_output_activate_zoom(struct weston_output *output, - struct weston_seat *seat) -{ - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - if (!pointer || output->zoom.active) - return; - - output->zoom.active = true; - output->zoom.seat = seat; - weston_output_disable_planes_incr(output); - wl_signal_add(&pointer->motion_signal, - &output->zoom.motion_listener); -} - -WL_EXPORT void -weston_output_init_zoom(struct weston_output *output) -{ - output->zoom.active = false; - output->zoom.seat = NULL; - output->zoom.increment = 0.07; - output->zoom.max_level = 0.95; - output->zoom.level = 0.0; - output->zoom.trans_x = 0.0; - output->zoom.trans_y = 0.0; - weston_spring_init(&output->zoom.spring_z, 250.0, 0.0, 0.0); - output->zoom.spring_z.friction = 1000; - output->zoom.animation_z.frame = weston_zoom_frame_z; - wl_list_init(&output->zoom.animation_z.link); - output->zoom.motion_listener.notify = motion; -} |