aboutsummaryrefslogtreecommitdiffstats
path: root/sway/workspace.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/workspace.c')
-rw-r--r--sway/workspace.c374
1 files changed, 0 insertions, 374 deletions
diff --git a/sway/workspace.c b/sway/workspace.c
deleted file mode 100644
index e0367190..00000000
--- a/sway/workspace.c
+++ /dev/null
@@ -1,374 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdlib.h>
3#include <stdbool.h>
4#include <limits.h>
5#include <ctype.h>
6#include <wlc/wlc.h>
7#include <string.h>
8#include <strings.h>
9#include <sys/types.h>
10#include "sway/ipc-server.h"
11#include "sway/extensions.h"
12#include "sway/workspace.h"
13#include "sway/layout.h"
14#include "sway/container.h"
15#include "sway/handlers.h"
16#include "sway/config.h"
17#include "sway/focus.h"
18#include "stringop.h"
19#include "util.h"
20#include "list.h"
21#include "log.h"
22#include "ipc.h"
23
24char *prev_workspace_name = NULL;
25struct workspace_by_number_data {
26 int len;
27 const char *cset;
28 const char *name;
29};
30
31static bool workspace_valid_on_output(const char *output_name, const char *ws_name) {
32 int i;
33 for (i = 0; i < config->workspace_outputs->length; ++i) {
34 struct workspace_output *wso = config->workspace_outputs->items[i];
35 if (strcasecmp(wso->workspace, ws_name) == 0) {
36 if (strcasecmp(wso->output, output_name) != 0) {
37 return false;
38 }
39 }
40 }
41
42 return true;
43}
44
45char *workspace_next_name(const char *output_name) {
46 sway_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", output_name);
47 int i;
48 int l = 1;
49 // Scan all workspace bindings to find the next available workspace name,
50 // if none are found/available then default to a number
51 struct sway_mode *mode = config->current_mode;
52
53 int order = INT_MAX;
54 char *target = NULL;
55 for (i = 0; i < mode->bindings->length; ++i) {
56 struct sway_binding *binding = mode->bindings->items[i];
57 char *cmdlist = strdup(binding->command);
58 char *dup = cmdlist;
59 char *name = NULL;
60
61 // workspace n
62 char *cmd = argsep(&cmdlist, " ");
63 if (cmdlist) {
64 name = argsep(&cmdlist, ",;");
65 }
66
67 if (strcmp("workspace", cmd) == 0 && name) {
68 sway_log(L_DEBUG, "Got valid workspace command for target: '%s'", name);
69 char *_target = strdup(name);
70 strip_quotes(_target);
71 while (isspace(*_target))
72 _target++;
73
74 // Make sure that the command references an actual workspace
75 // not a command about workspaces
76 if (strcmp(_target, "next") == 0 ||
77 strcmp(_target, "prev") == 0 ||
78 strcmp(_target, "next_on_output") == 0 ||
79 strcmp(_target, "prev_on_output") == 0 ||
80 strcmp(_target, "number") == 0 ||
81 strcmp(_target, "back_and_forth") == 0 ||
82 strcmp(_target, "current") == 0)
83 {
84 free(_target);
85 free(dup);
86 continue;
87 }
88
89 // Make sure that the workspace doesn't already exist
90 if (workspace_by_name(_target)) {
91 free(_target);
92 free(dup);
93 continue;
94 }
95
96 // make sure that the workspace can appear on the given
97 // output
98 if (!workspace_valid_on_output(output_name, _target)) {
99 free(_target);
100 free(dup);
101 continue;
102 }
103
104 if (binding->order < order) {
105 order = binding->order;
106 free(target);
107 target = _target;
108 sway_log(L_DEBUG, "Workspace: Found free name %s", _target);
109 }
110 }
111 free(dup);
112 }
113 if (target != NULL) {
114 return target;
115 }
116 // As a fall back, get the current number of active workspaces
117 // and return that + 1 for the next workspace's name
118 int ws_num = root_container.children->length;
119 if (ws_num >= 10) {
120 l = 2;
121 } else if (ws_num >= 100) {
122 l = 3;
123 }
124 char *name = malloc(l + 1);
125 if (!name) {
126 sway_log(L_ERROR, "Could not allocate workspace name");
127 return NULL;
128 }
129 sprintf(name, "%d", ws_num++);
130 return name;
131}
132
133swayc_t *workspace_create(const char* name) {
134 swayc_t *parent;
135 // Search for workspace<->output pair
136 int i, e = config->workspace_outputs->length;
137 for (i = 0; i < e; ++i) {
138 struct workspace_output *wso = config->workspace_outputs->items[i];
139 if (strcasecmp(wso->workspace, name) == 0)
140 {
141 // Find output to use if it exists
142 e = root_container.children->length;
143 for (i = 0; i < e; ++i) {
144 parent = root_container.children->items[i];
145 if (strcmp(parent->name, wso->output) == 0) {
146 return new_workspace(parent, name);
147 }
148 }
149 break;
150 }
151 }
152 // Otherwise create a new one
153 parent = get_focused_container(&root_container);
154 parent = swayc_parent_by_type(parent, C_OUTPUT);
155 return new_workspace(parent, name);
156}
157
158static bool _workspace_by_name(swayc_t *view, void *data) {
159 return (view->type == C_WORKSPACE) &&
160 (strcasecmp(view->name, (char *) data) == 0);
161}
162
163swayc_t *workspace_by_name(const char* name) {
164 if (strcmp(name, "prev") == 0) {
165 return workspace_prev();
166 }
167 else if (strcmp(name, "prev_on_output") == 0) {
168 return workspace_output_prev();
169 }
170 else if (strcmp(name, "next") == 0) {
171 return workspace_next();
172 }
173 else if (strcmp(name, "next_on_output") == 0) {
174 return workspace_output_next();
175 }
176 else if (strcmp(name, "current") == 0) {
177 return swayc_active_workspace();
178 }
179 else {
180 return swayc_by_test(&root_container, _workspace_by_name, (void *) name);
181 }
182}
183
184static bool _workspace_by_number(swayc_t *view, void *data) {
185 if (view->type != C_WORKSPACE) {
186 return false;
187 }
188 struct workspace_by_number_data *wbnd = data;
189 int a = strspn(view->name, wbnd->cset);
190 return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0;
191}
192swayc_t *workspace_by_number(const char* name) {
193 struct workspace_by_number_data wbnd = {0, "1234567890", name};
194 wbnd.len = strspn(name, wbnd.cset);
195 if (wbnd.len <= 0) {
196 return NULL;
197 }
198 return swayc_by_test(&root_container, _workspace_by_number, (void *) &wbnd);
199}
200
201/**
202 * Get the previous or next workspace on the specified output.
203 * Wraps around at the end and beginning.
204 * If next is false, the previous workspace is returned, otherwise the next one is returned.
205 */
206swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) {
207 if (!sway_assert(output->type == C_OUTPUT, "Argument must be an output, is %d", output->type)) {
208 return NULL;
209 }
210
211 int i;
212 for (i = 0; i < output->children->length; i++) {
213 if (output->children->items[i] == output->focused) {
214 return output->children->items[wrap(i + (next ? 1 : -1), output->children->length)];
215 }
216 }
217
218 // Doesn't happen, at worst the for loop returns the previously active workspace
219 return NULL;
220}
221
222/**
223 * Get the previous or next workspace. If the first/last workspace on an output is active,
224 * proceed to the previous/next output's previous/next workspace.
225 * If next is false, the previous workspace is returned, otherwise the next one is returned.
226 */
227swayc_t *workspace_prev_next_impl(swayc_t *workspace, bool next) {
228 if (!sway_assert(workspace->type == C_WORKSPACE, "Argument must be a workspace, is %d", workspace->type)) {
229 return NULL;
230 }
231
232 swayc_t *current_output = workspace->parent;
233 int offset = next ? 1 : -1;
234 int start = next ? 0 : 1;
235 int end = next ? (current_output->children->length) - 1 : current_output->children->length;
236 int i;
237 for (i = start; i < end; i++) {
238 if (current_output->children->items[i] == workspace) {
239 return current_output->children->items[i + offset];
240 }
241 }
242
243 // Given workspace is the first/last on the output, jump to the previous/next output
244 int num_outputs = root_container.children->length;
245 for (i = 0; i < num_outputs; i++) {
246 if (root_container.children->items[i] == current_output) {
247 swayc_t *next_output = root_container.children->items[wrap(i + offset, num_outputs)];
248 return workspace_output_prev_next_impl(next_output, next);
249 }
250 }
251
252 // Doesn't happen, at worst the for loop returns the previously active workspace on the active output
253 return NULL;
254}
255
256swayc_t *workspace_output_next() {
257 return workspace_output_prev_next_impl(swayc_active_output(), true);
258}
259
260swayc_t *workspace_next() {
261 return workspace_prev_next_impl(swayc_active_workspace(), true);
262}
263
264swayc_t *workspace_output_prev() {
265 return workspace_output_prev_next_impl(swayc_active_output(), false);
266}
267
268swayc_t *workspace_prev() {
269 return workspace_prev_next_impl(swayc_active_workspace(), false);
270}
271
272bool workspace_switch(swayc_t *workspace) {
273 if (!workspace) {
274 return false;
275 }
276 swayc_t *active_ws = swayc_active_workspace();
277 if (config->auto_back_and_forth && active_ws == workspace && prev_workspace_name) {
278 swayc_t *new_ws = workspace_by_name(prev_workspace_name);
279 workspace = new_ws ? new_ws : workspace_create(prev_workspace_name);
280 }
281
282 if (!prev_workspace_name
283 || (strcmp(prev_workspace_name, active_ws->name)
284 && active_ws != workspace)) {
285 free(prev_workspace_name);
286 prev_workspace_name = malloc(strlen(active_ws->name) + 1);
287 if (!prev_workspace_name) {
288 sway_log(L_ERROR, "Unable to allocate previous workspace name");
289 return false;
290 }
291 strcpy(prev_workspace_name, active_ws->name);
292 }
293
294 // move sticky containers
295 if (swayc_parent_by_type(active_ws, C_OUTPUT) == swayc_parent_by_type(workspace, C_OUTPUT)) {
296 // don't change list while traversing it, use intermediate list instead
297 list_t *stickies = create_list();
298 for (int i = 0; i < active_ws->floating->length; i++) {
299 swayc_t *cont = active_ws->floating->items[i];
300 if (cont->sticky) {
301 list_add(stickies, cont);
302 }
303 }
304 for (int i = 0; i < stickies->length; i++) {
305 swayc_t *cont = stickies->items[i];
306 sway_log(L_DEBUG, "Moving sticky container %p to %p:%s",
307 cont, workspace, workspace->name);
308 swayc_t *parent = remove_child(cont);
309 add_floating(workspace, cont);
310 // Destroy old container if we need to
311 destroy_container(parent);
312 }
313 list_free(stickies);
314 }
315 sway_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name);
316 if (!set_focused_container(get_focused_view(workspace))) {
317 return false;
318 }
319 swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
320 arrange_backgrounds();
321 arrange_windows(output, -1, -1);
322 return true;
323}
324
325swayc_t *workspace_for_pid(pid_t pid) {
326 int i;
327 swayc_t *ws = NULL;
328 struct pid_workspace *pw = NULL;
329
330 sway_log(L_DEBUG, "looking for workspace for pid %d", pid);
331
332 // leaving this here as it's useful for debugging
333 // sway_log(L_DEBUG, "all pid_workspaces");
334 // for (int k = 0; k < config->pid_workspaces->length; k++) {
335 // pw = config->pid_workspaces->items[k];
336 // sway_log(L_DEBUG, "pid %d workspace %s time_added %li", *pw->pid, pw->workspace, *pw->time_added);
337 // }
338
339 do {
340 for (i = 0; i < config->pid_workspaces->length; i++) {
341 pw = config->pid_workspaces->items[i];
342 pid_t *pw_pid = pw->pid;
343
344 if (pid == *pw_pid) {
345 sway_log(L_DEBUG, "found pid_workspace for pid %d, workspace %s", pid, pw->workspace);
346 break; // out of for loop
347 }
348
349 pw = NULL;
350 }
351
352 if (pw) {
353 break; // out of do-while loop
354 }
355
356 pid = get_parent_pid(pid);
357 // no sense in looking for matches for pid 0.
358 // also, if pid == getpid(), that is the compositor's
359 // pid, which definitely isn't helpful
360 } while (pid > 0 && pid != getpid());
361
362 if (pw) {
363 ws = workspace_by_name(pw->workspace);
364
365 if (!ws) {
366 sway_log(L_DEBUG, "Creating workspace %s for pid %d because it disappeared", pw->workspace, pid);
367 ws = workspace_create(pw->workspace);
368 }
369
370 list_del(config->pid_workspaces, i);
371 }
372
373 return ws;
374}