2018-08-03 23:06:01 +10:00
|
|
|
#define _POSIX_C_SOURCE 200809L
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <wlr/types/wlr_output_layout.h>
|
Prepare arrange code for type safe arguments
This commit changes the arrange code in a way that will support type
safe arguments.
The arrange_output et al functions are now public, however I opted not
to use them directly yet. I've kept the generic arrange_windows there
for convenience until type safety is fully implemented. This means this
patch has much less risk of breaking things as it would otherwise.
To be type safe, arrange_children_of cannot exist in its previous form
because the thing passed to it could be either a workspace or a
container. So it's now renamed to arrange_children and accepts a list_t,
as well as the parent layout and parent's box.
There was some code which checked the grandparent's layout to see if it
was tabbed or stacked and adjusted the Y offset of the grandchild
accordingly. Accessing the grandparent layout isn't easy when using type
safe arguments, and it seemed odd to even need to do this. I determined
that this was needed because a child of a tabbed container would have a
swayc Y matching the top of the tab bar. I've changed this so a child of
a tabbed container will have a swayc Y matching the bottom of the tab
bar, which means we don't need to access the grandparent layout. Some
tweaks to the rendering and autoconfigure code have been made to
implement this, and the container_at code appears to work without
needing any changes.
arrange_children_of (now arrange_children) would check if the parent had
gaps and would copy them to the child, effectively making the
workspace's gaps recurse into all children. We can't do this any more
without passing has_gaps, gaps_inner and gaps_outer as arguments to
arrange_children, so I've changed the add_gaps function to retrieve it
from the workspace directly.
apply_tabbed_or_stacked_layout has been split into two functions, as it
had different logic depending on the layout.
Lastly, arrange.h had an unnecessary include of transaction.h. I've
removed it, which means I've had to add it to several other files.
2018-08-26 10:16:49 +10:00
|
|
|
#include "sway/desktop/transaction.h"
|
2018-08-03 23:06:01 +10:00
|
|
|
#include "sway/input/seat.h"
|
2018-08-04 14:01:49 +10:00
|
|
|
#include "sway/output.h"
|
2018-08-03 23:06:01 +10:00
|
|
|
#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"
|
2018-08-04 14:01:49 +10:00
|
|
|
#include "util.h"
|
2018-08-03 23:06:01 +10:00
|
|
|
|
|
|
|
struct sway_container root_container;
|
|
|
|
|
|
|
|
static void output_layout_handle_change(struct wl_listener *listener,
|
|
|
|
void *data) {
|
|
|
|
arrange_windows(&root_container);
|
|
|
|
transaction_commit_dirty();
|
|
|
|
}
|
|
|
|
|
|
|
|
void root_create(void) {
|
|
|
|
root_container.id = 0; // normally assigned in new_swayc()
|
|
|
|
root_container.type = C_ROOT;
|
|
|
|
root_container.layout = L_NONE;
|
|
|
|
root_container.name = strdup("root");
|
|
|
|
root_container.children = create_list();
|
|
|
|
root_container.current.children = create_list();
|
|
|
|
wl_signal_init(&root_container.events.destroy);
|
|
|
|
|
|
|
|
root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
|
|
|
|
root_container.sway_root->output_layout = wlr_output_layout_create();
|
2018-08-25 19:29:04 +10:00
|
|
|
wl_list_init(&root_container.sway_root->all_outputs);
|
2018-08-03 23:06:01 +10:00
|
|
|
#ifdef HAVE_XWAYLAND
|
|
|
|
wl_list_init(&root_container.sway_root->xwayland_unmanaged);
|
|
|
|
#endif
|
|
|
|
wl_list_init(&root_container.sway_root->drag_icons);
|
|
|
|
wl_signal_init(&root_container.sway_root->events.new_container);
|
|
|
|
root_container.sway_root->scratchpad = create_list();
|
2018-08-20 15:54:30 +10:00
|
|
|
root_container.sway_root->saved_workspaces = create_list();
|
2018-08-03 23:06:01 +10:00
|
|
|
|
|
|
|
root_container.sway_root->output_layout_change.notify =
|
|
|
|
output_layout_handle_change;
|
|
|
|
wl_signal_add(&root_container.sway_root->output_layout->events.change,
|
|
|
|
&root_container.sway_root->output_layout_change);
|
|
|
|
}
|
|
|
|
|
|
|
|
void root_destroy(void) {
|
|
|
|
// sway_root
|
|
|
|
wl_list_remove(&root_container.sway_root->output_layout_change.link);
|
|
|
|
list_free(root_container.sway_root->scratchpad);
|
2018-08-20 15:54:30 +10:00
|
|
|
list_free(root_container.sway_root->saved_workspaces);
|
2018-08-03 23:06:01 +10:00
|
|
|
wlr_output_layout_destroy(root_container.sway_root->output_layout);
|
|
|
|
free(root_container.sway_root);
|
|
|
|
|
|
|
|
// root_container
|
|
|
|
list_free(root_container.children);
|
|
|
|
list_free(root_container.current.children);
|
|
|
|
free(root_container.name);
|
|
|
|
|
|
|
|
memset(&root_container, 0, sizeof(root_container));
|
|
|
|
}
|
|
|
|
|
|
|
|
void root_scratchpad_add_container(struct sway_container *con) {
|
|
|
|
if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
con->scratchpad = true;
|
|
|
|
list_add(root_container.sway_root->scratchpad, con);
|
|
|
|
|
|
|
|
struct sway_container *parent = con->parent;
|
|
|
|
container_set_floating(con, true);
|
|
|
|
container_remove_child(con);
|
|
|
|
arrange_windows(parent);
|
|
|
|
|
|
|
|
struct sway_seat *seat = input_manager_current_seat(input_manager);
|
|
|
|
seat_set_focus(seat, seat_get_focus_inactive(seat, parent));
|
|
|
|
}
|
|
|
|
|
|
|
|
void root_scratchpad_remove_container(struct sway_container *con) {
|
|
|
|
if (!sway_assert(con->scratchpad, "Container is not in scratchpad")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
con->scratchpad = false;
|
Use list_find in more places and refactor/fix workspace prev_next functions
The original purpose of this commit is to replace some for loops with
list_find. But while doing this I found the workspace_prev_next_impl
functions to be difficult to read and also contained a bug, so I
refactored them and fixed the bug.
To reproduce the bug:
* Have two outputs, where the left output has workspaces 1, 2, 3 and the
right output has workspaces 4, 5, 6. Make workspace 2 focused_inactive
and workspace 4 focused.
* Run `workspace prev`.
* Previously it would visit the left output, then apply `workspace prev`
to workspace 2, which focuses workspace 1.
* Now it will focus the rightmost workspace on the left output
(workspace 3).
The refactoring I made to the workspace functions are:
* Added the static keyword.
* They now accept an int dir rather than bool, to avoid an unnecessary
conversion.
* Rather than preparing start and end variables for the purpose of
iterating, just iterate everything.
* Replace for loops with list_find.
* Don't call workspace_output_prev_next_impl (this fixes the bug).
2018-08-15 15:14:35 +10:00
|
|
|
int index = list_find(root_container.sway_root->scratchpad, con);
|
|
|
|
if (index != -1) {
|
|
|
|
list_del(root_container.sway_root->scratchpad, index);
|
2018-08-03 23:06:01 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void root_scratchpad_show(struct sway_container *con) {
|
|
|
|
struct sway_seat *seat = input_manager_current_seat(input_manager);
|
|
|
|
struct sway_container *ws = seat_get_focus(seat);
|
|
|
|
if (ws->type != C_WORKSPACE) {
|
|
|
|
ws = container_parent(ws, C_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 (ws->sway_workspace->fullscreen) {
|
|
|
|
container_set_fullscreen(ws->sway_workspace->fullscreen, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Show the container
|
|
|
|
if (con->parent) {
|
|
|
|
container_remove_child(con);
|
|
|
|
}
|
2018-08-19 15:07:07 +10:00
|
|
|
workspace_add_floating(ws, con);
|
2018-08-03 23:06:01 +10:00
|
|
|
|
|
|
|
// Make sure the container's center point overlaps this workspace
|
|
|
|
double center_lx = con->x + con->width / 2;
|
|
|
|
double center_ly = con->y + con->height / 2;
|
|
|
|
|
|
|
|
struct wlr_box workspace_box;
|
|
|
|
container_get_box(ws, &workspace_box);
|
|
|
|
if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
|
|
|
|
// Maybe resize it
|
|
|
|
if (con->width > ws->width || con->height > ws->height) {
|
|
|
|
container_init_floating(con);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Center it
|
|
|
|
double new_lx = ws->x + (ws->width - con->width) / 2;
|
|
|
|
double new_ly = ws->y + (ws->height - con->height) / 2;
|
|
|
|
container_floating_move_to(con, new_lx, new_ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
arrange_windows(ws);
|
|
|
|
seat_set_focus(seat, seat_get_focus_inactive(seat, con));
|
|
|
|
|
|
|
|
container_set_dirty(con->parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void root_scratchpad_hide(struct sway_container *con) {
|
|
|
|
struct sway_seat *seat = input_manager_current_seat(input_manager);
|
|
|
|
struct sway_container *focus = seat_get_focus(seat);
|
|
|
|
struct sway_container *ws = container_parent(con, C_WORKSPACE);
|
|
|
|
|
|
|
|
container_remove_child(con);
|
|
|
|
arrange_windows(ws);
|
|
|
|
if (con == focus) {
|
|
|
|
seat_set_focus(seat, seat_get_focus_inactive(seat, ws));
|
|
|
|
}
|
|
|
|
list_move_to_end(root_container.sway_root->scratchpad, con);
|
|
|
|
}
|
2018-08-04 14:01:49 +10:00
|
|
|
|
|
|
|
struct pid_workspace {
|
|
|
|
pid_t pid;
|
|
|
|
char *workspace;
|
|
|
|
struct timespec time_added;
|
|
|
|
|
|
|
|
struct sway_container *output;
|
|
|
|
struct wl_listener output_destroy;
|
|
|
|
|
|
|
|
struct wl_list link;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct wl_list pid_workspaces;
|
|
|
|
|
|
|
|
struct sway_container *root_workspace_for_pid(pid_t pid) {
|
|
|
|
if (!pid_workspaces.prev && !pid_workspaces.next) {
|
|
|
|
wl_list_init(&pid_workspaces);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sway_container *ws = NULL;
|
|
|
|
struct pid_workspace *pw = NULL;
|
|
|
|
|
|
|
|
wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid);
|
|
|
|
|
|
|
|
do {
|
|
|
|
struct pid_workspace *_pw = NULL;
|
|
|
|
wl_list_for_each(_pw, &pid_workspaces, link) {
|
|
|
|
if (pid == _pw->pid) {
|
|
|
|
pw = _pw;
|
|
|
|
wlr_log(WLR_DEBUG,
|
|
|
|
"found pid_workspace for pid %d, workspace %s",
|
|
|
|
pid, pw->workspace);
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pid = get_parent_pid(pid);
|
|
|
|
} while (pid > 1);
|
|
|
|
found:
|
|
|
|
|
|
|
|
if (pw && pw->workspace) {
|
|
|
|
ws = workspace_by_name(pw->workspace);
|
|
|
|
|
|
|
|
if (!ws) {
|
|
|
|
wlr_log(WLR_DEBUG,
|
|
|
|
"Creating workspace %s for pid %d because it disappeared",
|
|
|
|
pw->workspace, pid);
|
|
|
|
ws = workspace_create(pw->output, pw->workspace);
|
|
|
|
}
|
|
|
|
|
|
|
|
wl_list_remove(&pw->output_destroy.link);
|
|
|
|
wl_list_remove(&pw->link);
|
|
|
|
free(pw->workspace);
|
|
|
|
free(pw);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ws;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pw_handle_output_destroy(struct wl_listener *listener, void *data) {
|
|
|
|
struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy);
|
|
|
|
pw->output = NULL;
|
|
|
|
wl_list_remove(&pw->output_destroy.link);
|
|
|
|
wl_list_init(&pw->output_destroy.link);
|
|
|
|
}
|
|
|
|
|
|
|
|
void root_record_workspace_pid(pid_t pid) {
|
|
|
|
wlr_log(WLR_DEBUG, "Recording workspace for process %d", pid);
|
|
|
|
if (!pid_workspaces.prev && !pid_workspaces.next) {
|
|
|
|
wl_list_init(&pid_workspaces);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sway_seat *seat = input_manager_current_seat(input_manager);
|
|
|
|
struct sway_container *ws =
|
|
|
|
seat_get_focus_inactive(seat, &root_container);
|
|
|
|
if (ws && ws->type != C_WORKSPACE) {
|
|
|
|
ws = container_parent(ws, C_WORKSPACE);
|
|
|
|
}
|
|
|
|
if (!ws) {
|
|
|
|
wlr_log(WLR_DEBUG, "Bailing out, no workspace");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
struct sway_container *output = ws->parent;
|
|
|
|
if (!output) {
|
|
|
|
wlr_log(WLR_DEBUG, "Bailing out, no output");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timespec now;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
|
|
|
|
// Remove expired entries
|
|
|
|
static const int timeout = 60;
|
|
|
|
struct pid_workspace *old, *_old;
|
|
|
|
wl_list_for_each_safe(old, _old, &pid_workspaces, link) {
|
|
|
|
if (now.tv_sec - old->time_added.tv_sec >= timeout) {
|
|
|
|
wl_list_remove(&old->output_destroy.link);
|
|
|
|
wl_list_remove(&old->link);
|
|
|
|
free(old->workspace);
|
|
|
|
free(old);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace));
|
|
|
|
pw->workspace = strdup(ws->name);
|
|
|
|
pw->output = output;
|
|
|
|
pw->pid = pid;
|
|
|
|
memcpy(&pw->time_added, &now, sizeof(struct timespec));
|
|
|
|
pw->output_destroy.notify = pw_handle_output_destroy;
|
|
|
|
wl_signal_add(&output->sway_output->wlr_output->events.destroy,
|
|
|
|
&pw->output_destroy);
|
|
|
|
wl_list_insert(&pid_workspaces, &pw->link);
|
|
|
|
}
|
2018-08-17 19:48:34 +10:00
|
|
|
|
|
|
|
void root_for_each_workspace(void (*f)(struct sway_container *con, void *data),
|
|
|
|
void *data) {
|
|
|
|
for (int i = 0; i < root_container.children->length; ++i) {
|
|
|
|
struct sway_container *output = root_container.children->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_container.children->length; ++i) {
|
|
|
|
struct sway_container *output = root_container.children->items[i];
|
|
|
|
output_for_each_container(output, f, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scratchpad
|
|
|
|
for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) {
|
|
|
|
struct sway_container *container =
|
|
|
|
root_container.sway_root->scratchpad->items[i];
|
|
|
|
// If the container has a parent then it's visible on a workspace
|
|
|
|
// and will have been iterated in the previous for loop. So we only
|
|
|
|
// iterate the hidden scratchpad containers here.
|
|
|
|
if (!container->parent) {
|
|
|
|
f(container, data);
|
|
|
|
container_for_each_child(container, f, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sway_container *root_find_output(
|
|
|
|
bool (*test)(struct sway_container *con, void *data), void *data) {
|
|
|
|
for (int i = 0; i < root_container.children->length; ++i) {
|
|
|
|
struct sway_container *output = root_container.children->items[i];
|
|
|
|
if (test(output, data)) {
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sway_container *root_find_workspace(
|
|
|
|
bool (*test)(struct sway_container *con, void *data), void *data) {
|
|
|
|
struct sway_container *result = NULL;
|
|
|
|
for (int i = 0; i < root_container.children->length; ++i) {
|
|
|
|
struct sway_container *output = root_container.children->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_container.children->length; ++i) {
|
|
|
|
struct sway_container *output = root_container.children->items[i];
|
|
|
|
if ((result = output_find_container(output, test, data))) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scratchpad
|
|
|
|
for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) {
|
|
|
|
struct sway_container *container =
|
|
|
|
root_container.sway_root->scratchpad->items[i];
|
|
|
|
if (!container->parent) {
|
|
|
|
if (test(container, data)) {
|
|
|
|
return container;
|
|
|
|
}
|
|
|
|
if ((result = container_find_child(container, test, data))) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|