Skip to content
Commits on Source (28)
  • Marius Vlad's avatar
    kiosk-shell: Check if app_ids have been set after initial commit · c8d638b3
    Marius Vlad authored
    
    
    Some applications would set-up the app_id after the initial commit
    (without a buffer) which is too late to correctly assign the application
    to the corresponding output set-up in the configuration file.
    
    This patch fixes that by checking one more time, after a buffer has been
    attached, if indeed there's an output with an app_id set.
    
    Fixes: #469
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit 8a1849db)
    c8d638b3
  • Marius Vlad's avatar
    kiosk-shell: Don't occlude shsurf on other outputs · 52c924ad
    Marius Vlad authored
    
    
    This adds an additional check to make sure the current focus surface
    is on the same output as the surface that is going to be activated.
    
    This is necessary in order to avoid placing the currently focused one in
    the inactive layer, which shouldn't happen in situations where the new
    surface is going to be placed on a different output than the currently
    focused one.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit f3ad5939)
    52c924ad
  • Marius Vlad's avatar
    kiosk-shell: Favor out views on same output · a08be338
    Marius Vlad authored
    
    
    In multiple output cases, finding the succesor from the inactive layer
    might result in picking the wrong view when there are multiple views
    being stacked in the inactive layer. This adds two additional checks to
    favor views on the same output as the one being destroyed/removed.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit f3221832)
    a08be338
  • Robert Mader's avatar
    libweston/compositor: Cache buffer damage for synced subsurfaces · 673943d7
    Robert Mader authored and Marius Vlad's avatar Marius Vlad committed
    The spec states:
    > Because buffer transformation changes and damage requests may be
    > interleaved in the protocol stream, it is impossible to determine
    > the actual mapping between surface and buffer damage until
    > wl_surface.commit time. Therefore, compositors wishing to take both
    > kinds of damage into account will have to accumulate damage from the
    > two requests separately and only transform from one to the other after
    > receiving the wl_surface.commit.
    
    For subsurfaces in sync mode, arguably the same is the case until the
    cached state gets applied eventually. Thus, in order to keep complexity
    to a sane level, just accumulate buffer damage and convert it only
    when the cached state gets applied.
    
    This mirrors how other compositors like Mutter implement cached damage
    and what the spec arguably should demand.
    
    Closes wayland/weston#446
    
    
    
    Signed-off-by: default avatarRobert Mader <robert.mader@collabora.com>
    (cherry picked from commit 933290e6)
    673943d7
  • Robert Mader's avatar
    tests: Add test for synced subsurfaces and buffer damage · 2fa690a9
    Robert Mader authored and Marius Vlad's avatar Marius Vlad committed
    
    
    Changing `wl_surface_damage()` to `wl_surface_damage_buffer()`
    should not have an effect on the existing tests.
    The new test will fail without the commit
    "libweston/compositor: Cache buffer damage for synced subsurfaces"
    
    Signed-off-by: default avatarRobert Mader <robert.mader@collabora.com>
    (cherry picked from commit dc3b3493)
    2fa690a9
  • Robert Mader's avatar
    libweston/compositor: Do not map subsurfaces without buffer · 7c30ab2d
    Robert Mader authored and Marius Vlad's avatar Marius Vlad committed
    We can end in `subsurface_committed()` in different scenarios
    without the surface having an attached buffer. While setting
    the mapped state to `true` in that case doesn't matter for
    that (sub)surface itself, it triggers its own child subsurfaces
    to get mapped when they shouldn't.
    
    Closes #426
    
    
    
    Signed-off-by: default avatarRobert Mader <robert.mader@collabora.com>
    (cherry picked from commit 8b04534c)
    7c30ab2d
  • Robert Mader's avatar
    tests: Add test for subsurfaces mapping hierachies · 7818b054
    Robert Mader authored and Marius Vlad's avatar Marius Vlad committed
    
    
    Test different scenarios where child subsurfaces of unmapped
    subsurfaces would get mapped. This test will fail in various
    ways without the commit
    "libweston/compositor: Do not map subsurfaces without buffer"
    
    Also try to test potential regressions of that patch.
    
    Signed-off-by: default avatarRobert Mader <robert.mader@collabora.com>
    (cherry picked from commit c83f0a15)
    7818b054
  • James Le Cuirot's avatar
    meson.build: Fix -Dbackend-default=auto following fbdev deprecation · a40ae688
    James Le Cuirot authored and Marius Vlad's avatar Marius Vlad committed
    
    
    Signed-off-by: default avatarJames Le Cuirot <chewi@gentoo.org>
    (cherry picked from commit 89587db3)
    a40ae688
  • Marius Vlad's avatar
    libweston: Assert if ref-count balance is wrong · e4522507
    Marius Vlad authored
    
    
    Calling weston_surface_destroy() one time too many could be a sign we
    haven't correctly increased the ref count for it.
    
    Also, if we don't have a surface being passed, do no attempt to
    use it.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit d3ed2eb3)
    e4522507
  • Marius Vlad's avatar
    libweston, desktop-shell: Add a wrapper for weston_surface reference · 8df487e0
    Marius Vlad authored
    
    
    Similar to how we do it with drm_fb ref counts, increase a reference
    count and return the same object.
    
    Plug-in in desktop-shell when we map up the view in order to survive a
    weston_surface destruction.
    
    Astute readers will notice that this patch removes weston_view_destroy()
    while keeping the balance between removing and adding a
    weston_surface_unref() call in desktop_shell_destroy_surface().
    
    The reason is to let weston_surface_unref() handle destruction on its
    own. If multiple references are taken, then weston_surface_unref()
    doesn't destroy the view, it just decreases the reference, with
    a latter call to weston_surface_unref() to determine if the view
    should be destroyed as well.  This situation happens if we have
    close animation enabled, were we have more than one reference taken: one
    when mapping the view/surface and when when the surface itself was created,
    (what we call, a weak reference).
    
    If only a single reference is taken (for instance if we don't have close
    animations enabled) then this weston_surface_unref()
    call is inert as that reference is not set-up, leaving libweston to
    handle the view destruction.
    
    Following that with a weston_view_destroy() explicit call would cause a
    UAF as the view was previous destroyed by a weston_surface_unref() call.
    
    A side-effect of not keeping the weston_view_destroy() call would
    happen when tearing down the compositor. If close animations are enabled,
    weston_surface_unref() would not destroy the view, and because
    weston_view_destroy() no longer exists, we would still have the
    view in the other layers by the time we check-up if layers
    have views present.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit bd831407)
    
    While the original patch was adding a weston_surface_ref() wrapper, this
    patch doesn't add any wrapper to avoid a minor version bump, but instead
    achieves a similar outcome by inlining the wrapper version being added
    by the original commit.
    
    Further more, as the original patch was dependent on another patch,
    any mention of weston_surface_unref() in the commit description
    should instead be replaced with weston_surface_destroy().
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    8df487e0
  • Marius Vlad's avatar
    desktop-shell: Create a distinct view for the fade-out close anim · e34bb982
    Marius Vlad authored
    
    
    Creates a distinct view, separated from the one created by
    libweston-desktop, in order to avoid a potential ownership fight with
    libweston-desktop upon destroying the view. Upon weston_desktop_surface
    destruction, libweston-desktop inflicts damage on the view it creates,
    so we need the view to be alive at that time.
    
    This wasn't such an issue before because we had different destruction paths but
    with commit 'desktop-shell: Do not leave views in layers upon shell
    destruction' all of the destruction paths now land in the same spot
    + handle compositor tear down.
    
    Note as we still use the same weston_surface we'll keep the previous
    construct where we were taking a reference to keep it alive.
    
    The original view is destroyed when releasing the ownership, while for
    the view created in this patch we handle the destruction directly upon
    compositor tear down.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit 9cf60284)
    e34bb982
  • Marius Vlad's avatar
    desktop-shell: Rename destroy_layer functions · 1d95c099
    Marius Vlad authored
    
    
    No functional change. Makes it obvious that we also call
    weston_layer_fini().
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit be5b6f9c)
    1d95c099
  • Marius Vlad's avatar
    desktop-shell: Migrate surface_unlink_view · 8c9dff01
    Marius Vlad authored
    
    
    Moving weston_desktop_surface_unlink_view() to
    desktop_shell_destroy_surface() makes sure we don't leak the underlying
    weston_desktop_view when tearing/shutting down the compositor.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit c41cdcab)
    8c9dff01
  • Marius Vlad's avatar
    desktop-shell: Check for a valid desktop_surface · 975e03b8
    Marius Vlad authored
    
    
    This patch fixes the following trace:
    
        #0 0x7f07d1bcecfa in weston_desktop_surface_get_surface ../libweston-desktop/surface.c:585
        #1 0x7f07d1bfc9b8 in move_grab_motion ../desktop-shell/shell.c:1499
        #2 0x7f07e539f841 in notify_motion ../libweston/input.c:1794
        #3 0x7f07e1e8ace4 in handle_pointer_motion ../libweston/libinput-device.c:132
        #4 0x7f07e1e8cad5 in evdev_device_process_event ../libweston/libinput-device.c:535
        #5 0x7f07e1e89311 in udev_input_process_event ../libweston/libinput-seat.c:208
        #6 0x7f07e1e8932f in process_event ../libweston/libinput-seat.c:218
        #7 0x7f07e1e8935f in process_events ../libweston/libinput-seat.c:228
        #8 0x7f07e1e8940a in udev_input_dispatch ../libweston/libinput-seat.c:239
        #9 0x7f07e1e89437 in libinput_source_dispatch ../libweston/libinput-seat.c:249
        #10 0x7f07e53122b1 in wl_event_loop_dispatch ../src/event-loop.c:1027
        #11 0x7f07e5310114 in wl_display_run ../src/wayland-server.c:1408
        #12 0x7f07e579c7ba in wet_main ../compositor/main.c:3589
        #13 0x555611d6b17d in main ../compositor/executable.c:33
        #14 0x7f07e55be7fc in __libc_start_main ../csu/libc-start.c:332
        #15 0x555611d6b099 in _start (/home/mvlad/install-amd64/bin/weston+0x1099)
    
    A highly unlikely, but still valid operation, is to close/destroy the
    window while still having it grabbed and moved around, basically having
    an in-flight destruction of grabbed moving window. Another situation
    would be that the client terminates abruptly (crashing for instance),
    while being dragged which might take down the compositor.
    
    This could happen for both touch/pointer grab operations and could cause
    a NULL pointer access while accessing desktop_surface when being used
    to retrieve the underlying weston_surface.
    
    With this patch we check for a valid desktop_surface and return early
    if that's not the case.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit d03f0137)
    975e03b8
  • Marius Vlad's avatar
    desktop-shell: Clarify weston_view destruction at tear down · 41864644
    Marius Vlad authored
    
    
    This documents the fact that other views are handled implictly by
    libweston-desktop, and we shouldn't attempt to destroy indiscriminately
    views that aren't created by desktop-shell.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit 299f87f0)
    41864644
  • Marius Vlad's avatar
    desktop-shell: Add missing weston_view_destroy() · ec999796
    Marius Vlad authored
    
    
    This fixes the following weston_view leak at compositor shutdown:
    
        #0 0x7f4250247987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154
        #1 0x7f424fd37aa3 in zalloc ../include/libweston/zalloc.h:38
        #2 0x7f424fd3a05f in weston_view_create ../libweston/compositor.c:386
        #3 0x7f423be7be6a in weston_desktop_surface_create_desktop_view ../libweston-desktop/surface.c:364
        #4 0x7f423be7c0a8 in weston_desktop_surface_create_view ../libweston-desktop/surface.c:404
        #5 0x7f423beae91d in desktop_surface_added ../desktop-shell/shell.c:2273
        #6 0x7f423be77db1 in weston_desktop_api_surface_added ../libweston-desktop/libweston-desktop.c:138
        #7 0x7f423be80c73 in weston_desktop_xdg_toplevel_ensure_added ../libweston-desktop/xdg-shell.c:362
        #8 0x7f423be8207a in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:697
        #9 0x7f423be84d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374
        #10 0x7f423be7b382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174
        #11 0x7f424fd378a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481
        #12 0x7f424fd510e2 in weston_surface_commit_state ../libweston/compositor.c:4062
        #13 0x7f424fd51161 in weston_surface_commit ../libweston/compositor.c:4068
        #14 0x7f424fd516ef in surface_commit ../libweston/compositor.c:4146
        #15 0x7f424fb597e9  (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9)
    
    With commit 'libweston, desktop-shell: Add a wrapper for weston_surface
    reference' we've removed an explicit weston_view_destroy() call due to a
    UAF which would've happen if we had close animations enabled, upon
    terminating a client. In that patch I've incorrectly wrote this happened
    when animations are off, but in fact it happened when they're on, see the
    following trace:
    
    READ of size 8 at 0x616000026498 thread T0
        #0 0x7f757fba8797 in weston_signal_emit_mutable ../shared/signal.c:52
        #1 0x7f757fb4bba1 in weston_view_destroy ../libweston/compositor.c:2269
        #2 0x7f756bca89c0 in desktop_shell_destroy_surface ../desktop-shell/shell.c:275
        #3 0x7f756bcb379e in fade_out_done_idle_cb ../desktop-shell/shell.c:2228
        #4 0x7f757faec1da in wl_event_loop_dispatch_idle ../src/event-loop.c:969
        #5 0x7f757faec31d in wl_event_loop_dispatch ../src/event-loop.c:1032
        #6 0x7f757faea114 in wl_display_run ../src/wayland-server.c:1408
        #7 0x7f757ff777ba in wet_main ../compositor/main.c:3589
        #8 0x55f765c8d17d in main ../compositor/executable.c:33
        #9 0x7f757fd997fc in __libc_start_main ../csu/libc-start.c:332
        #10 0x55f765c8d099 in _start (/home/mvlad/install-amd64/bin/weston+0x1099)
    
    0x616000026498 is located 24 bytes inside of 608-byte region [0x616000026480,0x6160000266e0)
    freed by thread T0 here:
        #0 0x7f758004c4d7 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127
        #1 0x7f757fb4bdc8 in weston_view_destroy ../libweston/compositor.c:2295
        #2 0x7f757fb4c14d in weston_surface_unref ../libweston/compositor.c:2334
        #3 0x7f756bca898b in desktop_shell_destroy_surface ../desktop-shell/shell.c:273
        #4 0x7f756bcb379e in fade_out_done_idle_cb ../desktop-shell/shell.c:2228
        #5 0x7f757faec1da in wl_event_loop_dispatch_idle ../src/event-loop.c:969
    
    This patch re-introduces it to avoid leaking the view upon compositor
    shutdown, but it does it in tandem with weston_desktop_surface_unlink_view(),
    (which was added in a later patch) and before weston_surface_unref() call.
    
    This way we should be safe to terminate/close  clients with additional views
    created by libweston-desktop (pop-ups), but also in other different situations.
    
    Verified it in the following circumstances:
    
    - terminating a client with close animation on
    - terminating a client with close animations off
    - shutting down the compositor with clients running, with and
      without close animations
    - terminating top-level clients with additional pop-ups with both with
      and without close animations
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit ab42159b)
    ec999796
  • Marius Vlad's avatar
    simple-egl: Add start as maximized · b58344b2
    Marius Vlad authored
    
    
    Just like start as fullscreen, let us add a start as maximized as well.
    It tests out the maximized state and with clients geometry checks.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit 01ef3746)
    b58344b2
  • Marius Vlad's avatar
    libweston-desktop: Replace buffer with geometry · a158b435
    Marius Vlad authored
    
    
    A previous patch modified this for fullscreen, but missed out the
    maximized state. As we check the geometry rather than the buffer
    dimensions use the same terminology.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit cc877d4b)
    a158b435
  • Marius Vlad's avatar
    simple-egl: Remove uneeded check · 870abb15
    Marius Vlad authored
    
    
    display->wm_base is checked right after handling registry object, and
    with it the globals, so there's no to perform and additional check for
    xwm_base.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    (cherry picked from commit c15699b7)
    870abb15
  • Marius Vlad's avatar
    simple-egl: Defer EGL surface/window creation · ac83c29e
    Marius Vlad authored
    
    
    Rather than creating the wl_egl_window at the same time as wl_surface,
    do it after we get the first configure event.
    
    With it, we also defer eglMakeCurrent() as according to the spec, the
    first time a OpenGL or OpenGL ES context is made current, the viewport
    and scissor dimensions are set to the size of the draw surface.
    
    This is particulary important when attempting to start simple-egl either
    as fullscreen or as maximized, as not doing so will either incorrectly
    commit a buffer with the original dimensions, and later on to resize to
    the correct dimensions (which is the case for fullscreen), or it will
    terminate the wayland connection abruptly due to xdg-shell protocol
    violation, with a mismatch for the client's geometry (the case for
    maximized).
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    Suggested-by: default avatarDaniel Stone <daniel.stone@collabora.com>
    (cherry picked from commit 0277046a)
    ac83c29e
  • Marius Vlad's avatar
    simple-egl: Move set_fullscreen/set_maximized before initial commit · f573612d
    Marius Vlad authored
    
    
    Rather than setting the fullscreen/maximized before initial
    wl_surface.commit, make it part of the initial window state.
    
    Signed-off-by: default avatarMarius Vlad <marius.vlad@collabora.com>
    Suggested-by: default avatarPekka Paalanen <pekka.paalanen@collabora.com>
    (cherry picked from commit 054aaa5a)
    f573612d
  • Leandro Ribeiro's avatar
    clients/simple-dmabuf-feedback: do not use buffer before compositor's response · d2d5c666
    Leandro Ribeiro authored and Marius Vlad's avatar Marius Vlad committed
    
    
    This fixes an issue when running simple-dmabuf-feedback:
    "wl_display@1: error 1: invalid arguments for wl_surface@3.attach".
    
    As we are not using create_immed request from zwp_linux_dmabuf_v1, we
    can't start to use a dma-buf buffer before we process compositor's event
    telling us that the creation succeeded.
    
    This was causing problems in the following scenario:
    
    1. buffer is marked to be recreated (because of dma-buf feedback);
    2. in buffer_release() event, we destroy the buffer and recreate it;
    3. after we recreate it, roundtrip is not called, as we don't want to
       block during the drawing loop;
    4. buffer status is not being properly tracked, so we are trying to
       use a buffer before receiving the event from the compositor telling
       us that the creation succeeded.
    
    To fix this, this patch improves buffer status tracking. Now we only
    pick a buffer in the drawing loop when it is available. Also, if we have
    no buffers available we perform a roundtrip and try again, as we may
    have recreated all of them but still didn't have the chance to process
    compositor's events telling us that creation succeeded.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    (cherry picked from commit 7724c5ea)
    d2d5c666
  • Robert Mader's avatar
    clients/simple-dmabuf-feedback: Support multi-tranche feedbacks · 89c965eb
    Robert Mader authored and Marius Vlad's avatar Marius Vlad committed
    
    
    Compositors may choose to send multiple scanout or non-scanout
    tranches. So instead of assuming that the first respective tranche
    contains the format/modifier we're looking for, check all tranches.
    
    While on it, make sure that in case a compositor sends scanout
    tranches on the initial feedback, `pick_format_from_scanout_tranche()`
    does not unintentionally pick `INITIAL_BUFFER_FORMAT`.
    
    Signed-off-by: default avatarRobert Mader <robert.mader@collabora.com>
    (cherry picked from commit 46a6b5b4)
    89c965eb
  • Robert Mader's avatar
    clients/simple-dmabuf-*: Increase buffer limit to four · f5d5114d
    Robert Mader authored and Marius Vlad's avatar Marius Vlad committed
    
    
    In certain situations these clients crash a lot due to the low
    buffer limit. Four buffers is also what EGL allows without blocking
    and what is arguably the upper limit of what a compositor should
    demand.
    
    Signed-off-by: default avatarRobert Mader <robert.mader@collabora.com>
    (cherry picked from commit 3e6ef529)
    f5d5114d
  • Robert Mader's avatar
    clients/simple-dmabuf-feedback: Add fallback print method for unknown formats · 7b390f1d
    Robert Mader authored and Marius Vlad's avatar Marius Vlad committed
    
    
    Using `pixel_format_get_info()` can result in formats being
    reported as `UNKNOWN` when used on compositors other than Weston.
    
    As `weston-simple-dmabuf-feedback` somewhat succeeds `wayland-info`
    as tool for `zwp_linux_dmabuf_v1` debugging from version 4 on, copy
    the approach from the later for these cases.
    
    Signed-off-by: default avatarRobert Mader <robert.mader@collabora.com>
    (cherry picked from commit 26698535)
    7b390f1d
  • Robert Mader's avatar
    backend-drm: Add failure reasons for failing gbm_bo_import · 35c6a4b8
    Robert Mader authored and Marius Vlad's avatar Marius Vlad committed
    And add it to the list of failures triggering a resend of
    dmabuf feedback scanout tranches.
    
    Closes wayland/weston#614
    
    
    
    Signed-off-by: default avatarRobert Mader <robert.mader@collabora.com>
    (cherry picked from commit 29d48081)
    35c6a4b8
  • Robert Mader's avatar
    clients/simple-dmabuf-feedback: use time instead of redraws · 01af0e4e
    Robert Mader authored and Marius Vlad's avatar Marius Vlad committed
    
    
    Weston uses a timeout of 2 seconds before it sends scanout
    tranches to clients in order to not trigger excessive buffer
    reallocations in clients.
    `simple-dmabuf-feedback` in turn counts redraws (200) before
    exiting. That doesn't work on e.g. 144Hz screens, thus use a
    timer here as well.
    
    Signed-off-by: default avatarRobert Mader <robert.mader@collabora.com>
    (cherry picked from commit 34f7e01c)
    01af0e4e
  • Robert Mader's avatar
    libweston/linux-dmabuf: create surface feedback on demand · f7c5fa17
    Robert Mader authored and Marius Vlad's avatar Marius Vlad committed
    
    
    Unconditionally creating a surface feedback for each surface
    creates unnecessary overhead and noise in the logs. Thus
    create it when the first surface feedback resource for a
    surface is requested and delete it again once all those
    resources have been destroyed.
    
    Signed-off-by: default avatarRobert Mader <robert.mader@collabora.com>
    (cherry picked from commit 53a221cc)
    f7c5fa17
