labwc/src/desktop.c
Consolatis c6dc7e31ee src/ssd: Use LAB_SSD_PART_CORNER_x for rounded corner backgrounds
This matches the behavior of the surrounding borders and will usually
cause resizing instead of triggering the button itself. This effect
is only really visible when using a border width settings of > 1.

Fixes #379
2022-06-09 03:13:38 +02:00

347 lines
7.7 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include "config.h"
#include <assert.h>
#include "labwc.h"
#include "layers.h"
#include "node.h"
#include "ssd.h"
#include "common/scene-helpers.h"
static void
move_to_front(struct view *view)
{
wl_list_remove(&view->link);
wl_list_insert(&view->server->views, &view->link);
wlr_scene_node_raise_to_top(&view->scene_tree->node);
}
#if HAVE_XWAYLAND
static struct wlr_xwayland_surface *
top_parent_of(struct view *view)
{
struct wlr_xwayland_surface *s = view->xwayland_surface;
while (s->parent) {
s = s->parent;
}
return s;
}
static void
move_xwayland_sub_views_to_front(struct view *parent)
{
if (!parent || parent->type != LAB_XWAYLAND_VIEW) {
return;
}
struct view *view, *next;
wl_list_for_each_reverse_safe(view, next, &parent->server->views, link)
{
/* need to stop here, otherwise loops keeps going forever */
if (view == parent) {
break;
}
if (view->type != LAB_XWAYLAND_VIEW) {
continue;
}
if (!view->mapped && !view->minimized) {
continue;
}
if (top_parent_of(view) != parent->xwayland_surface) {
continue;
}
move_to_front(view);
/* TODO: we should probably focus on these too here */
}
}
#endif
void
desktop_move_to_front(struct view *view)
{
if (!view) {
return;
}
move_to_front(view);
#if HAVE_XWAYLAND
move_xwayland_sub_views_to_front(view);
#endif
cursor_update_focus(view->server);
}
static void
wl_list_insert_tail(struct wl_list *list, struct wl_list *elm)
{
elm->prev = list->prev;
elm->next = list;
list->prev = elm;
elm->prev->next = elm;
}
void
desktop_move_to_back(struct view *view)
{
if (!view) {
return;
}
wl_list_remove(&view->link);
wl_list_insert_tail(&view->server->views, &view->link);
}
static void
deactivate_all_views(struct server *server)
{
struct view *view;
wl_list_for_each (view, &server->views, link) {
if (!view->mapped) {
continue;
}
view_set_activated(view, false);
}
}
void
desktop_focus_and_activate_view(struct seat *seat, struct view *view)
{
if (!view) {
seat_focus_surface(seat, NULL);
return;
}
/*
* Guard against views with no mapped surfaces when handling
* 'request_activate' and 'request_minimize'.
* See notes by isfocusable()
*/
if (!view->surface) {
return;
}
if (input_inhibit_blocks_surface(seat, view->surface->resource)) {
return;
}
if (view->minimized) {
/*
* Unminimizing will map the view which triggers a call to this
* function again.
*/
view_minimize(view, false);
return;
}
if (!view->mapped) {
return;
}
struct wlr_surface *prev_surface;
prev_surface = seat->seat->keyboard_state.focused_surface;
/* Do not re-focus an already focused surface. */
if (prev_surface == view->surface) {
return;
}
deactivate_all_views(view->server);
view_set_activated(view, true);
seat_focus_surface(seat, view->surface);
}
/*
* Some xwayland apps produce unmapped surfaces on startup and also leave
* some unmapped surfaces kicking around on 'close' (for example leafpad's
* "about" dialogue). Whilst this is not normally a problem, we have to be
* careful when cycling between views. The only views we should focus are
* those that are already mapped and those that have been minimized.
*/
bool
isfocusable(struct view *view)
{
/* filter out those xwayland surfaces that have never been mapped */
if (!view->surface) {
return false;
}
return (view->mapped || view->minimized);
}
static bool
has_focusable_view(struct wl_list *wl_list)
{
struct view *view;
wl_list_for_each (view, wl_list, link) {
if (isfocusable(view)) {
return true;
}
}
return false;
}
static struct view *
first_view(struct server *server)
{
struct view *view;
view = wl_container_of(server->views.next, view, link);
return view;
}
struct view *
desktop_cycle_view(struct server *server, struct view *current,
enum lab_cycle_dir dir)
{
if (!has_focusable_view(&server->views)) {
return NULL;
}
struct view *view = current ? current : first_view(server);
if (dir == LAB_CYCLE_DIR_FORWARD) {
/* Replacement for wl_list_for_each_from() */
do {
view = wl_container_of(view->link.next, view, link);
} while (&view->link == &server->views || !isfocusable(view));
} else if (dir == LAB_CYCLE_DIR_BACKWARD) {
do {
view = wl_container_of(view->link.prev, view, link);
} while (&view->link == &server->views || !isfocusable(view));
}
return view;
}
static bool
has_mapped_view(struct wl_list *wl_list)
{
struct view *view;
wl_list_for_each (view, wl_list, link) {
if (view->mapped) {
return true;
}
}
return false;
}
static struct view *
topmost_mapped_view(struct server *server)
{
if (!has_mapped_view(&server->views)) {
return NULL;
}
/* start from tail of server->views */
struct view *view = wl_container_of(server->views.prev, view, link);
do {
view = wl_container_of(view->link.next, view, link);
} while (&view->link == &server->views || !view->mapped);
return view;
}
struct view *
desktop_focused_view(struct server *server)
{
struct seat *seat = &server->seat;
struct wlr_surface *focused_surface;
focused_surface = seat->seat->keyboard_state.focused_surface;
if (!focused_surface) {
return NULL;
}
struct view *view;
wl_list_for_each (view, &server->views, link) {
if (view->surface == focused_surface) {
return view;
}
}
return NULL;
}
void
desktop_focus_topmost_mapped_view(struct server *server)
{
struct view *view = topmost_mapped_view(server);
desktop_focus_and_activate_view(&server->seat, view);
desktop_move_to_front(view);
}
struct view *
desktop_node_and_view_at(struct server *server, double lx, double ly,
struct wlr_scene_node **scene_node, double *sx, double *sy,
enum ssd_part_type *view_area)
{
struct wlr_scene_node *node =
wlr_scene_node_at(&server->scene->tree.node, lx, ly, sx, sy);
*scene_node = node;
if (!node) {
*view_area = LAB_SSD_ROOT;
return NULL;
}
if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_surface *surface = lab_wlr_surface_from_node(node);
if (surface && wlr_surface_is_layer_surface(surface)) {
*view_area = LAB_SSD_LAYER_SURFACE;
return NULL;
}
#if HAVE_XWAYLAND
if (node->parent == server->unmanaged_tree) {
*view_area = LAB_SSD_UNMANAGED;
return NULL;
}
#endif
}
while (node) {
struct node_descriptor *desc = node->data;
/* TODO: convert to switch() */
if (desc) {
if (desc->type == LAB_NODE_DESC_VIEW) {
goto has_view_data;
}
if (desc->type == LAB_NODE_DESC_XDG_POPUP) {
goto has_view_data;
}
if (desc->type == LAB_NODE_DESC_SSD_BUTTON) {
/* Always return the top scene node for SSD buttons */
*scene_node = node;
goto has_view_data;
}
if (desc->type == LAB_NODE_DESC_LAYER_SURFACE) {
/* FIXME: we shouldn't have to set *view_area */
*view_area = LAB_SSD_CLIENT;
return NULL;
}
if (desc->type == LAB_NODE_DESC_LAYER_POPUP) {
/* FIXME: we shouldn't have to set *view_area */
*view_area = LAB_SSD_CLIENT;
return NULL;
}
if (desc->type == LAB_NODE_DESC_MENUITEM) {
/* Always return the top scene node for menu items */
*scene_node = node;
*view_area = LAB_SSD_MENU;
return NULL;
}
}
/* node->parent is always a *wlr_scene_tree */
node = node->parent ? &node->parent->node : NULL;
}
if (!node) {
wlr_log(WLR_ERROR, "Unknown node detected");
}
*view_area = LAB_SSD_NONE;
return NULL;
struct view *view;
struct node_descriptor *desc;
has_view_data:
desc = node->data;
view = desc->data;
*view_area = ssd_get_part_type(view, *scene_node);
return view;
}
struct view *
desktop_view_at_cursor(struct server *server)
{
double sx, sy;
struct wlr_scene_node *node;
enum ssd_part_type view_area = LAB_SSD_NONE;
return desktop_node_and_view_at(server,
server->seat.cursor->x, server->seat.cursor->y,
&node, &sx, &sy, &view_area);
}