aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-07-22 14:10:40 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-07-23 08:24:32 +1000
commit81e8f31cc6f284b54ab206e14af7ecbc1a9ed1bb (patch)
treeea124a94869d06edb3f98e85ecd649275e86ef91
parentMerge pull request #2328 from emersion/xdg-shell-set-title (diff)
downloadsway-81e8f31cc6f284b54ab206e14af7ecbc1a9ed1bb.tar.gz
sway-81e8f31cc6f284b54ab206e14af7ecbc1a9ed1bb.tar.zst
sway-81e8f31cc6f284b54ab206e14af7ecbc1a9ed1bb.zip
Implement scratchpad
Implements the following commands: * move scratchpad * scratchpad show * [criteria] scratchpad show Also fixes these: * Fix memory leak when executing command with criteria (use `list_free(views)` instead of `free(views)`) * Fix crash when running `move to` with no further arguments
-rw-r--r--include/sway/scratchpad.h26
-rw-r--r--include/sway/server.h2
-rw-r--r--include/sway/tree/container.h5
-rw-r--r--sway/commands.c3
-rw-r--r--sway/commands/move.c19
-rw-r--r--sway/commands/scratchpad.c37
-rw-r--r--sway/criteria.c8
-rw-r--r--sway/meson.build2
-rw-r--r--sway/scratchpad.c173
-rw-r--r--sway/server.c3
-rw-r--r--sway/tree/container.c8
-rw-r--r--sway/tree/layout.c7
-rw-r--r--sway/tree/view.c2
13 files changed, 290 insertions, 5 deletions
diff --git a/include/sway/scratchpad.h b/include/sway/scratchpad.h
new file mode 100644
index 00000000..5af5256f
--- /dev/null
+++ b/include/sway/scratchpad.h
@@ -0,0 +1,26 @@
1#ifndef _SWAY_SCRATCHPAD_H
2#define _SWAY_SCRATCHPAD_H
3
4#include "tree/container.h"
5
6/**
7 * Move a container to the scratchpad.
8 */
9void scratchpad_add_container(struct sway_container *con);
10
11/**
12 * Remove a container from the scratchpad.
13 */
14void scratchpad_remove_container(struct sway_container *con);
15
16/**
17 * Show or hide the next container on the scratchpad.
18 */
19void scratchpad_toggle_auto(void);
20
21/**
22 * Show or hide a specific container on the scratchpad.
23 */
24void scratchpad_toggle_container(struct sway_container *con);
25
26#endif
diff --git a/include/sway/server.h b/include/sway/server.h
index 70bde6d4..6cef2e58 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -48,6 +48,8 @@ struct sway_server {
48 48
49 list_t *transactions; 49 list_t *transactions;
50 list_t *dirty_containers; 50 list_t *dirty_containers;
51
52 list_t *scratchpad; // struct sway_container
51}; 53};
52 54
53struct sway_server server; 55struct sway_server server;
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 59c5b4c7..2a4be18c 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -135,6 +135,11 @@ struct sway_container {
135 135
136 struct sway_container *parent; 136 struct sway_container *parent;
137 137
138 // Indicates that the container is a scratchpad container.
139 // Both hidden and visible scratchpad containers have scratchpad=true.
140 // Hidden scratchpad containers have a NULL parent.
141 bool scratchpad;
142
138 float alpha; 143 float alpha;
139 144
140 struct wlr_texture *title_focused; 145 struct wlr_texture *title_focused;
diff --git a/sway/commands.c b/sway/commands.c
index f40f0e9d..fdae1961 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -149,6 +149,7 @@ static struct cmd_handler command_handlers[] = {
149 { "reload", cmd_reload }, 149 { "reload", cmd_reload },
150 { "rename", cmd_rename }, 150 { "rename", cmd_rename },
151 { "resize", cmd_resize }, 151 { "resize", cmd_resize },
152 { "scratchpad", cmd_scratchpad },
152 { "split", cmd_split }, 153 { "split", cmd_split },
153 { "splith", cmd_splith }, 154 { "splith", cmd_splith },
154 { "splitt", cmd_splitt }, 155 { "splitt", cmd_splitt },
@@ -326,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
326 } while(head); 327 } while(head);
327cleanup: 328cleanup:
328 free(exec); 329 free(exec);
329 free(views); 330 list_free(views);
330 if (!results) { 331 if (!results) {
331 results = cmd_results_new(CMD_SUCCESS, NULL, NULL); 332 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
332 } 333 }
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 6ec050a8..1940043d 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -9,6 +9,7 @@
9#include "sway/input/cursor.h" 9#include "sway/input/cursor.h"
10#include "sway/input/seat.h" 10#include "sway/input/seat.h"
11#include "sway/output.h" 11#include "sway/output.h"
12#include "sway/scratchpad.h"
12#include "sway/tree/arrange.h" 13#include "sway/tree/arrange.h"
13#include "sway/tree/container.h" 14#include "sway/tree/container.h"
14#include "sway/tree/layout.h" 15#include "sway/tree/layout.h"
@@ -296,6 +297,19 @@ static struct cmd_results *move_to_position(struct sway_container *container,
296 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 297 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
297} 298}
298 299
300static struct cmd_results *move_to_scratchpad(struct sway_container *con) {
301 if (con->type != C_CONTAINER && con->type != C_VIEW) {
302 return cmd_results_new(CMD_INVALID, "move",
303 "Only views and containers can be moved to the scratchpad");
304 }
305 if (con->scratchpad) {
306 return cmd_results_new(CMD_INVALID, "move",
307 "Container is already in the scratchpad");
308 }
309 scratchpad_add_container(con);
310 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
311}
312
299struct cmd_results *cmd_move(int argc, char **argv) { 313struct cmd_results *cmd_move(int argc, char **argv) {
300 struct cmd_results *error = NULL; 314 struct cmd_results *error = NULL;
301 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { 315 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
@@ -317,10 +331,9 @@ struct cmd_results *cmd_move(int argc, char **argv) {
317 } else if (strcasecmp(argv[0], "workspace") == 0) { 331 } else if (strcasecmp(argv[0], "workspace") == 0) {
318 return cmd_move_workspace(current, argc, argv); 332 return cmd_move_workspace(current, argc, argv);
319 } else if (strcasecmp(argv[0], "scratchpad") == 0 333 } else if (strcasecmp(argv[0], "scratchpad") == 0
320 || (strcasecmp(argv[0], "to") == 0 334 || (strcasecmp(argv[0], "to") == 0 && argc == 2
321 && strcasecmp(argv[1], "scratchpad") == 0)) { 335 && strcasecmp(argv[1], "scratchpad") == 0)) {
322 // TODO: scratchpad 336 return move_to_scratchpad(current);
323 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
324 } else if (strcasecmp(argv[0], "position") == 0) { 337 } else if (strcasecmp(argv[0], "position") == 0) {
325 return move_to_position(current, argc, argv); 338 return move_to_position(current, argc, argv);
326 } else if (strcasecmp(argv[0], "absolute") == 0) { 339 } else if (strcasecmp(argv[0], "absolute") == 0) {
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c
new file mode 100644
index 00000000..8a529cb4
--- /dev/null
+++ b/sway/commands/scratchpad.c
@@ -0,0 +1,37 @@
1#include "log.h"
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "sway/scratchpad.h"
5#include "sway/server.h"
6#include "sway/tree/container.h"
7
8struct cmd_results *cmd_scratchpad(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) {
11 return error;
12 }
13 if (strcmp(argv[0], "show") != 0) {
14 return cmd_results_new(CMD_INVALID, "scratchpad",
15 "Expected 'scratchpad show'");
16 }
17 if (!server.scratchpad->length) {
18 return cmd_results_new(CMD_INVALID, "scratchpad",
19 "Scratchpad is empty");
20 }
21
22 if (config->handler_context.using_criteria) {
23 // If using criteria, this command is executed for every container which
24 // matches the criteria. If this container isn't in the scratchpad,
25 // we'll just silently return a success.
26 struct sway_container *con = config->handler_context.current_container;
27 wlr_log(WLR_INFO, "cmd_scratchpad(%s)", con->name);
28 if (!con->scratchpad) {
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30 }
31 scratchpad_toggle_container(con);
32 } else {
33 scratchpad_toggle_auto();
34 }
35
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
37}
diff --git a/sway/criteria.c b/sway/criteria.c
index e2b248de..6af97d5b 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -225,6 +225,14 @@ list_t *criteria_get_views(struct criteria *criteria) {
225 }; 225 };
226 container_for_each_descendant_dfs(&root_container, 226 container_for_each_descendant_dfs(&root_container,
227 criteria_get_views_iterator, &data); 227 criteria_get_views_iterator, &data);
228
229 // Scratchpad items which are hidden are not in the tree.
230 for (int i = 0; i < server.scratchpad->length; ++i) {
231 struct sway_container *con = server.scratchpad->items[i];
232 if (!con->parent) {
233 criteria_get_views_iterator(con, &data);
234 }
235 }
228 return matches; 236 return matches;
229} 237}
230 238
diff --git a/sway/meson.build b/sway/meson.build
index f4fdc8ea..30c848e2 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -7,6 +7,7 @@ sway_sources = files(
7 'debug-tree.c', 7 'debug-tree.c',
8 'ipc-json.c', 8 'ipc-json.c',
9 'ipc-server.c', 9 'ipc-server.c',
10 'scratchpad.c',
10 'security.c', 11 'security.c',
11 12
12 'desktop/desktop.c', 13 'desktop/desktop.c',
@@ -67,6 +68,7 @@ sway_sources = files(
67 'commands/reload.c', 68 'commands/reload.c',
68 'commands/rename.c', 69 'commands/rename.c',
69 'commands/resize.c', 70 'commands/resize.c',
71 'commands/scratchpad.c',
70 'commands/seat.c', 72 'commands/seat.c',
71 'commands/seat/attach.c', 73 'commands/seat/attach.c',
72 'commands/seat/cursor.c', 74 'commands/seat/cursor.c',
diff --git a/sway/scratchpad.c b/sway/scratchpad.c
new file mode 100644
index 00000000..e1f931a4
--- /dev/null
+++ b/sway/scratchpad.c
@@ -0,0 +1,173 @@
1#define _XOPEN_SOURCE 700
2#include <stdlib.h>
3#include <stdio.h>
4#include <stdbool.h>
5#include "sway/scratchpad.h"
6#include "sway/input/seat.h"
7#include "sway/tree/arrange.h"
8#include "sway/tree/container.h"
9#include "sway/tree/view.h"
10#include "sway/tree/workspace.h"
11#include "list.h"
12#include "log.h"
13
14void scratchpad_add_container(struct sway_container *con) {
15 if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) {
16 return;
17 }
18 con->scratchpad = true;
19 list_add(server.scratchpad, con);
20
21 struct sway_container *parent = con->parent;
22 container_set_floating(con, true);
23 container_remove_child(con);
24 arrange_windows(parent);
25
26 struct sway_seat *seat = input_manager_current_seat(input_manager);
27 seat_set_focus(seat, seat_get_focus_inactive(seat, parent));
28}
29
30void scratchpad_remove_container(struct sway_container *con) {
31 if (!sway_assert(con->scratchpad, "Container is not in scratchpad")) {
32 return;
33 }
34 con->scratchpad = false;
35 for (int i = 0; i < server.scratchpad->length; ++i) {
36 if (server.scratchpad->items[i] == con) {
37 list_del(server.scratchpad, i);
38 break;
39 }
40 }
41}
42
43/**
44 * Show a single scratchpad container.
45 * The container might be visible on another workspace already.
46 */
47static void scratchpad_show(struct sway_container *con) {
48 struct sway_seat *seat = input_manager_current_seat(input_manager);
49 struct sway_container *ws = seat_get_focus(seat);
50 if (ws->type != C_WORKSPACE) {
51 ws = container_parent(ws, C_WORKSPACE);
52 }
53
54 // If the current con or any of its parents are in fullscreen mode, we
55 // first need to disable it before showing the scratchpad con.
56 if (ws->sway_workspace->fullscreen) {
57 view_set_fullscreen(ws->sway_workspace->fullscreen, false);
58 }
59
60 // Show the container
61 if (con->parent) {
62 container_remove_child(con);
63 }
64 container_add_child(ws->sway_workspace->floating, con);
65
66 // Make sure the container's center point overlaps this workspace
67 double center_lx = con->x + con->width / 2;
68 double center_ly = con->y + con->height / 2;
69
70 struct wlr_box workspace_box;
71 container_get_box(ws, &workspace_box);
72 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
73 // Maybe resize it
74 if (con->width > ws->width || con->height > ws->height) {
75 // TODO: Do this properly once we can float C_CONTAINERs
76 if (con->type == C_VIEW) {
77 view_init_floating(con->sway_view);
78 arrange_windows(con);
79 }
80 }
81
82 // Center it
83 double new_lx = ws->x + (ws->width - con->width) / 2;
84 double new_ly = ws->y + (ws->height - con->height) / 2;
85 container_floating_move_to(con, new_lx, new_ly);
86 }
87
88 seat_set_focus(seat, con);
89
90 container_set_dirty(con->parent);
91}
92
93/**
94 * Hide a single scratchpad container.
95 * The container might not be the focused container (eg. when using criteria).
96 */
97static void scratchpad_hide(struct sway_container *con) {
98 struct sway_seat *seat = input_manager_current_seat(input_manager);
99 struct sway_container *focus = seat_get_focus(seat);
100 struct sway_container *ws = container_parent(con, C_WORKSPACE);
101
102 container_remove_child(con);
103 arrange_windows(ws);
104 if (con == focus) {
105 seat_set_focus(seat, seat_get_focus_inactive(seat, ws));
106 }
107 list_move_to_end(server.scratchpad, con);
108}
109
110void scratchpad_toggle_auto(void) {
111 struct sway_seat *seat = input_manager_current_seat(input_manager);
112 struct sway_container *focus = seat_get_focus(seat);
113 struct sway_container *ws = focus->type == C_WORKSPACE ?
114 focus : container_parent(focus, C_WORKSPACE);
115
116 // Check if the currently focused window is a scratchpad window and should
117 // be hidden again.
118 if (focus->scratchpad) {
119 wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s",
120 focus->name);
121 scratchpad_hide(focus);
122 return;
123 }
124
125 // Check if there is an unfocused scratchpad window on the current workspace
126 // and focus it.
127 for (int i = 0; i < ws->sway_workspace->floating->children->length; ++i) {
128 struct sway_container *floater =
129 ws->sway_workspace->floating->children->items[i];
130 if (floater->scratchpad && focus != floater) {
131 wlr_log(WLR_DEBUG,
132 "Focusing other scratchpad window (%s) in this workspace",
133 floater->name);
134 scratchpad_show(floater);
135 return;
136 }
137 }
138
139 // Check if there is a visible scratchpad window on another workspace.
140 // In this case we move it to the current workspace.
141 for (int i = 0; i < server.scratchpad->length; ++i) {
142 struct sway_container *con = server.scratchpad->items[i];
143 if (con->parent) {
144 wlr_log(WLR_DEBUG,
145 "Moving a visible scratchpad window (%s) to this workspace",
146 con->name);
147 scratchpad_show(con);
148 return;
149 }
150 }
151
152 // Take the container at the bottom of the scratchpad list
153 if (!sway_assert(server.scratchpad->length, "Scratchpad is empty")) {
154 return;
155 }
156 struct sway_container *con = server.scratchpad->items[0];
157 wlr_log(WLR_DEBUG, "Showing %s from list", con->name);
158 scratchpad_show(con);
159}
160
161void scratchpad_toggle_container(struct sway_container *con) {
162 if (!sway_assert(con->scratchpad, "Container isn't in the scratchpad")) {
163 return;
164 }
165
166 // Check if it matches a currently visible scratchpad window and hide it.
167 if (con->parent) {
168 scratchpad_hide(con);
169 return;
170 }
171
172 scratchpad_show(con);
173}
diff --git a/sway/server.c b/sway/server.c
index 89dfbf8c..916e6b71 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -126,6 +126,8 @@ bool server_init(struct sway_server *server) {
126 server->dirty_containers = create_list(); 126 server->dirty_containers = create_list();
127 server->transactions = create_list(); 127 server->transactions = create_list();
128 128
129 server->scratchpad = create_list();
130
129 input_manager = input_manager_create(server); 131 input_manager = input_manager_create(server);
130 return true; 132 return true;
131} 133}
@@ -135,6 +137,7 @@ void server_fini(struct sway_server *server) {
135 wl_display_destroy(server->wl_display); 137 wl_display_destroy(server->wl_display);
136 list_free(server->dirty_containers); 138 list_free(server->dirty_containers);
137 list_free(server->transactions); 139 list_free(server->transactions);
140 list_free(server->scratchpad);
138} 141}
139 142
140bool server_start_backend(struct sway_server *server) { 143bool server_start_backend(struct sway_server *server) {
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 42c1d024..4f743c40 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -17,6 +17,7 @@
17#include "sway/input/seat.h" 17#include "sway/input/seat.h"
18#include "sway/ipc-server.h" 18#include "sway/ipc-server.h"
19#include "sway/output.h" 19#include "sway/output.h"
20#include "sway/scratchpad.h"
20#include "sway/server.h" 21#include "sway/server.h"
21#include "sway/tree/arrange.h" 22#include "sway/tree/arrange.h"
22#include "sway/tree/layout.h" 23#include "sway/tree/layout.h"
@@ -328,6 +329,10 @@ static struct sway_container *container_destroy_noreaping(
328 con->destroying = true; 329 con->destroying = true;
329 container_set_dirty(con); 330 container_set_dirty(con);
330 331
332 if (con->scratchpad) {
333 scratchpad_remove_container(con);
334 }
335
331 if (!con->parent) { 336 if (!con->parent) {
332 return NULL; 337 return NULL;
333 } 338 }
@@ -955,6 +960,9 @@ void container_set_floating(struct sway_container *container, bool enable) {
955 container_reap_empty_recursive(workspace); 960 container_reap_empty_recursive(workspace);
956 } else { 961 } else {
957 // Returning to tiled 962 // Returning to tiled
963 if (container->scratchpad) {
964 scratchpad_remove_container(container);
965 }
958 container_remove_child(container); 966 container_remove_child(container);
959 container_add_child(workspace, container); 967 container_add_child(workspace, container);
960 container->width = container->parent->width; 968 container->width = container->parent->width;
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 533906fa..af37611f 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -135,6 +135,10 @@ void container_add_child(struct sway_container *parent,
135 list_add(parent->children, child); 135 list_add(parent->children, child);
136 child->parent = parent; 136 child->parent = parent;
137 container_handle_fullscreen_reparent(child, old_parent); 137 container_handle_fullscreen_reparent(child, old_parent);
138 if (old_parent) {
139 container_set_dirty(old_parent);
140 }
141 container_set_dirty(child);
138} 142}
139 143
140struct sway_container *container_remove_child(struct sway_container *child) { 144struct sway_container *container_remove_child(struct sway_container *child) {
@@ -153,6 +157,9 @@ struct sway_container *container_remove_child(struct sway_container *child) {
153 child->parent = NULL; 157 child->parent = NULL;
154 container_notify_subtree_changed(parent); 158 container_notify_subtree_changed(parent);
155 159
160 container_set_dirty(parent);
161 container_set_dirty(child);
162
156 return parent; 163 return parent;
157} 164}
158 165
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 89150a69..9d88d7aa 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -1065,7 +1065,7 @@ void view_update_marks_textures(struct sway_view *view) {
1065} 1065}
1066 1066
1067bool view_is_visible(struct sway_view *view) { 1067bool view_is_visible(struct sway_view *view) {
1068 if (!view->swayc || view->swayc->destroying) { 1068 if (!view->swayc || view->swayc->destroying || !view->swayc->parent) {
1069 return false; 1069 return false;
1070 } 1070 }
1071 struct sway_container *workspace = 1071 struct sway_container *workspace =