Skip to content

Commit

Permalink
Add support for wlr-layer-shell ON_DEMAND keyboard interactivity
Browse files Browse the repository at this point in the history
This allows for layer shell surfaces to receive focus while the surface is explicitly focused, i.e allowing
text fields to receive keyboard input just like a regular surface.
  • Loading branch information
ErikReider authored and kennylevinsen committed Jun 6, 2023
1 parent 7ab8cb2 commit 913a767
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 13 deletions.
3 changes: 2 additions & 1 deletion include/sway/input/seat.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,9 @@ struct sway_seat {
struct sway_workspace *workspace;
char *prev_workspace_name; // for workspace back_and_forth

// If the focused layer is set, views cannot receive keyboard focus
struct wlr_layer_surface_v1 *focused_layer;
// If the exclusive layer is set, views cannot receive keyboard focus
bool has_exclusive_layer;

// If exclusive_client is set, no other clients will receive input events
struct wl_client *exclusive_client;
Expand Down
4 changes: 4 additions & 0 deletions include/sway/layers.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ struct sway_layer_subsurface {
};

struct sway_output;

struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
struct wlr_surface *surface);

void arrange_layers(struct sway_output *output);

struct sway_layer_surface *layer_from_wlr_layer_surface_v1(
Expand Down
40 changes: 38 additions & 2 deletions sway/desktop/layer_shell.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,39 @@
#include "sway/tree/arrange.h"
#include "sway/tree/workspace.h"

struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
struct wlr_surface *surface) {
struct wlr_layer_surface_v1 *layer;
do {
if (!surface) {
return NULL;
}
// Topmost layer surface
if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) {
return layer;
}
// Layer subsurface
if (wlr_subsurface_try_from_wlr_surface(surface)) {
surface = wlr_surface_get_root_surface(surface);
continue;
}

// Layer surface popup
struct wlr_xdg_surface * xdg_popup = NULL;
if ((xdg_popup = wlr_xdg_surface_try_from_wlr_surface(surface)) &&
xdg_popup->role == WLR_XDG_SURFACE_ROLE_POPUP) {
if (!xdg_popup->popup->parent) {
return NULL;
}
surface = wlr_surface_get_root_surface(xdg_popup->popup->parent);
continue;
}

// Return early if the surface is not a layer/xdg_popup/sub surface
return NULL;
} while (true);
}

static void apply_exclusive(struct wlr_box *usable_area,
uint32_t anchor, int32_t exclusive,
int32_t margin_top, int32_t margin_right,
Expand Down Expand Up @@ -218,7 +251,8 @@ void arrange_layers(struct sway_output *output) {
for (size_t i = 0; i < nlayers; ++i) {
wl_list_for_each_reverse(layer,
&output->layers[layers_above_shell[i]], link) {
if (layer->layer_surface->current.keyboard_interactive &&
if (layer->layer_surface->current.keyboard_interactive
== ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE &&
layer->layer_surface->surface->mapped) {
topmost = layer;
break;
Expand All @@ -231,10 +265,12 @@ void arrange_layers(struct sway_output *output) {

struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
seat->has_exclusive_layer = false;
if (topmost != NULL) {
seat_set_focus_layer(seat, topmost->layer_surface);
} else if (seat->focused_layer &&
!seat->focused_layer->current.keyboard_interactive) {
seat->focused_layer->current.keyboard_interactive
!= ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
seat_set_focus_layer(seat, NULL);
}
}
Expand Down
20 changes: 15 additions & 5 deletions sway/input/seat.c
Original file line number Diff line number Diff line change
Expand Up @@ -1295,11 +1295,15 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n
}

void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
if (seat->focused_layer) {
// Prevents the layer from losing focus if it has keyboard exclusivity
if (seat->has_exclusive_layer) {
struct wlr_layer_surface_v1 *layer = seat->focused_layer;
seat_set_focus_layer(seat, NULL);
seat_set_workspace_focus(seat, node);
seat_set_focus_layer(seat, layer);
} else if (seat->focused_layer) {
seat_set_focus_layer(seat, NULL);
seat_set_workspace_focus(seat, node);
} else {
seat_set_workspace_focus(seat, node);
}
Expand Down Expand Up @@ -1347,14 +1351,20 @@ void seat_set_focus_layer(struct sway_seat *seat,
seat_set_focus(seat, previous);
}
return;
} else if (!layer || seat->focused_layer == layer) {
} else if (!layer) {
return;
}
assert(layer->surface->mapped);
seat_set_focus_surface(seat, layer->surface, true);
if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
seat->focused_layer = layer;
if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP &&
layer->current.keyboard_interactive
== ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
seat->has_exclusive_layer = true;
}
if (seat->focused_layer == layer) {
return;
}
seat_set_focus_surface(seat, layer->surface, true);
seat->focused_layer = layer;
}

void seat_set_exclusive_client(struct sway_seat *seat,
Expand Down
24 changes: 20 additions & 4 deletions sway/input/seatop_default.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
#include <float.h>
#include <libevdev/libevdev.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_tablet_v2.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include "gesture.h"
#include "sway/desktop/transaction.h"
#include "sway/input/cursor.h"
#include "sway/input/seat.h"
#include "sway/input/tablet.h"
#include "sway/layers.h"
#include "sway/output.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
Expand Down Expand Up @@ -365,10 +367,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
return;
}

// Handle clicking a layer surface
struct wlr_layer_surface_v1 *layer;
if (surface &&
(layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) {
// Handle clicking a layer surface and its popups/subsurfaces
struct wlr_layer_surface_v1 *layer = NULL;
if ((layer = toplevel_layer_surface_from_surface(surface))) {
if (layer->current.keyboard_interactive) {
seat_set_focus_layer(seat, layer);
transaction_commit_dirty();
Expand Down Expand Up @@ -544,6 +545,21 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
if (wlr_output == NULL) {
return;
}

struct wlr_surface *surface = NULL;
double sx, sy;
node_at_coords(seat, seat->cursor->cursor->x, seat->cursor->cursor->y,
&surface, &sx, &sy);

// Focus topmost layer surface
struct wlr_layer_surface_v1 *layer = NULL;
if ((layer = toplevel_layer_surface_from_surface(surface)) &&
layer->current.keyboard_interactive) {
seat_set_focus_layer(seat, layer);
transaction_commit_dirty();
return;
}

struct sway_output *hovered_output = wlr_output->data;
if (focus && hovered_output != node_get_output(focus)) {
struct sway_workspace *ws = output_get_active_workspace(hovered_output);
Expand Down
2 changes: 1 addition & 1 deletion sway/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
#endif

#define SWAY_XDG_SHELL_VERSION 2
#define SWAY_LAYER_SHELL_VERSION 3
#define SWAY_LAYER_SHELL_VERSION 4

#if WLR_HAS_DRM_BACKEND
static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
Expand Down

0 comments on commit 913a767

Please sign in to comment.