diff options
Diffstat (limited to 'sway/scratchpad.c')
-rw-r--r-- | sway/scratchpad.c | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/sway/scratchpad.c b/sway/scratchpad.c new file mode 100644 index 00000000..b7d6fd99 --- /dev/null +++ b/sway/scratchpad.c | |||
@@ -0,0 +1,181 @@ | |||
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 | |||
14 | void 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 | |||
30 | void 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 | */ | ||
47 | static 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 | container_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 | container_init_floating(con); | ||
76 | } | ||
77 | |||
78 | // Center it | ||
79 | double new_lx = ws->x + (ws->width - con->width) / 2; | ||
80 | double new_ly = ws->y + (ws->height - con->height) / 2; | ||
81 | container_floating_move_to(con, new_lx, new_ly); | ||
82 | } | ||
83 | |||
84 | arrange_windows(ws); | ||
85 | seat_set_focus(seat, seat_get_focus_inactive(seat, con)); | ||
86 | |||
87 | container_set_dirty(con->parent); | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * Hide a single scratchpad container. | ||
92 | * The container might not be the focused container (eg. when using criteria). | ||
93 | */ | ||
94 | static void scratchpad_hide(struct sway_container *con) { | ||
95 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
96 | struct sway_container *focus = seat_get_focus(seat); | ||
97 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
98 | |||
99 | container_remove_child(con); | ||
100 | arrange_windows(ws); | ||
101 | if (con == focus) { | ||
102 | seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); | ||
103 | } | ||
104 | list_move_to_end(root_container.sway_root->scratchpad, con); | ||
105 | } | ||
106 | |||
107 | void scratchpad_toggle_auto(void) { | ||
108 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
109 | struct sway_container *focus = seat_get_focus(seat); | ||
110 | struct sway_container *ws = focus->type == C_WORKSPACE ? | ||
111 | focus : container_parent(focus, C_WORKSPACE); | ||
112 | |||
113 | // If the focus is in a floating split container, | ||
114 | // operate on the split container instead of the child. | ||
115 | if (container_is_floating_or_child(focus)) { | ||
116 | while (focus->parent->layout != L_FLOATING) { | ||
117 | focus = focus->parent; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | |||
122 | // Check if the currently focused window is a scratchpad window and should | ||
123 | // be hidden again. | ||
124 | if (focus->scratchpad) { | ||
125 | wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s", | ||
126 | focus->name); | ||
127 | scratchpad_hide(focus); | ||
128 | return; | ||
129 | } | ||
130 | |||
131 | // Check if there is an unfocused scratchpad window on the current workspace | ||
132 | // and focus it. | ||
133 | for (int i = 0; i < ws->sway_workspace->floating->children->length; ++i) { | ||
134 | struct sway_container *floater = | ||
135 | ws->sway_workspace->floating->children->items[i]; | ||
136 | if (floater->scratchpad && focus != floater) { | ||
137 | wlr_log(WLR_DEBUG, | ||
138 | "Focusing other scratchpad window (%s) in this workspace", | ||
139 | floater->name); | ||
140 | scratchpad_show(floater); | ||
141 | return; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | // Check if there is a visible scratchpad window on another workspace. | ||
146 | // In this case we move it to the current workspace. | ||
147 | for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { | ||
148 | struct sway_container *con = | ||
149 | root_container.sway_root->scratchpad->items[i]; | ||
150 | if (con->parent) { | ||
151 | wlr_log(WLR_DEBUG, | ||
152 | "Moving a visible scratchpad window (%s) to this workspace", | ||
153 | con->name); | ||
154 | scratchpad_show(con); | ||
155 | return; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | // Take the container at the bottom of the scratchpad list | ||
160 | if (!sway_assert(root_container.sway_root->scratchpad->length, | ||
161 | "Scratchpad is empty")) { | ||
162 | return; | ||
163 | } | ||
164 | struct sway_container *con = root_container.sway_root->scratchpad->items[0]; | ||
165 | wlr_log(WLR_DEBUG, "Showing %s from list", con->name); | ||
166 | scratchpad_show(con); | ||
167 | } | ||
168 | |||
169 | void scratchpad_toggle_container(struct sway_container *con) { | ||
170 | if (!sway_assert(con->scratchpad, "Container isn't in the scratchpad")) { | ||
171 | return; | ||
172 | } | ||
173 | |||
174 | // Check if it matches a currently visible scratchpad window and hide it. | ||
175 | if (con->parent) { | ||
176 | scratchpad_hide(con); | ||
177 | return; | ||
178 | } | ||
179 | |||
180 | scratchpad_show(con); | ||
181 | } | ||