diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 02cfaa0d..b6445093 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -296,7 +296,9 @@ x86_64-debian-full-build: - .build-options-full artifacts: reports: - cobertura: $BUILDDIR/meson-logs/coverage.xml + coverage_report: + coverage_format: cobertura + path: $BUILDDIR/meson-logs/coverage.xml aarch64-debian-full-build: extends: diff --git a/clients/simple-dmabuf-egl.c b/clients/simple-dmabuf-egl.c index 33df4cf9..ef0d9de6 100644 --- a/clients/simple-dmabuf-egl.c +++ b/clients/simple-dmabuf-egl.c @@ -131,7 +131,7 @@ struct buffer { int release_fence_fd; }; -#define NUM_BUFFERS 3 +#define NUM_BUFFERS 4 struct window { struct display *display; diff --git a/clients/simple-dmabuf-feedback.c b/clients/simple-dmabuf-feedback.c index dd2e64a5..516cdbc8 100644 --- a/clients/simple-dmabuf-feedback.c +++ b/clients/simple-dmabuf-feedback.c @@ -26,11 +26,13 @@ #include "config.h" #include +#include #include #include #include #include #include +#include #include "shared/helpers.h" #include "shared/platform.h" @@ -47,7 +49,7 @@ #include #include -#define NUM_BUFFERS 3 +#define NUM_BUFFERS 4 /* We have to hack the DRM-backend to pretend that planes of the underlying * hardware don't support this format. If you change the value of this constant, @@ -140,7 +142,11 @@ struct display { struct buffer { struct window *window; struct wl_buffer *buffer; - bool busy; + enum { + NOT_CREATED, + IN_USE, + AVAILABLE + } status; bool recreate; int dmabuf_fds[4]; struct gbm_bo *bo; @@ -469,7 +475,7 @@ buffer_release(void *data, struct wl_buffer *buffer) { struct buffer *buf = data; - buf->busy = false; + buf->status = AVAILABLE; if (buf->recreate) buffer_recreate(buf); @@ -485,6 +491,7 @@ create_succeeded(void *data, struct zwp_linux_buffer_params_v1 *params, { struct buffer *buf = data; + buf->status = AVAILABLE; buf->buffer = new_buffer; wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); zwp_linux_buffer_params_v1_destroy(params); @@ -516,6 +523,7 @@ create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width, struct zwp_linux_buffer_params_v1 *params; int i; + buf->status = NOT_CREATED; buf->window = window; buf->width = width; buf->height = height; @@ -573,8 +581,22 @@ window_next_buffer(struct window *window) unsigned int i; for (i = 0; i < NUM_BUFFERS; i++) - if (!window->buffers[i].busy) + if (window->buffers[i].status == AVAILABLE) return &window->buffers[i]; + + /* In this client, we sometimes have to recreate the buffers. As we are + * not using the create_immed request from zwp_linux_dmabuf_v1, we need + * to wait an event from the server (what leads to create_succeeded() + * being called in this client). So if all buffers are busy, it may be + * the case in which all the buffers were recreated but the server still + * didn't send the events. This is very unlikely to happen, but a + * roundtrip() guarantees that we receive and process the events. */ + wl_display_roundtrip(window->display->display); + + for (i = 0; i < NUM_BUFFERS; i++) + if (window->buffers[i].status == AVAILABLE) + return &window->buffers[i]; + return NULL; } @@ -639,15 +661,13 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) window->callback = wl_surface_frame(window->surface); wl_callback_add_listener(window->callback, &frame_listener, window); wl_surface_commit(window->surface); - buf->busy = true; + buf->status = IN_USE; region = wl_compositor_create_region(window->display->compositor); wl_region_add(region, 0, 0, window->display->output.width, window->display->output.height); wl_surface_set_opaque_region(window->surface, region); wl_region_destroy(region); - - window->n_redraws++; } static const struct wl_callback_listener frame_listener = { @@ -1050,18 +1070,54 @@ dmabuf_feedback_tranche_formats(void *data, } } +static char +bits2graph(uint32_t value, unsigned bitoffset) +{ + int c = (value >> bitoffset) & 0xff; + + if (isgraph(c) || isspace(c)) + return c; + + return '?'; +} + +static void +fourcc2str(uint32_t format, char *str, int len) +{ + int i; + + assert(len >= 5); + + for (i = 0; i < 4; i++) + str[i] = bits2graph(format, i * 8); + str[i] = '\0'; +} + static void print_tranche_format_modifier(uint32_t format, uint64_t modifier) { const struct pixel_format_info *fmt_info; + char *format_str; char *mod_name; + int len; - fmt_info = pixel_format_get_info(format); mod_name = pixel_format_get_modifier(modifier); + fmt_info = pixel_format_get_info(format); + + if (fmt_info) { + len = asprintf(&format_str, "%s", fmt_info->drm_format_name); + } else { + char fourcc_str[5]; + + fourcc2str(format, fourcc_str, sizeof(fourcc_str)); + len = asprintf(&format_str, "0x%08x (%s)", format, fourcc_str); + } + assert(len > 0); fprintf(stderr, "│ ├────────tranche format/modifier pair - format %s, modifier %s\n", - fmt_info ? fmt_info->drm_format_name : "UNKNOWN", mod_name); + format_str, mod_name); + free(format_str); free(mod_name); } @@ -1103,7 +1159,7 @@ dmabuf_feedback_tranche_done(void *data, dmabuf_feedback_tranche_init(&feedback->pending_tranche); } -static void +static bool pick_initial_format_from_renderer_tranche(struct window *window, struct dmabuf_feedback_tranche *tranche) { @@ -1117,13 +1173,12 @@ pick_initial_format_from_renderer_tranche(struct window *window, window->format.format = fmt->format; wl_array_copy(&window->format.modifiers, &fmt->modifiers); - return; + return true; } - - assert(0 && "error: INITIAL_BUFFER_FORMAT not supported by the hardware"); + return false; } -static void +static bool pick_format_from_scanout_tranche(struct window *window, struct dmabuf_feedback_tranche *tranche) { @@ -1132,8 +1187,9 @@ pick_format_from_scanout_tranche(struct window *window, wl_array_for_each(fmt, &tranche->formats.arr) { - /* Ignore format that we're already using. */ - if (fmt->format == window->format.format) + /* Ignore the format that we want to pick from the render + * tranche. */ + if (fmt->format == INITIAL_BUFFER_FORMAT) continue; /* Format should be supported by the compositor. */ @@ -1147,10 +1203,9 @@ pick_format_from_scanout_tranche(struct window *window, window->format.format = fmt->format; wl_array_copy(&window->format.modifiers, &fmt->modifiers); - return; + return true; } - - assert(0 && "error: no valid pair of format/modifier in the scanout tranche"); + return false; } static void @@ -1158,25 +1213,37 @@ dmabuf_feedback_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_fee { struct window *window = data; struct dmabuf_feedback_tranche *tranche; + bool got_scanout_tranche = false; unsigned int i; fprintf(stderr, "└end of dma-buf feedback\n\n"); /* The first time that we receive dma-buf feedback for a surface it - * contains only the renderer tranche. We pick the INITIAL_BUFFER_FORMAT + * contains only the renderer tranches. We pick the INITIAL_BUFFER_FORMAT * from there. Then the compositor should detect that the format is * unsupported by the underlying hardware (not actually, but you should - * have faked this in the DRM-backend) and send the scanout tranche. We - * use the formats/modifiers of the scanout tranche to reallocate our + * have faked this in the DRM-backend) and send the scanout tranches. We + * use the formats/modifiers of the scanout tranches to reallocate our * buffers. */ wl_array_for_each(tranche, &window->pending_dmabuf_feedback.tranches) { if (tranche->is_scanout_tranche) { - pick_format_from_scanout_tranche(window, tranche); - for (i = 0; i < NUM_BUFFERS; i++) - window->buffers[i].recreate = true; - break; + got_scanout_tranche = true; + if (pick_format_from_scanout_tranche(window, tranche)) { + for (i = 0; i < NUM_BUFFERS; i++) + window->buffers[i].recreate = true; + break; + } } - pick_initial_format_from_renderer_tranche(window, tranche); + if (pick_initial_format_from_renderer_tranche(window, tranche)) + break; + } + + if (got_scanout_tranche) { + assert(window->format.format != INITIAL_BUFFER_FORMAT && + "error: no valid pair of format/modifier in the scanout tranches"); + } else { + assert(window->format.format == INITIAL_BUFFER_FORMAT && + "error: INITIAL_BUFFER_FORMAT not supported by the hardware"); } dmabuf_feedback_fini(&window->dmabuf_feedback); @@ -1383,6 +1450,9 @@ main(int argc, char **argv) struct display *display; struct window *window; int ret = 0; + struct timespec start_time, current_time; + const time_t MAX_TIME_SECONDS = 3; + time_t delta_time = 0; fprintf(stderr, "This client was written with the purpose of manually test " \ "Weston's dma-buf feedback implementation. See main() " \ @@ -1391,9 +1461,14 @@ main(int argc, char **argv) display = create_display(); window = create_window(display); + clock_gettime(CLOCK_MONOTONIC, &start_time); + redraw(window, NULL, 0); - while (ret != -1 && window->n_redraws < 200) + while (ret != -1 && delta_time < MAX_TIME_SECONDS) { ret = wl_display_dispatch(display->display); + clock_gettime(CLOCK_MONOTONIC, ¤t_time); + delta_time = current_time.tv_sec - start_time.tv_sec; + } destroy_window(window); destroy_display(display); diff --git a/clients/simple-dmabuf-v4l.c b/clients/simple-dmabuf-v4l.c index 85dd7a3d..a19570f9 100644 --- a/clients/simple-dmabuf-v4l.c +++ b/clients/simple-dmabuf-v4l.c @@ -127,7 +127,7 @@ struct buffer { int data_offsets[VIDEO_MAX_PLANES]; }; -#define NUM_BUFFERS 3 +#define NUM_BUFFERS 4 struct window { struct display *display; diff --git a/clients/simple-egl.c b/clients/simple-egl.c index a0f418a5..2c7059c0 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -262,6 +262,19 @@ init_gl(struct window *window) GLuint frag, vert; GLuint program; GLint status; + EGLBoolean ret; + + window->native = wl_egl_window_create(window->surface, + window->geometry.width, + window->geometry.height); + window->egl_surface = + weston_platform_create_egl_surface(window->display->egl.dpy, + window->display->egl.conf, + window->native, NULL); + + ret = eglMakeCurrent(window->display->egl.dpy, window->egl_surface, + window->egl_surface, window->display->egl.ctx); + assert(ret == EGL_TRUE); frag = create_shader(window, frag_shader_text, GL_FRAGMENT_SHADER); vert = create_shader(window, vert_shader_text, GL_VERTEX_SHADER); @@ -362,19 +375,9 @@ static void create_surface(struct window *window) { struct display *display = window->display; - EGLBoolean ret; window->surface = wl_compositor_create_surface(display->compositor); - window->native = - wl_egl_window_create(window->surface, - window->geometry.width, - window->geometry.height); - window->egl_surface = - weston_platform_create_egl_surface(display->egl.dpy, - display->egl.conf, - window->native, NULL); - window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base, window->surface); xdg_surface_add_listener(window->xdg_surface, @@ -389,21 +392,16 @@ create_surface(struct window *window) xdg_toplevel_set_app_id(window->xdg_toplevel, "org.freedesktop.weston.simple-egl"); + if (window->fullscreen) + xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL); + else if (window->maximized) + xdg_toplevel_set_maximized(window->xdg_toplevel); + window->wait_for_configure = true; wl_surface_commit(window->surface); - ret = eglMakeCurrent(window->display->egl.dpy, window->egl_surface, - window->egl_surface, window->display->egl.ctx); - assert(ret == EGL_TRUE); - if (!window->frame_sync) eglSwapInterval(display->egl.dpy, 0); - - if (!display->wm_base) - return; - - if (window->fullscreen) - xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL); } static void @@ -806,6 +804,7 @@ usage(int error_code) fprintf(stderr, "Usage: simple-egl [OPTIONS]\n\n" " -d \tBuffer swap delay in microseconds\n" " -f\tRun in fullscreen mode\n" + " -m\tRun in maximized mode\n" " -o\tCreate an opaque surface\n" " -s\tUse a 16 bpp EGL config\n" " -b\tDon't sync to compositor redraw (eglSwapInterval 0)\n" @@ -836,6 +835,8 @@ main(int argc, char **argv) window.delay = atoi(argv[++i]); else if (strcmp("-f", argv[i]) == 0) window.fullscreen = 1; + else if (strcmp("-m", argv[i]) == 0) + window.maximized = 1; else if (strcmp("-o", argv[i]) == 0) window.opaque = 1; else if (strcmp("-s", argv[i]) == 0) @@ -864,7 +865,17 @@ main(int argc, char **argv) init_egl(&display, &window); create_surface(&window); - init_gl(&window); + + /* we already have wait_for_configure set after create_surface() */ + while (running && ret != -1 && window.wait_for_configure) { + ret = wl_display_dispatch(display.display); + + /* wait until xdg_surface::configure acks the new dimensions */ + if (window.wait_for_configure) + continue; + + init_gl(&window); + } display.cursor_surface = wl_compositor_create_surface(display.compositor); @@ -874,17 +885,9 @@ main(int argc, char **argv) sigint.sa_flags = SA_RESETHAND; sigaction(SIGINT, &sigint, NULL); - /* The mainloop here is a little subtle. Redrawing will cause - * EGL to read events so we can just call - * wl_display_dispatch_pending() to handle any events that got - * queued up as a side effect. */ while (running && ret != -1) { - if (window.wait_for_configure) { - ret = wl_display_dispatch(display.display); - } else { - ret = wl_display_dispatch_pending(display.display); - redraw(&window, NULL, 0); - } + ret = wl_display_dispatch_pending(display.display); + redraw(&window, NULL, 0); } fprintf(stderr, "simple-egl exiting\n"); diff --git a/clients/window.c b/clients/window.c index a0d988f4..1d98ee04 100644 --- a/clients/window.c +++ b/clients/window.c @@ -352,8 +352,11 @@ struct input { struct toytimer cursor_timer; bool cursor_timer_running; struct wl_surface *pointer_surface; + bool pointer_surface_has_role; + int hotspot_x, hotspot_y; uint32_t modifiers; uint32_t pointer_enter_serial; + uint32_t cursor_serial; float sx, sy; struct wl_list link; @@ -2748,12 +2751,6 @@ input_remove_pointer_focus(struct input *input) input->pointer_focus = NULL; input->current_cursor = CURSOR_UNSET; cancel_pointer_image_update(input); - wl_surface_destroy(input->pointer_surface); - input->pointer_surface = NULL; - if (input->cursor_frame_cb) { - wl_callback_destroy(input->cursor_frame_cb); - input->cursor_frame_cb = NULL; - } } static void @@ -2782,6 +2779,16 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, input->pointer_enter_serial = serial; input->pointer_focus = window; + /* Some compositors advertise wl_seat before wl_compositor. This + * makes it potentially impossible to create the pointer surface + * when we bind the seat, so we need to create our pointer surface + * now instead. + */ + if (!input->pointer_surface) + input->pointer_surface = wl_compositor_create_surface(input->display->compositor); + + input->pointer_surface_has_role = false; + input->sx = sx; input->sy = sy; @@ -3793,8 +3800,7 @@ input_set_pointer_image_index(struct input *input, int index) struct wl_buffer *buffer; struct wl_cursor *cursor; struct wl_cursor_image *image; - struct wl_surface *prev_surface; - struct display *d = input->display; + int dx = 0, dy = 0; if (!input->pointer) return; @@ -3813,21 +3819,24 @@ input_set_pointer_image_index(struct input *input, int index) if (!buffer) return; - /* Don't re-use the previous surface, otherwise the new buffer and the - * new hotspot aren't applied atomically. */ - prev_surface = input->pointer_surface; - input->pointer_surface = wl_compositor_create_surface(d->compositor); - - wl_surface_attach(input->pointer_surface, buffer, 0, 0); + if (input->pointer_surface_has_role) { + dx = input->hotspot_x - image->hotspot_x; + dy = input->hotspot_y - image->hotspot_y; + } + wl_surface_attach(input->pointer_surface, buffer, dx, dy); wl_surface_damage(input->pointer_surface, 0, 0, image->width, image->height); wl_surface_commit(input->pointer_surface); - wl_pointer_set_cursor(input->pointer, input->pointer_enter_serial, - input->pointer_surface, - image->hotspot_x, image->hotspot_y); - if (prev_surface) - wl_surface_destroy(prev_surface); + if (!input->pointer_surface_has_role) { + wl_pointer_set_cursor(input->pointer, + input->pointer_enter_serial, + input->pointer_surface, + image->hotspot_x, image->hotspot_y); + input->pointer_surface_has_role = true; + } + input->hotspot_x = image->hotspot_x; + input->hotspot_y = image->hotspot_y; } static const struct wl_callback_listener pointer_surface_listener; @@ -3839,11 +3848,14 @@ input_set_pointer_special(struct input *input) wl_pointer_set_cursor(input->pointer, input->pointer_enter_serial, NULL, 0, 0); + input->pointer_surface_has_role = false; return true; } - if (input->current_cursor == CURSOR_UNSET) + if (input->current_cursor == CURSOR_UNSET) { + input->pointer_surface_has_role = false; return true; + } return false; } @@ -3883,7 +3895,6 @@ schedule_pointer_image_update(struct input *input, wl_callback_add_listener(input->cursor_frame_cb, &pointer_surface_listener, input); - wl_surface_commit(input->pointer_surface); } static void @@ -3933,11 +3944,11 @@ pointer_surface_frame_callback(void *data, struct wl_callback *callback, time - input->cursor_anim_start, &duration); - input_set_pointer_image_index(input, i); - if (cursor->image_count > 1) schedule_pointer_image_update(input, cursor, duration, force_frame); + + input_set_pointer_image_index(input, i); } static void @@ -3967,15 +3978,30 @@ static const struct wl_callback_listener pointer_surface_listener = { void input_set_pointer_image(struct input *input, int pointer) { + int force = 0; + if (!input->pointer) return; - if (pointer == input->current_cursor) + if (input->pointer_enter_serial > input->cursor_serial) + force = 1; + + if (!force && pointer == input->current_cursor) return; input->current_cursor = pointer; + input->cursor_serial = input->pointer_enter_serial; if (!input->cursor_frame_cb) pointer_surface_frame_callback(input, NULL, 0); + else if (force && !input_set_pointer_special(input)) { + /* The current frame callback may be stuck if, for instance, + * the set cursor request was processed by the server after + * this client lost the focus. In this case the cursor surface + * might not be mapped and the frame callback wouldn't ever + * complete. Send a set_cursor and attach to try to map the + * cursor surface again so that the callback will finish */ + input_set_pointer_image_index(input, 0); + } } struct wl_data_device * @@ -5923,6 +5949,8 @@ display_add_input(struct display *d, uint32_t id, int display_seat_version) input); } + input->pointer_surface_has_role = false; + toytimer_init(&input->cursor_timer, CLOCK_MONOTONIC, d, cursor_timer_func); diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 4ca78979..63e14311 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -101,6 +101,8 @@ struct shell_surface { struct weston_desktop_surface *desktop_surface; struct weston_view *view; + struct weston_surface *wsurface_anim_fade; + struct weston_view *wview_anim_fade; int32_t last_width, last_height; struct desktop_shell *shell; @@ -194,6 +196,10 @@ struct shell_seat { }; +static struct weston_view * +shell_fade_create_fade_out_view(struct shell_surface *shsurf, + struct weston_surface *surface); + static struct desktop_shell * shell_surface_get_shell(struct shell_surface *shsurf); @@ -261,10 +267,12 @@ desktop_shell_destroy_surface(struct shell_surface *shsurf) wl_list_init(&shsurf_child->children_link); } wl_list_remove(&shsurf->children_link); + weston_desktop_surface_unlink_view(shsurf->view); + weston_view_destroy(shsurf->view); wl_signal_emit(&shsurf->destroy_signal, shsurf); + weston_surface_destroy(shsurf->wsurface_anim_fade); - weston_view_destroy(shsurf->view); if (shsurf->output_destroy_listener.notify) { wl_list_remove(&shsurf->output_destroy_listener.link); shsurf->output_destroy_listener.notify = NULL; @@ -877,7 +885,7 @@ animate_focus_change(struct desktop_shell *shell, struct workspace *ws, } static void -desktop_shell_destroy_views_on_layer(struct weston_layer *layer); +desktop_shell_destroy_layer(struct weston_layer *layer); static void workspace_destroy(struct workspace *ws) @@ -892,7 +900,7 @@ workspace_destroy(struct workspace *ws) if (ws->fsurf_back) focus_surface_destroy(ws->fsurf_back); - desktop_shell_destroy_views_on_layer(&ws->layer); + desktop_shell_destroy_layer(&ws->layer); free(ws); } @@ -1381,7 +1389,7 @@ touch_move_grab_motion(struct weston_touch_grab *grab, int dx = wl_fixed_to_int(grab->touch->grab_x + move->dx); int dy = wl_fixed_to_int(grab->touch->grab_y + move->dy); - if (!shsurf || !move->active) + if (!shsurf || !shsurf->desktop_surface || !move->active) return; es = weston_desktop_surface_get_surface(shsurf->desktop_surface); @@ -1513,7 +1521,7 @@ move_grab_motion(struct weston_pointer_grab *grab, int cx, cy; weston_pointer_move(pointer, event); - if (!shsurf) + if (!shsurf || !shsurf->desktop_surface) return; surface = weston_desktop_surface_get_surface(shsurf->desktop_surface); @@ -2253,8 +2261,8 @@ fade_out_done(struct weston_view_animation *animation, void *data) loop = wl_display_get_event_loop(shsurf->shell->compositor->wl_display); - if (weston_view_is_mapped(shsurf->view)) { - weston_view_unmap(shsurf->view); + if (weston_view_is_mapped(shsurf->wview_anim_fade)) { + weston_view_unmap(shsurf->wview_anim_fade); wl_event_loop_add_idle(loop, fade_out_done_idle_cb, shsurf); } } @@ -2364,7 +2372,6 @@ desktop_surface_removed(struct weston_desktop_surface *desktop_surface, weston_desktop_surface_set_user_data(shsurf->desktop_surface, NULL); shsurf->desktop_surface = NULL; - weston_desktop_surface_unlink_view(shsurf->view); if (weston_surface_is_mapped(surface) && shsurf->shell->win_close_animation_type == ANIMATION_FADE) { @@ -2373,11 +2380,26 @@ desktop_surface_removed(struct weston_desktop_surface *desktop_surface, pixman_region32_init(&surface->pending.input); pixman_region32_fini(&surface->input); pixman_region32_init(&surface->input); - weston_fade_run(shsurf->view, 1.0, 0.0, 300.0, + + /* its location might have changed, but also might've + * migrated to a different output, so re-compute this + * as the animation requires having the same output as + * the view */ + weston_view_set_output(shsurf->wview_anim_fade, + shsurf->view->output); + weston_view_set_position(shsurf->wview_anim_fade, + shsurf->view->geometry.x, + shsurf->view->geometry.y); + + weston_layer_entry_insert(&shsurf->view->layer_link, + &shsurf->wview_anim_fade->layer_link); + + /* unmap the "original" view */ + weston_view_unmap(shsurf->view); + weston_fade_run(shsurf->wview_anim_fade, 1.0, 0.0, 300.0, fade_out_done, shsurf); + return; - } else { - weston_surface_destroy(surface); } } @@ -2500,8 +2522,14 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, if (!weston_surface_is_mapped(surface)) { map(shell, shsurf, sx, sy); surface->is_mapped = true; - if (shsurf->shell->win_close_animation_type == ANIMATION_FADE) - ++surface->ref_count; + /* as we need to survive the weston_surface destruction we'll + * need to take another reference */ + if (shsurf->shell->win_close_animation_type == ANIMATION_FADE) { + surface->ref_count++; + shsurf->wsurface_anim_fade = surface; + shsurf->wview_anim_fade = + shell_fade_create_fade_out_view(shsurf, surface); + } return; } @@ -3992,6 +4020,29 @@ shell_fade_create_surface_for_output(struct desktop_shell *shell, struct shell_o return view; } +static struct weston_view * +shell_fade_create_fade_out_view(struct shell_surface *shsurf, + struct weston_surface *surface) +{ + struct weston_view *view; + struct weston_output *woutput; + + view = weston_view_create(surface); + if (!view) + return NULL; + + woutput = get_focused_output(surface->compositor); + /* set the initial position and output just in case we happen to not + * move it around and just destroy it */ + weston_view_set_output(view, woutput); + weston_view_set_position(view, + shsurf->view->geometry.x, + shsurf->view->geometry.y); + view->is_mapped = true; + + return view; +} + static void shell_fade(struct desktop_shell *shell, enum fade_type type) { @@ -4905,7 +4956,7 @@ setup_output_destroy_handler(struct weston_compositor *ec, } static void -desktop_shell_destroy_views_on_layer(struct weston_layer *layer) +desktop_shell_destroy_layer(struct weston_layer *layer) { struct weston_view *view, *view_next; @@ -4916,9 +4967,17 @@ desktop_shell_destroy_views_on_layer(struct weston_layer *layer) * additional black_view created and added to its layer_link * fullscreen view. See shell_ensure_fullscreen_black_view() * - * As that black_view it is not a weston_desktop_surface - * we can't have a shsurf for it so we just destroy it like - * we do it in desktop_surface_removed() */ + * Note that we do not choose to destroy all other potential + * views we find in the layer, but instead we explicitly verify + * if the view in question was explicitly created by + * desktop-shell, rather than libweston-desktop (in + * desktop_surface_added()). + * + * This is particularly important because libweston-desktop + * could create additional views, which are managed implicitly, + * but which are still being added to the layer list. + * + */ if (shsurf) desktop_shell_destroy_surface(shsurf); else @@ -4970,12 +5029,12 @@ shell_destroy(struct wl_listener *listener, void *data) workspace_destroy(*ws); wl_array_release(&shell->workspaces.array); - desktop_shell_destroy_views_on_layer(&shell->panel_layer); - desktop_shell_destroy_views_on_layer(&shell->background_layer); - desktop_shell_destroy_views_on_layer(&shell->lock_layer); - desktop_shell_destroy_views_on_layer(&shell->input_panel_layer); - desktop_shell_destroy_views_on_layer(&shell->minimized_layer); - desktop_shell_destroy_views_on_layer(&shell->fullscreen_layer); + desktop_shell_destroy_layer(&shell->panel_layer); + desktop_shell_destroy_layer(&shell->background_layer); + desktop_shell_destroy_layer(&shell->lock_layer); + desktop_shell_destroy_layer(&shell->input_panel_layer); + desktop_shell_destroy_layer(&shell->minimized_layer); + desktop_shell_destroy_layer(&shell->fullscreen_layer); free(shell->client); free(shell); diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index d99dc763..2174d34d 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1289,6 +1289,7 @@ struct weston_view { struct weston_surface *surface; struct wl_list surface_link; struct wl_signal destroy_signal; + struct wl_signal unmap_signal; /* struct weston_paint_node::view_link */ struct wl_list paint_node_list; @@ -1441,7 +1442,7 @@ struct weston_pointer_constraint { bool hint_is_pending; struct wl_listener pointer_destroy_listener; - struct wl_listener surface_destroy_listener; + struct wl_listener view_unmap_listener; struct wl_listener surface_commit_listener; struct wl_listener surface_activate_listener; }; diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 5995baea..14857ecc 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -173,8 +173,10 @@ kiosk_shell_surface_find_best_output(struct kiosk_shell_surface *shsurf) app_id = weston_desktop_surface_get_app_id(shsurf->desktop_surface); if (app_id) { wl_list_for_each(shoutput, &shsurf->shell->output_list, link) { - if (kiosk_shell_output_has_app_id(shoutput, app_id)) + if (kiosk_shell_output_has_app_id(shoutput, app_id)) { + shsurf->appid_output_assigned = true; return shoutput->output; + } } } @@ -354,6 +356,7 @@ kiosk_shell_surface_create(struct kiosk_shell *shell, shsurf->desktop_surface = desktop_surface; shsurf->view = view; shsurf->shell = shell; + shsurf->appid_output_assigned = false; weston_desktop_surface_set_user_data(desktop_surface, shsurf); @@ -387,8 +390,10 @@ kiosk_shell_surface_activate(struct kiosk_shell_surface *shsurf, /* removes it from the normal_layer and move it to inactive * one, without occluding the top-level window if the new one - * is a child to that */ - if (!shsurf->parent) { + * is a child to that. Also, do not occlude another view + * (currently focused one) on a different output when activating + * a new one. */ + if (!shsurf->parent && (shsurf->output == current_focus->output)) { weston_layer_entry_remove(¤t_focus->view->layer_link); weston_layer_entry_insert(&shsurf->shell->inactive_layer.view_list, ¤t_focus->view->layer_link); @@ -645,12 +650,18 @@ find_focus_successor(struct weston_layer *layer, struct weston_view *top_view = NULL; struct weston_view *view; + /* we need to take into account that the surface being destroyed it not * always the same as the focus_surface, which could result in picking * and *activating* the wrong window, so avoid returning a view for * that case. A particular case is when a top-level child window, would - * pick a parent window below the focused_surface. */ - if (focused_surface != shsurf->view->surface) + * pick a parent window below the focused_surface. + * + * Apply that only on the same output to avoid incorrectly returning an + * invalid/empty view, which could happen if the view being destroyed + * is on a output different than the focused_surface output */ + if (focused_surface && focused_surface != shsurf->view->surface && + shsurf->output == focused_surface->output) return top_view; wl_list_for_each(view, &layer->view_list.link, layer_link.link) { @@ -660,6 +671,10 @@ find_focus_successor(struct weston_layer *layer, if (!view->is_mapped || view == shsurf->view) continue; + /* pick views only on the same output */ + if (view->output != shsurf->output) + continue; + view_shsurf = get_kiosk_shell_surface(view->surface); if (!view_shsurf) continue; @@ -721,6 +736,8 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, weston_desktop_surface_get_user_data(desktop_surface); struct weston_surface *surface = weston_desktop_surface_get_surface(desktop_surface); + const char *app_id = + weston_desktop_surface_get_app_id(desktop_surface); bool is_resized; bool is_fullscreen; @@ -729,6 +746,24 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, if (surface->width == 0) return; + if (!shsurf->appid_output_assigned && app_id) { + struct weston_output *output = NULL; + + /* reset previous output being set in _added() as the output is + * being cached */ + shsurf->output = NULL; + output = kiosk_shell_surface_find_best_output(shsurf); + + kiosk_shell_surface_set_output(shsurf, output); + weston_desktop_surface_set_size(shsurf->desktop_surface, + shsurf->output->width, + shsurf->output->height); + /* even if we couldn't find an appid set for a particular + * output still flag the shsurf as to a avoid changing the + * output every time */ + shsurf->appid_output_assigned = true; + } + /* TODO: When the top-level surface is committed with a new size after an * output resize, sometimes the view appears scaled. What state are we not * updating? diff --git a/kiosk-shell/kiosk-shell.h b/kiosk-shell/kiosk-shell.h index 9f680806..070ba1ab 100644 --- a/kiosk-shell/kiosk-shell.h +++ b/kiosk-shell/kiosk-shell.h @@ -73,6 +73,8 @@ struct kiosk_shell_surface { int32_t x; int32_t y; } xwayland; + + bool appid_output_assigned; }; struct kiosk_shell_seat { diff --git a/libweston-desktop/xdg-shell.c b/libweston-desktop/xdg-shell.c index ff76c39c..6cbf55e4 100644 --- a/libweston-desktop/xdg-shell.c +++ b/libweston-desktop/xdg-shell.c @@ -713,7 +713,7 @@ weston_desktop_xdg_toplevel_committed(struct weston_desktop_xdg_toplevel *toplev wl_resource_post_error(client_resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, - "xdg_surface buffer (%" PRIi32 " x %" PRIi32 ") " + "xdg_surface geometry (%" PRIi32 " x %" PRIi32 ") " "does not match the configured maximized state (%" PRIi32 " x %" PRIi32 ")", geometry.width, geometry.height, toplevel->next.size.width, diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 55e9414b..48600880 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -236,6 +236,7 @@ enum try_view_on_plane_failure_reasons { FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE = (1 << 1), FAILURE_REASONS_DMABUF_MODIFIER_INVALID = (1 << 2), FAILURE_REASONS_ADD_FB_FAILED = (1 << 3), + FAILURE_REASONS_GBM_BO_IMPORT_FAILED = (1 << 4) }; /** diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index ffe2cc52..ba0c177e 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -276,8 +276,12 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf, fb->bo = gbm_bo_import(backend->gbm, GBM_BO_IMPORT_FD_MODIFIER, &import_mod, GBM_BO_USE_SCANOUT); - if (!fb->bo) + if (!fb->bo) { + if (try_view_on_plane_failure_reasons) + *try_view_on_plane_failure_reasons |= + FAILURE_REASONS_GBM_BO_IMPORT_FAILED; goto err_free; + } fb->width = dmabuf->attributes.width; fb->height = dmabuf->attributes.height; diff --git a/libweston/backend-drm/meson.build b/libweston/backend-drm/meson.build index 23db9127..b7af834b 100644 --- a/libweston/backend-drm/meson.build +++ b/libweston/backend-drm/meson.build @@ -42,7 +42,7 @@ deps_drm = [ ] if get_option('renderer-gl') - dep_gbm = dependency('gbm', required: false) + dep_gbm = dependency('gbm', required: false, version: '>= 21.1.1') 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 diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 72e4db93..7b350aa4 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -602,7 +602,8 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev, if (try_view_on_plane_failure_reasons & (FAILURE_REASONS_ADD_FB_FAILED | FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE | - FAILURE_REASONS_DMABUF_MODIFIER_INVALID)) + FAILURE_REASONS_DMABUF_MODIFIER_INVALID | + FAILURE_REASONS_GBM_BO_IMPORT_FAILED)) action_needed |= ACTION_NEEDED_ADD_SCANOUT_TRANCHE; assert(action_needed != (ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE | diff --git a/libweston/compositor.c b/libweston/compositor.c index 1670c500..b194bc40 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -393,6 +393,7 @@ weston_view_create(struct weston_surface *surface) wl_list_insert(&surface->views, &view->surface_link); wl_signal_init(&view->destroy_signal); + wl_signal_init(&view->unmap_signal); wl_list_init(&view->link); wl_list_init(&view->layer_link.link); wl_list_init(&view->paint_node_list); @@ -2225,22 +2226,22 @@ weston_view_unmap(struct weston_view *view) 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); + if (!weston_surface_is_mapped(view->surface)) { + 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); + } } + weston_signal_emit_mutable(&view->unmap_signal, view); } WL_EXPORT void @@ -2306,6 +2307,10 @@ weston_surface_destroy(struct weston_surface *surface) struct weston_pointer_constraint *constraint, *next_constraint; struct weston_paint_node *pnode, *pntmp; + if (!surface) + return; + + assert(surface->ref_count > 0); if (--surface->ref_count > 0) return; @@ -4044,54 +4049,22 @@ static const struct wl_surface_interface surface_interface = { surface_damage_buffer }; -static int -create_surface_dmabuf_feedback(struct weston_compositor *ec, - struct weston_surface *surface) -{ - struct weston_dmabuf_feedback_tranche *tranche; - dev_t main_device = ec->default_dmabuf_feedback->main_device; - uint32_t flags = 0; - - surface->dmabuf_feedback = weston_dmabuf_feedback_create(main_device); - if (!surface->dmabuf_feedback) - return -1; - - tranche = weston_dmabuf_feedback_tranche_create(surface->dmabuf_feedback, - ec->dmabuf_feedback_format_table, - main_device, flags, - RENDERER_PREF); - if (!tranche) { - weston_dmabuf_feedback_destroy(surface->dmabuf_feedback); - surface->dmabuf_feedback = NULL; - return -1; - } - - return 0; -} - 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; - int ret; surface = weston_surface_create(ec); if (surface == NULL) goto err; - if (ec->default_dmabuf_feedback) { - ret = create_surface_dmabuf_feedback(ec, surface); - if (ret < 0) - goto err_dmabuf_feedback; - } - surface->resource = wl_resource_create(client, &wl_surface_interface, wl_resource_get_version(resource), id); if (surface->resource == NULL) - goto err_dmabuf_feedback; + goto err_res; wl_resource_set_implementation(surface->resource, &surface_interface, surface, destroy_surface); @@ -4099,7 +4072,7 @@ compositor_create_surface(struct wl_client *client, return; -err_dmabuf_feedback: +err_res: weston_surface_destroy(surface); err: wl_resource_post_no_memory(resource); @@ -4211,6 +4184,11 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub) &surface->pending.damage_surface); pixman_region32_clear(&surface->pending.damage_surface); + pixman_region32_union(&sub->cached.damage_buffer, + &sub->cached.damage_buffer, + &surface->pending.damage_buffer); + pixman_region32_clear(&surface->pending.damage_buffer); + if (surface->pending.newly_attached) { sub->cached.newly_attached = 1; weston_surface_state_set_buffer(&sub->cached, @@ -4233,8 +4211,6 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub) 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 = @@ -4361,7 +4337,7 @@ subsurface_committed(struct weston_surface *surface, int32_t dx, int32_t dy) */ if (!weston_surface_is_mapped(surface)) { - surface->is_mapped = true; + surface->is_mapped = surface->buffer_ref.buffer != NULL; /* Cannot call weston_view_update_transform(), * because that would call it also for the parent surface, diff --git a/libweston/input.c b/libweston/input.c index 6fb4bed3..ec25271a 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -3655,8 +3655,8 @@ enable_pointer_constraint(struct weston_pointer_constraint *constraint, 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); + wl_signal_add(&constraint->view->unmap_signal, + &constraint->view_unmap_listener); } static bool @@ -3671,6 +3671,8 @@ weston_pointer_constraint_disable(struct weston_pointer_constraint *constraint) constraint->view = NULL; pointer_constraint_notify_deactivated(constraint); weston_pointer_end_grab(constraint->grab.pointer); + wl_list_remove(&constraint->view_unmap_listener.link); + wl_list_init(&constraint->view_unmap_listener.link); } void @@ -3680,7 +3682,6 @@ weston_pointer_constraint_destroy(struct weston_pointer_constraint *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); @@ -3877,13 +3878,13 @@ pointer_constraint_pointer_destroyed(struct wl_listener *listener, void *data) } static void -pointer_constraint_surface_destroyed(struct wl_listener *listener, void *data) +pointer_constraint_view_unmapped(struct wl_listener *listener, void *data) { - struct weston_pointer_constraint *constraint = - container_of(listener, struct weston_pointer_constraint, - surface_destroy_listener); + struct weston_pointer_constraint *constraint = + container_of(listener, struct weston_pointer_constraint, + view_unmap_listener); - weston_pointer_constraint_destroy(constraint); + disable_pointer_constraint(constraint); } static void @@ -3947,8 +3948,8 @@ weston_pointer_constraint_create(struct weston_surface *surface, constraint->surface_activate_listener.notify = pointer_constraint_surface_activate; - constraint->surface_destroy_listener.notify = - pointer_constraint_surface_destroyed; + constraint->view_unmap_listener.notify = + pointer_constraint_view_unmapped; constraint->surface_commit_listener.notify = pointer_constraint_surface_committed; constraint->pointer_destroy_listener.notify = @@ -3958,8 +3959,6 @@ weston_pointer_constraint_create(struct weston_surface *surface, &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); @@ -4729,6 +4728,7 @@ maybe_warp_confined_pointer(struct weston_pointer_constraint *constraint) pixman_region32_intersect(&confine_region, &constraint->view->surface->input, &constraint->region); + assert(pixman_region32_not_empty(&confine_region)); region_to_outline(&confine_region, &borders); pixman_region32_fini(&confine_region); diff --git a/libweston/linux-dmabuf.c b/libweston/linux-dmabuf.c index 66702a4c..21de498d 100644 --- a/libweston/linux-dmabuf.c +++ b/libweston/linux-dmabuf.c @@ -684,6 +684,7 @@ weston_dmabuf_feedback_destroy(struct weston_dmabuf_feedback *dmabuf_feedback) wl_resource_for_each_safe(res, res_tmp, &dmabuf_feedback->resource_list) { wl_list_remove(wl_resource_get_link(res)); wl_list_init(wl_resource_get_link(res)); + wl_resource_set_user_data(res, NULL); } free(dmabuf_feedback); @@ -786,6 +787,7 @@ weston_dmabuf_feedback_send_all(struct weston_dmabuf_feedback *dmabuf_feedback, { struct wl_resource *res; + assert(!wl_list_empty(&dmabuf_feedback->resource_list)); wl_resource_for_each(res, &dmabuf_feedback->resource_list) weston_dmabuf_feedback_send(dmabuf_feedback, format_table, res, false); @@ -794,7 +796,16 @@ weston_dmabuf_feedback_send_all(struct weston_dmabuf_feedback *dmabuf_feedback, static void dmabuf_feedback_resource_destroy(struct wl_resource *resource) { + struct weston_surface *surface = + wl_resource_get_user_data(resource); + wl_list_remove(wl_resource_get_link(resource)); + + if (surface && + wl_list_empty(&surface->dmabuf_feedback->resource_list)) { + weston_dmabuf_feedback_destroy(surface->dmabuf_feedback); + surface->dmabuf_feedback = NULL; + } } static void @@ -810,7 +821,8 @@ zwp_linux_dmabuf_feedback_implementation = { static struct wl_resource * dmabuf_feedback_resource_create(struct wl_resource *dmabuf_resource, - struct wl_client *client, uint32_t dmabuf_feedback_id) + struct wl_client *client, uint32_t dmabuf_feedback_id, + struct weston_surface *surface) { struct wl_resource *dmabuf_feedback_res; uint32_t version; @@ -826,7 +838,7 @@ dmabuf_feedback_resource_create(struct wl_resource *dmabuf_resource, wl_list_init(wl_resource_get_link(dmabuf_feedback_res)); wl_resource_set_implementation(dmabuf_feedback_res, &zwp_linux_dmabuf_feedback_implementation, - NULL, dmabuf_feedback_resource_destroy); + surface, dmabuf_feedback_resource_destroy); return dmabuf_feedback_res; } @@ -842,7 +854,8 @@ linux_dmabuf_get_default_feedback(struct wl_client *client, dmabuf_feedback_resource = dmabuf_feedback_resource_create(dmabuf_resource, - client, dmabuf_feedback_id); + client, dmabuf_feedback_id, + NULL); if (!dmabuf_feedback_resource) { wl_resource_post_no_memory(dmabuf_resource); return; @@ -853,22 +866,55 @@ linux_dmabuf_get_default_feedback(struct wl_client *client, dmabuf_feedback_resource, true); } +static int +create_surface_dmabuf_feedback(struct weston_compositor *ec, + struct weston_surface *surface) +{ + struct weston_dmabuf_feedback_tranche *tranche; + dev_t main_device = ec->default_dmabuf_feedback->main_device; + uint32_t flags = 0; + + surface->dmabuf_feedback = weston_dmabuf_feedback_create(main_device); + if (!surface->dmabuf_feedback) + return -1; + + tranche = weston_dmabuf_feedback_tranche_create(surface->dmabuf_feedback, + ec->dmabuf_feedback_format_table, + main_device, flags, + RENDERER_PREF); + if (!tranche) { + weston_dmabuf_feedback_destroy(surface->dmabuf_feedback); + surface->dmabuf_feedback = NULL; + return -1; + } + + return 0; +} + static void linux_dmabuf_get_per_surface_feedback(struct wl_client *client, struct wl_resource *dmabuf_resource, uint32_t dmabuf_feedback_id, struct wl_resource *surface_resource) { + struct weston_compositor *compositor = + wl_resource_get_user_data(dmabuf_resource); struct weston_surface *surface = wl_resource_get_user_data(surface_resource); struct wl_resource *dmabuf_feedback_resource; + int ret; dmabuf_feedback_resource = dmabuf_feedback_resource_create(dmabuf_resource, - client, dmabuf_feedback_id); - if (!dmabuf_feedback_resource) { - wl_resource_post_no_memory(dmabuf_resource); - return; + client, dmabuf_feedback_id, + surface); + if (!dmabuf_feedback_resource) + goto err; + + if (!surface->dmabuf_feedback) { + ret = create_surface_dmabuf_feedback(compositor, surface); + if (ret < 0) + goto err_feedback; } /* Surface dma-buf feedback is dynamic and may need to be resent to @@ -879,6 +925,13 @@ linux_dmabuf_get_per_surface_feedback(struct wl_client *client, weston_dmabuf_feedback_send(surface->dmabuf_feedback, surface->compositor->dmabuf_feedback_format_table, dmabuf_feedback_resource, true); + return; + +err_feedback: + wl_resource_set_user_data(dmabuf_feedback_resource, NULL); + wl_resource_destroy(dmabuf_feedback_resource); +err: + wl_resource_post_no_memory(dmabuf_resource); } /** Get the linux_dmabuf_buffer from a wl_buffer resource diff --git a/libweston/renderer-gl/fragment.glsl b/libweston/renderer-gl/fragment.glsl index 63a20cd6..cfadb885 100644 --- a/libweston/renderer-gl/fragment.glsl +++ b/libweston/renderer-gl/fragment.glsl @@ -67,6 +67,9 @@ compile_const bool c_input_is_premult = DEF_INPUT_IS_PREMULT; compile_const bool c_green_tint = DEF_GREEN_TINT; compile_const int c_color_pre_curve = DEF_COLOR_PRE_CURVE; +compile_const bool c_need_color_pipeline = + c_color_pre_curve != SHADER_COLOR_CURVE_IDENTITY; + vec4 yuva2rgba(vec4 yuva) { @@ -202,9 +205,6 @@ color_pre_curve(vec3 color) vec4 color_pipeline(vec4 color) { - /* View alpha (opacity) */ - color.a *= alpha; - color.rgb = color_pre_curve(color.rgb); return color; @@ -218,18 +218,35 @@ main() /* Electrical (non-linear) RGBA values, may be premult or not */ color = sample_input_texture(); - /* Ensure straight alpha */ - if (c_input_is_premult) { - if (color.a == 0.0) - color.rgb = vec3(0, 0, 0); - else - color.rgb *= 1.0 / color.a; - } + if (c_need_color_pipeline) { + /* Ensure straight alpha */ + if (c_input_is_premult) { + if (color.a == 0.0) + color.rgb = vec3(0, 0, 0); + else + color.rgb *= 1.0 / color.a; + } - color = color_pipeline(color); + color = color_pipeline(color); - /* pre-multiply for blending */ - color.rgb *= color.a; + /* View alpha (opacity) */ + color.a *= alpha; + + /* pre-multiply for blending */ + color.rgb *= color.a; + } else { + /* Fast path for disabled color management */ + + if (c_input_is_premult) { + /* View alpha (opacity) */ + color *= alpha; + } else { + /* View alpha (opacity) */ + color.a *= alpha; + /* pre-multiply for blending */ + color.rgb *= color.a; + } + } if (c_green_tint) color = vec4(0.0, 0.3, 0.0, 0.2) + color * 0.8; diff --git a/meson.build b/meson.build index 7b80214d..8b3d46e7 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('weston', 'c', - version: '10.0.0', + version: '10.0.5', default_options: [ 'warning_level=3', 'c_std=gnu99', @@ -121,7 +121,7 @@ config_h.set10('TEST_GL_RENDERER', get_option('test-gl-renderer')) backend_default = get_option('backend-default') if backend_default == 'auto' - foreach b : [ 'headless', 'fbdev', 'x11', 'wayland', 'drm' ] + foreach b : [ 'headless', 'x11', 'wayland', 'drm' ] if get_option('backend-' + b) backend_default = b endif diff --git a/tests/devices-test.c b/tests/devices-test.c index 719f4595..e84273cc 100644 --- a/tests/devices-test.c +++ b/tests/devices-test.c @@ -36,6 +36,8 @@ fixture_setup(struct weston_test_harness *harness) compositor_setup_defaults(&setup); + setup.shell = SHELL_TEST_DESKTOP; + return weston_test_harness_execute_as_client(harness, &setup); } DECLARE_FIXTURE_SETUP(fixture_setup); diff --git a/tests/ivi-layout-test-plugin.c b/tests/ivi-layout-test-plugin.c index 75ce1436..8b8bdc8d 100644 --- a/tests/ivi-layout-test-plugin.c +++ b/tests/ivi-layout-test-plugin.c @@ -53,7 +53,7 @@ struct runner_test { static void runner_func_##name(struct test_context *); \ \ const struct runner_test runner_test_##name \ - __attribute__ ((section ("plugin_test_section"))) = \ + __attribute__ ((used, section ("plugin_test_section"))) = \ { \ #name, runner_func_##name \ }; \ diff --git a/tests/reference/subsurface_empty_mapping-00.png b/tests/reference/subsurface_empty_mapping-00.png new file mode 100644 index 00000000..f127154c Binary files /dev/null and b/tests/reference/subsurface_empty_mapping-00.png differ diff --git a/tests/reference/subsurface_empty_mapping-01.png b/tests/reference/subsurface_empty_mapping-01.png new file mode 100644 index 00000000..19c603cc Binary files /dev/null and b/tests/reference/subsurface_empty_mapping-01.png differ diff --git a/tests/reference/subsurface_sync_damage_buffer-00.png b/tests/reference/subsurface_sync_damage_buffer-00.png new file mode 100644 index 00000000..f127154c Binary files /dev/null and b/tests/reference/subsurface_sync_damage_buffer-00.png differ diff --git a/tests/reference/subsurface_sync_damage_buffer-01.png b/tests/reference/subsurface_sync_damage_buffer-01.png new file mode 100644 index 00000000..d3565281 Binary files /dev/null and b/tests/reference/subsurface_sync_damage_buffer-01.png differ diff --git a/tests/reference/subsurface_sync_damage_buffer-02.png b/tests/reference/subsurface_sync_damage_buffer-02.png new file mode 100644 index 00000000..9ef82e06 Binary files /dev/null and b/tests/reference/subsurface_sync_damage_buffer-02.png differ diff --git a/tests/subsurface-shot-test.c b/tests/subsurface-shot-test.c index 3bdc82dc..16534497 100644 --- a/tests/subsurface-shot-test.c +++ b/tests/subsurface-shot-test.c @@ -117,7 +117,7 @@ surface_commit_color(struct client *client, struct wl_surface *surface, buf = create_shm_buffer_a8r8g8b8(client, width, height); fill_image_with_color(buf->image, color); wl_surface_attach(surface, buf->proxy, 0, 0); - wl_surface_damage(surface, 0, 0, width, height); + wl_surface_damage_buffer(surface, 0, 0, width, height); wl_surface_commit(surface); return buf; @@ -213,3 +213,199 @@ TEST(subsurface_z_order) wl_subcompositor_destroy(subco); client_destroy(client); } + +TEST(subsurface_sync_damage_buffer) +{ + struct client *client; + struct wl_subcompositor *subco; + struct buffer *bufs[2] = { 0 }; + struct wl_surface *surf[2] = { 0 }; + struct wl_subsurface *sub[2] = { 0 }; + struct rectangle clip = { 40, 40, 280, 200 }; + int fail = 0; + unsigned i; + pixman_color_t red; + pixman_color_t blue; + pixman_color_t green; + + color_rgb888(&red, 255, 0, 0); + color_rgb888(&blue, 0, 0, 255); + color_rgb888(&green, 0, 255, 0); + + client = create_client_and_test_surface(100, 50, 100, 100); + assert(client); + subco = get_subcompositor(client); + + /* move the pointer clearly away from our screenshooting area */ + weston_test_move_pointer(client->test->weston_test, 0, 1, 0, 2, 30); + + /* make the parent surface red */ + surf[0] = client->surface->wl_surface; + client->surface->wl_surface = NULL; /* we stole it and destroy it */ + bufs[0] = surface_commit_color(client, surf[0], &red, 100, 100); + /* sub[0] is not used */ + + fail += check_screen(client, "subsurface_sync_damage_buffer", 0, &clip, 0); + + /* create a blue sub-surface above red */ + surf[1] = wl_compositor_create_surface(client->wl_compositor); + sub[1] = wl_subcompositor_get_subsurface(subco, surf[1], surf[0]); + bufs[1] = surface_commit_color(client, surf[1], &blue, 100, 100); + + wl_subsurface_set_position(sub[1], 20, 20); + wl_surface_commit(surf[0]); + + fail += check_screen(client, "subsurface_sync_damage_buffer", 1, &clip, 1); + + buffer_destroy(bufs[1]); + bufs[1] = surface_commit_color(client, surf[1], &green, 100, 100); + wl_surface_commit(surf[0]); + + fail += check_screen(client, "subsurface_sync_damage_buffer", 2, &clip, 2); + + assert(fail == 0); + + for (i = 0; i < ARRAY_LENGTH(sub); i++) + if (sub[i]) + wl_subsurface_destroy(sub[i]); + + for (i = 0; i < ARRAY_LENGTH(surf); i++) + if (surf[i]) + wl_surface_destroy(surf[i]); + + for (i = 0; i < ARRAY_LENGTH(bufs); i++) + if (bufs[i]) + buffer_destroy(bufs[i]); + + wl_subcompositor_destroy(subco); + client_destroy(client); +} + +TEST(subsurface_empty_mapping) +{ + struct client *client; + struct wl_subcompositor *subco; + struct wp_viewporter *viewporter; + struct buffer *bufs[3] = { 0 }; + struct wl_surface *surf[3] = { 0 }; + struct wl_subsurface *sub[3] = { 0 }; + struct wp_viewport *viewport; + struct rectangle clip = { 40, 40, 280, 200 }; + int fail = 0; + unsigned i; + pixman_color_t red; + pixman_color_t blue; + pixman_color_t green; + + color_rgb888(&red, 255, 0, 0); + color_rgb888(&blue, 0, 0, 255); + color_rgb888(&green, 0, 255, 0); + + client = create_client_and_test_surface(100, 50, 100, 100); + assert(client); + subco = get_subcompositor(client); + viewporter = bind_to_singleton_global(client, + &wp_viewporter_interface, 1); + + /* move the pointer clearly away from our screenshooting area */ + weston_test_move_pointer(client->test->weston_test, 0, 1, 0, 2, 30); + + /* make the parent surface red */ + surf[0] = client->surface->wl_surface; + client->surface->wl_surface = NULL; /* we stole it and destroy it */ + bufs[0] = surface_commit_color(client, surf[0], &red, 100, 100); + /* sub[0] is not used */ + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 0); + + /* create an empty subsurface on top */ + surf[1] = wl_compositor_create_surface(client->wl_compositor); + sub[1] = wl_subcompositor_get_subsurface(subco, surf[1], surf[0]); + wl_subsurface_set_desync (sub[1]); + + wl_subsurface_set_position(sub[1], 20, 20); + wl_surface_commit(surf[0]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 1); + + /* create a green subsurface on top */ + surf[2] = wl_compositor_create_surface(client->wl_compositor); + sub[2] = wl_subcompositor_get_subsurface(subco, surf[2], surf[1]); + wl_subsurface_set_desync (sub[2]); + bufs[2] = surface_commit_color(client, surf[2], &green, 100, 100); + + wl_subsurface_set_position(sub[2], 20, 20); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 2); + + wl_surface_attach(surf[1], NULL, 0, 0); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 3); + + wl_surface_set_buffer_scale (surf[1], 1); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 4); + + viewport = wp_viewporter_get_viewport(viewporter, surf[1]); + wp_viewport_set_destination(viewport, 5, 5); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 5); + + wp_viewport_set_destination(viewport, -1, -1); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 6); + + /* map the previously empty middle surface with a blue buffer */ + bufs[1] = surface_commit_color(client, surf[1], &blue, 100, 100); + + fail += check_screen(client, "subsurface_empty_mapping", 1, &clip, 7); + + /* try to trigger a recomputation of the buffer size with the + * shm-buffer potentially being released already */ + wl_surface_set_buffer_scale (surf[1], 1); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 1, &clip, 8); + + /* try more */ + wp_viewport_set_destination(viewport, 100, 100); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 1, &clip, 9); + + /* unmap the middle surface again to ensure recursive unmapping */ + wl_surface_attach(surf[1], NULL, 0, 0); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 10); + + /* remap middle surface to ensure recursive mapping */ + bufs[1] = surface_commit_color(client, surf[1], &blue, 100, 100); + + fail += check_screen(client, "subsurface_empty_mapping", 1, &clip, 11); + + assert(fail == 0); + + wp_viewport_destroy(viewport); + + for (i = 0; i < ARRAY_LENGTH(sub); i++) + if (sub[i]) + wl_subsurface_destroy(sub[i]); + + for (i = 0; i < ARRAY_LENGTH(surf); i++) + if (surf[i]) + wl_surface_destroy(surf[i]); + + for (i = 0; i < ARRAY_LENGTH(bufs); i++) + if (bufs[i]) + buffer_destroy(bufs[i]); + + wp_viewporter_destroy(viewporter); + wl_subcompositor_destroy(subco); + client_destroy(client); +}