diff options
author | 2018-08-08 15:26:44 -0400 | |
---|---|---|
committer | 2018-08-08 15:26:44 -0400 | |
commit | 3c26536267e13859eb6088ce0192579f10ac871f (patch) | |
tree | a4676629358071b0f0748dde33a32d82c9a58175 /sway | |
parent | Fix segv when sway is terminating (diff) | |
parent | 私の日本語が上手じゃないですね (diff) | |
download | sway-3c26536267e13859eb6088ce0192579f10ac871f.tar.gz sway-3c26536267e13859eb6088ce0192579f10ac871f.tar.zst sway-3c26536267e13859eb6088ce0192579f10ac871f.zip |
Merge branch 'master' into master
Diffstat (limited to 'sway')
-rw-r--r-- | sway/commands/layout.c | 105 | ||||
-rw-r--r-- | sway/commands/move.c | 323 | ||||
-rw-r--r-- | sway/commands/rename.c | 10 | ||||
-rw-r--r-- | sway/commands/workspace.c | 50 | ||||
-rw-r--r-- | sway/criteria.c | 26 | ||||
-rw-r--r-- | sway/decoration.c | 71 | ||||
-rw-r--r-- | sway/desktop/xdg_shell.c | 19 | ||||
-rw-r--r-- | sway/desktop/xdg_shell_v6.c | 25 | ||||
-rw-r--r-- | sway/input/cursor.c | 4 | ||||
-rw-r--r-- | sway/input/seat.c | 4 | ||||
-rw-r--r-- | sway/meson.build | 5 | ||||
-rw-r--r-- | sway/server.c | 12 | ||||
-rw-r--r-- | sway/sway.5.scd | 59 | ||||
-rw-r--r-- | sway/tree/container.c | 37 | ||||
-rw-r--r-- | sway/tree/layout.c | 76 | ||||
-rw-r--r-- | sway/tree/view.c | 18 | ||||
-rw-r--r-- | sway/tree/workspace.c | 38 |
17 files changed, 633 insertions, 249 deletions
diff --git a/sway/commands/layout.c b/sway/commands/layout.c index c446f1f9..f4e4dda9 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #include <stdbool.h> | ||
1 | #include <string.h> | 2 | #include <string.h> |
2 | #include <strings.h> | 3 | #include <strings.h> |
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
@@ -5,6 +6,26 @@ | |||
5 | #include "sway/tree/container.h" | 6 | #include "sway/tree/container.h" |
6 | #include "log.h" | 7 | #include "log.h" |
7 | 8 | ||
9 | static bool parse_layout_string(char *s, enum sway_container_layout *ptr) { | ||
10 | if (strcasecmp(s, "splith") == 0) { | ||
11 | *ptr = L_HORIZ; | ||
12 | } else if (strcasecmp(s, "splitv") == 0) { | ||
13 | *ptr = L_VERT; | ||
14 | } else if (strcasecmp(s, "tabbed") == 0) { | ||
15 | *ptr = L_TABBED; | ||
16 | } else if (strcasecmp(s, "stacking") == 0) { | ||
17 | *ptr = L_STACKED; | ||
18 | } else { | ||
19 | return false; | ||
20 | } | ||
21 | return true; | ||
22 | } | ||
23 | |||
24 | static const char* expected_syntax = | ||
25 | "Expected 'layout default|tabbed|stacking|splitv|splith' or " | ||
26 | "'layout toggle [split|all]' or " | ||
27 | "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'"; | ||
28 | |||
8 | struct cmd_results *cmd_layout(int argc, char **argv) { | 29 | struct cmd_results *cmd_layout(int argc, char **argv) { |
9 | struct cmd_results *error = NULL; | 30 | struct cmd_results *error = NULL; |
10 | if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { | 31 | if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { |
@@ -21,35 +42,69 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
21 | parent = parent->parent; | 42 | parent = parent->parent; |
22 | } | 43 | } |
23 | 44 | ||
24 | if (strcasecmp(argv[0], "default") == 0) { | 45 | enum sway_container_layout prev = parent->layout; |
25 | parent->layout = parent->prev_layout; | 46 | bool assigned_directly = parse_layout_string(argv[0], &parent->layout); |
26 | if (parent->layout == L_NONE) { | 47 | if (!assigned_directly) { |
27 | parent->layout = container_get_default_layout(parent); | 48 | if (strcasecmp(argv[0], "default") == 0) { |
28 | } | 49 | parent->layout = parent->prev_split_layout; |
29 | } else { | 50 | } else if (strcasecmp(argv[0], "toggle") == 0) { |
30 | if (parent->layout != L_TABBED && parent->layout != L_STACKED) { | 51 | if (argc == 1) { |
31 | parent->prev_layout = parent->layout; | 52 | parent->layout = |
32 | } | 53 | parent->layout == L_STACKED ? L_TABBED : |
33 | 54 | parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED; | |
34 | if (strcasecmp(argv[0], "splith") == 0) { | 55 | } else if (argc == 2) { |
35 | parent->layout = L_HORIZ; | 56 | if (strcasecmp(argv[1], "all") == 0) { |
36 | } else if (strcasecmp(argv[0], "splitv") == 0) { | 57 | parent->layout = |
37 | parent->layout = L_VERT; | 58 | parent->layout == L_HORIZ ? L_VERT : |
38 | } else if (strcasecmp(argv[0], "tabbed") == 0) { | 59 | parent->layout == L_VERT ? L_STACKED : |
39 | parent->layout = L_TABBED; | 60 | parent->layout == L_STACKED ? L_TABBED : L_HORIZ; |
40 | } else if (strcasecmp(argv[0], "stacking") == 0) { | 61 | } else if (strcasecmp(argv[1], "split") == 0) { |
41 | parent->layout = L_STACKED; | 62 | parent->layout = |
42 | } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { | 63 | parent->layout == L_HORIZ ? L_VERT : |
43 | if (parent->layout == L_HORIZ) { | 64 | parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout; |
44 | parent->layout = L_VERT; | 65 | } else { |
66 | return cmd_results_new(CMD_INVALID, "layout", expected_syntax); | ||
67 | } | ||
45 | } else { | 68 | } else { |
46 | parent->layout = L_HORIZ; | 69 | enum sway_container_layout parsed_layout; |
70 | int curr = 1; | ||
71 | for (; curr < argc; curr++) { | ||
72 | bool valid = parse_layout_string(argv[curr], &parsed_layout); | ||
73 | if ((valid && parsed_layout == parent->layout) || | ||
74 | (strcmp(argv[curr], "split") == 0 && | ||
75 | (parent->layout == L_VERT || parent->layout == L_HORIZ))) { | ||
76 | break; | ||
77 | } | ||
78 | } | ||
79 | for (int i = curr + 1; i != curr; ++i) { | ||
80 | // cycle round to find next valid layout | ||
81 | if (i >= argc) { | ||
82 | i = 1; | ||
83 | } | ||
84 | if (parse_layout_string(argv[i], &parent->layout)) { | ||
85 | break; | ||
86 | } else if (strcmp(argv[i], "split") == 0) { | ||
87 | parent->layout = | ||
88 | parent->layout == L_HORIZ ? L_VERT : | ||
89 | parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout; | ||
90 | break; | ||
91 | } // invalid layout strings are silently ignored | ||
92 | } | ||
47 | } | 93 | } |
94 | } else { | ||
95 | return cmd_results_new(CMD_INVALID, "layout", expected_syntax); | ||
48 | } | 96 | } |
49 | } | 97 | } |
50 | 98 | if (parent->layout == L_NONE) { | |
51 | container_notify_subtree_changed(parent); | 99 | parent->layout = container_get_default_layout(parent); |
52 | arrange_windows(parent); | 100 | } |
101 | if (prev != parent->layout) { | ||
102 | if (prev != L_TABBED && prev != L_STACKED) { | ||
103 | parent->prev_split_layout = prev; | ||
104 | } | ||
105 | container_notify_subtree_changed(parent); | ||
106 | arrange_windows(parent); | ||
107 | } | ||
53 | 108 | ||
54 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 109 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
55 | } | 110 | } |
diff --git a/sway/commands/move.c b/sway/commands/move.c index 841da4c4..de6b1b0a 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _XOPEN_SOURCE 500 | 1 | #define _XOPEN_SOURCE 500 |
2 | #include <stdbool.h> | ||
2 | #include <string.h> | 3 | #include <string.h> |
3 | #include <strings.h> | 4 | #include <strings.h> |
4 | #include <wlr/types/wlr_cursor.h> | 5 | #include <wlr/types/wlr_cursor.h> |
@@ -8,6 +9,7 @@ | |||
8 | #include "sway/commands.h" | 9 | #include "sway/commands.h" |
9 | #include "sway/input/cursor.h" | 10 | #include "sway/input/cursor.h" |
10 | #include "sway/input/seat.h" | 11 | #include "sway/input/seat.h" |
12 | #include "sway/ipc-server.h" | ||
11 | #include "sway/output.h" | 13 | #include "sway/output.h" |
12 | #include "sway/tree/arrange.h" | 14 | #include "sway/tree/arrange.h" |
13 | #include "sway/tree/container.h" | 15 | #include "sway/tree/container.h" |
@@ -15,12 +17,13 @@ | |||
15 | #include "sway/tree/workspace.h" | 17 | #include "sway/tree/workspace.h" |
16 | #include "stringop.h" | 18 | #include "stringop.h" |
17 | #include "list.h" | 19 | #include "list.h" |
20 | #include "log.h" | ||
18 | 21 | ||
19 | static const char* expected_syntax = | 22 | static const char *expected_syntax = |
20 | "Expected 'move <left|right|up|down> <[px] px>' or " | 23 | "Expected 'move <left|right|up|down> <[px] px>' or " |
21 | "'move <container|window> to workspace <name>' or " | 24 | "'move [--no-auto-back-and-forth] <container|window> [to] workspace <name>' or " |
22 | "'move <container|window|workspace> to output <name|direction>' or " | 25 | "'move [--no-auto-back-and-forth] <container|window|workspace> [to] output <name|direction>' or " |
23 | "'move position mouse'"; | 26 | "'move <container|window> [to] mark <mark>'"; |
24 | 27 | ||
25 | static struct sway_container *output_in_direction(const char *direction, | 28 | static struct sway_container *output_in_direction(const char *direction, |
26 | struct wlr_output *reference, int ref_lx, int ref_ly) { | 29 | struct wlr_output *reference, int ref_lx, int ref_ly) { |
@@ -52,128 +55,236 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, | |||
52 | int argc, char **argv) { | 55 | int argc, char **argv) { |
53 | struct cmd_results *error = NULL; | 56 | struct cmd_results *error = NULL; |
54 | if ((error = checkarg(argc, "move container/window", | 57 | if ((error = checkarg(argc, "move container/window", |
55 | EXPECTED_AT_LEAST, 4))) { | 58 | EXPECTED_AT_LEAST, 3))) { |
56 | return error; | 59 | return error; |
57 | } else if (strcasecmp(argv[1], "to") == 0 | 60 | } |
58 | && strcasecmp(argv[2], "workspace") == 0) { | 61 | |
59 | // move container to workspace x | 62 | if (current->type == C_WORKSPACE) { |
60 | if (current->type == C_WORKSPACE) { | 63 | if (current->children->length == 0) { |
61 | if (current->children->length == 0) { | ||
62 | return cmd_results_new(CMD_FAILURE, "move", | ||
63 | "Can't move an empty workspace"); | ||
64 | } | ||
65 | current = container_wrap_children(current); | ||
66 | } else if (current->type != C_CONTAINER && current->type != C_VIEW) { | ||
67 | return cmd_results_new(CMD_FAILURE, "move", | 64 | return cmd_results_new(CMD_FAILURE, "move", |
68 | "Can only move containers and views."); | 65 | "Can't move an empty workspace"); |
69 | } | 66 | } |
70 | struct sway_container *ws; | 67 | current = container_wrap_children(current); |
71 | char *ws_name = NULL; | 68 | } else if (current->type != C_CONTAINER && current->type != C_VIEW) { |
72 | if (argc == 5 && strcasecmp(argv[3], "number") == 0) { | 69 | return cmd_results_new(CMD_FAILURE, "move", |
73 | // move "container to workspace number x" | 70 | "Can only move containers and views."); |
74 | ws_name = strdup(argv[4]); | 71 | } |
75 | ws = workspace_by_number(ws_name); | 72 | |
76 | } else { | 73 | bool no_auto_back_and_forth = false; |
77 | ws_name = join_args(argv + 3, argc - 3); | 74 | while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { |
78 | ws = workspace_by_name(ws_name); | 75 | no_auto_back_and_forth = true; |
76 | if (--argc < 3) { | ||
77 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
78 | } | ||
79 | ++argv; | ||
80 | } | ||
81 | while (strcasecmp(argv[1], "--no-auto-back-and-forth") == 0) { | ||
82 | no_auto_back_and_forth = true; | ||
83 | if (--argc < 3) { | ||
84 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
85 | } | ||
86 | argv++; | ||
87 | } | ||
88 | |||
89 | while (strcasecmp(argv[1], "to") == 0) { | ||
90 | if (--argc < 3) { | ||
91 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
79 | } | 92 | } |
93 | argv++; | ||
94 | } | ||
95 | |||
96 | struct sway_container *old_parent = current->parent; | ||
97 | struct sway_container *old_ws = container_parent(current, C_WORKSPACE); | ||
98 | struct sway_container *destination = NULL; | ||
80 | 99 | ||
81 | if (config->auto_back_and_forth && prev_workspace_name) { | 100 | // determine destination |
82 | // auto back and forth move | 101 | if (strcasecmp(argv[1], "workspace") == 0) { |
83 | struct sway_container *curr_ws = container_parent(current, C_WORKSPACE); | 102 | // move container to workspace x |
84 | if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) { | 103 | struct sway_container *ws = NULL; |
85 | // if target workspace is the current one | 104 | char *ws_name = NULL; |
86 | free(ws_name); | 105 | if (strcasecmp(argv[2], "next") == 0 || |
87 | ws_name = strdup(prev_workspace_name); | 106 | strcasecmp(argv[2], "prev") == 0 || |
107 | strcasecmp(argv[2], "next_on_output") == 0 || | ||
108 | strcasecmp(argv[2], "prev_on_output") == 0 || | ||
109 | strcasecmp(argv[2], "current") == 0) { | ||
110 | ws = workspace_by_name(argv[2]); | ||
111 | } else if (strcasecmp(argv[2], "back_and_forth") == 0) { | ||
112 | if (!(ws = workspace_by_name(argv[2]))) { | ||
113 | if (prev_workspace_name) { | ||
114 | ws_name = strdup(prev_workspace_name); | ||
115 | } else { | ||
116 | return cmd_results_new(CMD_FAILURE, "move", | ||
117 | "No workspace was previously active."); | ||
118 | } | ||
119 | } | ||
120 | } else { | ||
121 | if (strcasecmp(argv[2], "number") == 0) { | ||
122 | // move "container to workspace number x" | ||
123 | if (argc < 4) { | ||
124 | return cmd_results_new(CMD_INVALID, "move", | ||
125 | expected_syntax); | ||
126 | } | ||
127 | ws_name = strdup(argv[3]); | ||
128 | ws = workspace_by_number(ws_name); | ||
129 | } else { | ||
130 | ws_name = join_args(argv + 2, argc - 2); | ||
88 | ws = workspace_by_name(ws_name); | 131 | ws = workspace_by_name(ws_name); |
89 | } | 132 | } |
90 | } | ||
91 | 133 | ||
134 | if (!no_auto_back_and_forth && config->auto_back_and_forth && | ||
135 | prev_workspace_name) { | ||
136 | // auto back and forth move | ||
137 | if (old_ws->name && strcmp(old_ws->name, ws_name) == 0) { | ||
138 | // if target workspace is the current one | ||
139 | free(ws_name); | ||
140 | ws_name = strdup(prev_workspace_name); | ||
141 | ws = workspace_by_name(ws_name); | ||
142 | } | ||
143 | } | ||
144 | } | ||
92 | if (!ws) { | 145 | if (!ws) { |
146 | // We have to create the workspace, but if the container is | ||
147 | // sticky and the workspace is going to be created on the same | ||
148 | // output, we'll bail out first. | ||
149 | if (container_is_floating(current) && current->is_sticky) { | ||
150 | struct sway_container *old_output = | ||
151 | container_parent(current, C_OUTPUT); | ||
152 | struct sway_container *new_output = | ||
153 | workspace_get_initial_output(ws_name); | ||
154 | if (old_output == new_output) { | ||
155 | free(ws_name); | ||
156 | return cmd_results_new(CMD_FAILURE, "move", | ||
157 | "Can't move sticky container to another workspace " | ||
158 | "on the same output"); | ||
159 | } | ||
160 | } | ||
93 | ws = workspace_create(NULL, ws_name); | 161 | ws = workspace_create(NULL, ws_name); |
94 | } | 162 | } |
95 | free(ws_name); | 163 | free(ws_name); |
96 | struct sway_container *old_parent = current->parent; | 164 | destination = seat_get_focus_inactive(config->handler_context.seat, ws); |
97 | struct sway_container *old_ws = container_parent(current, C_WORKSPACE); | 165 | } else if (strcasecmp(argv[1], "output") == 0) { |
98 | struct sway_container *destination = seat_get_focus_inactive( | ||
99 | config->handler_context.seat, ws); | ||
100 | container_move_to(current, destination); | ||
101 | struct sway_container *focus = seat_get_focus_inactive( | ||
102 | config->handler_context.seat, old_parent); | ||
103 | seat_set_focus_warp(config->handler_context.seat, focus, true, false); | ||
104 | container_reap_empty(old_parent); | ||
105 | container_reap_empty(destination->parent); | ||
106 | |||
107 | // TODO: Ideally we would arrange the surviving parent after reaping, | ||
108 | // but container_reap_empty does not return it, so we arrange the | ||
109 | // workspace instead. | ||
110 | arrange_windows(old_ws); | ||
111 | arrange_windows(destination->parent); | ||
112 | |||
113 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
114 | } else if (strcasecmp(argv[1], "to") == 0 | ||
115 | && strcasecmp(argv[2], "output") == 0) { | ||
116 | if (current->type == C_WORKSPACE) { | ||
117 | // TODO: Wrap children in a container and move that | ||
118 | return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); | ||
119 | } else if (current->type != C_CONTAINER | ||
120 | && current->type != C_VIEW) { | ||
121 | return cmd_results_new(CMD_FAILURE, "move", | ||
122 | "Can only move containers and views."); | ||
123 | } | ||
124 | struct sway_container *source = container_parent(current, C_OUTPUT); | 166 | struct sway_container *source = container_parent(current, C_OUTPUT); |
125 | struct sway_container *destination = output_in_direction(argv[3], | 167 | struct sway_container *dest_output = output_in_direction(argv[2], |
126 | source->sway_output->wlr_output, current->x, current->y); | 168 | source->sway_output->wlr_output, current->x, current->y); |
127 | if (!destination) { | 169 | if (!dest_output) { |
128 | return cmd_results_new(CMD_FAILURE, "move workspace", | 170 | return cmd_results_new(CMD_FAILURE, "move workspace", |
129 | "Can't find output with name/direction '%s'", argv[3]); | 171 | "Can't find output with name/direction '%s'", argv[2]); |
130 | } | 172 | } |
131 | struct sway_container *focus = seat_get_focus_inactive( | 173 | destination = seat_get_focus_inactive( |
132 | config->handler_context.seat, destination); | 174 | config->handler_context.seat, dest_output); |
133 | if (!focus) { | 175 | if (!destination) { |
134 | // We've never been to this output before | 176 | // We've never been to this output before |
135 | focus = destination->children->items[0]; | 177 | destination = dest_output->children->items[0]; |
178 | } | ||
179 | } else if (strcasecmp(argv[1], "mark") == 0) { | ||
180 | struct sway_view *dest_view = view_find_mark(argv[2]); | ||
181 | if (dest_view == NULL) { | ||
182 | return cmd_results_new(CMD_FAILURE, "move", | ||
183 | "Mark '%s' not found", argv[2]); | ||
136 | } | 184 | } |
137 | struct sway_container *old_parent = current->parent; | 185 | destination = dest_view->swayc; |
138 | struct sway_container *old_ws = container_parent(current, C_WORKSPACE); | 186 | } else { |
139 | container_move_to(current, focus); | 187 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); |
140 | seat_set_focus_warp(config->handler_context.seat, old_parent, true, false); | 188 | } |
141 | container_reap_empty(old_parent); | ||
142 | container_reap_empty(focus->parent); | ||
143 | |||
144 | // TODO: Ideally we would arrange the surviving parent after reaping, | ||
145 | // but container_reap_empty does not return it, so we arrange the | ||
146 | // workspace instead. | ||
147 | arrange_windows(old_ws); | ||
148 | arrange_windows(focus->parent); | ||
149 | 189 | ||
150 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 190 | if (container_is_floating(current) && current->is_sticky) { |
191 | struct sway_container *old_output = container_parent(current, C_OUTPUT); | ||
192 | struct sway_container *new_output = destination->type == C_OUTPUT ? | ||
193 | destination : container_parent(destination, C_OUTPUT); | ||
194 | if (old_output == new_output) { | ||
195 | return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky " | ||
196 | "container to another workspace on the same output"); | ||
197 | } | ||
151 | } | 198 | } |
152 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | 199 | |
200 | // move container, arrange windows and return focus | ||
201 | container_move_to(current, destination); | ||
202 | struct sway_container *focus = | ||
203 | seat_get_focus_inactive(config->handler_context.seat, old_parent); | ||
204 | seat_set_focus_warp(config->handler_context.seat, focus, true, false); | ||
205 | container_reap_empty(old_parent); | ||
206 | container_reap_empty(destination->parent); | ||
207 | |||
208 | // TODO: Ideally we would arrange the surviving parent after reaping, | ||
209 | // but container_reap_empty does not return it, so we arrange the | ||
210 | // workspace instead. | ||
211 | arrange_windows(old_ws); | ||
212 | arrange_windows(destination->parent); | ||
213 | |||
214 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
215 | } | ||
216 | |||
217 | static void workspace_move_to_output(struct sway_container *workspace, | ||
218 | struct sway_container *output) { | ||
219 | if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { | ||
220 | return; | ||
221 | } | ||
222 | if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { | ||
223 | return; | ||
224 | } | ||
225 | if (workspace->parent == output) { | ||
226 | return; | ||
227 | } | ||
228 | struct sway_container *old_output = container_remove_child(workspace); | ||
229 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | ||
230 | struct sway_container *new_output_focus = | ||
231 | seat_get_focus_inactive(seat, output); | ||
232 | |||
233 | container_add_child(output, workspace); | ||
234 | wl_signal_emit(&workspace->events.reparent, old_output); | ||
235 | |||
236 | // If moving the last workspace from the old output, create a new workspace | ||
237 | // on the old output | ||
238 | if (old_output->children->length == 0) { | ||
239 | char *ws_name = workspace_next_name(old_output->name); | ||
240 | struct sway_container *ws = workspace_create(old_output, ws_name); | ||
241 | free(ws_name); | ||
242 | seat_set_focus(seat, ws); | ||
243 | } | ||
244 | |||
245 | // Try to remove an empty workspace from the destination output. | ||
246 | container_reap_empty_recursive(new_output_focus); | ||
247 | |||
248 | container_sort_workspaces(output); | ||
249 | seat_set_focus(seat, output); | ||
250 | workspace_output_raise_priority(workspace, old_output, output); | ||
251 | ipc_event_workspace(NULL, workspace, "move"); | ||
252 | |||
253 | container_notify_subtree_changed(old_output); | ||
254 | container_notify_subtree_changed(output); | ||
153 | } | 255 | } |
154 | 256 | ||
155 | static struct cmd_results *cmd_move_workspace(struct sway_container *current, | 257 | static struct cmd_results *cmd_move_workspace(struct sway_container *current, |
156 | int argc, char **argv) { | 258 | int argc, char **argv) { |
157 | struct cmd_results *error = NULL; | 259 | struct cmd_results *error = NULL; |
158 | if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { | 260 | if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { |
159 | return error; | 261 | return error; |
160 | } else if (strcasecmp(argv[1], "to") != 0 | 262 | } |
161 | || strcasecmp(argv[2], "output") != 0) { | 263 | |
264 | while (strcasecmp(argv[1], "to") == 0) { | ||
265 | if (--argc < 3) { | ||
266 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
267 | } | ||
268 | ++argv; | ||
269 | } | ||
270 | |||
271 | if (strcasecmp(argv[1], "output") != 0) { | ||
162 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | 272 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); |
163 | } | 273 | } |
274 | |||
164 | struct sway_container *source = container_parent(current, C_OUTPUT); | 275 | struct sway_container *source = container_parent(current, C_OUTPUT); |
165 | int center_x = current->width / 2 + current->x, | 276 | int center_x = current->width / 2 + current->x, |
166 | center_y = current->height / 2 + current->y; | 277 | center_y = current->height / 2 + current->y; |
167 | struct sway_container *destination = output_in_direction(argv[3], | 278 | struct sway_container *destination = output_in_direction(argv[2], |
168 | source->sway_output->wlr_output, center_x, center_y); | 279 | source->sway_output->wlr_output, center_x, center_y); |
169 | if (!destination) { | 280 | if (!destination) { |
170 | return cmd_results_new(CMD_FAILURE, "move workspace", | 281 | return cmd_results_new(CMD_FAILURE, "move workspace", |
171 | "Can't find output with name/direction '%s'", argv[3]); | 282 | "Can't find output with name/direction '%s'", argv[2]); |
172 | } | 283 | } |
173 | if (current->type != C_WORKSPACE) { | 284 | if (current->type != C_WORKSPACE) { |
174 | current = container_parent(current, C_WORKSPACE); | 285 | current = container_parent(current, C_WORKSPACE); |
175 | } | 286 | } |
176 | container_move_to(current, destination); | 287 | workspace_move_to_output(current, destination); |
177 | 288 | ||
178 | arrange_windows(source); | 289 | arrange_windows(source); |
179 | arrange_windows(destination); | 290 | arrange_windows(destination); |
@@ -242,9 +353,9 @@ static struct cmd_results *move_in_direction(struct sway_container *container, | |||
242 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 353 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
243 | } | 354 | } |
244 | 355 | ||
245 | static const char* expected_position_syntax = | 356 | static const char *expected_position_syntax = |
246 | "Expected 'move [absolute] position <x> <y>' or " | 357 | "Expected 'move [absolute] position <x> [px] <y> [px]' or " |
247 | "'move [absolute] position mouse'"; | 358 | "'move [absolute] position center|mouse'"; |
248 | 359 | ||
249 | static struct cmd_results *move_to_position(struct sway_container *container, | 360 | static struct cmd_results *move_to_position(struct sway_container *container, |
250 | int argc, char **argv) { | 361 | int argc, char **argv) { |
@@ -279,10 +390,18 @@ static struct cmd_results *move_to_position(struct sway_container *container, | |||
279 | double ly = seat->cursor->cursor->y - container->height / 2; | 390 | double ly = seat->cursor->cursor->y - container->height / 2; |
280 | container_floating_move_to(container, lx, ly); | 391 | container_floating_move_to(container, lx, ly); |
281 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 392 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
393 | } else if (strcmp(argv[0], "center") == 0) { | ||
394 | struct sway_container *ws = container_parent(container, C_WORKSPACE); | ||
395 | double lx = ws->x + (ws->width - container->width) / 2; | ||
396 | double ly = ws->y + (ws->height - container->height) / 2; | ||
397 | container_floating_move_to(container, lx, ly); | ||
398 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
282 | } | 399 | } |
283 | if (argc != 2) { | 400 | |
401 | if (argc < 2) { | ||
284 | return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); | 402 | return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); |
285 | } | 403 | } |
404 | |||
286 | double lx, ly; | 405 | double lx, ly; |
287 | char *inv; | 406 | char *inv; |
288 | lx = (double)strtol(argv[0], &inv, 10); | 407 | lx = (double)strtol(argv[0], &inv, 10); |
@@ -290,11 +409,22 @@ static struct cmd_results *move_to_position(struct sway_container *container, | |||
290 | return cmd_results_new(CMD_FAILURE, "move", | 409 | return cmd_results_new(CMD_FAILURE, "move", |
291 | "Invalid position specified"); | 410 | "Invalid position specified"); |
292 | } | 411 | } |
412 | if (strcmp(argv[1], "px") == 0) { | ||
413 | --argc; | ||
414 | ++argv; | ||
415 | } | ||
416 | |||
417 | if (argc > 3) { | ||
418 | return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); | ||
419 | } | ||
420 | |||
293 | ly = (double)strtol(argv[1], &inv, 10); | 421 | ly = (double)strtol(argv[1], &inv, 10); |
294 | if (*inv != '\0' && strcasecmp(inv, "px") != 0) { | 422 | if ((*inv != '\0' && strcasecmp(inv, "px") != 0) || |
423 | (argc == 3 && strcmp(argv[2], "px") != 0)) { | ||
295 | return cmd_results_new(CMD_FAILURE, "move", | 424 | return cmd_results_new(CMD_FAILURE, "move", |
296 | "Invalid position specified"); | 425 | "Invalid position specified"); |
297 | } | 426 | } |
427 | |||
298 | container_floating_move_to(container, lx, ly); | 428 | container_floating_move_to(container, lx, ly); |
299 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 429 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
300 | } | 430 | } |
@@ -342,8 +472,11 @@ struct cmd_results *cmd_move(int argc, char **argv) { | |||
342 | return move_in_direction(current, MOVE_UP, argc, argv); | 472 | return move_in_direction(current, MOVE_UP, argc, argv); |
343 | } else if (strcasecmp(argv[0], "down") == 0) { | 473 | } else if (strcasecmp(argv[0], "down") == 0) { |
344 | return move_in_direction(current, MOVE_DOWN, argc, argv); | 474 | return move_in_direction(current, MOVE_DOWN, argc, argv); |
345 | } else if (strcasecmp(argv[0], "container") == 0 | 475 | } else if ((strcasecmp(argv[0], "container") == 0 |
346 | || strcasecmp(argv[0], "window") == 0) { | 476 | || strcasecmp(argv[0], "window") == 0) || |
477 | (strcasecmp(argv[0], "--no-auto-back-and-forth") && | ||
478 | (strcasecmp(argv[0], "container") == 0 | ||
479 | || strcasecmp(argv[0], "window") == 0))) { | ||
347 | return cmd_move_container(current, argc, argv); | 480 | return cmd_move_container(current, argc, argv); |
348 | } else if (strcasecmp(argv[0], "workspace") == 0) { | 481 | } else if (strcasecmp(argv[0], "workspace") == 0) { |
349 | return cmd_move_workspace(current, argc, argv); | 482 | return cmd_move_workspace(current, argc, argv); |
diff --git a/sway/commands/rename.c b/sway/commands/rename.c index a380ff9c..c6952bbb 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c | |||
@@ -61,6 +61,16 @@ struct cmd_results *cmd_rename(int argc, char **argv) { | |||
61 | } | 61 | } |
62 | 62 | ||
63 | char *new_name = join_args(argv + argn, argc - argn); | 63 | char *new_name = join_args(argv + argn, argc - argn); |
64 | if (strcasecmp(new_name, "next") == 0 || | ||
65 | strcasecmp(new_name, "prev") == 0 || | ||
66 | strcasecmp(new_name, "next_on_output") == 0 || | ||
67 | strcasecmp(new_name, "prev_on_output") == 0 || | ||
68 | strcasecmp(new_name, "back_and_forth") == 0 || | ||
69 | strcasecmp(new_name, "current") == 0) { | ||
70 | free(new_name); | ||
71 | return cmd_results_new(CMD_INVALID, "rename", | ||
72 | "Cannot use special workspace name '%s'", argv[argn]); | ||
73 | } | ||
64 | struct sway_container *tmp_workspace = workspace_by_name(new_name); | 74 | struct sway_container *tmp_workspace = workspace_by_name(new_name); |
65 | if (tmp_workspace) { | 75 | if (tmp_workspace) { |
66 | free(new_name); | 76 | free(new_name); |
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index e8b37182..f5558bb4 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c | |||
@@ -17,17 +17,6 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
17 | 17 | ||
18 | int output_location = -1; | 18 | int output_location = -1; |
19 | 19 | ||
20 | struct sway_container *current_container = config->handler_context.current_container; | ||
21 | struct sway_container *old_workspace = NULL, *old_output = NULL; | ||
22 | if (current_container) { | ||
23 | if (current_container->type == C_WORKSPACE) { | ||
24 | old_workspace = current_container; | ||
25 | } else { | ||
26 | old_workspace = container_parent(current_container, C_WORKSPACE); | ||
27 | } | ||
28 | old_output = container_parent(current_container, C_OUTPUT); | ||
29 | } | ||
30 | |||
31 | for (int i = 0; i < argc; ++i) { | 20 | for (int i = 0; i < argc; ++i) { |
32 | if (strcasecmp(argv[i], "output") == 0) { | 21 | if (strcasecmp(argv[i], "output") == 0) { |
33 | output_location = i; | 22 | output_location = i; |
@@ -57,29 +46,36 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
57 | if (config->reading || !config->active) { | 46 | if (config->reading || !config->active) { |
58 | return cmd_results_new(CMD_DEFER, "workspace", NULL); | 47 | return cmd_results_new(CMD_DEFER, "workspace", NULL); |
59 | } | 48 | } |
49 | |||
50 | bool no_auto_back_and_forth = false; | ||
51 | while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { | ||
52 | no_auto_back_and_forth = true; | ||
53 | if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { | ||
54 | return error; | ||
55 | } | ||
56 | ++argv; | ||
57 | } | ||
58 | |||
59 | |||
60 | struct sway_container *ws = NULL; | 60 | struct sway_container *ws = NULL; |
61 | if (strcasecmp(argv[0], "number") == 0) { | 61 | if (strcasecmp(argv[0], "number") == 0) { |
62 | if (argc < 2) { | ||
63 | cmd_results_new(CMD_INVALID, "workspace", | ||
64 | "Expected workspace number"); | ||
65 | } | ||
62 | if (!(ws = workspace_by_number(argv[1]))) { | 66 | if (!(ws = workspace_by_number(argv[1]))) { |
63 | char *name = join_args(argv + 1, argc - 1); | 67 | char *name = join_args(argv + 1, argc - 1); |
64 | ws = workspace_create(NULL, name); | 68 | ws = workspace_create(NULL, name); |
65 | free(name); | 69 | free(name); |
66 | } | 70 | } |
67 | } else if (strcasecmp(argv[0], "next") == 0) { | 71 | } else if (strcasecmp(argv[0], "next") == 0 || |
68 | ws = workspace_next(old_workspace); | 72 | strcasecmp(argv[0], "prev") == 0 || |
69 | } else if (strcasecmp(argv[0], "prev") == 0) { | 73 | strcasecmp(argv[0], "next_on_output") == 0 || |
70 | ws = workspace_prev(old_workspace); | 74 | strcasecmp(argv[0], "prev_on_output") == 0 || |
71 | } else if (strcasecmp(argv[0], "next_on_output") == 0) { | 75 | strcasecmp(argv[0], "current") == 0) { |
72 | ws = workspace_output_next(old_output); | 76 | ws = workspace_by_name(argv[0]); |
73 | } else if (strcasecmp(argv[0], "prev_on_output") == 0) { | ||
74 | ws = workspace_output_prev(old_output); | ||
75 | } else if (strcasecmp(argv[0], "back_and_forth") == 0) { | 77 | } else if (strcasecmp(argv[0], "back_and_forth") == 0) { |
76 | // if auto_back_and_forth is enabled, workspace_switch will swap | 78 | if (!(ws = workspace_by_name(argv[0])) && prev_workspace_name) { |
77 | // the workspaces. If we created prev_workspace here, workspace_switch | ||
78 | // would put us back on original workspace. | ||
79 | if (config->auto_back_and_forth) { | ||
80 | ws = old_workspace; | ||
81 | } else if (prev_workspace_name | ||
82 | && !(ws = workspace_by_name(prev_workspace_name))) { | ||
83 | ws = workspace_create(NULL, prev_workspace_name); | 79 | ws = workspace_create(NULL, prev_workspace_name); |
84 | } | 80 | } |
85 | } else { | 81 | } else { |
@@ -89,7 +85,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
89 | } | 85 | } |
90 | free(name); | 86 | free(name); |
91 | } | 87 | } |
92 | workspace_switch(ws); | 88 | workspace_switch(ws, no_auto_back_and_forth); |
93 | } | 89 | } |
94 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 90 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
95 | } | 91 | } |
diff --git a/sway/criteria.c b/sway/criteria.c index 39d300ea..9077aa9b 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -361,8 +361,17 @@ static char *get_focused_prop(enum criteria_token token) { | |||
361 | } | 361 | } |
362 | } | 362 | } |
363 | break; | 363 | break; |
364 | case T_CON_ID: // These do not support __focused__ | 364 | case T_CON_ID: |
365 | case T_CON_MARK: | 365 | if (view->swayc == NULL) { |
366 | return NULL; | ||
367 | } | ||
368 | size_t id = view->swayc->id; | ||
369 | size_t id_size = snprintf(NULL, 0, "%zu", id) + 1; | ||
370 | char *id_str = malloc(id_size); | ||
371 | snprintf(id_str, id_size, "%zu", id); | ||
372 | value = id_str; | ||
373 | break; | ||
374 | case T_CON_MARK: // These do not support __focused__ | ||
366 | case T_FLOATING: | 375 | case T_FLOATING: |
367 | #ifdef HAVE_XWAYLAND | 376 | #ifdef HAVE_XWAYLAND |
368 | case T_ID: | 377 | case T_ID: |
@@ -425,7 +434,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
425 | case T_CON_ID: | 434 | case T_CON_ID: |
426 | criteria->con_id = strtoul(effective_value, &endptr, 10); | 435 | criteria->con_id = strtoul(effective_value, &endptr, 10); |
427 | if (*endptr != 0) { | 436 | if (*endptr != 0) { |
428 | error = strdup("The value for 'con_id' should be numeric"); | 437 | error = strdup("The value for 'con_id' should be '__focused__' or numeric"); |
429 | } | 438 | } |
430 | break; | 439 | break; |
431 | case T_CON_MARK: | 440 | case T_CON_MARK: |
@@ -452,13 +461,18 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
452 | criteria->tiling = true; | 461 | criteria->tiling = true; |
453 | break; | 462 | break; |
454 | case T_URGENT: | 463 | case T_URGENT: |
455 | if (strcmp(effective_value, "latest") == 0) { | 464 | if (strcmp(effective_value, "latest") == 0 || |
465 | strcmp(effective_value, "newest") == 0 || | ||
466 | strcmp(effective_value, "last") == 0 || | ||
467 | strcmp(effective_value, "recent") == 0) { | ||
456 | criteria->urgent = 'l'; | 468 | criteria->urgent = 'l'; |
457 | } else if (strcmp(effective_value, "oldest") == 0) { | 469 | } else if (strcmp(effective_value, "oldest") == 0 || |
470 | strcmp(effective_value, "first") == 0) { | ||
458 | criteria->urgent = 'o'; | 471 | criteria->urgent = 'o'; |
459 | } else { | 472 | } else { |
460 | error = | 473 | error = |
461 | strdup("The value for 'urgent' must be 'latest' or 'oldest'"); | 474 | strdup("The value for 'urgent' must be 'first', 'last', " |
475 | "'latest', 'newest', 'oldest' or 'recent'"); | ||
462 | } | 476 | } |
463 | break; | 477 | break; |
464 | case T_WORKSPACE: | 478 | case T_WORKSPACE: |
diff --git a/sway/decoration.c b/sway/decoration.c new file mode 100644 index 00000000..0e3e67ac --- /dev/null +++ b/sway/decoration.c | |||
@@ -0,0 +1,71 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include "sway/decoration.h" | ||
3 | #include "sway/server.h" | ||
4 | #include "sway/tree/view.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | static void server_decoration_handle_destroy(struct wl_listener *listener, | ||
8 | void *data) { | ||
9 | struct sway_server_decoration *deco = | ||
10 | wl_container_of(listener, deco, destroy); | ||
11 | wl_list_remove(&deco->destroy.link); | ||
12 | wl_list_remove(&deco->mode.link); | ||
13 | wl_list_remove(&deco->link); | ||
14 | free(deco); | ||
15 | } | ||
16 | |||
17 | static void server_decoration_handle_mode(struct wl_listener *listener, | ||
18 | void *data) { | ||
19 | struct sway_server_decoration *deco = | ||
20 | wl_container_of(listener, deco, mode); | ||
21 | struct sway_view *view = | ||
22 | view_from_wlr_surface(deco->wlr_server_decoration->surface); | ||
23 | if (view == NULL || view->surface != deco->wlr_server_decoration->surface) { | ||
24 | return; | ||
25 | } | ||
26 | |||
27 | switch (view->type) { | ||
28 | case SWAY_VIEW_XDG_SHELL_V6:; | ||
29 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
30 | (struct sway_xdg_shell_v6_view *)view; | ||
31 | xdg_shell_v6_view->deco_mode = deco->wlr_server_decoration->mode; | ||
32 | break; | ||
33 | case SWAY_VIEW_XDG_SHELL:; | ||
34 | struct sway_xdg_shell_view *xdg_shell_view = | ||
35 | (struct sway_xdg_shell_view *)view; | ||
36 | xdg_shell_view->deco_mode = deco->wlr_server_decoration->mode; | ||
37 | break; | ||
38 | default: | ||
39 | break; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | void handle_server_decoration(struct wl_listener *listener, void *data) { | ||
44 | struct wlr_server_decoration *wlr_deco = data; | ||
45 | |||
46 | struct sway_server_decoration *deco = calloc(1, sizeof(*deco)); | ||
47 | if (deco == NULL) { | ||
48 | return; | ||
49 | } | ||
50 | |||
51 | deco->wlr_server_decoration = wlr_deco; | ||
52 | |||
53 | wl_signal_add(&wlr_deco->events.destroy, &deco->destroy); | ||
54 | deco->destroy.notify = server_decoration_handle_destroy; | ||
55 | |||
56 | wl_signal_add(&wlr_deco->events.mode, &deco->mode); | ||
57 | deco->mode.notify = server_decoration_handle_mode; | ||
58 | |||
59 | wl_list_insert(&server.decorations, &deco->link); | ||
60 | } | ||
61 | |||
62 | struct sway_server_decoration *decoration_from_surface( | ||
63 | struct wlr_surface *surface) { | ||
64 | struct sway_server_decoration *deco; | ||
65 | wl_list_for_each(deco, &server.decorations, link) { | ||
66 | if (deco->wlr_server_decoration->surface == surface) { | ||
67 | return deco; | ||
68 | } | ||
69 | } | ||
70 | return NULL; | ||
71 | } | ||
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index b364663d..3b73f99c 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <wlr/types/wlr_xdg_shell.h> | 6 | #include <wlr/types/wlr_xdg_shell.h> |
7 | #include <wlr/util/edges.h> | 7 | #include <wlr/util/edges.h> |
8 | #include "log.h" | 8 | #include "log.h" |
9 | #include "sway/decoration.h" | ||
9 | #include "sway/input/input-manager.h" | 10 | #include "sway/input/input-manager.h" |
10 | #include "sway/input/seat.h" | 11 | #include "sway/input/seat.h" |
11 | #include "sway/server.h" | 12 | #include "sway/server.h" |
@@ -170,6 +171,15 @@ static bool wants_floating(struct sway_view *view) { | |||
170 | || toplevel->parent; | 171 | || toplevel->parent; |
171 | } | 172 | } |
172 | 173 | ||
174 | static bool has_client_side_decorations(struct sway_view *view) { | ||
175 | struct sway_xdg_shell_view *xdg_shell_view = | ||
176 | xdg_shell_view_from_view(view); | ||
177 | if (xdg_shell_view == NULL) { | ||
178 | return true; | ||
179 | } | ||
180 | return xdg_shell_view->deco_mode != WLR_SERVER_DECORATION_MANAGER_MODE_SERVER; | ||
181 | } | ||
182 | |||
173 | static void for_each_surface(struct sway_view *view, | 183 | static void for_each_surface(struct sway_view *view, |
174 | wlr_surface_iterator_func_t iterator, void *user_data) { | 184 | wlr_surface_iterator_func_t iterator, void *user_data) { |
175 | if (xdg_shell_view_from_view(view) == NULL) { | 185 | if (xdg_shell_view_from_view(view) == NULL) { |
@@ -226,6 +236,7 @@ static const struct sway_view_impl view_impl = { | |||
226 | .set_tiled = set_tiled, | 236 | .set_tiled = set_tiled, |
227 | .set_fullscreen = set_fullscreen, | 237 | .set_fullscreen = set_fullscreen, |
228 | .wants_floating = wants_floating, | 238 | .wants_floating = wants_floating, |
239 | .has_client_side_decorations = has_client_side_decorations, | ||
229 | .for_each_surface = for_each_surface, | 240 | .for_each_surface = for_each_surface, |
230 | .for_each_popup = for_each_popup, | 241 | .for_each_popup = for_each_popup, |
231 | .close = _close, | 242 | .close = _close, |
@@ -357,6 +368,14 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
357 | view->natural_height = view->wlr_xdg_surface->surface->current.height; | 368 | view->natural_height = view->wlr_xdg_surface->surface->current.height; |
358 | } | 369 | } |
359 | 370 | ||
371 | struct sway_server_decoration *deco = | ||
372 | decoration_from_surface(xdg_surface->surface); | ||
373 | if (deco != NULL) { | ||
374 | xdg_shell_view->deco_mode = deco->wlr_server_decoration->mode; | ||
375 | } else { | ||
376 | xdg_shell_view->deco_mode = WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; | ||
377 | } | ||
378 | |||
360 | view_map(view, view->wlr_xdg_surface->surface); | 379 | view_map(view, view->wlr_xdg_surface->surface); |
361 | 380 | ||
362 | if (xdg_surface->toplevel->client_pending.fullscreen) { | 381 | if (xdg_surface->toplevel->client_pending.fullscreen) { |
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index ffea03ad..a947fb35 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c | |||
@@ -4,14 +4,15 @@ | |||
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include <wayland-server.h> | 5 | #include <wayland-server.h> |
6 | #include <wlr/types/wlr_xdg_shell_v6.h> | 6 | #include <wlr/types/wlr_xdg_shell_v6.h> |
7 | #include "log.h" | ||
8 | #include "sway/decoration.h" | ||
9 | #include "sway/input/input-manager.h" | ||
10 | #include "sway/input/seat.h" | ||
7 | #include "sway/server.h" | 11 | #include "sway/server.h" |
8 | #include "sway/tree/arrange.h" | 12 | #include "sway/tree/arrange.h" |
9 | #include "sway/tree/container.h" | 13 | #include "sway/tree/container.h" |
10 | #include "sway/tree/layout.h" | 14 | #include "sway/tree/layout.h" |
11 | #include "sway/tree/view.h" | 15 | #include "sway/tree/view.h" |
12 | #include "sway/input/seat.h" | ||
13 | #include "sway/input/input-manager.h" | ||
14 | #include "log.h" | ||
15 | 16 | ||
16 | static const struct sway_view_child_impl popup_impl; | 17 | static const struct sway_view_child_impl popup_impl; |
17 | 18 | ||
@@ -166,6 +167,15 @@ static bool wants_floating(struct sway_view *view) { | |||
166 | || toplevel->parent; | 167 | || toplevel->parent; |
167 | } | 168 | } |
168 | 169 | ||
170 | static bool has_client_side_decorations(struct sway_view *view) { | ||
171 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
172 | xdg_shell_v6_view_from_view(view); | ||
173 | if (xdg_shell_v6_view == NULL) { | ||
174 | return true; | ||
175 | } | ||
176 | return xdg_shell_v6_view->deco_mode != WLR_SERVER_DECORATION_MANAGER_MODE_SERVER; | ||
177 | } | ||
178 | |||
169 | static void for_each_surface(struct sway_view *view, | 179 | static void for_each_surface(struct sway_view *view, |
170 | wlr_surface_iterator_func_t iterator, void *user_data) { | 180 | wlr_surface_iterator_func_t iterator, void *user_data) { |
171 | if (xdg_shell_v6_view_from_view(view) == NULL) { | 181 | if (xdg_shell_v6_view_from_view(view) == NULL) { |
@@ -223,6 +233,7 @@ static const struct sway_view_impl view_impl = { | |||
223 | .set_tiled = set_tiled, | 233 | .set_tiled = set_tiled, |
224 | .set_fullscreen = set_fullscreen, | 234 | .set_fullscreen = set_fullscreen, |
225 | .wants_floating = wants_floating, | 235 | .wants_floating = wants_floating, |
236 | .has_client_side_decorations = has_client_side_decorations, | ||
226 | .for_each_surface = for_each_surface, | 237 | .for_each_surface = for_each_surface, |
227 | .for_each_popup = for_each_popup, | 238 | .for_each_popup = for_each_popup, |
228 | .close = _close, | 239 | .close = _close, |
@@ -353,6 +364,14 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
353 | view->natural_height = view->wlr_xdg_surface_v6->surface->current.height; | 364 | view->natural_height = view->wlr_xdg_surface_v6->surface->current.height; |
354 | } | 365 | } |
355 | 366 | ||
367 | struct sway_server_decoration *deco = | ||
368 | decoration_from_surface(xdg_surface->surface); | ||
369 | if (deco != NULL) { | ||
370 | xdg_shell_v6_view->deco_mode = deco->wlr_server_decoration->mode; | ||
371 | } else { | ||
372 | xdg_shell_v6_view->deco_mode = WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; | ||
373 | } | ||
374 | |||
356 | view_map(view, view->wlr_xdg_surface_v6->surface); | 375 | view_map(view, view->wlr_xdg_surface_v6->surface); |
357 | 376 | ||
358 | if (xdg_surface->toplevel->client_pending.fullscreen) { | 377 | if (xdg_surface->toplevel->client_pending.fullscreen) { |
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 80b4f9dc..3f417e96 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -429,6 +429,8 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, | |||
429 | struct sway_container *cont) { | 429 | struct sway_container *cont) { |
430 | struct sway_seat *seat = cursor->seat; | 430 | struct sway_seat *seat = cursor->seat; |
431 | 431 | ||
432 | seat_set_focus(seat, cont); | ||
433 | |||
432 | // Deny moving or resizing a fullscreen container | 434 | // Deny moving or resizing a fullscreen container |
433 | if (container_is_fullscreen_or_child(cont)) { | 435 | if (container_is_fullscreen_or_child(cont)) { |
434 | seat_pointer_notify_button(seat, time_msec, button, state); | 436 | seat_pointer_notify_button(seat, time_msec, button, state); |
@@ -469,8 +471,6 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, | |||
469 | return; | 471 | return; |
470 | } | 472 | } |
471 | 473 | ||
472 | // Send event to surface | ||
473 | seat_set_focus(seat, cont); | ||
474 | seat_pointer_notify_button(seat, time_msec, button, state); | 474 | seat_pointer_notify_button(seat, time_msec, button, state); |
475 | } | 475 | } |
476 | 476 | ||
diff --git a/sway/input/seat.c b/sway/input/seat.c index dd4d5c3b..6dd7cf7d 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -717,12 +717,8 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
717 | 717 | ||
718 | // If we've focused a floating container, bring it to the front. | 718 | // If we've focused a floating container, bring it to the front. |
719 | // We do this by putting it at the end of the floating list. | 719 | // We do this by putting it at the end of the floating list. |
720 | // This must happen for both the pending and current children lists. | ||
721 | if (container && container_is_floating(container)) { | 720 | if (container && container_is_floating(container)) { |
722 | list_move_to_end(container->parent->children, container); | 721 | list_move_to_end(container->parent->children, container); |
723 | if (container_has_ancestor(container, container->current.parent)) { | ||
724 | list_move_to_end(container->parent->current.children, container); | ||
725 | } | ||
726 | } | 722 | } |
727 | 723 | ||
728 | // clean up unfocused empty workspace on new output | 724 | // clean up unfocused empty workspace on new output |
diff --git a/sway/meson.build b/sway/meson.build index c18fb6e2..2a457270 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -1,13 +1,14 @@ | |||
1 | sway_sources = files( | 1 | sway_sources = files( |
2 | 'main.c', | ||
3 | 'server.c', | ||
4 | 'commands.c', | 2 | 'commands.c', |
5 | 'config.c', | 3 | 'config.c', |
6 | 'criteria.c', | 4 | 'criteria.c', |
7 | 'debug-tree.c', | 5 | 'debug-tree.c', |
6 | 'decoration.c', | ||
8 | 'ipc-json.c', | 7 | 'ipc-json.c', |
9 | 'ipc-server.c', | 8 | 'ipc-server.c', |
9 | 'main.c', | ||
10 | 'security.c', | 10 | 'security.c', |
11 | 'server.c', | ||
11 | 'swaynag.c', | 12 | 'swaynag.c', |
12 | 13 | ||
13 | 'desktop/desktop.c', | 14 | 'desktop/desktop.c', |
diff --git a/sway/server.c b/sway/server.c index e8755360..e8dc63be 100644 --- a/sway/server.c +++ b/sway/server.c | |||
@@ -19,7 +19,6 @@ | |||
19 | #include <wlr/types/wlr_xcursor_manager.h> | 19 | #include <wlr/types/wlr_xcursor_manager.h> |
20 | #include <wlr/types/wlr_xdg_output.h> | 20 | #include <wlr/types/wlr_xdg_output.h> |
21 | #include <wlr/util/log.h> | 21 | #include <wlr/util/log.h> |
22 | // TODO WLR: make Xwayland optional | ||
23 | #include "list.h" | 22 | #include "list.h" |
24 | #include "sway/config.h" | 23 | #include "sway/config.h" |
25 | #include "sway/desktop/idle_inhibit_v1.h" | 24 | #include "sway/desktop/idle_inhibit_v1.h" |
@@ -85,7 +84,6 @@ bool server_init(struct sway_server *server) { | |||
85 | &server->xdg_shell_surface); | 84 | &server->xdg_shell_surface); |
86 | server->xdg_shell_surface.notify = handle_xdg_shell_surface; | 85 | server->xdg_shell_surface.notify = handle_xdg_shell_surface; |
87 | 86 | ||
88 | // TODO make xwayland optional | ||
89 | #ifdef HAVE_XWAYLAND | 87 | #ifdef HAVE_XWAYLAND |
90 | server->xwayland.wlr_xwayland = | 88 | server->xwayland.wlr_xwayland = |
91 | wlr_xwayland_create(server->wl_display, server->compositor, true); | 89 | wlr_xwayland_create(server->wl_display, server->compositor, true); |
@@ -109,11 +107,15 @@ bool server_init(struct sway_server *server) { | |||
109 | } | 107 | } |
110 | #endif | 108 | #endif |
111 | 109 | ||
112 | // TODO: Integration with sway borders | 110 | server->server_decoration_manager = |
113 | struct wlr_server_decoration_manager *deco_manager = | ||
114 | wlr_server_decoration_manager_create(server->wl_display); | 111 | wlr_server_decoration_manager_create(server->wl_display); |
115 | wlr_server_decoration_manager_set_default_mode( | 112 | wlr_server_decoration_manager_set_default_mode( |
116 | deco_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); | 113 | server->server_decoration_manager, |
114 | WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); | ||
115 | wl_signal_add(&server->server_decoration_manager->events.new_decoration, | ||
116 | &server->server_decoration); | ||
117 | server->server_decoration.notify = handle_server_decoration; | ||
118 | wl_list_init(&server->decorations); | ||
117 | 119 | ||
118 | wlr_linux_dmabuf_v1_create(server->wl_display, renderer); | 120 | wlr_linux_dmabuf_v1_create(server->wl_display, renderer); |
119 | wlr_export_dmabuf_manager_v1_create(server->wl_display); | 121 | wlr_export_dmabuf_manager_v1_create(server->wl_display); |
diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 82df38e3..73a01152 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd | |||
@@ -84,6 +84,9 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). | |||
84 | *floating* enable|disable|toggle | 84 | *floating* enable|disable|toggle |
85 | Make focused view floating, non-floating, or the opposite of what it is now. | 85 | Make focused view floating, non-floating, or the opposite of what it is now. |
86 | 86 | ||
87 | <criteria> *focus* | ||
88 | Moves focus to the container that matches the specified criteria. | ||
89 | |||
87 | *focus* up|right|down|left | 90 | *focus* up|right|down|left |
88 | Moves focus to the next container in the specified direction. | 91 | Moves focus to the next container in the specified direction. |
89 | 92 | ||
@@ -111,33 +114,53 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). | |||
111 | *fullscreen* | 114 | *fullscreen* |
112 | Toggles fullscreen for the focused view. | 115 | Toggles fullscreen for the focused view. |
113 | 116 | ||
114 | *layout* splith|splitv|stacking|tabbed | 117 | *layout* default|splith|splitv|stacking|tabbed |
115 | Sets the layout mode of the focused container. | 118 | Sets the layout mode of the focused container. |
116 | 119 | ||
117 | *layout* toggle split | 120 | *layout* toggle [split|all] |
118 | Switches the focused container between the splitv and splith layouts. | 121 | Cycles the layout mode of the focused container though a preset list of |
122 | layouts. If no argument is given, then it cycles through stacking, tabbed | ||
123 | and the last split layout. If "split" is given, then it cycles through | ||
124 | splith and splitv. If "all" is given, then it cycles through every layout. | ||
125 | |||
126 | *layout* toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]... | ||
127 | Cycles the layout mode of the focused container through a list of layouts. | ||
119 | 128 | ||
120 | *move* left|right|up|down [<px>] | 129 | *move* left|right|up|down [<px> px] |
121 | Moves the focused container in the direction specified. If the container, | 130 | Moves the focused container in the direction specified. If the container, |
122 | the optional _px_ argument specifies how many pixels to move the container. | 131 | the optional _px_ argument specifies how many pixels to move the container. |
123 | If unspecified, the default is 10 pixels. Pixels are ignored when moving | 132 | If unspecified, the default is 10 pixels. Pixels are ignored when moving |
124 | tiled containers. | 133 | tiled containers. |
125 | 134 | ||
126 | *move* container|window to workspace <name> | 135 | *move* [absolute] position <pos_x> [px] <pos_y> [px] |
127 | Moves the focused container to the specified workspace. | 136 | Moves the focused container to the specified position. |
128 | 137 | ||
129 | *move* container|window to workspace prev|next | 138 | *move* [absolute] position center|mouse |
130 | Moves the focused container to the previous or next workspace on this | 139 | Moves the focused container to be centered on the workspace or mouse. |
131 | output, or if no workspaces remain, the previous or next output. | 140 | |
141 | *move* container|window [to] mark <mark> | ||
142 | Moves the focused container to the specified mark. | ||
143 | |||
144 | *move* [--no-auto-back-and-forth] container|window [to] workspace [number] <name> | ||
145 | Moves the focused container to the specified workspace. The string "number" | ||
146 | is optional and is used to match a workspace with the same number, even if | ||
147 | it has a different name. | ||
132 | 148 | ||
133 | *move* container|window to workspace prev\_on\_output|next\_on\_output | 149 | *move* container|window [to] workspace prev|next|current |
150 | Moves the focused container to the previous, next or current workspace on | ||
151 | this output, or if no workspaces remain, the previous or next output. | ||
152 | |||
153 | *move* container|window [to] workspace prev\_on\_output|next\_on\_output | ||
134 | Moves the focused container to the previous or next workspace on this | 154 | Moves the focused container to the previous or next workspace on this |
135 | output, wrapping around if already at the first or last workspace. | 155 | output, wrapping around if already at the first or last workspace. |
136 | 156 | ||
137 | *move* container|window|workspace to output <name> | 157 | *move* container|window [to] workspace back_and_forth |
158 | Moves the focused container to previously focused workspace. | ||
159 | |||
160 | *move* container|window|workspace [to] output <name> | ||
138 | Moves the focused container or workspace to the specified output. | 161 | Moves the focused container or workspace to the specified output. |
139 | 162 | ||
140 | *move* container|window|workspace to output up|right|down|left | 163 | *move* container|window|workspace [to] output up|right|down|left |
141 | Moves the focused container or workspace to next output in the specified | 164 | Moves the focused container or workspace to next output in the specified |
142 | direction. | 165 | direction. |
143 | 166 | ||
@@ -511,7 +534,7 @@ config after the others, or it will be matched instead of the others. | |||
511 | state. Using _allow_ or _deny_ controls the window's ability to set itself | 534 | state. Using _allow_ or _deny_ controls the window's ability to set itself |
512 | as urgent. By default, windows are allowed to set their own urgency. | 535 | as urgent. By default, windows are allowed to set their own urgency. |
513 | 536 | ||
514 | *workspace* [number] <name> | 537 | *workspace* [--no-auto-back-and-forth] [number] <name> |
515 | Switches to the specified workspace. The string "number" is optional and is | 538 | Switches to the specified workspace. The string "number" is optional and is |
516 | used to sort workspaces. | 539 | used to sort workspaces. |
517 | 540 | ||
@@ -522,6 +545,9 @@ config after the others, or it will be matched instead of the others. | |||
522 | *workspace* prev\_on\_output|next\_on\_output | 545 | *workspace* prev\_on\_output|next\_on\_output |
523 | Switches to the next workspace on the current output. | 546 | Switches to the next workspace on the current output. |
524 | 547 | ||
548 | *workspace* back_and_forth | ||
549 | Switches to the previously focused workspace. | ||
550 | |||
525 | *workspace* <name> output <output> | 551 | *workspace* <name> output <output> |
526 | Specifies that workspace _name_ should be shown on the specified _output_. | 552 | Specifies that workspace _name_ should be shown on the specified _output_. |
527 | 553 | ||
@@ -582,7 +608,9 @@ The following attributes may be matched with: | |||
582 | the currently focused window. | 608 | the currently focused window. |
583 | 609 | ||
584 | *con\_id* | 610 | *con\_id* |
585 | Compare against the internal container ID, which you can find via IPC. | 611 | Compare against the internal container ID, which you can find via IPC. If |
612 | value is \_\_focused\_\_, then the id must be the same as that of the | ||
613 | currently focused window. | ||
586 | 614 | ||
587 | *con\_mark* | 615 | *con\_mark* |
588 | Compare against the window marks. Can be a regular expression. | 616 | Compare against the window marks. Can be a regular expression. |
@@ -612,7 +640,8 @@ The following attributes may be matched with: | |||
612 | currently focused window. | 640 | currently focused window. |
613 | 641 | ||
614 | *urgent* | 642 | *urgent* |
615 | Compares the urgent state of the window. Can be "latest" or "oldest". | 643 | Compares the urgent state of the window. Can be "first", "last", "latest", |
644 | "newest", "oldest" or "recent". | ||
616 | 645 | ||
617 | *window\_role* | 646 | *window\_role* |
618 | Compare against the window role (WM\_WINDOW\_ROLE). Can be a regular | 647 | Compare against the window role (WM\_WINDOW\_ROLE). Can be a regular |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 6da5ac3c..aecb2ac6 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -533,11 +533,10 @@ struct sway_container *container_parent(struct sway_container *container, | |||
533 | return container; | 533 | return container; |
534 | } | 534 | } |
535 | 535 | ||
536 | static struct sway_container *container_at_view(struct sway_container *swayc, | 536 | static void surface_at_view(struct sway_container *swayc, double lx, double ly, |
537 | double lx, double ly, | ||
538 | struct wlr_surface **surface, double *sx, double *sy) { | 537 | struct wlr_surface **surface, double *sx, double *sy) { |
539 | if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { | 538 | if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { |
540 | return NULL; | 539 | return; |
541 | } | 540 | } |
542 | struct sway_view *sview = swayc->sway_view; | 541 | struct sway_view *sview = swayc->sway_view; |
543 | double view_sx = lx - sview->x; | 542 | double view_sx = lx - sview->x; |
@@ -567,9 +566,7 @@ static struct sway_container *container_at_view(struct sway_container *swayc, | |||
567 | *sx = _sx; | 566 | *sx = _sx; |
568 | *sy = _sy; | 567 | *sy = _sy; |
569 | *surface = _surface; | 568 | *surface = _surface; |
570 | return swayc; | ||
571 | } | 569 | } |
572 | return NULL; | ||
573 | } | 570 | } |
574 | 571 | ||
575 | /** | 572 | /** |
@@ -682,7 +679,8 @@ struct sway_container *tiling_container_at( | |||
682 | struct sway_container *con, double lx, double ly, | 679 | struct sway_container *con, double lx, double ly, |
683 | struct wlr_surface **surface, double *sx, double *sy) { | 680 | struct wlr_surface **surface, double *sx, double *sy) { |
684 | if (con->type == C_VIEW) { | 681 | if (con->type == C_VIEW) { |
685 | return container_at_view(con, lx, ly, surface, sx, sy); | 682 | surface_at_view(con, lx, ly, surface, sx, sy); |
683 | return con; | ||
686 | } | 684 | } |
687 | if (!con->children->length) { | 685 | if (!con->children->length) { |
688 | return NULL; | 686 | return NULL; |
@@ -745,7 +743,7 @@ struct sway_container *container_at(struct sway_container *workspace, | |||
745 | struct sway_container *focus = | 743 | struct sway_container *focus = |
746 | seat_get_focus_inactive(seat, &root_container); | 744 | seat_get_focus_inactive(seat, &root_container); |
747 | if (focus && focus->type == C_VIEW) { | 745 | if (focus && focus->type == C_VIEW) { |
748 | container_at_view(focus, lx, ly, surface, sx, sy); | 746 | surface_at_view(focus, lx, ly, surface, sx, sy); |
749 | if (*surface && surface_is_popup(*surface)) { | 747 | if (*surface && surface_is_popup(*surface)) { |
750 | return focus; | 748 | return focus; |
751 | } | 749 | } |
@@ -1163,19 +1161,16 @@ void container_floating_translate(struct sway_container *con, | |||
1163 | double x_amount, double y_amount) { | 1161 | double x_amount, double y_amount) { |
1164 | con->x += x_amount; | 1162 | con->x += x_amount; |
1165 | con->y += y_amount; | 1163 | con->y += y_amount; |
1166 | con->current.swayc_x += x_amount; | ||
1167 | con->current.swayc_y += y_amount; | ||
1168 | if (con->type == C_VIEW) { | 1164 | if (con->type == C_VIEW) { |
1169 | con->sway_view->x += x_amount; | 1165 | con->sway_view->x += x_amount; |
1170 | con->sway_view->y += y_amount; | 1166 | con->sway_view->y += y_amount; |
1171 | con->current.view_x += x_amount; | ||
1172 | con->current.view_y += y_amount; | ||
1173 | } else { | 1167 | } else { |
1174 | for (int i = 0; i < con->children->length; ++i) { | 1168 | for (int i = 0; i < con->children->length; ++i) { |
1175 | struct sway_container *child = con->children->items[i]; | 1169 | struct sway_container *child = con->children->items[i]; |
1176 | container_floating_translate(child, x_amount, y_amount); | 1170 | container_floating_translate(child, x_amount, y_amount); |
1177 | } | 1171 | } |
1178 | } | 1172 | } |
1173 | container_set_dirty(con); | ||
1179 | } | 1174 | } |
1180 | 1175 | ||
1181 | /** | 1176 | /** |
@@ -1185,7 +1180,7 @@ void container_floating_translate(struct sway_container *con, | |||
1185 | * one, otherwise we'll choose whichever output is closest to the container's | 1180 | * one, otherwise we'll choose whichever output is closest to the container's |
1186 | * center. | 1181 | * center. |
1187 | */ | 1182 | */ |
1188 | static struct sway_container *container_floating_find_output( | 1183 | struct sway_container *container_floating_find_output( |
1189 | struct sway_container *con) { | 1184 | struct sway_container *con) { |
1190 | double center_x = con->x + con->width / 2; | 1185 | double center_x = con->x + con->width / 2; |
1191 | double center_y = con->y + con->height / 2; | 1186 | double center_y = con->y + con->height / 2; |
@@ -1219,9 +1214,7 @@ void container_floating_move_to(struct sway_container *con, | |||
1219 | "Expected a floating container")) { | 1214 | "Expected a floating container")) { |
1220 | return; | 1215 | return; |
1221 | } | 1216 | } |
1222 | desktop_damage_whole_container(con); | ||
1223 | container_floating_translate(con, lx - con->x, ly - con->y); | 1217 | container_floating_translate(con, lx - con->x, ly - con->y); |
1224 | desktop_damage_whole_container(con); | ||
1225 | struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); | 1218 | struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); |
1226 | struct sway_container *new_output = container_floating_find_output(con); | 1219 | struct sway_container *new_output = container_floating_find_output(con); |
1227 | if (!sway_assert(new_output, "Unable to find any output")) { | 1220 | if (!sway_assert(new_output, "Unable to find any output")) { |
@@ -1239,6 +1232,17 @@ void container_floating_move_to(struct sway_container *con, | |||
1239 | } | 1232 | } |
1240 | } | 1233 | } |
1241 | 1234 | ||
1235 | void container_floating_move_to_center(struct sway_container *con) { | ||
1236 | if (!sway_assert(container_is_floating(con), | ||
1237 | "Expected a floating container")) { | ||
1238 | return; | ||
1239 | } | ||
1240 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
1241 | double new_lx = ws->x + (ws->width - con->width) / 2; | ||
1242 | double new_ly = ws->y + (ws->height - con->height) / 2; | ||
1243 | container_floating_translate(con, new_lx - con->x, new_ly - con->y); | ||
1244 | } | ||
1245 | |||
1242 | void container_set_dirty(struct sway_container *container) { | 1246 | void container_set_dirty(struct sway_container *container) { |
1243 | if (container->dirty) { | 1247 | if (container->dirty) { |
1244 | return; | 1248 | return; |
@@ -1318,6 +1322,11 @@ void container_set_fullscreen(struct sway_container *container, bool enable) { | |||
1318 | container->y = container->saved_y; | 1322 | container->y = container->saved_y; |
1319 | container->width = container->saved_width; | 1323 | container->width = container->saved_width; |
1320 | container->height = container->saved_height; | 1324 | container->height = container->saved_height; |
1325 | struct sway_container *output = | ||
1326 | container_floating_find_output(container); | ||
1327 | if (!container_has_ancestor(container, output)) { | ||
1328 | container_floating_move_to_center(container); | ||
1329 | } | ||
1321 | } else { | 1330 | } else { |
1322 | container->width = container->saved_width; | 1331 | container->width = container->saved_width; |
1323 | container->height = container->saved_height; | 1332 | container->height = container->saved_height; |
diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 07de9664..38e14d00 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c | |||
@@ -142,49 +142,55 @@ struct sway_container *container_remove_child(struct sway_container *child) { | |||
142 | 142 | ||
143 | void container_move_to(struct sway_container *container, | 143 | void container_move_to(struct sway_container *container, |
144 | struct sway_container *destination) { | 144 | struct sway_container *destination) { |
145 | if (!sway_assert(container->type == C_CONTAINER || | ||
146 | container->type == C_VIEW, "Expected a container or view")) { | ||
147 | return; | ||
148 | } | ||
145 | if (container == destination | 149 | if (container == destination |
146 | || container_has_ancestor(container, destination)) { | 150 | || container_has_ancestor(container, destination)) { |
147 | return; | 151 | return; |
148 | } | 152 | } |
153 | struct sway_container *old_parent = NULL; | ||
154 | struct sway_container *new_parent = NULL; | ||
149 | if (container_is_floating(container)) { | 155 | if (container_is_floating(container)) { |
150 | // TODO | 156 | // Resolve destination into a workspace |
151 | return; | 157 | struct sway_container *new_ws = NULL; |
152 | } | 158 | if (destination->type == C_OUTPUT) { |
153 | struct sway_container *old_parent = container_remove_child(container); | 159 | new_ws = output_get_active_workspace(destination->sway_output); |
154 | container->width = container->height = 0; | 160 | } else if (destination->type == C_WORKSPACE) { |
155 | container->saved_width = container->saved_height = 0; | 161 | new_ws = destination; |
156 | 162 | } else { | |
157 | struct sway_container *new_parent, *new_parent_focus; | 163 | new_ws = container_parent(destination, C_WORKSPACE); |
158 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | 164 | } |
159 | 165 | if (!new_ws) { | |
160 | // Get the focus of the destination before we change it. | 166 | // This can happen if the user has run "move container to mark foo", |
161 | new_parent_focus = seat_get_focus_inactive(seat, destination); | 167 | // where mark foo is on a hidden scratchpad container. |
162 | if (destination->type == C_VIEW) { | 168 | return; |
163 | new_parent = container_add_sibling(destination, container); | 169 | } |
170 | struct sway_container *old_output = | ||
171 | container_parent(container, C_OUTPUT); | ||
172 | old_parent = container_remove_child(container); | ||
173 | container_add_child(new_ws->sway_workspace->floating, container); | ||
174 | // If changing output, center it within the workspace | ||
175 | if (old_output != new_ws->parent && !container->is_fullscreen) { | ||
176 | container_floating_move_to_center(container); | ||
177 | } | ||
164 | } else { | 178 | } else { |
165 | new_parent = destination; | 179 | old_parent = container_remove_child(container); |
166 | container_add_child(destination, container); | 180 | container->width = container->height = 0; |
167 | } | 181 | container->saved_width = container->saved_height = 0; |
168 | wl_signal_emit(&container->events.reparent, old_parent); | ||
169 | 182 | ||
170 | if (container->type == C_WORKSPACE) { | 183 | if (destination->type == C_VIEW) { |
171 | // If moving a workspace to a new output, maybe create a new workspace | 184 | new_parent = container_add_sibling(destination, container); |
172 | // on the previous output | 185 | } else { |
173 | if (old_parent->children->length == 0) { | 186 | new_parent = destination; |
174 | char *ws_name = workspace_next_name(old_parent->name); | 187 | container_add_child(destination, container); |
175 | struct sway_container *ws = workspace_create(old_parent, ws_name); | ||
176 | free(ws_name); | ||
177 | seat_set_focus(seat, ws); | ||
178 | } | 188 | } |
189 | } | ||
179 | 190 | ||
180 | // Try to remove an empty workspace from the destination output. | 191 | wl_signal_emit(&container->events.reparent, old_parent); |
181 | container_reap_empty_recursive(new_parent_focus); | ||
182 | 192 | ||
183 | container_sort_workspaces(new_parent); | 193 | if (container->type == C_VIEW) { |
184 | seat_set_focus(seat, new_parent); | ||
185 | workspace_output_raise_priority(container, old_parent, new_parent); | ||
186 | ipc_event_workspace(NULL, container, "move"); | ||
187 | } else if (container->type == C_VIEW) { | ||
188 | ipc_event_window(container, "move"); | 194 | ipc_event_window(container, "move"); |
189 | } | 195 | } |
190 | container_notify_subtree_changed(old_parent); | 196 | container_notify_subtree_changed(old_parent); |
@@ -859,7 +865,7 @@ struct sway_container *container_split(struct sway_container *child, | |||
859 | } | 865 | } |
860 | if (child->type == C_WORKSPACE && child->children->length == 0) { | 866 | if (child->type == C_WORKSPACE && child->children->length == 0) { |
861 | // Special case: this just behaves like splitt | 867 | // Special case: this just behaves like splitt |
862 | child->prev_layout = child->layout; | 868 | child->prev_split_layout = child->layout; |
863 | child->layout = layout; | 869 | child->layout = layout; |
864 | return child; | 870 | return child; |
865 | } | 871 | } |
@@ -870,7 +876,7 @@ struct sway_container *container_split(struct sway_container *child, | |||
870 | 876 | ||
871 | remove_gaps(child); | 877 | remove_gaps(child); |
872 | 878 | ||
873 | cont->prev_layout = L_NONE; | 879 | cont->prev_split_layout = L_NONE; |
874 | cont->width = child->width; | 880 | cont->width = child->width; |
875 | cont->height = child->height; | 881 | cont->height = child->height; |
876 | cont->x = child->x; | 882 | cont->x = child->x; |
diff --git a/sway/tree/view.c b/sway/tree/view.c index 06cef900..950494d8 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include <strings.h> | ||
3 | #include <wayland-server.h> | 4 | #include <wayland-server.h> |
4 | #include <wlr/render/wlr_renderer.h> | 5 | #include <wlr/render/wlr_renderer.h> |
5 | #include <wlr/types/wlr_buffer.h> | 6 | #include <wlr/types/wlr_buffer.h> |
@@ -456,7 +457,13 @@ static struct sway_container *select_workspace(struct sway_view *view) { | |||
456 | if (criteria->type == CT_ASSIGN_WORKSPACE) { | 457 | if (criteria->type == CT_ASSIGN_WORKSPACE) { |
457 | ws = workspace_by_name(criteria->target); | 458 | ws = workspace_by_name(criteria->target); |
458 | if (!ws) { | 459 | if (!ws) { |
459 | ws = workspace_create(NULL, criteria->target); | 460 | if (strcasecmp(criteria->target, "back_and_forth") == 0) { |
461 | if (prev_workspace_name) { | ||
462 | ws = workspace_create(NULL, prev_workspace_name); | ||
463 | } | ||
464 | } else { | ||
465 | ws = workspace_create(NULL, criteria->target); | ||
466 | } | ||
460 | } | 467 | } |
461 | break; | 468 | break; |
462 | } else { | 469 | } else { |
@@ -891,6 +898,15 @@ static bool find_by_mark_iterator(struct sway_container *con, | |||
891 | return con->type == C_VIEW && view_has_mark(con->sway_view, mark); | 898 | return con->type == C_VIEW && view_has_mark(con->sway_view, mark); |
892 | } | 899 | } |
893 | 900 | ||
901 | struct sway_view *view_find_mark(char *mark) { | ||
902 | struct sway_container *container = container_find(&root_container, | ||
903 | find_by_mark_iterator, mark); | ||
904 | if (!container) { | ||
905 | return NULL; | ||
906 | } | ||
907 | return container->sway_view; | ||
908 | } | ||
909 | |||
894 | bool view_find_and_unmark(char *mark) { | 910 | bool view_find_and_unmark(char *mark) { |
895 | struct sway_container *container = container_find(&root_container, | 911 | struct sway_container *container = container_find(&root_container, |
896 | find_by_mark_iterator, mark); | 912 | find_by_mark_iterator, mark); |
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index cc225e79..b8bec044 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -18,7 +18,7 @@ | |||
18 | #include "log.h" | 18 | #include "log.h" |
19 | #include "util.h" | 19 | #include "util.h" |
20 | 20 | ||
21 | static struct sway_container *get_workspace_initial_output(const char *name) { | 21 | struct sway_container *workspace_get_initial_output(const char *name) { |
22 | struct sway_container *parent; | 22 | struct sway_container *parent; |
23 | // Search for workspace<->output pair | 23 | // Search for workspace<->output pair |
24 | int e = config->workspace_outputs->length; | 24 | int e = config->workspace_outputs->length; |
@@ -48,7 +48,7 @@ static struct sway_container *get_workspace_initial_output(const char *name) { | |||
48 | struct sway_container *workspace_create(struct sway_container *output, | 48 | struct sway_container *workspace_create(struct sway_container *output, |
49 | const char *name) { | 49 | const char *name) { |
50 | if (output == NULL) { | 50 | if (output == NULL) { |
51 | output = get_workspace_initial_output(name); | 51 | output = workspace_get_initial_output(name); |
52 | } | 52 | } |
53 | 53 | ||
54 | wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name); | 54 | wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name); |
@@ -59,7 +59,7 @@ struct sway_container *workspace_create(struct sway_container *output, | |||
59 | workspace->width = output->width; | 59 | workspace->width = output->width; |
60 | workspace->height = output->height; | 60 | workspace->height = output->height; |
61 | workspace->name = !name ? NULL : strdup(name); | 61 | workspace->name = !name ? NULL : strdup(name); |
62 | workspace->prev_layout = L_NONE; | 62 | workspace->prev_split_layout = L_NONE; |
63 | workspace->layout = container_get_default_layout(output); | 63 | workspace->layout = container_get_default_layout(output); |
64 | 64 | ||
65 | struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); | 65 | struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); |
@@ -250,6 +250,7 @@ struct sway_container *workspace_by_name(const char *name) { | |||
250 | current_workspace = container_parent(focus, C_WORKSPACE); | 250 | current_workspace = container_parent(focus, C_WORKSPACE); |
251 | current_output = container_parent(focus, C_OUTPUT); | 251 | current_output = container_parent(focus, C_OUTPUT); |
252 | } | 252 | } |
253 | |||
253 | if (strcmp(name, "prev") == 0) { | 254 | if (strcmp(name, "prev") == 0) { |
254 | return workspace_prev(current_workspace); | 255 | return workspace_prev(current_workspace); |
255 | } else if (strcmp(name, "prev_on_output") == 0) { | 256 | } else if (strcmp(name, "prev_on_output") == 0) { |
@@ -260,6 +261,9 @@ struct sway_container *workspace_by_name(const char *name) { | |||
260 | return workspace_output_next(current_output); | 261 | return workspace_output_next(current_output); |
261 | } else if (strcmp(name, "current") == 0) { | 262 | } else if (strcmp(name, "current") == 0) { |
262 | return current_workspace; | 263 | return current_workspace; |
264 | } else if (strcasecmp(name, "back_and_forth") == 0) { | ||
265 | return prev_workspace_name ? container_find(&root_container, | ||
266 | _workspace_by_name, (void *)prev_workspace_name) : NULL; | ||
263 | } else { | 267 | } else { |
264 | return container_find(&root_container, _workspace_by_name, | 268 | return container_find(&root_container, _workspace_by_name, |
265 | (void *)name); | 269 | (void *)name); |
@@ -364,7 +368,8 @@ struct sway_container *workspace_prev(struct sway_container *current) { | |||
364 | return workspace_prev_next_impl(current, false); | 368 | return workspace_prev_next_impl(current, false); |
365 | } | 369 | } |
366 | 370 | ||
367 | bool workspace_switch(struct sway_container *workspace) { | 371 | bool workspace_switch(struct sway_container *workspace, |
372 | bool no_auto_back_and_forth) { | ||
368 | if (!workspace) { | 373 | if (!workspace) { |
369 | return false; | 374 | return false; |
370 | } | 375 | } |
@@ -379,7 +384,7 @@ bool workspace_switch(struct sway_container *workspace) { | |||
379 | active_ws = container_parent(focus, C_WORKSPACE); | 384 | active_ws = container_parent(focus, C_WORKSPACE); |
380 | } | 385 | } |
381 | 386 | ||
382 | if (config->auto_back_and_forth | 387 | if (!no_auto_back_and_forth && config->auto_back_and_forth |
383 | && active_ws == workspace | 388 | && active_ws == workspace |
384 | && prev_workspace_name) { | 389 | && prev_workspace_name) { |
385 | struct sway_container *new_ws = workspace_by_name(prev_workspace_name); | 390 | struct sway_container *new_ws = workspace_by_name(prev_workspace_name); |
@@ -406,17 +411,20 @@ bool workspace_switch(struct sway_container *workspace) { | |||
406 | struct sway_container *floating = | 411 | struct sway_container *floating = |
407 | next_output_prev_ws->sway_workspace->floating; | 412 | next_output_prev_ws->sway_workspace->floating; |
408 | bool has_sticky = false; | 413 | bool has_sticky = false; |
409 | for (int i = 0; i < floating->children->length; ++i) { | 414 | if (workspace != next_output_prev_ws) { |
410 | struct sway_container *floater = floating->children->items[i]; | 415 | for (int i = 0; i < floating->children->length; ++i) { |
411 | if (floater->is_sticky) { | 416 | struct sway_container *floater = floating->children->items[i]; |
412 | has_sticky = true; | 417 | if (floater->is_sticky) { |
413 | container_remove_child(floater); | 418 | has_sticky = true; |
414 | container_add_child(workspace->sway_workspace->floating, floater); | 419 | container_remove_child(floater); |
415 | if (floater == focus) { | 420 | container_add_child(workspace->sway_workspace->floating, |
416 | seat_set_focus(seat, NULL); | 421 | floater); |
417 | seat_set_focus(seat, floater); | 422 | if (floater == focus) { |
423 | seat_set_focus(seat, NULL); | ||
424 | seat_set_focus(seat, floater); | ||
425 | } | ||
426 | --i; | ||
418 | } | 427 | } |
419 | --i; | ||
420 | } | 428 | } |
421 | } | 429 | } |
422 | 430 | ||