WIP Output-namespaced workspaces

This commit is contained in:
Kenny Levinsen 2019-12-23 13:36:55 +01:00
parent 088b374b1a
commit 59e308480e
14 changed files with 127 additions and 51 deletions

View file

@ -190,6 +190,7 @@ sway_cmd cmd_unmark;
sway_cmd cmd_urgent;
sway_cmd cmd_workspace;
sway_cmd cmd_workspace_layout;
sway_cmd cmd_workspace_namespace;
sway_cmd cmd_ws_auto_back_and_forth;
sway_cmd cmd_xwayland;

View file

@ -471,6 +471,11 @@ enum xwayland_mode {
XWAYLAND_MODE_IMMEDIATE
};
enum sway_workspace_namespace {
WORKSPACE_NAMESPACE_GLOBAL,
WORKSPACE_NAMESPACE_OUTPUT,
};
/**
* The configuration struct. The result of loading a config file.
*/
@ -512,6 +517,7 @@ struct sway_config {
enum sway_fowa focus_on_window_activation;
enum sway_popup_during_fullscreen popup_during_fullscreen;
enum xwayland_mode xwayland;
enum sway_workspace_namespace workspace_namespace;
// swaybg
char *swaybg_command;

View file

@ -58,14 +58,14 @@ void workspace_begin_destroy(struct sway_workspace *workspace);
void workspace_consider_destroy(struct sway_workspace *ws);
char *workspace_next_name(const char *output_name);
char *workspace_next_name(struct sway_output *output);
bool workspace_switch(struct sway_workspace *workspace,
bool no_auto_back_and_forth);
struct sway_workspace *workspace_by_number(const char* name);
struct sway_workspace *workspace_by_number(struct sway_output *output, const char* name);
struct sway_workspace *workspace_by_name(const char*);
struct sway_workspace *workspace_by_name(struct sway_output *output, const char* name);
struct sway_workspace *workspace_output_next(
struct sway_workspace *current, bool create);

View file

@ -105,6 +105,7 @@ static struct cmd_handler config_handlers[] = {
{ "swaybg_command", cmd_swaybg_command },
{ "swaynag_command", cmd_swaynag_command },
{ "workspace_layout", cmd_workspace_layout },
{ "workspace_namespace", cmd_workspace_namespace },
{ "xwayland", cmd_xwayland },
};

View file

@ -430,9 +430,9 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
strcasecmp(argv[1], "next_on_output") == 0 ||
strcasecmp(argv[1], "prev_on_output") == 0 ||
strcasecmp(argv[1], "current") == 0) {
ws = workspace_by_name(argv[1]);
ws = workspace_by_name(old_output, argv[1]);
} else if (strcasecmp(argv[1], "back_and_forth") == 0) {
if (!(ws = workspace_by_name(argv[1]))) {
if (!(ws = workspace_by_name(old_output, argv[1]))) {
if (seat->prev_workspace_name) {
ws_name = strdup(seat->prev_workspace_name);
} else {
@ -451,10 +451,10 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
"Invalid workspace number '%s'", argv[2]);
}
ws_name = join_args(argv + 2, argc - 2);
ws = workspace_by_number(ws_name);
ws = workspace_by_number(old_output, ws_name);
} else {
ws_name = join_args(argv + 1, argc - 1);
ws = workspace_by_name(ws_name);
ws = workspace_by_name(old_output, ws_name);
}
if (!no_auto_back_and_forth && config->auto_back_and_forth &&
@ -465,7 +465,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
// if target workspace is the current one
free(ws_name);
ws_name = strdup(seat->prev_workspace_name);
ws = workspace_by_name(ws_name);
ws = workspace_by_name(old_output, ws_name);
}
}
}
@ -483,7 +483,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
"on the same output");
}
}
ws = workspace_create(NULL, ws_name);
ws = workspace_create(old_output, ws_name);
}
free(ws_name);
struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws);
@ -614,7 +614,7 @@ static void workspace_move_to_output(struct sway_workspace *workspace,
// on the old output
struct sway_seat *seat = config->handler_context.seat;
if (old_output->workspaces->length == 0) {
char *ws_name = workspace_next_name(old_output->wlr_output->name);
char *ws_name = workspace_next_name(old_output);
struct sway_workspace *ws = workspace_create(old_output, ws_name);
free(ws_name);
seat_set_raw_focus(seat, &ws->node);
@ -658,6 +658,11 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
return cmd_results_new(CMD_FAILURE,
"Can't find output with name/direction '%s'", argv[0]);
}
if (config->workspace_namespace == WORKSPACE_NAMESPACE_OUTPUT &&
workspace_by_name(new_output, workspace->name) != NULL) {
free(workspace->name);
workspace->name = workspace_next_name(new_output);
}
workspace_move_to_output(workspace, new_output);
arrange_output(old_output);

View file

@ -31,6 +31,12 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
int argn = 1;
struct sway_workspace *workspace = NULL;
struct sway_seat *seat = config->handler_context.seat;
struct sway_workspace *current = seat_get_focused_workspace(seat);
if (!current) {
return cmd_results_new(CMD_FAILURE, "No workspace available");
}
if (strcasecmp(argv[1], "to") == 0) {
// 'rename workspace to new_name'
workspace = config->handler_context.workspace;
@ -40,7 +46,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
return cmd_results_new(CMD_INVALID,
"Invalid workspace number '%s'", argv[2]);
}
workspace = workspace_by_number(argv[2]);
workspace = workspace_by_number(current->output, argv[2]);
while (argn < argc && strcasecmp(argv[argn], "to") != 0) {
++argn;
}
@ -51,7 +57,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
++end;
}
char *old_name = join_args(argv + argn, end - argn);
workspace = workspace_by_name(old_name);
workspace = workspace_by_name(current->output, old_name);
free(old_name);
argn = end;
}
@ -79,7 +85,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
return cmd_results_new(CMD_INVALID,
"Cannot use special workspace name '%s'", argv[argn]);
}
struct sway_workspace *tmp_workspace = workspace_by_name(new_name);
struct sway_workspace *tmp_workspace = workspace_by_name(current->output, new_name);
if (tmp_workspace) {
free(new_name);
if (tmp_workspace == workspace) {

View file

@ -198,15 +198,15 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
return cmd_results_new(CMD_INVALID,
"Invalid workspace number '%s'", argv[1]);
}
if (!(ws = workspace_by_number(argv[1]))) {
if (!(ws = workspace_by_number(current->output, argv[1]))) {
char *name = join_args(argv + 1, argc - 1);
ws = workspace_create(NULL, name);
ws = workspace_create(current->output, name);
free(name);
}
} else if (strcasecmp(argv[0], "next") == 0 ||
strcasecmp(argv[0], "prev") == 0 ||
strcasecmp(argv[0], "current") == 0) {
ws = workspace_by_name(argv[0]);
ws = workspace_by_name(current->output, argv[0]);
} else if (strcasecmp(argv[0], "next_on_output") == 0) {
ws = workspace_output_next(current, create);
} else if (strcasecmp(argv[0], "prev_on_output") == 0) {
@ -216,13 +216,13 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
return cmd_results_new(CMD_INVALID,
"There is no previous workspace");
}
if (!(ws = workspace_by_name(argv[0]))) {
ws = workspace_create(NULL, seat->prev_workspace_name);
if (!(ws = workspace_by_name(current->output, argv[0]))) {
ws = workspace_create(current->output, seat->prev_workspace_name);
}
} else {
char *name = join_args(argv, argc);
if (!(ws = workspace_by_name(name))) {
ws = workspace_create(NULL, name);
if (!(ws = workspace_by_name(current->output, name))) {
ws = workspace_create(current->output, name);
}
free(name);
}

View file

@ -0,0 +1,27 @@
#include <strings.h>
#include "log.h"
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "util.h"
// workspace_namespace [global|output]
struct cmd_results *cmd_workspace_namespace(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "workspace_namespace", EXPECTED_EQUAL_TO, 1))) {
return error;
}
if (strcasecmp(argv[0], "global") == 0 ||
strcasecmp(argv[0], "default") == 0) {
config->workspace_namespace = WORKSPACE_NAMESPACE_GLOBAL;
} else if (strcasecmp(argv[0], "output") == 0) {
config->workspace_namespace = WORKSPACE_NAMESPACE_OUTPUT;
} else {
return cmd_results_new(CMD_FAILURE,
"Expected 'workspace_namespace <default|global|output>'");
}
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -114,6 +114,7 @@ sway_sources = files(
'commands/urgent.c',
'commands/workspace.c',
'commands/workspace_layout.c',
'commands/workspace_namespace.c',
'commands/ws_auto_back_and_forth.c',
'commands/xwayland.c',

View file

@ -88,6 +88,9 @@ The following commands may only be used in the configuration file.
*workspace_layout* default|stacking|tabbed
Specifies the initial layout for new workspaces.
*workspace_namespace* default|global|output
Specifies the workspace namespacing.
*xwayland* enable|disable|force
Enables or disables Xwayland support, which allows X11 applications to be
used. _enable_ will lazily load Xwayland so Xwayland will not be launched

View file

@ -48,7 +48,7 @@ static void restore_workspaces(struct sway_output *output) {
}
if (other->workspaces->length == 0) {
char *next = workspace_next_name(other->wlr_output->name);
char *next = workspace_next_name(other);
struct sway_workspace *ws = workspace_create(other, next);
free(next);
ipc_event_workspace(NULL, ws, "init");
@ -109,7 +109,6 @@ bool output_enable(struct sway_output *output, struct output_config *oc) {
if (!sway_assert(!output->enabled, "output is already enabled")) {
return false;
}
struct wlr_output *wlr_output = output->wlr_output;
size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
for (size_t i = 0; i < len; ++i) {
wl_list_init(&output->layers[i]);
@ -129,7 +128,7 @@ bool output_enable(struct sway_output *output, struct output_config *oc) {
struct sway_workspace *ws = NULL;
if (!output->workspaces->length) {
// Create workspace
char *ws_name = workspace_next_name(wlr_output->name);
char *ws_name = workspace_next_name(output);
sway_log(SWAY_DEBUG, "Creating default workspace %s", ws_name);
ws = workspace_create(output, ws_name);
// Set each seat's focus if not already set
@ -212,6 +211,13 @@ static void output_evacuate(struct sway_output *output) {
continue;
}
if (config->workspace_namespace == WORKSPACE_NAMESPACE_OUTPUT &&
workspace_by_name(new_output, workspace->name) != NULL) {
// Rename the workspace to avoid name collision
free(workspace->name);
workspace->name = workspace_next_name(new_output);
}
struct sway_workspace *new_output_ws =
output_get_active_workspace(new_output);

View file

@ -252,7 +252,7 @@ struct sway_workspace *root_workspace_for_pid(pid_t pid) {
found:
if (pw && pw->workspace) {
ws = workspace_by_name(pw->workspace);
ws = workspace_by_name(pw->output, pw->workspace);
if (!ws) {
sway_log(SWAY_DEBUG,

View file

@ -470,6 +470,11 @@ static void view_populate_pid(struct sway_view *view) {
static struct sway_workspace *select_workspace(struct sway_view *view) {
struct sway_seat *seat = input_manager_current_seat();
struct sway_workspace *current = seat_get_focused_workspace(seat);
if (!sway_assert(current != NULL, "no workspace is focused")) {
return NULL;
}
struct sway_output *output = current->output;
// Check if there's any `assign` criteria for the view
list_t *criterias = criteria_for_view(view,
@ -486,16 +491,16 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
} else {
// CT_ASSIGN_WORKSPACE(_NUMBER)
ws = criteria->type == CT_ASSIGN_WORKSPACE_NUMBER ?
workspace_by_number(criteria->target) :
workspace_by_name(criteria->target);
workspace_by_number(output, criteria->target) :
workspace_by_name(output, criteria->target);
if (!ws) {
if (strcasecmp(criteria->target, "back_and_forth") == 0) {
if (seat->prev_workspace_name) {
ws = workspace_create(NULL, seat->prev_workspace_name);
ws = workspace_create(output, seat->prev_workspace_name);
}
} else {
ws = workspace_create(NULL, criteria->target);
ws = workspace_create(output, criteria->target);
}
}
break;

View file

@ -171,15 +171,14 @@ void workspace_consider_destroy(struct sway_workspace *ws) {
workspace_begin_destroy(ws);
}
static bool workspace_valid_on_output(const char *output_name,
static bool workspace_valid_on_output(struct sway_output *output,
const char *ws_name) {
struct workspace_config *wsc = workspace_find_config(ws_name);
char identifier[128];
struct sway_output *output = output_by_name_or_id(output_name);
if (!output) {
return false;
}
output_name = output->wlr_output->name;
char *output_name = output->wlr_output->name;
output_get_identifier(identifier, sizeof(identifier), output);
if (!wsc) {
@ -198,7 +197,7 @@ static bool workspace_valid_on_output(const char *output_name,
}
static void workspace_name_from_binding(const struct sway_binding * binding,
const char* output_name, int *min_order, char **earliest_name) {
struct sway_output* output, int *min_order, char **earliest_name) {
char *cmdlist = strdup(binding->command);
char *dup = cmdlist;
char *name = NULL;
@ -245,7 +244,7 @@ static void workspace_name_from_binding(const struct sway_binding * binding,
sway_log(SWAY_DEBUG, "Isolated name from workspace number: '%s'", _target);
// Make sure the workspace number doesn't already exist
if (isdigit(_target[0]) && workspace_by_number(_target)) {
if (isdigit(_target[0]) && workspace_by_number(output, _target)) {
free(_target);
free(dup);
return;
@ -253,7 +252,7 @@ static void workspace_name_from_binding(const struct sway_binding * binding,
}
// Make sure that the workspace doesn't already exist
if (workspace_by_name(_target)) {
if (workspace_by_name(output, _target)) {
free(_target);
free(dup);
return;
@ -261,7 +260,7 @@ static void workspace_name_from_binding(const struct sway_binding * binding,
// make sure that the workspace can appear on the given
// output
if (!workspace_valid_on_output(output_name, _target)) {
if (!workspace_valid_on_output(output, _target)) {
free(_target);
free(dup);
return;
@ -279,7 +278,8 @@ static void workspace_name_from_binding(const struct sway_binding * binding,
free(dup);
}
char *workspace_next_name(const char *output_name) {
char *workspace_next_name(struct sway_output* output) {
char *output_name = output->wlr_output->name;
sway_log(SWAY_DEBUG, "Workspace: Generating new workspace name for output %s",
output_name);
// Scan for available workspace names by looking through output-workspace
@ -287,27 +287,22 @@ char *workspace_next_name(const char *output_name) {
struct sway_mode *mode = config->current_mode;
char identifier[128];
struct sway_output *output = output_by_name_or_id(output_name);
if (!output) {
return NULL;
}
output_name = output->wlr_output->name;
output_get_identifier(identifier, sizeof(identifier), output);
int order = INT_MAX;
char *target = NULL;
for (int i = 0; i < mode->keysym_bindings->length; ++i) {
workspace_name_from_binding(mode->keysym_bindings->items[i],
output_name, &order, &target);
output, &order, &target);
}
for (int i = 0; i < mode->keycode_bindings->length; ++i) {
workspace_name_from_binding(mode->keycode_bindings->items[i],
output_name, &order, &target);
output, &order, &target);
}
for (int i = 0; i < config->workspace_configs->length; ++i) {
// Unlike with bindings, this does not guarantee order
const struct workspace_config *wsc = config->workspace_configs->items[i];
if (workspace_by_name(wsc->workspace)) {
if (workspace_by_name(output, wsc->workspace)) {
continue;
}
bool found = false;
@ -333,7 +328,7 @@ char *workspace_next_name(const char *output_name) {
unsigned int ws_num = 1;
do {
snprintf(name, sizeof(name), "%u", ws_num++);
} while (workspace_by_number(name));
} while (workspace_by_number(output, name));
return strdup(name);
}
@ -348,7 +343,12 @@ static bool _workspace_by_number(struct sway_workspace *ws, void *data) {
return !isdigit(*ws_name);
}
struct sway_workspace *workspace_by_number(const char* name) {
struct sway_workspace *workspace_by_number(struct sway_output *output,
const char* name) {
if (config->workspace_namespace == WORKSPACE_NAMESPACE_OUTPUT) {
return output_find_workspace(output, _workspace_by_number,
(void*)name);
}
return root_find_workspace(_workspace_by_number, (void *) name);
}
@ -356,7 +356,8 @@ static bool _workspace_by_name(struct sway_workspace *ws, void *data) {
return strcasecmp(ws->name, data) == 0;
}
struct sway_workspace *workspace_by_name(const char *name) {
struct sway_workspace *workspace_by_name(struct sway_output *output,
const char *name) {
struct sway_seat *seat = input_manager_current_seat();
struct sway_workspace *current = seat_get_focused_workspace(seat);
@ -375,9 +376,23 @@ struct sway_workspace *workspace_by_name(const char *name) {
if (!seat->prev_workspace_name) {
return NULL;
}
if (config->workspace_namespace == WORKSPACE_NAMESPACE_OUTPUT) {
if (output == NULL) {
return NULL;
}
return output_find_workspace(output, _workspace_by_name,
(void*)seat->prev_workspace_name);
}
return root_find_workspace(_workspace_by_name,
(void*)seat->prev_workspace_name);
} else {
if (config->workspace_namespace == WORKSPACE_NAMESPACE_OUTPUT) {
if (output == NULL) {
return NULL;
}
return output_find_workspace(output, _workspace_by_name,
(void*)name);
}
return root_find_workspace(_workspace_by_name, (void*)name);
}
}
@ -401,7 +416,7 @@ static struct sway_workspace *workspace_output_prev_next_impl(
if (!workspace_is_empty(workspace) && create &&
(index + dir < 0 || index + dir == output->workspaces->length)) {
struct sway_output *output = workspace->output;
char *next = workspace_next_name(output->wlr_output->name);
char *next = workspace_next_name(output);
workspace_create(output, next);
free(next);
}
@ -467,10 +482,10 @@ bool workspace_switch(struct sway_workspace *workspace,
if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws
&& active_ws == workspace && seat->prev_workspace_name) {
struct sway_workspace *new_ws =
workspace_by_name(seat->prev_workspace_name);
workspace_by_name(NULL, seat->prev_workspace_name);
workspace = new_ws ?
new_ws :
workspace_create(NULL, seat->prev_workspace_name);
workspace_create(active_ws->output, seat->prev_workspace_name);
}
sway_log(SWAY_DEBUG, "Switching to workspace %p:%s",