diff options
Diffstat (limited to 'sway/scratchpad.c')
-rw-r--r-- | sway/scratchpad.c | 173 |
1 files changed, 173 insertions, 0 deletions
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 | |||
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(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 | |||
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 < 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 | */ | ||
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 | 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 | */ | ||
97 | static 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 | |||
110 | void 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 | |||
161 | void 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 | } | ||