aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ISSUE_TEMPLATE.md34
-rw-r--r--README.ja.md22
-rw-r--r--README.md23
-rw-r--r--common/util.c68
-rw-r--r--completions/fish/swayidle.fish3
-rw-r--r--completions/fish/swaylock.fish38
-rw-r--r--completions/fish/swaymsg.fish17
-rw-r--r--completions/fish/swaynag.fish29
-rw-r--r--include/sway/output.h4
-rw-r--r--include/sway/tree/container.h51
-rw-r--r--include/sway/tree/view.h44
-rw-r--r--include/util.h20
-rw-r--r--meson.build2
-rw-r--r--sway/commands/border.c60
-rw-r--r--sway/commands/client.c4
-rw-r--r--sway/commands/focus.c109
-rw-r--r--sway/commands/mark.c19
-rw-r--r--sway/commands/move.c76
-rw-r--r--sway/commands/reload.c4
-rw-r--r--sway/commands/show_marks.c4
-rw-r--r--sway/commands/swap.c4
-rw-r--r--sway/commands/unmark.c41
-rw-r--r--sway/criteria.c5
-rw-r--r--sway/desktop/output.c17
-rw-r--r--sway/desktop/render.c30
-rw-r--r--sway/desktop/transaction.c16
-rw-r--r--sway/input/cursor.c15
-rw-r--r--sway/input/input-manager.c1
-rw-r--r--sway/input/seat.c6
-rw-r--r--sway/ipc-json.c38
-rw-r--r--sway/ipc-server.c8
-rw-r--r--sway/main.c29
-rw-r--r--sway/tree/container.c170
-rw-r--r--sway/tree/output.c22
-rw-r--r--sway/tree/view.c216
-rw-r--r--sway/tree/workspace.c10
-rw-r--r--swayidle/main.c214
-rw-r--r--swayidle/swayidle.1.scd14
-rw-r--r--swaylock/swaylock.1.scd26
-rw-r--r--swaymsg/main.c13
40 files changed, 800 insertions, 726 deletions
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..5f3bb6bb
--- /dev/null
+++ b/ISSUE_TEMPLATE.md
@@ -0,0 +1,34 @@
1If you are using the nvidia proprietary driver for any reason, you have two choices:
2
31. Uninstall it and use nouveau instead
42. Use X11+i3 and close your browser tab
5
6If `lsmod | grep nvidia | wc -l` shows anything other than zero, your bug report is not welcome here.
7
8Otherwise, please include the following four components in your bug report: sway version, debug log, configuration (if applicable), and an explanation of steps taken to reproduce the issue.
9
10Obtain your version like so:
11
12 swaymsg -t get_version
13
14If this doesn't work, use:
15
16 sway -v
17
18* Sway Version:
19
20Obtain a debug log like so:
21
22 sway -d 2> ~/sway.log
23
24This will record information about sway's activity when it's running. Briefly reproduce your problem and exit sway. When preparing a debug log, brevity is important - start up sway, do the minimum work necessary to reproduce the error, then close sway.
25
26Upload the debug log to a pastebin service such as [gist.github.com](https://gist.github.com), and link to it below.
27
28* Debug Log:
29
30You should try to reproduce the issue with the default configuration. If you cannot, please reproduce with a minimal configuration, upload the config to a pastebin service, and link to it below.
31
32* Configuration File:
33
34Finally, explain the steps you took in plain English to reproduce the problem below.
diff --git a/README.ja.md b/README.ja.md
index ae2301fb..b9e541f0 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -29,24 +29,22 @@ Swayは沢山のディストリビューションで提供されています。"
29 29
30次の依存パッケージをインストールしてください: 30次の依存パッケージをインストールしてください:
31 31
32* meson 32* meson \*
33* [wlc](https://github.com/Cloudef/wlc) 33* [wlroots](https://github.com/swaywm/wlroots)
34* wayland 34* wayland
35* xwayland 35* wayland-protocols \*
36* libinput >= 1.6.0
37* libcap
38* pcre 36* pcre
39* json-c >= 0.13 37* json-c
40* pango 38* pango
41* cairo 39* cairo
42* gdk-pixbuf2 * 40* gdk-pixbuf2 \*\*
43* pam ** 41* pam (オプション: swaylockとPAMで必要)
44* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (manで必要です) 42* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (manで必要です) \*
45* git 43* git \*
46 44
47_\*swaybar,swaybg,swaylock必要す_ 45_\*_
48 46
49_\*\*swaylockでのみ必要です_ 47_\*\*オプション: swaybgとswaylockでのみ必要です_
50 48
51次のコマンドを実行してください: 49次のコマンドを実行してください:
52 50
diff --git a/README.md b/README.md
index ca47b876..6cb212fc 100644
--- a/README.md
+++ b/README.md
@@ -32,27 +32,22 @@ channel or shoot an email to sir@cmpwn.com for advice.
32 32
33Install dependencies: 33Install dependencies:
34 34
35* meson 35* meson \*
36* [wlroots](https://github.com/swaywm/wlroots) 36* [wlroots](https://github.com/swaywm/wlroots)
37* wayland 37* wayland
38* xwayland 38* wayland-protocols \*
39* libinput >= 1.6.0
40* libcap
41* pcre 39* pcre
42* json-c >= 0.13 40* json-c
43* pango 41* pango
44* cairo 42* cairo
45* gdk-pixbuf2 * 43* gdk-pixbuf2 \*\*
46* pam ** 44* pam (optional: PAM support for swaylock)
47* dbus >= 1.10 *** 45* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional: man pages) \*
48* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) 46* git \*
49* git
50 47
51_\*Only required for swaybar, swaybg, and swaylock_ 48_\*Compile-time dep_
52 49
53_\*\*Only required for swaylock_ 50_\*\*optional: required for swaybg and swaylock_
54
55_\*\*\*Only required for tray support_
56 51
57Run these commands: 52Run these commands:
58 53
diff --git a/common/util.c b/common/util.c
index 561b3804..f4588b57 100644
--- a/common/util.c
+++ b/common/util.c
@@ -1,4 +1,5 @@
1#define _XOPEN_SOURCE 700 1#define _XOPEN_SOURCE 700
2#include <assert.h>
2#include <sys/types.h> 3#include <sys/types.h>
3#include <sys/stat.h> 4#include <sys/stat.h>
4#include <unistd.h> 5#include <unistd.h>
@@ -139,60 +140,17 @@ bool parse_boolean(const char *boolean, bool current) {
139 return false; 140 return false;
140} 141}
141 142
142char* resolve_path(const char* path) { 143enum wlr_direction opposite_direction(enum wlr_direction d) {
143 struct stat sb; 144 switch (d) {
144 ssize_t r; 145 case WLR_DIRECTION_UP:
145 int i; 146 return WLR_DIRECTION_DOWN;
146 char *current = NULL; 147 case WLR_DIRECTION_DOWN:
147 char *resolved = NULL; 148 return WLR_DIRECTION_UP;
148 149 case WLR_DIRECTION_RIGHT:
149 if(!(current = strdup(path))) { 150 return WLR_DIRECTION_LEFT;
150 return NULL; 151 case WLR_DIRECTION_LEFT:
151 } 152 return WLR_DIRECTION_RIGHT;
152 for (i = 0; i < 16; ++i) {
153 if (lstat(current, &sb) == -1) {
154 goto failed;
155 }
156 if((sb.st_mode & S_IFMT) != S_IFLNK) {
157 return current;
158 }
159 if (!(resolved = malloc(sb.st_size + 1))) {
160 goto failed;
161 }
162 r = readlink(current, resolved, sb.st_size);
163 if (r == -1 || r > sb.st_size) {
164 goto failed;
165 }
166 resolved[r] = '\0';
167 free(current);
168 current = strdup(resolved);
169 free(resolved);
170 resolved = NULL;
171 } 153 }
172 154 assert(false);
173failed: 155 return 0;
174 free(resolved);
175 free(current);
176 return NULL;
177}
178
179bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out) {
180 switch (dir) {
181 case MOVE_UP:
182 *out = WLR_DIRECTION_UP;
183 break;
184 case MOVE_DOWN:
185 *out = WLR_DIRECTION_DOWN;
186 break;
187 case MOVE_LEFT:
188 *out = WLR_DIRECTION_LEFT;
189 break;
190 case MOVE_RIGHT:
191 *out = WLR_DIRECTION_RIGHT;
192 break;
193 default:
194 return false;
195 }
196
197 return true;
198} 156}
diff --git a/completions/fish/swayidle.fish b/completions/fish/swayidle.fish
new file mode 100644
index 00000000..71279925
--- /dev/null
+++ b/completions/fish/swayidle.fish
@@ -0,0 +1,3 @@
1# swayidle
2complete -c swayidle -s h --description 'show help'
3complete -c swayidle -s d --description 'debug'
diff --git a/completions/fish/swaylock.fish b/completions/fish/swaylock.fish
index 965a22d2..99dff48f 100644
--- a/completions/fish/swaylock.fish
+++ b/completions/fish/swaylock.fish
@@ -1,11 +1,39 @@
1# swaylock(1) completion 1# swaylock(1) completion
2 2
3complete -c swaylock -s C -l config --description 'The config file to use. Default: $HOME/.swaylock/config, $XDG_CONFIG_HOME/swaylock/config, and SYSCONFDIR/swaylock/config.'
3complete -c swaylock -s h -l help --description "Show help message and quit." 4complete -c swaylock -s h -l help --description "Show help message and quit."
4complete -c swaylock -s c -l color --description "Turn the screen into the given color. If -i is used, this sets the background of the image into the given color. Defaults to white (ffffff), or transparent (00000000) if an image is in use."
5complete -c swaylock -s f -l daemonize --description "Fork into the background after spawning. Note: this is the default bahavior of i3lock." 5complete -c swaylock -s f -l daemonize --description "Fork into the background after spawning. Note: this is the default bahavior of i3lock."
6complete -c swaylock -s v -l version --description "Show the version number and quit."
7complete -c swaylock -s s -l socket --description "Use the specified socket path. Otherwise, swaymsg will as sway where the socket is (which is the value of $SWAYSOCK, then of $I350CK)."
8complete -c swaylock -s e -l ignore-empty-password --description 'When an empty password is provided by the user, do not validate it.'
9
10# Appearance
11complete -c swaylock -s u -l no-unlock-indicator --description "Disable the unlock indicator."
6complete -c swaylock -s i -l image --description "Display the given image, optionally on the given output. Use -c to set a background color." 12complete -c swaylock -s i -l image --description "Display the given image, optionally on the given output. Use -c to set a background color."
7complete -c swaylock -l scaling --description "Scaling mode for images: stretch, fill, fit, center, or tile." 13complete -c swaylock -s s -l scaling --description "Scaling mode for images: stretch, fill, fit, center, or tile."
8complete -c swaylock -s t -l tiling --description "Same as --scaling=tile." 14complete -c swaylock -s t -l tiling --description "Same as --scaling=tile."
9complete -c swaylock -s u -l no-unlock-indicator --description "Disable the unlock indicator." 15complete -c swaylock -s c -l color --description "Turn the screen into the given color. If -i is used, this sets the background of the image into the given color. Defaults to white (ffffff), or transparent (00000000) if an image is in use."
10complete -c swaylock -s v -l version --description "Show the version number and quit." 16complete -c swaylock -l bs-hl-color --description 'Sets the color of backspace highlight segments.'
11complete -c swaylock -l socket --description "Use the specified socket path. Othherwise, swaymsg will as sway where the socket is (which is the value of $SWAYSOCK, then of $I350CK)." 17complete -c swaylock -l font --description 'Sets the font of the text inside the indicator.'
18complete -c swaylock -l indicator-radius --description 'Sets the radius of the indicator to radius pixels. Default: 50'
19complete -c swaylock -l indicator-thickness --description 'Sets the thickness of the indicator to thickness pixels. Default: 10'
20complete -c swaylock -l inside-color --description 'Sets the color of the inside of the indicator when typing or idle.'
21complete -c swaylock -l inside-clear-color --description 'Sets the color of the inside of the indicator when cleared.'
22complete -c swaylock -l inside-ver-color --description 'Sets the color of the inside of the indicator when verifying.'
23complete -c swaylock -l inside-wrong-color --description 'Sets the color of the inside of the indicator when invalid.'
24complete -c swaylock -l key-hl-color --description 'Sets the color of key press highlight segments.'
25complete -c swaylock -l line-color --description 'Sets the color of the lines that separate the inside and outside of the indicator when typing or idle.'
26complete -c swaylock -l line-clear-color --description 'Sets the color of the lines that separate the inside and outside of the indicator when cleared.'
27complete -c swaylock -l line-ver-color --description 'Sets the color of the lines that separate the inside and outside of the indicator when verifying.'
28complete -c swaylock -l line-wrong-color --description 'Sets the color of the lines that separate the inside and outside of the indicator when invalid.'
29complete -c swaylock -s n -l line-uses-inside --description 'Use the color of the inside of the indicator for the line separating the inside and outside of the indicator.'
30complete -c swaylock -s r -l line-uses-ring --description 'Use the outer ring\'s color for the line separating the inside and outside of the indicator.'
31complete -c swaylock -l ring-color --description 'Sets the color of the outside of the indicator when typing or idle.'
32complete -c swaylock -l ring-clear-color --description 'Sets the color of the outside of the indicator when cleared.'
33complete -c swaylock -l ring-ver-color --description 'Sets the color of the outside of the indicator when verifying.'
34complete -c swaylock -l ring-wrong-color --description 'Sets the color of the outside of the indicator when invalid.'
35complete -c swaylock -l separator-color --description 'Sets the color of the lines that separate highlight segments.'
36complete -c swaylock -l text-color --description 'Sets the color of the text inside the indicator when typing or idle.'
37complete -c swaylock -l text-clear-color --description 'Sets the color of the text inside the indicator when cleared.'
38complete -c swaylock -l text-ver-color --description 'Sets the color of the text inside the indicator when verifying.'
39complete -c swaylock -l text-wrong-color --description 'Sets the color of the text inside the indicator when invalid.'
diff --git a/completions/fish/swaymsg.fish b/completions/fish/swaymsg.fish
index e798db77..1e5bf3da 100644
--- a/completions/fish/swaymsg.fish
+++ b/completions/fish/swaymsg.fish
@@ -2,7 +2,18 @@
2 2
3complete -c swaymsg -s h -l help --description "Show help message and quit." 3complete -c swaymsg -s h -l help --description "Show help message and quit."
4complete -c swaymsg -s q -l quiet --description "Sends the IPC message but does not print the response from sway." 4complete -c swaymsg -s q -l quiet --description "Sends the IPC message but does not print the response from sway."
5complete -c swaymsg -s r -l raw --description "Use raw output even if using tty."
6complete -c swaymsg -s s -l socket --description "Use the specified socket path. Otherwise, swaymsg will ask where the socket is (which is the value of $SWAYSOCK, then of $I3SOCK)."
7complete -c swaymsg -s t -l type --description "Specify the type of IPC message."
8complete -c swaymsg -s v -l version --description "Print the version (of swaymsg) and quit." 5complete -c swaymsg -s v -l version --description "Print the version (of swaymsg) and quit."
6complete -c swaymsg -s r -l raw --description "Use raw output even if using tty."
7complete -c swaymsg -s s -l socket -r --description "Use the specified socket path. Otherwise, swaymsg will ask where the socket is (which is the value of $SWAYSOCK, then of $I3SOCK)."
8
9complete -c swaymsg -s t -l type -fr --description "Specify the type of IPC message."
10complete -c swaymsg -s t -l type -fra 'get_workspaces' --description "Gets a JSON-encoded list of workspaces and their status."
11complete -c swaymsg -s t -l type -fra 'get_inputs' --description "Gets a JSON-encoded list of current inputs."
12complete -c swaymsg -s t -l type -fra 'get_outputs' --description "Gets a JSON-encoded list of current outputs."
13complete -c swaymsg -s t -l type -fra 'get_tree' --description "Gets a JSON-encoded layout tree of all open windows, containers, outputs, workspaces, and so on."
14complete -c swaymsg -s t -l type -fra 'get_marks' --description "Get a JSON-encoded list of marks."
15complete -c swaymsg -s t -l type -fra 'get_bar_config' --description "Get a JSON-encoded configuration for swaybar."
16complete -c swaymsg -s t -l type -fra 'get_version' --description "Get JSON-encoded version information for the running instance of sway."
17complete -c swaymsg -s t -l type -fra 'get_binding_modes' --description "Gets a JSON-encoded list of currently configured binding modes."
18complete -c swaymsg -s t -l type -fra 'get_config' --description "Gets a JSON-encoded copy of the current configuration."
19complete -c swaymsg -s t -l type -fra 'send_tick' --description "Sends a tick event to all subscribed clients."
diff --git a/completions/fish/swaynag.fish b/completions/fish/swaynag.fish
new file mode 100644
index 00000000..784d7fad
--- /dev/null
+++ b/completions/fish/swaynag.fish
@@ -0,0 +1,29 @@
1# swaynag
2complete -c swaynag -s C -l config --description 'The config file to use. Default: $HOME/.swaylock/config, $XDG_CONFIG_HOME/swaylock/config, and SYSCONFDIR/swaylock/config.'
3complete -c swaynag -s d -l debug --description 'Enable debugging.'
4complete -c swaynag -s e -l edge --description 'Set the edge to use: top or bottom'
5complete -c swaynag -s f -l font --description 'Set the font to use.'
6complete -c swaynag -s h -l help --description 'Show help message and quit.'
7complete -c swaynag -s b -l button --description 'Create a button with a text and an action which is executed when pressed. Multiple buttons can be defined by providing the flag multiple times.'
8complete -c swaynag -s l -l detailed-message --description 'Read a detailed message from stdin. A button to toggle details will be added. Details are shown in a scrollable multi-line text area.'
9complete -c swaynag -s L -l detailed-button --description 'Set the text for the button that toggles details. This has no effect if there is not a detailed message. The default is "Toggle details".'
10complete -c swaynag -s m -l message --description 'Set the message text.'
11complete -c swaynag -s o -l output --description 'Set the output to use.'
12complete -c swaynag -s s -l dismiss-button --description 'Sets the text for the dismiss nagbar button. The default is "X".'
13complete -c swaynag -s t -l type --description 'Set the message type. Two types are created by default "error" and "warning". Custom types can be defined in the config file.'
14complete -c swaynag -s v -l version --description 'Show the version number and quit.'
15
16# Appearance
17complete -c swaynag -l background --description 'Set the color of the background.'
18complete -c swaynag -l border --description 'Set the color of the border.'
19complete -c swaynag -l border-bottom --description 'Set the color of the bottom border.'
20complete -c swaynag -l button-background --description 'Set the color for the background for buttons.'
21complete -c swaynag -l text --description 'Set the text color.'
22complete -c swaynag -l border-bottom-size --description 'Set the thickness of the bottom border.'
23complete -c swaynag -l message-padding --description 'Set the padding for the message.'
24complete -c swaynag -l details-border-size --description 'Set the thickness for the details border.'
25complete -c swaynag -l button-border-size --description 'Set the thickness for the button border.'
26complete -c swaynag -l button-gap --description 'Set the size of the gap between buttons.'
27complete -c swaynag -l button-dismiss-gap --description 'Set the size of the gap between the dismiss button and another button.'
28complete -c swaynag -l button-margin-right --description 'Set the margin from the right of the dismiss button to edge.'
29complete -c swaynag -l button-padding --description 'Set the padding for the button text.'
diff --git a/include/sway/output.h b/include/sway/output.h
index 369e62ce..5efe1660 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -62,7 +62,7 @@ void output_begin_destroy(struct sway_output *output);
62struct sway_output *output_from_wlr_output(struct wlr_output *output); 62struct sway_output *output_from_wlr_output(struct wlr_output *output);
63 63
64struct sway_output *output_get_in_direction(struct sway_output *reference, 64struct sway_output *output_get_in_direction(struct sway_output *reference,
65 enum movement_direction direction); 65 enum wlr_direction direction);
66 66
67void output_add_workspace(struct sway_output *output, 67void output_add_workspace(struct sway_output *output,
68 struct sway_workspace *workspace); 68 struct sway_workspace *workspace);
@@ -86,6 +86,8 @@ void output_damage_whole_container(struct sway_output *output,
86 86
87struct sway_output *output_by_name(const char *name); 87struct sway_output *output_by_name(const char *name);
88 88
89struct sway_output *output_by_identifier(const char *identifier);
90
89void output_sort_workspaces(struct sway_output *output); 91void output_sort_workspaces(struct sway_output *output);
90 92
91struct output_config *output_find_config(struct sway_output *output); 93struct output_config *output_find_config(struct sway_output *output);
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 920ef038..4366a010 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -36,7 +36,6 @@ struct sway_output;
36struct sway_workspace; 36struct sway_workspace;
37struct sway_view; 37struct sway_view;
38 38
39enum movement_direction;
40enum wlr_direction; 39enum wlr_direction;
41 40
42struct sway_container_state { 41struct sway_container_state {
@@ -54,16 +53,16 @@ struct sway_container_state {
54 struct sway_container *focused_inactive_child; 53 struct sway_container *focused_inactive_child;
55 bool focused; 54 bool focused;
56 55
57 // View properties
58 double view_x, view_y;
59 double view_width, view_height;
60
61 enum sway_container_border border; 56 enum sway_container_border border;
62 int border_thickness; 57 int border_thickness;
63 bool border_top; 58 bool border_top;
64 bool border_bottom; 59 bool border_bottom;
65 bool border_left; 60 bool border_left;
66 bool border_right; 61 bool border_right;
62
63 // View properties
64 double view_x, view_y;
65 double view_width, view_height;
67}; 66};
68 67
69struct sway_container { 68struct sway_container {
@@ -92,6 +91,18 @@ struct sway_container {
92 91
93 bool is_fullscreen; 92 bool is_fullscreen;
94 93
94 enum sway_container_border border;
95
96 // Used when the view changes to CSD unexpectedly. This will be a non-B_CSD
97 // border which we use to restore when the view returns to SSD.
98 enum sway_container_border saved_border;
99
100 int border_thickness;
101 bool border_top;
102 bool border_bottom;
103 bool border_left;
104 bool border_right;
105
95 // The gaps currently applied to the container. 106 // The gaps currently applied to the container.
96 double current_gaps; 107 double current_gaps;
97 108
@@ -116,6 +127,12 @@ struct sway_container {
116 size_t title_height; 127 size_t title_height;
117 size_t title_baseline; 128 size_t title_baseline;
118 129
130 list_t *marks; // char *
131 struct wlr_texture *marks_focused;
132 struct wlr_texture *marks_focused_inactive;
133 struct wlr_texture *marks_unfocused;
134 struct wlr_texture *marks_urgent;
135
119 struct { 136 struct {
120 struct wl_signal destroy; 137 struct wl_signal destroy;
121 } events; 138 } events;
@@ -287,12 +304,32 @@ void container_detach(struct sway_container *child);
287void container_replace(struct sway_container *container, 304void container_replace(struct sway_container *container,
288 struct sway_container *replacement); 305 struct sway_container *replacement);
289 306
290bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out);
291
292struct sway_container *container_split(struct sway_container *child, 307struct sway_container *container_split(struct sway_container *child,
293 enum sway_container_layout layout); 308 enum sway_container_layout layout);
294 309
295bool container_is_transient_for(struct sway_container *child, 310bool container_is_transient_for(struct sway_container *child,
296 struct sway_container *ancestor); 311 struct sway_container *ancestor);
297 312
313/**
314 * Find any container that has the given mark and return it.
315 */
316struct sway_container *container_find_mark(char *mark);
317
318/**
319 * Find any container that has the given mark and remove the mark from the
320 * container. Returns true if it matched a container.
321 */
322bool container_find_and_unmark(char *mark);
323
324/**
325 * Remove all marks from the container.
326 */
327void container_clear_marks(struct sway_container *container);
328
329bool container_has_mark(struct sway_container *container, char *mark);
330
331void container_add_mark(struct sway_container *container, char *mark);
332
333void container_update_marks_textures(struct sway_container *container);
334
298#endif 335#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 0240f294..4a8c3cb1 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -80,24 +80,8 @@ struct sway_view {
80 80
81 char *title_format; 81 char *title_format;
82 82
83 // Our border types are B_NONE, B_PIXEL, B_NORMAL and B_CSD. We normally
84 // just assign this to the border property and ignore the other two.
85 // However, when a view using CSD is tiled, we want to render our own
86 // borders as well. So in this case the border property becomes one of the
87 // first three, and using_csd is true.
88 // Lastly, views can change their decoration mode at any time. When an SSD
89 // view becomes CSD without our approval, we save the SSD border type so it
90 // can be restored if/when the view returns from CSD to SSD.
91 enum sway_container_border border;
92 enum sway_container_border saved_border;
93 bool using_csd; 83 bool using_csd;
94 84
95 int border_thickness;
96 bool border_top;
97 bool border_bottom;
98 bool border_left;
99 bool border_right;
100
101 struct timespec urgent; 85 struct timespec urgent;
102 bool allow_request_urgent; 86 bool allow_request_urgent;
103 struct wl_event_source *urgent_timer; 87 struct wl_event_source *urgent_timer;
@@ -116,12 +100,6 @@ struct sway_view {
116 bool destroying; 100 bool destroying;
117 101
118 list_t *executed_criteria; // struct criteria * 102 list_t *executed_criteria; // struct criteria *
119 list_t *marks; // char *
120
121 struct wlr_texture *marks_focused;
122 struct wlr_texture *marks_focused_inactive;
123 struct wlr_texture *marks_unfocused;
124 struct wlr_texture *marks_urgent;
125 103
126 union { 104 union {
127 struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; 105 struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6;
@@ -369,28 +347,6 @@ void view_update_title(struct sway_view *view, bool force);
369void view_execute_criteria(struct sway_view *view); 347void view_execute_criteria(struct sway_view *view);
370 348
371/** 349/**
372 * Find any view that has the given mark and return it.
373 */
374struct sway_view *view_find_mark(char *mark);
375
376/**
377 * Find any view that has the given mark and remove the mark from the view.
378 * Returns true if it matched a view.
379 */
380bool view_find_and_unmark(char *mark);
381
382/**
383 * Remove all marks from the view.
384 */
385void view_clear_marks(struct sway_view *view);
386
387bool view_has_mark(struct sway_view *view, char *mark);
388
389void view_add_mark(struct sway_view *view, char *mark);
390
391void view_update_marks_textures(struct sway_view *view);
392
393/**
394 * Returns true if there's a possibility the view may be rendered on screen. 350 * Returns true if there's a possibility the view may be rendered on screen.
395 * Intended for damage tracking. 351 * Intended for damage tracking.
396 */ 352 */
diff --git a/include/util.h b/include/util.h
index 19d2e7cf..f143d0c0 100644
--- a/include/util.h
+++ b/include/util.h
@@ -7,15 +7,6 @@
7#include <wlr/types/wlr_output_layout.h> 7#include <wlr/types/wlr_output_layout.h>
8#include <xkbcommon/xkbcommon.h> 8#include <xkbcommon/xkbcommon.h>
9 9
10enum movement_direction {
11 MOVE_LEFT,
12 MOVE_RIGHT,
13 MOVE_UP,
14 MOVE_DOWN,
15 MOVE_PARENT,
16 MOVE_CHILD,
17};
18
19/** 10/**
20 * Wrap i into the range [0, max[ 11 * Wrap i into the range [0, max[
21 */ 12 */
@@ -68,15 +59,6 @@ uint32_t parse_color(const char *color);
68 */ 59 */
69bool parse_boolean(const char *boolean, bool current); 60bool parse_boolean(const char *boolean, bool current);
70 61
71/** 62enum wlr_direction opposite_direction(enum wlr_direction d);
72 * Given a path string, recurseively resolves any symlinks to their targets
73 * (which may be a file, directory) and returns the result.
74 * argument is returned. Caller must free the returned buffer.
75 * If an error occures, if the path does not exist or if the path corresponds
76 * to a dangling symlink, NULL is returned.
77 */
78char* resolve_path(const char* path);
79
80bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out);
81 63
82#endif 64#endif
diff --git a/meson.build b/meson.build
index 1e2b53fa..6b23b4e3 100644
--- a/meson.build
+++ b/meson.build
@@ -244,8 +244,10 @@ endif
244if (get_option('fish-completions')) 244if (get_option('fish-completions'))
245 fish_files = files( 245 fish_files = files(
246 'completions/fish/sway.fish', 246 'completions/fish/sway.fish',
247 'completions/fish/swayidle.fish',
247 'completions/fish/swaylock.fish', 248 'completions/fish/swaylock.fish',
248 'completions/fish/swaymsg.fish', 249 'completions/fish/swaymsg.fish',
250 'completions/fish/swaynag.fish',
249 ) 251 )
250 fish_install_dir = datadir + '/fish/completions' 252 fish_install_dir = datadir + '/fish/completions'
251 253
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 37047812..b6eab550 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -12,37 +12,41 @@
12// in use (we set using_csd instead and render a sway border). 12// in use (we set using_csd instead and render a sway border).
13// - view->saved_border should be the last applied border when switching to CSD. 13// - view->saved_border should be the last applied border when switching to CSD.
14// - view->using_csd should always reflect whether CSD is applied or not. 14// - view->using_csd should always reflect whether CSD is applied or not.
15static void set_border(struct sway_view *view, 15static void set_border(struct sway_container *con,
16 enum sway_container_border new_border) { 16 enum sway_container_border new_border) {
17 if (view->using_csd && new_border != B_CSD) { 17 if (con->view) {
18 view_set_csd_from_server(view, false); 18 if (con->view->using_csd && new_border != B_CSD) {
19 } else if (!view->using_csd && new_border == B_CSD) { 19 view_set_csd_from_server(con->view, false);
20 view_set_csd_from_server(view, true); 20 } else if (!con->view->using_csd && new_border == B_CSD) {
21 view->saved_border = view->border; 21 view_set_csd_from_server(con->view, true);
22 con->saved_border = con->border;
23 }
24 }
25 if (new_border != B_CSD || container_is_floating(con)) {
26 con->border = new_border;
22 } 27 }
23 if (new_border != B_CSD || container_is_floating(view->container)) { 28 if (con->view) {
24 view->border = new_border; 29 con->view->using_csd = new_border == B_CSD;
25 } 30 }
26 view->using_csd = new_border == B_CSD;
27} 31}
28 32
29static void border_toggle(struct sway_view *view) { 33static void border_toggle(struct sway_container *con) {
30 if (view->using_csd) { 34 if (con->view && con->view->using_csd) {
31 set_border(view, B_NONE); 35 set_border(con, B_NONE);
32 return; 36 return;
33 } 37 }
34 switch (view->border) { 38 switch (con->border) {
35 case B_NONE: 39 case B_NONE:
36 set_border(view, B_PIXEL); 40 set_border(con, B_PIXEL);
37 break; 41 break;
38 case B_PIXEL: 42 case B_PIXEL:
39 set_border(view, B_NORMAL); 43 set_border(con, B_NORMAL);
40 break; 44 break;
41 case B_NORMAL: 45 case B_NORMAL:
42 if (view->xdg_decoration) { 46 if (con->view && con->view->xdg_decoration) {
43 set_border(view, B_CSD); 47 set_border(con, B_CSD);
44 } else { 48 } else {
45 set_border(view, B_NONE); 49 set_border(con, B_NONE);
46 } 50 }
47 break; 51 break;
48 case B_CSD: 52 case B_CSD:
@@ -66,33 +70,33 @@ struct cmd_results *cmd_border(int argc, char **argv) {
66 struct sway_view *view = container->view; 70 struct sway_view *view = container->view;
67 71
68 if (strcmp(argv[0], "none") == 0) { 72 if (strcmp(argv[0], "none") == 0) {
69 set_border(view, B_NONE); 73 set_border(container, B_NONE);
70 } else if (strcmp(argv[0], "normal") == 0) { 74 } else if (strcmp(argv[0], "normal") == 0) {
71 set_border(view, B_NORMAL); 75 set_border(container, B_NORMAL);
72 } else if (strcmp(argv[0], "pixel") == 0) { 76 } else if (strcmp(argv[0], "pixel") == 0) {
73 set_border(view, B_PIXEL); 77 set_border(container, B_PIXEL);
74 } else if (strcmp(argv[0], "csd") == 0) { 78 } else if (strcmp(argv[0], "csd") == 0) {
75 if (!view->xdg_decoration) { 79 if (!view || !view->xdg_decoration) {
76 return cmd_results_new(CMD_INVALID, "border", 80 return cmd_results_new(CMD_INVALID, "border",
77 "This window doesn't support client side decorations"); 81 "This window doesn't support client side decorations");
78 } 82 }
79 set_border(view, B_CSD); 83 set_border(container, B_CSD);
80 } else if (strcmp(argv[0], "toggle") == 0) { 84 } else if (strcmp(argv[0], "toggle") == 0) {
81 border_toggle(view); 85 border_toggle(container);
82 } else { 86 } else {
83 return cmd_results_new(CMD_INVALID, "border", 87 return cmd_results_new(CMD_INVALID, "border",
84 "Expected 'border <none|normal|pixel|csd|toggle>' " 88 "Expected 'border <none|normal|pixel|csd|toggle>' "
85 "or 'border pixel <px>'"); 89 "or 'border pixel <px>'");
86 } 90 }
87 if (argc == 2) { 91 if (argc == 2) {
88 view->border_thickness = atoi(argv[1]); 92 container->border_thickness = atoi(argv[1]);
89 } 93 }
90 94
91 if (container_is_floating(view->container)) { 95 if (container_is_floating(container)) {
92 container_set_geometry_from_floating_view(view->container); 96 container_set_geometry_from_floating_view(container);
93 } 97 }
94 98
95 arrange_container(view->container); 99 arrange_container(container);
96 100
97 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 101 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
98} 102}
diff --git a/sway/commands/client.c b/sway/commands/client.c
index 9f54fa94..746e8713 100644
--- a/sway/commands/client.c
+++ b/sway/commands/client.c
@@ -5,9 +5,7 @@
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6 6
7static void rebuild_textures_iterator(struct sway_container *con, void *data) { 7static void rebuild_textures_iterator(struct sway_container *con, void *data) {
8 if (con->view) { 8 container_update_marks_textures(con);
9 view_update_marks_textures(con->view);
10 }
11 container_update_title_textures(con); 9 container_update_title_textures(con);
12} 10}
13 11
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 2204f722..cef92144 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -1,4 +1,5 @@
1#include <strings.h> 1#include <strings.h>
2#include <wlr/types/wlr_output_layout.h>
2#include <wlr/util/log.h> 3#include <wlr/util/log.h>
3#include "log.h" 4#include "log.h"
4#include "sway/commands.h" 5#include "sway/commands.h"
@@ -13,20 +14,16 @@
13#include "stringop.h" 14#include "stringop.h"
14#include "util.h" 15#include "util.h"
15 16
16static bool parse_movement_direction(const char *name, 17static bool parse_direction(const char *name,
17 enum movement_direction *out) { 18 enum wlr_direction *out) {
18 if (strcasecmp(name, "left") == 0) { 19 if (strcasecmp(name, "left") == 0) {
19 *out = MOVE_LEFT; 20 *out = WLR_DIRECTION_LEFT;
20 } else if (strcasecmp(name, "right") == 0) { 21 } else if (strcasecmp(name, "right") == 0) {
21 *out = MOVE_RIGHT; 22 *out = WLR_DIRECTION_RIGHT;
22 } else if (strcasecmp(name, "up") == 0) { 23 } else if (strcasecmp(name, "up") == 0) {
23 *out = MOVE_UP; 24 *out = WLR_DIRECTION_UP;
24 } else if (strcasecmp(name, "down") == 0) { 25 } else if (strcasecmp(name, "down") == 0) {
25 *out = MOVE_DOWN; 26 *out = WLR_DIRECTION_DOWN;
26 } else if (strcasecmp(name, "parent") == 0) {
27 *out = MOVE_PARENT;
28 } else if (strcasecmp(name, "child") == 0) {
29 *out = MOVE_CHILD;
30 } else { 27 } else {
31 return false; 28 return false;
32 } 29 }
@@ -38,7 +35,7 @@ static bool parse_movement_direction(const char *name,
38 * Get node in the direction of newly entered output. 35 * Get node in the direction of newly entered output.
39 */ 36 */
40static struct sway_node *get_node_in_output_direction( 37static struct sway_node *get_node_in_output_direction(
41 struct sway_output *output, enum movement_direction dir) { 38 struct sway_output *output, enum wlr_direction dir) {
42 struct sway_seat *seat = config->handler_context.seat; 39 struct sway_seat *seat = config->handler_context.seat;
43 struct sway_workspace *ws = output_get_active_workspace(output); 40 struct sway_workspace *ws = output_get_active_workspace(output);
44 if (ws->fullscreen) { 41 if (ws->fullscreen) {
@@ -48,7 +45,7 @@ static struct sway_node *get_node_in_output_direction(
48 45
49 if (ws->tiling->length > 0) { 46 if (ws->tiling->length > 0) {
50 switch (dir) { 47 switch (dir) {
51 case MOVE_LEFT: 48 case WLR_DIRECTION_LEFT:
52 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { 49 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
53 // get most right child of new output 50 // get most right child of new output
54 container = ws->tiling->items[ws->tiling->length-1]; 51 container = ws->tiling->items[ws->tiling->length-1];
@@ -56,7 +53,7 @@ static struct sway_node *get_node_in_output_direction(
56 container = seat_get_focus_inactive_tiling(seat, ws); 53 container = seat_get_focus_inactive_tiling(seat, ws);
57 } 54 }
58 break; 55 break;
59 case MOVE_RIGHT: 56 case WLR_DIRECTION_RIGHT:
60 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { 57 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
61 // get most left child of new output 58 // get most left child of new output
62 container = ws->tiling->items[0]; 59 container = ws->tiling->items[0];
@@ -64,7 +61,7 @@ static struct sway_node *get_node_in_output_direction(
64 container = seat_get_focus_inactive_tiling(seat, ws); 61 container = seat_get_focus_inactive_tiling(seat, ws);
65 } 62 }
66 break; 63 break;
67 case MOVE_UP: 64 case WLR_DIRECTION_UP:
68 if (ws->layout == L_VERT || ws->layout == L_STACKED) { 65 if (ws->layout == L_VERT || ws->layout == L_STACKED) {
69 // get most bottom child of new output 66 // get most bottom child of new output
70 container = ws->tiling->items[ws->tiling->length-1]; 67 container = ws->tiling->items[ws->tiling->length-1];
@@ -72,7 +69,7 @@ static struct sway_node *get_node_in_output_direction(
72 container = seat_get_focus_inactive_tiling(seat, ws); 69 container = seat_get_focus_inactive_tiling(seat, ws);
73 } 70 }
74 break; 71 break;
75 case MOVE_DOWN: { 72 case WLR_DIRECTION_DOWN:
76 if (ws->layout == L_VERT || ws->layout == L_STACKED) { 73 if (ws->layout == L_VERT || ws->layout == L_STACKED) {
77 // get most top child of new output 74 // get most top child of new output
78 container = ws->tiling->items[0]; 75 container = ws->tiling->items[0];
@@ -81,9 +78,6 @@ static struct sway_node *get_node_in_output_direction(
81 } 78 }
82 break; 79 break;
83 } 80 }
84 default:
85 break;
86 }
87 } 81 }
88 82
89 if (container) { 83 if (container) {
@@ -95,11 +89,8 @@ static struct sway_node *get_node_in_output_direction(
95} 89}
96 90
97static struct sway_node *node_get_in_direction(struct sway_container *container, 91static struct sway_node *node_get_in_direction(struct sway_container *container,
98 struct sway_seat *seat, enum movement_direction dir) { 92 struct sway_seat *seat, enum wlr_direction dir) {
99 if (container->is_fullscreen) { 93 if (container->is_fullscreen) {
100 if (dir == MOVE_PARENT) {
101 return NULL;
102 }
103 // Fullscreen container with a direction - go straight to outputs 94 // Fullscreen container with a direction - go straight to outputs
104 struct sway_output *output = container->workspace->output; 95 struct sway_output *output = container->workspace->output;
105 struct sway_output *new_output = output_get_in_direction(output, dir); 96 struct sway_output *new_output = output_get_in_direction(output, dir);
@@ -108,9 +99,6 @@ static struct sway_node *node_get_in_direction(struct sway_container *container,
108 } 99 }
109 return get_node_in_output_direction(new_output, dir); 100 return get_node_in_output_direction(new_output, dir);
110 } 101 }
111 if (dir == MOVE_PARENT) {
112 return node_get_parent(&container->node);
113 }
114 102
115 struct sway_container *wrap_candidate = NULL; 103 struct sway_container *wrap_candidate = NULL;
116 struct sway_container *current = container; 104 struct sway_container *current = container;
@@ -122,15 +110,15 @@ static struct sway_node *node_get_in_direction(struct sway_container *container,
122 container_parent_layout(current); 110 container_parent_layout(current);
123 list_t *siblings = container_get_siblings(current); 111 list_t *siblings = container_get_siblings(current);
124 112
125 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { 113 if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT) {
126 if (parent_layout == L_HORIZ || parent_layout == L_TABBED) { 114 if (parent_layout == L_HORIZ || parent_layout == L_TABBED) {
127 can_move = true; 115 can_move = true;
128 desired = idx + (dir == MOVE_LEFT ? -1 : 1); 116 desired = idx + (dir == WLR_DIRECTION_LEFT ? -1 : 1);
129 } 117 }
130 } else { 118 } else {
131 if (parent_layout == L_VERT || parent_layout == L_STACKED) { 119 if (parent_layout == L_VERT || parent_layout == L_STACKED) {
132 can_move = true; 120 can_move = true;
133 desired = idx + (dir == MOVE_UP ? -1 : 1); 121 desired = idx + (dir == WLR_DIRECTION_UP ? -1 : 1);
134 } 122 }
135 } 123 }
136 124
@@ -200,15 +188,25 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
200 struct sway_output *output = output_by_name(identifier); 188 struct sway_output *output = output_by_name(identifier);
201 189
202 if (!output) { 190 if (!output) {
203 enum movement_direction direction; 191 enum wlr_direction direction;
204 if (!parse_movement_direction(identifier, &direction) || 192 if (!parse_direction(identifier, &direction)) {
205 direction == MOVE_PARENT || direction == MOVE_CHILD) {
206 free(identifier); 193 free(identifier);
207 return cmd_results_new(CMD_INVALID, "focus", 194 return cmd_results_new(CMD_INVALID, "focus",
208 "There is no output with that name"); 195 "There is no output with that name");
209 } 196 }
210 struct sway_workspace *ws = seat_get_focused_workspace(seat); 197 struct sway_workspace *ws = seat_get_focused_workspace(seat);
211 output = output_get_in_direction(ws->output, direction); 198 output = output_get_in_direction(ws->output, direction);
199
200 if (!output) {
201 int center_lx = ws->output->lx + ws->output->width / 2;
202 int center_ly = ws->output->ly + ws->output->height / 2;
203 struct wlr_output *target = wlr_output_layout_farthest_output(
204 root->output_layout, opposite_direction(direction),
205 ws->output->wlr_output, center_lx, center_ly);
206 if (target) {
207 output = output_from_wlr_output(target);
208 }
209 }
212 } 210 }
213 211
214 free(identifier); 212 free(identifier);
@@ -220,6 +218,31 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
220 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 218 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
221} 219}
222 220
221static struct cmd_results *focus_parent(void) {
222 struct sway_seat *seat = config->handler_context.seat;
223 struct sway_container *con = config->handler_context.container;
224 if (!con || con->is_fullscreen) {
225 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
226 }
227 struct sway_node *parent = node_get_parent(&con->node);
228 if (parent) {
229 seat_set_focus(seat, parent);
230 seat_consider_warp_to_focus(seat);
231 }
232 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
233}
234
235static struct cmd_results *focus_child(void) {
236 struct sway_seat *seat = config->handler_context.seat;
237 struct sway_node *node = config->handler_context.node;
238 struct sway_node *focus = seat_get_active_tiling_child(seat, node);
239 if (focus) {
240 seat_set_focus(seat, focus);
241 seat_consider_warp_to_focus(seat);
242 }
243 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
244}
245
223struct cmd_results *cmd_focus(int argc, char **argv) { 246struct cmd_results *cmd_focus(int argc, char **argv) {
224 if (config->reading || !config->active) { 247 if (config->reading || !config->active) {
225 return cmd_results_new(CMD_DEFER, NULL, NULL); 248 return cmd_results_new(CMD_DEFER, NULL, NULL);
@@ -257,27 +280,21 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
257 return focus_output(seat, argc, argv); 280 return focus_output(seat, argc, argv);
258 } 281 }
259 282
260 enum movement_direction direction = 0; 283 if (strcasecmp(argv[0], "parent") == 0) {
261 if (!parse_movement_direction(argv[0], &direction)) { 284 return focus_parent();
285 }
286 if (strcasecmp(argv[0], "child") == 0) {
287 return focus_child();
288 }
289
290 enum wlr_direction direction = 0;
291 if (!parse_direction(argv[0], &direction)) {
262 return cmd_results_new(CMD_INVALID, "focus", 292 return cmd_results_new(CMD_INVALID, "focus",
263 "Expected 'focus <direction|parent|child|mode_toggle|floating|tiling>' " 293 "Expected 'focus <direction|parent|child|mode_toggle|floating|tiling>' "
264 "or 'focus output <direction|name>'"); 294 "or 'focus output <direction|name>'");
265 } 295 }
266 296
267 if (direction == MOVE_CHILD) {
268 struct sway_node *focus = seat_get_active_tiling_child(seat, node);
269 if (focus) {
270 seat_set_focus(seat, focus);
271 seat_consider_warp_to_focus(seat);
272 }
273 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
274 }
275
276 if (node->type == N_WORKSPACE) { 297 if (node->type == N_WORKSPACE) {
277 if (direction == MOVE_PARENT) {
278 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
279 }
280
281 // Jump to the next output 298 // Jump to the next output
282 struct sway_output *new_output = 299 struct sway_output *new_output =
283 output_get_in_direction(workspace->output, direction); 300 output_get_in_direction(workspace->output, direction);
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
index b1f47be1..c76e1d63 100644
--- a/sway/commands/mark.c
+++ b/sway/commands/mark.c
@@ -19,11 +19,10 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
19 return error; 19 return error;
20 } 20 }
21 struct sway_container *container = config->handler_context.container; 21 struct sway_container *container = config->handler_context.container;
22 if (!container || !container->view) { 22 if (!container) {
23 return cmd_results_new(CMD_INVALID, "mark", 23 return cmd_results_new(CMD_INVALID, "mark",
24 "Only views can have marks"); 24 "Only containers can have marks");
25 } 25 }
26 struct sway_view *view = container->view;
27 26
28 bool add = false, toggle = false; 27 bool add = false, toggle = false;
29 while (argc > 0 && strncmp(*argv, "--", 2) == 0) { 28 while (argc > 0 && strncmp(*argv, "--", 2) == 0) {
@@ -47,22 +46,24 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
47 } 46 }
48 47
49 char *mark = join_args(argv, argc); 48 char *mark = join_args(argv, argc);
50 bool had_mark = view_has_mark(view, mark); 49 bool had_mark = container_has_mark(container, mark);
51 50
52 if (!add) { 51 if (!add) {
53 // Replacing 52 // Replacing
54 view_clear_marks(view); 53 container_clear_marks(container);
55 } 54 }
56 55
57 view_find_and_unmark(mark); 56 container_find_and_unmark(mark);
58 57
59 if (!toggle || !had_mark) { 58 if (!toggle || !had_mark) {
60 view_add_mark(view, mark); 59 container_add_mark(container, mark);
61 } 60 }
62 61
63 free(mark); 62 free(mark);
64 view_update_marks_textures(view); 63 container_update_marks_textures(container);
65 view_execute_criteria(view); 64 if (container->view) {
65 view_execute_criteria(container->view);
66 }
66 67
67 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 68 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
68} 69}
diff --git a/sway/commands/move.c b/sway/commands/move.c
index ffe12d41..7d8c1f1a 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -27,19 +27,6 @@ static const char *expected_syntax =
27 "'move <container|window|workspace> [to] output <name|direction>' or " 27 "'move <container|window|workspace> [to] output <name|direction>' or "
28 "'move <container|window> [to] mark <mark>'"; 28 "'move <container|window> [to] mark <mark>'";
29 29
30enum wlr_direction opposite_direction(enum wlr_direction d) {
31 switch (d) {
32 case WLR_DIRECTION_UP:
33 return WLR_DIRECTION_DOWN;
34 case WLR_DIRECTION_DOWN:
35 return WLR_DIRECTION_UP;
36 case WLR_DIRECTION_RIGHT:
37 return WLR_DIRECTION_LEFT;
38 default:
39 return WLR_DIRECTION_RIGHT;
40 }
41}
42
43static struct sway_output *output_in_direction(const char *direction_string, 30static struct sway_output *output_in_direction(const char *direction_string,
44 struct sway_output *reference, int ref_lx, int ref_ly) { 31 struct sway_output *reference, int ref_lx, int ref_ly) {
45 struct { 32 struct {
@@ -81,14 +68,14 @@ static struct sway_output *output_in_direction(const char *direction_string,
81} 68}
82 69
83static bool is_parallel(enum sway_container_layout layout, 70static bool is_parallel(enum sway_container_layout layout,
84 enum movement_direction dir) { 71 enum wlr_direction dir) {
85 switch (layout) { 72 switch (layout) {
86 case L_TABBED: 73 case L_TABBED:
87 case L_HORIZ: 74 case L_HORIZ:
88 return dir == MOVE_LEFT || dir == MOVE_RIGHT; 75 return dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT;
89 case L_STACKED: 76 case L_STACKED:
90 case L_VERT: 77 case L_VERT:
91 return dir == MOVE_UP || dir == MOVE_DOWN; 78 return dir == WLR_DIRECTION_UP || dir == WLR_DIRECTION_DOWN;
92 default: 79 default:
93 return false; 80 return false;
94 } 81 }
@@ -115,7 +102,7 @@ static void workspace_focus_fullscreen(struct sway_workspace *workspace) {
115 102
116static void container_move_to_container_from_direction( 103static void container_move_to_container_from_direction(
117 struct sway_container *container, struct sway_container *destination, 104 struct sway_container *container, struct sway_container *destination,
118 enum movement_direction move_dir) { 105 enum wlr_direction move_dir) {
119 if (destination->view) { 106 if (destination->view) {
120 if (destination->parent == container->parent && 107 if (destination->parent == container->parent &&
121 destination->workspace == container->workspace) { 108 destination->workspace == container->workspace) {
@@ -126,7 +113,8 @@ static void container_move_to_container_from_direction(
126 list_swap(siblings, container_index, destination_index); 113 list_swap(siblings, container_index, destination_index);
127 } else { 114 } else {
128 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin"); 115 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
129 int offset = move_dir == MOVE_LEFT || move_dir == MOVE_UP; 116 int offset =
117 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP;
130 int index = container_sibling_index(destination) + offset; 118 int index = container_sibling_index(destination) + offset;
131 if (destination->parent) { 119 if (destination->parent) {
132 container_insert_child(destination->parent, container, index); 120 container_insert_child(destination->parent, container, index);
@@ -141,7 +129,8 @@ static void container_move_to_container_from_direction(
141 129
142 if (is_parallel(destination->layout, move_dir)) { 130 if (is_parallel(destination->layout, move_dir)) {
143 wlr_log(WLR_DEBUG, "Reparenting container (parallel)"); 131 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
144 int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ? 132 int index =
133 move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ?
145 0 : destination->children->length; 134 0 : destination->children->length;
146 container_insert_child(destination, container, index); 135 container_insert_child(destination, container, index);
147 container->width = container->height = 0; 136 container->width = container->height = 0;
@@ -164,10 +153,11 @@ static void container_move_to_container_from_direction(
164 153
165static void container_move_to_workspace_from_direction( 154static void container_move_to_workspace_from_direction(
166 struct sway_container *container, struct sway_workspace *workspace, 155 struct sway_container *container, struct sway_workspace *workspace,
167 enum movement_direction move_dir) { 156 enum wlr_direction move_dir) {
168 if (is_parallel(workspace->layout, move_dir)) { 157 if (is_parallel(workspace->layout, move_dir)) {
169 wlr_log(WLR_DEBUG, "Reparenting container (parallel)"); 158 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
170 int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ? 159 int index =
160 move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ?
171 0 : workspace->tiling->length; 161 0 : workspace->tiling->length;
172 workspace_insert_tiling(workspace, container, index); 162 workspace_insert_tiling(workspace, container, index);
173 return; 163 return;
@@ -258,28 +248,31 @@ static void container_move_to_container(struct sway_container *container,
258 * container, switches the layout of the workspace, and drops the child back in. 248 * container, switches the layout of the workspace, and drops the child back in.
259 * In other words, rejigger it. */ 249 * In other words, rejigger it. */
260static void workspace_rejigger(struct sway_workspace *ws, 250static void workspace_rejigger(struct sway_workspace *ws,
261 struct sway_container *child, enum movement_direction move_dir) { 251 struct sway_container *child, enum wlr_direction move_dir) {
262 if (!child->parent && ws->tiling->length == 1) { 252 if (!child->parent && ws->tiling->length == 1) {
263 ws->layout = 253 ws->layout =
264 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 254 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_RIGHT ?
255 L_HORIZ : L_VERT;
265 workspace_update_representation(ws); 256 workspace_update_representation(ws);
266 return; 257 return;
267 } 258 }
268 container_detach(child); 259 container_detach(child);
269 struct sway_container *new_parent = workspace_wrap_children(ws); 260 struct sway_container *new_parent = workspace_wrap_children(ws);
270 261
271 int index = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? 0 : 1; 262 int index =
263 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP ? 0 : 1;
272 workspace_insert_tiling(ws, child, index); 264 workspace_insert_tiling(ws, child, index);
273 container_flatten(new_parent); 265 container_flatten(new_parent);
274 ws->layout = 266 ws->layout =
275 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 267 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_RIGHT ?
268 L_HORIZ : L_VERT;
276 workspace_update_representation(ws); 269 workspace_update_representation(ws);
277 child->width = child->height = 0; 270 child->width = child->height = 0;
278} 271}
279 272
280// Returns true if moved 273// Returns true if moved
281static bool container_move_in_direction(struct sway_container *container, 274static bool container_move_in_direction(struct sway_container *container,
282 enum movement_direction move_dir) { 275 enum wlr_direction move_dir) {
283 // If moving a fullscreen view, only consider outputs 276 // If moving a fullscreen view, only consider outputs
284 if (container->is_fullscreen) { 277 if (container->is_fullscreen) {
285 struct sway_output *new_output = 278 struct sway_output *new_output =
@@ -305,7 +298,8 @@ static bool container_move_in_direction(struct sway_container *container,
305 // The below loop stops once we hit the workspace because current->parent 298 // The below loop stops once we hit the workspace because current->parent
306 // is NULL for the topmost containers in a workspace. 299 // is NULL for the topmost containers in a workspace.
307 struct sway_container *current = container; 300 struct sway_container *current = container;
308 int offs = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1; 301 int offs =
302 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP ? -1 : 1;
309 303
310 while (current) { 304 while (current) {
311 list_t *siblings = container_get_siblings(current); 305 list_t *siblings = container_get_siblings(current);
@@ -494,12 +488,12 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) {
494 } 488 }
495 destination = seat_get_focus_inactive(seat, &new_output->node); 489 destination = seat_get_focus_inactive(seat, &new_output->node);
496 } else if (strcasecmp(argv[1], "mark") == 0) { 490 } else if (strcasecmp(argv[1], "mark") == 0) {
497 struct sway_view *dest_view = view_find_mark(argv[2]); 491 struct sway_container *dest_con = container_find_mark(argv[2]);
498 if (dest_view == NULL) { 492 if (dest_con == NULL) {
499 return cmd_results_new(CMD_FAILURE, "move", 493 return cmd_results_new(CMD_FAILURE, "move",
500 "Mark '%s' not found", argv[2]); 494 "Mark '%s' not found", argv[2]);
501 } 495 }
502 destination = &dest_view->container->node; 496 destination = &dest_con->node;
503 } else { 497 } else {
504 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 498 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
505 } 499 }
@@ -642,7 +636,7 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
642} 636}
643 637
644static struct cmd_results *cmd_move_in_direction( 638static struct cmd_results *cmd_move_in_direction(
645 enum movement_direction direction, int argc, char **argv) { 639 enum wlr_direction direction, int argc, char **argv) {
646 int move_amt = 10; 640 int move_amt = 10;
647 if (argc > 1) { 641 if (argc > 1) {
648 char *inv; 642 char *inv;
@@ -666,22 +660,18 @@ static struct cmd_results *cmd_move_in_direction(
666 double lx = container->x; 660 double lx = container->x;
667 double ly = container->y; 661 double ly = container->y;
668 switch (direction) { 662 switch (direction) {
669 case MOVE_LEFT: 663 case WLR_DIRECTION_LEFT:
670 lx -= move_amt; 664 lx -= move_amt;
671 break; 665 break;
672 case MOVE_RIGHT: 666 case WLR_DIRECTION_RIGHT:
673 lx += move_amt; 667 lx += move_amt;
674 break; 668 break;
675 case MOVE_UP: 669 case WLR_DIRECTION_UP:
676 ly -= move_amt; 670 ly -= move_amt;
677 break; 671 break;
678 case MOVE_DOWN: 672 case WLR_DIRECTION_DOWN:
679 ly += move_amt; 673 ly += move_amt;
680 break; 674 break;
681 case MOVE_PARENT:
682 case MOVE_CHILD:
683 return cmd_results_new(CMD_FAILURE, "move",
684 "Cannot move floating container to parent or child");
685 } 675 }
686 container_floating_move_to(container, lx, ly); 676 container_floating_move_to(container, lx, ly);
687 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 677 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -850,13 +840,13 @@ struct cmd_results *cmd_move(int argc, char **argv) {
850 } 840 }
851 841
852 if (strcasecmp(argv[0], "left") == 0) { 842 if (strcasecmp(argv[0], "left") == 0) {
853 return cmd_move_in_direction(MOVE_LEFT, argc, argv); 843 return cmd_move_in_direction(WLR_DIRECTION_LEFT, argc, argv);
854 } else if (strcasecmp(argv[0], "right") == 0) { 844 } else if (strcasecmp(argv[0], "right") == 0) {
855 return cmd_move_in_direction(MOVE_RIGHT, argc, argv); 845 return cmd_move_in_direction(WLR_DIRECTION_RIGHT, argc, argv);
856 } else if (strcasecmp(argv[0], "up") == 0) { 846 } else if (strcasecmp(argv[0], "up") == 0) {
857 return cmd_move_in_direction(MOVE_UP, argc, argv); 847 return cmd_move_in_direction(WLR_DIRECTION_UP, argc, argv);
858 } else if (strcasecmp(argv[0], "down") == 0) { 848 } else if (strcasecmp(argv[0], "down") == 0) {
859 return cmd_move_in_direction(MOVE_DOWN, argc, argv); 849 return cmd_move_in_direction(WLR_DIRECTION_DOWN, argc, argv);
860 } else if ((strcasecmp(argv[0], "container") == 0 850 } else if ((strcasecmp(argv[0], "container") == 0
861 || strcasecmp(argv[0], "window") == 0) || 851 || strcasecmp(argv[0], "window") == 0) ||
862 (strcasecmp(argv[0], "--no-auto-back-and-forth") && argc >= 2 852 (strcasecmp(argv[0], "--no-auto-back-and-forth") && argc >= 2
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 791081a8..62105cdc 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -10,9 +10,7 @@
10#include "log.h" 10#include "log.h"
11 11
12static void rebuild_textures_iterator(struct sway_container *con, void *data) { 12static void rebuild_textures_iterator(struct sway_container *con, void *data) {
13 if (con->view) { 13 container_update_marks_textures(con);
14 view_update_marks_textures(con->view);
15 }
16 container_update_title_textures(con); 14 container_update_title_textures(con);
17} 15}
18 16
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
index d501584a..0baf6852 100644
--- a/sway/commands/show_marks.c
+++ b/sway/commands/show_marks.c
@@ -11,9 +11,7 @@
11#include "util.h" 11#include "util.h"
12 12
13static void rebuild_marks_iterator(struct sway_container *con, void *data) { 13static void rebuild_marks_iterator(struct sway_container *con, void *data) {
14 if (con->view) { 14 container_update_marks_textures(con);
15 view_update_marks_textures(con->view);
16 }
17} 15}
18 16
19struct cmd_results *cmd_show_marks(int argc, char **argv) { 17struct cmd_results *cmd_show_marks(int argc, char **argv) {
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index a70a6cdd..23e8d583 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -159,8 +159,8 @@ static bool test_id(struct sway_container *container, void *id) {
159} 159}
160 160
161static bool test_mark(struct sway_container *container, void *mark) { 161static bool test_mark(struct sway_container *container, void *mark) {
162 if (container->view && container->view->marks->length) { 162 if (container->marks->length) {
163 return !list_seq_find(container->view->marks, 163 return !list_seq_find(container->marks,
164 (int (*)(const void *, const void *))strcmp, mark); 164 (int (*)(const void *, const void *))strcmp, mark);
165 } 165 }
166 return false; 166 return false;
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
index c671ed4e..98ac6ff2 100644
--- a/sway/commands/unmark.c
+++ b/sway/commands/unmark.c
@@ -9,10 +9,8 @@
9#include "stringop.h" 9#include "stringop.h"
10 10
11static void remove_all_marks_iterator(struct sway_container *con, void *data) { 11static void remove_all_marks_iterator(struct sway_container *con, void *data) {
12 if (con->view) { 12 container_clear_marks(con);
13 view_clear_marks(con->view); 13 container_update_marks_textures(con);
14 view_update_marks_textures(con->view);
15 }
16} 14}
17 15
18// unmark Remove all marks from all views 16// unmark Remove all marks from all views
@@ -21,15 +19,10 @@ static void remove_all_marks_iterator(struct sway_container *con, void *data) {
21// [criteria] unmark foo Remove single mark from matched view 19// [criteria] unmark foo Remove single mark from matched view
22 20
23struct cmd_results *cmd_unmark(int argc, char **argv) { 21struct cmd_results *cmd_unmark(int argc, char **argv) {
24 // Determine the view 22 // Determine the container
25 struct sway_view *view = NULL; 23 struct sway_container *con = NULL;
26 if (config->handler_context.using_criteria) { 24 if (config->handler_context.using_criteria) {
27 struct sway_container *container = config->handler_context.container; 25 con = config->handler_context.container;
28 if (!container || !container->view) {
29 return cmd_results_new(CMD_INVALID, "unmark",
30 "Only views can have marks");
31 }
32 view = container->view;
33 } 26 }
34 27
35 // Determine the mark 28 // Determine the mark
@@ -38,20 +31,20 @@ struct cmd_results *cmd_unmark(int argc, char **argv) {
38 mark = join_args(argv, argc); 31 mark = join_args(argv, argc);
39 } 32 }
40 33
41 if (view && mark) { 34 if (con && mark) {
42 // Remove the mark from the given view 35 // Remove the mark from the given container
43 if (view_has_mark(view, mark)) { 36 if (container_has_mark(con, mark)) {
44 view_find_and_unmark(mark); 37 container_find_and_unmark(mark);
45 } 38 }
46 } else if (view && !mark) { 39 } else if (con && !mark) {
47 // Clear all marks from the given view 40 // Clear all marks from the given container
48 view_clear_marks(view); 41 container_clear_marks(con);
49 view_update_marks_textures(view); 42 container_update_marks_textures(con);
50 } else if (!view && mark) { 43 } else if (!con && mark) {
51 // Remove mark from whichever view has it 44 // Remove mark from whichever container has it
52 view_find_and_unmark(mark); 45 container_find_and_unmark(mark);
53 } else { 46 } else {
54 // Remove all marks from all views 47 // Remove all marks from all containers
55 root_for_each_container(remove_all_marks_iterator, NULL); 48 root_for_each_container(remove_all_marks_iterator, NULL);
56 } 49 }
57 free(mark); 50 free(mark);
diff --git a/sway/criteria.c b/sway/criteria.c
index 89630d90..2f9992e9 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -121,8 +121,9 @@ static bool criteria_matches_view(struct criteria *criteria,
121 121
122 if (criteria->con_mark) { 122 if (criteria->con_mark) {
123 bool exists = false; 123 bool exists = false;
124 for (int i = 0; i < view->marks->length; ++i) { 124 struct sway_container *con = view->container;
125 if (regex_cmp(view->marks->items[i], criteria->con_mark) == 0) { 125 for (int i = 0; i < con->marks->length; ++i) {
126 if (regex_cmp(con->marks->items[i], criteria->con_mark) == 0) {
126 exists = true; 127 exists = true;
127 break; 128 break;
128 } 129 }
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index ed9300bb..2b90f151 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -39,6 +39,19 @@ struct sway_output *output_by_name(const char *name) {
39 return NULL; 39 return NULL;
40} 40}
41 41
42struct sway_output *output_by_identifier(const char *identifier) {
43 for (int i = 0; i < root->outputs->length; ++i) {
44 struct sway_output *output = root->outputs->items[i];
45 char output_identifier[128];
46 snprintf(output_identifier, sizeof(output_identifier), "%s %s %s", output->wlr_output->make,
47 output->wlr_output->model, output->wlr_output->serial);
48 if (strcasecmp(output_identifier, identifier) == 0) {
49 return output;
50 }
51 }
52 return NULL;
53}
54
42/** 55/**
43 * Rotate a child's position relative to a parent. The parent size is (pw, ph), 56 * Rotate a child's position relative to a parent. The parent size is (pw, ph),
44 * the child position is (*sx, *sy) and its size is (sw, sh). 57 * the child position is (*sx, *sy) and its size is (sw, sh).
@@ -519,9 +532,7 @@ static void handle_transform(struct wl_listener *listener, void *data) {
519 532
520static void update_textures(struct sway_container *con, void *data) { 533static void update_textures(struct sway_container *con, void *data) {
521 container_update_title_textures(con); 534 container_update_title_textures(con);
522 if (con->view) { 535 container_update_marks_textures(con);
523 view_update_marks_textures(con->view);
524 }
525} 536}
526 537
527static void handle_scale(struct wl_listener *listener, void *data) { 538static void handle_scale(struct wl_listener *listener, void *data) {
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 1a72f752..cf6da682 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -625,19 +625,19 @@ static void render_containers_linear(struct sway_output *output,
625 if (view_is_urgent(view)) { 625 if (view_is_urgent(view)) {
626 colors = &config->border_colors.urgent; 626 colors = &config->border_colors.urgent;
627 title_texture = child->title_urgent; 627 title_texture = child->title_urgent;
628 marks_texture = view->marks_urgent; 628 marks_texture = child->marks_urgent;
629 } else if (state->focused || parent->focused) { 629 } else if (state->focused || parent->focused) {
630 colors = &config->border_colors.focused; 630 colors = &config->border_colors.focused;
631 title_texture = child->title_focused; 631 title_texture = child->title_focused;
632 marks_texture = view->marks_focused; 632 marks_texture = child->marks_focused;
633 } else if (child == parent->active_child) { 633 } else if (child == parent->active_child) {
634 colors = &config->border_colors.focused_inactive; 634 colors = &config->border_colors.focused_inactive;
635 title_texture = child->title_focused_inactive; 635 title_texture = child->title_focused_inactive;
636 marks_texture = view->marks_focused_inactive; 636 marks_texture = child->marks_focused_inactive;
637 } else { 637 } else {
638 colors = &config->border_colors.unfocused; 638 colors = &config->border_colors.unfocused;
639 title_texture = child->title_unfocused; 639 title_texture = child->title_unfocused;
640 marks_texture = view->marks_unfocused; 640 marks_texture = child->marks_unfocused;
641 } 641 }
642 642
643 if (state->border == B_NORMAL) { 643 if (state->border == B_NORMAL) {
@@ -681,19 +681,19 @@ static void render_containers_tabbed(struct sway_output *output,
681 if (urgent) { 681 if (urgent) {
682 colors = &config->border_colors.urgent; 682 colors = &config->border_colors.urgent;
683 title_texture = child->title_urgent; 683 title_texture = child->title_urgent;
684 marks_texture = view ? view->marks_urgent : NULL; 684 marks_texture = child->marks_urgent;
685 } else if (cstate->focused || parent->focused) { 685 } else if (cstate->focused || parent->focused) {
686 colors = &config->border_colors.focused; 686 colors = &config->border_colors.focused;
687 title_texture = child->title_focused; 687 title_texture = child->title_focused;
688 marks_texture = view ? view->marks_focused : NULL; 688 marks_texture = child->marks_focused;
689 } else if (child == parent->active_child) { 689 } else if (child == parent->active_child) {
690 colors = &config->border_colors.focused_inactive; 690 colors = &config->border_colors.focused_inactive;
691 title_texture = child->title_focused_inactive; 691 title_texture = child->title_focused_inactive;
692 marks_texture = view ? view->marks_focused_inactive : NULL; 692 marks_texture = child->marks_focused_inactive;
693 } else { 693 } else {
694 colors = &config->border_colors.unfocused; 694 colors = &config->border_colors.unfocused;
695 title_texture = child->title_unfocused; 695 title_texture = child->title_unfocused;
696 marks_texture = view ? view->marks_unfocused : NULL; 696 marks_texture = child->marks_unfocused;
697 } 697 }
698 698
699 int x = cstate->con_x + tab_width * i; 699 int x = cstate->con_x + tab_width * i;
@@ -746,19 +746,19 @@ static void render_containers_stacked(struct sway_output *output,
746 if (urgent) { 746 if (urgent) {
747 colors = &config->border_colors.urgent; 747 colors = &config->border_colors.urgent;
748 title_texture = child->title_urgent; 748 title_texture = child->title_urgent;
749 marks_texture = view ? view->marks_urgent : NULL; 749 marks_texture = child->marks_urgent;
750 } else if (cstate->focused || parent->focused) { 750 } else if (cstate->focused || parent->focused) {
751 colors = &config->border_colors.focused; 751 colors = &config->border_colors.focused;
752 title_texture = child->title_focused; 752 title_texture = child->title_focused;
753 marks_texture = view ? view->marks_focused : NULL; 753 marks_texture = child->marks_focused;
754 } else if (child == parent->active_child) { 754 } else if (child == parent->active_child) {
755 colors = &config->border_colors.focused_inactive; 755 colors = &config->border_colors.focused_inactive;
756 title_texture = child->title_focused_inactive; 756 title_texture = child->title_focused_inactive;
757 marks_texture = view ? view->marks_focused_inactive : NULL; 757 marks_texture = child->marks_focused_inactive;
758 } else { 758 } else {
759 colors = &config->border_colors.unfocused; 759 colors = &config->border_colors.unfocused;
760 title_texture = child->title_unfocused; 760 title_texture = child->title_unfocused;
761 marks_texture = view ? view->marks_unfocused : NULL; 761 marks_texture = child->marks_unfocused;
762 } 762 }
763 763
764 int y = parent->box.y + titlebar_height * i; 764 int y = parent->box.y + titlebar_height * i;
@@ -841,15 +841,15 @@ static void render_floating_container(struct sway_output *soutput,
841 if (view_is_urgent(view)) { 841 if (view_is_urgent(view)) {
842 colors = &config->border_colors.urgent; 842 colors = &config->border_colors.urgent;
843 title_texture = con->title_urgent; 843 title_texture = con->title_urgent;
844 marks_texture = view->marks_urgent; 844 marks_texture = con->marks_urgent;
845 } else if (con->current.focused) { 845 } else if (con->current.focused) {
846 colors = &config->border_colors.focused; 846 colors = &config->border_colors.focused;
847 title_texture = con->title_focused; 847 title_texture = con->title_focused;
848 marks_texture = view->marks_focused; 848 marks_texture = con->marks_focused;
849 } else { 849 } else {
850 colors = &config->border_colors.unfocused; 850 colors = &config->border_colors.unfocused;
851 title_texture = con->title_unfocused; 851 title_texture = con->title_unfocused;
852 marks_texture = view->marks_unfocused; 852 marks_texture = con->marks_unfocused;
853 } 853 }
854 854
855 if (con->current.border == B_NORMAL) { 855 if (con->current.border == B_NORMAL) {
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 955b05d6..44156d41 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -137,6 +137,12 @@ static void copy_container_state(struct sway_container *container,
137 state->is_fullscreen = container->is_fullscreen; 137 state->is_fullscreen = container->is_fullscreen;
138 state->parent = container->parent; 138 state->parent = container->parent;
139 state->workspace = container->workspace; 139 state->workspace = container->workspace;
140 state->border = container->border;
141 state->border_thickness = container->border_thickness;
142 state->border_top = container->border_top;
143 state->border_left = container->border_left;
144 state->border_right = container->border_right;
145 state->border_bottom = container->border_bottom;
140 146
141 if (container->view) { 147 if (container->view) {
142 struct sway_view *view = container->view; 148 struct sway_view *view = container->view;
@@ -144,12 +150,6 @@ static void copy_container_state(struct sway_container *container,
144 state->view_y = view->y; 150 state->view_y = view->y;
145 state->view_width = view->width; 151 state->view_width = view->width;
146 state->view_height = view->height; 152 state->view_height = view->height;
147 state->border = view->border;
148 state->border_thickness = view->border_thickness;
149 state->border_top = view->border_top;
150 state->border_left = view->border_left;
151 state->border_right = view->border_right;
152 state->border_bottom = view->border_bottom;
153 } else { 153 } else {
154 state->children = create_list(); 154 state->children = create_list();
155 list_cat(state->children, container->children); 155 list_cat(state->children, container->children);
@@ -301,7 +301,9 @@ static void transaction_apply(struct sway_transaction *transaction) {
301 if (root->outputs->length) { 301 if (root->outputs->length) {
302 struct sway_seat *seat; 302 struct sway_seat *seat;
303 wl_list_for_each(seat, &server.input->seats, link) { 303 wl_list_for_each(seat, &server.input->seats, link) {
304 cursor_rebase(seat->cursor); 304 if (seat->operation == OP_NONE) {
305 cursor_rebase(seat->cursor);
306 }
305 } 307 }
306 } 308 }
307} 309}
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 3942b64f..c539df40 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -187,23 +187,22 @@ static enum wlr_edges find_edge(struct sway_container *cont,
187 if (!cont->view) { 187 if (!cont->view) {
188 return WLR_EDGE_NONE; 188 return WLR_EDGE_NONE;
189 } 189 }
190 struct sway_view *view = cont->view; 190 if (cont->border == B_NONE || !cont->border_thickness ||
191 if (view->border == B_NONE || !view->border_thickness || 191 cont->border == B_CSD) {
192 view->border == B_CSD) {
193 return WLR_EDGE_NONE; 192 return WLR_EDGE_NONE;
194 } 193 }
195 194
196 enum wlr_edges edge = 0; 195 enum wlr_edges edge = 0;
197 if (cursor->cursor->x < cont->x + view->border_thickness) { 196 if (cursor->cursor->x < cont->x + cont->border_thickness) {
198 edge |= WLR_EDGE_LEFT; 197 edge |= WLR_EDGE_LEFT;
199 } 198 }
200 if (cursor->cursor->y < cont->y + view->border_thickness) { 199 if (cursor->cursor->y < cont->y + cont->border_thickness) {
201 edge |= WLR_EDGE_TOP; 200 edge |= WLR_EDGE_TOP;
202 } 201 }
203 if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) { 202 if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) {
204 edge |= WLR_EDGE_RIGHT; 203 edge |= WLR_EDGE_RIGHT;
205 } 204 }
206 if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) { 205 if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) {
207 edge |= WLR_EDGE_BOTTOM; 206 edge |= WLR_EDGE_BOTTOM;
208 } 207 }
209 208
@@ -585,7 +584,7 @@ static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec,
585 584
586void cursor_rebase(struct sway_cursor *cursor) { 585void cursor_rebase(struct sway_cursor *cursor) {
587 uint32_t time_msec = get_current_time_msec(); 586 uint32_t time_msec = get_current_time_msec();
588 struct wlr_surface *surface; 587 struct wlr_surface *surface = NULL;
589 double sx, sy; 588 double sx, sy;
590 cursor->previous.node = node_at_coords(cursor->seat, 589 cursor->previous.node = node_at_coords(cursor->seat,
591 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); 590 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 91c45dd1..68445d68 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -431,6 +431,7 @@ void input_manager_set_focus(struct sway_node *node) {
431 struct sway_seat *seat; 431 struct sway_seat *seat;
432 wl_list_for_each(seat, &server.input->seats, link) { 432 wl_list_for_each(seat, &server.input->seats, link) {
433 seat_set_focus(seat, node); 433 seat_set_focus(seat, node);
434 seat_consider_warp_to_focus(seat);
434 } 435 }
435} 436}
436 437
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 577619a7..64419afa 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -185,7 +185,11 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
185 seat_set_focus(seat, next_focus); 185 seat_set_focus(seat, next_focus);
186 } else { 186 } else {
187 // Setting focus_inactive 187 // Setting focus_inactive
188 focus = seat_get_focus_inactive(seat, &root->node);
188 seat_set_raw_focus(seat, next_focus); 189 seat_set_raw_focus(seat, next_focus);
190 if (focus->type == N_CONTAINER) {
191 seat_set_raw_focus(seat, &focus->sway_container->workspace->node);
192 }
189 seat_set_raw_focus(seat, focus); 193 seat_set_raw_focus(seat, focus);
190 } 194 }
191} 195}
@@ -944,7 +948,7 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) {
944 if (!seat->has_focus) { 948 if (!seat->has_focus) {
945 return NULL; 949 return NULL;
946 } 950 }
947 if (wl_list_length(&seat->focus_stack) == 0) { 951 if (wl_list_empty(&seat->focus_stack)) {
948 return NULL; 952 return NULL;
949 } 953 }
950 struct sway_seat_node *current = 954 struct sway_seat_node *current =
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 2cd0cb2d..7cc965c8 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -136,13 +136,18 @@ static void ipc_json_describe_output(struct sway_output *output,
136 json_object_new_int(mode->width)); 136 json_object_new_int(mode->width));
137 json_object_object_add(mode_object, "height", 137 json_object_object_add(mode_object, "height",
138 json_object_new_int(mode->height)); 138 json_object_new_int(mode->height));
139 json_object_object_add(mode_object, "refresh",
140 json_object_new_int(mode->refresh));
141 json_object_array_add(modes_array, mode_object); 139 json_object_array_add(modes_array, mode_object);
142 } 140 }
143 141
144 json_object_object_add(object, "modes", modes_array); 142 json_object_object_add(object, "modes", modes_array);
145 143
144 json_object *current_mode_object = json_object_new_object();
145 json_object_object_add(current_mode_object, "width",
146 json_object_new_int(wlr_output->width));
147 json_object_object_add(current_mode_object, "height",
148 json_object_new_int(wlr_output->height));
149 json_object_object_add(object, "current_mode", current_mode_object);
150
146 struct sway_node *parent = node_get_parent(&output->node); 151 struct sway_node *parent = node_get_parent(&output->node);
147 struct wlr_box parent_box = {0, 0, 0, 0}; 152 struct wlr_box parent_box = {0, 0, 0, 0};
148 153
@@ -229,14 +234,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
229 json_object_object_add(object, "app_id", 234 json_object_object_add(object, "app_id",
230 app_id ? json_object_new_string(app_id) : NULL); 235 app_id ? json_object_new_string(app_id) : NULL);
231 236
232 const char *class = view_get_class(c->view);
233 json_object_object_add(object, "class",
234 class ? json_object_new_string(class) : NULL);
235
236 json_object *marks = json_object_new_array(); 237 json_object *marks = json_object_new_array();
237 list_t *view_marks = c->view->marks; 238 list_t *con_marks = c->marks;
238 for (int i = 0; i < view_marks->length; ++i) { 239 for (int i = 0; i < con_marks->length; ++i) {
239 json_object_array_add(marks, json_object_new_string(view_marks->items[i])); 240 json_object_array_add(marks, json_object_new_string(con_marks->items[i]));
240 } 241 }
241 242
242 json_object_object_add(object, "marks", marks); 243 json_object_object_add(object, "marks", marks);
@@ -269,13 +270,17 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
269 270
270 json_object *window_props = json_object_new_object(); 271 json_object *window_props = json_object_new_object();
271 272
272 json_object_object_add(window_props, "class", 273 const char *class = view_get_class(c->view);
273 class ? json_object_new_string(class) : NULL); 274 if (class) {
275 json_object_object_add(window_props, "class", json_object_new_string(class));
276 }
274 const char *instance = view_get_instance(c->view); 277 const char *instance = view_get_instance(c->view);
275 json_object_object_add(window_props, "instance", 278 if (instance) {
276 instance ? json_object_new_string(instance) : NULL); 279 json_object_object_add(window_props, "instance", json_object_new_string(instance));
277 json_object_object_add(window_props, "title", 280 }
278 c->title ? json_object_new_string(c->title) : NULL); 281 if (c->title) {
282 json_object_object_add(window_props, "title", json_object_new_string(c->title));
283 }
279 284
280 // the transient_for key is always present in i3's output 285 // the transient_for key is always present in i3's output
281 uint32_t parent_id = view_get_x11_parent_id(c->view); 286 uint32_t parent_id = view_get_x11_parent_id(c->view);
@@ -284,8 +289,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
284 289
285 const char *role = view_get_window_role(c->view); 290 const char *role = view_get_window_role(c->view);
286 if (role) { 291 if (role) {
287 json_object_object_add(window_props, "window_role", 292 json_object_object_add(window_props, "window_role", json_object_new_string(role));
288 json_object_new_string(role));
289 } 293 }
290 294
291 json_object_object_add(object, "window_properties", window_props); 295 json_object_object_add(object, "window_properties", window_props);
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 21f431be..6466d263 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -563,11 +563,9 @@ static void ipc_get_workspaces_callback(struct sway_workspace *workspace,
563 563
564static void ipc_get_marks_callback(struct sway_container *con, void *data) { 564static void ipc_get_marks_callback(struct sway_container *con, void *data) {
565 json_object *marks = (json_object *)data; 565 json_object *marks = (json_object *)data;
566 if (con->view && con->view->marks) { 566 for (int i = 0; i < con->marks->length; ++i) {
567 for (int i = 0; i < con->view->marks->length; ++i) { 567 char *mark = (char *)con->marks->items[i];
568 char *mark = (char *)con->view->marks->items[i]; 568 json_object_array_add(marks, json_object_new_string(mark));
569 json_object_array_add(marks, json_object_new_string(mark));
570 }
571 } 569 }
572} 570}
573 571
diff --git a/sway/main.c b/sway/main.c
index cc5f7187..920cea11 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -29,7 +29,7 @@
29 29
30static bool terminate_request = false; 30static bool terminate_request = false;
31static int exit_value = 0; 31static int exit_value = 0;
32struct sway_server server; 32struct sway_server server = {0};
33 33
34void sway_terminate(int exit_code) { 34void sway_terminate(int exit_code) {
35 terminate_request = true; 35 terminate_request = true;
@@ -194,21 +194,23 @@ static void log_kernel(void) {
194} 194}
195 195
196 196
197static void drop_permissions(void) { 197static bool drop_permissions(void) {
198 if (getuid() != geteuid() || getgid() != getegid()) { 198 if (getuid() != geteuid() || getgid() != getegid()) {
199 if (setgid(getgid()) != 0) { 199 if (setgid(getgid()) != 0) {
200 wlr_log(WLR_ERROR, "Unable to drop root"); 200 wlr_log(WLR_ERROR, "Unable to drop root, refusing to start");
201 exit(EXIT_FAILURE); 201 return false;
202 } 202 }
203 if (setuid(getuid()) != 0) { 203 if (setuid(getuid()) != 0) {
204 wlr_log(WLR_ERROR, "Unable to drop root"); 204 wlr_log(WLR_ERROR, "Unable to drop root, refusing to start");
205 exit(EXIT_FAILURE); 205 return false;
206 } 206 }
207 } 207 }
208 if (setuid(0) != -1) { 208 if (setuid(0) != -1) {
209 wlr_log(WLR_ERROR, "Root privileges can be restored."); 209 wlr_log(WLR_ERROR, "Unable to drop root (we shouldn't be able to "
210 exit(EXIT_FAILURE); 210 "restore it after setuid), refusing to start");
211 return false;
211 } 212 }
213 return true;
212} 214}
213 215
214void enable_debug_flag(const char *flag) { 216void enable_debug_flag(const char *flag) {
@@ -317,11 +319,13 @@ int main(int argc, char **argv) {
317 } 319 }
318 320
319 if (optind < argc) { // Behave as IPC client 321 if (optind < argc) { // Behave as IPC client
320 if(optind != 1) { 322 if (optind != 1) {
321 wlr_log(WLR_ERROR, "Don't use options with the IPC client"); 323 wlr_log(WLR_ERROR, "Don't use options with the IPC client");
322 exit(EXIT_FAILURE); 324 exit(EXIT_FAILURE);
323 } 325 }
324 drop_permissions(); 326 if (!drop_permissions()) {
327 exit(EXIT_FAILURE);
328 }
325 char *socket_path = getenv("SWAYSOCK"); 329 char *socket_path = getenv("SWAYSOCK");
326 if (!socket_path) { 330 if (!socket_path) {
327 wlr_log(WLR_ERROR, "Unable to retrieve socket path"); 331 wlr_log(WLR_ERROR, "Unable to retrieve socket path");
@@ -341,7 +345,10 @@ int main(int argc, char **argv) {
341 detect_proprietary(allow_unsupported_gpu); 345 detect_proprietary(allow_unsupported_gpu);
342 detect_raspi(); 346 detect_raspi();
343 347
344 drop_permissions(); 348 if (!drop_permissions()) {
349 server_fini(&server);
350 exit(EXIT_FAILURE);
351 }
345 352
346 // handle SIGTERM signals 353 // handle SIGTERM signals
347 signal(SIGTERM, sig_handler); 354 signal(SIGTERM, sig_handler);
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 58d3df34..458ed7ff 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -39,6 +39,7 @@ struct sway_container *container_create(struct sway_view *view) {
39 c->children = create_list(); 39 c->children = create_list();
40 c->current.children = create_list(); 40 c->current.children = create_list();
41 } 41 }
42 c->marks = create_list();
42 c->outputs = create_list(); 43 c->outputs = create_list();
43 44
44 wl_signal_init(&c->events.destroy); 45 wl_signal_init(&c->events.destroy);
@@ -66,6 +67,13 @@ void container_destroy(struct sway_container *con) {
66 list_free(con->current.children); 67 list_free(con->current.children);
67 list_free(con->outputs); 68 list_free(con->outputs);
68 69
70 list_foreach(con->marks, free);
71 list_free(con->marks);
72 wlr_texture_destroy(con->marks_focused);
73 wlr_texture_destroy(con->marks_focused_inactive);
74 wlr_texture_destroy(con->marks_unfocused);
75 wlr_texture_destroy(con->marks_urgent);
76
69 if (con->view) { 77 if (con->view) {
70 if (con->view->container == con) { 78 if (con->view->container == con) {
71 con->view->container = NULL; 79 con->view->container = NULL;
@@ -639,8 +647,8 @@ void container_init_floating(struct sway_container *con) {
639 view->y = ws->y + (ws->height - view->height) / 2; 647 view->y = ws->y + (ws->height - view->height) / 2;
640 648
641 // If the view's border is B_NONE then these properties are ignored. 649 // If the view's border is B_NONE then these properties are ignored.
642 view->border_top = view->border_bottom = true; 650 con->border_top = con->border_bottom = true;
643 view->border_left = view->border_right = true; 651 con->border_left = con->border_right = true;
644 652
645 container_set_geometry_from_floating_view(con); 653 container_set_geometry_from_floating_view(con);
646 } 654 }
@@ -662,7 +670,7 @@ void container_set_floating(struct sway_container *container, bool enable) {
662 if (container->view) { 670 if (container->view) {
663 view_set_tiled(container->view, false); 671 view_set_tiled(container->view, false);
664 if (container->view->using_csd) { 672 if (container->view->using_csd) {
665 container->view->border = B_CSD; 673 container->border = B_CSD;
666 } 674 }
667 } 675 }
668 if (old_parent) { 676 if (old_parent) {
@@ -676,11 +684,8 @@ void container_set_floating(struct sway_container *container, bool enable) {
676 container_detach(container); 684 container_detach(container);
677 struct sway_container *reference = 685 struct sway_container *reference =
678 seat_get_focus_inactive_tiling(seat, workspace); 686 seat_get_focus_inactive_tiling(seat, workspace);
679 if (reference && reference->view) {
680 reference = reference->parent;
681 }
682 if (reference) { 687 if (reference) {
683 container_add_child(reference, container); 688 container_add_sibling(reference, container, 1);
684 container->width = reference->width; 689 container->width = reference->width;
685 container->height = reference->height; 690 container->height = reference->height;
686 } else { 691 } else {
@@ -691,7 +696,7 @@ void container_set_floating(struct sway_container *container, bool enable) {
691 if (container->view) { 696 if (container->view) {
692 view_set_tiled(container->view, true); 697 view_set_tiled(container->view, true);
693 if (container->view->using_csd) { 698 if (container->view->using_csd) {
694 container->view->border = container->view->saved_border; 699 container->border = container->saved_border;
695 } 700 }
696 } 701 }
697 container->is_sticky = false; 702 container->is_sticky = false;
@@ -713,9 +718,9 @@ void container_set_geometry_from_floating_view(struct sway_container *con) {
713 size_t border_width = 0; 718 size_t border_width = 0;
714 size_t top = 0; 719 size_t top = 0;
715 720
716 if (view->border != B_CSD) { 721 if (con->border != B_CSD) {
717 border_width = view->border_thickness * (view->border != B_NONE); 722 border_width = con->border_thickness * (con->border != B_NONE);
718 top = view->border == B_NORMAL ? 723 top = con->border == B_NORMAL ?
719 container_titlebar_height() : border_width; 724 container_titlebar_height() : border_width;
720 } 725 }
721 726
@@ -999,9 +1004,7 @@ void container_discover_outputs(struct sway_container *con) {
999 double new_scale = new_output ? new_output->wlr_output->scale : -1; 1004 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1000 if (old_scale != new_scale) { 1005 if (old_scale != new_scale) {
1001 container_update_title_textures(con); 1006 container_update_title_textures(con);
1002 if (con->view) { 1007 container_update_marks_textures(con);
1003 view_update_marks_textures(con->view);
1004 }
1005 } 1008 }
1006} 1009}
1007 1010
@@ -1221,3 +1224,142 @@ bool container_is_transient_for(struct sway_container *child,
1221 child->view && ancestor->view && 1224 child->view && ancestor->view &&
1222 view_is_transient_for(child->view, ancestor->view); 1225 view_is_transient_for(child->view, ancestor->view);
1223} 1226}
1227
1228static bool find_by_mark_iterator(struct sway_container *con, void *data) {
1229 char *mark = data;
1230 return container_has_mark(con, mark);
1231}
1232
1233struct sway_container *container_find_mark(char *mark) {
1234 return root_find_container(find_by_mark_iterator, mark);
1235}
1236
1237bool container_find_and_unmark(char *mark) {
1238 struct sway_container *con = root_find_container(
1239 find_by_mark_iterator, mark);
1240 if (!con) {
1241 return false;
1242 }
1243
1244 for (int i = 0; i < con->marks->length; ++i) {
1245 char *con_mark = con->marks->items[i];
1246 if (strcmp(con_mark, mark) == 0) {
1247 free(con_mark);
1248 list_del(con->marks, i);
1249 container_update_marks_textures(con);
1250 ipc_event_window(con, "mark");
1251 return true;
1252 }
1253 }
1254 return false;
1255}
1256
1257void container_clear_marks(struct sway_container *con) {
1258 list_foreach(con->marks, free);
1259 con->marks->length = 0;
1260 ipc_event_window(con, "mark");
1261}
1262
1263bool container_has_mark(struct sway_container *con, char *mark) {
1264 for (int i = 0; i < con->marks->length; ++i) {
1265 char *item = con->marks->items[i];
1266 if (strcmp(item, mark) == 0) {
1267 return true;
1268 }
1269 }
1270 return false;
1271}
1272
1273void container_add_mark(struct sway_container *con, char *mark) {
1274 list_add(con->marks, strdup(mark));
1275 ipc_event_window(con, "mark");
1276}
1277
1278static void update_marks_texture(struct sway_container *con,
1279 struct wlr_texture **texture, struct border_colors *class) {
1280 struct sway_output *output = container_get_effective_output(con);
1281 if (!output) {
1282 return;
1283 }
1284 if (*texture) {
1285 wlr_texture_destroy(*texture);
1286 *texture = NULL;
1287 }
1288 if (!con->marks->length) {
1289 return;
1290 }
1291
1292 size_t len = 0;
1293 for (int i = 0; i < con->marks->length; ++i) {
1294 char *mark = con->marks->items[i];
1295 if (mark[0] != '_') {
1296 len += strlen(mark) + 2;
1297 }
1298 }
1299 char *buffer = calloc(len + 1, 1);
1300 char *part = malloc(len + 1);
1301
1302 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
1303 free(buffer);
1304 return;
1305 }
1306
1307 for (int i = 0; i < con->marks->length; ++i) {
1308 char *mark = con->marks->items[i];
1309 if (mark[0] != '_') {
1310 sprintf(part, "[%s]", mark);
1311 strcat(buffer, part);
1312 }
1313 }
1314 free(part);
1315
1316 double scale = output->wlr_output->scale;
1317 int width = 0;
1318 int height = con->title_height * scale;
1319
1320 cairo_t *c = cairo_create(NULL);
1321 get_text_size(c, config->font, &width, NULL, NULL, scale, false,
1322 "%s", buffer);
1323 cairo_destroy(c);
1324
1325 cairo_surface_t *surface = cairo_image_surface_create(
1326 CAIRO_FORMAT_ARGB32, width, height);
1327 cairo_t *cairo = cairo_create(surface);
1328 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
1329 class->background[2], class->background[3]);
1330 cairo_paint(cairo);
1331 PangoContext *pango = pango_cairo_create_context(cairo);
1332 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
1333 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
1334 class->text[2], class->text[3]);
1335 cairo_move_to(cairo, 0, 0);
1336
1337 pango_printf(cairo, config->font, scale, false, "%s", buffer);
1338
1339 cairo_surface_flush(surface);
1340 unsigned char *data = cairo_image_surface_get_data(surface);
1341 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
1342 struct wlr_renderer *renderer = wlr_backend_get_renderer(
1343 output->wlr_output->backend);
1344 *texture = wlr_texture_from_pixels(
1345 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
1346 cairo_surface_destroy(surface);
1347 g_object_unref(pango);
1348 cairo_destroy(cairo);
1349 free(buffer);
1350}
1351
1352void container_update_marks_textures(struct sway_container *con) {
1353 if (!config->show_marks) {
1354 return;
1355 }
1356 update_marks_texture(con, &con->marks_focused,
1357 &config->border_colors.focused);
1358 update_marks_texture(con, &con->marks_focused_inactive,
1359 &config->border_colors.focused_inactive);
1360 update_marks_texture(con, &con->marks_unfocused,
1361 &config->border_colors.unfocused);
1362 update_marks_texture(con, &con->marks_urgent,
1363 &config->border_colors.urgent);
1364 container_damage_whole(con);
1365}
diff --git a/sway/tree/output.c b/sway/tree/output.c
index e5794b8a..2704920d 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -65,8 +65,13 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
65 return; 65 return;
66 } 66 }
67 struct wlr_output *wlr_output = output->wlr_output; 67 struct wlr_output *wlr_output = output->wlr_output;
68 size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
69 for (size_t i = 0; i < len; ++i) {
70 wl_list_init(&output->layers[i]);
71 }
72 wl_signal_init(&output->events.destroy);
73
68 output->enabled = true; 74 output->enabled = true;
69 apply_output_config(oc, output);
70 list_add(root->outputs, output); 75 list_add(root->outputs, output);
71 76
72 output->lx = wlr_output->lx; 77 output->lx = wlr_output->lx;
@@ -92,11 +97,8 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
92 ipc_event_workspace(NULL, ws, "init"); 97 ipc_event_workspace(NULL, ws, "init");
93 } 98 }
94 99
95 size_t len = sizeof(output->layers) / sizeof(output->layers[0]); 100
96 for (size_t i = 0; i < len; ++i) { 101 apply_output_config(oc, output);
97 wl_list_init(&output->layers[i]);
98 }
99 wl_signal_init(&output->events.destroy);
100 102
101 input_manager_configure_xcursor(); 103 input_manager_configure_xcursor();
102 104
@@ -274,16 +276,14 @@ struct sway_output *output_from_wlr_output(struct wlr_output *output) {
274} 276}
275 277
276struct sway_output *output_get_in_direction(struct sway_output *reference, 278struct sway_output *output_get_in_direction(struct sway_output *reference,
277 enum movement_direction direction) { 279 enum wlr_direction direction) {
278 enum wlr_direction wlr_dir = 0; 280 if (!sway_assert(direction, "got invalid direction: %d", direction)) {
279 if (!sway_assert(sway_dir_to_wlr(direction, &wlr_dir),
280 "got invalid direction: %d", direction)) {
281 return NULL; 281 return NULL;
282 } 282 }
283 int lx = reference->wlr_output->lx + reference->width / 2; 283 int lx = reference->wlr_output->lx + reference->width / 2;
284 int ly = reference->wlr_output->ly + reference->height / 2; 284 int ly = reference->wlr_output->ly + reference->height / 2;
285 struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( 285 struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output(
286 root->output_layout, wlr_dir, reference->wlr_output, lx, ly); 286 root->output_layout, direction, reference->wlr_output, lx, ly);
287 if (!wlr_adjacent) { 287 if (!wlr_adjacent) {
288 return NULL; 288 return NULL;
289 } 289 }
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 4bc9e0f3..1aa59e68 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -35,7 +35,6 @@ void view_init(struct sway_view *view, enum sway_view_type type,
35 view->type = type; 35 view->type = type;
36 view->impl = impl; 36 view->impl = impl;
37 view->executed_criteria = create_list(); 37 view->executed_criteria = create_list();
38 view->marks = create_list();
39 view->allow_request_urgent = true; 38 view->allow_request_urgent = true;
40 wl_signal_init(&view->events.unmap); 39 wl_signal_init(&view->events.unmap);
41} 40}
@@ -55,13 +54,6 @@ void view_destroy(struct sway_view *view) {
55 } 54 }
56 list_free(view->executed_criteria); 55 list_free(view->executed_criteria);
57 56
58 list_foreach(view->marks, free);
59 list_free(view->marks);
60
61 wlr_texture_destroy(view->marks_focused);
62 wlr_texture_destroy(view->marks_focused_inactive);
63 wlr_texture_destroy(view->marks_unfocused);
64 wlr_texture_destroy(view->marks_urgent);
65 free(view->title_format); 57 free(view->title_format);
66 58
67 if (view->impl->destroy) { 59 if (view->impl->destroy) {
@@ -225,21 +217,21 @@ void view_autoconfigure(struct sway_view *view) {
225 bool no_gaps = config->hide_edge_borders != E_SMART_NO_GAPS 217 bool no_gaps = config->hide_edge_borders != E_SMART_NO_GAPS
226 || !gaps_to_edge(view); 218 || !gaps_to_edge(view);
227 219
228 view->border_top = view->border_bottom = true; 220 con->border_top = con->border_bottom = true;
229 view->border_left = view->border_right = true; 221 con->border_left = con->border_right = true;
230 if (config->hide_edge_borders == E_BOTH 222 if (config->hide_edge_borders == E_BOTH
231 || config->hide_edge_borders == E_VERTICAL 223 || config->hide_edge_borders == E_VERTICAL
232 || (smart && !other_views && no_gaps)) { 224 || (smart && !other_views && no_gaps)) {
233 view->border_left = con->x - con->current_gaps != ws->x; 225 con->border_left = con->x - con->current_gaps != ws->x;
234 int right_x = con->x + con->width + con->current_gaps; 226 int right_x = con->x + con->width + con->current_gaps;
235 view->border_right = right_x != ws->x + ws->width; 227 con->border_right = right_x != ws->x + ws->width;
236 } 228 }
237 if (config->hide_edge_borders == E_BOTH 229 if (config->hide_edge_borders == E_BOTH
238 || config->hide_edge_borders == E_HORIZONTAL 230 || config->hide_edge_borders == E_HORIZONTAL
239 || (smart && !other_views && no_gaps)) { 231 || (smart && !other_views && no_gaps)) {
240 view->border_top = con->y - con->current_gaps != ws->y; 232 con->border_top = con->y - con->current_gaps != ws->y;
241 int bottom_y = con->y + con->height + con->current_gaps; 233 int bottom_y = con->y + con->height + con->current_gaps;
242 view->border_bottom = bottom_y != ws->y + ws->height; 234 con->border_bottom = bottom_y != ws->y + ws->height;
243 } 235 }
244 236
245 double x, y, width, height; 237 double x, y, width, height;
@@ -252,14 +244,14 @@ void view_autoconfigure(struct sway_view *view) {
252 enum sway_container_layout layout = container_parent_layout(con); 244 enum sway_container_layout layout = container_parent_layout(con);
253 if (layout == L_TABBED && !container_is_floating(con)) { 245 if (layout == L_TABBED && !container_is_floating(con)) {
254 y_offset = container_titlebar_height(); 246 y_offset = container_titlebar_height();
255 view->border_top = false; 247 con->border_top = false;
256 } else if (layout == L_STACKED && !container_is_floating(con)) { 248 } else if (layout == L_STACKED && !container_is_floating(con)) {
257 list_t *siblings = container_get_siblings(con); 249 list_t *siblings = container_get_siblings(con);
258 y_offset = container_titlebar_height() * siblings->length; 250 y_offset = container_titlebar_height() * siblings->length;
259 view->border_top = false; 251 con->border_top = false;
260 } 252 }
261 253
262 switch (view->border) { 254 switch (con->border) {
263 case B_CSD: 255 case B_CSD:
264 case B_NONE: 256 case B_NONE:
265 x = con->x; 257 x = con->x;
@@ -268,29 +260,29 @@ void view_autoconfigure(struct sway_view *view) {
268 height = con->height - y_offset; 260 height = con->height - y_offset;
269 break; 261 break;
270 case B_PIXEL: 262 case B_PIXEL:
271 x = con->x + view->border_thickness * view->border_left; 263 x = con->x + con->border_thickness * con->border_left;
272 y = con->y + view->border_thickness * view->border_top + y_offset; 264 y = con->y + con->border_thickness * con->border_top + y_offset;
273 width = con->width 265 width = con->width
274 - view->border_thickness * view->border_left 266 - con->border_thickness * con->border_left
275 - view->border_thickness * view->border_right; 267 - con->border_thickness * con->border_right;
276 height = con->height - y_offset 268 height = con->height - y_offset
277 - view->border_thickness * view->border_top 269 - con->border_thickness * con->border_top
278 - view->border_thickness * view->border_bottom; 270 - con->border_thickness * con->border_bottom;
279 break; 271 break;
280 case B_NORMAL: 272 case B_NORMAL:
281 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border 273 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border
282 x = con->x + view->border_thickness * view->border_left; 274 x = con->x + con->border_thickness * con->border_left;
283 width = con->width 275 width = con->width
284 - view->border_thickness * view->border_left 276 - con->border_thickness * con->border_left
285 - view->border_thickness * view->border_right; 277 - con->border_thickness * con->border_right;
286 if (y_offset) { 278 if (y_offset) {
287 y = con->y + y_offset; 279 y = con->y + y_offset;
288 height = con->height - y_offset 280 height = con->height - y_offset
289 - view->border_thickness * view->border_bottom; 281 - con->border_thickness * con->border_bottom;
290 } else { 282 } else {
291 y = con->y + container_titlebar_height(); 283 y = con->y + container_titlebar_height();
292 height = con->height - container_titlebar_height() 284 height = con->height - container_titlebar_height()
293 - view->border_thickness * view->border_bottom; 285 - con->border_thickness * con->border_bottom;
294 } 286 }
295 break; 287 break;
296 } 288 }
@@ -347,13 +339,14 @@ void view_set_csd_from_server(struct sway_view *view, bool enabled) {
347 339
348void view_update_csd_from_client(struct sway_view *view, bool enabled) { 340void view_update_csd_from_client(struct sway_view *view, bool enabled) {
349 wlr_log(WLR_DEBUG, "View %p updated CSD to %i", view, enabled); 341 wlr_log(WLR_DEBUG, "View %p updated CSD to %i", view, enabled);
350 if (enabled && view->border != B_CSD) { 342 struct sway_container *con = view->container;
351 view->saved_border = view->border; 343 if (enabled && con && con->border != B_CSD) {
352 if (view->container && container_is_floating(view->container)) { 344 con->saved_border = con->border;
353 view->border = B_CSD; 345 if (container_is_floating(con)) {
346 con->border = B_CSD;
354 } 347 }
355 } else if (!enabled && view->border == B_CSD) { 348 } else if (!enabled && con && con->border == B_CSD) {
356 view->border = view->saved_border; 349 con->border = con->saved_border;
357 } 350 }
358 view->using_csd = enabled; 351 view->using_csd = enabled;
359} 352}
@@ -584,12 +577,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
584 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; 577 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
585 578
586 if (view->impl->wants_floating && view->impl->wants_floating(view)) { 579 if (view->impl->wants_floating && view->impl->wants_floating(view)) {
587 view->border = config->floating_border; 580 view->container->border = config->floating_border;
588 view->border_thickness = config->floating_border_thickness; 581 view->container->border_thickness = config->floating_border_thickness;
589 container_set_floating(view->container, true); 582 container_set_floating(view->container, true);
590 } else { 583 } else {
591 view->border = config->border; 584 view->container->border = config->border;
592 view->border_thickness = config->border_thickness; 585 view->container->border_thickness = config->border_thickness;
593 view_set_tiled(view, true); 586 view_set_tiled(view, true);
594 } 587 }
595 588
@@ -936,153 +929,6 @@ void view_update_title(struct sway_view *view, bool force) {
936 ipc_event_window(view->container, "title"); 929 ipc_event_window(view->container, "title");
937} 930}
938 931
939static bool find_by_mark_iterator(struct sway_container *con,
940 void *data) {
941 char *mark = data;
942 return con->view && view_has_mark(con->view, mark);
943}
944
945struct sway_view *view_find_mark(char *mark) {
946 struct sway_container *container = root_find_container(
947 find_by_mark_iterator, mark);
948 if (!container) {
949 return NULL;
950 }
951 return container->view;
952}
953
954bool view_find_and_unmark(char *mark) {
955 struct sway_container *container = root_find_container(
956 find_by_mark_iterator, mark);
957 if (!container) {
958 return false;
959 }
960 struct sway_view *view = container->view;
961
962 for (int i = 0; i < view->marks->length; ++i) {
963 char *view_mark = view->marks->items[i];
964 if (strcmp(view_mark, mark) == 0) {
965 free(view_mark);
966 list_del(view->marks, i);
967 view_update_marks_textures(view);
968 ipc_event_window(container, "mark");
969 return true;
970 }
971 }
972 return false;
973}
974
975void view_clear_marks(struct sway_view *view) {
976 list_foreach(view->marks, free);
977 view->marks->length = 0;
978 ipc_event_window(view->container, "mark");
979}
980
981bool view_has_mark(struct sway_view *view, char *mark) {
982 for (int i = 0; i < view->marks->length; ++i) {
983 char *item = view->marks->items[i];
984 if (strcmp(item, mark) == 0) {
985 return true;
986 }
987 }
988 return false;
989}
990
991void view_add_mark(struct sway_view *view, char *mark) {
992 list_add(view->marks, strdup(mark));
993 ipc_event_window(view->container, "mark");
994}
995
996static void update_marks_texture(struct sway_view *view,
997 struct wlr_texture **texture, struct border_colors *class) {
998 struct sway_output *output =
999 container_get_effective_output(view->container);
1000 if (!output) {
1001 return;
1002 }
1003 if (*texture) {
1004 wlr_texture_destroy(*texture);
1005 *texture = NULL;
1006 }
1007 if (!view->marks->length) {
1008 return;
1009 }
1010
1011 size_t len = 0;
1012 for (int i = 0; i < view->marks->length; ++i) {
1013 char *mark = view->marks->items[i];
1014 if (mark[0] != '_') {
1015 len += strlen(mark) + 2;
1016 }
1017 }
1018 char *buffer = calloc(len + 1, 1);
1019 char *part = malloc(len + 1);
1020
1021 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
1022 free(buffer);
1023 return;
1024 }
1025
1026 for (int i = 0; i < view->marks->length; ++i) {
1027 char *mark = view->marks->items[i];
1028 if (mark[0] != '_') {
1029 sprintf(part, "[%s]", mark);
1030 strcat(buffer, part);
1031 }
1032 }
1033 free(part);
1034
1035 double scale = output->wlr_output->scale;
1036 int width = 0;
1037 int height = view->container->title_height * scale;
1038
1039 cairo_t *c = cairo_create(NULL);
1040 get_text_size(c, config->font, &width, NULL, NULL, scale, false,
1041 "%s", buffer);
1042 cairo_destroy(c);
1043
1044 cairo_surface_t *surface = cairo_image_surface_create(
1045 CAIRO_FORMAT_ARGB32, width, height);
1046 cairo_t *cairo = cairo_create(surface);
1047 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
1048 class->background[2], class->background[3]);
1049 cairo_paint(cairo);
1050 PangoContext *pango = pango_cairo_create_context(cairo);
1051 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
1052 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
1053 class->text[2], class->text[3]);
1054 cairo_move_to(cairo, 0, 0);
1055
1056 pango_printf(cairo, config->font, scale, false, "%s", buffer);
1057
1058 cairo_surface_flush(surface);
1059 unsigned char *data = cairo_image_surface_get_data(surface);
1060 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
1061 struct wlr_renderer *renderer = wlr_backend_get_renderer(
1062 output->wlr_output->backend);
1063 *texture = wlr_texture_from_pixels(
1064 renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
1065 cairo_surface_destroy(surface);
1066 g_object_unref(pango);
1067 cairo_destroy(cairo);
1068 free(buffer);
1069}
1070
1071void view_update_marks_textures(struct sway_view *view) {
1072 if (!config->show_marks) {
1073 return;
1074 }
1075 update_marks_texture(view, &view->marks_focused,
1076 &config->border_colors.focused);
1077 update_marks_texture(view, &view->marks_focused_inactive,
1078 &config->border_colors.focused_inactive);
1079 update_marks_texture(view, &view->marks_unfocused,
1080 &config->border_colors.unfocused);
1081 update_marks_texture(view, &view->marks_urgent,
1082 &config->border_colors.urgent);
1083 container_damage_whole(view->container);
1084}
1085
1086bool view_is_visible(struct sway_view *view) { 932bool view_is_visible(struct sway_view *view) {
1087 if (view->container->node.destroying) { 933 if (view->container->node.destroying) {
1088 return false; 934 return false;
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 27e9ac7a..05cda5c0 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -35,6 +35,10 @@ struct sway_output *workspace_get_initial_output(const char *name) {
35 struct workspace_config *wsc = workspace_find_config(name); 35 struct workspace_config *wsc = workspace_find_config(name);
36 if (wsc && wsc->output) { 36 if (wsc && wsc->output) {
37 struct sway_output *output = output_by_name(wsc->output); 37 struct sway_output *output = output_by_name(wsc->output);
38 if (!output) {
39 output = output_by_identifier(wsc->output);
40 }
41
38 if (output) { 42 if (output) {
39 return output; 43 return output;
40 } 44 }
@@ -143,7 +147,11 @@ void workspace_consider_destroy(struct sway_workspace *ws) {
143static bool workspace_valid_on_output(const char *output_name, 147static bool workspace_valid_on_output(const char *output_name,
144 const char *ws_name) { 148 const char *ws_name) {
145 struct workspace_config *wsc = workspace_find_config(ws_name); 149 struct workspace_config *wsc = workspace_find_config(ws_name);
146 return !wsc || !wsc->output || strcmp(wsc->output, output_name) == 0; 150 char identifier[128];
151 struct sway_output *output = output_by_name(output_name);
152 output_get_identifier(identifier, sizeof(identifier), output);
153
154 return !wsc || !wsc->output || strcmp(wsc->output, output_name) == 0 || strcasecmp(identifier, output_name) == 0;
147} 155}
148 156
149static void workspace_name_from_binding(const struct sway_binding * binding, 157static void workspace_name_from_binding(const struct sway_binding * binding,
diff --git a/swayidle/main.c b/swayidle/main.c
index 93f4c94b..7d0f23f4 100644
--- a/swayidle/main.c
+++ b/swayidle/main.c
@@ -1,4 +1,5 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
2#include <errno.h> 3#include <errno.h>
3#include <getopt.h> 4#include <getopt.h>
4#include <pthread.h> 5#include <pthread.h>
@@ -25,38 +26,24 @@
25#include <elogind/sd-login.h> 26#include <elogind/sd-login.h>
26#endif 27#endif
27 28
28typedef void (*timer_callback_func)(void *data);
29
30static struct org_kde_kwin_idle *idle_manager = NULL; 29static struct org_kde_kwin_idle *idle_manager = NULL;
31static struct wl_seat *seat = NULL; 30static struct wl_seat *seat = NULL;
32bool debug = false;
33 31
34struct swayidle_state { 32struct swayidle_state {
35 struct wl_display *display; 33 struct wl_display *display;
36 struct org_kde_kwin_idle_timeout *idle_timer;
37 struct org_kde_kwin_idle_timeout *lock_timer;
38 struct wl_event_loop *event_loop; 34 struct wl_event_loop *event_loop;
39 list_t *timeout_cmds; 35 list_t *timeout_cmds; // struct swayidle_timeout_cmd *
36 char *lock_cmd;
40} state; 37} state;
41 38
42struct swayidle_cmd {
43 timer_callback_func callback;
44 char *param;
45};
46
47struct swayidle_cmd *lock_cmd = NULL;
48
49struct swayidle_timeout_cmd { 39struct swayidle_timeout_cmd {
50 uint32_t timeout; 40 int timeout, registered_timeout;
51 struct swayidle_cmd *idle_cmd; 41 struct org_kde_kwin_idle_timeout *idle_timer;
52 struct swayidle_cmd *resume_cmd; 42 char *idle_cmd;
43 char *resume_cmd;
53}; 44};
54 45
55static void cmd_exec(void *data) { 46static void cmd_exec(char *param) {
56 if (data == NULL) {
57 return;
58 }
59 char *param = (char *)data;
60 wlr_log(WLR_DEBUG, "Cmd exec %s", param); 47 wlr_log(WLR_DEBUG, "Cmd exec %s", param);
61 pid_t pid = fork(); 48 pid_t pid = fork();
62 if (pid == 0) { 49 if (pid == 0) {
@@ -82,6 +69,7 @@ static void cmd_exec(void *data) {
82#if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND) 69#if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND)
83static int lock_fd = -1; 70static int lock_fd = -1;
84static int ongoing_fd = -1; 71static int ongoing_fd = -1;
72static struct sd_bus *bus = NULL;
85 73
86static int release_lock(void *data) { 74static int release_lock(void *data) {
87 wlr_log(WLR_INFO, "Releasing sleep lock %d", ongoing_fd); 75 wlr_log(WLR_INFO, "Releasing sleep lock %d", ongoing_fd);
@@ -92,19 +80,10 @@ static int release_lock(void *data) {
92 return 0; 80 return 0;
93} 81}
94 82
95void acquire_sleep_lock(void) { 83static void acquire_sleep_lock(void) {
96 sd_bus_message *msg = NULL; 84 sd_bus_message *msg;
97 sd_bus_error error = SD_BUS_ERROR_NULL; 85 sd_bus_error error;
98 struct sd_bus *bus; 86 int ret = sd_bus_call_method(bus, "org.freedesktop.login1",
99 int ret = sd_bus_default_system(&bus);
100
101 if (ret < 0) {
102 wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s",
103 strerror(-ret));
104 return;
105 }
106
107 ret = sd_bus_call_method(bus, "org.freedesktop.login1",
108 "/org/freedesktop/login1", 87 "/org/freedesktop/login1",
109 "org.freedesktop.login1.Manager", "Inhibit", 88 "org.freedesktop.login1.Manager", "Inhibit",
110 &error, &msg, "ssss", "sleep", "swayidle", 89 &error, &msg, "ssss", "sleep", "swayidle",
@@ -112,14 +91,16 @@ void acquire_sleep_lock(void) {
112 if (ret < 0) { 91 if (ret < 0) {
113 wlr_log(WLR_ERROR, "Failed to send Inhibit signal: %s", 92 wlr_log(WLR_ERROR, "Failed to send Inhibit signal: %s",
114 strerror(-ret)); 93 strerror(-ret));
115 } else { 94 return;
116 ret = sd_bus_message_read(msg, "h", &lock_fd); 95 }
117 if (ret < 0) { 96
118 wlr_log(WLR_ERROR, 97 ret = sd_bus_message_read(msg, "h", &lock_fd);
119 "Failed to parse D-Bus response for Inhibit: %s", 98 if (ret < 0) {
120 strerror(-ret)); 99 wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
121 } 100 strerror(-ret));
101 return;
122 } 102 }
103
123 wlr_log(WLR_INFO, "Got sleep lock: %d", lock_fd); 104 wlr_log(WLR_INFO, "Got sleep lock: %d", lock_fd);
124} 105}
125 106
@@ -140,8 +121,8 @@ static int prepare_for_sleep(sd_bus_message *msg, void *userdata,
140 121
141 ongoing_fd = lock_fd; 122 ongoing_fd = lock_fd;
142 123
143 if (lock_cmd && lock_cmd->callback) { 124 if (state.lock_cmd) {
144 lock_cmd->callback(lock_cmd->param); 125 cmd_exec(state.lock_cmd);
145 } 126 }
146 127
147 if (ongoing_fd >= 0) { 128 if (ongoing_fd >= 0) {
@@ -149,6 +130,7 @@ static int prepare_for_sleep(sd_bus_message *msg, void *userdata,
149 wl_event_loop_add_timer(state.event_loop, release_lock, NULL); 130 wl_event_loop_add_timer(state.event_loop, release_lock, NULL);
150 wl_event_source_timer_update(source, 1000); 131 wl_event_source_timer_update(source, 1000);
151 } 132 }
133
152 wlr_log(WLR_DEBUG, "Prepare for sleep done"); 134 wlr_log(WLR_DEBUG, "Prepare for sleep done");
153 return 0; 135 return 0;
154} 136}
@@ -161,9 +143,7 @@ static int dbus_event(int fd, uint32_t mask, void *data) {
161 return 1; 143 return 1;
162} 144}
163 145
164void setup_sleep_listener(void) { 146static void setup_sleep_listener(void) {
165 struct sd_bus *bus;
166
167 int ret = sd_bus_default_system(&bus); 147 int ret = sd_bus_default_system(&bus);
168 if (ret < 0) { 148 if (ret < 0) {
169 wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s", 149 wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s",
@@ -203,6 +183,7 @@ static void handle_global(void *data, struct wl_registry *registry,
203 183
204static void handle_global_remove(void *data, struct wl_registry *registry, 184static void handle_global_remove(void *data, struct wl_registry *registry,
205 uint32_t name) { 185 uint32_t name) {
186 // Who cares
206} 187}
207 188
208static const struct wl_registry_listener registry_listener = { 189static const struct wl_registry_listener registry_listener = {
@@ -210,19 +191,42 @@ static const struct wl_registry_listener registry_listener = {
210 .global_remove = handle_global_remove, 191 .global_remove = handle_global_remove,
211}; 192};
212 193
194static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener;
195
196static void register_timeout(struct swayidle_timeout_cmd *cmd,
197 int timeout) {
198 if (cmd->idle_timer != NULL) {
199 org_kde_kwin_idle_timeout_destroy(cmd->idle_timer);
200 cmd->idle_timer = NULL;
201 }
202 if (timeout < 0) {
203 wlr_log(WLR_DEBUG, "Not registering idle timeout");
204 return;
205 }
206 wlr_log(WLR_DEBUG, "Register with timeout: %d", timeout);
207 cmd->idle_timer =
208 org_kde_kwin_idle_get_idle_timeout(idle_manager, seat, timeout);
209 org_kde_kwin_idle_timeout_add_listener(cmd->idle_timer,
210 &idle_timer_listener, cmd);
211 cmd->registered_timeout = timeout;
212}
213
213static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) { 214static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) {
214 struct swayidle_timeout_cmd *cmd = data; 215 struct swayidle_timeout_cmd *cmd = data;
215 wlr_log(WLR_DEBUG, "idle state"); 216 wlr_log(WLR_DEBUG, "idle state");
216 if (cmd && cmd->idle_cmd && cmd->idle_cmd->callback) { 217 if (cmd->idle_cmd) {
217 cmd->idle_cmd->callback(cmd->idle_cmd->param); 218 cmd_exec(cmd->idle_cmd);
218 } 219 }
219} 220}
220 221
221static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) { 222static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) {
222 struct swayidle_timeout_cmd *cmd = data; 223 struct swayidle_timeout_cmd *cmd = data;
223 wlr_log(WLR_DEBUG, "active state"); 224 wlr_log(WLR_DEBUG, "active state");
224 if (cmd && cmd->resume_cmd && cmd->resume_cmd->callback) { 225 if (cmd->registered_timeout != cmd->timeout) {
225 cmd->resume_cmd->callback(cmd->resume_cmd->param); 226 register_timeout(cmd, cmd->timeout);
227 }
228 if (cmd->resume_cmd) {
229 cmd_exec(cmd->resume_cmd);
226 } 230 }
227} 231}
228 232
@@ -231,20 +235,17 @@ static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
231 .resumed = handle_resume, 235 .resumed = handle_resume,
232}; 236};
233 237
234struct swayidle_cmd *parse_command(int argc, char **argv) { 238static char *parse_command(int argc, char **argv) {
235 if (argc < 1) { 239 if (argc < 1) {
236 wlr_log(WLR_ERROR, "Too few parameters for command in parse_command"); 240 wlr_log(WLR_ERROR, "Missing command");
237 return NULL; 241 return NULL;
238 } 242 }
239 243
240 struct swayidle_cmd *cmd = calloc(1, sizeof(struct swayidle_cmd));
241 wlr_log(WLR_DEBUG, "Command: %s", argv[0]); 244 wlr_log(WLR_DEBUG, "Command: %s", argv[0]);
242 cmd->callback = cmd_exec; 245 return strdup(argv[0]);
243 cmd->param = argv[0];
244 return cmd;
245} 246}
246 247
247int parse_timeout(int argc, char **argv) { 248static int parse_timeout(int argc, char **argv) {
248 if (argc < 3) { 249 if (argc < 3) {
249 wlr_log(WLR_ERROR, "Too few parameters to timeout command. " 250 wlr_log(WLR_ERROR, "Too few parameters to timeout command. "
250 "Usage: timeout <seconds> <command>"); 251 "Usage: timeout <seconds> <command>");
@@ -258,9 +259,15 @@ int parse_timeout(int argc, char **argv) {
258 "numeric value representing seconds", optarg); 259 "numeric value representing seconds", optarg);
259 exit(-1); 260 exit(-1);
260 } 261 }
262
261 struct swayidle_timeout_cmd *cmd = 263 struct swayidle_timeout_cmd *cmd =
262 calloc(1, sizeof(struct swayidle_timeout_cmd)); 264 calloc(1, sizeof(struct swayidle_timeout_cmd));
263 cmd->timeout = seconds * 1000; 265
266 if (seconds > 0) {
267 cmd->timeout = seconds * 1000;
268 } else {
269 cmd->timeout = -1;
270 }
264 271
265 wlr_log(WLR_DEBUG, "Register idle timeout at %d ms", cmd->timeout); 272 wlr_log(WLR_DEBUG, "Register idle timeout at %d ms", cmd->timeout);
266 wlr_log(WLR_DEBUG, "Setup idle"); 273 wlr_log(WLR_DEBUG, "Setup idle");
@@ -276,27 +283,27 @@ int parse_timeout(int argc, char **argv) {
276 return result; 283 return result;
277} 284}
278 285
279int parse_sleep(int argc, char **argv) { 286static int parse_sleep(int argc, char **argv) {
280 if (argc < 2) { 287 if (argc < 2) {
281 wlr_log(WLR_ERROR, "Too few parameters to before-sleep command. " 288 wlr_log(WLR_ERROR, "Too few parameters to before-sleep command. "
282 "Usage: before-sleep <command>"); 289 "Usage: before-sleep <command>");
283 exit(-1); 290 exit(-1);
284 } 291 }
285 292
286 lock_cmd = parse_command(argc - 1, &argv[1]); 293 state.lock_cmd = parse_command(argc - 1, &argv[1]);
287 if (lock_cmd) { 294 if (state.lock_cmd) {
288 wlr_log(WLR_DEBUG, "Setup sleep lock: %s", lock_cmd->param); 295 wlr_log(WLR_DEBUG, "Setup sleep lock: %s", state.lock_cmd);
289 } 296 }
290 297
291 return 2; 298 return 2;
292} 299}
293 300
301static int parse_args(int argc, char *argv[]) {
302 bool debug = false;
294 303
295int parse_args(int argc, char *argv[]) {
296 int c; 304 int c;
297 305 while ((c = getopt(argc, argv, "hd")) != -1) {
298 while ((c = getopt(argc, argv, "hs:d")) != -1) { 306 switch (c) {
299 switch(c) {
300 case 'd': 307 case 'd':
301 debug = true; 308 debug = true;
302 break; 309 break;
@@ -311,13 +318,7 @@ int parse_args(int argc, char *argv[]) {
311 } 318 }
312 } 319 }
313 320
314 if (debug) { 321 wlr_log_init(debug ? WLR_DEBUG : WLR_INFO, NULL);
315 wlr_log_init(WLR_DEBUG, NULL);
316 wlr_log(WLR_DEBUG, "Loglevel debug");
317 } else {
318 wlr_log_init(WLR_INFO, NULL);
319 }
320
321 322
322 state.timeout_cmds = create_list(); 323 state.timeout_cmds = create_list();
323 324
@@ -331,24 +332,36 @@ int parse_args(int argc, char *argv[]) {
331 i += parse_sleep(argc - i, &argv[i]); 332 i += parse_sleep(argc - i, &argv[i]);
332 } else { 333 } else {
333 wlr_log(WLR_ERROR, "Unsupported command '%s'", argv[i]); 334 wlr_log(WLR_ERROR, "Unsupported command '%s'", argv[i]);
334 exit(-1); 335 return 1;
335 } 336 }
336 } 337 }
338
337 return 0; 339 return 0;
338} 340}
339 341
340void sway_terminate(int exit_code) { 342void sway_terminate(int exit_code) {
341 if (state.event_loop) { 343 wl_display_disconnect(state.display);
342 wl_event_loop_destroy(state.event_loop); 344 wl_event_loop_destroy(state.event_loop);
343 }
344 if (state.display) {
345 wl_display_disconnect(state.display);
346 }
347 exit(exit_code); 345 exit(exit_code);
348} 346}
349 347
350void sig_handler(int signal) { 348static void register_zero_idle_timeout(void *item) {
351 sway_terminate(0); 349 struct swayidle_timeout_cmd *cmd = item;
350 register_timeout(cmd, 0);
351}
352
353static int handle_signal(int sig, void *data) {
354 switch (sig) {
355 case SIGINT:
356 case SIGTERM:
357 sway_terminate(0);
358 return 0;
359 case SIGUSR1:
360 wlr_log(WLR_DEBUG, "Got SIGUSR1");
361 list_foreach(state.timeout_cmds, register_zero_idle_timeout);
362 return 1;
363 }
364 assert(false); // not reached
352} 365}
353 366
354static int display_event(int fd, uint32_t mask, void *data) { 367static int display_event(int fd, uint32_t mask, void *data) {
@@ -359,33 +372,26 @@ static int display_event(int fd, uint32_t mask, void *data) {
359 wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting"); 372 wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting");
360 sway_terminate(0); 373 sway_terminate(0);
361 } 374 }
375 wl_display_flush(state.display);
362 return 0; 376 return 0;
363} 377}
364 378
365void register_idle_timeout(void *item) { 379static void register_idle_timeout(void *item) {
366 struct swayidle_timeout_cmd *cmd = item; 380 struct swayidle_timeout_cmd *cmd = item;
367 if (cmd == NULL || !cmd->timeout) { 381 register_timeout(cmd, cmd->timeout);
368 wlr_log(WLR_ERROR, "Invalid idle cmd, will not register");
369 return;
370 }
371 state.idle_timer =
372 org_kde_kwin_idle_get_idle_timeout(idle_manager, seat, cmd->timeout);
373 if (state.idle_timer != NULL) {
374 org_kde_kwin_idle_timeout_add_listener(state.idle_timer,
375 &idle_timer_listener, cmd);
376 } else {
377 wlr_log(WLR_ERROR, "Could not create idle timer");
378 }
379} 382}
380 383
381int main(int argc, char *argv[]) { 384int main(int argc, char *argv[]) {
382 signal(SIGINT, sig_handler);
383 signal(SIGTERM, sig_handler);
384
385 if (parse_args(argc, argv) != 0) { 385 if (parse_args(argc, argv) != 0) {
386 return -1; 386 return -1;
387 } 387 }
388 388
389 state.event_loop = wl_event_loop_create();
390
391 wl_event_loop_add_signal(state.event_loop, SIGINT, handle_signal, NULL);
392 wl_event_loop_add_signal(state.event_loop, SIGTERM, handle_signal, NULL);
393 wl_event_loop_add_signal(state.event_loop, SIGUSR1, handle_signal, NULL);
394
389 state.display = wl_display_connect(NULL); 395 state.display = wl_display_connect(NULL);
390 if (state.display == NULL) { 396 if (state.display == NULL) {
391 wlr_log(WLR_ERROR, "Unable to connect to the compositor. " 397 wlr_log(WLR_ERROR, "Unable to connect to the compositor. "
@@ -397,7 +403,6 @@ int main(int argc, char *argv[]) {
397 struct wl_registry *registry = wl_display_get_registry(state.display); 403 struct wl_registry *registry = wl_display_get_registry(state.display);
398 wl_registry_add_listener(registry, &registry_listener, NULL); 404 wl_registry_add_listener(registry, &registry_listener, NULL);
399 wl_display_roundtrip(state.display); 405 wl_display_roundtrip(state.display);
400 state.event_loop = wl_event_loop_create();
401 406
402 if (idle_manager == NULL) { 407 if (idle_manager == NULL) {
403 wlr_log(WLR_ERROR, "Display doesn't support idle protocol"); 408 wlr_log(WLR_ERROR, "Display doesn't support idle protocol");
@@ -410,7 +415,7 @@ int main(int argc, char *argv[]) {
410 415
411 bool should_run = state.timeout_cmds->length > 0; 416 bool should_run = state.timeout_cmds->length > 0;
412#if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND) 417#if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND)
413 if (lock_cmd) { 418 if (state.lock_cmd) {
414 should_run = true; 419 should_run = true;
415 setup_sleep_listener(); 420 setup_sleep_listener();
416 } 421 }
@@ -419,12 +424,15 @@ int main(int argc, char *argv[]) {
419 wlr_log(WLR_INFO, "No command specified! Nothing to do, will exit"); 424 wlr_log(WLR_INFO, "No command specified! Nothing to do, will exit");
420 sway_terminate(0); 425 sway_terminate(0);
421 } 426 }
427
422 list_foreach(state.timeout_cmds, register_idle_timeout); 428 list_foreach(state.timeout_cmds, register_idle_timeout);
423 429
424 wl_display_roundtrip(state.display); 430 wl_display_roundtrip(state.display);
425 431
426 wl_event_loop_add_fd(state.event_loop, wl_display_get_fd(state.display), 432 struct wl_event_source *source = wl_event_loop_add_fd(state.event_loop,
427 WL_EVENT_READABLE, display_event, NULL); 433 wl_display_get_fd(state.display), WL_EVENT_READABLE,
434 display_event, NULL);
435 wl_event_source_check(source);
428 436
429 while (wl_event_loop_dispatch(state.event_loop, -1) != 1) { 437 while (wl_event_loop_dispatch(state.event_loop, -1) != 1) {
430 // This space intentionally left blank 438 // This space intentionally left blank
diff --git a/swayidle/swayidle.1.scd b/swayidle/swayidle.1.scd
index 7c1b138a..3083163f 100644
--- a/swayidle/swayidle.1.scd
+++ b/swayidle/swayidle.1.scd
@@ -22,11 +22,13 @@ swayidle listens for idle activity on your Wayland compositor and executes tasks
22on various idle-related events. You can specify any number of events at the 22on various idle-related events. You can specify any number of events at the
23command line. 23command line.
24 24
25Sending SIGUSR1 to swayidle will immediately enter idle state.
26
25# EVENTS 27# EVENTS
26 28
27*timeout* <timeout> <timeout command> [resume <resume command>] 29*timeout* <timeout> <timeout command> [resume <resume command>]
28 Execute _timeout command_ if there is no activity for <timeout> seconds. 30 Execute _timeout command_ if there is no activity for <timeout> seconds.
29 31
30 If you specify "resume <resume command>", _resume command_ will be run when 32 If you specify "resume <resume command>", _resume command_ will be run when
31 there is activity again. 33 there is activity again.
32 34
@@ -39,11 +41,11 @@ All commands are executed in a shell.
39# EXAMPLE 41# EXAMPLE
40 42
41``` 43```
42 swayidle \ 44swayidle \
43 timeout 300 'swaylock -c 000000' \ 45 timeout 300 'swaylock -c 000000' \
44 timeout 600 'swaymsg "output * dpms off"' \ 46 timeout 600 'swaymsg "output * dpms off"' \
45 resume 'swaymsg "output * dpms on"' \ 47 resume 'swaymsg "output * dpms on"' \
46 before-sleep 'swaylock -c 000000' 48 before-sleep 'swaylock -c 000000'
47``` 49```
48 50
49This will lock your screen after 300 seconds of inactivity, then turn off your 51This will lock your screen after 300 seconds of inactivity, then turn off your
@@ -58,4 +60,4 @@ https://github.com/swaywm/sway.
58 60
59# SEE ALSO 61# SEE ALSO
60 62
61*sway*(5) *swaymsg*(1) *sway-input*(5) *sway-bar*(5) 63*sway*(5) *swaymsg*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5)
diff --git a/swaylock/swaylock.1.scd b/swaylock/swaylock.1.scd
index 3107124f..8ddc7d3a 100644
--- a/swaylock/swaylock.1.scd
+++ b/swaylock/swaylock.1.scd
@@ -21,20 +21,25 @@ Locks your Wayland session.
21 All leading dashes should be omitted and the equals sign is required for 21 All leading dashes should be omitted and the equals sign is required for
22 flags that take an argument. 22 flags that take an argument.
23 23
24*-c, --color* <rrggbb[aa]>
25 Turn the screen into the given color. If -i is used, this sets the
26 background of the image to the given color. Defaults to white (FFFFFF), or
27 transparent (00000000) if an image is in use.
28
29*-e, --ignore-empty-password* 24*-e, --ignore-empty-password*
30 When an empty password is provided by the user, do not validate it. 25 When an empty password is provided by the user, do not validate it.
31 26
32*-f, --daemonize* 27*-f, --daemonize*
33 Detach from the controlling terminal after locking. 28 Detach from the controlling terminal after locking.
34 29
30 Note: this is the default bahavior of i3lock.
31
35*-h, --help* 32*-h, --help*
36 Show help message and quit. 33 Show help message and quit.
37 34
35*-v, --version*
36 Show the version number and quit.
37
38# APPEARANCE
39
40*-u, --no-unlock-indicator*
41 Disable the unlock indicator.
42
38*-i, --image* [<output>:]<path> 43*-i, --image* [<output>:]<path>
39 Display the given image, optionally only on the given output. Use -c to set 44 Display the given image, optionally only on the given output. Use -c to set
40 a background color. 45 a background color.
@@ -45,13 +50,10 @@ Locks your Wayland session.
45*-t, --tiling* 50*-t, --tiling*
46 Same as --scaling=tile. 51 Same as --scaling=tile.
47 52
48*-u, --no-unlock-indicator* 53*-c, --color* <rrggbb[aa]>
49 Disable the unlock indicator. 54 Turn the screen into the given color. If -i is used, this sets the
50 55 background of the image to the given color. Defaults to white (FFFFFF), or
51*-v, --version* 56 transparent (00000000) if an image is in use.
52 Show the version number and quit.
53
54# APPEARANCE
55 57
56*--bs-hl-color* <rrggbb[aa]> 58*--bs-hl-color* <rrggbb[aa]>
57 Sets the color of backspace highlight segments. 59 Sets the color of backspace highlight segments.
diff --git a/swaymsg/main.c b/swaymsg/main.c
index 4688737c..e13dd7ec 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -23,7 +23,7 @@ static bool success_object(json_object *result) {
23 json_object *success; 23 json_object *success;
24 24
25 if (!json_object_object_get_ex(result, "success", &success)) { 25 if (!json_object_object_get_ex(result, "success", &success)) {
26 return false; 26 return true;
27 } 27 }
28 28
29 return json_object_get_boolean(success); 29 return json_object_get_boolean(success);
@@ -183,13 +183,15 @@ static void pretty_print_output(json_object *o) {
183 json_object_object_get_ex(rect, "height", &height); 183 json_object_object_get_ex(rect, "height", &height);
184 json_object *modes; 184 json_object *modes;
185 json_object_object_get_ex(o, "modes", &modes); 185 json_object_object_get_ex(o, "modes", &modes);
186 json_object *current_mode;
187 json_object_object_get_ex(o, "current_mode", &current_mode);
186 188
187 if (json_object_get_boolean(active)) { 189 if (json_object_get_boolean(active)) {
188 printf( 190 printf(
189 "Output %s '%s %s %s'%s\n" 191 "Output %s '%s %s %s'%s\n"
190 " Current mode: %dx%d @ %f Hz\n" 192 " Current mode: %dx%d @ %f Hz\n"
191 " Position: %d,%d\n" 193 " Position: %d,%d\n"
192 " Scale factor: %dx\n" 194 " Scale factor: %f\n"
193 " Transform: %s\n" 195 " Transform: %s\n"
194 " Workspace: %s\n", 196 " Workspace: %s\n",
195 json_object_get_string(name), 197 json_object_get_string(name),
@@ -197,10 +199,13 @@ static void pretty_print_output(json_object *o) {
197 json_object_get_string(model), 199 json_object_get_string(model),
198 json_object_get_string(serial), 200 json_object_get_string(serial),
199 json_object_get_boolean(focused) ? " (focused)" : "", 201 json_object_get_boolean(focused) ? " (focused)" : "",
200 json_object_get_int(width), json_object_get_int(height), 202 json_object_get_int(
203 json_object_object_get(current_mode, "width")),
204 json_object_get_int(
205 json_object_object_get(current_mode, "height")),
201 (float)json_object_get_int(refresh) / 1000, 206 (float)json_object_get_int(refresh) / 1000,
202 json_object_get_int(x), json_object_get_int(y), 207 json_object_get_int(x), json_object_get_int(y),
203 json_object_get_int(scale), 208 json_object_get_double(scale),
204 json_object_get_string(transform), 209 json_object_get_string(transform),
205 json_object_get_string(ws) 210 json_object_get_string(ws)
206 ); 211 );