aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Brian Ashworth <RedSoxFan@users.noreply.github.com>2018-08-08 15:26:44 -0400
committerLibravatar GitHub <noreply@github.com>2018-08-08 15:26:44 -0400
commit3c26536267e13859eb6088ce0192579f10ac871f (patch)
treea4676629358071b0f0748dde33a32d82c9a58175
parentFix segv when sway is terminating (diff)
parent私の日本語が上手じゃないですね (diff)
downloadsway-3c26536267e13859eb6088ce0192579f10ac871f.tar.gz
sway-3c26536267e13859eb6088ce0192579f10ac871f.tar.zst
sway-3c26536267e13859eb6088ce0192579f10ac871f.zip
Merge branch 'master' into master
-rw-r--r--README.de.md4
-rw-r--r--README.el.md4
-rw-r--r--README.fr.md4
-rw-r--r--README.it.md4
-rw-r--r--README.ja.md6
-rw-r--r--README.md6
-rw-r--r--common/ipc-client.c2
-rw-r--r--include/sway/decoration.h17
-rw-r--r--include/sway/server.h11
-rw-r--r--include/sway/tree/container.h13
-rw-r--r--include/sway/tree/view.h9
-rw-r--r--include/sway/tree/workspace.h6
-rw-r--r--sway/commands/layout.c105
-rw-r--r--sway/commands/move.c323
-rw-r--r--sway/commands/rename.c10
-rw-r--r--sway/commands/workspace.c50
-rw-r--r--sway/criteria.c26
-rw-r--r--sway/decoration.c71
-rw-r--r--sway/desktop/xdg_shell.c19
-rw-r--r--sway/desktop/xdg_shell_v6.c25
-rw-r--r--sway/input/cursor.c4
-rw-r--r--sway/input/seat.c4
-rw-r--r--sway/meson.build5
-rw-r--r--sway/server.c12
-rw-r--r--sway/sway.5.scd59
-rw-r--r--sway/tree/container.c37
-rw-r--r--sway/tree/layout.c76
-rw-r--r--sway/tree/view.c18
-rw-r--r--sway/tree/workspace.c38
29 files changed, 692 insertions, 276 deletions
diff --git a/README.de.md b/README.de.md
index a872e888..9d8a41ce 100644
--- a/README.de.md
+++ b/README.de.md
@@ -76,10 +76,6 @@ Führe diese Befehle aus:
76 ninja -C build 76 ninja -C build
77 sudo ninja -C build install 77 sudo ninja -C build install
78 78
79In Systemen mit logind musst du `sway` einige Capabilities geben:
80
81 sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway
82
83In Systemen ohne logind musst du `sway` das suid-Flag geben: 79In Systemen ohne logind musst du `sway` das suid-Flag geben:
84 80
85 sudo chmod a+s /usr/local/bin/sway 81 sudo chmod a+s /usr/local/bin/sway
diff --git a/README.el.md b/README.el.md
index 6887fe8e..53fdd2d9 100644
--- a/README.el.md
+++ b/README.el.md
@@ -69,10 +69,6 @@ _\*\*Απαιτείται μόνο για swaylock_
69 ninja -C build 69 ninja -C build
70 sudo ninja -C build install 70 sudo ninja -C build install
71 71
72Σε συστήματα με logind, χρειάζεται να ορίσετε μερικά δικαιώματα caps στο εκτελέσιμο αρχείο:
73
74 sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway
75
76Σε συστήματα χωρίς logind, χρειάζεται να θέσετε το suid bit στο εκτελέσιμο αρχείο: 72Σε συστήματα χωρίς logind, χρειάζεται να θέσετε το suid bit στο εκτελέσιμο αρχείο:
77 73
78 sudo chmod a+s /usr/local/bin/sway 74 sudo chmod a+s /usr/local/bin/sway
diff --git a/README.fr.md b/README.fr.md
index 6ea4d14c..0a9697b8 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -71,10 +71,6 @@ Exécutez ces commandes :
71 ninja -C build 71 ninja -C build
72 sudo ninja -C build install 72 sudo ninja -C build install
73 73
74Sur les systèmes avec logind, vous devez définir quelques caps sur le binaire :
75
76 sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway
77
78Sur les systèmes sans logind, vous devez suid le binaire de sway : 74Sur les systèmes sans logind, vous devez suid le binaire de sway :
79 75
80 sudo chmod a+s /usr/local/bin/sway 76 sudo chmod a+s /usr/local/bin/sway
diff --git a/README.it.md b/README.it.md
index 3b1b1ebc..653e6aea 100644
--- a/README.it.md
+++ b/README.it.md
@@ -72,10 +72,6 @@ Esegui questi comandi:
72 ninja -C build 72 ninja -C build
73 sudo ninja -C build install 73 sudo ninja -C build install
74 74
75Per i sistemi con logind, devi impostare un paio di caps sull'eseguibile:
76
77 sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway
78
79Per i sistemi senza logind, devi cambiare i permessi (suid): 75Per i sistemi senza logind, devi cambiare i permessi (suid):
80 76
81 sudo chmod a+s /usr/local/bin/sway 77 sudo chmod a+s /usr/local/bin/sway
diff --git a/README.ja.md b/README.ja.md
index 7b437966..75d29c73 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -62,14 +62,12 @@ _\*\*swaylockでのみ必要です_
62 ninja -C build 62 ninja -C build
63 sudo ninja -C build install 63 sudo ninja -C build install
64 64
65logindを使用しているシステムでは、バイナリにいくつかのケーパビリティを設定する必要があります:
66
67 sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway
68
69logindを使用していないシステムでは、バイナリにsuidを設定する必要があります: 65logindを使用していないシステムでは、バイナリにsuidを設定する必要があります:
70 66
71 sudo chmod a+s /usr/local/bin/sway 67 sudo chmod a+s /usr/local/bin/sway
72 68
69swayは起動後、すぐにroot許可を落とします。
70
73## 設定 71## 設定
74 72
75既にi3を使用している場合は、i3の設定ファイルを`~/.config/sway/config`にコピーすれば動きます。そうでない場合は、サンプルの設定ファイルを`~/.config/sway/config`にコピーしてください。サンプルの設定ファイルは、通常`/etc/sway/config`にあります。`man 5 sway`を実行することで、設定に関する情報を見ることができます。 73既にi3を使用している場合は、i3の設定ファイルを`~/.config/sway/config`にコピーすれば動きます。そうでない場合は、サンプルの設定ファイルを`~/.config/sway/config`にコピーしてください。サンプルの設定ファイルは、通常`/etc/sway/config`にあります。`man 5 sway`を実行することで、設定に関する情報を見ることができます。
diff --git a/README.md b/README.md
index ce753265..0bad8343 100644
--- a/README.md
+++ b/README.md
@@ -72,14 +72,12 @@ Run these commands:
72 ninja -C build 72 ninja -C build
73 sudo ninja -C build install 73 sudo ninja -C build install
74 74
75On systems with logind, you need to set a few caps on the binary:
76
77 sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway
78
79On systems without logind, you need to suid the sway binary: 75On systems without logind, you need to suid the sway binary:
80 76
81 sudo chmod a+s /usr/local/bin/sway 77 sudo chmod a+s /usr/local/bin/sway
82 78
79Sway will drop root permissions shortly after startup.
80
83## Configuration 81## Configuration
84 82
85If you already use i3, then copy your i3 config to `~/.config/sway/config` and 83If you already use i3, then copy your i3 config to `~/.config/sway/config` and
diff --git a/common/ipc-client.c b/common/ipc-client.c
index 4d2d88cc..24a2f9c2 100644
--- a/common/ipc-client.c
+++ b/common/ipc-client.c
@@ -25,6 +25,7 @@ char *get_socketpath(void) {
25 if (line && *line) { 25 if (line && *line) {
26 return line; 26 return line;
27 } 27 }
28 free(line);
28 } 29 }
29 const char *i3sock = getenv("I3SOCK"); 30 const char *i3sock = getenv("I3SOCK");
30 if (i3sock) { 31 if (i3sock) {
@@ -37,6 +38,7 @@ char *get_socketpath(void) {
37 if (line && *line) { 38 if (line && *line) {
38 return line; 39 return line;
39 } 40 }
41 free(line);
40 } 42 }
41 return NULL; 43 return NULL;
42} 44}
diff --git a/include/sway/decoration.h b/include/sway/decoration.h
new file mode 100644
index 00000000..7916746e
--- /dev/null
+++ b/include/sway/decoration.h
@@ -0,0 +1,17 @@
1#ifndef _SWAY_DECORATION_H
2#define _SWAY_DECORATION_H
3
4#include <wlr/types/wlr_server_decoration.h>
5
6struct sway_server_decoration {
7 struct wlr_server_decoration *wlr_server_decoration;
8 struct wl_list link;
9
10 struct wl_listener destroy;
11 struct wl_listener mode;
12};
13
14struct sway_server_decoration *decoration_from_surface(
15 struct wlr_surface *surface);
16
17#endif
diff --git a/include/sway/server.h b/include/sway/server.h
index a3782f91..b93584b6 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -4,12 +4,13 @@
4#include <wayland-server.h> 4#include <wayland-server.h>
5#include <wlr/backend.h> 5#include <wlr/backend.h>
6#include <wlr/backend/session.h> 6#include <wlr/backend/session.h>
7#include <wlr/render/wlr_renderer.h>
7#include <wlr/types/wlr_compositor.h> 8#include <wlr/types/wlr_compositor.h>
8#include <wlr/types/wlr_data_device.h> 9#include <wlr/types/wlr_data_device.h>
9#include <wlr/types/wlr_layer_shell.h> 10#include <wlr/types/wlr_layer_shell.h>
11#include <wlr/types/wlr_server_decoration.h>
10#include <wlr/types/wlr_xdg_shell_v6.h> 12#include <wlr/types/wlr_xdg_shell_v6.h>
11#include <wlr/types/wlr_xdg_shell.h> 13#include <wlr/types/wlr_xdg_shell.h>
12#include <wlr/render/wlr_renderer.h>
13// TODO WLR: make Xwayland optional 14// TODO WLR: make Xwayland optional
14#include "list.h" 15#include "list.h"
15#include "config.h" 16#include "config.h"
@@ -42,11 +43,17 @@ struct sway_server {
42 43
43 struct wlr_xdg_shell *xdg_shell; 44 struct wlr_xdg_shell *xdg_shell;
44 struct wl_listener xdg_shell_surface; 45 struct wl_listener xdg_shell_surface;
46
45#ifdef HAVE_XWAYLAND 47#ifdef HAVE_XWAYLAND
46 struct sway_xwayland xwayland; 48 struct sway_xwayland xwayland;
47 struct wl_listener xwayland_surface; 49 struct wl_listener xwayland_surface;
48 struct wl_listener xwayland_ready; 50 struct wl_listener xwayland_ready;
49#endif 51#endif
52
53 struct wlr_server_decoration_manager *server_decoration_manager;
54 struct wl_listener server_decoration;
55 struct wl_list decorations; // sway_server_decoration::link
56
50 bool debug_txn_timings; 57 bool debug_txn_timings;
51 58
52 list_t *transactions; 59 list_t *transactions;
@@ -71,4 +78,6 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
71#ifdef HAVE_XWAYLAND 78#ifdef HAVE_XWAYLAND
72void handle_xwayland_surface(struct wl_listener *listener, void *data); 79void handle_xwayland_surface(struct wl_listener *listener, void *data);
73#endif 80#endif
81void handle_server_decoration(struct wl_listener *listener, void *data);
82
74#endif 83#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 44ff9f7d..4d0e6003 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -113,7 +113,7 @@ struct sway_container {
113 113
114 enum sway_container_type type; 114 enum sway_container_type type;
115 enum sway_container_layout layout; 115 enum sway_container_layout layout;
116 enum sway_container_layout prev_layout; 116 enum sway_container_layout prev_split_layout;
117 117
118 bool is_sticky; 118 bool is_sticky;
119 119
@@ -323,12 +323,23 @@ void container_floating_translate(struct sway_container *con,
323 double x_amount, double y_amount); 323 double x_amount, double y_amount);
324 324
325/** 325/**
326 * Choose an output for the floating container's new position.
327 */
328struct sway_container *container_floating_find_output(
329 struct sway_container *con);
330
331/**
326 * Move a floating container to a new layout-local position. 332 * Move a floating container to a new layout-local position.
327 */ 333 */
328void container_floating_move_to(struct sway_container *con, 334void container_floating_move_to(struct sway_container *con,
329 double lx, double ly); 335 double lx, double ly);
330 336
331/** 337/**
338 * Move a floating container to the center of the workspace.
339 */
340void container_floating_move_to_center(struct sway_container *con);
341
342/**
332 * Mark a container as dirty if it isn't already. Dirty containers will be 343 * Mark a container as dirty if it isn't already. Dirty containers will be
333 * included in the next transaction then unmarked as dirty. 344 * included in the next transaction then unmarked as dirty.
334 */ 345 */
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 37fd02bc..c2225bcb 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -118,6 +118,8 @@ struct sway_view {
118struct sway_xdg_shell_v6_view { 118struct sway_xdg_shell_v6_view {
119 struct sway_view view; 119 struct sway_view view;
120 120
121 enum wlr_server_decoration_manager_mode deco_mode;
122
121 struct wl_listener commit; 123 struct wl_listener commit;
122 struct wl_listener request_move; 124 struct wl_listener request_move;
123 struct wl_listener request_resize; 125 struct wl_listener request_resize;
@@ -134,6 +136,8 @@ struct sway_xdg_shell_v6_view {
134struct sway_xdg_shell_view { 136struct sway_xdg_shell_view {
135 struct sway_view view; 137 struct sway_view view;
136 138
139 enum wlr_server_decoration_manager_mode deco_mode;
140
137 struct wl_listener commit; 141 struct wl_listener commit;
138 struct wl_listener request_move; 142 struct wl_listener request_move;
139 struct wl_listener request_resize; 143 struct wl_listener request_resize;
@@ -316,6 +320,11 @@ void view_update_title(struct sway_view *view, bool force);
316void view_execute_criteria(struct sway_view *view); 320void view_execute_criteria(struct sway_view *view);
317 321
318/** 322/**
323 * Find any view that has the given mark and return it.
324 */
325struct sway_view *view_find_mark(char *mark);
326
327/**
319 * Find any view that has the given mark and remove the mark from the view. 328 * Find any view that has the given mark and remove the mark from the view.
320 * Returns true if it matched a view. 329 * Returns true if it matched a view.
321 */ 330 */
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index 3337f2c8..056f2329 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -1,6 +1,7 @@
1#ifndef _SWAY_WORKSPACE_H 1#ifndef _SWAY_WORKSPACE_H
2#define _SWAY_WORKSPACE_H 2#define _SWAY_WORKSPACE_H
3 3
4#include <stdbool.h>
4#include "sway/tree/container.h" 5#include "sway/tree/container.h"
5 6
6struct sway_view; 7struct sway_view;
@@ -15,9 +16,12 @@ struct sway_workspace {
15 16
16extern char *prev_workspace_name; 17extern char *prev_workspace_name;
17 18
19struct sway_container *workspace_get_initial_output(const char *name);
20
18char *workspace_next_name(const char *output_name); 21char *workspace_next_name(const char *output_name);
19 22
20bool workspace_switch(struct sway_container *workspace); 23bool workspace_switch(struct sway_container *workspace,
24 bool no_auto_back_and_forth);
21 25
22struct sway_container *workspace_by_number(const char* name); 26struct sway_container *workspace_by_number(const char* name);
23 27
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
9static 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
24static 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
8struct cmd_results *cmd_layout(int argc, char **argv) { 29struct 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
19static const char* expected_syntax = 22static 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
25static struct sway_container *output_in_direction(const char *direction, 28static 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
217static 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
155static struct cmd_results *cmd_move_workspace(struct sway_container *current, 257static 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
245static const char* expected_position_syntax = 356static 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
249static struct cmd_results *move_to_position(struct sway_container *container, 360static 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
7static 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
17static 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
43void 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
62struct 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
174static 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
173static void for_each_surface(struct sway_view *view, 183static 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
16static const struct sway_view_child_impl popup_impl; 17static 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
170static 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
169static void for_each_surface(struct sway_view *view, 179static 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 @@
1sway_sources = files( 1sway_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
536static struct sway_container *container_at_view(struct sway_container *swayc, 536static 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 */
1188static struct sway_container *container_floating_find_output( 1183struct 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
1235void 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
1242void container_set_dirty(struct sway_container *container) { 1246void 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
143void container_move_to(struct sway_container *container, 143void 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
901struct 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
894bool view_find_and_unmark(char *mark) { 910bool 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
21static struct sway_container *get_workspace_initial_output(const char *name) { 21struct 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) {
48struct sway_container *workspace_create(struct sway_container *output, 48struct 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
367bool workspace_switch(struct sway_container *workspace) { 371bool 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