Merge branch 'master' into workspace-move-to-output

This commit is contained in:
Brian Ashworth 2018-08-06 11:47:00 -04:00 committed by GitHub
commit 639f3368e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 534 additions and 192 deletions

17
include/sway/decoration.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef _SWAY_DECORATION_H
#define _SWAY_DECORATION_H
#include <wlr/types/wlr_server_decoration.h>
struct sway_server_decoration {
struct wlr_server_decoration *wlr_server_decoration;
struct wl_list link;
struct wl_listener destroy;
struct wl_listener mode;
};
struct sway_server_decoration *decoration_from_surface(
struct wlr_surface *surface);
#endif

View File

@ -4,12 +4,13 @@
#include <wayland-server.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_layer_shell.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/render/wlr_renderer.h>
// TODO WLR: make Xwayland optional
#include "list.h"
#include "config.h"
@ -42,11 +43,17 @@ struct sway_server {
struct wlr_xdg_shell *xdg_shell;
struct wl_listener xdg_shell_surface;
#ifdef HAVE_XWAYLAND
struct sway_xwayland xwayland;
struct wl_listener xwayland_surface;
struct wl_listener xwayland_ready;
#endif
struct wlr_server_decoration_manager *server_decoration_manager;
struct wl_listener server_decoration;
struct wl_list decorations; // sway_server_decoration::link
bool debug_txn_timings;
list_t *transactions;
@ -71,4 +78,6 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
#ifdef HAVE_XWAYLAND
void handle_xwayland_surface(struct wl_listener *listener, void *data);
#endif
void handle_server_decoration(struct wl_listener *listener, void *data);
#endif

View File

@ -113,7 +113,7 @@ struct sway_container {
enum sway_container_type type;
enum sway_container_layout layout;
enum sway_container_layout prev_layout;
enum sway_container_layout prev_split_layout;
bool is_sticky;

View File

@ -118,6 +118,8 @@ struct sway_view {
struct sway_xdg_shell_v6_view {
struct sway_view view;
enum wlr_server_decoration_manager_mode deco_mode;
struct wl_listener commit;
struct wl_listener request_move;
struct wl_listener request_resize;
@ -134,6 +136,8 @@ struct sway_xdg_shell_v6_view {
struct sway_xdg_shell_view {
struct sway_view view;
enum wlr_server_decoration_manager_mode deco_mode;
struct wl_listener commit;
struct wl_listener request_move;
struct wl_listener request_resize;
@ -315,6 +319,11 @@ void view_update_title(struct sway_view *view, bool force);
*/
void view_execute_criteria(struct sway_view *view);
/**
* Find any view that has the given mark and return it.
*/
struct sway_view *view_find_mark(char *mark);
/**
* Find any view that has the given mark and remove the mark from the view.
* Returns true if it matched a view.

View File

@ -1,6 +1,7 @@
#ifndef _SWAY_WORKSPACE_H
#define _SWAY_WORKSPACE_H
#include <stdbool.h>
#include "sway/tree/container.h"
struct sway_view;
@ -17,7 +18,8 @@ extern char *prev_workspace_name;
char *workspace_next_name(const char *output_name);
bool workspace_switch(struct sway_container *workspace);
bool workspace_switch(struct sway_container *workspace,
bool no_auto_back_and_forth);
struct sway_container *workspace_by_number(const char* name);

View File

@ -1,3 +1,4 @@
#include <stdbool.h>
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
@ -5,6 +6,26 @@
#include "sway/tree/container.h"
#include "log.h"
static bool parse_layout_string(char *s, enum sway_container_layout *ptr) {
if (strcasecmp(s, "splith") == 0) {
*ptr = L_HORIZ;
} else if (strcasecmp(s, "splitv") == 0) {
*ptr = L_VERT;
} else if (strcasecmp(s, "tabbed") == 0) {
*ptr = L_TABBED;
} else if (strcasecmp(s, "stacking") == 0) {
*ptr = L_STACKED;
} else {
return false;
}
return true;
}
static const char* expected_syntax =
"Expected 'layout default|tabbed|stacking|splitv|splith' or "
"'layout toggle [split|all]' or "
"'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'";
struct cmd_results *cmd_layout(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
@ -21,35 +42,69 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
parent = parent->parent;
}
if (strcasecmp(argv[0], "default") == 0) {
parent->layout = parent->prev_layout;
if (parent->layout == L_NONE) {
parent->layout = container_get_default_layout(parent);
}
} else {
if (parent->layout != L_TABBED && parent->layout != L_STACKED) {
parent->prev_layout = parent->layout;
}
if (strcasecmp(argv[0], "splith") == 0) {
parent->layout = L_HORIZ;
} else if (strcasecmp(argv[0], "splitv") == 0) {
parent->layout = L_VERT;
} else if (strcasecmp(argv[0], "tabbed") == 0) {
parent->layout = L_TABBED;
} else if (strcasecmp(argv[0], "stacking") == 0) {
parent->layout = L_STACKED;
} else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
if (parent->layout == L_HORIZ) {
parent->layout = L_VERT;
enum sway_container_layout prev = parent->layout;
bool assigned_directly = parse_layout_string(argv[0], &parent->layout);
if (!assigned_directly) {
if (strcasecmp(argv[0], "default") == 0) {
parent->layout = parent->prev_split_layout;
} else if (strcasecmp(argv[0], "toggle") == 0) {
if (argc == 1) {
parent->layout =
parent->layout == L_STACKED ? L_TABBED :
parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED;
} else if (argc == 2) {
if (strcasecmp(argv[1], "all") == 0) {
parent->layout =
parent->layout == L_HORIZ ? L_VERT :
parent->layout == L_VERT ? L_STACKED :
parent->layout == L_STACKED ? L_TABBED : L_HORIZ;
} else if (strcasecmp(argv[1], "split") == 0) {
parent->layout =
parent->layout == L_HORIZ ? L_VERT :
parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
} else {
return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
}
} else {
parent->layout = L_HORIZ;
enum sway_container_layout parsed_layout;
int curr = 1;
for (; curr < argc; curr++) {
bool valid = parse_layout_string(argv[curr], &parsed_layout);
if ((valid && parsed_layout == parent->layout) ||
(strcmp(argv[curr], "split") == 0 &&
(parent->layout == L_VERT || parent->layout == L_HORIZ))) {
break;
}
}
for (int i = curr + 1; i != curr; ++i) {
// cycle round to find next valid layout
if (i >= argc) {
i = 1;
}
if (parse_layout_string(argv[i], &parent->layout)) {
break;
} else if (strcmp(argv[i], "split") == 0) {
parent->layout =
parent->layout == L_HORIZ ? L_VERT :
parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
break;
} // invalid layout strings are silently ignored
}
}
} else {
return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
}
}
container_notify_subtree_changed(parent);
arrange_windows(parent);
if (parent->layout == L_NONE) {
parent->layout = container_get_default_layout(parent);
}
if (prev != parent->layout) {
if (prev != L_TABBED && prev != L_STACKED) {
parent->prev_split_layout = prev;
}
container_notify_subtree_changed(parent);
arrange_windows(parent);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

View File

@ -1,4 +1,5 @@
#define _XOPEN_SOURCE 500
#include <stdbool.h>
#include <string.h>
#include <strings.h>
#include <wlr/types/wlr_cursor.h>
@ -18,11 +19,11 @@
#include "list.h"
#include "log.h"
static const char* expected_syntax =
static const char *expected_syntax =
"Expected 'move <left|right|up|down> <[px] px>' or "
"'move <container|window> to workspace <name>' or "
"'move <container|window|workspace> to output <name|direction>' or "
"'move position mouse'";
"'move [--no-auto-back-and-forth] <container|window> [to] workspace <name>' or "
"'move [--no-auto-back-and-forth] <container|window|workspace> [to] output <name|direction>' or "
"'move <container|window> [to] mark <mark>'";
static struct sway_container *output_in_direction(const char *direction,
struct wlr_output *reference, int ref_lx, int ref_ly) {
@ -54,104 +55,140 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "move container/window",
EXPECTED_AT_LEAST, 4))) {
EXPECTED_AT_LEAST, 3))) {
return error;
} else if (strcasecmp(argv[1], "to") == 0
&& strcasecmp(argv[2], "workspace") == 0) {
// move container to workspace x
if (current->type == C_WORKSPACE) {
if (current->children->length == 0) {
return cmd_results_new(CMD_FAILURE, "move",
"Can't move an empty workspace");
}
current = container_wrap_children(current);
} else if (current->type != C_CONTAINER && current->type != C_VIEW) {
return cmd_results_new(CMD_FAILURE, "move",
"Can only move containers and views.");
}
struct sway_container *ws;
char *ws_name = NULL;
if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
// move "container to workspace number x"
ws_name = strdup(argv[4]);
ws = workspace_by_number(ws_name);
} else {
ws_name = join_args(argv + 3, argc - 3);
ws = workspace_by_name(ws_name);
}
}
if (config->auto_back_and_forth && prev_workspace_name) {
// auto back and forth move
struct sway_container *curr_ws = container_parent(current, C_WORKSPACE);
if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) {
// if target workspace is the current one
free(ws_name);
ws_name = strdup(prev_workspace_name);
if (current->type == C_WORKSPACE) {
if (current->children->length == 0) {
return cmd_results_new(CMD_FAILURE, "move",
"Can't move an empty workspace");
}
current = container_wrap_children(current);
} else if (current->type != C_CONTAINER && current->type != C_VIEW) {
return cmd_results_new(CMD_FAILURE, "move",
"Can only move containers and views.");
}
bool no_auto_back_and_forth = false;
while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
no_auto_back_and_forth = true;
if (--argc < 3) {
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
}
++argv;
}
while (strcasecmp(argv[1], "--no-auto-back-and-forth") == 0) {
no_auto_back_and_forth = true;
if (--argc < 3) {
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
}
argv++;
}
while (strcasecmp(argv[1], "to") == 0) {
if (--argc < 3) {
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
}
argv++;
}
struct sway_container *old_parent = current->parent;
struct sway_container *old_ws = container_parent(current, C_WORKSPACE);
struct sway_container *destination = NULL;
// determine destination
if (strcasecmp(argv[1], "workspace") == 0) {
// move container to workspace x
struct sway_container *ws;
if (strcasecmp(argv[2], "next") == 0 ||
strcasecmp(argv[2], "prev") == 0 ||
strcasecmp(argv[2], "next_on_output") == 0 ||
strcasecmp(argv[2], "prev_on_output") == 0 ||
strcasecmp(argv[2], "back_and_forth") == 0 ||
strcasecmp(argv[2], "current") == 0) {
ws = workspace_by_name(argv[2]);
} else if (strcasecmp(argv[2], "back_and_forth") == 0) {
if (!(ws = workspace_by_name(argv[2]))) {
if (prev_workspace_name) {
ws = workspace_create(NULL, prev_workspace_name);
} else {
return cmd_results_new(CMD_FAILURE, "move",
"No workspace was previously active.");
}
}
} else {
char *ws_name = NULL;
if (strcasecmp(argv[2], "number") == 0) {
// move "container to workspace number x"
if (argc < 4) {
return cmd_results_new(CMD_INVALID, "move",
expected_syntax);
}
ws_name = strdup(argv[3]);
ws = workspace_by_number(ws_name);
} else {
ws_name = join_args(argv + 2, argc - 2);
ws = workspace_by_name(ws_name);
}
}
if (!ws) {
ws = workspace_create(NULL, ws_name);
}
free(ws_name);
struct sway_container *old_parent = current->parent;
struct sway_container *old_ws = container_parent(current, C_WORKSPACE);
struct sway_container *destination = seat_get_focus_inactive(
config->handler_context.seat, ws);
container_move_to(current, destination);
struct sway_container *focus = seat_get_focus_inactive(
config->handler_context.seat, old_parent);
seat_set_focus_warp(config->handler_context.seat, focus, true, false);
container_reap_empty(old_parent);
container_reap_empty(destination->parent);
if (!no_auto_back_and_forth && config->auto_back_and_forth &&
prev_workspace_name) {
// auto back and forth move
if (old_ws->name && strcmp(old_ws->name, ws_name) == 0) {
// if target workspace is the current one
free(ws_name);
ws_name = strdup(prev_workspace_name);
ws = workspace_by_name(ws_name);
}
}
// TODO: Ideally we would arrange the surviving parent after reaping,
// but container_reap_empty does not return it, so we arrange the
// workspace instead.
arrange_windows(old_ws);
arrange_windows(destination->parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} else if (strcasecmp(argv[1], "to") == 0
&& strcasecmp(argv[2], "output") == 0) {
if (current->type == C_WORKSPACE) {
// TODO: Wrap children in a container and move that
return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
} else if (current->type != C_CONTAINER
&& current->type != C_VIEW) {
return cmd_results_new(CMD_FAILURE, "move",
"Can only move containers and views.");
if (!ws) {
ws = workspace_create(NULL, ws_name);
}
free(ws_name);
}
destination = seat_get_focus_inactive(config->handler_context.seat, ws);
} else if (strcasecmp(argv[1], "output") == 0) {
struct sway_container *source = container_parent(current, C_OUTPUT);
struct sway_container *destination = output_in_direction(argv[3],
struct sway_container *dest_output = output_in_direction(argv[2],
source->sway_output->wlr_output, current->x, current->y);
if (!destination) {
if (!dest_output) {
return cmd_results_new(CMD_FAILURE, "move workspace",
"Can't find output with name/direction '%s'", argv[3]);
"Can't find output with name/direction '%s'", argv[2]);
}
struct sway_container *focus = seat_get_focus_inactive(
config->handler_context.seat, destination);
if (!focus) {
destination = seat_get_focus_inactive(
config->handler_context.seat, dest_output);
if (!destination) {
// We've never been to this output before
focus = destination->children->items[0];
destination = dest_output->children->items[0];
}
struct sway_container *old_parent = current->parent;
struct sway_container *old_ws = container_parent(current, C_WORKSPACE);
container_move_to(current, focus);
seat_set_focus_warp(config->handler_context.seat, old_parent, true, false);
container_reap_empty(old_parent);
container_reap_empty(focus->parent);
// TODO: Ideally we would arrange the surviving parent after reaping,
// but container_reap_empty does not return it, so we arrange the
// workspace instead.
arrange_windows(old_ws);
arrange_windows(focus->parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} else if (strcasecmp(argv[1], "mark") == 0) {
struct sway_view *dest_view = view_find_mark(argv[2]);
if (dest_view == NULL) {
return cmd_results_new(CMD_FAILURE, "move",
"Mark '%s' not found", argv[2]);
}
destination = dest_view->swayc;
} else {
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
}
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
// move container, arrange windows and return focus
container_move_to(current, destination);
struct sway_container *focus =
seat_get_focus_inactive(config->handler_context.seat, old_parent);
seat_set_focus_warp(config->handler_context.seat, focus, true, false);
container_reap_empty(old_parent);
container_reap_empty(destination->parent);
// TODO: Ideally we would arrange the surviving parent after reaping,
// but container_reap_empty does not return it, so we arrange the
// workspace instead.
arrange_windows(old_ws);
arrange_windows(destination->parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
static void workspace_move_to_output(struct sway_container *workspace,
@ -197,20 +234,29 @@ static void workspace_move_to_output(struct sway_container *workspace,
static struct cmd_results *cmd_move_workspace(struct sway_container *current,
int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) {
return error;
} else if (strcasecmp(argv[1], "to") != 0
|| strcasecmp(argv[2], "output") != 0) {
}
while (strcasecmp(argv[1], "to") == 0) {
if (--argc < 3) {
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
}
++argv;
}
if (strcasecmp(argv[1], "output") != 0) {
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
}
struct sway_container *source = container_parent(current, C_OUTPUT);
int center_x = current->width / 2 + current->x,
center_y = current->height / 2 + current->y;
struct sway_container *destination = output_in_direction(argv[3],
struct sway_container *destination = output_in_direction(argv[2],
source->sway_output->wlr_output, center_x, center_y);
if (!destination) {
return cmd_results_new(CMD_FAILURE, "move workspace",
"Can't find output with name/direction '%s'", argv[3]);
"Can't find output with name/direction '%s'", argv[2]);
}
if (current->type != C_WORKSPACE) {
current = container_parent(current, C_WORKSPACE);
@ -284,9 +330,9 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
static const char* expected_position_syntax =
"Expected 'move [absolute] position <x> <y>' or "
"'move [absolute] position mouse'";
static const char *expected_position_syntax =
"Expected 'move [absolute] position <x> [px] <y> [px]' or "
"'move [absolute] position center|mouse'";
static struct cmd_results *move_to_position(struct sway_container *container,
int argc, char **argv) {
@ -321,10 +367,18 @@ static struct cmd_results *move_to_position(struct sway_container *container,
double ly = seat->cursor->cursor->y - container->height / 2;
container_floating_move_to(container, lx, ly);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} else if (strcmp(argv[0], "center") == 0) {
struct sway_container *ws = container_parent(container, C_WORKSPACE);
double lx = ws->x + (ws->width - container->width) / 2;
double ly = ws->y + (ws->height - container->height) / 2;
container_floating_move_to(container, lx, ly);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
if (argc != 2) {
if (argc < 2) {
return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
}
double lx, ly;
char *inv;
lx = (double)strtol(argv[0], &inv, 10);
@ -332,11 +386,22 @@ static struct cmd_results *move_to_position(struct sway_container *container,
return cmd_results_new(CMD_FAILURE, "move",
"Invalid position specified");
}
if (strcmp(argv[1], "px") == 0) {
--argc;
++argv;
}
if (argc > 3) {
return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
}
ly = (double)strtol(argv[1], &inv, 10);
if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
if ((*inv != '\0' && strcasecmp(inv, "px") != 0) ||
(argc == 3 && strcmp(argv[2], "px") != 0)) {
return cmd_results_new(CMD_FAILURE, "move",
"Invalid position specified");
}
container_floating_move_to(container, lx, ly);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
@ -384,8 +449,11 @@ struct cmd_results *cmd_move(int argc, char **argv) {
return move_in_direction(current, MOVE_UP, argc, argv);
} else if (strcasecmp(argv[0], "down") == 0) {
return move_in_direction(current, MOVE_DOWN, argc, argv);
} else if (strcasecmp(argv[0], "container") == 0
|| strcasecmp(argv[0], "window") == 0) {
} else if ((strcasecmp(argv[0], "container") == 0
|| strcasecmp(argv[0], "window") == 0) ||
(strcasecmp(argv[0], "--no-auto-back-and-forth") &&
(strcasecmp(argv[0], "container") == 0
|| strcasecmp(argv[0], "window") == 0))) {
return cmd_move_container(current, argc, argv);
} else if (strcasecmp(argv[0], "workspace") == 0) {
return cmd_move_workspace(current, argc, argv);

View File

@ -61,6 +61,16 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
}
char *new_name = join_args(argv + argn, argc - argn);
if (strcasecmp(new_name, "next") == 0 ||
strcasecmp(new_name, "prev") == 0 ||
strcasecmp(new_name, "next_on_output") == 0 ||
strcasecmp(new_name, "prev_on_output") == 0 ||
strcasecmp(new_name, "back_and_forth") == 0 ||
strcasecmp(new_name, "current") == 0) {
free(new_name);
return cmd_results_new(CMD_INVALID, "rename",
"Cannot use special workspace name '%s'", argv[argn]);
}
struct sway_container *tmp_workspace = workspace_by_name(new_name);
if (tmp_workspace) {
free(new_name);

View File

@ -17,17 +17,6 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
int output_location = -1;
struct sway_container *current_container = config->handler_context.current_container;
struct sway_container *old_workspace = NULL, *old_output = NULL;
if (current_container) {
if (current_container->type == C_WORKSPACE) {
old_workspace = current_container;
} else {
old_workspace = container_parent(current_container, C_WORKSPACE);
}
old_output = container_parent(current_container, C_OUTPUT);
}
for (int i = 0; i < argc; ++i) {
if (strcasecmp(argv[i], "output") == 0) {
output_location = i;
@ -57,29 +46,36 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
if (config->reading || !config->active) {
return cmd_results_new(CMD_DEFER, "workspace", NULL);
}
bool no_auto_back_and_forth = false;
while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
no_auto_back_and_forth = true;
if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) {
return error;
}
++argv;
}
struct sway_container *ws = NULL;
if (strcasecmp(argv[0], "number") == 0) {
if (argc < 2) {
cmd_results_new(CMD_INVALID, "workspace",
"Expected workspace number");
}
if (!(ws = workspace_by_number(argv[1]))) {
char *name = join_args(argv + 1, argc - 1);
ws = workspace_create(NULL, name);
free(name);
}
} else if (strcasecmp(argv[0], "next") == 0) {
ws = workspace_next(old_workspace);
} else if (strcasecmp(argv[0], "prev") == 0) {
ws = workspace_prev(old_workspace);
} else if (strcasecmp(argv[0], "next_on_output") == 0) {
ws = workspace_output_next(old_output);
} else if (strcasecmp(argv[0], "prev_on_output") == 0) {
ws = workspace_output_prev(old_output);
} else if (strcasecmp(argv[0], "next") == 0 ||
strcasecmp(argv[0], "prev") == 0 ||
strcasecmp(argv[0], "next_on_output") == 0 ||
strcasecmp(argv[0], "prev_on_output") == 0 ||
strcasecmp(argv[0], "current") == 0) {
ws = workspace_by_name(argv[0]);
} else if (strcasecmp(argv[0], "back_and_forth") == 0) {
// if auto_back_and_forth is enabled, workspace_switch will swap
// the workspaces. If we created prev_workspace here, workspace_switch
// would put us back on original workspace.
if (config->auto_back_and_forth) {
ws = old_workspace;
} else if (prev_workspace_name
&& !(ws = workspace_by_name(prev_workspace_name))) {
if (!(ws = workspace_by_name(argv[0])) && prev_workspace_name) {
ws = workspace_create(NULL, prev_workspace_name);
}
} else {
@ -89,7 +85,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
}
free(name);
}
workspace_switch(ws);
workspace_switch(ws, no_auto_back_and_forth);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

View File

@ -361,8 +361,17 @@ static char *get_focused_prop(enum criteria_token token) {
}
}
break;
case T_CON_ID: // These do not support __focused__
case T_CON_MARK:
case T_CON_ID:
if (view->swayc == NULL) {
return NULL;
}
size_t id = view->swayc->id;
size_t id_size = snprintf(NULL, 0, "%zu", id) + 1;
char *id_str = malloc(id_size);
snprintf(id_str, id_size, "%zu", id);
value = id_str;
break;
case T_CON_MARK: // These do not support __focused__
case T_FLOATING:
#ifdef HAVE_XWAYLAND
case T_ID:
@ -425,7 +434,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
case T_CON_ID:
criteria->con_id = strtoul(effective_value, &endptr, 10);
if (*endptr != 0) {
error = strdup("The value for 'con_id' should be numeric");
error = strdup("The value for 'con_id' should be '__focused__' or numeric");
}
break;
case T_CON_MARK:
@ -452,13 +461,18 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
criteria->tiling = true;
break;
case T_URGENT:
if (strcmp(effective_value, "latest") == 0) {
if (strcmp(effective_value, "latest") == 0 ||
strcmp(effective_value, "newest") == 0 ||
strcmp(effective_value, "last") == 0 ||
strcmp(effective_value, "recent") == 0) {
criteria->urgent = 'l';
} else if (strcmp(effective_value, "oldest") == 0) {
} else if (strcmp(effective_value, "oldest") == 0 ||
strcmp(effective_value, "first") == 0) {
criteria->urgent = 'o';
} else {
error =
strdup("The value for 'urgent' must be 'latest' or 'oldest'");
strdup("The value for 'urgent' must be 'first', 'last', "
"'latest', 'newest', 'oldest' or 'recent'");
}
break;
case T_WORKSPACE:

71
sway/decoration.c Normal file
View File

@ -0,0 +1,71 @@
#include <stdlib.h>
#include "sway/decoration.h"
#include "sway/server.h"
#include "sway/tree/view.h"
#include "log.h"
static void server_decoration_handle_destroy(struct wl_listener *listener,
void *data) {
struct sway_server_decoration *deco =
wl_container_of(listener, deco, destroy);
wl_list_remove(&deco->destroy.link);
wl_list_remove(&deco->mode.link);
wl_list_remove(&deco->link);
free(deco);
}
static void server_decoration_handle_mode(struct wl_listener *listener,
void *data) {
struct sway_server_decoration *deco =
wl_container_of(listener, deco, mode);
struct sway_view *view =
view_from_wlr_surface(deco->wlr_server_decoration->surface);
if (view == NULL || view->surface != deco->wlr_server_decoration->surface) {
return;
}
switch (view->type) {
case SWAY_VIEW_XDG_SHELL_V6:;
struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
(struct sway_xdg_shell_v6_view *)view;
xdg_shell_v6_view->deco_mode = deco->wlr_server_decoration->mode;
break;
case SWAY_VIEW_XDG_SHELL:;
struct sway_xdg_shell_view *xdg_shell_view =
(struct sway_xdg_shell_view *)view;
xdg_shell_view->deco_mode = deco->wlr_server_decoration->mode;
break;
default:
break;
}
}
void handle_server_decoration(struct wl_listener *listener, void *data) {
struct wlr_server_decoration *wlr_deco = data;
struct sway_server_decoration *deco = calloc(1, sizeof(*deco));
if (deco == NULL) {
return;
}
deco->wlr_server_decoration = wlr_deco;
wl_signal_add(&wlr_deco->events.destroy, &deco->destroy);
deco->destroy.notify = server_decoration_handle_destroy;
wl_signal_add(&wlr_deco->events.mode, &deco->mode);
deco->mode.notify = server_decoration_handle_mode;
wl_list_insert(&server.decorations, &deco->link);
}
struct sway_server_decoration *decoration_from_surface(
struct wlr_surface *surface) {
struct sway_server_decoration *deco;
wl_list_for_each(deco, &server.decorations, link) {
if (deco->wlr_server_decoration->surface == surface) {
return deco;
}
}
return NULL;
}

View File

@ -6,6 +6,7 @@
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/edges.h>
#include "log.h"
#include "sway/decoration.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
#include "sway/server.h"
@ -170,6 +171,15 @@ static bool wants_floating(struct sway_view *view) {
|| toplevel->parent;
}
static bool has_client_side_decorations(struct sway_view *view) {
struct sway_xdg_shell_view *xdg_shell_view =
xdg_shell_view_from_view(view);
if (xdg_shell_view == NULL) {
return true;
}
return xdg_shell_view->deco_mode != WLR_SERVER_DECORATION_MANAGER_MODE_SERVER;
}
static void for_each_surface(struct sway_view *view,
wlr_surface_iterator_func_t iterator, void *user_data) {
if (xdg_shell_view_from_view(view) == NULL) {
@ -226,6 +236,7 @@ static const struct sway_view_impl view_impl = {
.set_tiled = set_tiled,
.set_fullscreen = set_fullscreen,
.wants_floating = wants_floating,
.has_client_side_decorations = has_client_side_decorations,
.for_each_surface = for_each_surface,
.for_each_popup = for_each_popup,
.close = _close,
@ -357,6 +368,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
view->natural_height = view->wlr_xdg_surface->surface->current.height;
}
struct sway_server_decoration *deco =
decoration_from_surface(xdg_surface->surface);
if (deco != NULL) {
xdg_shell_view->deco_mode = deco->wlr_server_decoration->mode;
} else {
xdg_shell_view->deco_mode = WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
}
view_map(view, view->wlr_xdg_surface->surface);
if (xdg_surface->toplevel->client_pending.fullscreen) {

View File

@ -4,14 +4,15 @@
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include "log.h"
#include "sway/decoration.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
#include "sway/server.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/layout.h"
#include "sway/tree/view.h"
#include "sway/input/seat.h"
#include "sway/input/input-manager.h"
#include "log.h"
static const struct sway_view_child_impl popup_impl;
@ -166,6 +167,15 @@ static bool wants_floating(struct sway_view *view) {
|| toplevel->parent;
}
static bool has_client_side_decorations(struct sway_view *view) {
struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
xdg_shell_v6_view_from_view(view);
if (xdg_shell_v6_view == NULL) {
return true;
}
return xdg_shell_v6_view->deco_mode != WLR_SERVER_DECORATION_MANAGER_MODE_SERVER;
}
static void for_each_surface(struct sway_view *view,
wlr_surface_iterator_func_t iterator, void *user_data) {
if (xdg_shell_v6_view_from_view(view) == NULL) {
@ -223,6 +233,7 @@ static const struct sway_view_impl view_impl = {
.set_tiled = set_tiled,
.set_fullscreen = set_fullscreen,
.wants_floating = wants_floating,
.has_client_side_decorations = has_client_side_decorations,
.for_each_surface = for_each_surface,
.for_each_popup = for_each_popup,
.close = _close,
@ -353,6 +364,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
view->natural_height = view->wlr_xdg_surface_v6->surface->current.height;
}
struct sway_server_decoration *deco =
decoration_from_surface(xdg_surface->surface);
if (deco != NULL) {
xdg_shell_v6_view->deco_mode = deco->wlr_server_decoration->mode;
} else {
xdg_shell_v6_view->deco_mode = WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
}
view_map(view, view->wlr_xdg_surface_v6->surface);
if (xdg_surface->toplevel->client_pending.fullscreen) {

View File

@ -1,13 +1,14 @@
sway_sources = files(
'main.c',
'server.c',
'commands.c',
'config.c',
'criteria.c',
'debug-tree.c',
'decoration.c',
'ipc-json.c',
'ipc-server.c',
'main.c',
'security.c',
'server.c',
'swaynag.c',
'desktop/desktop.c',

View File

@ -19,7 +19,6 @@
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_output.h>
#include <wlr/util/log.h>
// TODO WLR: make Xwayland optional
#include "list.h"
#include "sway/config.h"
#include "sway/desktop/idle_inhibit_v1.h"
@ -85,7 +84,6 @@ bool server_init(struct sway_server *server) {
&server->xdg_shell_surface);
server->xdg_shell_surface.notify = handle_xdg_shell_surface;
// TODO make xwayland optional
#ifdef HAVE_XWAYLAND
server->xwayland.wlr_xwayland =
wlr_xwayland_create(server->wl_display, server->compositor, true);
@ -109,11 +107,15 @@ bool server_init(struct sway_server *server) {
}
#endif
// TODO: Integration with sway borders
struct wlr_server_decoration_manager *deco_manager =
server->server_decoration_manager =
wlr_server_decoration_manager_create(server->wl_display);
wlr_server_decoration_manager_set_default_mode(
deco_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
server->server_decoration_manager,
WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
wl_signal_add(&server->server_decoration_manager->events.new_decoration,
&server->server_decoration);
server->server_decoration.notify = handle_server_decoration;
wl_list_init(&server->decorations);
wlr_linux_dmabuf_v1_create(server->wl_display, renderer);
wlr_export_dmabuf_manager_v1_create(server->wl_display);

View File

@ -84,6 +84,9 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
*floating* enable|disable|toggle
Make focused view floating, non-floating, or the opposite of what it is now.
<criteria> *focus*
Moves focus to the container that matches the specified criteria.
*focus* up|right|down|left
Moves focus to the next container in the specified direction.
@ -111,33 +114,53 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
*fullscreen*
Toggles fullscreen for the focused view.
*layout* splith|splitv|stacking|tabbed
*layout* default|splith|splitv|stacking|tabbed
Sets the layout mode of the focused container.
*layout* toggle split
Switches the focused container between the splitv and splith layouts.
*layout* toggle [split|all]
Cycles the layout mode of the focused container though a preset list of
layouts. If no argument is given, then it cycles through stacking, tabbed
and the last split layout. If "split" is given, then it cycles through
splith and splitv. If "all" is given, then it cycles through every layout.
*move* left|right|up|down [<px>]
*layout* toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...
Cycles the layout mode of the focused container through a list of layouts.
*move* left|right|up|down [<px> px]
Moves the focused container in the direction specified. If the container,
the optional _px_ argument specifies how many pixels to move the container.
If unspecified, the default is 10 pixels. Pixels are ignored when moving
tiled containers.
*move* container|window to workspace <name>
Moves the focused container to the specified workspace.
*move* [absolute] position <pos_x> [px] <pos_y> [px]
Moves the focused container to the specified position.
*move* container|window to workspace prev|next
Moves the focused container to the previous or next workspace on this
output, or if no workspaces remain, the previous or next output.
*move* [absolute] position center|mouse
Moves the focused container to be centered on the workspace or mouse.
*move* container|window to workspace prev\_on\_output|next\_on\_output
*move* container|window [to] mark <mark>
Moves the focused container to the specified mark.
*move* [--no-auto-back-and-forth] container|window [to] workspace [number] <name>
Moves the focused container to the specified workspace. The string "number"
is optional and is used to match a workspace with the same number, even if
it has a different name.
*move* container|window [to] workspace prev|next|current
Moves the focused container to the previous, next or current workspace on
this output, or if no workspaces remain, the previous or next output.
*move* container|window [to] workspace prev\_on\_output|next\_on\_output
Moves the focused container to the previous or next workspace on this
output, wrapping around if already at the first or last workspace.
*move* container|window|workspace to output <name>
*move* container|window [to] workspace back_and_forth
Moves the focused container to previously focused workspace.
*move* container|window|workspace [to] output <name>
Moves the focused container or workspace to the specified output.
*move* container|window|workspace to output up|right|down|left
*move* container|window|workspace [to] output up|right|down|left
Moves the focused container or workspace to next output in the specified
direction.
@ -511,7 +534,7 @@ config after the others, or it will be matched instead of the others.
state. Using _allow_ or _deny_ controls the window's ability to set itself
as urgent. By default, windows are allowed to set their own urgency.
*workspace* [number] <name>
*workspace* [--no-auto-back-and-forth] [number] <name>
Switches to the specified workspace. The string "number" is optional and is
used to sort workspaces.
@ -522,6 +545,9 @@ config after the others, or it will be matched instead of the others.
*workspace* prev\_on\_output|next\_on\_output
Switches to the next workspace on the current output.
*workspace* back_and_forth
Switches to the previously focused workspace.
*workspace* <name> output <output>
Specifies that workspace _name_ should be shown on the specified _output_.
@ -582,7 +608,9 @@ The following attributes may be matched with:
the currently focused window.
*con\_id*
Compare against the internal container ID, which you can find via IPC.
Compare against the internal container ID, which you can find via IPC. If
value is \_\_focused\_\_, then the id must be the same as that of the
currently focused window.
*con\_mark*
Compare against the window marks. Can be a regular expression.
@ -612,7 +640,8 @@ The following attributes may be matched with:
currently focused window.
*urgent*
Compares the urgent state of the window. Can be "latest" or "oldest".
Compares the urgent state of the window. Can be "first", "last", "latest",
"newest", "oldest" or "recent".
*window\_role*
Compare against the window role (WM\_WINDOW\_ROLE). Can be a regular

View File

@ -843,7 +843,7 @@ struct sway_container *container_split(struct sway_container *child,
}
if (child->type == C_WORKSPACE && child->children->length == 0) {
// Special case: this just behaves like splitt
child->prev_layout = child->layout;
child->prev_split_layout = child->layout;
child->layout = layout;
return child;
}
@ -854,7 +854,7 @@ struct sway_container *container_split(struct sway_container *child,
remove_gaps(child);
cont->prev_layout = L_NONE;
cont->prev_split_layout = L_NONE;
cont->width = child->width;
cont->height = child->height;
cont->x = child->x;

View File

@ -1,5 +1,6 @@
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <strings.h>
#include <wayland-server.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_buffer.h>
@ -456,7 +457,13 @@ static struct sway_container *select_workspace(struct sway_view *view) {
if (criteria->type == CT_ASSIGN_WORKSPACE) {
ws = workspace_by_name(criteria->target);
if (!ws) {
ws = workspace_create(NULL, criteria->target);
if (strcasecmp(criteria->target, "back_and_forth") == 0) {
if (prev_workspace_name) {
ws = workspace_create(NULL, prev_workspace_name);
}
} else {
ws = workspace_create(NULL, criteria->target);
}
}
break;
} else {
@ -891,6 +898,15 @@ static bool find_by_mark_iterator(struct sway_container *con,
return con->type == C_VIEW && view_has_mark(con->sway_view, mark);
}
struct sway_view *view_find_mark(char *mark) {
struct sway_container *container = container_find(&root_container,
find_by_mark_iterator, mark);
if (!container) {
return NULL;
}
return container->sway_view;
}
bool view_find_and_unmark(char *mark) {
struct sway_container *container = container_find(&root_container,
find_by_mark_iterator, mark);

View File

@ -59,7 +59,7 @@ struct sway_container *workspace_create(struct sway_container *output,
workspace->width = output->width;
workspace->height = output->height;
workspace->name = !name ? NULL : strdup(name);
workspace->prev_layout = L_NONE;
workspace->prev_split_layout = L_NONE;
workspace->layout = container_get_default_layout(output);
struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace));
@ -250,6 +250,7 @@ struct sway_container *workspace_by_name(const char *name) {
current_workspace = container_parent(focus, C_WORKSPACE);
current_output = container_parent(focus, C_OUTPUT);
}
if (strcmp(name, "prev") == 0) {
return workspace_prev(current_workspace);
} else if (strcmp(name, "prev_on_output") == 0) {
@ -260,6 +261,9 @@ struct sway_container *workspace_by_name(const char *name) {
return workspace_output_next(current_output);
} else if (strcmp(name, "current") == 0) {
return current_workspace;
} else if (strcasecmp(name, "back_and_forth") == 0) {
return prev_workspace_name ? container_find(&root_container,
_workspace_by_name, (void *)prev_workspace_name) : NULL;
} else {
return container_find(&root_container, _workspace_by_name,
(void *)name);
@ -364,7 +368,8 @@ struct sway_container *workspace_prev(struct sway_container *current) {
return workspace_prev_next_impl(current, false);
}
bool workspace_switch(struct sway_container *workspace) {
bool workspace_switch(struct sway_container *workspace,
bool no_auto_back_and_forth) {
if (!workspace) {
return false;
}
@ -379,7 +384,7 @@ bool workspace_switch(struct sway_container *workspace) {
active_ws = container_parent(focus, C_WORKSPACE);
}
if (config->auto_back_and_forth
if (!no_auto_back_and_forth && config->auto_back_and_forth
&& active_ws == workspace
&& prev_workspace_name) {
struct sway_container *new_ws = workspace_by_name(prev_workspace_name);