aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-07-23 07:57:48 -0400
committerLibravatar GitHub <noreply@github.com>2018-07-23 07:57:48 -0400
commit0515b37dfde41cfa85529ac0f8b991a1bec749d2 (patch)
tree5f855ab05c0ec079f4487e9527f87f37c1929bf1
parentMerge pull request #2328 from emersion/xdg-shell-set-title (diff)
parentStore scratchpad list in sway_root instead of server (diff)
downloadsway-0515b37dfde41cfa85529ac0f8b991a1bec749d2.tar.gz
sway-0515b37dfde41cfa85529ac0f8b991a1bec749d2.tar.zst
sway-0515b37dfde41cfa85529ac0f8b991a1bec749d2.zip
Merge pull request #2323 from RyanDwyer/scratchpad
Implement scratchpad
-rw-r--r--include/sway/scratchpad.h26
-rw-r--r--include/sway/tree/container.h5
-rw-r--r--include/sway/tree/layout.h2
-rw-r--r--sway/commands.c3
-rw-r--r--sway/commands/move.c19
-rw-r--r--sway/commands/scratchpad.c36
-rw-r--r--sway/criteria.c9
-rw-r--r--sway/meson.build2
-rw-r--r--sway/scratchpad.c175
-rw-r--r--sway/tree/container.c8
-rw-r--r--sway/tree/layout.c8
-rw-r--r--sway/tree/view.c2
12 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/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/include/sway/tree/layout.h b/include/sway/tree/layout.h
index 5a78fd58..7d7da2d7 100644
--- a/include/sway/tree/layout.h
+++ b/include/sway/tree/layout.h
@@ -35,6 +35,8 @@ struct sway_root {
35 35
36 struct wl_list outputs; // sway_output::link 36 struct wl_list outputs; // sway_output::link
37 37
38 list_t *scratchpad; // struct sway_container
39
38 struct { 40 struct {
39 struct wl_signal new_container; 41 struct wl_signal new_container;
40 } events; 42 } events;
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..ccc07c87
--- /dev/null
+++ b/sway/commands/scratchpad.c
@@ -0,0 +1,36 @@
1#include "log.h"
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "sway/scratchpad.h"
5#include "sway/tree/container.h"
6
7struct cmd_results *cmd_scratchpad(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12 if (strcmp(argv[0], "show") != 0) {
13 return cmd_results_new(CMD_INVALID, "scratchpad",
14 "Expected 'scratchpad show'");
15 }
16 if (!root_container.sway_root->scratchpad->length) {
17 return cmd_results_new(CMD_INVALID, "scratchpad",
18 "Scratchpad is empty");
19 }
20
21 if (config->handler_context.using_criteria) {
22 // If using criteria, this command is executed for every container which
23 // matches the criteria. If this container isn't in the scratchpad,
24 // we'll just silently return a success.
25 struct sway_container *con = config->handler_context.current_container;
26 wlr_log(WLR_INFO, "cmd_scratchpad(%s)", con->name);
27 if (!con->scratchpad) {
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29 }
30 scratchpad_toggle_container(con);
31 } else {
32 scratchpad_toggle_auto();
33 }
34
35 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
36}
diff --git a/sway/criteria.c b/sway/criteria.c
index e2b248de..c2e9c07e 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -225,6 +225,15 @@ 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 < root_container.sway_root->scratchpad->length; ++i) {
231 struct sway_container *con =
232 root_container.sway_root->scratchpad->items[i];
233 if (!con->parent) {
234 criteria_get_views_iterator(con, &data);
235 }
236 }
228 return matches; 237 return matches;
229} 238}
230 239
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..1e836e7d
--- /dev/null
+++ b/sway/scratchpad.c
@@ -0,0 +1,175 @@
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(root_container.sway_root->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 < root_container.sway_root->scratchpad->length; ++i) {
36 if (root_container.sway_root->scratchpad->items[i] == con) {
37 list_del(root_container.sway_root->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(root_container.sway_root->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 < root_container.sway_root->scratchpad->length; ++i) {
142 struct sway_container *con =
143 root_container.sway_root->scratchpad->items[i];
144 if (con->parent) {
145 wlr_log(WLR_DEBUG,
146 "Moving a visible scratchpad window (%s) to this workspace",
147 con->name);
148 scratchpad_show(con);
149 return;
150 }
151 }
152
153 // Take the container at the bottom of the scratchpad list
154 if (!sway_assert(root_container.sway_root->scratchpad->length,
155 "Scratchpad is empty")) {
156 return;
157 }
158 struct sway_container *con = root_container.sway_root->scratchpad->items[0];
159 wlr_log(WLR_DEBUG, "Showing %s from list", con->name);
160 scratchpad_show(con);
161}
162
163void scratchpad_toggle_container(struct sway_container *con) {
164 if (!sway_assert(con->scratchpad, "Container isn't in the scratchpad")) {
165 return;
166 }
167
168 // Check if it matches a currently visible scratchpad window and hide it.
169 if (con->parent) {
170 scratchpad_hide(con);
171 return;
172 }
173
174 scratchpad_show(con);
175}
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..a2be0ef3 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -42,6 +42,7 @@ void layout_init(void) {
42 wl_list_init(&root_container.sway_root->xwayland_unmanaged); 42 wl_list_init(&root_container.sway_root->xwayland_unmanaged);
43 wl_list_init(&root_container.sway_root->drag_icons); 43 wl_list_init(&root_container.sway_root->drag_icons);
44 wl_signal_init(&root_container.sway_root->events.new_container); 44 wl_signal_init(&root_container.sway_root->events.new_container);
45 root_container.sway_root->scratchpad = create_list();
45 46
46 root_container.sway_root->output_layout_change.notify = 47 root_container.sway_root->output_layout_change.notify =
47 output_layout_handle_change; 48 output_layout_handle_change;
@@ -135,6 +136,10 @@ void container_add_child(struct sway_container *parent,
135 list_add(parent->children, child); 136 list_add(parent->children, child);
136 child->parent = parent; 137 child->parent = parent;
137 container_handle_fullscreen_reparent(child, old_parent); 138 container_handle_fullscreen_reparent(child, old_parent);
139 if (old_parent) {
140 container_set_dirty(old_parent);
141 }
142 container_set_dirty(child);
138} 143}
139 144
140struct sway_container *container_remove_child(struct sway_container *child) { 145struct sway_container *container_remove_child(struct sway_container *child) {
@@ -153,6 +158,9 @@ struct sway_container *container_remove_child(struct sway_container *child) {
153 child->parent = NULL; 158 child->parent = NULL;
154 container_notify_subtree_changed(parent); 159 container_notify_subtree_changed(parent);
155 160
161 container_set_dirty(parent);
162 container_set_dirty(child);
163
156 return parent; 164 return parent;
157} 165}
158 166
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 =