......@@ -131,7 +131,7 @@ struct buffer {
int release_fence_fd;
};
#define NUM_BUFFERS 3
#define NUM_BUFFERS 4
struct window {
struct display *display;
......
......@@ -26,11 +26,13 @@
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <libudev.h>
#include <sys/mman.h>
#include <time.h>
#include "shared/helpers.h"
#include "shared/platform.h"
......@@ -47,7 +49,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#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, &current_time);
delta_time = current_time.tv_sec - start_time.tv_sec;
}
destroy_window(window);
destroy_display(display);
......
......@@ -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;
......
......@@ -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 <us>\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");
......
......@@ -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);
......
......@@ -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(&current_focus->view->layer_link);
weston_layer_entry_insert(&shsurf->shell->inactive_layer.view_list,
&current_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?
......
......@@ -73,6 +73,8 @@ struct kiosk_shell_surface {
int32_t x;
int32_t y;
} xwayland;
bool appid_output_assigned;
};
struct kiosk_shell_seat {
......
......@@ -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,
......
......@@ -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)
};
/**
......
......@@ -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;
......
......@@ -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 |
......
......@@ -2306,6 +2306,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 +4048,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 +4071,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 +4183,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 +4210,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 +4336,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,
......
......@@ -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
......
......@@ -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
......
......@@ -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);
}