mirror of
https://github.com/gwenhael-le-moine/sway-patched-tray-menu.git
synced 2024-12-25 21:58:31 +01:00
9704152414
Instead of having a build-time option to enable/disable xwayland support, just use the wlroots build config: enable xwayland in Sway if it was enabled when building wlroots. I don't see any use-case for disabling xwayland in Sway when enabled in wlroots: Sway doesn't pull in any additional dependency (just pulls in dependencies that wlroots already needs). We have a config command to disable xwayland at runtime anyways. This makes it so xwayland behaves the same way as other features such as libinput backend and session support. This also reduces the build matrix (less combinations of build options). I think we originally introduced the xwayland option when we didn't have a good way to figure out the wlroots build config from the Sway build system.
338 lines
10 KiB
C
338 lines
10 KiB
C
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wlr/types/wlr_output_layout.h>
|
|
#include <wlr/types/wlr_scene.h>
|
|
#include <wlr/util/transform.h>
|
|
#include "sway/desktop/transaction.h"
|
|
#include "sway/input/seat.h"
|
|
#include "sway/ipc-server.h"
|
|
#include "sway/output.h"
|
|
#include "sway/scene_descriptor.h"
|
|
#include "sway/tree/arrange.h"
|
|
#include "sway/tree/container.h"
|
|
#include "sway/tree/root.h"
|
|
#include "sway/tree/workspace.h"
|
|
#include "list.h"
|
|
#include "log.h"
|
|
#include "util.h"
|
|
|
|
struct sway_root *root;
|
|
|
|
static void output_layout_handle_change(struct wl_listener *listener,
|
|
void *data) {
|
|
arrange_root();
|
|
transaction_commit_dirty();
|
|
}
|
|
|
|
struct sway_root *root_create(struct wl_display *wl_display) {
|
|
struct sway_root *root = calloc(1, sizeof(struct sway_root));
|
|
if (!root) {
|
|
sway_log(SWAY_ERROR, "Unable to allocate sway_root");
|
|
return NULL;
|
|
}
|
|
|
|
struct wlr_scene *root_scene = wlr_scene_create();
|
|
if (!root_scene) {
|
|
sway_log(SWAY_ERROR, "Unable to allocate root scene node");
|
|
free(root);
|
|
return NULL;
|
|
}
|
|
|
|
node_init(&root->node, N_ROOT, root);
|
|
root->root_scene = root_scene;
|
|
|
|
bool failed = false;
|
|
root->staging = alloc_scene_tree(&root_scene->tree, &failed);
|
|
root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed);
|
|
|
|
root->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed);
|
|
root->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed);
|
|
root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed);
|
|
root->layers.floating = alloc_scene_tree(root->layer_tree, &failed);
|
|
root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed);
|
|
root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed);
|
|
root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed);
|
|
#if WLR_HAS_XWAYLAND
|
|
root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed);
|
|
#endif
|
|
root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed);
|
|
root->layers.popup = alloc_scene_tree(root->layer_tree, &failed);
|
|
root->layers.seat = alloc_scene_tree(root->layer_tree, &failed);
|
|
root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed);
|
|
|
|
if (!failed && !scene_descriptor_assign(&root->layers.seat->node,
|
|
SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) {
|
|
failed = true;
|
|
}
|
|
|
|
if (failed) {
|
|
wlr_scene_node_destroy(&root_scene->tree.node);
|
|
free(root);
|
|
return NULL;
|
|
}
|
|
|
|
wlr_scene_node_set_enabled(&root->staging->node, false);
|
|
|
|
root->output_layout = wlr_output_layout_create(wl_display);
|
|
wl_list_init(&root->all_outputs);
|
|
wl_signal_init(&root->events.new_node);
|
|
root->outputs = create_list();
|
|
root->non_desktop_outputs = create_list();
|
|
root->scratchpad = create_list();
|
|
|
|
root->output_layout_change.notify = output_layout_handle_change;
|
|
wl_signal_add(&root->output_layout->events.change,
|
|
&root->output_layout_change);
|
|
return root;
|
|
}
|
|
|
|
void root_destroy(struct sway_root *root) {
|
|
wl_list_remove(&root->output_layout_change.link);
|
|
list_free(root->scratchpad);
|
|
list_free(root->non_desktop_outputs);
|
|
list_free(root->outputs);
|
|
wlr_scene_node_destroy(&root->root_scene->tree.node);
|
|
free(root);
|
|
}
|
|
|
|
static void set_container_transform(struct sway_workspace *ws,
|
|
struct sway_container *con) {
|
|
struct sway_output *output = ws->output;
|
|
struct wlr_box box = {0};
|
|
if (output) {
|
|
output_get_box(output, &box);
|
|
}
|
|
con->transform = box;
|
|
}
|
|
|
|
void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) {
|
|
if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) {
|
|
return;
|
|
}
|
|
|
|
struct sway_container *parent = con->pending.parent;
|
|
struct sway_workspace *workspace = con->pending.workspace;
|
|
|
|
set_container_transform(workspace, con);
|
|
|
|
// Clear the fullscreen mode when sending to the scratchpad
|
|
if (con->pending.fullscreen_mode != FULLSCREEN_NONE) {
|
|
container_fullscreen_disable(con);
|
|
}
|
|
|
|
// When a tiled window is sent to scratchpad, center and resize it.
|
|
if (!container_is_floating(con)) {
|
|
container_set_floating(con, true);
|
|
container_floating_set_default_size(con);
|
|
container_floating_move_to_center(con);
|
|
}
|
|
|
|
container_detach(con);
|
|
con->scratchpad = true;
|
|
list_add(root->scratchpad, con);
|
|
if (ws) {
|
|
workspace_add_floating(ws, con);
|
|
}
|
|
|
|
if (!ws) {
|
|
struct sway_seat *seat = input_manager_current_seat();
|
|
struct sway_node *new_focus = NULL;
|
|
if (parent) {
|
|
arrange_container(parent);
|
|
new_focus = seat_get_focus_inactive(seat, &parent->node);
|
|
}
|
|
if (!new_focus) {
|
|
arrange_workspace(workspace);
|
|
new_focus = seat_get_focus_inactive(seat, &workspace->node);
|
|
}
|
|
seat_set_focus(seat, new_focus);
|
|
}
|
|
|
|
ipc_event_window(con, "move");
|
|
}
|
|
|
|
void root_scratchpad_remove_container(struct sway_container *con) {
|
|
if (!sway_assert(con->scratchpad, "Container is not in scratchpad")) {
|
|
return;
|
|
}
|
|
con->scratchpad = false;
|
|
int index = list_find(root->scratchpad, con);
|
|
if (index != -1) {
|
|
list_del(root->scratchpad, index);
|
|
ipc_event_window(con, "move");
|
|
}
|
|
}
|
|
|
|
void root_scratchpad_show(struct sway_container *con) {
|
|
struct sway_seat *seat = input_manager_current_seat();
|
|
struct sway_workspace *new_ws = seat_get_focused_workspace(seat);
|
|
if (!new_ws) {
|
|
sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on");
|
|
return;
|
|
}
|
|
struct sway_workspace *old_ws = con->pending.workspace;
|
|
|
|
// If the current con or any of its parents are in fullscreen mode, we
|
|
// first need to disable it before showing the scratchpad con.
|
|
if (new_ws->fullscreen) {
|
|
container_fullscreen_disable(new_ws->fullscreen);
|
|
}
|
|
if (root->fullscreen_global) {
|
|
container_fullscreen_disable(root->fullscreen_global);
|
|
}
|
|
|
|
// Show the container
|
|
if (old_ws) {
|
|
container_detach(con);
|
|
// Make sure the last inactive container on the old workspace is above
|
|
// the workspace itself in the focus stack.
|
|
struct sway_node *node = seat_get_focus_inactive(seat, &old_ws->node);
|
|
seat_set_raw_focus(seat, node);
|
|
} else {
|
|
// Act on the ancestor of scratchpad hidden split containers
|
|
while (con->pending.parent) {
|
|
con = con->pending.parent;
|
|
}
|
|
}
|
|
workspace_add_floating(new_ws, con);
|
|
|
|
if (new_ws->output) {
|
|
struct wlr_box output_box;
|
|
output_get_box(new_ws->output, &output_box);
|
|
floating_fix_coordinates(con, &con->transform, &output_box);
|
|
}
|
|
set_container_transform(new_ws, con);
|
|
|
|
arrange_workspace(new_ws);
|
|
seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));
|
|
if (old_ws) {
|
|
workspace_consider_destroy(old_ws);
|
|
}
|
|
}
|
|
|
|
static void disable_fullscreen(struct sway_container *con, void *data) {
|
|
if (con->pending.fullscreen_mode != FULLSCREEN_NONE) {
|
|
container_fullscreen_disable(con);
|
|
}
|
|
}
|
|
|
|
void root_scratchpad_hide(struct sway_container *con) {
|
|
struct sway_seat *seat = input_manager_current_seat();
|
|
struct sway_node *focus = seat_get_focus_inactive(seat, &root->node);
|
|
struct sway_workspace *ws = con->pending.workspace;
|
|
|
|
if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL && !con->pending.workspace) {
|
|
// If the container was made fullscreen global while in the scratchpad,
|
|
// it should be shown until fullscreen has been disabled
|
|
return;
|
|
}
|
|
|
|
set_container_transform(con->pending.workspace, con);
|
|
|
|
disable_fullscreen(con, NULL);
|
|
container_for_each_child(con, disable_fullscreen, NULL);
|
|
container_detach(con);
|
|
arrange_workspace(ws);
|
|
if (&con->node == focus || node_has_ancestor(focus, &con->node)) {
|
|
seat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node));
|
|
}
|
|
list_move_to_end(root->scratchpad, con);
|
|
|
|
ipc_event_window(con, "move");
|
|
}
|
|
|
|
void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
|
|
void *data) {
|
|
for (int i = 0; i < root->outputs->length; ++i) {
|
|
struct sway_output *output = root->outputs->items[i];
|
|
output_for_each_workspace(output, f, data);
|
|
}
|
|
}
|
|
|
|
void root_for_each_container(void (*f)(struct sway_container *con, void *data),
|
|
void *data) {
|
|
for (int i = 0; i < root->outputs->length; ++i) {
|
|
struct sway_output *output = root->outputs->items[i];
|
|
output_for_each_container(output, f, data);
|
|
}
|
|
|
|
// Scratchpad
|
|
for (int i = 0; i < root->scratchpad->length; ++i) {
|
|
struct sway_container *container = root->scratchpad->items[i];
|
|
if (container_is_scratchpad_hidden(container)) {
|
|
f(container, data);
|
|
container_for_each_child(container, f, data);
|
|
}
|
|
}
|
|
|
|
// Saved workspaces
|
|
for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
|
|
struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
|
|
workspace_for_each_container(ws, f, data);
|
|
}
|
|
}
|
|
|
|
struct sway_output *root_find_output(
|
|
bool (*test)(struct sway_output *output, void *data), void *data) {
|
|
for (int i = 0; i < root->outputs->length; ++i) {
|
|
struct sway_output *output = root->outputs->items[i];
|
|
if (test(output, data)) {
|
|
return output;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct sway_workspace *root_find_workspace(
|
|
bool (*test)(struct sway_workspace *ws, void *data), void *data) {
|
|
struct sway_workspace *result = NULL;
|
|
for (int i = 0; i < root->outputs->length; ++i) {
|
|
struct sway_output *output = root->outputs->items[i];
|
|
if ((result = output_find_workspace(output, test, data))) {
|
|
return result;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct sway_container *root_find_container(
|
|
bool (*test)(struct sway_container *con, void *data), void *data) {
|
|
struct sway_container *result = NULL;
|
|
for (int i = 0; i < root->outputs->length; ++i) {
|
|
struct sway_output *output = root->outputs->items[i];
|
|
if ((result = output_find_container(output, test, data))) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Scratchpad
|
|
for (int i = 0; i < root->scratchpad->length; ++i) {
|
|
struct sway_container *container = root->scratchpad->items[i];
|
|
if (container_is_scratchpad_hidden(container)) {
|
|
if (test(container, data)) {
|
|
return container;
|
|
}
|
|
if ((result = container_find_child(container, test, data))) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Saved workspaces
|
|
for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
|
|
struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
|
|
if ((result = workspace_find_container(ws, test, data))) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void root_get_box(struct sway_root *root, struct wlr_box *box) {
|
|
box->x = root->x;
|
|
box->y = root->y;
|
|
box->width = root->width;
|
|
box->height = root->height;
|
|
}
|