weston/desktop-shell/shell.c

4987 lines
134 KiB
C
Raw Normal View History

/*
* Copyright © 2010-2012 Intel Corporation
* Copyright © 2011-2012 Collabora, Ltd.
* Copyright © 2013 Raspberry Pi Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
2011-04-12 21:25:42 +00:00
#include <linux/input.h>
#include <assert.h>
#include <signal.h>
#include <math.h>
#include <sys/types.h>
#include "shell.h"
#include "compositor/weston.h"
#include "weston-desktop-shell-server-protocol.h"
#include <libweston/config-parser.h>
#include "shared/helpers.h"
#include "shared/timespec-util.h"
#include <libweston/shell-utils.h>
#include <libweston/desktop.h>
#define DEFAULT_NUM_WORKSPACES 1
#define DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH 200
struct focus_state {
struct desktop_shell *shell;
struct weston_seat *seat;
struct workspace *ws;
struct weston_surface *keyboard_focus;
struct wl_list link;
struct wl_listener seat_destroy_listener;
struct wl_listener surface_destroy_listener;
};
/*
* Surface stacking and ordering.
*
* This is handled using several linked lists of surfaces, organised into
* layers. The layers are ordered, and each of the surfaces in one layer are
* above all of the surfaces in the layer below. The set of layers is static and
* in the following order (top-most first):
* Lock layer (only ever displayed on its own)
* Cursor layer
* Input panel layer
* Fullscreen layer
* Panel layer
* Workspace layers
* Background layer
*
* The list of layers may be manipulated to remove whole layers of surfaces from
* display. For example, when locking the screen, all layers except the lock
* layer are removed.
*
* A surfaces layer is modified on configuring the surface, in
* set_surface_type() (which is only called when the surfaces type change is
* _committed_). If a surfaces type changes (e.g. when making a window
* fullscreen) its layer changes too.
*
* In order to allow popup and transient surfaces to be correctly stacked above
* their parent surfaces, each surface tracks both its parent surface, and a
* linked list of its children. When a surfaces layer is updated, so are the
* layers of its children. Note that child surfaces are *not* the same as
* subsurfaces child/parent surfaces are purely for maintaining stacking
* order.
*
* The children_link list of siblings of a surface (i.e. those surfaces which
* have the same parent) only contains weston_surfaces which have a
* shell_surface. Stacking is not implemented for non-shell_surface
* weston_surfaces. This means that the following implication does *not* hold:
* (shsurf->parent != NULL) !wl_list_is_empty(shsurf->children_link)
*/
struct shell_surface {
struct wl_signal destroy_signal;
struct weston_desktop_surface *desktop_surface;
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
struct weston_view *view;
libweston, desktop-shell: Add a wrapper for weston_surface reference 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: Marius Vlad <marius.vlad@collabora.com>
2022-02-14 20:42:22 +00:00
struct weston_surface *wsurface_anim_fade;
struct weston_view *wview_anim_fade;
int32_t last_width, last_height;
struct desktop_shell *shell;
struct wl_list children_list;
struct wl_list children_link;
struct weston_coord_global saved_pos;
bool saved_position_valid;
bool saved_rotation_valid;
int unresponsive, grabbed;
uint32_t resize_edges;
uint32_t orientation;
struct {
struct weston_transform transform;
struct weston_matrix rotation;
} rotation;
struct {
struct weston_curtain *black_view;
} fullscreen;
struct weston_output *fullscreen_output;
struct weston_output *output;
struct wl_listener output_destroy_listener;
struct surface_state {
bool fullscreen;
bool maximized;
bool lowered;
} state;
struct {
bool is_set;
struct weston_coord_global pos;
} xwayland;
int focus_count;
bool destroying;
struct wl_list link; /** desktop_shell::shsurf_list */
};
struct shell_grab {
struct weston_pointer_grab grab;
struct shell_surface *shsurf;
struct wl_listener shsurf_destroy_listener;
};
struct shell_touch_grab {
struct weston_touch_grab grab;
struct shell_surface *shsurf;
struct wl_listener shsurf_destroy_listener;
struct weston_touch *touch;
};
struct shell_tablet_tool_grab {
struct weston_tablet_tool_grab grab;
struct shell_surface *shsurf;
struct wl_listener shsurf_destroy_listener;
struct weston_tablet_tool *tool;
};
struct weston_move_grab {
struct shell_grab base;
struct weston_coord_global delta;
bool client_initiated;
};
struct weston_touch_move_grab {
struct shell_touch_grab base;
int active;
struct weston_coord_global delta;
};
struct weston_tablet_tool_move_grab {
struct shell_tablet_tool_grab base;
wl_fixed_t dx, dy;
};
struct rotate_grab {
struct shell_grab base;
struct weston_matrix rotation;
struct {
2012-09-30 00:57:21 +00:00
float x;
float y;
} center;
};
struct shell_seat {
struct weston_seat *seat;
struct wl_listener seat_destroy_listener;
struct weston_surface *focused_surface;
struct wl_listener caps_changed_listener;
struct wl_listener pointer_focus_listener;
struct wl_listener keyboard_focus_listener;
struct wl_listener tablet_tool_added_listener;
struct wl_list link; /** shell::seat_list */
};
struct tablet_tool_listener {
struct wl_listener base;
struct wl_listener removed_listener;
};
static struct desktop_shell *
shell_surface_get_shell(struct shell_surface *shsurf);
static void
set_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer);
static void
surface_rotate(struct shell_surface *surface, struct weston_pointer *pointer);
static void
shell_fade_startup(struct desktop_shell *shell);
static void
shell_fade(struct desktop_shell *shell, enum fade_type type);
static struct shell_seat *
get_shell_seat(struct weston_seat *seat);
static void
shell_surface_update_child_surface_layers(struct shell_surface *shsurf);
static void
get_maximized_size(struct shell_surface *shsurf, int32_t *width, int32_t *height);
static struct shell_output *
find_shell_output_from_weston_output(struct desktop_shell *shell,
struct weston_output *output)
{
struct shell_output *shell_output;
wl_list_for_each(shell_output, &shell->output_list, link) {
if (shell_output->output == output)
return shell_output;
}
return NULL;
}
static bool
shsurf_is_max_or_fullscreen(struct shell_surface *shsurf)
{
struct weston_desktop_surface *dsurface = shsurf->desktop_surface;
return weston_desktop_surface_get_maximized(dsurface) ||
weston_desktop_surface_get_fullscreen(dsurface);
}
/*
* helper to take into account panels and send the appropriate dimensions
*/
static void
set_shsurf_size_maximized_or_fullscreen(struct shell_surface *shsurf,
bool max_requested,
bool fullscreen_requested)
{
int width = 0; int height = 0;
if (fullscreen_requested) {
if (shsurf->output) {
width = shsurf->output->width;
height = shsurf->output->height;
}
}
/* take the panels into considerations */
if (max_requested)
get_maximized_size(shsurf, &width, &height);
/* (0, 0) means we're back from one of the maximized/fullcreen states */
weston_desktop_surface_set_size(shsurf->desktop_surface, width, height);
}
static void
destroy_shell_grab_shsurf(struct wl_listener *listener, void *data)
{
struct shell_grab *grab;
grab = container_of(listener, struct shell_grab,
shsurf_destroy_listener);
grab->shsurf = NULL;
}
struct weston_view *
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
get_default_view(struct weston_surface *surface)
{
struct shell_surface *shsurf;
struct weston_view *view;
if (!surface || wl_list_empty(&surface->views))
return NULL;
shsurf = get_shell_surface(surface);
if (shsurf)
return shsurf->view;
wl_list_for_each(view, &surface->views, surface_link)
if (weston_view_is_mapped(view))
return view;
return container_of(surface->views.next, struct weston_view, surface_link);
}
static void
desktop_shell_destroy_surface(struct shell_surface *shsurf)
{
struct shell_surface *shsurf_child, *tmp;
desktop-shell: Handle weston_curtain destruction This fixes the following leaks for weston_curtain/weston_buffer_create_solid_rgba when shutting down the compositor: #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f915bfeb8b7 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f915bfec71d in weston_curtain_create ../shell-utils/shell-utils.c:150 #3 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #4 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #5 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #6 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #7 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #8 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #9 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #10 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #11 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #12 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #13 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #14 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f916fe62aa3 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f916fe7398d in weston_buffer_create_solid_rgba ../libweston/compositor.c:2603 #3 0x7f915bfec879 in weston_curtain_create ../shell-utils/shell-utils.c:162 #4 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #5 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #6 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #7 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #8 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #9 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #10 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #11 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #12 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #13 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #14 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #15 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) We do not migrate the weston_curtain destruction from desktop_surface_removed() to desktop_shell_destroy_surface() because we'd want have the curtain removed before the animation starts. If we were to move the black view destruction, *and* only handle it from desktop_shell_destroy_surface() the animation runs but the black curtain will be removed right at the end, effectively diminishing the effect of the animations. To this end, we keep both of the two worlds, if the client terminates on its own, we keep the same animation effect, but if the compositor is shutting down we destroy it immediately. We remove wl_list_for_each_safe() and instead loop each time to avoid using a stale pointer iterator which could cause a UAF as the shsurf would be free'ed. Suggested-by: Pekka Paalanen <pekka.paalanen@collabora.com> Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
2022-05-17 14:49:35 +00:00
if (shsurf->fullscreen.black_view)
weston_shell_utils_curtain_destroy(shsurf->fullscreen.black_view);
desktop-shell: Handle weston_curtain destruction This fixes the following leaks for weston_curtain/weston_buffer_create_solid_rgba when shutting down the compositor: #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f915bfeb8b7 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f915bfec71d in weston_curtain_create ../shell-utils/shell-utils.c:150 #3 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #4 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #5 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #6 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #7 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #8 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #9 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #10 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #11 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #12 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #13 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #14 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f916fe62aa3 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f916fe7398d in weston_buffer_create_solid_rgba ../libweston/compositor.c:2603 #3 0x7f915bfec879 in weston_curtain_create ../shell-utils/shell-utils.c:162 #4 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #5 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #6 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #7 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #8 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #9 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #10 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #11 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #12 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #13 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #14 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #15 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) We do not migrate the weston_curtain destruction from desktop_surface_removed() to desktop_shell_destroy_surface() because we'd want have the curtain removed before the animation starts. If we were to move the black view destruction, *and* only handle it from desktop_shell_destroy_surface() the animation runs but the black curtain will be removed right at the end, effectively diminishing the effect of the animations. To this end, we keep both of the two worlds, if the client terminates on its own, we keep the same animation effect, but if the compositor is shutting down we destroy it immediately. We remove wl_list_for_each_safe() and instead loop each time to avoid using a stale pointer iterator which could cause a UAF as the shsurf would be free'ed. Suggested-by: Pekka Paalanen <pekka.paalanen@collabora.com> Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
2022-05-17 14:49:35 +00:00
wl_list_for_each_safe(shsurf_child, tmp, &shsurf->children_list, children_link) {
wl_list_remove(&shsurf_child->children_link);
wl_list_init(&shsurf_child->children_link);
}
wl_list_remove(&shsurf->children_link);
weston_desktop_surface_unlink_view(shsurf->view);
wl_list_remove(&shsurf->link);
desktop-shell: Add missing weston_view_destroy() 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: Marius Vlad <marius.vlad@collabora.com>
2022-05-13 17:11:46 +00:00
weston_view_destroy(shsurf->view);
wl_signal_emit(&shsurf->destroy_signal, shsurf);
libweston, desktop-shell: Add a wrapper for weston_surface reference 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: Marius Vlad <marius.vlad@collabora.com>
2022-02-14 20:42:22 +00:00
weston_surface_unref(shsurf->wsurface_anim_fade);
if (shsurf->output_destroy_listener.notify) {
wl_list_remove(&shsurf->output_destroy_listener.link);
shsurf->output_destroy_listener.notify = NULL;
}
free(shsurf);
}
static void
shell_grab_start(struct shell_grab *grab,
const struct weston_pointer_grab_interface *interface,
struct shell_surface *shsurf,
struct weston_pointer *pointer,
enum weston_desktop_shell_cursor cursor)
{
struct desktop_shell *shell = shsurf->shell;
weston_seat_break_desktop_grabs(pointer->seat);
grab->grab.interface = interface;
grab->shsurf = shsurf;
grab->shsurf_destroy_listener.notify = destroy_shell_grab_shsurf;
wl_signal_add(&shsurf->destroy_signal,
&grab->shsurf_destroy_listener);
shsurf->grabbed = 1;
weston_pointer_start_grab(pointer, &grab->grab);
if (shell->child.desktop_shell) {
weston_desktop_shell_send_grab_cursor(shell->child.desktop_shell,
cursor);
weston_pointer_set_focus(pointer,
get_default_view(shell->grab_surface));
}
}
void
get_output_work_area(struct desktop_shell *shell,
struct weston_output *output,
pixman_rectangle32_t *area)
{
struct shell_output *sh_output;
area->x = 0;
area->y = 0;
area->width = 0;
area->height = 0;
if (!output)
return;
sh_output = find_shell_output_from_weston_output(shell, output);
assert(sh_output);
area->x = output->pos.c.x;
area->y = output->pos.c.y;
area->width = output->width;
area->height = output->height;
if (!sh_output->panel_view ||
!weston_view_is_mapped(sh_output->panel_view)) {
return;
}
switch (shell->panel_position) {
case WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP:
area->y += sh_output->panel_surface->height;
/* fallthrough */
case WESTON_DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
area->height -= sh_output->panel_surface->height;
break;
case WESTON_DESKTOP_SHELL_PANEL_POSITION_LEFT:
area->x += sh_output->panel_surface->width;
/* fallthrough */
case WESTON_DESKTOP_SHELL_PANEL_POSITION_RIGHT:
area->width -= sh_output->panel_surface->width;
break;
default:
unreachable("unknown panel position");
}
}
static void
shell_grab_end(struct shell_grab *grab)
{
if (grab->shsurf) {
wl_list_remove(&grab->shsurf_destroy_listener.link);
grab->shsurf->grabbed = 0;
if (grab->shsurf->resize_edges) {
grab->shsurf->resize_edges = 0;
}
}
weston_pointer_end_grab(grab->grab.pointer);
}
static void
shell_touch_grab_start(struct shell_touch_grab *grab,
const struct weston_touch_grab_interface *interface,
struct shell_surface *shsurf,
struct weston_touch *touch)
{
struct desktop_shell *shell = shsurf->shell;
weston_seat_break_desktop_grabs(touch->seat);
grab->grab.interface = interface;
grab->shsurf = shsurf;
grab->shsurf_destroy_listener.notify = destroy_shell_grab_shsurf;
wl_signal_add(&shsurf->destroy_signal,
&grab->shsurf_destroy_listener);
grab->touch = touch;
shsurf->grabbed = 1;
weston_touch_start_grab(touch, &grab->grab);
if (shell->child.desktop_shell)
weston_touch_set_focus(touch,
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
get_default_view(shell->grab_surface));
}
static void
shell_touch_grab_end(struct shell_touch_grab *grab)
{
if (grab->shsurf) {
wl_list_remove(&grab->shsurf_destroy_listener.link);
grab->shsurf->grabbed = 0;
}
weston_touch_end_grab(grab->touch);
}
static void
shell_tablet_tool_grab_start(struct shell_tablet_tool_grab *grab,
const struct weston_tablet_tool_grab_interface *interface,
struct shell_surface *shsurf,
struct weston_tablet_tool *tool)
{
struct desktop_shell *shell = shsurf->shell;
weston_seat_break_desktop_grabs(tool->seat);
grab->grab.interface = interface;
grab->shsurf = shsurf;
grab->shsurf_destroy_listener.notify = destroy_shell_grab_shsurf;
wl_signal_add(&shsurf->destroy_signal, &grab->shsurf_destroy_listener);
grab->tool = tool;
shsurf->grabbed = 1;
weston_tablet_tool_start_grab(tool, &grab->grab);
if (shell->child.desktop_shell)
weston_tablet_tool_set_focus(tool,
get_default_view(shell->grab_surface),
0);
}
static void
shell_tablet_tool_grab_end(struct shell_tablet_tool_grab *grab)
{
if (grab->shsurf) {
wl_list_remove(&grab->shsurf_destroy_listener.link);
grab->shsurf->grabbed = 0;
}
weston_tablet_tool_end_grab(grab->tool);
}
static enum animation_type
get_animation_type(char *animation)
{
if (!animation)
return ANIMATION_NONE;
if (!strcmp("zoom", animation))
return ANIMATION_ZOOM;
else if (!strcmp("fade", animation))
return ANIMATION_FADE;
else if (!strcmp("dim-layer", animation))
return ANIMATION_DIM_LAYER;
else
return ANIMATION_NONE;
}
static bool
shell_configuration(struct desktop_shell *shell)
{
2013-05-24 01:40:56 +00:00
struct weston_config_section *section;
struct weston_config *config;
char *s, *client;
bool allow_zap;
2013-05-24 01:40:56 +00:00
config = wet_get_config(shell->compositor);
section = weston_config_get_section(config, "shell", NULL, NULL);
client = wet_get_libexec_path(WESTON_SHELL_CLIENT);
weston_config_section_get_string(section, "client", &s, client);
free(client);
2013-11-20 12:22:29 +00:00
shell->client = s;
weston_config_section_get_bool(section,
"allow-zap", &allow_zap, true);
shell->allow_zap = allow_zap;
shell->binding_modifier = weston_config_get_binding_modifier(config, MODIFIER_SUPER);
2013-05-24 01:40:56 +00:00
weston_config_section_get_string(section, "animation", &s, "none");
shell->win_animation_type = get_animation_type(s);
free(s);
weston_config_section_get_string(section, "close-animation", &s, "fade");
shell->win_close_animation_type = get_animation_type(s);
free(s);
weston_config_section_get_string(section,
"startup-animation", &s, "fade");
shell->startup_animation_type = get_animation_type(s);
if (shell->startup_animation_type == ANIMATION_ZOOM) {
weston_log("invalid startup animation type %s\n", s);
free(s);
return false;
}
free(s);
weston_config_section_get_string(section, "focus-animation", &s, "none");
shell->focus_animation_type = get_animation_type(s);
if (shell->focus_animation_type != ANIMATION_NONE &&
shell->focus_animation_type != ANIMATION_DIM_LAYER) {
weston_log("invalid focus animation type %s\n", s);
free(s);
return false;
}
free(s);
return true;
}
static int
focus_surface_get_label(struct weston_surface *surface, char *buf, size_t len)
{
return snprintf(buf, len, "focus highlight effect for output %s",
(surface->output ? surface->output->name : "NULL"));
}
/* no-op func for checking focus surface */
static void
focus_surface_committed(struct weston_surface *es,
struct weston_coord_surface new_origin)
{
}
static bool
is_focus_view (struct weston_view *view)
{
return (view->surface->committed == focus_surface_committed);
}
static struct focus_surface *
create_focus_surface(struct weston_compositor *ec,
struct weston_output *output)
{
struct focus_surface *fsurf = NULL;
struct weston_curtain_params curtain_params = {
.r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0,
.pos = output->pos,
.width = output->width, .height = output->height,
.surface_committed = focus_surface_committed,
.get_label = focus_surface_get_label,
.surface_private = NULL,
.capture_input = false,
};
fsurf = malloc(sizeof *fsurf);
if (!fsurf)
return NULL;
curtain_params.surface_private = fsurf;
fsurf->curtain = weston_shell_utils_curtain_create(ec, &curtain_params);
weston_view_set_output(fsurf->curtain->view, output);
return fsurf;
}
static void
focus_surface_destroy(struct focus_surface *fsurf)
{
weston_shell_utils_curtain_destroy(fsurf->curtain);
free(fsurf);
}
static void
focus_animation_done(struct weston_view_animation *animation, void *data)
{
struct workspace *ws = data;
ws->focus_animation = NULL;
}
static void
animate_focus_change(struct desktop_shell *shell, struct workspace *ws,
struct weston_view *from, struct weston_view *to)
{
struct weston_view *front = ws->fsurf_front->curtain->view;
struct weston_view *back = ws->fsurf_back->curtain->view;
if ((from && from == to) || shell->focus_animation_type == ANIMATION_NONE)
return;
if (ws->focus_animation) {
weston_view_animation_destroy(ws->focus_animation);
ws->focus_animation = NULL;
}
if (to) {
weston_view_move_to_layer(front, &to->layer_link);
if (from)
weston_view_move_to_layer(back, &from->layer_link);
else
weston_view_move_to_layer(back, &ws->layer.view_list);
ws->focus_animation =
weston_stable_fade_run(front, 0.0, back, 0.4,
focus_animation_done, ws);
} else {
weston_view_move_to_layer(front, &ws->layer.view_list);
weston_view_move_to_layer(back, NULL);
ws->focus_animation =
weston_fade_run(front, front->alpha, 0.0, 300,
focus_animation_done, ws);
}
}
static void
focus_state_destroy(struct focus_state *state)
{
wl_list_remove(&state->seat_destroy_listener.link);
wl_list_remove(&state->surface_destroy_listener.link);
free(state);
}
static void
focus_state_seat_destroy(struct wl_listener *listener, void *data)
{
struct focus_state *state = container_of(listener,
struct focus_state,
seat_destroy_listener);
wl_list_remove(&state->link);
focus_state_destroy(state);
}
static void
focus_state_surface_destroy(struct wl_listener *listener, void *data)
{
struct focus_state *state = container_of(listener,
struct focus_state,
surface_destroy_listener);
struct weston_surface *main_surface;
struct weston_view *next;
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
struct weston_view *view;
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
main_surface = weston_surface_get_main_surface(state->keyboard_focus);
next = NULL;
wl_list_for_each(view,
&state->ws->layer.view_list.link, layer_link.link) {
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
if (view->surface == main_surface)
continue;
if (is_focus_view(view))
continue;
if (!get_shell_surface(view->surface))
continue;
next = view;
break;
}
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
/* if the focus was a sub-surface, activate its main surface */
if (main_surface != state->keyboard_focus)
next = get_default_view(main_surface);
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
if (next) {
if (state->keyboard_focus) {
wl_list_remove(&state->surface_destroy_listener.link);
wl_list_init(&state->surface_destroy_listener.link);
}
state->keyboard_focus = NULL;
activate(state->shell, next, state->seat,
WESTON_ACTIVATE_FLAG_CONFIGURE);
} else {
if (state->shell->focus_animation_type != ANIMATION_NONE) {
animate_focus_change(state->shell, state->ws,
get_default_view(main_surface), NULL);
}
wl_list_remove(&state->link);
focus_state_destroy(state);
}
}
static struct focus_state *
focus_state_create(struct desktop_shell *shell, struct weston_seat *seat,
struct workspace *ws)
{
struct focus_state *state;
state = malloc(sizeof *state);
if (state == NULL)
return NULL;
state->shell = shell;
state->keyboard_focus = NULL;
state->ws = ws;
state->seat = seat;
wl_list_insert(&ws->focus_list, &state->link);
state->seat_destroy_listener.notify = focus_state_seat_destroy;
state->surface_destroy_listener.notify = focus_state_surface_destroy;
wl_signal_add(&seat->destroy_signal,
&state->seat_destroy_listener);
wl_list_init(&state->surface_destroy_listener.link);
return state;
}
static struct focus_state *
ensure_focus_state(struct desktop_shell *shell, struct weston_seat *seat)
{
struct workspace *ws = get_current_workspace(shell);
struct focus_state *state;
wl_list_for_each(state, &ws->focus_list, link)
if (state->seat == seat)
break;
if (&state->link == &ws->focus_list)
state = focus_state_create(shell, seat, ws);
return state;
}
static void
focus_state_set_focus(struct focus_state *state,
struct weston_surface *surface)
{
if (state->keyboard_focus) {
wl_list_remove(&state->surface_destroy_listener.link);
wl_list_init(&state->surface_destroy_listener.link);
}
state->keyboard_focus = surface;
if (surface)
wl_signal_add(&surface->destroy_signal,
&state->surface_destroy_listener);
}
static void
restore_focus_state(struct desktop_shell *shell, struct workspace *ws)
{
struct focus_state *state, *next;
struct weston_surface *surface;
struct wl_list pending_seat_list;
struct weston_seat *seat, *next_seat;
/* Temporarily steal the list of seats so that we can keep
* track of the seats we've already processed */
wl_list_init(&pending_seat_list);
wl_list_insert_list(&pending_seat_list, &shell->compositor->seat_list);
wl_list_init(&shell->compositor->seat_list);
wl_list_for_each_safe(state, next, &ws->focus_list, link) {
struct weston_keyboard *keyboard =
weston_seat_get_keyboard(state->seat);
wl_list_remove(&state->seat->link);
wl_list_insert(&shell->compositor->seat_list,
&state->seat->link);
if (!keyboard)
continue;
surface = state->keyboard_focus;
weston_keyboard_set_focus(keyboard, surface);
}
/* For any remaining seats that we don't have a focus state
* for we'll reset the keyboard focus to NULL */
wl_list_for_each_safe(seat, next_seat, &pending_seat_list, link) {
struct weston_keyboard *keyboard =
weston_seat_get_keyboard(seat);
wl_list_insert(&shell->compositor->seat_list, &seat->link);
if (!keyboard)
continue;
weston_keyboard_set_focus(keyboard, NULL);
}
}
static void
drop_focus_state(struct desktop_shell *shell, struct workspace *ws,
struct weston_surface *surface)
{
struct focus_state *state;
wl_list_for_each(state, &ws->focus_list, link)
if (state->keyboard_focus == surface)
focus_state_set_focus(state, NULL);
}
static void
desktop_shell_destroy_layer(struct weston_layer *layer);
static void
workspace_destroy(struct workspace *ws)
{
struct focus_state *state, *next;
wl_list_for_each_safe(state, next, &ws->focus_list, link)
focus_state_destroy(state);
if (ws->fsurf_front)
focus_surface_destroy(ws->fsurf_front);
if (ws->fsurf_back)
focus_surface_destroy(ws->fsurf_back);
desktop_shell_destroy_layer(&ws->layer);
}
static void
seat_destroyed(struct wl_listener *listener, void *data)
{
struct weston_seat *seat = data;
struct focus_state *state, *next;
struct workspace *ws = container_of(listener,
struct workspace,
seat_destroyed_listener);
wl_list_for_each_safe(state, next, &ws->focus_list, link)
if (state->seat == seat)
wl_list_remove(&state->link);
}
static void
workspace_create(struct desktop_shell *shell)
{
struct workspace *ws = &shell->workspace;
weston_layer_init(&ws->layer, shell->compositor);
weston_layer_set_position(&ws->layer, WESTON_LAYER_POSITION_NORMAL);
wl_list_init(&ws->focus_list);
wl_list_init(&ws->seat_destroyed_listener.link);
ws->seat_destroyed_listener.notify = seat_destroyed;
if (shell->focus_animation_type != ANIMATION_NONE) {
struct weston_output *output =
weston_shell_utils_get_default_output(shell->compositor);
assert(shell->focus_animation_type == ANIMATION_DIM_LAYER);
ws->fsurf_front = create_focus_surface(shell->compositor, output);
assert(ws->fsurf_front);
ws->fsurf_back = create_focus_surface(shell->compositor, output);
assert(ws->fsurf_back);
} else {
ws->fsurf_front = NULL;
ws->fsurf_back = NULL;
}
ws->focus_animation = NULL;
}
struct workspace *
get_current_workspace(struct desktop_shell *shell)
{
return &shell->workspace;
}
static void
surface_keyboard_focus_lost(struct weston_surface *surface)
{
struct weston_compositor *compositor = surface->compositor;
struct weston_seat *seat;
struct weston_surface *focus;
wl_list_for_each(seat, &compositor->seat_list, link) {
struct weston_keyboard *keyboard =
weston_seat_get_keyboard(seat);
if (!keyboard)
continue;
focus = weston_surface_get_main_surface(keyboard->focus);
if (focus == surface)
weston_keyboard_set_focus(keyboard, NULL);
}
}
static void
touch_move_grab_down(struct weston_touch_grab *grab,
const struct timespec *time,
int touch_id, struct weston_coord_global c)
{
}
static void
touch_move_grab_up(struct weston_touch_grab *grab, const struct timespec *time,
int touch_id)
{
struct weston_touch_move_grab *move =
(struct weston_touch_move_grab *) container_of(
grab, struct shell_touch_grab, grab);
if (touch_id == 0)
move->active = 0;
if (grab->touch->num_tp == 0) {
shell_touch_grab_end(&move->base);
free(move);
}
}
static void
touch_move_grab_motion(struct weston_touch_grab *grab,
const struct timespec *time, int touch_id,
struct weston_coord_global unused)
{
struct weston_touch_move_grab *move = (struct weston_touch_move_grab *) grab;
struct shell_surface *shsurf = move->base.shsurf;
struct weston_coord_global pos;
desktop-shell: Check for a valid desktop_surface 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: Marius Vlad <marius.vlad@collabora.com>
2022-03-18 20:49:29 +00:00
if (!shsurf || !shsurf->desktop_surface || !move->active)
return;
pos = weston_coord_global_add(grab->touch->grab_pos, move->delta);
pos.c = weston_coord_truncate(pos.c);
weston_view_set_position(shsurf->view, pos);
}
static void
touch_move_grab_frame(struct weston_touch_grab *grab)
{
}
static void
touch_move_grab_cancel(struct weston_touch_grab *grab)
{
struct weston_touch_move_grab *move =
(struct weston_touch_move_grab *) container_of(
grab, struct shell_touch_grab, grab);
shell_touch_grab_end(&move->base);
free(move);
}
static const struct weston_touch_grab_interface touch_move_grab_interface = {
touch_move_grab_down,
touch_move_grab_up,
touch_move_grab_motion,
touch_move_grab_frame,
touch_move_grab_cancel,
};
static int
surface_touch_move(struct shell_surface *shsurf, struct weston_touch *touch)
{
struct weston_touch_move_grab *move;
if (!shsurf)
return -1;
if (shsurf_is_max_or_fullscreen(shsurf))
return 0;
move = malloc(sizeof *move);
if (!move)
return -1;
move->active = 1;
move->delta = weston_coord_global_sub(
weston_view_get_pos_offset_global(shsurf->view),
touch->grab_pos);
shell_touch_grab_start(&move->base, &touch_move_grab_interface, shsurf,
touch);
return 0;
}
2012-01-05 02:30:29 +00:00
static void
noop_grab_focus(struct weston_pointer_grab *grab)
2012-01-05 02:30:29 +00:00
{
}
static void
noop_grab_axis(struct weston_pointer_grab *grab,
const struct timespec *time,
struct weston_pointer_axis_event *event)
{
}
static void
noop_grab_axis_source(struct weston_pointer_grab *grab,
uint32_t source)
{
}
static void
noop_grab_frame(struct weston_pointer_grab *grab)
{
}
static struct weston_coord_global
constrain_position(struct weston_move_grab *move)
{
struct shell_surface *shsurf = move->base.shsurf;
struct weston_surface *surface =
weston_desktop_surface_get_surface(shsurf->desktop_surface);
struct weston_pointer *pointer = move->base.grab.pointer;
int bottom;
const int safety = 50;
pixman_rectangle32_t area;
struct weston_geometry geometry;
struct weston_coord_global c;
c = weston_coord_global_add(pointer->pos, move->delta);
if (shsurf->shell->panel_position ==
WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP) {
get_output_work_area(shsurf->shell, surface->output, &area);
geometry =
weston_desktop_surface_get_geometry(shsurf->desktop_surface);
bottom = c.c.y + geometry.height + geometry.y;
if (bottom - safety < area.y)
c.c.y = area.y + safety - geometry.height
- geometry.y;
if (move->client_initiated &&
c.c.y + geometry.y < area.y)
c.c.y = area.y - geometry.y;
}
return c;
}
static void
move_grab_motion(struct weston_pointer_grab *grab,
const struct timespec *time,
struct weston_pointer_motion_event *event)
{
struct weston_move_grab *move = (struct weston_move_grab *) grab;
struct weston_pointer *pointer = grab->pointer;
struct shell_surface *shsurf = move->base.shsurf;
struct weston_coord_global pos;
weston_pointer_move(pointer, event);
desktop-shell: Check for a valid desktop_surface 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: Marius Vlad <marius.vlad@collabora.com>
2022-03-18 20:49:29 +00:00
if (!shsurf || !shsurf->desktop_surface)
return;
pos = constrain_position(move);
weston_view_set_position(shsurf->view, pos);
}
static void
move_grab_button(struct weston_pointer_grab *grab,
const struct timespec *time, uint32_t button, uint32_t state_w)
{
struct shell_grab *shell_grab = container_of(grab, struct shell_grab,
grab);
struct weston_pointer *pointer = grab->pointer;
enum wl_pointer_button_state state = state_w;
if (pointer->button_count == 0 &&
state == WL_POINTER_BUTTON_STATE_RELEASED) {
shell_grab_end(shell_grab);
2012-01-05 02:30:29 +00:00
free(grab);
}
}
static void
move_grab_cancel(struct weston_pointer_grab *grab)
{
struct shell_grab *shell_grab =
container_of(grab, struct shell_grab, grab);
shell_grab_end(shell_grab);
free(grab);
}
static const struct weston_pointer_grab_interface move_grab_interface = {
2012-01-05 02:30:29 +00:00
noop_grab_focus,
move_grab_motion,
move_grab_button,
noop_grab_axis,
noop_grab_axis_source,
noop_grab_frame,
move_grab_cancel,
};
static int
surface_move(struct shell_surface *shsurf, struct weston_pointer *pointer,
bool client_initiated)
{
struct weston_move_grab *move;
if (!shsurf)
return -1;
if (shsurf->grabbed || shsurf_is_max_or_fullscreen(shsurf))
return 0;
move = malloc(sizeof *move);
if (!move)
return -1;
move->delta = weston_coord_global_sub(
weston_view_get_pos_offset_global(shsurf->view),
pointer->grab_pos);
move->client_initiated = client_initiated;
weston_desktop_surface_set_orientation(shsurf->desktop_surface,
WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE);
shsurf->orientation = WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE;
shell_grab_start(&move->base, &move_grab_interface, shsurf,
pointer, WESTON_DESKTOP_SHELL_CURSOR_MOVE);
return 0;
}
struct weston_resize_grab {
struct shell_grab base;
uint32_t edges;
int32_t width, height;
};
static void
tablet_tool_noop_grab_proximity_in(struct weston_tablet_tool_grab *grab,
const struct timespec *time,
struct weston_tablet *tablet)
{
}
static void
tablet_tool_move_grab_proximity_out(struct weston_tablet_tool_grab *grab,
const struct timespec *time)
{
struct weston_tablet_tool_move_grab *move =
(struct weston_tablet_tool_move_grab *)grab;
shell_tablet_tool_grab_end(&move->base);
free(grab);
}
static void
tablet_tool_move_grab_up(struct weston_tablet_tool_grab *grab,
const struct timespec *time)
{
struct weston_tablet_tool_move_grab *move =
(struct weston_tablet_tool_move_grab *)grab;
shell_tablet_tool_grab_end(&move->base);
free(grab);
}
static void
tablet_tool_noop_grab_down(struct weston_tablet_tool_grab *grab,
const struct timespec *time)
{
}
static void
tablet_tool_move_grab_motion(struct weston_tablet_tool_grab *grab,
const struct timespec *time,
struct weston_coord_global pos)
{
struct weston_tablet_tool_move_grab *move =
(struct weston_tablet_tool_move_grab *)grab;
struct shell_surface *shsurf = move->base.shsurf;
weston_tablet_tool_cursor_move(grab->tool, pos);
if (!shsurf)
return;
pos.c.x += wl_fixed_to_double(move->dx);
pos.c.y += wl_fixed_to_double(move->dy);
weston_view_set_position(shsurf->view, pos);
}
static void
tablet_tool_noop_grab_pressure(struct weston_tablet_tool_grab *grab,
const struct timespec *time,
uint32_t pressure)
{
}
static void
tablet_tool_noop_grab_distance(struct weston_tablet_tool_grab *grab,
const struct timespec *time,
uint32_t distance)
{
}
static void
tablet_tool_noop_grab_tilt(struct weston_tablet_tool_grab *grab,
const struct timespec *time,
int32_t tilt_x, int32_t tilt_y)
{
}
static void tablet_tool_noop_grab_button(struct weston_tablet_tool_grab *grab,
const struct timespec *time, uint32_t button,
uint32_t state)
{
}
static void
tablet_tool_noop_grab_frame(struct weston_tablet_tool_grab *grab,
const struct timespec *time)
{
}
static void
tablet_tool_move_grab_cancel(struct weston_tablet_tool_grab *grab)
{
struct weston_tablet_tool_move_grab *move =
(struct weston_tablet_tool_move_grab *)grab;
shell_tablet_tool_grab_end(&move->base);
free(grab);
}
static struct weston_tablet_tool_grab_interface tablet_tool_move_grab_interface = {
tablet_tool_noop_grab_proximity_in,
tablet_tool_move_grab_proximity_out,
tablet_tool_move_grab_motion,
tablet_tool_noop_grab_down,
tablet_tool_move_grab_up,
tablet_tool_noop_grab_pressure,
tablet_tool_noop_grab_distance,
tablet_tool_noop_grab_tilt,
tablet_tool_noop_grab_button,
tablet_tool_noop_grab_frame,
tablet_tool_move_grab_cancel,
};
static int
surface_tablet_tool_move(struct shell_surface *shsurf, struct weston_tablet_tool *tool)
{
struct weston_tablet_tool_move_grab *move;
struct weston_coord_global offset;
if (!shsurf)
return -1;
if (shsurf->state.fullscreen || shsurf->state.maximized)
return 0;
move = malloc(sizeof(*move));
if (!move)
return -1;
offset = weston_coord_global_sub(
weston_view_get_pos_offset_global(shsurf->view),
tool->grab_pos);
move->dx = wl_fixed_from_double(offset.c.x);
move->dy = wl_fixed_from_double(offset.c.y);
shell_tablet_tool_grab_start(&move->base, &tablet_tool_move_grab_interface,
shsurf, tool);
return 0;
}
static void
resize_grab_motion(struct weston_pointer_grab *grab,
const struct timespec *time,
struct weston_pointer_motion_event *event)
{
struct weston_resize_grab *resize = (struct weston_resize_grab *) grab;
struct weston_pointer *pointer = grab->pointer;
struct shell_surface *shsurf = resize->base.shsurf;
int32_t width, height;
struct weston_size min_size, max_size;
struct weston_coord_surface tmp_s;
wl_fixed_t from_x, from_y;
wl_fixed_t to_x, to_y;
weston_pointer_move(pointer, event);
if (!shsurf || !shsurf->desktop_surface)
return;
weston_view_update_transform(shsurf->view);
tmp_s = weston_coord_global_to_surface(shsurf->view, pointer->grab_pos);
from_x = wl_fixed_from_double(tmp_s.c.x);
from_y = wl_fixed_from_double(tmp_s.c.y);
tmp_s = weston_coord_global_to_surface(shsurf->view, pointer->pos);
to_x = wl_fixed_from_double(tmp_s.c.x);
to_y = wl_fixed_from_double(tmp_s.c.y);
width = resize->width;
if (resize->edges & WESTON_DESKTOP_SURFACE_EDGE_LEFT) {
width += wl_fixed_to_int(from_x - to_x);
} else if (resize->edges & WESTON_DESKTOP_SURFACE_EDGE_RIGHT) {
width += wl_fixed_to_int(to_x - from_x);
}
height = resize->height;
if (resize->edges & WESTON_DESKTOP_SURFACE_EDGE_TOP) {
height += wl_fixed_to_int(from_y - to_y);
} else if (resize->edges & WESTON_DESKTOP_SURFACE_EDGE_BOTTOM) {
height += wl_fixed_to_int(to_y - from_y);
}
max_size = weston_desktop_surface_get_max_size(shsurf->desktop_surface);
min_size = weston_desktop_surface_get_min_size(shsurf->desktop_surface);
min_size.width = MAX(1, min_size.width);
min_size.height = MAX(1, min_size.height);
if (width < min_size.width)
width = min_size.width;
else if (max_size.width > 0 && width > max_size.width)
width = max_size.width;
if (height < min_size.height)
height = min_size.height;
else if (max_size.height > 0 && height > max_size.height)
height = max_size.height;
weston_desktop_surface_set_size(shsurf->desktop_surface, width, height);
}
static void
resize_grab_button(struct weston_pointer_grab *grab,
const struct timespec *time,
uint32_t button, uint32_t state_w)
{
struct weston_resize_grab *resize = (struct weston_resize_grab *) grab;
struct weston_pointer *pointer = grab->pointer;
enum wl_pointer_button_state state = state_w;
if (pointer->button_count == 0 &&
state == WL_POINTER_BUTTON_STATE_RELEASED) {
if (resize->base.shsurf && resize->base.shsurf->desktop_surface) {
struct weston_desktop_surface *desktop_surface =
resize->base.shsurf->desktop_surface;
weston_desktop_surface_set_resizing(desktop_surface,
false);
weston_desktop_surface_set_size(desktop_surface, 0, 0);
}
shell_grab_end(&resize->base);
free(grab);
}
}
static void
resize_grab_cancel(struct weston_pointer_grab *grab)
{
struct weston_resize_grab *resize = (struct weston_resize_grab *) grab;
if (resize->base.shsurf && resize->base.shsurf->desktop_surface) {
struct weston_desktop_surface *desktop_surface =
resize->base.shsurf->desktop_surface;
weston_desktop_surface_set_resizing(desktop_surface, false);
weston_desktop_surface_set_size(desktop_surface, 0, 0);
}
shell_grab_end(&resize->base);
free(grab);
}
static const struct weston_pointer_grab_interface resize_grab_interface = {
noop_grab_focus,
resize_grab_motion,
resize_grab_button,
noop_grab_axis,
noop_grab_axis_source,
noop_grab_frame,
resize_grab_cancel,
};
static int
surface_resize(struct shell_surface *shsurf,
struct weston_pointer *pointer, uint32_t edges)
{
struct weston_resize_grab *resize;
const unsigned resize_topbottom =
WESTON_DESKTOP_SURFACE_EDGE_TOP | WESTON_DESKTOP_SURFACE_EDGE_BOTTOM;
const unsigned resize_leftright =
WESTON_DESKTOP_SURFACE_EDGE_LEFT | WESTON_DESKTOP_SURFACE_EDGE_RIGHT;
const unsigned resize_any = resize_topbottom | resize_leftright;
struct weston_geometry geometry;
if (shsurf->grabbed || shsurf_is_max_or_fullscreen(shsurf))
return 0;
/* Check for invalid edge combinations. */
if (edges == WESTON_DESKTOP_SURFACE_EDGE_NONE || edges > resize_any ||
(edges & resize_topbottom) == resize_topbottom ||
(edges & resize_leftright) == resize_leftright)
return 0;
resize = malloc(sizeof *resize);
if (!resize)
return -1;
resize->edges = edges;
geometry = weston_desktop_surface_get_geometry(shsurf->desktop_surface);
resize->width = geometry.width;
resize->height = geometry.height;
shsurf->resize_edges = edges;
weston_desktop_surface_set_resizing(shsurf->desktop_surface, true);
weston_desktop_surface_set_orientation(shsurf->desktop_surface,
WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE);
shsurf->orientation = WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE;
shell_grab_start(&resize->base, &resize_grab_interface, shsurf,
pointer, edges);
return 0;
}
static void
busy_cursor_grab_focus(struct weston_pointer_grab *base)
{
struct shell_grab *grab = (struct shell_grab *) base;
struct weston_pointer *pointer = base->pointer;
struct weston_desktop_surface *desktop_surface;
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
struct weston_view *view;
view = weston_compositor_pick_view(pointer->seat->compositor,
pointer->pos);
desktop_surface = weston_surface_get_desktop_surface(view->surface);
if (!grab->shsurf || grab->shsurf->desktop_surface != desktop_surface) {
shell_grab_end(grab);
free(grab);
}
}
static void
busy_cursor_grab_motion(struct weston_pointer_grab *grab,
const struct timespec *time,
struct weston_pointer_motion_event *event)
{
weston_pointer_move(grab->pointer, event);
}
static void
busy_cursor_grab_button(struct weston_pointer_grab *base,
const struct timespec *time,
uint32_t button, uint32_t state)
{
struct shell_grab *grab = (struct shell_grab *) base;
struct shell_surface *shsurf = grab->shsurf;
struct weston_pointer *pointer = grab->grab.pointer;
struct weston_seat *seat = pointer->seat;
if (shsurf && button == BTN_LEFT && state) {
activate(shsurf->shell, shsurf->view, seat,
WESTON_ACTIVATE_FLAG_CONFIGURE);
surface_move(shsurf, pointer, false);
} else if (shsurf && button == BTN_RIGHT && state) {
activate(shsurf->shell, shsurf->view, seat,
WESTON_ACTIVATE_FLAG_CONFIGURE);
surface_rotate(shsurf, pointer);
}
}
static void
busy_cursor_grab_cancel(struct weston_pointer_grab *base)
{
struct shell_grab *grab = (struct shell_grab *) base;
shell_grab_end(grab);
free(grab);
}
static const struct weston_pointer_grab_interface busy_cursor_grab_interface = {
busy_cursor_grab_focus,
busy_cursor_grab_motion,
busy_cursor_grab_button,
noop_grab_axis,
noop_grab_axis_source,
noop_grab_frame,
busy_cursor_grab_cancel,
};
static void
handle_pointer_focus(struct wl_listener *listener, void *data)
{
struct weston_pointer *pointer = data;
struct weston_view *view = pointer->focus;
struct shell_surface *shsurf;
struct weston_desktop_client *client;
if (!view)
return;
shsurf = get_shell_surface(view->surface);
if (!shsurf)
return;
client = weston_desktop_surface_get_client(shsurf->desktop_surface);
if (shsurf->unresponsive)
set_busy_cursor(shsurf, pointer);
else
weston_desktop_client_ping(client);
}
static void
has_keyboard_focused_child_callback(struct weston_desktop_surface *surface,
void *user_data);
static void
has_keyboard_focused_child_callback(struct weston_desktop_surface *surface,
void *user_data)
{
struct weston_surface *es = weston_desktop_surface_get_surface(surface);
struct shell_surface *shsurf = get_shell_surface(es);
bool *has_keyboard_focus = user_data;
if (shsurf->focus_count > 0) {
*has_keyboard_focus = true;
return;
}
weston_desktop_surface_foreach_child(shsurf->desktop_surface,
has_keyboard_focused_child_callback,
&has_keyboard_focus);
}
static bool
has_keyboard_focused_child(struct shell_surface *shsurf)
{
bool has_keyboard_focus = false;
if (shsurf->focus_count > 0)
return true;
weston_desktop_surface_foreach_child(shsurf->desktop_surface,
has_keyboard_focused_child_callback,
&has_keyboard_focus);
return has_keyboard_focus;
}
static void
sync_surface_activated_state(struct shell_surface *shsurf)
{
struct weston_desktop_surface *surface = shsurf->desktop_surface;
struct weston_desktop_surface *parent;
struct weston_surface *parent_surface;
parent = weston_desktop_surface_get_parent(surface);
if (parent) {
parent_surface = weston_desktop_surface_get_surface(parent);
sync_surface_activated_state(get_shell_surface(parent_surface));
return;
}
if (has_keyboard_focused_child(shsurf))
weston_desktop_surface_set_activated(surface, true);
else
weston_desktop_surface_set_activated(surface, false);
}
static void
handle_tablet_tool_focus(struct wl_listener *listener, void *data)
{
struct weston_tablet_tool *tool = data;
struct weston_view *view = tool->focus;
struct shell_surface *shsurf;
struct weston_desktop_client *client;
if (!view)
return;
shsurf = get_shell_surface(view->surface);
if (!shsurf)
return;
client = weston_desktop_surface_get_client(shsurf->desktop_surface);
weston_desktop_client_ping(client);
}
static void
shell_surface_deactivate(struct shell_surface *shsurf)
{
if (--shsurf->focus_count == 0)
sync_surface_activated_state(shsurf);
}
static void
shell_surface_activate(struct shell_surface *shsurf)
{
if (shsurf->focus_count++ == 0)
sync_surface_activated_state(shsurf);
}
/* The surface will be inserted into the list immediately after the link
* returned by this function (i.e. will be stacked immediately above the
* returned link). */
static struct weston_layer_entry *
shell_surface_calculate_layer_link (struct shell_surface *shsurf)
{
struct workspace *ws;
if (weston_desktop_surface_get_fullscreen(shsurf->desktop_surface) &&
!shsurf->state.lowered) {
return &shsurf->shell->fullscreen_layer.view_list;
}
/* Move the surface to a normal workspace layer so that surfaces
* which were previously fullscreen or transient are no longer
* rendered on top. */
ws = get_current_workspace(shsurf->shell);
return &ws->layer.view_list;
}
static void
shell_surface_update_child_surface_layers (struct shell_surface *shsurf)
{
weston_desktop_surface_propagate_layer(shsurf->desktop_surface);
compositor: Implement JSON-timeline logging Logging is activated and deactivated with the debug key binding 't'. When activated, it creates a new log file, where it records the events. The log file contains events and detailed object information entries in JSON format, and is meant to be parsed in sequence from beginning to the end. The emitted events are mostly related to the output repaint cycle, like when repaint begins, is submitted to GPU, and when it completes on a vblank. This is recorded per-output. Also some per-surface events are recorded, including when surface damage is flushed. To reduce the log size, events refer to objects like outputs and surfaces by id numbers. Detailed object information is emitted only as needed: on the first object occurrence, and afterwards only if weston_timeline_object::force_refresh asks for it. The detailed information for surfaces includes the string returned by weston_surface::get_label. Therefore it is important to set weston_timeline_object::force_refresh = 1 whenever the string would change, so that the new details get recorded. A rudimentary parser and SVG generator can be found at: https://github.com/ppaalanen/wesgr The timeline logs can answer questions including: - How does the compositor repaint cycle work timing-wise? - When was the vblank deadline missed? - What is the latency from surface commit to showing the new content on screen? - How long does it take to process the scenegraph? v2: weston_surface::get_description renamed to get_label. v3: reafctor a bit into fprint_quoted_string(). Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
2014-11-12 13:09:24 +00:00
}
/* Update the surfaces layer. Mark both the old and new views as having dirty
* geometry to ensure the changes are redrawn.
*
* If any child surfaces exist and are mapped, ensure theyre in the same layer
* as this surface. */
static void
shell_surface_update_layer(struct shell_surface *shsurf)
{
struct weston_layer_entry *new_layer_link;
new_layer_link = shell_surface_calculate_layer_link(shsurf);
assert(new_layer_link);
weston_view_move_to_layer(shsurf->view, new_layer_link);
shell_surface_update_child_surface_layers(shsurf);
}
static void
notify_output_destroy(struct wl_listener *listener, void *data)
{
struct shell_surface *shsurf =
container_of(listener,
struct shell_surface, output_destroy_listener);
shsurf->output = NULL;
shsurf->output_destroy_listener.notify = NULL;
}
static void
shell_surface_set_output(struct shell_surface *shsurf,
struct weston_output *output)
{
struct weston_surface *es =
weston_desktop_surface_get_surface(shsurf->desktop_surface);
/* get the default output, if the client set it as NULL
check whether the output is available */
if (output)
shsurf->output = output;
else if (es->output)
shsurf->output = es->output;
else
shsurf->output = weston_shell_utils_get_default_output(es->compositor);
if (shsurf->output_destroy_listener.notify) {
wl_list_remove(&shsurf->output_destroy_listener.link);
shsurf->output_destroy_listener.notify = NULL;
}
if (!shsurf->output)
return;
shsurf->output_destroy_listener.notify = notify_output_destroy;
wl_signal_add(&shsurf->output->destroy_signal,
&shsurf->output_destroy_listener);
}
static void
weston_view_set_initial_position(struct weston_view *view,
struct desktop_shell *shell);
static void
unset_fullscreen(struct shell_surface *shsurf)
{
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
if (shsurf->fullscreen.black_view)
weston_shell_utils_curtain_destroy(shsurf->fullscreen.black_view);
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
shsurf->fullscreen.black_view = NULL;
if (shsurf->saved_position_valid)
weston_view_set_position(shsurf->view, shsurf->saved_pos);
else
weston_view_set_initial_position(shsurf->view, shsurf->shell);
shsurf->saved_position_valid = false;
weston_desktop_surface_set_orientation(shsurf->desktop_surface,
shsurf->orientation);
if (shsurf->saved_rotation_valid) {
weston_view_add_transform(shsurf->view,
&shsurf->view->geometry.transformation_list,
&shsurf->rotation.transform);
shsurf->saved_rotation_valid = false;
}
}
static void
unset_maximized(struct shell_surface *shsurf)
{
struct weston_surface *surface =
weston_desktop_surface_get_surface(shsurf->desktop_surface);
/* undo all maximized things here */
shell_surface_set_output(shsurf,
weston_shell_utils_get_default_output(surface->compositor));
if (shsurf->saved_position_valid)
weston_view_set_position(shsurf->view, shsurf->saved_pos);
else
weston_view_set_initial_position(shsurf->view, shsurf->shell);
shsurf->saved_position_valid = false;
weston_desktop_surface_set_orientation(shsurf->desktop_surface,
shsurf->orientation);
if (shsurf->saved_rotation_valid) {
weston_view_add_transform(shsurf->view,
&shsurf->view->geometry.transformation_list,
&shsurf->rotation.transform);
shsurf->saved_rotation_valid = false;
}
}
static void
set_minimized(struct weston_surface *surface)
{
struct shell_surface *shsurf;
struct workspace *current_ws;
struct weston_view *view;
view = get_default_view(surface);
if (!view)
return;
assert(weston_surface_get_main_surface(view->surface) == view->surface);
shsurf = get_shell_surface(surface);
current_ws = get_current_workspace(shsurf->shell);
weston_view_move_to_layer(view,
&shsurf->shell->minimized_layer.view_list);
drop_focus_state(shsurf->shell, current_ws, view->surface);
surface_keyboard_focus_lost(surface);
shell_surface_update_child_surface_layers(shsurf);
}
static struct desktop_shell *
shell_surface_get_shell(struct shell_surface *shsurf)
{
return shsurf->shell;
}
static int
black_surface_get_label(struct weston_surface *surface, char *buf, size_t len)
{
struct weston_view *fs_view = surface->committed_private;
struct weston_surface *fs_surface = fs_view->surface;
int n;
int rem;
int ret;
n = snprintf(buf, len, "black background surface for ");
if (n < 0)
return n;
rem = (int)len - n;
if (rem < 0)
rem = 0;
if (fs_surface->get_label)
ret = fs_surface->get_label(fs_surface, buf + n, rem);
else
ret = snprintf(buf + n, rem, "<unknown>");
if (ret < 0)
return n;
return n + ret;
}
static void
black_surface_committed(struct weston_surface *es,
struct weston_coord_surface new_origin)
{
}
static bool
is_black_surface_view(struct weston_view *view, struct weston_view **fs_view)
{
struct weston_surface *surface = view->surface;
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
if (surface->committed == black_surface_committed) {
if (fs_view)
*fs_view = surface->committed_private;
return true;
}
return false;
}
/* Set the shell surface as the current fullscreen view for its current output,
* centering it with a black background */
static void
shell_set_view_fullscreen(struct shell_surface *shsurf)
{
struct weston_surface *surface =
weston_desktop_surface_get_surface(shsurf->desktop_surface);
struct weston_compositor *ec = surface->compositor;
struct weston_output *output = shsurf->fullscreen_output;
struct weston_curtain_params curtain_params = {
.r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0,
.pos = output->pos,
.width = output->width, .height = output->height,
.surface_committed = black_surface_committed,
.get_label = black_surface_get_label,
.surface_private = shsurf->view,
.capture_input = true,
};
assert(weston_desktop_surface_get_fullscreen(shsurf->desktop_surface));
weston_view_move_to_layer(shsurf->view,
&shsurf->shell->fullscreen_layer.view_list);
weston_shell_utils_center_on_output(shsurf->view, shsurf->fullscreen_output);
if (!shsurf->fullscreen.black_view) {
shsurf->fullscreen.black_view =
weston_shell_utils_curtain_create(ec, &curtain_params);
}
weston_view_set_output(shsurf->fullscreen.black_view->view,
shsurf->fullscreen_output);
weston_view_move_to_layer(shsurf->fullscreen.black_view->view,
&shsurf->view->layer_link);
shsurf->state.lowered = false;
}
static void
desktop_shell_destroy_seat(struct shell_seat *shseat)
{
wl_list_remove(&shseat->keyboard_focus_listener.link);
wl_list_remove(&shseat->caps_changed_listener.link);
wl_list_remove(&shseat->pointer_focus_listener.link);
wl_list_remove(&shseat->seat_destroy_listener.link);
wl_list_remove(&shseat->tablet_tool_added_listener.link);
wl_list_remove(&shseat->link);
free(shseat);
}
static void
destroy_tablet_tool_listener(struct wl_listener *listener, void *data)
{
struct tablet_tool_listener *tool_listener =
container_of(listener, struct tablet_tool_listener, removed_listener);
wl_list_remove(&tool_listener->removed_listener.link);
wl_list_remove(&tool_listener->base.link);
free(tool_listener);
}
static void
handle_tablet_tool_added(struct wl_listener *listener, void *data)
{
struct weston_tablet_tool *tool = data;
struct tablet_tool_listener *tool_listener;
tool_listener = malloc(sizeof *tool_listener);
if (!tool_listener) {
weston_log("no memory to allocate to shell seat tablet listener\n");
return;
}
tool_listener->removed_listener.notify = destroy_tablet_tool_listener;
wl_signal_add(&tool->removed_signal,
&tool_listener->removed_listener);
tool_listener->base.notify = handle_tablet_tool_focus;
wl_signal_add(&tool->focus_signal, &tool_listener->base);
}
static void
destroy_shell_seat(struct wl_listener *listener, void *data)
{
struct shell_seat *shseat =
container_of(listener,
struct shell_seat, seat_destroy_listener);
desktop_shell_destroy_seat(shseat);
}
static void
shell_seat_caps_changed(struct wl_listener *listener, void *data)
{
struct weston_pointer *pointer;
struct shell_seat *seat;
seat = container_of(listener, struct shell_seat, caps_changed_listener);
pointer = weston_seat_get_pointer(seat->seat);
if (pointer &&
wl_list_empty(&seat->pointer_focus_listener.link)) {
wl_signal_add(&pointer->focus_signal,
&seat->pointer_focus_listener);
} else if (!pointer) {
wl_list_remove(&seat->pointer_focus_listener.link);
wl_list_init(&seat->pointer_focus_listener.link);
}
}
static struct shell_seat *
create_shell_seat(struct desktop_shell *shell, struct weston_seat *seat)
{
struct shell_seat *shseat;
struct weston_tablet_tool *tool;
shseat = calloc(1, sizeof *shseat);
if (!shseat) {
weston_log("no memory to allocate shell seat\n");
return NULL;
}
shseat->seat = seat;
shseat->seat_destroy_listener.notify = destroy_shell_seat;
wl_signal_add(&seat->destroy_signal,
&shseat->seat_destroy_listener);
wl_list_init(&shseat->keyboard_focus_listener.link);
shseat->pointer_focus_listener.notify = handle_pointer_focus;
wl_list_init(&shseat->pointer_focus_listener.link);
shseat->tablet_tool_added_listener.notify = handle_tablet_tool_added;
wl_list_init(&shseat->tablet_tool_added_listener.link);
wl_list_for_each(tool, &seat->tablet_tool_list, link) {
struct tablet_tool_listener *listener = malloc(sizeof *listener);
if (!listener) {
weston_log("no memory to allocate to shell seat tablet listener\n");
break;
}
listener->removed_listener.notify = destroy_tablet_tool_listener;
wl_signal_add(&tool->removed_signal,
&listener->removed_listener);
listener->base.notify = handle_tablet_tool_focus;
wl_signal_add(&tool->focus_signal, &listener->base);
}
shseat->caps_changed_listener.notify = shell_seat_caps_changed;
wl_signal_add(&seat->updated_caps_signal,
&shseat->caps_changed_listener);
shell_seat_caps_changed(&shseat->caps_changed_listener, NULL);
wl_list_insert(&shell->seat_list, &shseat->link);
return shseat;
}
static struct shell_seat *
get_shell_seat(struct weston_seat *seat)
{
struct wl_listener *listener;
if (!seat)
return NULL;
listener = wl_signal_get(&seat->destroy_signal, destroy_shell_seat);
if (!listener)
return NULL;
return container_of(listener,
struct shell_seat, seat_destroy_listener);
}
static void
fade_out_done_idle_cb(void *data)
{
struct shell_surface *shsurf = data;
desktop_shell_destroy_surface(shsurf);
}
static void
fade_out_done(struct weston_view_animation *animation, void *data)
{
struct shell_surface *shsurf = data;
struct wl_event_loop *loop;
loop = wl_display_get_event_loop(shsurf->shell->compositor->wl_display);
if (weston_view_is_mapped(shsurf->wview_anim_fade)) {
weston_view_move_to_layer(shsurf->wview_anim_fade, NULL);
wl_event_loop_add_idle(loop, fade_out_done_idle_cb, shsurf);
}
}
struct shell_surface *
get_shell_surface(struct weston_surface *surface)
{
if (weston_surface_is_desktop_surface(surface)) {
struct weston_desktop_surface *desktop_surface =
weston_surface_get_desktop_surface(surface);
return weston_desktop_surface_get_user_data(desktop_surface);
}
return NULL;
}
/*
* libweston-desktop
*/
static void
desktop_surface_added(struct weston_desktop_surface *desktop_surface,
void *shell)
{
struct weston_desktop_client *client =
weston_desktop_surface_get_client(desktop_surface);
struct wl_client *wl_client =
weston_desktop_client_get_client(client);
struct weston_view *view;
struct shell_surface *shsurf;
struct weston_surface *surface =
weston_desktop_surface_get_surface(desktop_surface);
view = weston_desktop_surface_create_view(desktop_surface);
if (!view)
return;
shsurf = calloc(1, sizeof *shsurf);
if (!shsurf) {
if (wl_client)
wl_client_post_no_memory(wl_client);
else
weston_log("no memory to allocate shell surface\n");
return;
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
}
weston_surface_set_label_func(surface, weston_shell_utils_surface_get_label);
shsurf->shell = (struct desktop_shell *) shell;
shsurf->unresponsive = 0;
shsurf->saved_position_valid = false;
shsurf->saved_rotation_valid = false;
shsurf->desktop_surface = desktop_surface;
shsurf->view = view;
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
shsurf->fullscreen.black_view = NULL;
shell_surface_set_output(
shsurf, weston_shell_utils_get_default_output(shsurf->shell->compositor));
wl_signal_init(&shsurf->destroy_signal);
/* empty when not in use */
wl_list_init(&shsurf->rotation.transform.link);
weston_matrix_init(&shsurf->rotation.rotation);
/*
* initialize list as well as link. The latter allows to use
* wl_list_remove() even when this surface is not in another list.
*/
wl_list_init(&shsurf->children_list);
wl_list_init(&shsurf->children_link);
wl_list_insert(&shsurf->shell->shsurf_list, &shsurf->link);
weston_desktop_surface_set_user_data(desktop_surface, shsurf);
}
static void
desktop_surface_removed(struct weston_desktop_surface *desktop_surface,
void *shell)
{
struct shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
struct weston_surface *surface =
weston_desktop_surface_get_surface(desktop_surface);
struct weston_seat *seat;
if (!shsurf)
return;
wl_list_for_each(seat, &shsurf->shell->compositor->seat_list, link) {
struct shell_seat *shseat = get_shell_seat(seat);
/* activate() controls the focused surface activation and
* removal of a surface requires invalidating the
* focused_surface to avoid activate() use a stale (and just
* removed) surface when attempting to de-activate it. It will
* also update the focused_surface once it has a chance to run.
*/
if (shseat && surface == shseat->focused_surface)
shseat->focused_surface = NULL;
}
desktop-shell: Handle weston_curtain destruction This fixes the following leaks for weston_curtain/weston_buffer_create_solid_rgba when shutting down the compositor: #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f915bfeb8b7 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f915bfec71d in weston_curtain_create ../shell-utils/shell-utils.c:150 #3 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #4 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #5 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #6 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #7 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #8 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #9 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #10 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #11 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #12 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #13 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #14 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f916fe62aa3 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f916fe7398d in weston_buffer_create_solid_rgba ../libweston/compositor.c:2603 #3 0x7f915bfec879 in weston_curtain_create ../shell-utils/shell-utils.c:162 #4 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #5 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #6 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #7 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #8 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #9 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #10 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #11 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #12 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #13 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #14 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #15 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) We do not migrate the weston_curtain destruction from desktop_surface_removed() to desktop_shell_destroy_surface() because we'd want have the curtain removed before the animation starts. If we were to move the black view destruction, *and* only handle it from desktop_shell_destroy_surface() the animation runs but the black curtain will be removed right at the end, effectively diminishing the effect of the animations. To this end, we keep both of the two worlds, if the client terminates on its own, we keep the same animation effect, but if the compositor is shutting down we destroy it immediately. We remove wl_list_for_each_safe() and instead loop each time to avoid using a stale pointer iterator which could cause a UAF as the shsurf would be free'ed. Suggested-by: Pekka Paalanen <pekka.paalanen@collabora.com> Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
2022-05-17 14:49:35 +00:00
if (shsurf->fullscreen.black_view) {
weston_shell_utils_curtain_destroy(shsurf->fullscreen.black_view);
desktop-shell: Handle weston_curtain destruction This fixes the following leaks for weston_curtain/weston_buffer_create_solid_rgba when shutting down the compositor: #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f915bfeb8b7 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f915bfec71d in weston_curtain_create ../shell-utils/shell-utils.c:150 #3 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #4 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #5 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #6 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #7 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #8 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #9 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #10 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #11 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #12 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #13 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #14 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f916fe62aa3 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f916fe7398d in weston_buffer_create_solid_rgba ../libweston/compositor.c:2603 #3 0x7f915bfec879 in weston_curtain_create ../shell-utils/shell-utils.c:162 #4 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #5 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #6 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #7 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #8 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #9 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #10 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #11 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #12 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #13 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #14 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #15 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) We do not migrate the weston_curtain destruction from desktop_surface_removed() to desktop_shell_destroy_surface() because we'd want have the curtain removed before the animation starts. If we were to move the black view destruction, *and* only handle it from desktop_shell_destroy_surface() the animation runs but the black curtain will be removed right at the end, effectively diminishing the effect of the animations. To this end, we keep both of the two worlds, if the client terminates on its own, we keep the same animation effect, but if the compositor is shutting down we destroy it immediately. We remove wl_list_for_each_safe() and instead loop each time to avoid using a stale pointer iterator which could cause a UAF as the shsurf would be free'ed. Suggested-by: Pekka Paalanen <pekka.paalanen@collabora.com> Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
2022-05-17 14:49:35 +00:00
shsurf->fullscreen.black_view = NULL;
}
weston_surface_set_label_func(surface, NULL);
weston_desktop_surface_set_user_data(shsurf->desktop_surface, NULL);
shsurf->desktop_surface = NULL;
if (weston_surface_is_mapped(surface) &&
shsurf->shell->win_close_animation_type == ANIMATION_FADE) {
if (shsurf->shell->compositor->state == WESTON_COMPOSITOR_ACTIVE &&
shsurf->view->output->power_state == WESTON_OUTPUT_POWER_NORMAL) {
struct weston_coord_global pos;
pixman_region32_fini(&surface->pending.input);
pixman_region32_init(&surface->pending.input);
pixman_region32_fini(&surface->input);
pixman_region32_init(&surface->input);
/* 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 */
shsurf->wview_anim_fade = weston_view_create(surface);
weston_view_set_output(shsurf->wview_anim_fade,
shsurf->view->output);
pos = weston_view_get_pos_offset_global(shsurf->view);
weston_view_set_position(shsurf->wview_anim_fade, pos);
weston_view_move_to_layer(shsurf->wview_anim_fade,
&shsurf->view->layer_link);
/* unmap the "original" view, which is owned by
* libweston-desktop */
weston_view_move_to_layer(shsurf->view, NULL);
weston_fade_run(shsurf->wview_anim_fade, 1.0, 0.0, 300.0,
fade_out_done, shsurf);
return;
}
}
desktop_shell_destroy_surface(shsurf);
}
static void
set_maximized_position(struct desktop_shell *shell,
struct shell_surface *shsurf)
{
pixman_rectangle32_t area;
struct weston_geometry geometry;
struct weston_coord_global pos;
struct weston_coord_surface offset;
get_output_work_area(shell, shsurf->output, &area);
geometry = weston_desktop_surface_get_geometry(shsurf->desktop_surface);
pos.c = weston_coord(area.x, area.y);
offset = weston_coord_surface(-geometry.x, -geometry.y, shsurf->view->surface);
weston_view_set_position_with_offset(shsurf->view, pos, offset);
}
static void
set_position_from_xwayland(struct shell_surface *shsurf)
{
struct weston_geometry geometry;
struct weston_coord_surface offs;
assert(shsurf->xwayland.is_set);
geometry = weston_desktop_surface_get_geometry(shsurf->desktop_surface);
offs = weston_coord_surface(-geometry.x, -geometry.y,
shsurf->view->surface);
weston_view_set_position_with_offset(shsurf->view,
shsurf->xwayland.pos,
offs);
#ifdef WM_DEBUG
weston_log("%s: XWM %d, %d; geometry %d, %d; view %f, %f\n",
__func__, (int)shsurf->xwayland.pos.c.x, (int)shsurf->xwayland.pos.c.y,
(int)geometry.x, (int)geometry.y, pos.c.x, pos.c.y);
#endif
}
static void
map(struct desktop_shell *shell, struct shell_surface *shsurf)
{
struct weston_surface *surface =
weston_desktop_surface_get_surface(shsurf->desktop_surface);
struct weston_compositor *compositor = shell->compositor;
struct weston_seat *seat;
/* initial positioning, see also configure() */
if (shsurf->state.fullscreen) {
shell_set_view_fullscreen(shsurf);
} else if (shsurf->state.maximized) {
set_maximized_position(shell, shsurf);
} else if (shsurf->xwayland.is_set) {
set_position_from_xwayland(shsurf);
} else {
weston_view_set_initial_position(shsurf->view, shell);
}
/* XXX: don't map without a buffer! */
weston_surface_map(surface);
/* Surface stacking order, see also activate(). */
shell_surface_update_layer(shsurf);
if (shsurf->state.maximized) {
surface->output = shsurf->output;
weston_view_set_output(shsurf->view, shsurf->output);
}
if (!shell->locked) {
wl_list_for_each(seat, &compositor->seat_list, link)
activate(shell, shsurf->view, seat,
WESTON_ACTIVATE_FLAG_CONFIGURE |
(shsurf->state.fullscreen ?
WESTON_ACTIVATE_FLAG_FULLSCREEN : 0));
}
if (!shsurf->state.fullscreen && !shsurf->state.maximized) {
switch (shell->win_animation_type) {
case ANIMATION_FADE:
weston_fade_run(shsurf->view, 0.0, 1.0, 300.0, NULL, NULL);
break;
case ANIMATION_ZOOM:
weston_zoom_run(shsurf->view, 0.5, 1.0, NULL, NULL);
break;
case ANIMATION_NONE:
default:
break;
}
}
}
static void
desktop_surface_committed(struct weston_desktop_surface *desktop_surface,
struct weston_coord_surface buf_offset, void *data)
{
struct shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
struct weston_surface *surface =
weston_desktop_surface_get_surface(desktop_surface);
struct weston_view *view = shsurf->view;
struct desktop_shell *shell = data;
bool was_fullscreen;
bool was_maximized;
if (!weston_surface_has_content(surface) &&
weston_surface_is_unmapping(surface) &&
shsurf->state.fullscreen) {
unset_fullscreen(shsurf);
return;
}
if (surface->width == 0) {
return;
}
was_fullscreen = shsurf->state.fullscreen;
was_maximized = shsurf->state.maximized;
shsurf->state.fullscreen =
weston_desktop_surface_get_fullscreen(desktop_surface);
shsurf->state.maximized =
weston_desktop_surface_get_maximized(desktop_surface);
if (!weston_surface_is_mapped(surface)) {
map(shell, shsurf);
libweston, desktop-shell: Add a wrapper for weston_surface reference 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: Marius Vlad <marius.vlad@collabora.com>
2022-02-14 20:42:22 +00:00
/* 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) {
libweston, desktop-shell: Add a wrapper for weston_surface reference 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: Marius Vlad <marius.vlad@collabora.com>
2022-02-14 20:42:22 +00:00
shsurf->wsurface_anim_fade =
weston_surface_ref(surface);
}
return;
}
if (buf_offset.c.x == 0 && buf_offset.c.y == 0 &&
shsurf->last_width == surface->width &&
shsurf->last_height == surface->height &&
was_fullscreen == shsurf->state.fullscreen &&
was_maximized == shsurf->state.maximized)
return;
if (was_fullscreen)
unset_fullscreen(shsurf);
if (was_maximized)
unset_maximized(shsurf);
if ((shsurf->state.fullscreen || shsurf->state.maximized) &&
!shsurf->saved_position_valid) {
shsurf->saved_pos = weston_view_get_pos_offset_global(shsurf->view);
shsurf->saved_position_valid = true;
if (!wl_list_empty(&shsurf->rotation.transform.link)) {
wl_list_remove(&shsurf->rotation.transform.link);
wl_list_init(&shsurf->rotation.transform.link);
weston_view_geometry_dirty(shsurf->view);
shsurf->saved_rotation_valid = true;
}
}
weston_view_update_transform(shsurf->view);
if (shsurf->state.fullscreen) {
struct weston_seat *seat;
wl_list_for_each(seat, &surface->compositor->seat_list,link) {
activate(shell, shsurf->view, seat,
WESTON_ACTIVATE_FLAG_CONFIGURE |
WESTON_ACTIVATE_FLAG_FULLSCREEN);
}
} else if (shsurf->state.maximized) {
set_maximized_position(shell, shsurf);
surface->output = shsurf->output;
} else {
struct weston_coord_surface offset = buf_offset;
struct weston_coord_global pos;
if (shsurf->resize_edges) {
offset.c.x = 0;
offset.c.y = 0;
}
if (shsurf->resize_edges & WESTON_DESKTOP_SURFACE_EDGE_LEFT)
offset.c.x = shsurf->last_width - surface->width;
if (shsurf->resize_edges & WESTON_DESKTOP_SURFACE_EDGE_TOP)
offset.c.y = shsurf->last_height - surface->height;
pos = weston_view_get_pos_offset_global(view);
weston_view_set_position_with_offset(shsurf->view, pos, offset);
}
shsurf->last_width = surface->width;
shsurf->last_height = surface->height;
/* XXX: would a fullscreen surface need the same handling? */
if (surface->output) {
wl_list_for_each(view, &surface->views, surface_link)
weston_view_update_transform(view);
}
}
static void
get_maximized_size(struct shell_surface *shsurf, int32_t *width, int32_t *height)
{
struct desktop_shell *shell;
pixman_rectangle32_t area;
shell = shell_surface_get_shell(shsurf);
get_output_work_area(shell, shsurf->output, &area);
*width = area.width;
*height = area.height;
}
static void
set_fullscreen(struct shell_surface *shsurf, bool fullscreen,
struct weston_output *output)
{
struct weston_desktop_surface *desktop_surface = shsurf->desktop_surface;
struct weston_surface *surface =
weston_desktop_surface_get_surface(shsurf->desktop_surface);
weston_desktop_surface_set_fullscreen(desktop_surface, fullscreen);
if (fullscreen) {
/* handle clients launching in fullscreen */
if (output == NULL && !weston_surface_is_mapped(surface)) {
/* Set the output to the one that has focus currently. */
output = weston_shell_utils_get_focused_output(surface->compositor);
}
shell_surface_set_output(shsurf, output);
shsurf->fullscreen_output = shsurf->output;
weston_desktop_surface_set_orientation(shsurf->desktop_surface,
WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE);
set_shsurf_size_maximized_or_fullscreen(shsurf, false, fullscreen);
} else {
int width;
int height;
width = 0;
height = 0;
/* this is a corner case where we set up the surface as
* maximized, then fullscreen, and back to maximized.
*
* we land here here when we're back from fullscreen and we
* were previously maximized: rather than sending (0, 0) send
* the area of the output minus the panels */
struct weston_desktop_surface *dsurface =
shsurf->desktop_surface;
if (weston_desktop_surface_get_maximized(dsurface) ||
weston_desktop_surface_get_pending_maximized(dsurface)) {
get_maximized_size(shsurf, &width, &height);
}
weston_desktop_surface_set_size(shsurf->desktop_surface, width, height);
}
}
static void
desktop_surface_move(struct weston_desktop_surface *desktop_surface,
struct weston_seat *seat, uint32_t serial, void *shell)
{
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
struct weston_touch *touch = weston_seat_get_touch(seat);
struct shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
struct weston_surface *surface =
weston_desktop_surface_get_surface(shsurf->desktop_surface);
struct wl_resource *resource = surface->resource;
struct weston_surface *focus;
if (pointer &&
pointer->focus &&
pointer->button_count > 0 &&
pointer->grab_serial == serial) {
focus = weston_surface_get_main_surface(pointer->focus->surface);
if ((focus == surface) &&
(surface_move(shsurf, pointer, true) < 0))
wl_resource_post_no_memory(resource);
} else if (touch &&
touch->focus &&
touch->grab_serial == serial) {
focus = weston_surface_get_main_surface(touch->focus->surface);
if ((focus == surface) &&
(surface_touch_move(shsurf, touch) < 0))
wl_resource_post_no_memory(resource);
} else if (!wl_list_empty(&seat->tablet_tool_list)) {
struct weston_tablet_tool *tool;
wl_list_for_each(tool, &seat->tablet_tool_list, link) {
if (tool->focus && tool->grab_serial == serial) {
focus = weston_surface_get_main_surface(
tool->focus->surface);
if (focus == surface &&
surface_tablet_tool_move(shsurf, tool) < 0)
wl_resource_post_no_memory(resource);
}
}
}
}
static void
desktop_surface_resize(struct weston_desktop_surface *desktop_surface,
struct weston_seat *seat, uint32_t serial,
enum weston_desktop_surface_edge edges, void *shell)
{
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
struct shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
struct weston_surface *surface =
weston_desktop_surface_get_surface(shsurf->desktop_surface);
struct wl_resource *resource = surface->resource;
struct weston_surface *focus;
if (!pointer ||
pointer->button_count == 0 ||
pointer->grab_serial != serial ||
pointer->focus == NULL)
return;
focus = weston_surface_get_main_surface(pointer->focus->surface);
if (focus != surface)
return;
if (surface_resize(shsurf, pointer, edges) < 0)
wl_resource_post_no_memory(resource);
}
static void
desktop_surface_set_parent(struct weston_desktop_surface *desktop_surface,
struct weston_desktop_surface *parent,
void *shell)
{
struct shell_surface *shsurf_parent;
struct shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
/* unlink any potential child */
wl_list_remove(&shsurf->children_link);
if (parent) {
shsurf_parent = weston_desktop_surface_get_user_data(parent);
wl_list_insert(shsurf_parent->children_list.prev,
&shsurf->children_link);
} else {
wl_list_init(&shsurf->children_link);
}
}
static void
desktop_surface_fullscreen_requested(struct weston_desktop_surface *desktop_surface,
bool fullscreen,
struct weston_output *output, void *shell)
{
struct shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
set_fullscreen(shsurf, fullscreen, output);
}
static void
set_maximized(struct shell_surface *shsurf, bool maximized)
{
struct weston_desktop_surface *desktop_surface = shsurf->desktop_surface;
struct weston_surface *surface =
weston_desktop_surface_get_surface(shsurf->desktop_surface);
if (weston_desktop_surface_get_fullscreen(desktop_surface))
return;
if (maximized) {
struct weston_output *output;
if (!weston_surface_is_mapped(surface))
output = weston_shell_utils_get_focused_output(surface->compositor);
else
output = surface->output;
shell_surface_set_output(shsurf, output);
weston_desktop_surface_set_orientation(shsurf->desktop_surface,
WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE);
}
weston_desktop_surface_set_maximized(desktop_surface, maximized);
set_shsurf_size_maximized_or_fullscreen(shsurf, maximized, false);
}
static void
desktop_surface_maximized_requested(struct weston_desktop_surface *desktop_surface,
bool maximized, void *shell)
{
struct shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
set_maximized(shsurf, maximized);
}
static void
desktop_surface_minimized_requested(struct weston_desktop_surface *desktop_surface,
void *shell)
{
struct weston_surface *surface =
weston_desktop_surface_get_surface(desktop_surface);
/* apply compositor's own minimization logic (hide) */
set_minimized(surface);
}
static void
set_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer)
{
struct shell_grab *grab;
if (pointer->grab->interface == &busy_cursor_grab_interface)
return;
grab = malloc(sizeof *grab);
if (!grab)
return;
shell_grab_start(grab, &busy_cursor_grab_interface, shsurf, pointer,
WESTON_DESKTOP_SHELL_CURSOR_BUSY);
/* Mark the shsurf as ungrabbed so that button binding is able
* to move it. */
shsurf->grabbed = 0;
}
static void
end_busy_cursor(struct weston_compositor *compositor,
struct weston_desktop_client *desktop_client)
{
struct shell_surface *shsurf;
struct shell_grab *grab;
struct weston_seat *seat;
wl_list_for_each(seat, &compositor->seat_list, link) {
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
struct weston_desktop_client *grab_client;
if (!pointer)
continue;
if (pointer->grab->interface != &busy_cursor_grab_interface)
continue;
grab = (struct shell_grab *) pointer->grab;
shsurf = grab->shsurf;
if (!shsurf)
continue;
grab_client =
weston_desktop_surface_get_client(shsurf->desktop_surface);
if (grab_client == desktop_client) {
shell_grab_end(grab);
free(grab);
}
}
}
static void
desktop_surface_set_unresponsive(struct weston_desktop_surface *desktop_surface,
void *user_data)
{
struct shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
bool *unresponsive = user_data;
shsurf->unresponsive = *unresponsive;
}
static void
desktop_surface_ping_timeout(struct weston_desktop_client *desktop_client,
void *shell_)
{
struct desktop_shell *shell = shell_;
struct shell_surface *shsurf;
struct weston_seat *seat;
bool unresponsive = true;
weston_desktop_client_for_each_surface(desktop_client,
desktop_surface_set_unresponsive,
&unresponsive);
wl_list_for_each(seat, &shell->compositor->seat_list, link) {
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
struct weston_desktop_client *grab_client;
if (!pointer || !pointer->focus)
continue;
shsurf = get_shell_surface(pointer->focus->surface);
if (!shsurf)
continue;
grab_client =
weston_desktop_surface_get_client(shsurf->desktop_surface);
if (grab_client == desktop_client)
set_busy_cursor(shsurf, pointer);
}
}
static void
desktop_surface_pong(struct weston_desktop_client *desktop_client,
void *shell_)
{
struct desktop_shell *shell = shell_;
bool unresponsive = false;
weston_desktop_client_for_each_surface(desktop_client,
desktop_surface_set_unresponsive,
&unresponsive);
end_busy_cursor(shell->compositor, desktop_client);
}
static void
desktop_surface_set_xwayland_position(struct weston_desktop_surface *surface,
struct weston_coord_global pos, void *shell_)
{
struct shell_surface *shsurf =
weston_desktop_surface_get_user_data(surface);
shsurf->xwayland.pos = pos;
shsurf->xwayland.is_set = true;
}
static void
desktop_surface_get_position(struct weston_desktop_surface *surface,
int32_t *x, int32_t *y,
void *shell_)
{
struct shell_surface *shsurf = weston_desktop_surface_get_user_data(surface);
*x = shsurf->view->geometry.pos_offset.x;
*y = shsurf->view->geometry.pos_offset.y;
}
static const struct weston_desktop_api shell_desktop_api = {
.struct_size = sizeof(struct weston_desktop_api),
.surface_added = desktop_surface_added,
.surface_removed = desktop_surface_removed,
.committed = desktop_surface_committed,
.move = desktop_surface_move,
.resize = desktop_surface_resize,
.set_parent = desktop_surface_set_parent,
.fullscreen_requested = desktop_surface_fullscreen_requested,
.maximized_requested = desktop_surface_maximized_requested,
.minimized_requested = desktop_surface_minimized_requested,
.ping_timeout = desktop_surface_ping_timeout,
.pong = desktop_surface_pong,
.set_xwayland_position = desktop_surface_set_xwayland_position,
.get_position = desktop_surface_get_position,
};
/* ************************ *
* end of libweston-desktop *
* ************************ */
static int
background_get_label(struct weston_surface *surface, char *buf, size_t len)
{
return snprintf(buf, len, "background for output %s",
(surface->output ? surface->output->name : "NULL"));
}
static void
background_committed(struct weston_surface *es,
struct weston_coord_surface new_origin)
{
struct shell_output *sh_output = es->committed_private;
struct desktop_shell *shell = sh_output->shell;
if (!weston_surface_has_content(es))
return;
if (!weston_surface_is_mapped(es)) {
weston_surface_map(es);
assert(wl_list_empty(&es->views));
sh_output->background_view = weston_view_create(es);
}
assert(sh_output->background_view);
weston_view_set_position(sh_output->background_view,
sh_output->output->pos);
weston_view_move_to_layer(sh_output->background_view,
&shell->background_layer.view_list);
}
static void
handle_background_surface_destroy(struct wl_listener *listener, void *data)
{
struct shell_output *output =
container_of(listener, struct shell_output, background_surface_listener);
wl_list_remove(&output->background_surface_listener.link);
output->background_surface = NULL;
output->background_view = NULL;
}
static void
desktop_shell_set_background(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *output_resource,
struct wl_resource *surface_resource)
{
struct desktop_shell *shell = wl_resource_get_user_data(resource);
struct weston_surface *surface =
wl_resource_get_user_data(surface_resource);
struct shell_output *sh_output;
struct weston_head *head = weston_head_from_resource(output_resource);
if (surface->committed) {
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"surface role already assigned");
return;
}
if (!head)
return;
surface->output = head->output;
sh_output = find_shell_output_from_weston_output(shell, surface->output);
if (sh_output->background_surface) {
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"output already has a background surface");
return;
}
surface->committed = background_committed;
surface->committed_private = sh_output;
weston_surface_set_label_func(surface, background_get_label);
weston_desktop_shell_send_configure(resource, 0,
surface_resource,
surface->output->width,
surface->output->height);
sh_output->background_surface = surface;
sh_output->background_surface_listener.notify =
handle_background_surface_destroy;
wl_signal_add(&surface->destroy_signal,
&sh_output->background_surface_listener);
}
static int
panel_get_label(struct weston_surface *surface, char *buf, size_t len)
{
return snprintf(buf, len, "panel for output %s",
(surface->output ? surface->output->name : "NULL"));
}
static void
panel_committed(struct weston_surface *es,
struct weston_coord_surface new_origin)
{
struct shell_output *sh_output = es->committed_private;
struct weston_output *output = sh_output->output;
struct weston_coord_global pos = output->pos;
struct desktop_shell *shell = sh_output->shell;
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
if (!weston_surface_has_content(es))
return;
switch (shell->panel_position) {
case WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP:
case WESTON_DESKTOP_SHELL_PANEL_POSITION_LEFT:
sh_output->panel_offset.c = weston_coord(0, 0);
break;
case WESTON_DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
sh_output->panel_offset.c =
weston_coord(0, output->height - es->height);
break;
case WESTON_DESKTOP_SHELL_PANEL_POSITION_RIGHT:
sh_output->panel_offset.c =
weston_coord(output->width - es->width, 0);
break;
default:
unreachable("unknown panel position");
break;
}
if (!weston_surface_is_mapped(es)) {
weston_surface_map(es);
assert(wl_list_empty(&es->views));
sh_output->panel_view = weston_view_create(es);
}
assert(sh_output->panel_view);
pos = weston_coord_global_add(output->pos, sh_output->panel_offset);
weston_view_set_position(sh_output->panel_view, pos);
weston_view_move_to_layer(sh_output->panel_view,
&shell->panel_layer.view_list);
}
static void
handle_panel_surface_destroy(struct wl_listener *listener, void *data)
{
struct shell_output *output =
container_of(listener, struct shell_output, panel_surface_listener);
wl_list_remove(&output->panel_surface_listener.link);
output->panel_surface = NULL;
output->panel_view = NULL;
}
static void
desktop_shell_set_panel(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *output_resource,
struct wl_resource *surface_resource)
{
struct desktop_shell *shell = wl_resource_get_user_data(resource);
struct weston_surface *surface =
wl_resource_get_user_data(surface_resource);
struct shell_output *sh_output;
struct weston_head *head = weston_head_from_resource(output_resource);
if (surface->committed) {
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"surface role already assigned");
return;
}
if (!head)
return;
surface->output = head->output;
sh_output = find_shell_output_from_weston_output(shell, surface->output);
if (sh_output->panel_surface) {
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"output already has a panel surface");
return;
}
surface->committed = panel_committed;
surface->committed_private = sh_output;
weston_surface_set_label_func(surface, panel_get_label);
weston_desktop_shell_send_configure(resource, 0,
surface_resource,
surface->output->width,
surface->output->height);
sh_output->panel_surface = surface;
sh_output->panel_surface_listener.notify = handle_panel_surface_destroy;
wl_signal_add(&surface->destroy_signal, &sh_output->panel_surface_listener);
}
static int
lock_surface_get_label(struct weston_surface *surface, char *buf, size_t len)
{
return snprintf(buf, len, "lock window");
}
static void
lock_surface_committed(struct weston_surface *surface,
struct weston_coord_surface new_origin)
{
struct desktop_shell *shell = surface->committed_private;
if (!weston_surface_has_content(surface))
return;
if (weston_surface_is_mapped(surface))
return;
weston_surface_map(surface);
assert(!shell->lock_view);
shell->lock_view = weston_view_create(surface);
weston_shell_utils_center_on_output(shell->lock_view,
weston_shell_utils_get_default_output(shell->compositor));
weston_view_move_to_layer(shell->lock_view,
&shell->lock_layer.view_list);
shell_fade(shell, FADE_IN);
}
static void
handle_lock_surface_destroy(struct wl_listener *listener, void *data)
{
struct desktop_shell *shell =
container_of(listener, struct desktop_shell, lock_surface_listener);
shell->lock_surface = NULL;
shell->lock_view = NULL;
}
static void
desktop_shell_set_lock_surface(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *surface_resource)
{
struct desktop_shell *shell = wl_resource_get_user_data(resource);
struct weston_surface *surface =
wl_resource_get_user_data(surface_resource);
shell->prepare_event_sent = false;
if (!shell->locked)
return;
if (shell->lock_surface) {
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"already have a lock surface");
return;
}
surface->committed = lock_surface_committed;
surface->committed_private = shell;
weston_surface_set_label_func(surface, lock_surface_get_label);
shell->lock_surface = surface;
shell->lock_surface_listener.notify = handle_lock_surface_destroy;
wl_signal_add(&surface->destroy_signal,
&shell->lock_surface_listener);
}
static void
resume_desktop(struct desktop_shell *shell)
{
struct workspace *ws = get_current_workspace(shell);
weston_layer_unset_position(&shell->lock_layer);
if (shell->showing_input_panels)
weston_layer_set_position(&shell->input_panel_layer,
WESTON_LAYER_POSITION_TOP_UI);
weston_layer_set_position(&shell->fullscreen_layer,
WESTON_LAYER_POSITION_FULLSCREEN);
weston_layer_set_position(&shell->panel_layer,
WESTON_LAYER_POSITION_UI);
weston_layer_set_position(&ws->layer, WESTON_LAYER_POSITION_NORMAL);
restore_focus_state(shell, get_current_workspace(shell));
shell->locked = false;
shell_fade(shell, FADE_IN);
weston_compositor_damage_all(shell->compositor);
}
static void
desktop_shell_unlock(struct wl_client *client,
struct wl_resource *resource)
{
struct desktop_shell *shell = wl_resource_get_user_data(resource);
shell->prepare_event_sent = false;
if (shell->locked)
resume_desktop(shell);
}
static void
desktop_shell_set_grab_surface(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *surface_resource)
{
struct desktop_shell *shell = wl_resource_get_user_data(resource);
shell->grab_surface = wl_resource_get_user_data(surface_resource);
weston_view_create(shell->grab_surface);
}
static void
desktop_shell_desktop_ready(struct wl_client *client,
struct wl_resource *resource)
{
struct desktop_shell *shell = wl_resource_get_user_data(resource);
shell_fade_startup(shell);
}
static void
desktop_shell_set_panel_position(struct wl_client *client,
struct wl_resource *resource,
uint32_t position)
{
struct desktop_shell *shell = wl_resource_get_user_data(resource);
if (position != WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP &&
position != WESTON_DESKTOP_SHELL_PANEL_POSITION_BOTTOM &&
position != WESTON_DESKTOP_SHELL_PANEL_POSITION_LEFT &&
position != WESTON_DESKTOP_SHELL_PANEL_POSITION_RIGHT) {
wl_resource_post_error(resource,
WESTON_DESKTOP_SHELL_ERROR_INVALID_ARGUMENT,
"bad position argument");
return;
}
shell->panel_position = position;
}
static const struct weston_desktop_shell_interface desktop_shell_implementation = {
desktop_shell_set_background,
desktop_shell_set_panel,
desktop_shell_set_lock_surface,
desktop_shell_unlock,
desktop_shell_set_grab_surface,
desktop_shell_desktop_ready,
desktop_shell_set_panel_position
};
2011-04-12 21:25:42 +00:00
static void
move_binding(struct weston_pointer *pointer, const struct timespec *time,
uint32_t button, void *data)
2011-04-12 21:25:42 +00:00
{
struct weston_surface *focus;
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
struct weston_surface *surface;
struct shell_surface *shsurf;
if (pointer->focus == NULL)
return;
focus = pointer->focus->surface;
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
surface = weston_surface_get_main_surface(focus);
if (surface == NULL)
return;
shsurf = get_shell_surface(surface);
if (shsurf == NULL || shsurf_is_max_or_fullscreen(shsurf))
return;
surface_move(shsurf, pointer, false);
2011-04-12 21:25:42 +00:00
}
static void
maximize_binding(struct weston_keyboard *keyboard, const struct timespec *time,
uint32_t button, void *data)
{
struct weston_surface *focus = keyboard->focus;
struct weston_surface *surface;
struct shell_surface *shsurf;
surface = weston_surface_get_main_surface(focus);
if (surface == NULL)
return;
shsurf = get_shell_surface(surface);
if (shsurf == NULL)
return;
set_maximized(shsurf, !weston_desktop_surface_get_maximized(shsurf->desktop_surface));
}
static void
fullscreen_binding(struct weston_keyboard *keyboard,
const struct timespec *time, uint32_t button, void *data)
{
struct weston_surface *focus = keyboard->focus;
struct weston_surface *surface;
struct shell_surface *shsurf;
bool fullscreen;
surface = weston_surface_get_main_surface(focus);
if (surface == NULL)
return;
shsurf = get_shell_surface(surface);
if (shsurf == NULL)
return;
fullscreen =
weston_desktop_surface_get_fullscreen(shsurf->desktop_surface);
set_fullscreen(shsurf, !fullscreen, NULL);
}
static void
set_tiled_orientation(struct weston_surface *focus,
enum weston_top_level_tiled_orientation orientation)
{
struct weston_surface *surface;
struct shell_surface *shsurf;
int width, height;
pixman_rectangle32_t area;
struct weston_geometry geom;
struct weston_coord_global pos;
int x, y;
surface = weston_surface_get_main_surface(focus);
if (surface == NULL)
return;
shsurf = get_shell_surface(surface);
if (shsurf == NULL)
return;
shsurf->orientation = orientation;
get_maximized_size(shsurf, &width, &height);
get_output_work_area(shsurf->shell, shsurf->output, &area);
geom = weston_desktop_surface_get_geometry(shsurf->desktop_surface);
if (orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_LEFT ||
orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_RIGHT)
width /= 2;
else if (orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_TOP ||
orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_BOTTOM)
height /= 2;
x = area.x - geom.x;
y = area.y - geom.y;
if (orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_RIGHT)
x += width;
else if (orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_BOTTOM)
y += height;
pos.c = weston_coord(x, y);
weston_view_set_position(shsurf->view, pos);
weston_desktop_surface_set_size(shsurf->desktop_surface, width, height);
weston_desktop_surface_set_orientation(shsurf->desktop_surface, orientation);
}
static void
set_tiled_orientation_left(struct weston_keyboard *keyboard,
const struct timespec *time,
uint32_t button, void *data)
{
set_tiled_orientation(keyboard->focus, WESTON_TOP_LEVEL_TILED_ORIENTATION_LEFT);
}
static void
set_tiled_orientation_right(struct weston_keyboard *keyboard,
const struct timespec *time,
uint32_t button, void *data)
{
set_tiled_orientation(keyboard->focus, WESTON_TOP_LEVEL_TILED_ORIENTATION_RIGHT);
}
static void
set_tiled_orientation_up(struct weston_keyboard *keyboard,
const struct timespec *time,
uint32_t button, void *data)
{
set_tiled_orientation(keyboard->focus, WESTON_TOP_LEVEL_TILED_ORIENTATION_TOP);
}
static void
set_tiled_orientation_down(struct weston_keyboard *keyboard,
const struct timespec *time,
uint32_t button, void *data)
{
set_tiled_orientation(keyboard->focus, WESTON_TOP_LEVEL_TILED_ORIENTATION_BOTTOM);
}
static void
touch_move_binding(struct weston_touch *touch, const struct timespec *time, void *data)
{
struct weston_surface *focus;
struct weston_surface *surface;
struct shell_surface *shsurf;
if (touch->focus == NULL)
return;
focus = touch->focus->surface;
surface = weston_surface_get_main_surface(focus);
if (surface == NULL)
return;
shsurf = get_shell_surface(surface);
if (shsurf == NULL || shsurf_is_max_or_fullscreen(shsurf))
return;
surface_touch_move(shsurf, touch);
}
2011-04-12 21:25:42 +00:00
static void
resize_binding(struct weston_pointer *pointer, const struct timespec *time,
uint32_t button, void *data)
2011-04-12 21:25:42 +00:00
{
struct weston_surface *focus;
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
struct weston_surface *surface;
2011-04-12 21:25:42 +00:00
uint32_t edges = 0;
int32_t x, y;
struct shell_surface *shsurf;
struct weston_coord_surface surf_pos;
if (pointer->focus == NULL)
return;
focus = pointer->focus->surface;
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
surface = weston_surface_get_main_surface(focus);
if (surface == NULL)
return;
shsurf = get_shell_surface(surface);
if (shsurf == NULL || shsurf_is_max_or_fullscreen(shsurf))
return;
surf_pos = weston_coord_global_to_surface(shsurf->view, pointer->grab_pos);
x = surf_pos.c.x;
y = surf_pos.c.y;
2011-04-12 21:25:42 +00:00
if (x < surface->width / 3)
edges |= WESTON_DESKTOP_SURFACE_EDGE_LEFT;
else if (x < 2 * surface->width / 3)
2011-04-12 21:25:42 +00:00
edges |= 0;
else
edges |= WESTON_DESKTOP_SURFACE_EDGE_RIGHT;
2011-04-12 21:25:42 +00:00
if (y < surface->height / 3)
edges |= WESTON_DESKTOP_SURFACE_EDGE_TOP;
else if (y < 2 * surface->height / 3)
2011-04-12 21:25:42 +00:00
edges |= 0;
else
edges |= WESTON_DESKTOP_SURFACE_EDGE_BOTTOM;
surface_resize(shsurf, pointer, edges);
}
2012-03-22 17:01:03 +00:00
static void
surface_opacity_binding(struct weston_pointer *pointer,
const struct timespec *time,
struct weston_pointer_axis_event *event,
void *data)
2012-03-22 17:01:03 +00:00
{
float step = 0.005;
2012-03-22 17:01:03 +00:00
struct shell_surface *shsurf;
struct weston_surface *focus = pointer->focus->surface;
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
struct weston_surface *surface;
float alpha;
2012-03-22 17:01:03 +00:00
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
/* XXX: broken for windows containing sub-surfaces */
surface = weston_surface_get_main_surface(focus);
2012-03-22 17:01:03 +00:00
if (surface == NULL)
return;
shsurf = get_shell_surface(surface);
if (!shsurf)
return;
alpha = shsurf->view->alpha - (event->value * step);
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
if (shsurf->view->alpha > 1.0)
shsurf->view->alpha = 1.0;
if (shsurf->view->alpha < step)
shsurf->view->alpha = step;
2012-03-22 17:01:03 +00:00
weston_view_set_alpha(shsurf->view, alpha);
2012-03-22 17:01:03 +00:00
}
static void
terminate_binding(struct weston_keyboard *keyboard, const struct timespec *time,
uint32_t key, void *data)
{
struct weston_compositor *compositor = data;
weston_compositor_exit(compositor);
}
static void
rotate_grab_motion(struct weston_pointer_grab *grab,
const struct timespec *time,
struct weston_pointer_motion_event *event)
{
struct rotate_grab *rotate =
container_of(grab, struct rotate_grab, base.grab);
struct weston_pointer *pointer = grab->pointer;
struct shell_surface *shsurf = rotate->base.shsurf;
struct weston_surface *surface;
2012-09-30 00:57:21 +00:00
float cx, cy, dx, dy, cposx, cposy, dposx, dposy, r;
weston_pointer_move(pointer, event);
if (!shsurf)
return;
surface = weston_desktop_surface_get_surface(shsurf->desktop_surface);
cx = 0.5f * surface->width;
cy = 0.5f * surface->height;
dx = pointer->pos.c.x - rotate->center.x;
dy = pointer->pos.c.y - rotate->center.y;
r = sqrtf(dx * dx + dy * dy);
if (r > 20.0f) {
struct weston_matrix *matrix =
&shsurf->rotation.transform.matrix;
weston_matrix_init(&rotate->rotation);
weston_matrix_rotate_xy(&rotate->rotation, dx / r, dy / r);
weston_matrix_init(matrix);
weston_matrix_translate(matrix, -cx, -cy, 0.0f);
weston_matrix_multiply(matrix, &shsurf->rotation.rotation);
weston_matrix_multiply(matrix, &rotate->rotation);
weston_matrix_translate(matrix, cx, cy, 0.0f);
weston_view_add_transform(shsurf->view,
&shsurf->view->geometry.transformation_list,
&shsurf->rotation.transform);
} else {
weston_view_remove_transform(shsurf->view,
&shsurf->rotation.transform);
weston_matrix_init(&shsurf->rotation.rotation);
weston_matrix_init(&rotate->rotation);
}
/* We need to adjust the position of the surface
* in case it was resized in a rotated state before */
cposx = shsurf->view->geometry.pos_offset.x + cx;
cposy = shsurf->view->geometry.pos_offset.y + cy;
dposx = rotate->center.x - cposx;
dposy = rotate->center.y - cposy;
if (dposx != 0.0f || dposy != 0.0f) {
struct weston_coord_global pos;
pos = weston_view_get_pos_offset_global(shsurf->view);
pos.c.x += dposx;
pos.c.y += dposy;
weston_view_set_position(shsurf->view, pos);
}
weston_view_update_transform(shsurf->view);
weston_surface_damage(shsurf->view->surface);
}
static void
rotate_grab_button(struct weston_pointer_grab *grab,
const struct timespec *time,
uint32_t button, uint32_t state_w)
{
struct rotate_grab *rotate =
container_of(grab, struct rotate_grab, base.grab);
struct weston_pointer *pointer = grab->pointer;
struct shell_surface *shsurf = rotate->base.shsurf;
enum wl_pointer_button_state state = state_w;
if (pointer->button_count == 0 &&
state == WL_POINTER_BUTTON_STATE_RELEASED) {
if (shsurf)
weston_matrix_multiply(&shsurf->rotation.rotation,
&rotate->rotation);
shell_grab_end(&rotate->base);
free(rotate);
}
}
static void
rotate_grab_cancel(struct weston_pointer_grab *grab)
{
struct rotate_grab *rotate =
container_of(grab, struct rotate_grab, base.grab);
shell_grab_end(&rotate->base);
free(rotate);
}
static const struct weston_pointer_grab_interface rotate_grab_interface = {
noop_grab_focus,
rotate_grab_motion,
rotate_grab_button,
noop_grab_axis,
noop_grab_axis_source,
noop_grab_frame,
rotate_grab_cancel,
};
static void
surface_rotate(struct shell_surface *shsurf, struct weston_pointer *pointer)
{
struct weston_surface *surface =
weston_desktop_surface_get_surface(shsurf->desktop_surface);
struct rotate_grab *rotate;
struct weston_coord_surface center;
struct weston_coord_global center_g;
2012-09-30 00:57:21 +00:00
float dx, dy;
float r;
rotate = malloc(sizeof *rotate);
if (!rotate)
return;
center = weston_coord_surface(surface->width * 0.5f,
surface->height * 0.5f,
shsurf->view->surface);
center_g = weston_coord_surface_to_global(shsurf->view, center);
rotate->center.x = center_g.c.x;
rotate->center.y = center_g.c.y;
dx = pointer->pos.c.x - rotate->center.x;
dy = pointer->pos.c.y - rotate->center.y;
r = sqrtf(dx * dx + dy * dy);
if (r > 20.0f) {
struct weston_matrix inverse;
weston_matrix_init(&inverse);
weston_matrix_rotate_xy(&inverse, dx / r, -dy / r);
weston_matrix_multiply(&shsurf->rotation.rotation, &inverse);
weston_matrix_init(&rotate->rotation);
weston_matrix_rotate_xy(&rotate->rotation, dx / r, dy / r);
} else {
weston_matrix_init(&shsurf->rotation.rotation);
weston_matrix_init(&rotate->rotation);
}
shell_grab_start(&rotate->base, &rotate_grab_interface, shsurf,
pointer, WESTON_DESKTOP_SHELL_CURSOR_ARROW);
}
static void
rotate_binding(struct weston_pointer *pointer, const struct timespec *time,
uint32_t button, void *data)
{
struct weston_surface *focus;
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
struct weston_surface *base_surface;
struct shell_surface *surface;
if (pointer->focus == NULL)
return;
focus = pointer->focus->surface;
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
base_surface = weston_surface_get_main_surface(focus);
if (base_surface == NULL)
return;
surface = get_shell_surface(base_surface);
if (surface == NULL || shsurf_is_max_or_fullscreen(surface))
return;
surface_rotate(surface, pointer);
}
/* Move all fullscreen layers down to the current workspace and hide their
* black views. The surfaces' state is set to both fullscreen and lowered,
* and this is reversed when such a surface is re-configured, see
* shell_set_view_fullscreen().
*
* lowering_output = NULL - Lower on all outputs, else only lower on the
* specified output.
*
* This should be used when implementing shell-wide overlays, such as
* the alt-tab switcher, which need to de-promote fullscreen layers. */
void
lower_fullscreen_layer(struct desktop_shell *shell,
struct weston_output *lowering_output)
{
struct workspace *ws;
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
struct weston_view *view, *prev;
ws = get_current_workspace(shell);
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
wl_list_for_each_reverse_safe(view, prev,
&shell->fullscreen_layer.view_list.link,
layer_link.link) {
struct shell_surface *shsurf = get_shell_surface(view->surface);
if (!shsurf)
continue;
/* Only lower surfaces which have lowering_output as their fullscreen
* output, unless a NULL output asks for lowering on all outputs.
*/
if (lowering_output && (shsurf->fullscreen_output != lowering_output))
continue;
if (shsurf->fullscreen.black_view)
weston_view_move_to_layer(shsurf->fullscreen.black_view->view, NULL);
/* Lower the view to the workspace layer */
weston_view_move_to_layer(view, &ws->layer.view_list);
shsurf->state.lowered = true;
}
}
static struct shell_surface *get_last_child(struct shell_surface *shsurf)
{
struct shell_surface *shsurf_child;
wl_list_for_each_reverse(shsurf_child, &shsurf->children_list, children_link) {
if (weston_view_is_mapped(shsurf_child->view))
return shsurf_child;
}
return NULL;
}
void
activate(struct desktop_shell *shell, struct weston_view *view,
struct weston_seat *seat, uint32_t flags)
{
struct weston_surface *es = view->surface;
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
struct weston_surface *main_surface;
struct focus_state *state;
struct workspace *ws;
struct weston_surface *old_es;
struct shell_surface *shsurf, *shsurf_child;
struct shell_seat *shseat = get_shell_seat(seat);
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
main_surface = weston_surface_get_main_surface(es);
shsurf = get_shell_surface(main_surface);
assert(shsurf);
shsurf_child = get_last_child(shsurf);
if (shsurf_child) {
/* Activate last xdg child instead of parent. */
activate(shell, shsurf_child->view, seat, flags);
return;
}
/* Only demote fullscreen surfaces on the output of activated shsurf.
* Leave fullscreen surfaces on unrelated outputs alone. */
if (shsurf->output)
lower_fullscreen_layer(shell, shsurf->output);
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
weston_view_activate_input(view, seat, flags);
if (shseat && shseat->focused_surface &&
shseat->focused_surface != main_surface) {
struct shell_surface *current_focus =
get_shell_surface(shseat->focused_surface);
assert(current_focus);
shell_surface_deactivate(current_focus);
}
if (shseat && shseat->focused_surface != main_surface) {
shell_surface_activate(shsurf);
shseat->focused_surface = main_surface;
}
state = ensure_focus_state(shell, seat);
if (state == NULL)
return;
old_es = state->keyboard_focus;
focus_state_set_focus(state, es);
if (weston_desktop_surface_get_fullscreen(shsurf->desktop_surface) &&
flags & WESTON_ACTIVATE_FLAG_CONFIGURE)
shell_set_view_fullscreen(shsurf);
/* Update the surfaces layer. This brings it to the top of the stacking
* order as appropriate. */
shell_surface_update_layer(shsurf);
if (shell->focus_animation_type != ANIMATION_NONE) {
assert(shell->focus_animation_type == ANIMATION_DIM_LAYER);
ws = get_current_workspace(shell);
animate_focus_change(shell, ws, get_default_view(old_es), get_default_view(es));
}
}
static void
activate_binding(struct weston_seat *seat,
struct desktop_shell *shell,
struct weston_view *focus_view,
uint32_t flags)
{
struct weston_view *main_view;
shell: keyboard focus and restacking fixes for sub-surfaces The shell needs to redirect some actions to the parent surface, when they originally target a sub-surface. This patch implements the following: - Move, resize, and rotate bindings always target the parent surface. - Opacity (full-surface alpha) binding targets the parent surface. This is broken, because it should change the opacity of the whole compound window, which is difficult to implement in the renderer. - click_to_activate_binding() needs to check the shell surface type from the main surface, because sub-surface would produce SHELL_SURFACE_NONE and prevent activation. - Also activate() needs to check the type from the main surface, and restack the main surface. Keyboard focus is assigned to the original (sub-)surface. - focus_state_surface_destroy() needs to handle sub-surfaces: only the main surface will be in a layer list. If the destroyed surface is indeed a sub-surface, activate the main surface next. This way a client that destroys a focused sub-surface still retains focus in the same window. - The workspace_manager.move_surface request can accept also sub-surfaces, and it will move the corresponding main surface. Changes in v2: - do not special-case keyboard focus for sub-surfaces - fix surface type checks for sub-surfaces in shell, fix restacking of sub-surfaces in shell, fix focus_state_surface_destroy() Changes in v3: - Renamed weston_surface_get_parent() to weston_surface_get_main_surface() to be more explicit that this is about sub-surfaces - Fixed move_surface_to_workspace() to handle keyboard focus on a sub-surface. - Used a temporary variable in several places to clarify code, instead of reassigning a variable. - Fixed workspace_manager_move_surface() to deal with sub-surfaces. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
2013-04-25 10:57:44 +00:00
struct weston_surface *main_surface;
if (!focus_view)
return;
if (is_black_surface_view(focus_view, &main_view))
focus_view = main_view;
main_surface = weston_surface_get_main_surface(focus_view->surface);
if (!get_shell_surface(main_surface))
return;
activate(shell, focus_view, seat, flags);
}
static void
click_to_activate_binding(struct weston_pointer *pointer,
const struct timespec *time,
uint32_t button, void *data)
{
if (pointer->grab != &pointer->default_grab)
return;
if (pointer->focus == NULL)
return;
activate_binding(pointer->seat, data, pointer->focus,
WESTON_ACTIVATE_FLAG_CLICKED |
WESTON_ACTIVATE_FLAG_CONFIGURE);
}
static void
touch_to_activate_binding(struct weston_touch *touch,
const struct timespec *time,
void *data)
{
if (touch->grab != &touch->default_grab)
return;
if (touch->focus == NULL)
return;
activate_binding(touch->seat, data, touch->focus,
WESTON_ACTIVATE_FLAG_CONFIGURE);
}
static void
tablet_tool_activate_binding(struct weston_tablet_tool *tool,
uint32_t button, void *data)
{
if (tool->grab != &tool->default_grab)
return;
if (tool->focus == NULL)
return;
activate_binding(tool->seat, data, tool->focus,
WESTON_ACTIVATE_FLAG_CLICKED |
WESTON_ACTIVATE_FLAG_CONFIGURE);
}
static void
unfocus_all_seats(struct desktop_shell *shell)
{
struct weston_seat *seat, *next;
wl_list_for_each_safe(seat, next, &shell->compositor->seat_list, link) {
struct weston_keyboard *keyboard =
weston_seat_get_keyboard(seat);
if (!keyboard)
continue;
weston_keyboard_set_focus(keyboard, NULL);
}
}
static void
lock(struct desktop_shell *shell)
{
struct workspace *ws = get_current_workspace(shell);
if (shell->locked) {
weston_compositor_sleep(shell->compositor);
return;
}
shell->locked = true;
/* Hide all surfaces by removing the fullscreen, panel and
* toplevel layers. This way nothing else can show or receive
* input events while we are locked. */
weston_layer_unset_position(&shell->panel_layer);
weston_layer_unset_position(&shell->fullscreen_layer);
if (shell->showing_input_panels)
weston_layer_unset_position(&shell->input_panel_layer);
weston_layer_unset_position(&ws->layer);
weston_layer_set_position(&shell->lock_layer,
WESTON_LAYER_POSITION_LOCK);
weston_compositor_sleep(shell->compositor);
/* Remove the keyboard focus on all seats. This will be
* restored to the workspace's saved state via
* restore_focus_state when the compositor is unlocked */
unfocus_all_seats(shell);
/* TODO: disable bindings that should not work while locked. */
/* All this must be undone in resume_desktop(). */
}
static void
unlock(struct desktop_shell *shell)
{
struct wl_resource *shell_resource;
if (!shell->locked || shell->lock_surface) {
shell_fade(shell, FADE_IN);
return;
}
/* If desktop-shell client has gone away, unlock immediately. */
if (!shell->child.desktop_shell) {
resume_desktop(shell);
return;
}
if (shell->prepare_event_sent)
return;
shell_resource = shell->child.desktop_shell;
weston_desktop_shell_send_prepare_lock_surface(shell_resource);
shell->prepare_event_sent = true;
}
static void
shell_fade_done(struct weston_view_animation *animation, void *data)
{
struct desktop_shell *shell = data;
shell->fade.animation = NULL;
switch (shell->fade.type) {
case FADE_IN:
weston_shell_utils_curtain_destroy(shell->fade.curtain);
shell->fade.curtain = NULL;
break;
case FADE_OUT:
lock(shell);
break;
default:
break;
}
}
static int
fade_surface_get_label(struct weston_surface *surface,
char *buf, size_t len)
{
return snprintf(buf, len, "desktop shell fade surface");
}
desktop-shell: Reuse curtains for fades Use the common infrastructure we have, rather than open-coding again. In changing to the common weston_curtain infrastructure which was introduced in a previous commit, there are two small functional derivations. After adding the view to a layer, we explicitly call weston_view_geometry_dirty(). This is believed to have no functional impact, as weston_views have their geometry-dirty flag set when they are created. weston_surface_damage() is also called to ensure that the surface is scheduled for a repaint. As there is currently no good common mechanic in the common code to ensure that output repaint will occur, due to the damage propagating outwards from the weston_surface, we are forced to call this to predictably ensure that the output will be repainted after we have made this change to the scene graph which should result in the user observing the new content being repainted. It is possible that these changes are not strictly required in order for the correct behaviour to occur. However, it is felt that explicitly adding these calls is perhaps the least fragile way to ensure that the behaviour continues to be correct and breakage is not observed, especially with both view mapping and surface damage identified as areas for future work which could be beneficial to Weston. If it is felt that these calls can be removed, then this is certainly possible at a later stage. Lastly, there are many users within desktop-shell of the common pattern of creating a weston_curtain and inserting it into the scene graph to be painted. These users have been an area of both theoretical concern and real-life observed fragility and breakage recently. By making each user follow a common and predictable pattern of usage, each user is no longer its own special case. This should make it easier to make the desktop-shell codebase more robust, not only simplifying the codebase, but improving observed behaviour. In doing this, it is hoped that future structural and interface changes become easier to make, allowing us to improve some of the more difficult corners of the libweston API. Signed-off-by: Daniel Stone <daniels@collabora.com>
2022-01-18 23:01:56 +00:00
static struct weston_curtain *
shell_fade_create_view(struct desktop_shell *shell)
{
struct weston_compositor *compositor = shell->compositor;
struct shell_output *shell_output;
desktop-shell: Reuse curtains for fades Use the common infrastructure we have, rather than open-coding again. In changing to the common weston_curtain infrastructure which was introduced in a previous commit, there are two small functional derivations. After adding the view to a layer, we explicitly call weston_view_geometry_dirty(). This is believed to have no functional impact, as weston_views have their geometry-dirty flag set when they are created. weston_surface_damage() is also called to ensure that the surface is scheduled for a repaint. As there is currently no good common mechanic in the common code to ensure that output repaint will occur, due to the damage propagating outwards from the weston_surface, we are forced to call this to predictably ensure that the output will be repainted after we have made this change to the scene graph which should result in the user observing the new content being repainted. It is possible that these changes are not strictly required in order for the correct behaviour to occur. However, it is felt that explicitly adding these calls is perhaps the least fragile way to ensure that the behaviour continues to be correct and breakage is not observed, especially with both view mapping and surface damage identified as areas for future work which could be beneficial to Weston. If it is felt that these calls can be removed, then this is certainly possible at a later stage. Lastly, there are many users within desktop-shell of the common pattern of creating a weston_curtain and inserting it into the scene graph to be painted. These users have been an area of both theoretical concern and real-life observed fragility and breakage recently. By making each user follow a common and predictable pattern of usage, each user is no longer its own special case. This should make it easier to make the desktop-shell codebase more robust, not only simplifying the codebase, but improving observed behaviour. In doing this, it is hoped that future structural and interface changes become easier to make, allowing us to improve some of the more difficult corners of the libweston API. Signed-off-by: Daniel Stone <daniels@collabora.com>
2022-01-18 23:01:56 +00:00
struct weston_curtain_params curtain_params = {
.r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0,
.surface_committed = black_surface_committed,
.get_label = fade_surface_get_label,
.surface_private = shell,
desktop-shell: Reuse curtains for fades Use the common infrastructure we have, rather than open-coding again. In changing to the common weston_curtain infrastructure which was introduced in a previous commit, there are two small functional derivations. After adding the view to a layer, we explicitly call weston_view_geometry_dirty(). This is believed to have no functional impact, as weston_views have their geometry-dirty flag set when they are created. weston_surface_damage() is also called to ensure that the surface is scheduled for a repaint. As there is currently no good common mechanic in the common code to ensure that output repaint will occur, due to the damage propagating outwards from the weston_surface, we are forced to call this to predictably ensure that the output will be repainted after we have made this change to the scene graph which should result in the user observing the new content being repainted. It is possible that these changes are not strictly required in order for the correct behaviour to occur. However, it is felt that explicitly adding these calls is perhaps the least fragile way to ensure that the behaviour continues to be correct and breakage is not observed, especially with both view mapping and surface damage identified as areas for future work which could be beneficial to Weston. If it is felt that these calls can be removed, then this is certainly possible at a later stage. Lastly, there are many users within desktop-shell of the common pattern of creating a weston_curtain and inserting it into the scene graph to be painted. These users have been an area of both theoretical concern and real-life observed fragility and breakage recently. By making each user follow a common and predictable pattern of usage, each user is no longer its own special case. This should make it easier to make the desktop-shell codebase more robust, not only simplifying the codebase, but improving observed behaviour. In doing this, it is hoped that future structural and interface changes become easier to make, allowing us to improve some of the more difficult corners of the libweston API. Signed-off-by: Daniel Stone <daniels@collabora.com>
2022-01-18 23:01:56 +00:00
.capture_input = false,
};
struct weston_curtain *curtain;
bool first = true;
int x1, y1, x2, y2;
wl_list_for_each(shell_output, &shell->output_list, link) {
struct weston_output *op = shell_output->output;
if (first) {
first = false;
x1 = op->pos.c.x;
y1 = op->pos.c.y;
x2 = op->pos.c.x + op->width;
y2 = op->pos.c.y + op->height;
continue;
}
x1 = MIN(x1, op->pos.c.x);
y1 = MIN(y1, op->pos.c.y);
x2 = MAX(x2, op->pos.c.x + op->width);
y2 = MAX(y2, op->pos.c.y + op->height);
}
curtain_params.pos.c.x = x1;
curtain_params.pos.c.y = y1;
curtain_params.width = x2 - x1;
curtain_params.height = y2 - y1;
curtain = weston_shell_utils_curtain_create(compositor, &curtain_params);
desktop-shell: Reuse curtains for fades Use the common infrastructure we have, rather than open-coding again. In changing to the common weston_curtain infrastructure which was introduced in a previous commit, there are two small functional derivations. After adding the view to a layer, we explicitly call weston_view_geometry_dirty(). This is believed to have no functional impact, as weston_views have their geometry-dirty flag set when they are created. weston_surface_damage() is also called to ensure that the surface is scheduled for a repaint. As there is currently no good common mechanic in the common code to ensure that output repaint will occur, due to the damage propagating outwards from the weston_surface, we are forced to call this to predictably ensure that the output will be repainted after we have made this change to the scene graph which should result in the user observing the new content being repainted. It is possible that these changes are not strictly required in order for the correct behaviour to occur. However, it is felt that explicitly adding these calls is perhaps the least fragile way to ensure that the behaviour continues to be correct and breakage is not observed, especially with both view mapping and surface damage identified as areas for future work which could be beneficial to Weston. If it is felt that these calls can be removed, then this is certainly possible at a later stage. Lastly, there are many users within desktop-shell of the common pattern of creating a weston_curtain and inserting it into the scene graph to be painted. These users have been an area of both theoretical concern and real-life observed fragility and breakage recently. By making each user follow a common and predictable pattern of usage, each user is no longer its own special case. This should make it easier to make the desktop-shell codebase more robust, not only simplifying the codebase, but improving observed behaviour. In doing this, it is hoped that future structural and interface changes become easier to make, allowing us to improve some of the more difficult corners of the libweston API. Signed-off-by: Daniel Stone <daniels@collabora.com>
2022-01-18 23:01:56 +00:00
assert(curtain);
weston_view_move_to_layer(curtain->view, &compositor->fade_layer.view_list);
desktop-shell: Reuse curtains for fades Use the common infrastructure we have, rather than open-coding again. In changing to the common weston_curtain infrastructure which was introduced in a previous commit, there are two small functional derivations. After adding the view to a layer, we explicitly call weston_view_geometry_dirty(). This is believed to have no functional impact, as weston_views have their geometry-dirty flag set when they are created. weston_surface_damage() is also called to ensure that the surface is scheduled for a repaint. As there is currently no good common mechanic in the common code to ensure that output repaint will occur, due to the damage propagating outwards from the weston_surface, we are forced to call this to predictably ensure that the output will be repainted after we have made this change to the scene graph which should result in the user observing the new content being repainted. It is possible that these changes are not strictly required in order for the correct behaviour to occur. However, it is felt that explicitly adding these calls is perhaps the least fragile way to ensure that the behaviour continues to be correct and breakage is not observed, especially with both view mapping and surface damage identified as areas for future work which could be beneficial to Weston. If it is felt that these calls can be removed, then this is certainly possible at a later stage. Lastly, there are many users within desktop-shell of the common pattern of creating a weston_curtain and inserting it into the scene graph to be painted. These users have been an area of both theoretical concern and real-life observed fragility and breakage recently. By making each user follow a common and predictable pattern of usage, each user is no longer its own special case. This should make it easier to make the desktop-shell codebase more robust, not only simplifying the codebase, but improving observed behaviour. In doing this, it is hoped that future structural and interface changes become easier to make, allowing us to improve some of the more difficult corners of the libweston API. Signed-off-by: Daniel Stone <daniels@collabora.com>
2022-01-18 23:01:56 +00:00
return curtain;
}
static void
shell_fade(struct desktop_shell *shell, enum fade_type type)
{
float tint;
switch (type) {
case FADE_IN:
tint = 0.0;
break;
case FADE_OUT:
tint = 1.0;
break;
default:
weston_log("shell: invalid fade type\n");
return;
}
shell->fade.type = type;
if (shell->fade.curtain == NULL) {
shell->fade.curtain = shell_fade_create_view(shell);
if (!shell->fade.curtain)
return;
weston_view_set_alpha(shell->fade.curtain->view, 1.0 - tint);
}
if (shell->fade.animation) {
weston_fade_update(shell->fade.animation, tint);
} else {
shell->fade.animation =
weston_fade_run(shell->fade.curtain->view,
1.0 - tint, tint, 300.0,
shell_fade_done, shell);
}
}
static void
do_shell_fade_startup(void *data)
{
struct desktop_shell *shell = data;
assert(shell->startup_animation_type == ANIMATION_FADE ||
shell->startup_animation_type == ANIMATION_NONE);
if (shell->startup_animation_type == ANIMATION_FADE)
shell_fade(shell, FADE_IN);
}
static void
shell_fade_startup(struct desktop_shell *shell)
{
struct wl_event_loop *loop;
bool has_fade = false;
if (!shell->fade.startup_timer)
return;
wl_event_source_remove(shell->fade.startup_timer);
shell->fade.startup_timer = NULL;
has_fade = true;
if (has_fade) {
loop = wl_display_get_event_loop(shell->compositor->wl_display);
wl_event_loop_add_idle(loop, do_shell_fade_startup, shell);
}
}
static int
fade_startup_timeout(void *data)
{
struct desktop_shell *shell = data;
shell_fade_startup(shell);
return 0;
}
static void
shell_fade_init(struct desktop_shell *shell)
{
/* Make compositor output all black, and wait for the desktop-shell
* client to signal it is ready, then fade in. The timer triggers a
* fade-in, in case the desktop-shell client takes too long.
*/
struct wl_event_loop *loop;
if (shell->startup_animation_type == ANIMATION_NONE)
return;
if (shell->fade.curtain != NULL) {
weston_log("%s: warning: fade surface already exists\n",
__func__);
return;
}
shell->fade.curtain = shell_fade_create_view(shell);
if (!shell->fade.curtain)
return;
weston_view_update_transform(shell->fade.curtain->view);
weston_surface_damage(shell->fade.curtain->view->surface);
loop = wl_display_get_event_loop(shell->compositor->wl_display);
shell->fade.startup_timer =
wl_event_loop_add_timer(loop, fade_startup_timeout, shell);
wl_event_source_timer_update(shell->fade.startup_timer, 15000);
}
static void
idle_handler(struct wl_listener *listener, void *data)
{
struct desktop_shell *shell =
container_of(listener, struct desktop_shell, idle_listener);
struct weston_seat *seat;
wl_list_for_each(seat, &shell->compositor->seat_list, link)
weston_seat_break_desktop_grabs(seat);
shell_fade(shell, FADE_OUT);
/* lock() is called from shell_fade_done() */
}
static void
wake_handler(struct wl_listener *listener, void *data)
{
struct desktop_shell *shell =
container_of(listener, struct desktop_shell, wake_listener);
unlock(shell);
}
static void
transform_handler(struct wl_listener *listener, void *data)
{
struct weston_surface *surface = data;
struct shell_surface *shsurf = get_shell_surface(surface);
const struct weston_xwayland_surface_api *api;
int x, y;
if (!shsurf)
return;
shell_surface_set_output(shsurf, shsurf->view->output);
api = shsurf->shell->xwayland_surface_api;
if (!api) {
api = weston_xwayland_surface_get_api(shsurf->shell->compositor);
shsurf->shell->xwayland_surface_api = api;
}
if (!api || !api->is_xwayland_surface(surface))
return;
if (!weston_view_is_mapped(shsurf->view))
return;
x = shsurf->view->geometry.pos_offset.x;
y = shsurf->view->geometry.pos_offset.y;
api->send_position(surface, x, y);
}
static void
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
weston_view_set_initial_position(struct weston_view *view,
struct desktop_shell *shell)
{
struct weston_compositor *compositor = shell->compositor;
int32_t range_x, range_y;
int32_t x, y;
struct weston_output *output, *target_output = NULL;
struct weston_seat *seat;
pixman_rectangle32_t area;
struct weston_coord_global pos;
/* As a heuristic place the new window on the same output as the
* pointer. Falling back to the output containing 0, 0.
*
* TODO: Do something clever for touch too?
*/
pos.c = weston_coord(0, 0);
wl_list_for_each(seat, &compositor->seat_list, link) {
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
if (pointer) {
pos = pointer->pos;
break;
}
}
wl_list_for_each(output, &compositor->output_list, link) {
if (weston_output_contains_coord(output, pos)) {
target_output = output;
break;
}
}
if (!target_output) {
pos.c = weston_coord(10 + random() % 400,
10 + random() % 400);
weston_view_set_position(view, pos);
return;
}
/* Valid range within output where the surface will still be onscreen.
* If this is negative it means that the surface is bigger than
* output.
*/
get_output_work_area(shell, target_output, &area);
x = area.x;
y = area.y;
range_x = area.width - view->surface->width;
range_y = area.height - view->surface->height;
if (range_x > 0)
x += random() % range_x;
if (range_y > 0)
y += random() % range_y;
pos.c = weston_coord(x, y);
weston_view_set_position(view, pos);
}
static bool
check_desktop_shell_crash_too_early(struct desktop_shell *shell)
{
struct timespec now;
if (clock_gettime(CLOCK_MONOTONIC, &now) < 0)
return false;
/*
* If the shell helper client dies before the session has been
* up for roughly 30 seconds, better just make Weston shut down,
* because the user likely has no way to interact with the desktop
* anyway.
*/
if (now.tv_sec - shell->startup_time.tv_sec < 30) {
weston_log("Error: %s apparently cannot run at all.\n",
shell->client);
weston_log_continue(STAMP_SPACE "Quitting...");
weston_compositor_exit_with_code(shell->compositor,
EXIT_FAILURE);
return true;
}
return false;
}
static void launch_desktop_shell_process(void *data);
static void
shell: fix race on desktop-shell exit The desktop shell plugin registers both a wl_client destroy signal listener, and a sigchld handler, when launching weston-desktop-shell. However, nothing guarantees in which order do the wl_client destructor and the sigchld handler run. Luckily, the sigchld handler cannot interrupt any code, because we handle the signal via signalfd, which means it is handled like any event in the compositor's main event loop. Still, shell.c has a race, that when lost, can cause a crash, as described in bug #82957. If the sigchld handler happens to run first, it will try to launch a new weston-desktop-shell without removing the destroy listener from the old wl_client first. This leads to list corruption, that may cause a crash when the old wl_client gets destroyed. Simply removing the destroy listener in the sigchld handler is not enough, because respawning sets shell->child.client pointer, and if the wl_client destructor runs after, it will reset it to NULL. OTOH, the wl_client destroy handler cannot reset shell->child.process, because that would cause the sigchld handler in weston core to not find the process tracker anymore, and report that an unknown process exited. Turns out, that to make everything work, we would need to wait for both the wl_client destructor and the sigchld handler to have run, before respawn. This gets tricky. Instead, solve the problem by removing shell->child.process. Use the new weston_client_start() which automatically creates and manages the struct weston_process. The shell does not need to know about the process exit, it only needs to know about the client disconnect. Weston-desktop-shell will never attempt to reconnect, and it would not work even if it did, so disconnect is equivalent to weston-desktop-shell exiting. This should permanently solve the race for weston-desktop-shell. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=82957 Cc: Boyan Ding <stu_dby@126.com> Cc: Derek Foreman <derekf@osg.samsung.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Reviewed-by: Derek Foreman <derekf@osg.samsung.com>
2014-08-27 09:11:53 +00:00
respawn_desktop_shell_process(struct desktop_shell *shell)
{
struct timespec time;
/* if desktop-shell dies more than 5 times in 30 seconds, give up */
weston_compositor_get_time(&time);
if (timespec_sub_to_msec(&time, &shell->child.deathstamp) > 30000) {
shell->child.deathstamp = time;
shell->child.deathcount = 0;
}
shell->child.deathcount++;
if (shell->child.deathcount > 5) {
shell: fix race on desktop-shell exit The desktop shell plugin registers both a wl_client destroy signal listener, and a sigchld handler, when launching weston-desktop-shell. However, nothing guarantees in which order do the wl_client destructor and the sigchld handler run. Luckily, the sigchld handler cannot interrupt any code, because we handle the signal via signalfd, which means it is handled like any event in the compositor's main event loop. Still, shell.c has a race, that when lost, can cause a crash, as described in bug #82957. If the sigchld handler happens to run first, it will try to launch a new weston-desktop-shell without removing the destroy listener from the old wl_client first. This leads to list corruption, that may cause a crash when the old wl_client gets destroyed. Simply removing the destroy listener in the sigchld handler is not enough, because respawning sets shell->child.client pointer, and if the wl_client destructor runs after, it will reset it to NULL. OTOH, the wl_client destroy handler cannot reset shell->child.process, because that would cause the sigchld handler in weston core to not find the process tracker anymore, and report that an unknown process exited. Turns out, that to make everything work, we would need to wait for both the wl_client destructor and the sigchld handler to have run, before respawn. This gets tricky. Instead, solve the problem by removing shell->child.process. Use the new weston_client_start() which automatically creates and manages the struct weston_process. The shell does not need to know about the process exit, it only needs to know about the client disconnect. Weston-desktop-shell will never attempt to reconnect, and it would not work even if it did, so disconnect is equivalent to weston-desktop-shell exiting. This should permanently solve the race for weston-desktop-shell. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=82957 Cc: Boyan Ding <stu_dby@126.com> Cc: Derek Foreman <derekf@osg.samsung.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Reviewed-by: Derek Foreman <derekf@osg.samsung.com>
2014-08-27 09:11:53 +00:00
weston_log("%s disconnected, giving up.\n", shell->client);
return;
}
shell: fix race on desktop-shell exit The desktop shell plugin registers both a wl_client destroy signal listener, and a sigchld handler, when launching weston-desktop-shell. However, nothing guarantees in which order do the wl_client destructor and the sigchld handler run. Luckily, the sigchld handler cannot interrupt any code, because we handle the signal via signalfd, which means it is handled like any event in the compositor's main event loop. Still, shell.c has a race, that when lost, can cause a crash, as described in bug #82957. If the sigchld handler happens to run first, it will try to launch a new weston-desktop-shell without removing the destroy listener from the old wl_client first. This leads to list corruption, that may cause a crash when the old wl_client gets destroyed. Simply removing the destroy listener in the sigchld handler is not enough, because respawning sets shell->child.client pointer, and if the wl_client destructor runs after, it will reset it to NULL. OTOH, the wl_client destroy handler cannot reset shell->child.process, because that would cause the sigchld handler in weston core to not find the process tracker anymore, and report that an unknown process exited. Turns out, that to make everything work, we would need to wait for both the wl_client destructor and the sigchld handler to have run, before respawn. This gets tricky. Instead, solve the problem by removing shell->child.process. Use the new weston_client_start() which automatically creates and manages the struct weston_process. The shell does not need to know about the process exit, it only needs to know about the client disconnect. Weston-desktop-shell will never attempt to reconnect, and it would not work even if it did, so disconnect is equivalent to weston-desktop-shell exiting. This should permanently solve the race for weston-desktop-shell. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=82957 Cc: Boyan Ding <stu_dby@126.com> Cc: Derek Foreman <derekf@osg.samsung.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Reviewed-by: Derek Foreman <derekf@osg.samsung.com>
2014-08-27 09:11:53 +00:00
weston_log("%s disconnected, respawning...\n", shell->client);
launch_desktop_shell_process(shell);
}
static void
desktop_shell_client_destroy(struct wl_listener *listener, void *data)
{
struct desktop_shell *shell;
shell = container_of(listener, struct desktop_shell,
child.client_destroy_listener);
shell: fix race on desktop-shell exit The desktop shell plugin registers both a wl_client destroy signal listener, and a sigchld handler, when launching weston-desktop-shell. However, nothing guarantees in which order do the wl_client destructor and the sigchld handler run. Luckily, the sigchld handler cannot interrupt any code, because we handle the signal via signalfd, which means it is handled like any event in the compositor's main event loop. Still, shell.c has a race, that when lost, can cause a crash, as described in bug #82957. If the sigchld handler happens to run first, it will try to launch a new weston-desktop-shell without removing the destroy listener from the old wl_client first. This leads to list corruption, that may cause a crash when the old wl_client gets destroyed. Simply removing the destroy listener in the sigchld handler is not enough, because respawning sets shell->child.client pointer, and if the wl_client destructor runs after, it will reset it to NULL. OTOH, the wl_client destroy handler cannot reset shell->child.process, because that would cause the sigchld handler in weston core to not find the process tracker anymore, and report that an unknown process exited. Turns out, that to make everything work, we would need to wait for both the wl_client destructor and the sigchld handler to have run, before respawn. This gets tricky. Instead, solve the problem by removing shell->child.process. Use the new weston_client_start() which automatically creates and manages the struct weston_process. The shell does not need to know about the process exit, it only needs to know about the client disconnect. Weston-desktop-shell will never attempt to reconnect, and it would not work even if it did, so disconnect is equivalent to weston-desktop-shell exiting. This should permanently solve the race for weston-desktop-shell. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=82957 Cc: Boyan Ding <stu_dby@126.com> Cc: Derek Foreman <derekf@osg.samsung.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Reviewed-by: Derek Foreman <derekf@osg.samsung.com>
2014-08-27 09:11:53 +00:00
wl_list_remove(&shell->child.client_destroy_listener.link);
shell->child.client = NULL;
shell: fix race on desktop-shell exit The desktop shell plugin registers both a wl_client destroy signal listener, and a sigchld handler, when launching weston-desktop-shell. However, nothing guarantees in which order do the wl_client destructor and the sigchld handler run. Luckily, the sigchld handler cannot interrupt any code, because we handle the signal via signalfd, which means it is handled like any event in the compositor's main event loop. Still, shell.c has a race, that when lost, can cause a crash, as described in bug #82957. If the sigchld handler happens to run first, it will try to launch a new weston-desktop-shell without removing the destroy listener from the old wl_client first. This leads to list corruption, that may cause a crash when the old wl_client gets destroyed. Simply removing the destroy listener in the sigchld handler is not enough, because respawning sets shell->child.client pointer, and if the wl_client destructor runs after, it will reset it to NULL. OTOH, the wl_client destroy handler cannot reset shell->child.process, because that would cause the sigchld handler in weston core to not find the process tracker anymore, and report that an unknown process exited. Turns out, that to make everything work, we would need to wait for both the wl_client destructor and the sigchld handler to have run, before respawn. This gets tricky. Instead, solve the problem by removing shell->child.process. Use the new weston_client_start() which automatically creates and manages the struct weston_process. The shell does not need to know about the process exit, it only needs to know about the client disconnect. Weston-desktop-shell will never attempt to reconnect, and it would not work even if it did, so disconnect is equivalent to weston-desktop-shell exiting. This should permanently solve the race for weston-desktop-shell. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=82957 Cc: Boyan Ding <stu_dby@126.com> Cc: Derek Foreman <derekf@osg.samsung.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Reviewed-by: Derek Foreman <derekf@osg.samsung.com>
2014-08-27 09:11:53 +00:00
/*
* unbind_desktop_shell() will reset shell->child.desktop_shell
* before the respawned process has a chance to create a new
* desktop_shell object, because we are being called from the
* wl_client destructor which destroys all wl_resources before
* returning.
*/
if (!shell->compositor->shutting_down &&
!check_desktop_shell_crash_too_early(shell)) {
respawn_desktop_shell_process(shell);
}
shell: fix race on desktop-shell exit The desktop shell plugin registers both a wl_client destroy signal listener, and a sigchld handler, when launching weston-desktop-shell. However, nothing guarantees in which order do the wl_client destructor and the sigchld handler run. Luckily, the sigchld handler cannot interrupt any code, because we handle the signal via signalfd, which means it is handled like any event in the compositor's main event loop. Still, shell.c has a race, that when lost, can cause a crash, as described in bug #82957. If the sigchld handler happens to run first, it will try to launch a new weston-desktop-shell without removing the destroy listener from the old wl_client first. This leads to list corruption, that may cause a crash when the old wl_client gets destroyed. Simply removing the destroy listener in the sigchld handler is not enough, because respawning sets shell->child.client pointer, and if the wl_client destructor runs after, it will reset it to NULL. OTOH, the wl_client destroy handler cannot reset shell->child.process, because that would cause the sigchld handler in weston core to not find the process tracker anymore, and report that an unknown process exited. Turns out, that to make everything work, we would need to wait for both the wl_client destructor and the sigchld handler to have run, before respawn. This gets tricky. Instead, solve the problem by removing shell->child.process. Use the new weston_client_start() which automatically creates and manages the struct weston_process. The shell does not need to know about the process exit, it only needs to know about the client disconnect. Weston-desktop-shell will never attempt to reconnect, and it would not work even if it did, so disconnect is equivalent to weston-desktop-shell exiting. This should permanently solve the race for weston-desktop-shell. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=82957 Cc: Boyan Ding <stu_dby@126.com> Cc: Derek Foreman <derekf@osg.samsung.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Reviewed-by: Derek Foreman <derekf@osg.samsung.com>
2014-08-27 09:11:53 +00:00
shell_fade_startup(shell);
}
static void
launch_desktop_shell_process(void *data)
{
struct desktop_shell *shell = data;
shell->child.client = wet_client_start(shell->compositor,
shell->client);
if (!shell->child.client) {
2013-11-20 12:22:29 +00:00
weston_log("not able to start %s\n", shell->client);
return;
}
shell->child.client_destroy_listener.notify =
desktop_shell_client_destroy;
wl_client_add_destroy_listener(shell->child.client,
&shell->child.client_destroy_listener);
}
static void
unbind_desktop_shell(struct wl_resource *resource)
{
struct desktop_shell *shell = wl_resource_get_user_data(resource);
if (shell->locked)
resume_desktop(shell);
shell->child.desktop_shell = NULL;
shell->prepare_event_sent = false;
}
static void
bind_desktop_shell(struct wl_client *client,
void *data, uint32_t version, uint32_t id)
{
struct desktop_shell *shell = data;
struct wl_resource *resource;
resource = wl_resource_create(client, &weston_desktop_shell_interface,
1, id);
if (client == shell->child.client) {
wl_resource_set_implementation(resource,
&desktop_shell_implementation,
shell, unbind_desktop_shell);
shell->child.desktop_shell = resource;
return;
}
wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
"permission to bind desktop_shell denied");
}
struct switcher {
struct desktop_shell *shell;
struct weston_view *current;
struct wl_listener listener;
2013-04-18 19:25:39 +00:00
struct weston_keyboard_grab grab;
struct wl_array minimized_array;
};
static void
switcher_next(struct switcher *switcher)
{
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
struct weston_view *view;
struct weston_view *first = NULL, *prev = NULL, *next = NULL;
2012-04-03 02:18:58 +00:00
struct shell_surface *shsurf;
struct workspace *ws = get_current_workspace(switcher->shell);
/* temporary re-display minimized surfaces */
struct weston_view *tmp;
struct weston_view **minimized;
wl_list_for_each_safe(view, tmp, &switcher->shell->minimized_layer.view_list.link, layer_link.link) {
weston_view_move_to_layer(view, &ws->layer.view_list);
minimized = wl_array_add(&switcher->minimized_array, sizeof *minimized);
*minimized = view;
}
wl_list_for_each(view, &ws->layer.view_list.link, layer_link.link) {
2013-12-03 17:35:43 +00:00
shsurf = get_shell_surface(view->surface);
if (shsurf) {
if (first == NULL)
first = view;
if (prev == switcher->current)
next = view;
prev = view;
weston_view_set_alpha(view, 0.25);
}
if (is_black_surface_view(view, NULL))
weston_view_set_alpha(view, 0.25);
}
if (next == NULL)
next = first;
if (next == NULL)
return;
wl_list_remove(&switcher->listener.link);
wl_signal_add(&next->destroy_signal, &switcher->listener);
switcher->current = next;
wl_list_for_each(view, &next->surface->views, surface_link)
weston_view_set_alpha(view, 1.0);
shsurf = get_shell_surface(switcher->current->surface);
if (shsurf && weston_desktop_surface_get_fullscreen(shsurf->desktop_surface))
weston_view_set_alpha(shsurf->fullscreen.black_view->view, 1.0);
}
static void
switcher_handle_view_destroy(struct wl_listener *listener, void *data)
{
struct switcher *switcher =
container_of(listener, struct switcher, listener);
switcher_next(switcher);
}
static void
switcher_destroy(struct switcher *switcher)
{
Split the geometry information from weston_surface out into weston_view The weston_surface structure is split into two structures: * The weston_surface structure storres everything required for a client-side or server-side surface. This includes buffers; callbacks; backend private data; input, damage, and opaque regions; and a few other bookkeeping bits. * The weston_view structure represents an entity in the scenegraph and storres all of the geometry information. This includes clip region, alpha, position, and the transformation list as well as all of the temporary information derived from the geometry state. Because a view, and not a surface, is a scenegraph element, the view is what is placed in layers and planes. There are a few things worth noting about the surface/view split: 1. This is *not* a modification to the protocol. It is, instead, a modification to Weston's internal scenegraph to allow a single surface to exist in multiple places at a time. Clients are completely unaware of how many views to a particular surface exist. 2. A view is considered a direct child of a surface and is destroyed when the surface is destroyed. Because of this, the view.surface pointer is always valid and non-null. 3. The compositor's surface_list is replaced with a view_list. Due to subsurfaces, building the view list is a little more complicated than it used to be and involves building a tree of views on the fly whenever subsurfaces are used. However, this means that backends can remain completely subsurface-agnostic. 4. Surfaces and views both keep track of which outputs they are on. 5. The weston_surface structure now has width and height fields. These are populated when a new buffer is attached before surface.configure is called. This is because there are many surface-based operations that really require the width and height and digging through the views didn't work well. Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
2013-10-13 03:38:11 +00:00
struct weston_view *view;
2013-04-18 19:25:39 +00:00
struct weston_keyboard *keyboard = switcher->grab.keyboard;
struct workspace *ws = get_current_workspace(switcher->shell);
wl_list_for_each(view, &ws->layer.view_list.link, layer_link.link) {
if (is_focus_view(view))
continue;
weston_view_set_alpha(view, 1.0);
}
if (switcher->current && get_shell_surface(switcher->current->surface)) {
activate(switcher->shell, switcher->current,
keyboard->seat,
WESTON_ACTIVATE_FLAG_CONFIGURE);
}
wl_list_remove(&switcher->listener.link);
2013-04-18 19:25:39 +00:00
weston_keyboard_end_grab(keyboard);
if (keyboard->input_method_resource)
keyboard->grab = &keyboard->input_method_grab;
/* re-hide surfaces that were temporary shown during the switch */
struct weston_view **minimized;
wl_array_for_each(minimized, &switcher->minimized_array) {
/* with the exception of the current selected */
if ((*minimized)->surface == switcher->current->surface)
continue;
weston_view_move_to_layer(*minimized,
&switcher->shell->minimized_layer.view_list);
}
wl_array_release(&switcher->minimized_array);
free(switcher);
}
static void
2013-04-18 19:25:39 +00:00
switcher_key(struct weston_keyboard_grab *grab,
const struct timespec *time, uint32_t key, uint32_t state_w)
{
struct switcher *switcher = container_of(grab, struct switcher, grab);
enum wl_keyboard_key_state state = state_w;
if (key == KEY_TAB && state == WL_KEYBOARD_KEY_STATE_PRESSED)
switcher_next(switcher);
}
static void
2013-04-18 19:25:39 +00:00
switcher_modifier(struct weston_keyboard_grab *grab, uint32_t serial,
uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group)
{
struct switcher *switcher = container_of(grab, struct switcher, grab);
struct weston_seat *seat = grab->keyboard->seat;
if ((seat->modifier_state & switcher->shell->binding_modifier) == 0)
switcher_destroy(switcher);
}
static void
switcher_cancel(struct weston_keyboard_grab *grab)
{
struct switcher *switcher = container_of(grab, struct switcher, grab);
switcher_destroy(switcher);
}
2013-04-18 19:25:39 +00:00
static const struct weston_keyboard_grab_interface switcher_grab = {
switcher_key,
switcher_modifier,
switcher_cancel,
};
static void
switcher_binding(struct weston_keyboard *keyboard, const struct timespec *time,
uint32_t key, void *data)
{
struct desktop_shell *shell = data;
struct switcher *switcher;
switcher = malloc(sizeof *switcher);
if (!switcher)
return;
switcher->shell = shell;
switcher->current = NULL;
switcher->listener.notify = switcher_handle_view_destroy;
wl_list_init(&switcher->listener.link);
wl_array_init(&switcher->minimized_array);
lower_fullscreen_layer(switcher->shell, NULL);
switcher->grab.interface = &switcher_grab;
weston_keyboard_start_grab(keyboard, &switcher->grab);
weston_keyboard_set_focus(keyboard, NULL);
switcher_next(switcher);
}
static void
backlight_binding(struct weston_keyboard *keyboard, const struct timespec *time,
uint32_t key, void *data)
{
struct weston_compositor *compositor = data;
struct weston_output *output;
long backlight_new = 0;
/* TODO: we're limiting to simple use cases, where we assume just
* control on the primary display. We'd have to extend later if we
* ever get support for setting backlights on random desktop LCD
* panels though */
output = weston_shell_utils_get_default_output(compositor);
if (!output)
return;
if (!output->set_backlight)
return;
if (key == KEY_F9 || key == KEY_BRIGHTNESSDOWN)
backlight_new = output->backlight_current - 25;
else if (key == KEY_F10 || key == KEY_BRIGHTNESSUP)
backlight_new = output->backlight_current + 25;
if (backlight_new < 5)
backlight_new = 5;
if (backlight_new > 255)
backlight_new = 255;
output->backlight_current = backlight_new;
output->set_backlight(output, output->backlight_current);
}
static void
force_kill_binding(struct weston_keyboard *keyboard,
const struct timespec *time, uint32_t key, void *data)
{
struct weston_surface *focus_surface;
struct wl_client *client;
struct desktop_shell *shell = data;
struct weston_compositor *compositor = shell->compositor;
pid_t pid;
focus_surface = keyboard->focus;
if (!focus_surface)
return;
wl_signal_emit(&compositor->kill_signal, focus_surface);
client = wl_resource_get_client(focus_surface->resource);
wl_client_get_credentials(client, &pid, NULL, NULL);
/* Skip clients that we launched ourselves (the credentials of
* the socketpair is ours) */
if (pid == getpid())
return;
kill(pid, SIGKILL);
}
static void
shell_reposition_view_on_output_change(struct weston_view *view)
{
struct weston_output *output, *first_output;
struct weston_compositor *ec = view->surface->compositor;
struct shell_surface *shsurf;
int visible;
if (wl_list_empty(&ec->output_list))
return;
/* At this point the destroyed output is not in the list anymore.
* If the view is still visible somewhere, we leave where it is,
* otherwise, move it to the first output. */
visible = 0;
wl_list_for_each(output, &ec->output_list, link) {
struct weston_coord_global pos;
pos = weston_view_get_pos_offset_global(view);
if (weston_output_contains_coord(output, pos)) {
visible = 1;
break;
}
}
shsurf = get_shell_surface(view->surface);
if (!shsurf)
return;
if (!visible) {
struct weston_coord_global pos;
first_output = container_of(ec->output_list.next,
struct weston_output, link);
pos = first_output->pos;
pos.c.x += first_output->width / 4;
pos.c.y += first_output->height / 4;
weston_view_set_position(view, pos);
} else {
weston_view_geometry_dirty(view);
if (shsurf->state.maximized ||
shsurf->state.fullscreen)
return;
}
shsurf->saved_position_valid = false;
set_maximized(shsurf, false);
set_fullscreen(shsurf, false, NULL);
}
void
shell_for_each_layer(struct desktop_shell *shell,
shell_for_each_layer_func_t func, void *data)
{
func(shell, &shell->fullscreen_layer, data);
func(shell, &shell->panel_layer, data);
func(shell, &shell->background_layer, data);
func(shell, &shell->lock_layer, data);
func(shell, &shell->input_panel_layer, data);
func(shell, &shell->workspace.layer, data);
}
static void
shell_output_changed_move_layer(struct desktop_shell *shell,
struct weston_layer *layer,
void *data)
{
struct weston_view *view;
wl_list_for_each(view, &layer->view_list.link, layer_link.link)
shell_reposition_view_on_output_change(view);
}
static void
desktop-shell: clean up shell_output better This fixes the following ASan detected leaks: Direct leak of 88 byte(s) in 1 object(s) allocated from: #0 0x7f8c3455f330 in __interceptor_malloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9330) #1 0x7f8c33c60906 in wl_event_loop_add_timer ../../git/wayland/src/event-loop.c:571 #2 0x7f8c2ff98f46 in shell_fade_init ../../git/weston/desktop-shell/shell.c:4211 #3 0x7f8c2ff9e1da in wet_shell_init ../../git/weston/desktop-shell/shell.c:5266 #4 0x7f8c3443ede5 in wet_load_shell ../../git/weston/compositor/main.c:956 #5 0x7f8c3444fdb9 in wet_main ../../git/weston/compositor/main.c:3434 #6 0x55878ad3bfc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #7 0x55878ad3f9fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #8 0x55878ad2fbd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #9 0x55878ad2fc58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #10 0x55878ad3ffaf in main ../../git/weston/tests/weston-test-runner.c:661 #11 0x7f8c340b409a in __libc_start_main ../csu/libc-start.c:308 #12 0x55878ad2f769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) Indirect leak of 856 byte(s) in 1 object(s) allocated from: #0 0x7f8c3455f518 in calloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9518) #1 0x7f8c33c99b73 in zalloc ../../git/weston/include/libweston/zalloc.h:38 #2 0x7f8c33c9cfb1 in weston_surface_create ../../git/weston/libweston/compositor.c:574 #3 0x7f8c2ff98230 in shell_fade_create_surface_for_output ../../git/weston/desktop-shell/shell.c:4059 #4 0x7f8c2ff98df6 in shell_fade_init ../../git/weston/desktop-shell/shell.c:4202 #5 0x7f8c2ff9e1da in wet_shell_init ../../git/weston/desktop-shell/shell.c:5266 #6 0x7f8c3443ede5 in wet_load_shell ../../git/weston/compositor/main.c:956 #7 0x7f8c3444fdb9 in wet_main ../../git/weston/compositor/main.c:3434 #8 0x55878ad3bfc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #9 0x55878ad3f9fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #10 0x55878ad2fbd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #11 0x55878ad2fc58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #12 0x55878ad3ffaf in main ../../git/weston/tests/weston-test-runner.c:661 #13 0x7f8c340b409a in __libc_start_main ../csu/libc-start.c:308 #14 0x55878ad2f769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) Indirect leak of 608 byte(s) in 1 object(s) allocated from: #0 0x7f8c3455f518 in calloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9518) #1 0x7f8c33c99b73 in zalloc ../../git/weston/include/libweston/zalloc.h:38 #2 0x7f8c33c9bed5 in weston_view_create ../../git/weston/libweston/compositor.c:365 #3 0x7f8c2ff98251 in shell_fade_create_surface_for_output ../../git/weston/desktop-shell/shell.c:4063 #4 0x7f8c2ff98df6 in shell_fade_init ../../git/weston/desktop-shell/shell.c:4202 #5 0x7f8c2ff9e1da in wet_shell_init ../../git/weston/desktop-shell/shell.c:5266 #6 0x7f8c3443ede5 in wet_load_shell ../../git/weston/compositor/main.c:956 #7 0x7f8c3444fdb9 in wet_main ../../git/weston/compositor/main.c:3434 #8 0x55878ad3bfc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #9 0x55878ad3f9fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #10 0x55878ad2fbd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #11 0x55878ad2fc58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #12 0x55878ad3ffaf in main ../../git/weston/tests/weston-test-runner.c:661 #13 0x7f8c340b409a in __libc_start_main ../csu/libc-start.c:308 #14 0x55878ad2f769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) They were found with: ASAN_OPTIONS=fast_unwind_on_malloc=0,malloc_context_size=50 \ LSAN_OPTIONS=suppressions=/home/pq/git/weston/.gitlab-ci/leak-sanitizer.supp \ ./tests/test-viewporter test_viewporter_double_create Turns out shell_destroy() had an open-coded and outdated copy of the tear-down sequence, so fixing the leaks in only handle_output_destroy() was not enough. Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
2021-05-24 13:53:40 +00:00
shell_output_destroy(struct shell_output *shell_output)
{
struct desktop_shell *shell = shell_output->shell;
shell_for_each_layer(shell, shell_output_changed_move_layer, NULL);
if (shell_output->panel_surface)
wl_list_remove(&shell_output->panel_surface_listener.link);
if (shell_output->background_surface)
wl_list_remove(&shell_output->background_surface_listener.link);
wl_list_remove(&shell_output->destroy_listener.link);
wl_list_remove(&shell_output->link);
free(shell_output);
}
desktop-shell: clean up shell_output better This fixes the following ASan detected leaks: Direct leak of 88 byte(s) in 1 object(s) allocated from: #0 0x7f8c3455f330 in __interceptor_malloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9330) #1 0x7f8c33c60906 in wl_event_loop_add_timer ../../git/wayland/src/event-loop.c:571 #2 0x7f8c2ff98f46 in shell_fade_init ../../git/weston/desktop-shell/shell.c:4211 #3 0x7f8c2ff9e1da in wet_shell_init ../../git/weston/desktop-shell/shell.c:5266 #4 0x7f8c3443ede5 in wet_load_shell ../../git/weston/compositor/main.c:956 #5 0x7f8c3444fdb9 in wet_main ../../git/weston/compositor/main.c:3434 #6 0x55878ad3bfc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #7 0x55878ad3f9fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #8 0x55878ad2fbd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #9 0x55878ad2fc58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #10 0x55878ad3ffaf in main ../../git/weston/tests/weston-test-runner.c:661 #11 0x7f8c340b409a in __libc_start_main ../csu/libc-start.c:308 #12 0x55878ad2f769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) Indirect leak of 856 byte(s) in 1 object(s) allocated from: #0 0x7f8c3455f518 in calloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9518) #1 0x7f8c33c99b73 in zalloc ../../git/weston/include/libweston/zalloc.h:38 #2 0x7f8c33c9cfb1 in weston_surface_create ../../git/weston/libweston/compositor.c:574 #3 0x7f8c2ff98230 in shell_fade_create_surface_for_output ../../git/weston/desktop-shell/shell.c:4059 #4 0x7f8c2ff98df6 in shell_fade_init ../../git/weston/desktop-shell/shell.c:4202 #5 0x7f8c2ff9e1da in wet_shell_init ../../git/weston/desktop-shell/shell.c:5266 #6 0x7f8c3443ede5 in wet_load_shell ../../git/weston/compositor/main.c:956 #7 0x7f8c3444fdb9 in wet_main ../../git/weston/compositor/main.c:3434 #8 0x55878ad3bfc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #9 0x55878ad3f9fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #10 0x55878ad2fbd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #11 0x55878ad2fc58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #12 0x55878ad3ffaf in main ../../git/weston/tests/weston-test-runner.c:661 #13 0x7f8c340b409a in __libc_start_main ../csu/libc-start.c:308 #14 0x55878ad2f769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) Indirect leak of 608 byte(s) in 1 object(s) allocated from: #0 0x7f8c3455f518 in calloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9518) #1 0x7f8c33c99b73 in zalloc ../../git/weston/include/libweston/zalloc.h:38 #2 0x7f8c33c9bed5 in weston_view_create ../../git/weston/libweston/compositor.c:365 #3 0x7f8c2ff98251 in shell_fade_create_surface_for_output ../../git/weston/desktop-shell/shell.c:4063 #4 0x7f8c2ff98df6 in shell_fade_init ../../git/weston/desktop-shell/shell.c:4202 #5 0x7f8c2ff9e1da in wet_shell_init ../../git/weston/desktop-shell/shell.c:5266 #6 0x7f8c3443ede5 in wet_load_shell ../../git/weston/compositor/main.c:956 #7 0x7f8c3444fdb9 in wet_main ../../git/weston/compositor/main.c:3434 #8 0x55878ad3bfc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #9 0x55878ad3f9fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #10 0x55878ad2fbd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #11 0x55878ad2fc58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #12 0x55878ad3ffaf in main ../../git/weston/tests/weston-test-runner.c:661 #13 0x7f8c340b409a in __libc_start_main ../csu/libc-start.c:308 #14 0x55878ad2f769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) They were found with: ASAN_OPTIONS=fast_unwind_on_malloc=0,malloc_context_size=50 \ LSAN_OPTIONS=suppressions=/home/pq/git/weston/.gitlab-ci/leak-sanitizer.supp \ ./tests/test-viewporter test_viewporter_double_create Turns out shell_destroy() had an open-coded and outdated copy of the tear-down sequence, so fixing the leaks in only handle_output_destroy() was not enough. Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
2021-05-24 13:53:40 +00:00
static void
handle_output_destroy(struct wl_listener *listener, void *data)
{
struct shell_output *shell_output =
container_of(listener, struct shell_output, destroy_listener);
shell_output_destroy(shell_output);
}
static void
shell_resize_surface_to_output(struct desktop_shell *shell,
struct weston_surface *surface,
const struct weston_output *output)
{
if (!surface)
return;
weston_desktop_shell_send_configure(shell->child.desktop_shell, 0,
surface->resource,
output->width,
output->height);
}
static void
handle_output_resized_shsurfs(struct desktop_shell *shell)
{
struct shell_surface *shsurf;
wl_list_for_each(shsurf, &shell->shsurf_list, link) {
struct weston_desktop_surface *dsurface =
shsurf->desktop_surface;
if (dsurface) {
bool is_maximized =
weston_desktop_surface_get_maximized(dsurface);
bool is_fullscreen =
weston_desktop_surface_get_fullscreen(dsurface);
if (is_maximized || is_fullscreen) {
set_shsurf_size_maximized_or_fullscreen(shsurf,
is_maximized,
is_fullscreen);
}
}
}
}
static void
handle_output_resized(struct wl_listener *listener, void *data)
{
struct desktop_shell *shell =
container_of(listener, struct desktop_shell, resized_listener);
struct weston_output *output = (struct weston_output *)data;
struct shell_output *sh_output = find_shell_output_from_weston_output(shell, output);
handle_output_resized_shsurfs(shell);
shell_resize_surface_to_output(shell, sh_output->background_surface, output);
shell_resize_surface_to_output(shell, sh_output->panel_surface, output);
}
static void
create_shell_output(struct desktop_shell *shell,
struct weston_output *output)
{
struct shell_output *shell_output;
shell_output = zalloc(sizeof *shell_output);
if (shell_output == NULL)
return;
shell_output->output = output;
shell_output->shell = shell;
shell_output->destroy_listener.notify = handle_output_destroy;
wl_signal_add(&output->destroy_signal,
&shell_output->destroy_listener);
wl_list_insert(shell->output_list.prev, &shell_output->link);
if (wl_list_length(&shell->output_list) == 1)
shell_for_each_layer(shell,
shell_output_changed_move_layer, NULL);
}
static void
handle_output_create(struct wl_listener *listener, void *data)
{
struct desktop_shell *shell =
container_of(listener, struct desktop_shell, output_create_listener);
struct weston_output *output = (struct weston_output *)data;
create_shell_output(shell, output);
}
static void
handle_output_move_layer(struct desktop_shell *shell,
struct weston_layer *layer, void *data)
{
struct weston_output *output = data;
struct weston_view *view;
wl_list_for_each(view, &layer->view_list.link, layer_link.link) {
struct weston_coord_global pos;
if (view->output != output)
continue;
pos = weston_coord_global_add(
weston_view_get_pos_offset_global(view),
output->move);
weston_view_set_position(view, pos);
}
}
static void
handle_output_move(struct wl_listener *listener, void *data)
{
struct desktop_shell *shell;
shell = container_of(listener, struct desktop_shell,
output_move_listener);
shell_for_each_layer(shell, handle_output_move_layer, data);
}
static void
setup_output_destroy_handler(struct weston_compositor *ec,
struct desktop_shell *shell)
{
struct weston_output *output;
wl_list_for_each(output, &ec->output_list, link)
create_shell_output(shell, output);
shell->output_create_listener.notify = handle_output_create;
wl_signal_add(&ec->output_created_signal,
&shell->output_create_listener);
shell->output_move_listener.notify = handle_output_move;
wl_signal_add(&ec->output_moved_signal, &shell->output_move_listener);
}
static void
desktop_shell_destroy_layer(struct weston_layer *layer)
{
desktop-shell: Handle weston_curtain destruction This fixes the following leaks for weston_curtain/weston_buffer_create_solid_rgba when shutting down the compositor: #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f915bfeb8b7 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f915bfec71d in weston_curtain_create ../shell-utils/shell-utils.c:150 #3 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #4 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #5 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #6 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #7 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #8 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #9 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #10 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #11 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #12 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #13 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #14 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f916fe62aa3 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f916fe7398d in weston_buffer_create_solid_rgba ../libweston/compositor.c:2603 #3 0x7f915bfec879 in weston_curtain_create ../shell-utils/shell-utils.c:162 #4 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #5 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #6 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #7 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #8 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #9 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #10 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #11 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #12 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #13 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #14 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #15 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) We do not migrate the weston_curtain destruction from desktop_surface_removed() to desktop_shell_destroy_surface() because we'd want have the curtain removed before the animation starts. If we were to move the black view destruction, *and* only handle it from desktop_shell_destroy_surface() the animation runs but the black curtain will be removed right at the end, effectively diminishing the effect of the animations. To this end, we keep both of the two worlds, if the client terminates on its own, we keep the same animation effect, but if the compositor is shutting down we destroy it immediately. We remove wl_list_for_each_safe() and instead loop each time to avoid using a stale pointer iterator which could cause a UAF as the shsurf would be free'ed. Suggested-by: Pekka Paalanen <pekka.paalanen@collabora.com> Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
2022-05-17 14:49:35 +00:00
struct weston_view *view;
bool removed;
desktop-shell: Handle weston_curtain destruction This fixes the following leaks for weston_curtain/weston_buffer_create_solid_rgba when shutting down the compositor: #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f915bfeb8b7 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f915bfec71d in weston_curtain_create ../shell-utils/shell-utils.c:150 #3 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #4 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #5 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #6 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #7 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #8 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #9 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #10 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #11 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #12 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #13 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #14 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f916fe62aa3 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f916fe7398d in weston_buffer_create_solid_rgba ../libweston/compositor.c:2603 #3 0x7f915bfec879 in weston_curtain_create ../shell-utils/shell-utils.c:162 #4 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #5 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #6 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #7 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #8 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #9 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #10 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #11 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #12 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #13 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #14 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #15 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) We do not migrate the weston_curtain destruction from desktop_surface_removed() to desktop_shell_destroy_surface() because we'd want have the curtain removed before the animation starts. If we were to move the black view destruction, *and* only handle it from desktop_shell_destroy_surface() the animation runs but the black curtain will be removed right at the end, effectively diminishing the effect of the animations. To this end, we keep both of the two worlds, if the client terminates on its own, we keep the same animation effect, but if the compositor is shutting down we destroy it immediately. We remove wl_list_for_each_safe() and instead loop each time to avoid using a stale pointer iterator which could cause a UAF as the shsurf would be free'ed. Suggested-by: Pekka Paalanen <pekka.paalanen@collabora.com> Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
2022-05-17 14:49:35 +00:00
do {
removed = false;
/* fullscreen_layer is special as it would have a view with an
* additional black_view created and added to its layer_link
* fullscreen view. See shell_ensure_fullscreen_black_view()
*
* 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.
*
desktop-shell: Handle weston_curtain destruction This fixes the following leaks for weston_curtain/weston_buffer_create_solid_rgba when shutting down the compositor: #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f915bfeb8b7 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f915bfec71d in weston_curtain_create ../shell-utils/shell-utils.c:150 #3 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #4 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #5 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #6 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #7 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #8 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #9 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #10 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #11 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #12 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #13 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #14 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f916fe62aa3 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f916fe7398d in weston_buffer_create_solid_rgba ../libweston/compositor.c:2603 #3 0x7f915bfec879 in weston_curtain_create ../shell-utils/shell-utils.c:162 #4 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #5 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #6 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #7 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #8 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #9 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #10 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #11 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #12 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #13 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #14 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #15 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) We do not migrate the weston_curtain destruction from desktop_surface_removed() to desktop_shell_destroy_surface() because we'd want have the curtain removed before the animation starts. If we were to move the black view destruction, *and* only handle it from desktop_shell_destroy_surface() the animation runs but the black curtain will be removed right at the end, effectively diminishing the effect of the animations. To this end, we keep both of the two worlds, if the client terminates on its own, we keep the same animation effect, but if the compositor is shutting down we destroy it immediately. We remove wl_list_for_each_safe() and instead loop each time to avoid using a stale pointer iterator which could cause a UAF as the shsurf would be free'ed. Suggested-by: Pekka Paalanen <pekka.paalanen@collabora.com> Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
2022-05-17 14:49:35 +00:00
* We avoid using wl_list_for_each_safe() as it can't handle
* removal of the next item in the list, so with this approach
* we restart the loop as long as we keep removing views from
* the list.
*/
desktop-shell: Handle weston_curtain destruction This fixes the following leaks for weston_curtain/weston_buffer_create_solid_rgba when shutting down the compositor: #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f915bfeb8b7 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f915bfec71d in weston_curtain_create ../shell-utils/shell-utils.c:150 #3 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #4 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #5 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #6 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #7 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #8 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #9 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #10 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #11 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #12 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #13 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #14 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f916fe62aa3 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f916fe7398d in weston_buffer_create_solid_rgba ../libweston/compositor.c:2603 #3 0x7f915bfec879 in weston_curtain_create ../shell-utils/shell-utils.c:162 #4 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #5 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #6 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #7 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #8 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #9 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #10 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #11 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #12 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #13 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #14 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #15 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) We do not migrate the weston_curtain destruction from desktop_surface_removed() to desktop_shell_destroy_surface() because we'd want have the curtain removed before the animation starts. If we were to move the black view destruction, *and* only handle it from desktop_shell_destroy_surface() the animation runs but the black curtain will be removed right at the end, effectively diminishing the effect of the animations. To this end, we keep both of the two worlds, if the client terminates on its own, we keep the same animation effect, but if the compositor is shutting down we destroy it immediately. We remove wl_list_for_each_safe() and instead loop each time to avoid using a stale pointer iterator which could cause a UAF as the shsurf would be free'ed. Suggested-by: Pekka Paalanen <pekka.paalanen@collabora.com> Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
2022-05-17 14:49:35 +00:00
wl_list_for_each(view, &layer->view_list.link, layer_link.link) {
struct shell_surface *shsurf =
get_shell_surface(view->surface);
if (shsurf) {
desktop_shell_destroy_surface(shsurf);
removed = true;
break;
}
}
} while (removed);
weston_layer_fini(layer);
}
static void
shell_destroy(struct wl_listener *listener, void *data)
{
struct desktop_shell *shell =
container_of(listener, struct desktop_shell, destroy_listener);
struct shell_output *shell_output, *tmp;
struct shell_seat *shseat, *shseat_next;
/* Force state to unlocked so we don't try to fade */
shell->locked = false;
shell: fix race on desktop-shell exit The desktop shell plugin registers both a wl_client destroy signal listener, and a sigchld handler, when launching weston-desktop-shell. However, nothing guarantees in which order do the wl_client destructor and the sigchld handler run. Luckily, the sigchld handler cannot interrupt any code, because we handle the signal via signalfd, which means it is handled like any event in the compositor's main event loop. Still, shell.c has a race, that when lost, can cause a crash, as described in bug #82957. If the sigchld handler happens to run first, it will try to launch a new weston-desktop-shell without removing the destroy listener from the old wl_client first. This leads to list corruption, that may cause a crash when the old wl_client gets destroyed. Simply removing the destroy listener in the sigchld handler is not enough, because respawning sets shell->child.client pointer, and if the wl_client destructor runs after, it will reset it to NULL. OTOH, the wl_client destroy handler cannot reset shell->child.process, because that would cause the sigchld handler in weston core to not find the process tracker anymore, and report that an unknown process exited. Turns out, that to make everything work, we would need to wait for both the wl_client destructor and the sigchld handler to have run, before respawn. This gets tricky. Instead, solve the problem by removing shell->child.process. Use the new weston_client_start() which automatically creates and manages the struct weston_process. The shell does not need to know about the process exit, it only needs to know about the client disconnect. Weston-desktop-shell will never attempt to reconnect, and it would not work even if it did, so disconnect is equivalent to weston-desktop-shell exiting. This should permanently solve the race for weston-desktop-shell. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=82957 Cc: Boyan Ding <stu_dby@126.com> Cc: Derek Foreman <derekf@osg.samsung.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Reviewed-by: Derek Foreman <derekf@osg.samsung.com>
2014-08-27 09:11:53 +00:00
if (shell->child.client) {
/* disable respawn */
wl_list_remove(&shell->child.client_destroy_listener.link);
wl_client_destroy(shell->child.client);
shell: fix race on desktop-shell exit The desktop shell plugin registers both a wl_client destroy signal listener, and a sigchld handler, when launching weston-desktop-shell. However, nothing guarantees in which order do the wl_client destructor and the sigchld handler run. Luckily, the sigchld handler cannot interrupt any code, because we handle the signal via signalfd, which means it is handled like any event in the compositor's main event loop. Still, shell.c has a race, that when lost, can cause a crash, as described in bug #82957. If the sigchld handler happens to run first, it will try to launch a new weston-desktop-shell without removing the destroy listener from the old wl_client first. This leads to list corruption, that may cause a crash when the old wl_client gets destroyed. Simply removing the destroy listener in the sigchld handler is not enough, because respawning sets shell->child.client pointer, and if the wl_client destructor runs after, it will reset it to NULL. OTOH, the wl_client destroy handler cannot reset shell->child.process, because that would cause the sigchld handler in weston core to not find the process tracker anymore, and report that an unknown process exited. Turns out, that to make everything work, we would need to wait for both the wl_client destructor and the sigchld handler to have run, before respawn. This gets tricky. Instead, solve the problem by removing shell->child.process. Use the new weston_client_start() which automatically creates and manages the struct weston_process. The shell does not need to know about the process exit, it only needs to know about the client disconnect. Weston-desktop-shell will never attempt to reconnect, and it would not work even if it did, so disconnect is equivalent to weston-desktop-shell exiting. This should permanently solve the race for weston-desktop-shell. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=82957 Cc: Boyan Ding <stu_dby@126.com> Cc: Derek Foreman <derekf@osg.samsung.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Reviewed-by: Derek Foreman <derekf@osg.samsung.com>
2014-08-27 09:11:53 +00:00
}
wl_list_remove(&shell->destroy_listener.link);
wl_list_remove(&shell->idle_listener.link);
wl_list_remove(&shell->wake_listener.link);
wl_list_remove(&shell->transform_listener.link);
text_backend: make destructor call explicit We used to rely on the order in which the weston_compositor::destroy_signal callbacks happened, to not access freed memory. Don't know when, but this broke at least with ivi-shell, which caused crashes in random places on compositor shutdown. Valgrind found the following: Invalid write of size 8 at 0xC2EDC69: unbind_input_panel (input-panel-ivi.c:340) by 0x4E3B6BB: destroy_resource (wayland-server.c:537) by 0x4E3E085: for_each_helper.isra.0 (wayland-util.c:359) by 0x4E3E60D: wl_map_for_each (wayland-util.c:365) by 0x4E3BEC7: wl_client_destroy (wayland-server.c:675) by 0x4182F2: text_backend_notifier_destroy (text-backend.c:1047) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Address 0x67ea360 is 208 bytes inside a block of size 232 free'd at 0x4C2A6BC: free (vg_replace_malloc.c:473) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Invalid write of size 8 at 0x4E3E0D7: wl_list_remove (wayland-util.c:57) by 0xC2EDEE9: destroy_input_panel_surface (input-panel-ivi.c:191) by 0x4E3B6BB: destroy_resource (wayland-server.c:537) by 0x4E3BC7B: wl_resource_destroy (wayland-server.c:550) by 0x40DB8B: wl_signal_emit (wayland-server-core.h:264) by 0x40DB8B: weston_surface_destroy (compositor.c:1883) by 0x40DB8B: weston_surface_destroy (compositor.c:1873) by 0x4E3B6BB: destroy_resource (wayland-server.c:537) by 0x4E3E085: for_each_helper.isra.0 (wayland-util.c:359) by 0x4E3E60D: wl_map_for_each (wayland-util.c:365) by 0x4E3BEC7: wl_client_destroy (wayland-server.c:675) by 0x4182F2: text_backend_notifier_destroy (text-backend.c:1047) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Address 0x67ea370 is 224 bytes inside a block of size 232 free'd at 0x4C2A6BC: free (vg_replace_malloc.c:473) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Invalid write of size 8 at 0x4E3E0E7: wl_list_remove (wayland-util.c:58) by 0xC2EDEE9: destroy_input_panel_surface (input-panel-ivi.c:191) by 0x4E3B6BB: destroy_resource (wayland-server.c:537) by 0x4E3BC7B: wl_resource_destroy (wayland-server.c:550) by 0x40DB8B: wl_signal_emit (wayland-server-core.h:264) by 0x40DB8B: weston_surface_destroy (compositor.c:1883) by 0x40DB8B: weston_surface_destroy (compositor.c:1873) by 0x4E3B6BB: destroy_resource (wayland-server.c:537) by 0x4E3E085: for_each_helper.isra.0 (wayland-util.c:359) by 0x4E3E60D: wl_map_for_each (wayland-util.c:365) by 0x4E3BEC7: wl_client_destroy (wayland-server.c:675) by 0x4182F2: text_backend_notifier_destroy (text-backend.c:1047) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Address 0x67ea368 is 216 bytes inside a block of size 232 free'd at 0x4C2A6BC: free (vg_replace_malloc.c:473) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Looking at the first of these, unbind_input_panel() gets called when the text-backend destroys its helper client which has bound to input_panel interface. This happens after the shell's destroy_signal callback has been called, so the shell has already been freed. The other two errors come from wl_list_remove(&input_panel_surface->link); which has gone stale when the shell was destroyed (shell->input_panel.surfaces list). Rather than creating even more destroy listeners and hooking them up in spaghetti, modify text-backend to not hook up to the compositor destroy signal. Instead, make it the text_backend_init() callers' responsibility to also call text_backend_destroy() appropriately, before the shell goes away. This fixed all the above Valgrind errors, and avoid a crash with ivi-shell when exiting Weston. Also using desktop-shell exhibited similar Valgrind errors which are fixed by this patch, but those didn't happen to cause any crashes AFAIK. Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Reviewed-By: Derek Foreman <derekf@osg.samsung.com>
2015-06-24 13:09:17 +00:00
text_backend_destroy(shell->text_backend);
input_panel_destroy(shell);
if (shell->fade.animation) {
weston_view_animation_destroy(shell->fade.animation);
shell->fade.animation = NULL;
}
if (shell->fade.curtain)
weston_shell_utils_curtain_destroy(shell->fade.curtain);
if (shell->fade.startup_timer)
wl_event_source_remove(shell->fade.startup_timer);
desktop-shell: clean up shell_output better This fixes the following ASan detected leaks: Direct leak of 88 byte(s) in 1 object(s) allocated from: #0 0x7f8c3455f330 in __interceptor_malloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9330) #1 0x7f8c33c60906 in wl_event_loop_add_timer ../../git/wayland/src/event-loop.c:571 #2 0x7f8c2ff98f46 in shell_fade_init ../../git/weston/desktop-shell/shell.c:4211 #3 0x7f8c2ff9e1da in wet_shell_init ../../git/weston/desktop-shell/shell.c:5266 #4 0x7f8c3443ede5 in wet_load_shell ../../git/weston/compositor/main.c:956 #5 0x7f8c3444fdb9 in wet_main ../../git/weston/compositor/main.c:3434 #6 0x55878ad3bfc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #7 0x55878ad3f9fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #8 0x55878ad2fbd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #9 0x55878ad2fc58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #10 0x55878ad3ffaf in main ../../git/weston/tests/weston-test-runner.c:661 #11 0x7f8c340b409a in __libc_start_main ../csu/libc-start.c:308 #12 0x55878ad2f769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) Indirect leak of 856 byte(s) in 1 object(s) allocated from: #0 0x7f8c3455f518 in calloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9518) #1 0x7f8c33c99b73 in zalloc ../../git/weston/include/libweston/zalloc.h:38 #2 0x7f8c33c9cfb1 in weston_surface_create ../../git/weston/libweston/compositor.c:574 #3 0x7f8c2ff98230 in shell_fade_create_surface_for_output ../../git/weston/desktop-shell/shell.c:4059 #4 0x7f8c2ff98df6 in shell_fade_init ../../git/weston/desktop-shell/shell.c:4202 #5 0x7f8c2ff9e1da in wet_shell_init ../../git/weston/desktop-shell/shell.c:5266 #6 0x7f8c3443ede5 in wet_load_shell ../../git/weston/compositor/main.c:956 #7 0x7f8c3444fdb9 in wet_main ../../git/weston/compositor/main.c:3434 #8 0x55878ad3bfc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #9 0x55878ad3f9fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #10 0x55878ad2fbd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #11 0x55878ad2fc58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #12 0x55878ad3ffaf in main ../../git/weston/tests/weston-test-runner.c:661 #13 0x7f8c340b409a in __libc_start_main ../csu/libc-start.c:308 #14 0x55878ad2f769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) Indirect leak of 608 byte(s) in 1 object(s) allocated from: #0 0x7f8c3455f518 in calloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9518) #1 0x7f8c33c99b73 in zalloc ../../git/weston/include/libweston/zalloc.h:38 #2 0x7f8c33c9bed5 in weston_view_create ../../git/weston/libweston/compositor.c:365 #3 0x7f8c2ff98251 in shell_fade_create_surface_for_output ../../git/weston/desktop-shell/shell.c:4063 #4 0x7f8c2ff98df6 in shell_fade_init ../../git/weston/desktop-shell/shell.c:4202 #5 0x7f8c2ff9e1da in wet_shell_init ../../git/weston/desktop-shell/shell.c:5266 #6 0x7f8c3443ede5 in wet_load_shell ../../git/weston/compositor/main.c:956 #7 0x7f8c3444fdb9 in wet_main ../../git/weston/compositor/main.c:3434 #8 0x55878ad3bfc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #9 0x55878ad3f9fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #10 0x55878ad2fbd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #11 0x55878ad2fc58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #12 0x55878ad3ffaf in main ../../git/weston/tests/weston-test-runner.c:661 #13 0x7f8c340b409a in __libc_start_main ../csu/libc-start.c:308 #14 0x55878ad2f769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) They were found with: ASAN_OPTIONS=fast_unwind_on_malloc=0,malloc_context_size=50 \ LSAN_OPTIONS=suppressions=/home/pq/git/weston/.gitlab-ci/leak-sanitizer.supp \ ./tests/test-viewporter test_viewporter_double_create Turns out shell_destroy() had an open-coded and outdated copy of the tear-down sequence, so fixing the leaks in only handle_output_destroy() was not enough. Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
2021-05-24 13:53:40 +00:00
wl_list_for_each_safe(shell_output, tmp, &shell->output_list, link)
shell_output_destroy(shell_output);
wl_list_remove(&shell->output_create_listener.link);
wl_list_remove(&shell->output_move_listener.link);
wl_list_remove(&shell->resized_listener.link);
wl_list_for_each_safe(shseat, shseat_next, &shell->seat_list, link)
desktop_shell_destroy_seat(shseat);
desktop-shell: destroy weston_desktop Plugs ASan reported leaks: Direct leak of 88 byte(s) in 1 object(s) allocated from: #0 0x7f338f70a518 in calloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9518) #1 0x7f338afe22f3 in zalloc ../../git/weston/include/libweston/zalloc.h:38 #2 0x7f338afe3cc2 in weston_desktop_xwayland_init ../../git/weston/libweston-desktop/xwayland.c:410 #3 0x7f338afdbaef in weston_desktop_create ../../git/weston/libweston-desktop/libweston-desktop.c:87 #4 0x7f338b148d39 in wet_shell_init ../../git/weston/desktop-shell/shell.c:5224 #5 0x7f338f5e9de5 in wet_load_shell ../../git/weston/compositor/main.c:956 #6 0x7f338f5fadb9 in wet_main ../../git/weston/compositor/main.c:3434 #7 0x5646d2392fc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #8 0x5646d23969fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #9 0x5646d2386bd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #10 0x5646d2386c58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #11 0x5646d2396faf in main ../../git/weston/tests/weston-test-runner.c:661 #12 0x7f338f25f09a in __libc_start_main ../csu/libc-start.c:308 #13 0x5646d2386769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) Indirect leak of 152 byte(s) in 1 object(s) allocated from: #0 0x7f338f70a518 in calloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9518) #1 0x7f338afdb811 in zalloc ../../git/weston/include/libweston/zalloc.h:38 #2 0x7f338afdb92d in weston_desktop_create ../../git/weston/libweston-desktop/libweston-desktop.c:65 #3 0x7f338b148d39 in wet_shell_init ../../git/weston/desktop-shell/shell.c:5224 #4 0x7f338f5e9de5 in wet_load_shell ../../git/weston/compositor/main.c:956 #5 0x7f338f5fadb9 in wet_main ../../git/weston/compositor/main.c:3434 #6 0x5646d2392fc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #7 0x5646d23969fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #8 0x5646d2386bd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #9 0x5646d2386c58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #10 0x5646d2396faf in main ../../git/weston/tests/weston-test-runner.c:661 #11 0x7f338f25f09a in __libc_start_main ../csu/libc-start.c:308 #12 0x5646d2386769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) Indirect leak of 72 byte(s) in 1 object(s) allocated from: #0 0x7f338f70a518 in calloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9518) #1 0x7f338afdc5ae in zalloc ../../git/weston/include/libweston/zalloc.h:38 #2 0x7f338afdc89e in weston_desktop_client_create ../../git/weston/libweston-desktop/client.c:108 #3 0x7f338afe3d2a in weston_desktop_xwayland_init ../../git/weston/libweston-desktop/xwayland.c:415 #4 0x7f338afdbaef in weston_desktop_create ../../git/weston/libweston-desktop/libweston-desktop.c:87 #5 0x7f338b148d39 in wet_shell_init ../../git/weston/desktop-shell/shell.c:5224 #6 0x7f338f5e9de5 in wet_load_shell ../../git/weston/compositor/main.c:956 #7 0x7f338f5fadb9 in wet_main ../../git/weston/compositor/main.c:3434 #8 0x5646d2392fc6 in execute_compositor ../../git/weston/tests/weston-test-fixture-compositor.c:432 #9 0x5646d23969fa in weston_test_harness_execute_as_client ../../git/weston/tests/weston-test-runner.c:528 #10 0x5646d2386bd6 in fixture_setup ../../git/weston/tests/viewporter-test.c:46 #11 0x5646d2386c58 in fixture_setup_run_ ../../git/weston/tests/viewporter-test.c:48 #12 0x5646d2396faf in main ../../git/weston/tests/weston-test-runner.c:661 #13 0x7f338f25f09a in __libc_start_main ../csu/libc-start.c:308 #14 0x5646d2386769 in _start (/home/pq/build/weston-meson/tests/test-viewporter+0xc769) Oops. Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
2021-05-24 11:42:00 +00:00
weston_desktop_destroy(shell->desktop);
workspace_destroy(&shell->workspace);
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);
2013-11-20 12:22:29 +00:00
free(shell->client);
free(shell);
}
static void
shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell)
{
uint32_t mod;
if (shell->allow_zap)
weston_compositor_add_key_binding(ec, KEY_BACKSPACE,
MODIFIER_CTRL | MODIFIER_ALT,
terminate_binding, ec);
/* fixed bindings */
weston_compositor_add_button_binding(ec, BTN_LEFT, 0,
click_to_activate_binding,
shell);
weston_compositor_add_button_binding(ec, BTN_RIGHT, 0,
click_to_activate_binding,
shell);
weston_compositor_add_touch_binding(ec, 0,
touch_to_activate_binding,
shell);
weston_compositor_add_tablet_tool_binding(ec, BTN_TOUCH, 0,
tablet_tool_activate_binding, shell);
weston_compositor_add_key_binding(ec, KEY_BRIGHTNESSDOWN, 0,
backlight_binding, ec);
weston_compositor_add_key_binding(ec, KEY_BRIGHTNESSUP, 0,
backlight_binding, ec);
mod = shell->binding_modifier;
if (!mod)
return;
/* This binding is not configurable, but is only enabled if there is a
* valid binding modifier. */
weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL,
MODIFIER_SUPER | MODIFIER_ALT,
surface_opacity_binding, NULL);
weston_compositor_add_key_binding(ec, KEY_M, mod | MODIFIER_SHIFT,
maximize_binding, NULL);
weston_compositor_add_key_binding(ec, KEY_F, mod | MODIFIER_SHIFT,
fullscreen_binding, NULL);
weston_compositor_add_button_binding(ec, BTN_LEFT, mod, move_binding,
shell);
weston_compositor_add_touch_binding(ec, mod, touch_move_binding, shell);
weston_compositor_add_button_binding(ec, BTN_RIGHT, mod,
resize_binding, shell);
weston_compositor_add_button_binding(ec, BTN_LEFT,
mod | MODIFIER_SHIFT,
resize_binding, shell);
weston_compositor_add_key_binding(ec, KEY_LEFT, mod | MODIFIER_SHIFT,
set_tiled_orientation_left, NULL);
weston_compositor_add_key_binding(ec, KEY_RIGHT, mod | MODIFIER_SHIFT,
set_tiled_orientation_right, NULL);
weston_compositor_add_key_binding(ec, KEY_UP, mod | MODIFIER_SHIFT,
set_tiled_orientation_up, NULL);
weston_compositor_add_key_binding(ec, KEY_DOWN, mod | MODIFIER_SHIFT,
set_tiled_orientation_down, NULL);
if (ec->capabilities & WESTON_CAP_ROTATION_ANY)
weston_compositor_add_button_binding(ec, BTN_MIDDLE, mod,
rotate_binding, NULL);
weston_compositor_add_key_binding(ec, KEY_TAB, mod, switcher_binding,
shell);
weston_compositor_add_key_binding(ec, KEY_F9, mod, backlight_binding,
ec);
weston_compositor_add_key_binding(ec, KEY_F10, mod, backlight_binding,
ec);
weston_compositor_add_key_binding(ec, KEY_K, mod,
force_kill_binding, shell);
weston_install_debug_key_binding(ec, mod);
}
static void
handle_seat_created(struct wl_listener *listener, void *data)
{
struct weston_seat *seat = data;
struct desktop_shell *shell =
container_of(listener, struct desktop_shell, seat_create_listener);
create_shell_seat(shell, seat);
}
WL_EXPORT int
wet_shell_init(struct weston_compositor *ec,
int *argc, char *argv[])
{
struct weston_seat *seat;
struct desktop_shell *shell;
struct wl_event_loop *loop;
shell = zalloc(sizeof *shell);
if (shell == NULL)
return -1;
shell->compositor = ec;
if (!weston_compositor_add_destroy_listener_once(ec,
&shell->destroy_listener,
shell_destroy)) {
free(shell);
return 0;
}
shell->idle_listener.notify = idle_handler;
wl_signal_add(&ec->idle_signal, &shell->idle_listener);
shell->wake_listener.notify = wake_handler;
wl_signal_add(&ec->wake_signal, &shell->wake_listener);
shell->transform_listener.notify = transform_handler;
wl_signal_add(&ec->transform_signal, &shell->transform_listener);
weston_layer_init(&shell->fullscreen_layer, ec);
weston_layer_init(&shell->panel_layer, ec);
weston_layer_init(&shell->background_layer, ec);
weston_layer_init(&shell->lock_layer, ec);
weston_layer_init(&shell->input_panel_layer, ec);
weston_layer_set_position(&shell->fullscreen_layer,
WESTON_LAYER_POSITION_FULLSCREEN);
weston_layer_set_position(&shell->panel_layer,
WESTON_LAYER_POSITION_UI);
weston_layer_set_position(&shell->background_layer,
WESTON_LAYER_POSITION_BACKGROUND);
wl_list_init(&shell->seat_list);
wl_list_init(&shell->shsurf_list);
wl_list_init(&shell->output_list);
wl_list_init(&shell->output_create_listener.link);
wl_list_init(&shell->output_move_listener.link);
wl_list_init(&shell->seat_create_listener.link);
wl_list_init(&shell->resized_listener.link);
wl_list_init(&shell->workspace.focus_list);
wl_list_init(&shell->workspace.seat_destroyed_listener.link);
weston_layer_init(&shell->minimized_layer, ec);
weston_layer_init(&shell->workspace.layer, ec);
if (input_panel_setup(shell) < 0)
return -1;
text_backend: make destructor call explicit We used to rely on the order in which the weston_compositor::destroy_signal callbacks happened, to not access freed memory. Don't know when, but this broke at least with ivi-shell, which caused crashes in random places on compositor shutdown. Valgrind found the following: Invalid write of size 8 at 0xC2EDC69: unbind_input_panel (input-panel-ivi.c:340) by 0x4E3B6BB: destroy_resource (wayland-server.c:537) by 0x4E3E085: for_each_helper.isra.0 (wayland-util.c:359) by 0x4E3E60D: wl_map_for_each (wayland-util.c:365) by 0x4E3BEC7: wl_client_destroy (wayland-server.c:675) by 0x4182F2: text_backend_notifier_destroy (text-backend.c:1047) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Address 0x67ea360 is 208 bytes inside a block of size 232 free'd at 0x4C2A6BC: free (vg_replace_malloc.c:473) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Invalid write of size 8 at 0x4E3E0D7: wl_list_remove (wayland-util.c:57) by 0xC2EDEE9: destroy_input_panel_surface (input-panel-ivi.c:191) by 0x4E3B6BB: destroy_resource (wayland-server.c:537) by 0x4E3BC7B: wl_resource_destroy (wayland-server.c:550) by 0x40DB8B: wl_signal_emit (wayland-server-core.h:264) by 0x40DB8B: weston_surface_destroy (compositor.c:1883) by 0x40DB8B: weston_surface_destroy (compositor.c:1873) by 0x4E3B6BB: destroy_resource (wayland-server.c:537) by 0x4E3E085: for_each_helper.isra.0 (wayland-util.c:359) by 0x4E3E60D: wl_map_for_each (wayland-util.c:365) by 0x4E3BEC7: wl_client_destroy (wayland-server.c:675) by 0x4182F2: text_backend_notifier_destroy (text-backend.c:1047) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Address 0x67ea370 is 224 bytes inside a block of size 232 free'd at 0x4C2A6BC: free (vg_replace_malloc.c:473) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Invalid write of size 8 at 0x4E3E0E7: wl_list_remove (wayland-util.c:58) by 0xC2EDEE9: destroy_input_panel_surface (input-panel-ivi.c:191) by 0x4E3B6BB: destroy_resource (wayland-server.c:537) by 0x4E3BC7B: wl_resource_destroy (wayland-server.c:550) by 0x40DB8B: wl_signal_emit (wayland-server-core.h:264) by 0x40DB8B: weston_surface_destroy (compositor.c:1883) by 0x40DB8B: weston_surface_destroy (compositor.c:1873) by 0x4E3B6BB: destroy_resource (wayland-server.c:537) by 0x4E3E085: for_each_helper.isra.0 (wayland-util.c:359) by 0x4E3E60D: wl_map_for_each (wayland-util.c:365) by 0x4E3BEC7: wl_client_destroy (wayland-server.c:675) by 0x4182F2: text_backend_notifier_destroy (text-backend.c:1047) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Address 0x67ea368 is 216 bytes inside a block of size 232 free'd at 0x4C2A6BC: free (vg_replace_malloc.c:473) by 0x4084FB: wl_signal_emit (wayland-server-core.h:264) by 0x4084FB: main (compositor.c:5465) Looking at the first of these, unbind_input_panel() gets called when the text-backend destroys its helper client which has bound to input_panel interface. This happens after the shell's destroy_signal callback has been called, so the shell has already been freed. The other two errors come from wl_list_remove(&input_panel_surface->link); which has gone stale when the shell was destroyed (shell->input_panel.surfaces list). Rather than creating even more destroy listeners and hooking them up in spaghetti, modify text-backend to not hook up to the compositor destroy signal. Instead, make it the text_backend_init() callers' responsibility to also call text_backend_destroy() appropriately, before the shell goes away. This fixed all the above Valgrind errors, and avoid a crash with ivi-shell when exiting Weston. Also using desktop-shell exhibited similar Valgrind errors which are fixed by this patch, but those didn't happen to cause any crashes AFAIK. Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Reviewed-By: Derek Foreman <derekf@osg.samsung.com>
2015-06-24 13:09:17 +00:00
shell->text_backend = text_backend_init(ec);
if (!shell_configuration(shell))
return -1;
workspace_create(shell);
shell->desktop = weston_desktop_create(ec, &shell_desktop_api, shell);
if (!shell->desktop)
return -1;
2013-07-08 23:03:57 +00:00
if (wl_global_create(ec->wl_display,
&weston_desktop_shell_interface, 1,
2013-07-08 23:03:57 +00:00
shell, bind_desktop_shell) == NULL)
return -1;
weston_compositor_get_time(&shell->child.deathstamp);
shell->panel_position = WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP;
setup_output_destroy_handler(ec, shell);
loop = wl_display_get_event_loop(ec->wl_display);
wl_event_loop_add_idle(loop, launch_desktop_shell_process, shell);
wl_list_for_each(seat, &ec->seat_list, link)
create_shell_seat(shell, seat);
shell->seat_create_listener.notify = handle_seat_created;
wl_signal_add(&ec->seat_created_signal, &shell->seat_create_listener);
shell->resized_listener.notify = handle_output_resized;
wl_signal_add(&ec->output_resized_signal, &shell->resized_listener);
screenshooter_create(ec);
shell_add_bindings(ec, shell);
2011-04-12 21:25:42 +00:00
shell_fade_init(shell);
clock_gettime(CLOCK_MONOTONIC, &shell->startup_time);
return 0;
}