aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-01-21 12:21:57 -0500
committerLibravatar Drew DeVault <sir@cmpwn.com>2018-01-21 12:21:57 -0500
commita6bc46eea9d7dec6a2b93f85bac2e3737e0c6725 (patch)
treeebb95fcc4a0709f253f87a0e900adc0fbb51fa32 /sway
parentMerge pull request #1570 from acrisci/config-references (diff)
downloadsway-a6bc46eea9d7dec6a2b93f85bac2e3737e0c6725.tar.gz
sway-a6bc46eea9d7dec6a2b93f85bac2e3737e0c6725.tar.zst
sway-a6bc46eea9d7dec6a2b93f85bac2e3737e0c6725.zip
Remove sway/old/
Diffstat (limited to 'sway')
-rw-r--r--sway/old/border.c510
-rw-r--r--sway/old/commands.c686
-rw-r--r--sway/old/commands/assign.c57
-rw-r--r--sway/old/commands/bar.c59
-rw-r--r--sway/old/commands/bar/activate_button.c26
-rw-r--r--sway/old/commands/bar/binding_mode_indicator.c27
-rw-r--r--sway/old/commands/bar/bindsym.c48
-rw-r--r--sway/old/commands/bar/colors.c129
-rw-r--r--sway/old/commands/bar/context_button.c26
-rw-r--r--sway/old/commands/bar/font.c26
-rw-r--r--sway/old/commands/bar/height.c21
-rw-r--r--sway/old/commands/bar/hidden_state.c79
-rw-r--r--sway/old/commands/bar/icon_theme.c25
-rw-r--r--sway/old/commands/bar/id.c33
-rw-r--r--sway/old/commands/bar/mode.c81
-rw-r--r--sway/old/commands/bar/modifier.c35
-rw-r--r--sway/old/commands/bar/output.c50
-rw-r--r--sway/old/commands/bar/pango_markup.c27
-rw-r--r--sway/old/commands/bar/position.c33
-rw-r--r--sway/old/commands/bar/secondary_button.c26
-rw-r--r--sway/old/commands/bar/separator_symbol.c21
-rw-r--r--sway/old/commands/bar/status_command.c21
-rw-r--r--sway/old/commands/bar/strip_workspace_numbers.c27
-rw-r--r--sway/old/commands/bar/swaybar_command.c21
-rw-r--r--sway/old/commands/bar/tray_output.c29
-rw-r--r--sway/old/commands/bar/tray_padding.c34
-rw-r--r--sway/old/commands/bar/workspace_buttons.c27
-rw-r--r--sway/old/commands/bar/wrap_scroll.c27
-rw-r--r--sway/old/commands/bind.c170
-rw-r--r--sway/old/commands/border.c65
-rw-r--r--sway/old/commands/client.c72
-rw-r--r--sway/old/commands/clipboard.c38
-rw-r--r--sway/old/commands/commands.c26
-rw-r--r--sway/old/commands/debuglog.c27
-rw-r--r--sway/old/commands/default_border.c44
-rw-r--r--sway/old/commands/default_floating_border.c45
-rw-r--r--sway/old/commands/exec.c16
-rw-r--r--sway/old/commands/exec_always.c85
-rw-r--r--sway/old/commands/exit.c17
-rw-r--r--sway/old/commands/floating.c81
-rw-r--r--sway/old/commands/floating_maximum_size.c38
-rw-r--r--sway/old/commands/floating_minimum_size.c38
-rw-r--r--sway/old/commands/floating_mod.c42
-rw-r--r--sway/old/commands/floating_scroll.c46
-rw-r--r--sway/old/commands/focus.c100
-rw-r--r--sway/old/commands/focus_follows_mouse.c13
-rw-r--r--sway/old/commands/font.c27
-rw-r--r--sway/old/commands/for_window.c41
-rw-r--r--sway/old/commands/force_focus_wrapping.c13
-rw-r--r--sway/old/commands/fullscreen.c60
-rw-r--r--sway/old/commands/gaps.c166
-rw-r--r--sway/old/commands/hide_edge_borders.c27
-rw-r--r--sway/old/commands/include.c15
-rw-r--r--sway/old/commands/input.c55
-rw-r--r--sway/old/commands/input/accel_profile.c27
-rw-r--r--sway/old/commands/input/click_method.c30
-rw-r--r--sway/old/commands/input/drag_lock.c26
-rw-r--r--sway/old/commands/input/dwt.c26
-rw-r--r--sway/old/commands/input/events.c30
-rw-r--r--sway/old/commands/input/left_handed.c26
-rw-r--r--sway/old/commands/input/middle_emulation.c26
-rw-r--r--sway/old/commands/input/natural_scroll.c26
-rw-r--r--sway/old/commands/input/pointer_accel.c24
-rw-r--r--sway/old/commands/input/scroll_method.c30
-rw-r--r--sway/old/commands/input/tap.c29
-rw-r--r--sway/old/commands/ipc.c162
-rw-r--r--sway/old/commands/kill.c12
-rw-r--r--sway/old/commands/layout.c196
-rw-r--r--sway/old/commands/log_colors.c22
-rw-r--r--sway/old/commands/mark.c87
-rw-r--r--sway/old/commands/mode.c57
-rw-r--r--sway/old/commands/mouse_warping.c17
-rw-r--r--sway/old/commands/move.c189
-rw-r--r--sway/old/commands/new_float.c8
-rw-r--r--sway/old/commands/new_window.c8
-rw-r--r--sway/old/commands/no_focus.c41
-rw-r--r--sway/old/commands/orientation.c21
-rw-r--r--sway/old/commands/output.c219
-rw-r--r--sway/old/commands/permit.c108
-rw-r--r--sway/old/commands/reload.c19
-rw-r--r--sway/old/commands/resize.c375
-rw-r--r--sway/old/commands/scratchpad.c72
-rw-r--r--sway/old/commands/seamless_mouse.c13
-rw-r--r--sway/old/commands/set.c61
-rw-r--r--sway/old/commands/show_marks.c13
-rw-r--r--sway/old/commands/smart_gaps.c20
-rw-r--r--sway/old/commands/split.c98
-rw-r--r--sway/old/commands/sticky.c25
-rw-r--r--sway/old/commands/unmark.c31
-rw-r--r--sway/old/commands/workspace.c92
-rw-r--r--sway/old/commands/workspace_auto_back_and_forth.c18
-rw-r--r--sway/old/commands/workspace_layout.c40
-rw-r--r--sway/old/config.c1313
-rw-r--r--sway/old/container.c1024
-rw-r--r--sway/old/criteria.c451
-rw-r--r--sway/old/focus.c278
-rw-r--r--sway/old/handlers.c1143
-rw-r--r--sway/old/input_state.c490
-rw-r--r--sway/old/ipc-json.c521
-rw-r--r--sway/old/ipc-server.c840
-rw-r--r--sway/old/layout.c1773
-rw-r--r--sway/old/output.c278
-rw-r--r--sway/old/security.c228
-rw-r--r--sway/old/workspace.c373
104 files changed, 0 insertions, 14614 deletions
diff --git a/sway/old/border.c b/sway/old/border.c
deleted file mode 100644
index df0022ce..00000000
--- a/sway/old/border.c
+++ /dev/null
@@ -1,510 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <wlc/wlc-render.h>
3#include <cairo/cairo.h>
4#include <pango/pangocairo.h>
5#include <stdlib.h>
6#include <stdio.h>
7#include <string.h>
8#include <strings.h>
9#include <arpa/inet.h>
10#include "sway/border.h"
11#include "sway/container.h"
12#include "sway/config.h"
13#include "client/pango.h"
14
15void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
16 color = htonl(color);
17
18 cairo_set_source_rgba(cairo,
19 (color >> (2*8) & 0xFF) / 255.0,
20 (color >> (1*8) & 0xFF) / 255.0,
21 (color >> (0*8) & 0xFF) / 255.0,
22 (color >> (3*8) & 0xFF) / 255.0);
23}
24
25void border_clear(struct border *border) {
26 if (border && border->buffer) {
27 free(border->buffer);
28 border->buffer = NULL;
29 }
30}
31
32static cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry g, cairo_surface_t **surface) {
33 if (view->border == NULL) {
34 view->border = malloc(sizeof(struct border));
35 if (!view->border) {
36 sway_log(L_ERROR, "Unable to allocate window border information");
37 return NULL;
38 }
39 }
40 cairo_t *cr;
41 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, g.size.w);
42 view->border->buffer = calloc(stride * g.size.h, sizeof(unsigned char));
43 view->border->geometry = g;
44 if (!view->border->buffer) {
45 sway_log(L_ERROR, "Unable to allocate window border buffer");
46 return NULL;
47 }
48 *surface = cairo_image_surface_create_for_data(view->border->buffer,
49 CAIRO_FORMAT_ARGB32, g.size.w, g.size.h, stride);
50 if (cairo_surface_status(*surface) != CAIRO_STATUS_SUCCESS) {
51 border_clear(view->border);
52 sway_log(L_ERROR, "Unable to allocate window border surface");
53 return NULL;
54 }
55 cr = cairo_create(*surface);
56 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
57 if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
58 cairo_surface_destroy(*surface);
59 border_clear(view->border);
60 sway_log(L_ERROR, "Unable to create cairo context");
61 return NULL;
62 }
63 return cr;
64}
65
66// TODO: move to client/cairo.h when local set_source_u32 is fixed.
67/**
68 * Renders a sharp line of any width and height.
69 *
70 * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0
71 * if the line has a width/height of one pixel, respectively.
72 */
73static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) {
74 cairo_set_source_u32(cairo, color);
75
76 if (width > 1 && height > 1) {
77 cairo_rectangle(cairo, x, y, width, height);
78 cairo_fill(cairo);
79 } else {
80 if (width == 1) {
81 x += 0.5;
82 height += y;
83 width = x;
84 }
85
86 if (height == 1) {
87 y += 0.5;
88 width += x;
89 height = y;
90 }
91
92 cairo_move_to(cairo, x, y);
93 cairo_set_line_width(cairo, 1.0);
94 cairo_line_to(cairo, width, height);
95 cairo_stroke(cairo);
96 }
97}
98
99int get_font_text_height(const char *font) {
100 cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
101 cairo_t *cr = cairo_create(surface);
102 int width, height;
103 get_text_size(cr, font, &width, &height, 1, false, "Gg");
104 cairo_surface_destroy(surface);
105 cairo_destroy(cr);
106 return height;
107}
108
109static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *colors, bool top) {
110 struct wlc_geometry *g = &view->border->geometry;
111 struct wlc_geometry *b = &view->border_geometry;
112 struct wlc_geometry *v = &view->actual_geometry;
113 enum swayc_layouts layout = view->parent->layout;
114 uint32_t color;
115
116 int x = b->origin.x - g->origin.x;
117 int y = b->origin.y - g->origin.y;
118
119 // draw vertical/horizontal indicator if container is the only child of its parent container
120 bool is_only_child = view->parent && view->parent->children && view->parent->children->length == 1;
121
122 // left border
123 int left_border = v->origin.x - b->origin.x;
124 if (left_border > 0) {
125 render_sharp_line(cr,
126 colors->child_border,
127 x, y,
128 left_border,
129 b->size.h);
130 }
131
132 // right border
133 int right_border = b->size.w - v->size.w - left_border;
134 if (right_border > 0) {
135 if (is_only_child && layout == L_HORIZ && !view->is_floating) {
136 color = colors->indicator;
137 } else {
138 color = colors->child_border;
139 }
140 render_sharp_line(cr,
141 color,
142 x + b->size.w - right_border,
143 y,
144 right_border,
145 b->size.h);
146 }
147
148 // top border
149 int top_border = v->origin.y - b->origin.y;
150 if (top && top_border > 0) {
151 render_sharp_line(cr,
152 colors->child_border,
153 x, y,
154 b->size.w,
155 top_border);
156 }
157
158 // bottom border
159 int bottom_border = b->size.h - (top_border + v->size.h);
160 if (bottom_border > 0) {
161 if (is_only_child && layout == L_VERT && !view->is_floating) {
162 color = colors->indicator;
163 } else {
164 color = colors->child_border;
165 }
166 render_sharp_line(cr,
167 color,
168 x,
169 y + b->size.h - bottom_border,
170 b->size.w,
171 bottom_border);
172 }
173}
174
175static void render_title_bar(swayc_t *view, cairo_t *cr, struct wlc_geometry *b, struct border_colors *colors) {
176 struct wlc_geometry *tb = &view->title_bar_geometry;
177 int x = MIN(tb->origin.x, tb->origin.x - b->origin.x);
178 int y = MIN(tb->origin.y, tb->origin.y - b->origin.y);
179
180 // title bar background
181 cairo_set_source_u32(cr, colors->background);
182 cairo_rectangle(cr, x, y, tb->size.w, tb->size.h);
183 cairo_fill(cr);
184
185 // header top line
186 render_sharp_line(cr, colors->border, x, y, tb->size.w, 1);
187
188 // text
189 if (view->name) {
190 int width, height;
191 get_text_size(cr, config->font, &width, &height, 1, false, "%s", view->name);
192 cairo_move_to(cr, x + 2, y + 2);
193 cairo_set_source_u32(cr, colors->text);
194 pango_printf(cr, config->font, 1, false, "%s", view->name);
195 }
196 // Marks
197 if (config->show_marks && view->marks) {
198 int total_len = 0;
199
200 for(int i = view->marks->length - 1; i >= 0; --i) {
201 char *mark = (char *)view->marks->items[i];
202 if (*mark != '_') {
203 int width, height;
204 get_text_size(cr, config->font, &width, &height, 1, false, "[%s]", mark);
205 total_len += width;
206 if ((int)tb->size.w + x - (total_len + 2) < x + 2) {
207 break;
208 } else {
209 cairo_move_to(cr, (int)tb->size.w + x - (total_len + 2), y + 2);
210 cairo_set_source_u32(cr, colors->text);
211 pango_printf(cr, config->font, 1, false, "[%s]", mark);
212 }
213 }
214 }
215 }
216
217 // titlebars has a border all around for tabbed layouts
218 if (view->parent->layout == L_TABBED) {
219 // header bottom line
220 render_sharp_line(cr, colors->border, x, y + tb->size.h - 1,
221 tb->size.w, 1);
222
223 // left border
224 render_sharp_line(cr, colors->border, x, y, 1, tb->size.h);
225
226 // right border
227 render_sharp_line(cr, colors->border, x + tb->size.w - 1, y,
228 1, tb->size.h);
229
230 return;
231 }
232
233 if ((uint32_t)(view->actual_geometry.origin.y - tb->origin.y) == tb->size.h) {
234 // header bottom line
235 render_sharp_line(cr, colors->border,
236 x + view->actual_geometry.origin.x - tb->origin.x,
237 y + tb->size.h - 1,
238 view->actual_geometry.size.w, 1);
239 } else {
240 // header bottom line
241 render_sharp_line(cr, colors->border, x,
242 y + tb->size.h - 1,
243 tb->size.w, 1);
244 }
245}
246
247/**
248 * Generate nested container title for tabbed/stacked layouts
249 */
250static char *generate_container_title(swayc_t *container) {
251 char layout = 'H';
252 char *name, *prev_name = NULL;
253 switch (container->layout) {
254 case L_TABBED:
255 layout = 'T';
256 break;
257 case L_STACKED:
258 layout = 'S';
259 break;
260 case L_VERT:
261 layout = 'V';
262 break;
263 default:
264 layout = 'H';
265 }
266 int len = 9;
267 name = malloc(len * sizeof(char));
268 if (!name) {
269 sway_log(L_ERROR, "Unable to allocate container title");
270 return NULL;
271 }
272 snprintf(name, len, "sway: %c[", layout);
273
274 int i;
275 for (i = 0; i < container->children->length; ++i) {
276 prev_name = name;
277 swayc_t* child = container->children->items[i];
278 const char *title = NULL;
279 if (child->type == C_VIEW) {
280 title = child->app_id ? child->app_id :
281 (child->instance ? child->instance :
282 (child->class ? child->class :"(null)"));
283 } else { //child->type == C_CONTAINER
284 title = generate_container_title(child);
285 }
286
287 len = strlen(name) + strlen(title) + 1;
288 if (i < container->children->length-1) {
289 len++;
290 }
291
292 name = malloc(len * sizeof(char));
293 if (!name) {
294 free(prev_name);
295 sway_log(L_ERROR, "Unable to allocate container title");
296 return NULL;
297 }
298 if (i < container->children->length-1) {
299 snprintf(name, len, "%s%s ", prev_name, title);
300 } else {
301 snprintf(name, len, "%s%s", prev_name, title);
302 }
303 free(prev_name);
304 }
305
306 prev_name = name;
307 len = strlen(name) + 2;
308 name = malloc(len * sizeof(char));
309 if (!name) {
310 free(prev_name);
311 sway_log(L_ERROR, "Unable to allocate container title");
312 return NULL;
313 }
314 snprintf(name, len, "%s]", prev_name);
315 free(prev_name);
316 free(container->name);
317 container->name = name;
318 return container->name + 6; // don't include "sway: "
319}
320
321void update_tabbed_stacked_titlebars(swayc_t *c, cairo_t *cr, struct wlc_geometry *g, swayc_t *focused, swayc_t *focused_inactive) {
322 if (c->type == C_CONTAINER) {
323 if (c->parent->focused == c) {
324 render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
325 } else {
326 render_title_bar(c, cr, g, &config->border_colors.unfocused);
327 }
328
329 if (!c->visible) {
330 return;
331 }
332
333 int i;
334 for (i = 0; i < c->children->length; ++i) {
335 swayc_t *child = c->children->items[i];
336 update_tabbed_stacked_titlebars(child, cr, g, focused, focused_inactive);
337 }
338 } else {
339 bool is_child_of_focused = swayc_is_child_of(c, get_focused_container(&root_container));
340
341 if (focused == c || is_child_of_focused) {
342 render_title_bar(c, cr, g, &config->border_colors.focused);
343 } else if (focused_inactive == c) {
344 render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
345 } else {
346 render_title_bar(c, cr, g, &config->border_colors.unfocused);
347 }
348 }
349}
350
351static void update_view_border(swayc_t *view) {
352 if (!view->visible) {
353 return;
354 }
355
356 cairo_t *cr = NULL;
357 cairo_surface_t *surface = NULL;
358
359 // clear previous border buffer.
360 border_clear(view->border);
361
362 // get focused and focused_inactive views
363 swayc_t *focused = get_focused_view(&root_container);
364 swayc_t *container = swayc_parent_by_type(view, C_CONTAINER);
365 swayc_t *focused_inactive = NULL;
366
367 bool is_child_of_focused = swayc_is_parent_of(get_focused_container(&root_container), view);
368
369 if (container) {
370 focused_inactive = swayc_focus_by_type(container, C_VIEW);
371 } else {
372 container = swayc_parent_by_type(view, C_WORKSPACE);
373 if (container) {
374 focused_inactive = swayc_focus_by_type(container, C_VIEW);
375 }
376 }
377
378 // for tabbed/stacked layouts the focused view has to draw all the
379 // titlebars of the hidden views.
380 swayc_t *p = NULL;
381 if (view->parent->focused == view && (p = swayc_tabbed_stacked_ancestor(view))) {
382 struct wlc_geometry g = {
383 .origin = {
384 .x = p->x,
385 .y = p->y
386 },
387 .size = {
388 .w = p->width,
389 .h = p->height
390 }
391 };
392 cr = create_border_buffer(view, g, &surface);
393 if (!cr) {
394 goto cleanup;
395 }
396
397 bool render_top = !should_hide_top_border(view, view->y);
398 if (view == focused || is_child_of_focused) {
399 render_borders(view, cr, &config->border_colors.focused, render_top);
400 } else {
401 render_borders(view, cr, &config->border_colors.focused_inactive, render_top);
402 }
403
404 // generate container titles
405 int i;
406 for (i = 0; i < p->children->length; ++i) {
407 swayc_t *child = p->children->items[i];
408 if (child->type == C_CONTAINER) {
409 generate_container_title(child);
410 }
411 }
412
413 update_tabbed_stacked_titlebars(p, cr, &g, focused, focused_inactive);
414 } else {
415 switch (view->border_type) {
416 case B_NONE:
417 break;
418 case B_PIXEL:
419 cr = create_border_buffer(view, view->border_geometry, &surface);
420 if (!cr) {
421 break;
422 }
423
424 if (focused == view || is_child_of_focused) {
425 render_borders(view, cr, &config->border_colors.focused, true);
426 } else if (focused_inactive == view) {
427 render_borders(view, cr, &config->border_colors.focused_inactive, true);
428 } else {
429 render_borders(view, cr, &config->border_colors.unfocused, true);
430 }
431
432 break;
433 case B_NORMAL:
434 cr = create_border_buffer(view, view->border_geometry, &surface);
435 if (!cr) {
436 break;
437 }
438
439 if (focused == view || is_child_of_focused) {
440 render_borders(view, cr, &config->border_colors.focused, false);
441 render_title_bar(view, cr, &view->border_geometry,
442 &config->border_colors.focused);
443 } else if (focused_inactive == view) {
444 render_borders(view, cr, &config->border_colors.focused_inactive, false);
445 render_title_bar(view, cr, &view->border_geometry,
446 &config->border_colors.focused_inactive);
447 } else {
448 render_borders(view, cr, &config->border_colors.unfocused, false);
449 render_title_bar(view, cr, &view->border_geometry,
450 &config->border_colors.unfocused);
451 }
452
453 break;
454 }
455 }
456
457cleanup:
458
459 if (surface) {
460 cairo_surface_flush(surface);
461 cairo_surface_destroy(surface);
462 }
463
464 if (cr) {
465 cairo_destroy(cr);
466 }
467}
468
469void update_container_border(swayc_t *container) {
470 if (container->type == C_VIEW) {
471 update_view_border(container);
472 return;
473 } else {
474 for (int i = 0; i < container->children->length; ++i) {
475 update_container_border(container->children->items[i]);
476 }
477 }
478}
479
480void render_view_borders(wlc_handle view) {
481 swayc_t *c = swayc_by_handle(view);
482
483
484 // emulate i3 behavior for drawing borders for tabbed and stacked layouts:
485 // if we are not the only child in the container, always draw borders,
486 // regardless of the border setting on the individual view
487 if (!c || (c->border_type == B_NONE
488 && !((c->parent->layout == L_TABBED || c->parent->layout == L_STACKED)
489 && c->parent->children->length > 1))) {
490 return;
491 }
492
493 if (c->border && c->border->buffer) {
494 wlc_pixels_write(WLC_RGBA8888, &c->border->geometry, c->border->buffer);
495 }
496}
497
498bool should_hide_top_border(swayc_t *con, double y) {
499 // returns true if container is child of tabbed/stacked layout and is
500 // sharing top border with tabbed titlebar
501 swayc_t *par = con->parent;
502 while (par->type != C_WORKSPACE) {
503 if (par->layout == L_TABBED || par->layout == L_STACKED) {
504 return con->y == y;
505 }
506 con = par;
507 par = par->parent;
508 }
509 return false;
510}
diff --git a/sway/old/commands.c b/sway/old/commands.c
deleted file mode 100644
index e1181893..00000000
--- a/sway/old/commands.c
+++ /dev/null
@@ -1,686 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <xkbcommon/xkbcommon.h>
3#include <xkbcommon/xkbcommon-names.h>
4#include <wlc/wlc.h>
5#include <wlc/wlc-render.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <errno.h>
9#include <string.h>
10#include <strings.h>
11#include <unistd.h>
12#include <ctype.h>
13#include <wordexp.h>
14#include <libgen.h>
15#include <sys/types.h>
16#include <sys/wait.h>
17#include <limits.h>
18#include <float.h>
19#include <libinput.h>
20#include "sway/layout.h"
21#include "sway/focus.h"
22#include "sway/workspace.h"
23#include "sway/commands.h"
24#include "sway/container.h"
25#include "sway/output.h"
26#include "sway/handlers.h"
27#include "sway/input_state.h"
28#include "sway/criteria.h"
29#include "sway/ipc-server.h"
30#include "sway/security.h"
31#include "sway/input.h"
32#include "sway/border.h"
33#include "stringop.h"
34#include "sway.h"
35#include "util.h"
36#include "list.h"
37#include "log.h"
38
39struct cmd_handler {
40 char *command;
41 sway_cmd *handle;
42};
43
44int sp_index = 0;
45
46swayc_t *current_container = NULL;
47
48// Returns error object, or NULL if check succeeds.
49struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) {
50 struct cmd_results *error = NULL;
51 switch (type) {
52 case EXPECTED_MORE_THAN:
53 if (argc > val) {
54 return NULL;
55 }
56 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
57 "(expected more than %d argument%s, got %d)",
58 name, val, (char*[2]){"s", ""}[argc==1], argc);
59 break;
60 case EXPECTED_AT_LEAST:
61 if (argc >= val) {
62 return NULL;
63 }
64 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
65 "(expected at least %d argument%s, got %d)",
66 name, val, (char*[2]){"s", ""}[argc==1], argc);
67 break;
68 case EXPECTED_LESS_THAN:
69 if (argc < val) {
70 return NULL;
71 };
72 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
73 "(expected less than %d argument%s, got %d)",
74 name, val, (char*[2]){"s", ""}[argc==1], argc);
75 break;
76 case EXPECTED_EQUAL_TO:
77 if (argc == val) {
78 return NULL;
79 };
80 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
81 "(expected %d arguments, got %d)", name, val, argc);
82 break;
83 }
84 return error;
85}
86
87void hide_view_in_scratchpad(swayc_t *sp_view) {
88 if (sp_view == NULL) {
89 return;
90 }
91
92 wlc_view_set_mask(sp_view->handle, 0);
93 sp_view->visible = false;
94 swayc_t *ws = sp_view->parent;
95 remove_child(sp_view);
96 if (swayc_active_workspace() != ws && ws->floating->length == 0 && ws->children->length == 0) {
97 destroy_workspace(ws);
98 } else {
99 arrange_windows(ws, -1, -1);
100 }
101 set_focused_container(container_under_pointer());
102}
103
104void input_cmd_apply(struct input_config *input) {
105 int i;
106 i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier);
107 if (i >= 0) {
108 // merge existing config
109 struct input_config *ic = config->input_configs->items[i];
110 merge_input_config(ic, input);
111 free_input_config(input);
112 input = ic;
113 } else {
114 list_add(config->input_configs, input);
115 }
116
117 current_input_config = input;
118
119 if (input->identifier) {
120 // Try to find the input device and apply configuration now. If
121 // this is during startup then there will be no container and config
122 // will be applied during normal "new input" event from wlc.
123 /* TODO WLR
124 struct libinput_device *device = NULL;
125 for (int i = 0; i < input_devices->length; ++i) {
126 device = input_devices->items[i];
127 char* dev_identifier = libinput_dev_unique_id(device);
128 if (!dev_identifier) {
129 break;
130 }
131 int match = dev_identifier && strcmp(dev_identifier, input->identifier) == 0;
132 free(dev_identifier);
133 if (match) {
134 apply_input_config(input, device);
135 break;
136 }
137 }
138 */
139 }
140}
141
142void remove_view_from_scratchpad(swayc_t *view) {
143 int i;
144 for (i = 0; i < scratchpad->length; i++) {
145 if (scratchpad->items[i] == view) {
146 if (sp_index == 0) {
147 sp_index = scratchpad->length - 1;
148 } else {
149 sp_index--;
150 }
151 list_del(scratchpad, sp_index);
152 sp_view = NULL;
153 }
154 }
155}
156
157/* Keep alphabetized */
158static struct cmd_handler handlers[] = {
159 { "assign", cmd_assign },
160 { "bar", cmd_bar },
161 { "bindcode", cmd_bindcode },
162 { "bindsym", cmd_bindsym },
163 { "border", cmd_border },
164 { "client.background", cmd_client_background },
165 { "client.focused", cmd_client_focused },
166 { "client.focused_inactive", cmd_client_focused_inactive },
167 { "client.placeholder", cmd_client_placeholder },
168 { "client.unfocused", cmd_client_unfocused },
169 { "client.urgent", cmd_client_urgent },
170 { "clipboard", cmd_clipboard },
171 { "commands", cmd_commands },
172 { "debuglog", cmd_debuglog },
173 { "default_border", cmd_default_border },
174 { "default_floating_border", cmd_default_floating_border },
175 { "default_orientation", cmd_orientation },
176 { "exec", cmd_exec },
177 { "exec_always", cmd_exec_always },
178 { "exit", cmd_exit },
179 { "floating", cmd_floating },
180 { "floating_maximum_size", cmd_floating_maximum_size },
181 { "floating_minimum_size", cmd_floating_minimum_size },
182 { "floating_modifier", cmd_floating_mod },
183 { "floating_scroll", cmd_floating_scroll },
184 { "focus", cmd_focus },
185 { "focus_follows_mouse", cmd_focus_follows_mouse },
186 { "font", cmd_font },
187 { "for_window", cmd_for_window },
188 { "force_focus_wrapping", cmd_force_focus_wrapping },
189 { "fullscreen", cmd_fullscreen },
190 { "gaps", cmd_gaps },
191 { "hide_edge_borders", cmd_hide_edge_borders },
192 { "include", cmd_include },
193 { "input", cmd_input },
194 { "ipc", cmd_ipc },
195 { "kill", cmd_kill },
196 { "layout", cmd_layout },
197 { "log_colors", cmd_log_colors },
198 { "mark", cmd_mark },
199 { "mode", cmd_mode },
200 { "mouse_warping", cmd_mouse_warping },
201 { "move", cmd_move },
202 { "new_float", cmd_new_float },
203 { "new_window", cmd_new_window },
204 { "no_focus", cmd_no_focus },
205 { "output", cmd_output },
206 { "permit", cmd_permit },
207 { "reject", cmd_reject },
208 { "reload", cmd_reload },
209 { "resize", cmd_resize },
210 { "scratchpad", cmd_scratchpad },
211 { "seamless_mouse", cmd_seamless_mouse },
212 { "set", cmd_set },
213 { "show_marks", cmd_show_marks },
214 { "smart_gaps", cmd_smart_gaps },
215 { "split", cmd_split },
216 { "splith", cmd_splith },
217 { "splitt", cmd_splitt },
218 { "splitv", cmd_splitv },
219 { "sticky", cmd_sticky },
220 { "unmark", cmd_unmark },
221 { "workspace", cmd_workspace },
222 { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
223 { "workspace_layout", cmd_workspace_layout },
224};
225
226static struct cmd_handler bar_handlers[] = {
227 { "activate_button", bar_cmd_activate_button },
228 { "binding_mode_indicator", bar_cmd_binding_mode_indicator },
229 { "bindsym", bar_cmd_bindsym },
230 { "colors", bar_cmd_colors },
231 { "context_button", bar_cmd_context_button },
232 { "font", bar_cmd_font },
233 { "height", bar_cmd_height },
234 { "hidden_state", bar_cmd_hidden_state },
235 { "icon_theme", bar_cmd_icon_theme },
236 { "id", bar_cmd_id },
237 { "mode", bar_cmd_mode },
238 { "modifier", bar_cmd_modifier },
239 { "output", bar_cmd_output },
240 { "pango_markup", bar_cmd_pango_markup },
241 { "position", bar_cmd_position },
242 { "secondary_button", bar_cmd_secondary_button },
243 { "separator_symbol", bar_cmd_separator_symbol },
244 { "status_command", bar_cmd_status_command },
245 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers },
246 { "swaybar_command", bar_cmd_swaybar_command },
247 { "tray_output", bar_cmd_tray_output },
248 { "tray_padding", bar_cmd_tray_padding },
249 { "workspace_buttons", bar_cmd_workspace_buttons },
250 { "wrap_scroll", bar_cmd_wrap_scroll },
251};
252
253/**
254 * Check and add color to buffer.
255 *
256 * return error object, or NULL if color is valid.
257 */
258struct cmd_results *add_color(const char *name, char *buffer, const char *color) {
259 int len = strlen(color);
260 if (len != 7 && len != 9) {
261 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
262 }
263
264 if (color[0] != '#') {
265 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
266 }
267
268 int i;
269 for (i = 1; i < len; ++i) {
270 if (!isxdigit(color[i])) {
271 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
272 }
273 }
274
275 // copy color to buffer
276 strncpy(buffer, color, len);
277 // add default alpha channel if color was defined without it
278 if (len == 7) {
279 buffer[7] = 'f';
280 buffer[8] = 'f';
281 }
282 buffer[9] = '\0';
283
284 return NULL;
285}
286
287static struct cmd_handler input_handlers[] = {
288 { "accel_profile", input_cmd_accel_profile },
289 { "click_method", input_cmd_click_method },
290 { "drag_lock", input_cmd_drag_lock },
291 { "dwt", input_cmd_dwt },
292 { "events", input_cmd_events },
293 { "left_handed", input_cmd_left_handed },
294 { "middle_emulation", input_cmd_middle_emulation },
295 { "natural_scroll", input_cmd_natural_scroll },
296 { "pointer_accel", input_cmd_pointer_accel },
297 { "scroll_method", input_cmd_scroll_method },
298 { "tap", input_cmd_tap },
299};
300
301static struct cmd_handler bar_colors_handlers[] = {
302 { "active_workspace", bar_colors_cmd_active_workspace },
303 { "background", bar_colors_cmd_background },
304 { "binding_mode", bar_colors_cmd_binding_mode },
305 { "focused_background", bar_colors_cmd_focused_background },
306 { "focused_separator", bar_colors_cmd_focused_separator },
307 { "focused_statusline", bar_colors_cmd_focused_statusline },
308 { "focused_workspace", bar_colors_cmd_focused_workspace },
309 { "inactive_workspace", bar_colors_cmd_inactive_workspace },
310 { "separator", bar_colors_cmd_separator },
311 { "statusline", bar_colors_cmd_statusline },
312 { "urgent_workspace", bar_colors_cmd_urgent_workspace },
313};
314
315static struct cmd_handler ipc_handlers[] = {
316 { "*", cmd_ipc_cmd },
317 { "bar-config", cmd_ipc_cmd },
318 { "command", cmd_ipc_cmd },
319 { "events", cmd_ipc_events },
320 { "inputs", cmd_ipc_cmd },
321 { "marks", cmd_ipc_cmd },
322 { "outputs", cmd_ipc_cmd },
323 { "tree", cmd_ipc_cmd },
324 { "workspaces", cmd_ipc_cmd },
325};
326
327static struct cmd_handler ipc_event_handlers[] = {
328 { "*", cmd_ipc_event_cmd },
329 { "binding", cmd_ipc_event_cmd },
330 { "input", cmd_ipc_event_cmd },
331 { "mode", cmd_ipc_event_cmd },
332 { "output", cmd_ipc_event_cmd },
333 { "window", cmd_ipc_event_cmd },
334 { "workspace", cmd_ipc_event_cmd },
335};
336
337static int handler_compare(const void *_a, const void *_b) {
338 const struct cmd_handler *a = _a;
339 const struct cmd_handler *b = _b;
340 return strcasecmp(a->command, b->command);
341}
342
343static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
344 struct cmd_handler d = { .command=line };
345 struct cmd_handler *res = NULL;
346 sway_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_INPUT);
347 if (block == CMD_BLOCK_BAR) {
348 res = bsearch(&d, bar_handlers,
349 sizeof(bar_handlers) / sizeof(struct cmd_handler),
350 sizeof(struct cmd_handler), handler_compare);
351 } else if (block == CMD_BLOCK_BAR_COLORS){
352 res = bsearch(&d, bar_colors_handlers,
353 sizeof(bar_colors_handlers) / sizeof(struct cmd_handler),
354 sizeof(struct cmd_handler), handler_compare);
355 } else if (block == CMD_BLOCK_INPUT) {
356 res = bsearch(&d, input_handlers,
357 sizeof(input_handlers) / sizeof(struct cmd_handler),
358 sizeof(struct cmd_handler), handler_compare);
359 } else if (block == CMD_BLOCK_IPC) {
360 res = bsearch(&d, ipc_handlers,
361 sizeof(ipc_handlers) / sizeof(struct cmd_handler),
362 sizeof(struct cmd_handler), handler_compare);
363 } else if (block == CMD_BLOCK_IPC_EVENTS) {
364 res = bsearch(&d, ipc_event_handlers,
365 sizeof(ipc_event_handlers) / sizeof(struct cmd_handler),
366 sizeof(struct cmd_handler), handler_compare);
367 } else {
368 res = bsearch(&d, handlers,
369 sizeof(handlers) / sizeof(struct cmd_handler),
370 sizeof(struct cmd_handler), handler_compare);
371 }
372 return res;
373}
374
375struct cmd_results *handle_command(char *_exec, enum command_context context) {
376 // Even though this function will process multiple commands we will only
377 // return the last error, if any (for now). (Since we have access to an
378 // error string we could e.g. concatonate all errors there.)
379 struct cmd_results *results = NULL;
380 char *exec = strdup(_exec);
381 char *head = exec;
382 char *cmdlist;
383 char *cmd;
384 list_t *containers = NULL;
385
386 head = exec;
387 do {
388 // Extract criteria (valid for this command list only).
389 if (*head == '[') {
390 ++head;
391 char *criteria_string = argsep(&head, "]");
392 if (head) {
393 ++head;
394 list_t *tokens = create_list();
395 char *error;
396
397 if ((error = extract_crit_tokens(tokens, criteria_string))) {
398 results = cmd_results_new(CMD_INVALID, criteria_string,
399 "Can't parse criteria string: %s", error);
400 free(error);
401 free(tokens);
402 goto cleanup;
403 }
404 containers = container_for(tokens);
405
406 free(tokens);
407 } else {
408 if (!results) {
409 results = cmd_results_new(CMD_INVALID, criteria_string, "Unmatched [");
410 }
411 goto cleanup;
412 }
413 // Skip leading whitespace
414 head += strspn(head, whitespace);
415 }
416 // Split command list
417 cmdlist = argsep(&head, ";");
418 cmdlist += strspn(cmdlist, whitespace);
419 do {
420 // Split commands
421 cmd = argsep(&cmdlist, ",");
422 cmd += strspn(cmd, whitespace);
423 if (strcmp(cmd, "") == 0) {
424 sway_log(L_INFO, "Ignoring empty command.");
425 continue;
426 }
427 sway_log(L_INFO, "Handling command '%s'", cmd);
428 //TODO better handling of argv
429 int argc;
430 char **argv = split_args(cmd, &argc);
431 if (strcmp(argv[0], "exec") != 0) {
432 int i;
433 for (i = 1; i < argc; ++i) {
434 if (*argv[i] == '\"' || *argv[i] == '\'') {
435 strip_quotes(argv[i]);
436 }
437 }
438 }
439 struct cmd_handler *handler = find_handler(argv[0], CMD_BLOCK_END);
440 if (!handler) {
441 if (results) {
442 free_cmd_results(results);
443 }
444 results = cmd_results_new(CMD_INVALID, cmd, "Unknown/invalid command");
445 free_argv(argc, argv);
446 goto cleanup;
447 }
448 if (!(get_command_policy_mask(argv[0]) & context)) {
449 if (results) {
450 free_cmd_results(results);
451 }
452 results = cmd_results_new(CMD_INVALID, cmd,
453 "Permission denied for %s via %s", cmd,
454 command_policy_str(context));
455 free_argv(argc, argv);
456 goto cleanup;
457 }
458 int i = 0;
459 do {
460 if (!containers) {
461 current_container = get_focused_container(&root_container);
462 } else if (containers->length == 0) {
463 if (results) {
464 free_cmd_results(results);
465 }
466 results = cmd_results_new(CMD_FAILURE, argv[0], "No matching container");
467 goto cleanup;
468 } else {
469 current_container = (swayc_t *)containers->items[i];
470 }
471 sway_log(L_INFO, "Running on container '%s'", current_container->name);
472
473 struct cmd_results *res = handler->handle(argc-1, argv+1);
474 if (res->status != CMD_SUCCESS) {
475 free_argv(argc, argv);
476 if (results) {
477 free_cmd_results(results);
478 }
479 results = res;
480 goto cleanup;
481 }
482 free_cmd_results(res);
483 ++i;
484 } while(containers && i < containers->length);
485
486 free_argv(argc, argv);
487 } while(cmdlist);
488
489 if (containers) {
490 list_free(containers);
491 containers = NULL;
492 }
493 } while(head);
494 cleanup:
495 free(exec);
496 if (containers) {
497 free(containers);
498 }
499 if (!results) {
500 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
501 }
502 return results;
503}
504
505// this is like handle_command above, except:
506// 1) it ignores empty commands (empty lines)
507// 2) it does variable substitution
508// 3) it doesn't split commands (because the multiple commands are supposed to
509// be chained together)
510// 4) handle_command handles all state internally while config_command has some
511// state handled outside (notably the block mode, in read_config)
512struct cmd_results *config_command(char *exec, enum cmd_status block) {
513 struct cmd_results *results = NULL;
514 int argc;
515 char **argv = split_args(exec, &argc);
516 if (!argc) {
517 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
518 goto cleanup;
519 }
520
521 sway_log(L_INFO, "handling config command '%s'", exec);
522 // Endblock
523 if (**argv == '}') {
524 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
525 goto cleanup;
526 }
527 struct cmd_handler *handler = find_handler(argv[0], block);
528 if (!handler) {
529 char *input = argv[0] ? argv[0] : "(empty)";
530 results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
531 goto cleanup;
532 }
533 int i;
534 // Var replacement, for all but first argument of set
535 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
536 argv[i] = do_var_replacement(argv[i]);
537 unescape_string(argv[i]);
538 }
539 /* Strip quotes for first argument.
540 * TODO This part needs to be handled much better */
541 if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
542 strip_quotes(argv[1]);
543 }
544 if (handler->handle) {
545 results = handler->handle(argc-1, argv+1);
546 } else {
547 results = cmd_results_new(CMD_INVALID, argv[0], "This command is shimmed, but unimplemented");
548 }
549
550cleanup:
551 free_argv(argc, argv);
552 return results;
553}
554
555struct cmd_results *config_commands_command(char *exec) {
556 struct cmd_results *results = NULL;
557 int argc;
558 char **argv = split_args(exec, &argc);
559 if (!argc) {
560 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
561 goto cleanup;
562 }
563
564 // Find handler for the command this is setting a policy for
565 char *cmd = argv[0];
566
567 if (strcmp(cmd, "}") == 0) {
568 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
569 goto cleanup;
570 }
571
572 struct cmd_handler *handler = find_handler(cmd, CMD_BLOCK_END);
573 if (!handler && strcmp(cmd, "*") != 0) {
574 char *input = cmd ? cmd : "(empty)";
575 results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
576 goto cleanup;
577 }
578
579 enum command_context context = 0;
580
581 struct {
582 char *name;
583 enum command_context context;
584 } context_names[] = {
585 { "config", CONTEXT_CONFIG },
586 { "binding", CONTEXT_BINDING },
587 { "ipc", CONTEXT_IPC },
588 { "criteria", CONTEXT_CRITERIA },
589 { "all", CONTEXT_ALL },
590 };
591
592 for (int i = 1; i < argc; ++i) {
593 size_t j;
594 for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) {
595 if (strcmp(context_names[j].name, argv[i]) == 0) {
596 break;
597 }
598 }
599 if (j == sizeof(context_names) / sizeof(context_names[0])) {
600 results = cmd_results_new(CMD_INVALID, cmd,
601 "Invalid command context %s", argv[i]);
602 goto cleanup;
603 }
604 context |= context_names[j].context;
605 }
606
607 struct command_policy *policy = NULL;
608 for (int i = 0; i < config->command_policies->length; ++i) {
609 struct command_policy *p = config->command_policies->items[i];
610 if (strcmp(p->command, cmd) == 0) {
611 policy = p;
612 break;
613 }
614 }
615 if (!policy) {
616 policy = alloc_command_policy(cmd);
617 sway_assert(policy, "Unable to allocate security policy");
618 if (policy) {
619 list_add(config->command_policies, policy);
620 }
621 }
622 policy->context = context;
623
624 sway_log(L_INFO, "Set command policy for %s to %d",
625 policy->command, policy->context);
626
627 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
628
629cleanup:
630 free_argv(argc, argv);
631 return results;
632}
633
634struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *format, ...) {
635 struct cmd_results *results = malloc(sizeof(struct cmd_results));
636 if (!results) {
637 sway_log(L_ERROR, "Unable to allocate command results");
638 return NULL;
639 }
640 results->status = status;
641 if (input) {
642 results->input = strdup(input); // input is the command name
643 } else {
644 results->input = NULL;
645 }
646 if (format) {
647 char *error = malloc(256);
648 va_list args;
649 va_start(args, format);
650 if (error) {
651 vsnprintf(error, 256, format, args);
652 }
653 va_end(args);
654 results->error = error;
655 } else {
656 results->error = NULL;
657 }
658 return results;
659}
660
661void free_cmd_results(struct cmd_results *results) {
662 if (results->input) {
663 free(results->input);
664 }
665 if (results->error) {
666 free(results->error);
667 }
668 free(results);
669}
670
671const char *cmd_results_to_json(struct cmd_results *results) {
672 json_object *result_array = json_object_new_array();
673 json_object *root = json_object_new_object();
674 json_object_object_add(root, "success", json_object_new_boolean(results->status == CMD_SUCCESS));
675 if (results->input) {
676 json_object_object_add(root, "input", json_object_new_string(results->input));
677 }
678 if (results->error) {
679 json_object_object_add(root, "error", json_object_new_string(results->error));
680 }
681 json_object_array_add(result_array, root);
682 const char *json = json_object_to_json_string(result_array);
683 free(result_array);
684 free(root);
685 return json;
686}
diff --git a/sway/old/commands/assign.c b/sway/old/commands/assign.c
deleted file mode 100644
index c3b03bbc..00000000
--- a/sway/old/commands/assign.c
+++ /dev/null
@@ -1,57 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <stdio.h>
3#include <string.h>
4#include "sway/commands.h"
5#include "sway/criteria.h"
6#include "list.h"
7#include "log.h"
8
9struct cmd_results *cmd_assign(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "assign", EXPECTED_AT_LEAST, 2))) {
12 return error;
13 }
14
15 char *criteria = *argv++;
16
17 if (strncmp(*argv, "→", strlen("→")) == 0) {
18 if (argc < 3) {
19 return cmd_results_new(CMD_INVALID, "assign", "Missing workspace");
20 }
21 argv++;
22 }
23
24 char *movecmd = "move container to workspace ";
25 int arglen = strlen(movecmd) + strlen(*argv) + 1;
26 char *cmdlist = calloc(1, arglen);
27 if (!cmdlist) {
28 return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate command list");
29 }
30 snprintf(cmdlist, arglen, "%s%s", movecmd, *argv);
31
32 struct criteria *crit = malloc(sizeof(struct criteria));
33 if (!crit) {
34 free(cmdlist);
35 return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate criteria");
36 }
37 crit->crit_raw = strdup(criteria);
38 crit->cmdlist = cmdlist;
39 crit->tokens = create_list();
40 char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
41
42 if (err_str) {
43 error = cmd_results_new(CMD_INVALID, "assign", err_str);
44 free(err_str);
45 free_criteria(crit);
46 } else if (crit->tokens->length == 0) {
47 error = cmd_results_new(CMD_INVALID, "assign", "Found no name/value pairs in criteria");
48 free_criteria(crit);
49 } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) {
50 sway_log(L_DEBUG, "assign: Duplicate, skipping.");
51 free_criteria(crit);
52 } else {
53 sway_log(L_DEBUG, "assign: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist);
54 list_add(config->criteria, crit);
55 }
56 return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
57}
diff --git a/sway/old/commands/bar.c b/sway/old/commands/bar.c
deleted file mode 100644
index 04745a6e..00000000
--- a/sway/old/commands/bar.c
+++ /dev/null
@@ -1,59 +0,0 @@
1#include <stdio.h>
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/config.h"
6#include "log.h"
7#include "util.h"
8
9struct cmd_results *cmd_bar(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "bar", EXPECTED_AT_LEAST, 1))) {
12 return error;
13 }
14
15 if (config->reading && strcmp("{", argv[0]) != 0) {
16 return cmd_results_new(CMD_INVALID, "bar",
17 "Expected '{' at start of bar config definition.");
18 }
19
20 if (!config->reading) {
21 if (argc > 1) {
22 if (strcasecmp("mode", argv[0]) == 0) {
23 return bar_cmd_mode(argc-1, argv + 1);
24 }
25
26 if (strcasecmp("hidden_state", argv[0]) == 0) {
27 return bar_cmd_hidden_state(argc-1, argv + 1);
28 }
29 }
30
31 return cmd_results_new(CMD_FAILURE, "bar", "Can only be used in config file.");
32 }
33
34 // Create new bar with default values
35 struct bar_config *bar = default_bar_config();
36 if (!bar) {
37 return cmd_results_new(CMD_FAILURE, "bar", "Unable to allocate bar state");
38 }
39
40 // set bar id
41 int i;
42 for (i = 0; i < config->bars->length; ++i) {
43 if (bar == config->bars->items[i]) {
44 const int len = 5 + numlen(i); // "bar-" + i + \0
45 bar->id = malloc(len * sizeof(char));
46 if (bar->id) {
47 snprintf(bar->id, len, "bar-%d", i);
48 } else {
49 return cmd_results_new(CMD_FAILURE, "bar", "Unable to allocate bar ID");
50 }
51 break;
52 }
53 }
54
55 // Set current bar
56 config->current_bar = bar;
57 sway_log(L_DEBUG, "Configuring bar %s", bar->id);
58 return cmd_results_new(CMD_BLOCK_BAR, NULL, NULL);
59}
diff --git a/sway/old/commands/bar/activate_button.c b/sway/old/commands/bar/activate_button.c
deleted file mode 100644
index 32a1d3e5..00000000
--- a/sway/old/commands/bar/activate_button.c
+++ /dev/null
@@ -1,26 +0,0 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_activate_button(int argc, char **argv) {
6 const char *cmd_name = "activate_button";
7#ifndef ENABLE_TRAY
8 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
9 "%s called, but sway was compiled without tray support",
10 cmd_name, cmd_name);
11#else
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
19 }
20
21 // User should be able to prefix with 0x or whatever they want
22 config->current_bar->secondary_button = strtoul(argv[0], NULL, 0);
23
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25#endif
26}
diff --git a/sway/old/commands/bar/binding_mode_indicator.c b/sway/old/commands/bar/binding_mode_indicator.c
deleted file mode 100644
index 64f5b84f..00000000
--- a/sway/old/commands/bar/binding_mode_indicator.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "binding_mode_indicator", "No bar defined.");
14 }
15
16 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->binding_mode_indicator = true;
18 sway_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", config->current_bar->id);
19 } else if (strcasecmp("no", argv[0]) == 0) {
20 config->current_bar->binding_mode_indicator = false;
21 sway_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", config->current_bar->id);
22 } else {
23 error = cmd_results_new(CMD_INVALID, "binding_mode_indicator", "Invalid value %s", argv[0]);
24 return error;
25 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
diff --git a/sway/old/commands/bar/bindsym.c b/sway/old/commands/bar/bindsym.c
deleted file mode 100644
index 5f90b51a..00000000
--- a/sway/old/commands/bar/bindsym.c
+++ /dev/null
@@ -1,48 +0,0 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5#include "list.h"
6#include "log.h"
7#include "stringop.h"
8
9struct cmd_results *bar_cmd_bindsym(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
12 return error;
13 } else if (!config->reading) {
14 return cmd_results_new(CMD_FAILURE, "bindsym", "Can only be used in config file.");
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, "bindsym", "No bar defined.");
19 }
20
21 if (strlen(argv[1]) != 7) {
22 return cmd_results_new(CMD_INVALID, "bindsym", "Invalid mouse binding %s", argv[1]);
23 }
24 uint32_t numbutton = (uint32_t)atoi(argv[1] + 6);
25 if (numbutton < 1 || numbutton > 5 || strncmp(argv[1], "button", 6) != 0) {
26 return cmd_results_new(CMD_INVALID, "bindsym", "Invalid mouse binding %s", argv[1]);
27 }
28 struct sway_mouse_binding *binding = malloc(sizeof(struct sway_mouse_binding));
29 if (!binding) {
30 return cmd_results_new(CMD_FAILURE, "bindsym", "Unable to allocate binding");
31 }
32 binding->button = numbutton;
33 binding->command = join_args(argv + 1, argc - 1);
34
35 struct bar_config *bar = config->current_bar;
36 int i = list_seq_find(bar->bindings, sway_mouse_binding_cmp_buttons, binding);
37 if (i > -1) {
38 sway_log(L_DEBUG, "bindsym - '%s' for swaybar already exists, overwriting", argv[0]);
39 struct sway_mouse_binding *dup = bar->bindings->items[i];
40 free_sway_mouse_binding(dup);
41 list_del(bar->bindings, i);
42 }
43 list_add(bar->bindings, binding);
44 list_qsort(bar->bindings, sway_mouse_binding_cmp_qsort);
45
46 sway_log(L_DEBUG, "bindsym - Bound %s to command %s when clicking swaybar", argv[0], binding->command);
47 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
48}
diff --git a/sway/old/commands/bar/colors.c b/sway/old/commands/bar/colors.c
deleted file mode 100644
index 8b3b0aac..00000000
--- a/sway/old/commands/bar/colors.c
+++ /dev/null
@@ -1,129 +0,0 @@
1#include <string.h>
2#include "sway/commands.h"
3
4static struct cmd_results *parse_single_color(char **color, const char *cmd_name, int argc, char **argv) {
5 struct cmd_results *error = NULL;
6 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
7 return error;
8 }
9
10 if (!*color) {
11 *color = malloc(10);
12 if (!*color) {
13 return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color");
14 }
15 }
16
17 error = add_color(cmd_name, *color, argv[0]);
18 if (error) {
19 return error;
20 }
21
22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
23}
24
25static struct cmd_results *parse_three_colors(char ***colors, const char *cmd_name, int argc, char **argv) {
26 struct cmd_results *error = NULL;
27 if (argc != 3) {
28 return cmd_results_new(CMD_INVALID, cmd_name, "Requires exactly three color values");
29 }
30
31 int i;
32 for (i = 0; i < 3; i++) {
33 if (!*colors[i]) {
34 *(colors[i]) = malloc(10);
35 if (!*(colors[i])) {
36 return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color");
37 }
38 }
39 error = add_color(cmd_name, *(colors[i]), argv[i]);
40 if (error) {
41 return error;
42 }
43 }
44
45 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
46}
47
48struct cmd_results *bar_cmd_colors(int argc, char **argv) {
49 struct cmd_results *error = NULL;
50 if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) {
51 return error;
52 }
53
54 if (strcmp("{", argv[0]) != 0) {
55 return cmd_results_new(CMD_INVALID, "colors",
56 "Expected '{' at the start of colors config definition.");
57 }
58
59 return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL);
60}
61
62struct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) {
63 char **colors[3] = {
64 &(config->current_bar->colors.active_workspace_border),
65 &(config->current_bar->colors.active_workspace_bg),
66 &(config->current_bar->colors.active_workspace_text)
67 };
68 return parse_three_colors(colors, "active_workspace", argc, argv);
69}
70
71struct cmd_results *bar_colors_cmd_background(int argc, char **argv) {
72 return parse_single_color(&(config->current_bar->colors.background), "background", argc, argv);
73}
74
75struct cmd_results *bar_colors_cmd_focused_background(int argc, char **argv) {
76 return parse_single_color(&(config->current_bar->colors.focused_background), "focused_background", argc, argv);
77}
78
79struct cmd_results *bar_colors_cmd_binding_mode(int argc, char **argv) {
80 char **colors[3] = {
81 &(config->current_bar->colors.binding_mode_border),
82 &(config->current_bar->colors.binding_mode_bg),
83 &(config->current_bar->colors.binding_mode_text)
84 };
85 return parse_three_colors(colors, "binding_mode", argc, argv);
86}
87
88struct cmd_results *bar_colors_cmd_focused_workspace(int argc, char **argv) {
89 char **colors[3] = {
90 &(config->current_bar->colors.focused_workspace_border),
91 &(config->current_bar->colors.focused_workspace_bg),
92 &(config->current_bar->colors.focused_workspace_text)
93 };
94 return parse_three_colors(colors, "focused_workspace", argc, argv);
95}
96
97struct cmd_results *bar_colors_cmd_inactive_workspace(int argc, char **argv) {
98 char **colors[3] = {
99 &(config->current_bar->colors.inactive_workspace_border),
100 &(config->current_bar->colors.inactive_workspace_bg),
101 &(config->current_bar->colors.inactive_workspace_text)
102 };
103 return parse_three_colors(colors, "inactive_workspace", argc, argv);
104}
105
106struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) {
107 return parse_single_color(&(config->current_bar->colors.separator), "separator", argc, argv);
108}
109
110struct cmd_results *bar_colors_cmd_focused_separator(int argc, char **argv) {
111 return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv);
112}
113
114struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) {
115 return parse_single_color(&(config->current_bar->colors.statusline), "statusline", argc, argv);
116}
117
118struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) {
119 return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv);
120}
121
122struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) {
123 char **colors[3] = {
124 &(config->current_bar->colors.urgent_workspace_border),
125 &(config->current_bar->colors.urgent_workspace_bg),
126 &(config->current_bar->colors.urgent_workspace_text)
127 };
128 return parse_three_colors(colors, "urgent_workspace", argc, argv);
129}
diff --git a/sway/old/commands/bar/context_button.c b/sway/old/commands/bar/context_button.c
deleted file mode 100644
index 6d7d7aec..00000000
--- a/sway/old/commands/bar/context_button.c
+++ /dev/null
@@ -1,26 +0,0 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_context_button(int argc, char **argv) {
6 const char *cmd_name = "context_button";
7#ifndef ENABLE_TRAY
8 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
9 "%s called, but sway was compiled without tray support",
10 cmd_name, cmd_name);
11#else
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
19 }
20
21 // User should be able to prefix with 0x or whatever they want
22 config->current_bar->context_button = strtoul(argv[0], NULL, 0);
23
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25#endif
26}
diff --git a/sway/old/commands/bar/font.c b/sway/old/commands/bar/font.c
deleted file mode 100644
index c586c5bc..00000000
--- a/sway/old/commands/bar/font.c
+++ /dev/null
@@ -1,26 +0,0 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "log.h"
4#include "stringop.h"
5
6struct cmd_results *bar_cmd_font(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "font", "No bar defined.");
14 }
15
16 char *font = join_args(argv, argc);
17 free(config->current_bar->font);
18 if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) {
19 config->current_bar->font = font;
20 } else {
21 config->current_bar->font = font;
22 }
23
24 sway_log(L_DEBUG, "Settings font '%s' for bar: %s", config->current_bar->font, config->current_bar->id);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/old/commands/bar/height.c b/sway/old/commands/bar/height.c
deleted file mode 100644
index eb576ab3..00000000
--- a/sway/old/commands/bar/height.c
+++ /dev/null
@@ -1,21 +0,0 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_height(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "height", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 int height = atoi(argv[0]);
13 if (height < 0) {
14 return cmd_results_new(CMD_INVALID, "height",
15 "Invalid height value: %s", argv[0]);
16 }
17
18 config->current_bar->height = height;
19 sway_log(L_DEBUG, "Setting bar height to %d on bar: %s", height, config->current_bar->id);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/old/commands/bar/hidden_state.c b/sway/old/commands/bar/hidden_state.c
deleted file mode 100644
index 0b49aa6b..00000000
--- a/sway/old/commands/bar/hidden_state.c
+++ /dev/null
@@ -1,79 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/config.h"
6#include "sway/ipc-server.h"
7#include "log.h"
8
9static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, const char *hidden_state) {
10 char *old_state = bar->hidden_state;
11 if (strcasecmp("toggle", hidden_state) == 0 && !config->reading) {
12 if (strcasecmp("hide", bar->hidden_state) == 0) {
13 bar->hidden_state = strdup("show");
14 } else if (strcasecmp("show", bar->hidden_state) == 0) {
15 bar->hidden_state = strdup("hide");
16 }
17 } else if (strcasecmp("hide", hidden_state) == 0) {
18 bar->hidden_state = strdup("hide");
19 } else if (strcasecmp("show", hidden_state) == 0) {
20 bar->hidden_state = strdup("show");
21 } else {
22 return cmd_results_new(CMD_INVALID, "hidden_state", "Invalid value %s", hidden_state);
23 }
24
25 if (strcmp(old_state, bar->hidden_state) != 0) {
26 if (!config->reading) {
27 ipc_event_barconfig_update(bar);
28 }
29 sway_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", bar->hidden_state, bar->id);
30 }
31
32 // free old mode
33 free(old_state);
34 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
35}
36
37struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
38 struct cmd_results *error = NULL;
39 if ((error = checkarg(argc, "hidden_state", EXPECTED_AT_LEAST, 1))) {
40 return error;
41 }
42 if ((error = checkarg(argc, "hidden_state", EXPECTED_LESS_THAN, 3))) {
43 return error;
44 }
45
46 if (config->reading && argc > 1) {
47 return cmd_results_new(CMD_INVALID, "hidden_state", "Unexpected value %s in config mode", argv[1]);
48 }
49
50 const char *state = argv[0];
51
52 if (config->reading) {
53 return bar_set_hidden_state(config->current_bar, state);
54 }
55
56 const char *id = NULL;
57 if (argc == 2) {
58 id = argv[1];
59 }
60
61 int i;
62 struct bar_config *bar;
63 for (i = 0; i < config->bars->length; ++i) {
64 bar = config->bars->items[i];
65 if (id && strcmp(id, bar->id) == 0) {
66 return bar_set_hidden_state(bar, state);
67 }
68
69 error = bar_set_hidden_state(bar, state);
70 if (error) {
71 return error;
72 }
73 }
74
75 // active bar modifiers might have changed.
76 update_active_bar_modifiers();
77
78 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
79}
diff --git a/sway/old/commands/bar/icon_theme.c b/sway/old/commands/bar/icon_theme.c
deleted file mode 100644
index cbfc0be5..00000000
--- a/sway/old/commands/bar/icon_theme.c
+++ /dev/null
@@ -1,25 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4
5struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) {
6 const char *cmd_name = "tray_output";
7#ifndef ENABLE_TRAY
8 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
9 "%s called, but sway was compiled without tray support",
10 cmd_name, cmd_name);
11#else
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
19 }
20
21 config->current_bar->icon_theme = strdup(argv[0]);
22
23 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
24#endif
25}
diff --git a/sway/old/commands/bar/id.c b/sway/old/commands/bar/id.c
deleted file mode 100644
index 1221ebf6..00000000
--- a/sway/old/commands/bar/id.c
+++ /dev/null
@@ -1,33 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_id(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "id", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 const char *name = argv[0];
13 const char *oldname = config->current_bar->id;
14
15 // check if id is used by a previously defined bar
16 int i;
17 for (i = 0; i < config->bars->length; ++i) {
18 struct bar_config *find = config->bars->items[i];
19 if (strcmp(name, find->id) == 0 && config->current_bar != find) {
20 return cmd_results_new(CMD_FAILURE, "id",
21 "Id '%s' already defined for another bar. Id unchanged (%s).",
22 name, oldname);
23 }
24 }
25
26 sway_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name);
27
28 // free old bar id
29 free(config->current_bar->id);
30
31 config->current_bar->id = strdup(name);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33}
diff --git a/sway/old/commands/bar/mode.c b/sway/old/commands/bar/mode.c
deleted file mode 100644
index 36816b93..00000000
--- a/sway/old/commands/bar/mode.c
+++ /dev/null
@@ -1,81 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/config.h"
6#include "sway/ipc-server.h"
7#include "log.h"
8
9static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode) {
10 char *old_mode = bar->mode;
11 if (strcasecmp("toggle", mode) == 0 && !config->reading) {
12 if (strcasecmp("dock", bar->mode) == 0) {
13 bar->mode = strdup("hide");
14 } else if (strcasecmp("hide", bar->mode) == 0) {
15 bar->mode = strdup("dock");
16 }
17 } else if (strcasecmp("dock", mode) == 0) {
18 bar->mode = strdup("dock");
19 } else if (strcasecmp("hide", mode) == 0) {
20 bar->mode = strdup("hide");
21 } else if (strcasecmp("invisible", mode) == 0) {
22 bar->mode = strdup("invisible");
23 } else {
24 return cmd_results_new(CMD_INVALID, "mode", "Invalid value %s", mode);
25 }
26
27 if (strcmp(old_mode, bar->mode) != 0) {
28 if (!config->reading) {
29 ipc_event_barconfig_update(bar);
30
31 // active bar modifiers might have changed.
32 update_active_bar_modifiers();
33 }
34 sway_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id);
35 }
36
37 // free old mode
38 free(old_mode);
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40}
41
42struct cmd_results *bar_cmd_mode(int argc, char **argv) {
43 struct cmd_results *error = NULL;
44 if ((error = checkarg(argc, "mode", EXPECTED_AT_LEAST, 1))) {
45 return error;
46 }
47 if ((error = checkarg(argc, "mode", EXPECTED_LESS_THAN, 3))) {
48 return error;
49 }
50
51 if (config->reading && argc > 1) {
52 return cmd_results_new(CMD_INVALID, "mode", "Unexpected value %s in config mode", argv[1]);
53 }
54
55 const char *mode = argv[0];
56
57 if (config->reading) {
58 return bar_set_mode(config->current_bar, mode);
59 }
60
61 const char *id = NULL;
62 if (argc == 2) {
63 id = argv[1];
64 }
65
66 int i;
67 struct bar_config *bar;
68 for (i = 0; i < config->bars->length; ++i) {
69 bar = config->bars->items[i];
70 if (id && strcmp(id, bar->id) == 0) {
71 return bar_set_mode(bar, mode);
72 }
73
74 error = bar_set_mode(bar, mode);
75 if (error) {
76 return error;
77 }
78 }
79
80 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
81}
diff --git a/sway/old/commands/bar/modifier.c b/sway/old/commands/bar/modifier.c
deleted file mode 100644
index 153d87e6..00000000
--- a/sway/old/commands/bar/modifier.c
+++ /dev/null
@@ -1,35 +0,0 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "log.h"
4#include "stringop.h"
5#include "util.h"
6
7struct cmd_results *bar_cmd_modifier(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "modifier", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12
13 if (!config->current_bar) {
14 return cmd_results_new(CMD_FAILURE, "modifier", "No bar defined.");
15 }
16
17 uint32_t mod = 0;
18
19 list_t *split = split_string(argv[0], "+");
20 for (int i = 0; i < split->length; ++i) {
21 uint32_t tmp_mod;
22 if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) {
23 mod |= tmp_mod;
24 continue;
25 } else {
26 free_flat_list(split);
27 return cmd_results_new(CMD_INVALID, "modifier", "Unknown modifier '%s'", split->items[i]);
28 }
29 }
30 free_flat_list(split);
31
32 config->current_bar->modifier = mod;
33 sway_log(L_DEBUG, "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);
34 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
35}
diff --git a/sway/old/commands/bar/output.c b/sway/old/commands/bar/output.c
deleted file mode 100644
index a5710bc0..00000000
--- a/sway/old/commands/bar/output.c
+++ /dev/null
@@ -1,50 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "list.h"
5#include "log.h"
6
7struct cmd_results *bar_cmd_output(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12
13 if (!config->current_bar) {
14 return cmd_results_new(CMD_FAILURE, "output", "No bar defined.");
15 }
16
17 const char *output = argv[0];
18 list_t *outputs = config->current_bar->outputs;
19 if (!outputs) {
20 outputs = create_list();
21 config->current_bar->outputs = outputs;
22 }
23
24 int i;
25 int add_output = 1;
26 if (strcmp("*", output) == 0) {
27 // remove all previous defined outputs and replace with '*'
28 for (i = 0; i < outputs->length; ++i) {
29 free(outputs->items[i]);
30 list_del(outputs, i);
31 }
32 } else {
33 // only add output if not already defined with either the same
34 // name or as '*'
35 for (i = 0; i < outputs->length; ++i) {
36 const char *find = outputs->items[i];
37 if (strcmp("*", find) == 0 || strcmp(output, find) == 0) {
38 add_output = 0;
39 break;
40 }
41 }
42 }
43
44 if (add_output) {
45 list_add(outputs, strdup(output));
46 sway_log(L_DEBUG, "Adding bar: '%s' to output '%s'", config->current_bar->id, output);
47 }
48
49 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
50}
diff --git a/sway/old/commands/bar/pango_markup.c b/sway/old/commands/bar/pango_markup.c
deleted file mode 100644
index f69e882f..00000000
--- a/sway/old/commands/bar/pango_markup.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined.");
14 }
15
16 if (strcasecmp("enabled", argv[0]) == 0) {
17 config->current_bar->pango_markup = true;
18 sway_log(L_DEBUG, "Enabling pango markup for bar: %s", config->current_bar->id);
19 } else if (strcasecmp("disabled", argv[0]) == 0) {
20 config->current_bar->pango_markup = false;
21 sway_log(L_DEBUG, "Disabling pango markup for bar: %s", config->current_bar->id);
22 } else {
23 error = cmd_results_new(CMD_INVALID, "pango_markup", "Invalid value %s", argv[0]);
24 return error;
25 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
diff --git a/sway/old/commands/bar/position.c b/sway/old/commands/bar/position.c
deleted file mode 100644
index 50de58e2..00000000
--- a/sway/old/commands/bar/position.c
+++ /dev/null
@@ -1,33 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_position(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "position", "No bar defined.");
14 }
15
16 if (strcasecmp("top", argv[0]) == 0) {
17 config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_TOP;
18 } else if (strcasecmp("bottom", argv[0]) == 0) {
19 config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
20 } else if (strcasecmp("left", argv[0]) == 0) {
21 sway_log(L_INFO, "Warning: swaybar currently only supports top and bottom positioning. YMMV");
22 config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_LEFT;
23 } else if (strcasecmp("right", argv[0]) == 0) {
24 sway_log(L_INFO, "Warning: swaybar currently only supports top and bottom positioning. YMMV");
25 config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_RIGHT;
26 } else {
27 error = cmd_results_new(CMD_INVALID, "position", "Invalid value %s", argv[0]);
28 return error;
29 }
30
31 sway_log(L_DEBUG, "Setting bar position '%s' for bar: %s", argv[0], config->current_bar->id);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33}
diff --git a/sway/old/commands/bar/secondary_button.c b/sway/old/commands/bar/secondary_button.c
deleted file mode 100644
index 745045c5..00000000
--- a/sway/old/commands/bar/secondary_button.c
+++ /dev/null
@@ -1,26 +0,0 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) {
6 const char *cmd_name = "secondary_button";
7#ifndef ENABLE_TRAY
8 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
9 "%s called, but sway was compiled without tray support",
10 cmd_name, cmd_name);
11#else
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
19 }
20
21 // User should be able to prefix with 0x or whatever they want
22 config->current_bar->secondary_button = strtoul(argv[0], NULL, 0);
23
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25#endif
26}
diff --git a/sway/old/commands/bar/separator_symbol.c b/sway/old/commands/bar/separator_symbol.c
deleted file mode 100644
index 2766d8a2..00000000
--- a/sway/old/commands/bar/separator_symbol.c
+++ /dev/null
@@ -1,21 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "separator_symbol", "No bar defined.");
14 }
15
16 free(config->current_bar->separator_symbol);
17 config->current_bar->separator_symbol = strdup(argv[0]);
18 sway_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", config->current_bar->separator_symbol, config->current_bar->id);
19
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/old/commands/bar/status_command.c b/sway/old/commands/bar/status_command.c
deleted file mode 100644
index b227ac47..00000000
--- a/sway/old/commands/bar/status_command.c
+++ /dev/null
@@ -1,21 +0,0 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "log.h"
4#include "stringop.h"
5
6struct cmd_results *bar_cmd_status_command(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "status_command", "No bar defined.");
14 }
15
16 free(config->current_bar->status_command);
17 config->current_bar->status_command = join_args(argv, argc);
18 sway_log(L_DEBUG, "Feeding bar with status command: %s", config->current_bar->status_command);
19
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/old/commands/bar/strip_workspace_numbers.c b/sway/old/commands/bar/strip_workspace_numbers.c
deleted file mode 100644
index 9ac32482..00000000
--- a/sway/old/commands/bar/strip_workspace_numbers.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "strip_workspace_numbers", "No bar defined.");
14 }
15
16 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->strip_workspace_numbers = true;
18 sway_log(L_DEBUG, "Stripping workspace numbers on bar: %s", config->current_bar->id);
19 } else if (strcasecmp("no", argv[0]) == 0) {
20 config->current_bar->strip_workspace_numbers = false;
21 sway_log(L_DEBUG, "Enabling workspace numbers on bar: %s", config->current_bar->id);
22 } else {
23 error = cmd_results_new(CMD_INVALID, "strip_workspace_numbers", "Invalid value %s", argv[0]);
24 return error;
25 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
diff --git a/sway/old/commands/bar/swaybar_command.c b/sway/old/commands/bar/swaybar_command.c
deleted file mode 100644
index 452e2df5..00000000
--- a/sway/old/commands/bar/swaybar_command.c
+++ /dev/null
@@ -1,21 +0,0 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "log.h"
4#include "stringop.h"
5
6struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "swaybar_command", "No bar defined.");
14 }
15
16 free(config->current_bar->swaybar_command);
17 config->current_bar->swaybar_command = join_args(argv, argc);
18 sway_log(L_DEBUG, "Using custom swaybar command: %s", config->current_bar->swaybar_command);
19
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/old/commands/bar/tray_output.c b/sway/old/commands/bar/tray_output.c
deleted file mode 100644
index 012304a9..00000000
--- a/sway/old/commands/bar/tray_output.c
+++ /dev/null
@@ -1,29 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4
5struct cmd_results *bar_cmd_tray_output(int argc, char **argv) {
6 const char *cmd_name = "tray_output";
7#ifndef ENABLE_TRAY
8 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
9 "%s called, but sway was compiled without tray support",
10 cmd_name, cmd_name);
11#else
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
19 }
20
21 if (strcmp(argv[0], "all") == 0) {
22 // Default behaviour
23 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
24 }
25 config->current_bar->tray_output = strdup(argv[0]);
26
27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
28#endif
29}
diff --git a/sway/old/commands/bar/tray_padding.c b/sway/old/commands/bar/tray_padding.c
deleted file mode 100644
index ac0572ce..00000000
--- a/sway/old/commands/bar/tray_padding.c
+++ /dev/null
@@ -1,34 +0,0 @@
1#include <stdlib.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) {
7 const char *cmd_name = "tray_padding";
8#ifndef ENABLE_TRAY
9 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command"
10 "%s called, but sway was compiled without tray support",
11 cmd_name, cmd_name);
12#else
13 struct cmd_results *error = NULL;
14 if ((error = checkarg(argc, cmd_name, EXPECTED_AT_LEAST, 1))) {
15 return error;
16 }
17
18 if (!config->current_bar) {
19 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
20 }
21
22 if (argc == 1 || (argc == 2 && strcasecmp("px", argv[1]) == 0)) {
23 char *inv;
24 uint32_t padding = strtoul(argv[0], &inv, 10);
25 if (*inv == '\0' || strcasecmp(inv, "px") == 0) {
26 config->current_bar->tray_padding = padding;
27 sway_log(L_DEBUG, "Enabling tray padding of %d px on bar: %s", padding, config->current_bar->id);
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29 }
30 }
31 return cmd_results_new(CMD_FAILURE, cmd_name,
32 "Expected 'tray_padding <padding>[px]'");
33#endif
34}
diff --git a/sway/old/commands/bar/workspace_buttons.c b/sway/old/commands/bar/workspace_buttons.c
deleted file mode 100644
index 67dd2d31..00000000
--- a/sway/old/commands/bar/workspace_buttons.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "workspace_buttons", "No bar defined.");
14 }
15
16 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->workspace_buttons = true;
18 sway_log(L_DEBUG, "Enabling workspace buttons on bar: %s", config->current_bar->id);
19 } else if (strcasecmp("no", argv[0]) == 0) {
20 config->current_bar->workspace_buttons = false;
21 sway_log(L_DEBUG, "Disabling workspace buttons on bar: %s", config->current_bar->id);
22 } else {
23 error = cmd_results_new(CMD_INVALID, "workspace_buttons", "Invalid value %s", argv[0]);
24 return error;
25 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
diff --git a/sway/old/commands/bar/wrap_scroll.c b/sway/old/commands/bar/wrap_scroll.c
deleted file mode 100644
index 4ed1f12a..00000000
--- a/sway/old/commands/bar/wrap_scroll.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined.");
14 }
15
16 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->wrap_scroll = true;
18 sway_log(L_DEBUG, "Enabling wrap scroll on bar: %s", config->current_bar->id);
19 } else if (strcasecmp("no", argv[0]) == 0) {
20 config->current_bar->wrap_scroll = false;
21 sway_log(L_DEBUG, "Disabling wrap scroll on bar: %s", config->current_bar->id);
22 } else {
23 error = cmd_results_new(CMD_INVALID, "wrap_scroll", "Invalid value %s", argv[0]);
24 return error;
25 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
diff --git a/sway/old/commands/bind.c b/sway/old/commands/bind.c
deleted file mode 100644
index d9ea37b7..00000000
--- a/sway/old/commands/bind.c
+++ /dev/null
@@ -1,170 +0,0 @@
1#include <xkbcommon/xkbcommon.h>
2#include <xkbcommon/xkbcommon-names.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/config.h"
6#include "sway/input_state.h"
7#include "list.h"
8#include "log.h"
9#include "stringop.h"
10#include "util.h"
11
12int binding_order = 0;
13
14struct cmd_results *cmd_bindsym(int argc, char **argv) {
15 struct cmd_results *error = NULL;
16 if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
17 return error;
18 }
19
20 struct sway_binding *binding = malloc(sizeof(struct sway_binding));
21 if (!binding) {
22 return cmd_results_new(CMD_FAILURE, "bindsym",
23 "Unable to allocate binding");
24 }
25 binding->keys = create_list();
26 binding->modifiers = 0;
27 binding->release = false;
28 binding->bindcode = false;
29
30 // Handle --release
31 if (strcmp("--release", argv[0]) == 0) {
32 if (argc >= 3) {
33 binding->release = true;
34 argv++;
35 argc--;
36 } else {
37 free_sway_binding(binding);
38 return cmd_results_new(CMD_FAILURE, "bindsym",
39 "Invalid bindsym command "
40 "(expected more than 2 arguments, got %d)", argc);
41 }
42 }
43
44 binding->command = join_args(argv + 1, argc - 1);
45
46 list_t *split = split_string(argv[0], "+");
47 for (int i = 0; i < split->length; ++i) {
48 // Check for a modifier key
49 uint32_t mod;
50 if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) {
51 binding->modifiers |= mod;
52 continue;
53 }
54 // Check for xkb key
55 xkb_keysym_t sym = xkb_keysym_from_name(split->items[i],
56 XKB_KEYSYM_CASE_INSENSITIVE);
57
58 // Check for mouse binding
59 if (strncasecmp(split->items[i], "button", strlen("button")) == 0 &&
60 strlen(split->items[i]) == strlen("button0")) {
61 sym = ((char *)split->items[i])[strlen("button")] - '1' + M_LEFT_CLICK;
62 }
63 if (!sym) {
64 struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym",
65 "Unknown key '%s'", (char *)split->items[i]);
66 free_sway_binding(binding);
67 free_flat_list(split);
68 return ret;
69 }
70 xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
71 if (!key) {
72 free_sway_binding(binding);
73 free_flat_list(split);
74 return cmd_results_new(CMD_FAILURE, "bindsym",
75 "Unable to allocate binding");
76 }
77 *key = sym;
78 list_add(binding->keys, key);
79 }
80 free_flat_list(split);
81
82 struct sway_mode *mode = config->current_mode;
83 int i = list_seq_find(mode->bindings, sway_binding_cmp_keys, binding);
84 if (i > -1) {
85 sway_log(L_DEBUG, "bindsym - '%s' already exists, overwriting", argv[0]);
86 struct sway_binding *dup = mode->bindings->items[i];
87 free_sway_binding(dup);
88 list_del(mode->bindings, i);
89 }
90 binding->order = binding_order++;
91 list_add(mode->bindings, binding);
92 list_qsort(mode->bindings, sway_binding_cmp_qsort);
93
94 sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command);
95 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
96}
97
98struct cmd_results *cmd_bindcode(int argc, char **argv) {
99 struct cmd_results *error = NULL;
100 if ((error = checkarg(argc, "bindcode", EXPECTED_MORE_THAN, 1))) {
101 return error;
102 }
103
104 struct sway_binding *binding = malloc(sizeof(struct sway_binding));
105 if (!binding) {
106 return cmd_results_new(CMD_FAILURE, "bindsym",
107 "Unable to allocate binding");
108 }
109 binding->keys = create_list();
110 binding->modifiers = 0;
111 binding->release = false;
112 binding->bindcode = true;
113
114 // Handle --release
115 if (strcmp("--release", argv[0]) == 0) {
116 if (argc >= 3) {
117 binding->release = true;
118 argv++;
119 argc--;
120 } else {
121 free_sway_binding(binding);
122 return cmd_results_new(CMD_FAILURE, "bindcode",
123 "Invalid bindcode command "
124 "(expected more than 2 arguments, got %d)", argc);
125 }
126 }
127
128 binding->command = join_args(argv + 1, argc - 1);
129
130 list_t *split = split_string(argv[0], "+");
131 for (int i = 0; i < split->length; ++i) {
132 // Check for a modifier key
133 uint32_t mod;
134 if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) {
135 binding->modifiers |= mod;
136 continue;
137 }
138 // parse keycode
139 xkb_keycode_t keycode = (int)strtol(split->items[i], NULL, 10);
140 if (!xkb_keycode_is_legal_ext(keycode)) {
141 error = cmd_results_new(CMD_INVALID, "bindcode", "Invalid keycode '%s'", (char *)split->items[i]);
142 free_sway_binding(binding);
143 list_free(split);
144 return error;
145 }
146 xkb_keycode_t *key = malloc(sizeof(xkb_keycode_t));
147 *key = keycode - 8;
148 list_add(binding->keys, key);
149 }
150 free_flat_list(split);
151
152 struct sway_mode *mode = config->current_mode;
153 int i = list_seq_find(mode->bindings, sway_binding_cmp_keys, binding);
154 if (i > -1) {
155 struct sway_binding *dup = mode->bindings->items[i];
156 if (dup->bindcode) {
157 sway_log(L_DEBUG, "bindcode - '%s' already exists, overwriting", argv[0]);
158 } else {
159 sway_log(L_DEBUG, "bindcode - '%s' already exists as bindsym, overwriting", argv[0]);
160 }
161 free_sway_binding(dup);
162 list_del(mode->bindings, i);
163 }
164 binding->order = binding_order++;
165 list_add(mode->bindings, binding);
166 list_qsort(mode->bindings, sway_binding_cmp_qsort);
167
168 sway_log(L_DEBUG, "bindcode - Bound %s to command %s", argv[0], binding->command);
169 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
170}
diff --git a/sway/old/commands/border.c b/sway/old/commands/border.c
deleted file mode 100644
index c888622e..00000000
--- a/sway/old/commands/border.c
+++ /dev/null
@@ -1,65 +0,0 @@
1#include <errno.h>
2#include <stdlib.h>
3#include <string.h>
4#include <strings.h>
5#include "sway/commands.h"
6#include "sway/container.h"
7#include "sway/focus.h"
8
9struct cmd_results *cmd_border(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if (!config->active) {
12 return cmd_results_new(CMD_FAILURE, "border", "Can only be used when sway is running.");
13 }
14 if ((error = checkarg(argc, "border", EXPECTED_AT_LEAST, 1))) {
15 return error;
16 }
17
18 if (argc > 2) {
19 return cmd_results_new(CMD_INVALID, "border",
20 "Expected 'border <normal|pixel|none|toggle> [<n>]");
21 }
22
23 swayc_t *view = current_container;
24 enum swayc_border_types border = view->border_type;
25 int thickness = view->border_thickness;
26
27 if (strcasecmp(argv[0], "none") == 0) {
28 border = B_NONE;
29 } else if (strcasecmp(argv[0], "normal") == 0) {
30 border = B_NORMAL;
31 } else if (strcasecmp(argv[0], "pixel") == 0) {
32 border = B_PIXEL;
33 } else if (strcasecmp(argv[0], "toggle") == 0) {
34 switch (border) {
35 case B_NONE:
36 border = B_PIXEL;
37 break;
38 case B_NORMAL:
39 border = B_NONE;
40 break;
41 case B_PIXEL:
42 border = B_NORMAL;
43 break;
44 }
45 } else {
46 return cmd_results_new(CMD_INVALID, "border",
47 "Expected 'border <normal|pixel|none|toggle>");
48 }
49
50 if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
51 thickness = (int)strtol(argv[1], NULL, 10);
52 if (errno == ERANGE || thickness < 0) {
53 errno = 0;
54 return cmd_results_new(CMD_INVALID, "border", "Number is out of range.");
55 }
56 }
57
58 if (view) {
59 view->border_type = border;
60 view->border_thickness = thickness;
61 update_geometry(view);
62 }
63
64 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
65}
diff --git a/sway/old/commands/client.c b/sway/old/commands/client.c
deleted file mode 100644
index f3d879cd..00000000
--- a/sway/old/commands/client.c
+++ /dev/null
@@ -1,72 +0,0 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4
5static struct cmd_results *parse_border_color(struct border_colors *border_colors, const char *cmd_name, int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if (argc < 3 || argc > 5) {
8 return cmd_results_new(CMD_INVALID, cmd_name, "Requires between three and five color values");
9 }
10
11 uint32_t *colors[5] = {
12 &border_colors->border,
13 &border_colors->background,
14 &border_colors->text,
15 &border_colors->indicator,
16 &border_colors->child_border
17 };
18 int i;
19 for (i = 0; i < argc; i++) {
20 char buffer[10];
21 error = add_color(cmd_name, buffer, argv[i]);
22 if (error) {
23 return error;
24 }
25 *colors[i] = strtoul(buffer + 1, NULL, 16);
26 }
27
28 if (argc < 5) {
29 border_colors->child_border = border_colors->background;
30 }
31
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33}
34
35struct cmd_results *cmd_client_focused(int argc, char **argv) {
36 return parse_border_color(&config->border_colors.focused, "client.focused", argc, argv);
37}
38
39struct cmd_results *cmd_client_focused_inactive(int argc, char **argv) {
40 return parse_border_color(&config->border_colors.focused_inactive, "client.focused_inactive", argc, argv);
41}
42
43struct cmd_results *cmd_client_unfocused(int argc, char **argv) {
44 return parse_border_color(&config->border_colors.unfocused, "client.unfocused", argc, argv);
45}
46
47struct cmd_results *cmd_client_urgent(int argc, char **argv) {
48 return parse_border_color(&config->border_colors.urgent, "client.urgent", argc, argv);
49}
50
51struct cmd_results *cmd_client_placeholder(int argc, char **argv) {
52 return parse_border_color(&config->border_colors.placeholder, "client.placeholder", argc, argv);
53}
54
55struct cmd_results *cmd_client_background(int argc, char **argv) {
56 char buffer[10];
57 struct cmd_results *error = NULL;
58 uint32_t background;
59
60 if (argc != 1) {
61 return cmd_results_new(CMD_INVALID, "client.background", "Requires exactly one color value");
62 }
63
64 error = add_color("client.background", buffer, argv[0]);
65 if (error) {
66 return error;
67 }
68
69 background = strtoul(buffer+1, NULL, 16);
70 config->border_colors.background = background;
71 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
72}
diff --git a/sway/old/commands/clipboard.c b/sway/old/commands/clipboard.c
deleted file mode 100644
index 95514e78..00000000
--- a/sway/old/commands/clipboard.c
+++ /dev/null
@@ -1,38 +0,0 @@
1#include <wlc/wlc.h>
2#include <unistd.h>
3#include <string.h>
4#include "sway/commands.h"
5#include "stringop.h"
6
7static void send_clipboard(void *data, const char *type, int fd) {
8 if (strcmp(type, "text/plain") != 0
9 && strcmp(type, "text/plain;charset=utf-8") != 0) {
10 close(fd);
11 return;
12 }
13
14 const char *str = data;
15 write(fd, str, strlen(str));
16 close(fd);
17}
18
19struct cmd_results *cmd_clipboard(int argc, char **argv) {
20 static char *current_data = NULL;
21
22 struct cmd_results *error = NULL;
23 if ((error = checkarg(argc, "clipboard", EXPECTED_AT_LEAST, 1))) {
24 return error;
25 }
26
27 static const char *types[2] = {
28 "text/plain",
29 "text/plain;charset=utf-8"
30 };
31
32 char *str = join_args(argv, argc);
33 wlc_set_selection(str, types, 2, &send_clipboard);
34
35 free(current_data);
36 current_data = str;
37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
38}
diff --git a/sway/old/commands/commands.c b/sway/old/commands/commands.c
deleted file mode 100644
index 0c64970c..00000000
--- a/sway/old/commands/commands.c
+++ /dev/null
@@ -1,26 +0,0 @@
1#include <stdbool.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5#include "list.h"
6#include "log.h"
7
8struct cmd_results *cmd_commands(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "commands", EXPECTED_EQUAL_TO, 1))) {
11 return error;
12 }
13 if ((error = check_security_config())) {
14 return error;
15 }
16
17 if (strcmp(argv[0], "{") != 0) {
18 return cmd_results_new(CMD_FAILURE, "commands", "Expected block declaration");
19 }
20
21 if (!config->reading) {
22 return cmd_results_new(CMD_FAILURE, "commands", "Can only be used in config file.");
23 }
24
25 return cmd_results_new(CMD_BLOCK_COMMANDS, NULL, NULL);
26}
diff --git a/sway/old/commands/debuglog.c b/sway/old/commands/debuglog.c
deleted file mode 100644
index 658d6165..00000000
--- a/sway/old/commands/debuglog.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *cmd_debuglog(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "debuglog", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 } else if (strcasecmp(argv[0], "toggle") == 0) {
11 if (config->reading) {
12 return cmd_results_new(CMD_FAILURE, "debuglog toggle", "Can't be used in config file.");
13 }
14 if (toggle_debug_logging()) {
15 sway_log(L_DEBUG, "Debuglog turned on.");
16 }
17 } else if (strcasecmp(argv[0], "on") == 0) {
18 set_log_level(L_DEBUG);
19 sway_log(L_DEBUG, "Debuglog turned on.");
20 } else if (strcasecmp(argv[0], "off") == 0) {
21 reset_log_level();
22 } else {
23 return cmd_results_new(CMD_FAILURE, "debuglog", "Expected 'debuglog on|off|toggle'");
24 }
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
27
diff --git a/sway/old/commands/default_border.c b/sway/old/commands/default_border.c
deleted file mode 100644
index 8fbe8d19..00000000
--- a/sway/old/commands/default_border.c
+++ /dev/null
@@ -1,44 +0,0 @@
1#include <errno.h>
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/container.h"
6
7struct cmd_results *cmd_default_border(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "default_border", EXPECTED_AT_LEAST, 1))) {
10 return error;
11 }
12
13 if (argc > 2) {
14 return cmd_results_new(CMD_INVALID, "default_border",
15 "Expected 'default_border <normal|none|pixel> [<n>]");
16 }
17
18 enum swayc_border_types border = config->border;
19 int thickness = config->border_thickness;
20
21 if (strcasecmp(argv[0], "none") == 0) {
22 border = B_NONE;
23 } else if (strcasecmp(argv[0], "normal") == 0) {
24 border = B_NORMAL;
25 } else if (strcasecmp(argv[0], "pixel") == 0) {
26 border = B_PIXEL;
27 } else {
28 return cmd_results_new(CMD_INVALID, "default_border",
29 "Expected 'default_border <normal|none|pixel> [<n>]");
30 }
31
32 if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
33 thickness = (int)strtol(argv[1], NULL, 10);
34 if (errno == ERANGE || thickness < 0) {
35 errno = 0;
36 return cmd_results_new(CMD_INVALID, "default_border", "Number is out out of range.");
37 }
38 }
39
40 config->border = border;
41 config->border_thickness = thickness;
42
43 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
44}
diff --git a/sway/old/commands/default_floating_border.c b/sway/old/commands/default_floating_border.c
deleted file mode 100644
index fb48c1c0..00000000
--- a/sway/old/commands/default_floating_border.c
+++ /dev/null
@@ -1,45 +0,0 @@
1#include <errno.h>
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/container.h"
6
7struct cmd_results *cmd_default_floating_border(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "default_floating_border", EXPECTED_AT_LEAST, 1))) {
10 return error;
11 }
12
13 if (argc > 2) {
14 return cmd_results_new(CMD_INVALID, "default_floating_border",
15 "Expected 'default_floating_border <normal|none|pixel> [<n>]");
16 }
17
18 enum swayc_border_types border = config->floating_border;
19 int thickness = config->floating_border_thickness;
20
21 if (strcasecmp(argv[0], "none") == 0) {
22 border = B_NONE;
23 } else if (strcasecmp(argv[0], "normal") == 0) {
24 border = B_NORMAL;
25 } else if (strcasecmp(argv[0], "pixel") == 0) {
26 border = B_PIXEL;
27 } else {
28 return cmd_results_new(CMD_INVALID, "default_floating_border",
29 "Expected 'default_floating_border <normal|none|pixel> [<n>]");
30 }
31
32 if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
33 thickness = (int)strtol(argv[1], NULL, 10);
34 if (errno == ERANGE || thickness < 0) {
35 errno = 0;
36 return cmd_results_new(CMD_INVALID, "default_floating_border",
37 "Number is out out of range.");
38 }
39 }
40
41 config->floating_border = border;
42 config->floating_border_thickness = thickness;
43
44 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
45}
diff --git a/sway/old/commands/exec.c b/sway/old/commands/exec.c
deleted file mode 100644
index 58ef5f94..00000000
--- a/sway/old/commands/exec.c
+++ /dev/null
@@ -1,16 +0,0 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "log.h"
4#include "stringop.h"
5
6struct cmd_results *cmd_exec(int argc, char **argv) {
7 if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL);
8 if (config->reloading) {
9 char *args = join_args(argv, argc);
10 sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args);
11 free(args);
12 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
13 }
14 return cmd_exec_always(argc, argv);
15}
16
diff --git a/sway/old/commands/exec_always.c b/sway/old/commands/exec_always.c
deleted file mode 100644
index ab2d8622..00000000
--- a/sway/old/commands/exec_always.c
+++ /dev/null
@@ -1,85 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include <sys/wait.h>
4#include <unistd.h>
5#include "sway/commands.h"
6#include "sway/config.h"
7#include "log.h"
8#include "stringop.h"
9
10struct cmd_results *cmd_exec_always(int argc, char **argv) {
11 struct cmd_results *error = NULL;
12 if (!config->active) return cmd_results_new(CMD_DEFER, NULL, NULL);
13 if ((error = checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0))) {
14 return error;
15 }
16
17 char *tmp = NULL;
18 if (strcmp((char*)*argv, "--no-startup-id") == 0) {
19 sway_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored.");
20 if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) {
21 return error;
22 }
23
24 tmp = join_args(argv + 1, argc - 1);
25 } else {
26 tmp = join_args(argv, argc);
27 }
28
29 // Put argument into cmd array
30 char cmd[4096];
31 strncpy(cmd, tmp, sizeof(cmd));
32 cmd[sizeof(cmd) - 1] = 0;
33 free(tmp);
34 sway_log(L_DEBUG, "Executing %s", cmd);
35
36 int fd[2];
37 if (pipe(fd) != 0) {
38 sway_log(L_ERROR, "Unable to create pipe for fork");
39 }
40
41 pid_t pid;
42 pid_t *child = malloc(sizeof(pid_t)); // malloc'd so that Linux can avoid copying the process space
43 if (!child) {
44 return cmd_results_new(CMD_FAILURE, "exec_always", "Unable to allocate child pid");
45 }
46 // Fork process
47 if ((pid = fork()) == 0) {
48 // Fork child process again
49 setsid();
50 if ((*child = fork()) == 0) {
51 execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL);
52 /* Not reached */
53 }
54 close(fd[0]);
55 ssize_t s = 0;
56 while ((size_t)s < sizeof(pid_t)) {
57 s += write(fd[1], ((uint8_t *)child) + s, sizeof(pid_t) - s);
58 }
59 close(fd[1]);
60 _exit(0); // Close child process
61 } else if (pid < 0) {
62 free(child);
63 return cmd_results_new(CMD_FAILURE, "exec_always", "fork() failed");
64 }
65 close(fd[1]); // close write
66 ssize_t s = 0;
67 while ((size_t)s < sizeof(pid_t)) {
68 s += read(fd[0], ((uint8_t *)child) + s, sizeof(pid_t) - s);
69 }
70 close(fd[0]);
71 // cleanup child process
72 wait(0);
73 swayc_t *ws = swayc_active_workspace();
74 if (*child > 0 && ws) {
75 sway_log(L_DEBUG, "Child process created with pid %d for workspace %s", *child, ws->name);
76 struct pid_workspace *pw = malloc(sizeof(struct pid_workspace));
77 pw->pid = child;
78 pw->workspace = strdup(ws->name);
79 pid_workspace_add(pw);
80 } else {
81 free(child);
82 }
83
84 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
85}
diff --git a/sway/old/commands/exit.c b/sway/old/commands/exit.c
deleted file mode 100644
index f192f86a..00000000
--- a/sway/old/commands/exit.c
+++ /dev/null
@@ -1,17 +0,0 @@
1#include "sway/commands.h"
2#include "sway/container.h"
3
4void sway_terminate(int exit_code);
5
6struct cmd_results *cmd_exit(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if (config->reading) return cmd_results_new(CMD_FAILURE, "exit", "Can't be used in config file.");
9 if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) {
10 return error;
11 }
12 // Close all views
13 close_views(&root_container);
14 sway_terminate(EXIT_SUCCESS);
15 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
16}
17
diff --git a/sway/old/commands/floating.c b/sway/old/commands/floating.c
deleted file mode 100644
index ccfde532..00000000
--- a/sway/old/commands/floating.c
+++ /dev/null
@@ -1,81 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/container.h"
5#include "sway/ipc-server.h"
6#include "sway/layout.h"
7#include "list.h"
8#include "log.h"
9
10struct cmd_results *cmd_floating(int argc, char **argv) {
11 struct cmd_results *error = NULL;
12 if (config->reading) return cmd_results_new(CMD_FAILURE, "floating", "Can't be used in config file.");
13 if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16 swayc_t *view = current_container;
17 bool wants_floating;
18 if (strcasecmp(argv[0], "enable") == 0) {
19 wants_floating = true;
20 } else if (strcasecmp(argv[0], "disable") == 0) {
21 wants_floating = false;
22 } else if (strcasecmp(argv[0], "toggle") == 0) {
23 wants_floating = !view->is_floating;
24 } else {
25 return cmd_results_new(CMD_FAILURE, "floating",
26 "Expected 'floating <enable|disable|toggle>");
27 }
28
29 // Prevent running floating commands on things like workspaces
30 if (view->type != C_VIEW) {
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
32 }
33
34 // Change from nonfloating to floating
35 if (!view->is_floating && wants_floating) {
36 // Remove view from its current location
37 destroy_container(remove_child(view));
38
39 // and move it into workspace floating
40 add_floating(swayc_active_workspace(), view);
41 view->x = (swayc_active_workspace()->width - view->width)/2;
42 view->y = (swayc_active_workspace()->height - view->height)/2;
43 if (view->desired_width != -1) {
44 view->width = view->desired_width;
45 }
46 if (view->desired_height != -1) {
47 view->height = view->desired_height;
48 }
49 arrange_windows(swayc_active_workspace(), -1, -1);
50
51 } else if (view->is_floating && !wants_floating) {
52 // Delete the view from the floating list and unset its is_floating flag
53 remove_child(view);
54 view->is_floating = false;
55 // Get the properly focused container, and add in the view there
56 swayc_t *focused = container_under_pointer();
57 // If focused is null, it's because the currently focused container is a workspace
58 if (focused == NULL || focused->is_floating) {
59 focused = swayc_active_workspace();
60 }
61 set_focused_container(focused);
62
63 sway_log(L_DEBUG, "Non-floating focused container is %p", focused);
64
65 // Case of focused workspace, just create as child of it
66 if (focused->type == C_WORKSPACE) {
67 add_child(focused, view);
68 }
69 // Regular case, create as sibling of current container
70 else {
71 add_sibling(focused, view);
72 }
73 // Refocus on the view once its been put back into the layout
74 view->width = view->height = 0;
75 arrange_windows(swayc_active_workspace(), -1, -1);
76 remove_view_from_scratchpad(view);
77 ipc_event_window(view, "floating");
78 }
79 set_focused_container(view);
80 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
81}
diff --git a/sway/old/commands/floating_maximum_size.c b/sway/old/commands/floating_maximum_size.c
deleted file mode 100644
index 5bca4d7c..00000000
--- a/sway/old/commands/floating_maximum_size.c
+++ /dev/null
@@ -1,38 +0,0 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *cmd_floating_maximum_size(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 int32_t width;
9 int32_t height;
10 char *ptr;
11
12 if ((error = checkarg(argc, "floating_maximum_size", EXPECTED_EQUAL_TO, 3))) {
13 return error;
14 }
15 width = strtol(argv[0], &ptr, 10);
16 height = strtol(argv[2], &ptr, 10);
17
18 if (width < -1) {
19 sway_log(L_DEBUG, "floating_maximum_size invalid width value: '%s'", argv[0]);
20
21 } else {
22 config->floating_maximum_width = width;
23
24 }
25
26 if (height < -1) {
27 sway_log(L_DEBUG, "floating_maximum_size invalid height value: '%s'", argv[2]);
28 }
29 else {
30 config->floating_maximum_height = height;
31
32 }
33
34 sway_log(L_DEBUG, "New floating_maximum_size: '%d' x '%d'", config->floating_maximum_width,
35 config->floating_maximum_height);
36
37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
38}
diff --git a/sway/old/commands/floating_minimum_size.c b/sway/old/commands/floating_minimum_size.c
deleted file mode 100644
index ba72454c..00000000
--- a/sway/old/commands/floating_minimum_size.c
+++ /dev/null
@@ -1,38 +0,0 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *cmd_floating_minimum_size(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 int32_t width;
9 int32_t height;
10 char *ptr;
11
12 if ((error = checkarg(argc, "floating_minimum_size", EXPECTED_EQUAL_TO, 3))) {
13 return error;
14 }
15 width = strtol(argv[0], &ptr, 10);
16 height = strtol(argv[2], &ptr, 10);
17
18 if (width <= 0) {
19 sway_log(L_DEBUG, "floating_minimum_size invalid width value: '%s'", argv[0]);
20
21 } else {
22 config->floating_minimum_width = width;
23
24 }
25
26 if (height <= 0) {
27 sway_log(L_DEBUG, "floating_minimum_size invalid height value: '%s'", argv[2]);
28 }
29 else {
30 config->floating_minimum_height = height;
31
32 }
33
34 sway_log(L_DEBUG, "New floating_minimum_size: '%d' x '%d'", config->floating_minimum_width,
35 config->floating_minimum_height);
36
37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
38}
diff --git a/sway/old/commands/floating_mod.c b/sway/old/commands/floating_mod.c
deleted file mode 100644
index b8e81ab9..00000000
--- a/sway/old/commands/floating_mod.c
+++ /dev/null
@@ -1,42 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input_state.h"
5#include "list.h"
6#include "log.h"
7#include "stringop.h"
8#include "util.h"
9
10struct cmd_results *cmd_floating_mod(int argc, char **argv) {
11 struct cmd_results *error = NULL;
12 if ((error = checkarg(argc, "floating_modifier", EXPECTED_AT_LEAST, 1))) {
13 return error;
14 }
15 int i;
16 list_t *split = split_string(argv[0], "+");
17 config->floating_mod = 0;
18
19 // set modifier keys
20 for (i = 0; i < split->length; ++i) {
21 config->floating_mod |= get_modifier_mask_by_name(split->items[i]);
22 }
23 free_flat_list(split);
24 if (!config->floating_mod) {
25 error = cmd_results_new(CMD_INVALID, "floating_modifier", "Unknown keys %s", argv[0]);
26 return error;
27 }
28
29 if (argc >= 2) {
30 if (strcasecmp("inverse", argv[1]) == 0) {
31 config->dragging_key = M_RIGHT_CLICK;
32 config->resizing_key = M_LEFT_CLICK;
33 } else if (strcasecmp("normal", argv[1]) == 0) {
34 config->dragging_key = M_LEFT_CLICK;
35 config->resizing_key = M_RIGHT_CLICK;
36 } else {
37 error = cmd_results_new(CMD_INVALID, "floating_modifier", "Invalid definition %s", argv[1]);
38 return error;
39 }
40 }
41 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
42}
diff --git a/sway/old/commands/floating_scroll.c b/sway/old/commands/floating_scroll.c
deleted file mode 100644
index 8c50c5bd..00000000
--- a/sway/old/commands/floating_scroll.c
+++ /dev/null
@@ -1,46 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "log.h"
6#include "stringop.h"
7
8struct cmd_results *cmd_floating_scroll(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "floating_scroll", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13 if (!strcasecmp("up", argv[0])) {
14 free(config->floating_scroll_up_cmd);
15 if (argc < 2) {
16 config->floating_scroll_up_cmd = strdup("");
17 } else {
18 config->floating_scroll_up_cmd = join_args(argv + 1, argc - 1);
19 }
20 } else if (!strcasecmp("down", argv[0])) {
21 free(config->floating_scroll_down_cmd);
22 if (argc < 2) {
23 config->floating_scroll_down_cmd = strdup("");
24 } else {
25 config->floating_scroll_down_cmd = join_args(argv + 1, argc - 1);
26 }
27 } else if (!strcasecmp("left", argv[0])) {
28 free(config->floating_scroll_left_cmd);
29 if (argc < 2) {
30 config->floating_scroll_left_cmd = strdup("");
31 } else {
32 config->floating_scroll_left_cmd = join_args(argv + 1, argc - 1);
33 }
34 } else if (!strcasecmp("right", argv[0])) {
35 free(config->floating_scroll_right_cmd);
36 if (argc < 2) {
37 config->floating_scroll_right_cmd = strdup("");
38 } else {
39 config->floating_scroll_right_cmd = join_args(argv + 1, argc - 1);
40 }
41 } else {
42 error = cmd_results_new(CMD_INVALID, "floating_scroll", "Unknown command: '%s'", argv[0]);
43 return error;
44 }
45 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
46}
diff --git a/sway/old/commands/focus.c b/sway/old/commands/focus.c
deleted file mode 100644
index c83157b8..00000000
--- a/sway/old/commands/focus.c
+++ /dev/null
@@ -1,100 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h>
3#include <strings.h>
4#include <wlc/wlc.h>
5#include "sway/commands.h"
6#include "sway/container.h"
7#include "sway/focus.h"
8#include "sway/input_state.h"
9#include "sway/output.h"
10#include "sway/workspace.h"
11
12struct cmd_results *cmd_focus(int argc, char **argv) {
13 if (config->reading) return cmd_results_new(CMD_FAILURE, "focus", "Can't be used in config file.");
14 struct cmd_results *error = NULL;
15 if (argc > 0 && strcasecmp(argv[0], "output") == 0) {
16 swayc_t *output = NULL;
17 struct wlc_point abs_pos;
18 get_absolute_center_position(get_focused_container(&root_container), &abs_pos);
19 if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 2))) {
20 return error;
21 } else if (!(output = output_by_name(argv[1], &abs_pos))) {
22 return cmd_results_new(CMD_FAILURE, "focus output",
23 "Can't find output with name/at direction '%s' @ (%i,%i)", argv[1], abs_pos.x, abs_pos.y);
24 } else if (!workspace_switch(swayc_active_workspace_for(output))) {
25 return cmd_results_new(CMD_FAILURE, "focus output",
26 "Switching to workspace on output '%s' was blocked", argv[1]);
27 } else if (config->mouse_warping) {
28 swayc_t *focused = get_focused_view(output);
29 if (focused && focused->type == C_VIEW) {
30 center_pointer_on(focused);
31 }
32 }
33 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
34 } else if (argc == 0) {
35 set_focused_container(current_container);
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
37 } else if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) {
38 return error;
39 }
40 static int floating_toggled_index = 0;
41 static int tiled_toggled_index = 0;
42 if (strcasecmp(argv[0], "left") == 0) {
43 move_focus(MOVE_LEFT);
44 } else if (strcasecmp(argv[0], "right") == 0) {
45 move_focus(MOVE_RIGHT);
46 } else if (strcasecmp(argv[0], "up") == 0) {
47 move_focus(MOVE_UP);
48 } else if (strcasecmp(argv[0], "down") == 0) {
49 move_focus(MOVE_DOWN);
50 } else if (strcasecmp(argv[0], "parent") == 0) {
51 move_focus(MOVE_PARENT);
52 } else if (strcasecmp(argv[0], "child") == 0) {
53 move_focus(MOVE_CHILD);
54 } else if (strcasecmp(argv[0], "next") == 0) {
55 move_focus(MOVE_NEXT);
56 } else if (strcasecmp(argv[0], "prev") == 0) {
57 move_focus(MOVE_PREV);
58 } else if (strcasecmp(argv[0], "mode_toggle") == 0) {
59 int i;
60 swayc_t *workspace = swayc_active_workspace();
61 swayc_t *focused = get_focused_view(workspace);
62 if (focused->is_floating) {
63 if (workspace->children->length > 0) {
64 for (i = 0;i < workspace->floating->length; i++) {
65 if (workspace->floating->items[i] == focused) {
66 floating_toggled_index = i;
67 break;
68 }
69 }
70 if (workspace->children->length > tiled_toggled_index) {
71 set_focused_container(get_focused_view(workspace->children->items[tiled_toggled_index]));
72 } else {
73 set_focused_container(get_focused_view(workspace->children->items[0]));
74 tiled_toggled_index = 0;
75 }
76 }
77 } else {
78 if (workspace->floating->length > 0) {
79 for (i = 0;i < workspace->children->length; i++) {
80 if (workspace->children->items[i] == focused) {
81 tiled_toggled_index = i;
82 break;
83 }
84 }
85 if (workspace->floating->length > floating_toggled_index) {
86 swayc_t *floating = workspace->floating->items[floating_toggled_index];
87 set_focused_container(get_focused_view(floating));
88 } else {
89 swayc_t *floating = workspace->floating->items[workspace->floating->length - 1];
90 set_focused_container(get_focused_view(floating));
91 tiled_toggled_index = workspace->floating->length - 1;
92 }
93 }
94 }
95 } else {
96 return cmd_results_new(CMD_INVALID, "focus",
97 "Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'");
98 }
99 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
100}
diff --git a/sway/old/commands/focus_follows_mouse.c b/sway/old/commands/focus_follows_mouse.c
deleted file mode 100644
index 7c9c2b13..00000000
--- a/sway/old/commands/focus_follows_mouse.c
+++ /dev/null
@@ -1,13 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10
11 config->focus_follows_mouse = !strcasecmp(argv[0], "yes");
12 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
13}
diff --git a/sway/old/commands/font.c b/sway/old/commands/font.c
deleted file mode 100644
index 32994f8a..00000000
--- a/sway/old/commands/font.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/border.h"
4#include "sway/commands.h"
5#include "log.h"
6#include "stringop.h"
7
8struct cmd_results *cmd_font(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13
14 char *font = join_args(argv, argc);
15 free(config->font);
16 if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) {
17 config->font = strdup(font + 6);
18 free(font);
19 } else {
20 config->font = font;
21 }
22
23 config->font_height = get_font_text_height(config->font);
24
25 sway_log(L_DEBUG, "Settings font %s", config->font);
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
diff --git a/sway/old/commands/for_window.c b/sway/old/commands/for_window.c
deleted file mode 100644
index d1fd1641..00000000
--- a/sway/old/commands/for_window.c
+++ /dev/null
@@ -1,41 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/criteria.h"
5#include "list.h"
6#include "log.h"
7#include "stringop.h"
8
9struct cmd_results *cmd_for_window(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "for_window", EXPECTED_AT_LEAST, 2))) {
12 return error;
13 }
14 // add command to a criteria/command pair that is run against views when they appear.
15 char *criteria = argv[0], *cmdlist = join_args(argv + 1, argc - 1);
16
17 struct criteria *crit = malloc(sizeof(struct criteria));
18 if (!crit) {
19 return cmd_results_new(CMD_FAILURE, "for_window", "Unable to allocate criteria");
20 }
21 crit->crit_raw = strdup(criteria);
22 crit->cmdlist = cmdlist;
23 crit->tokens = create_list();
24 char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
25
26 if (err_str) {
27 error = cmd_results_new(CMD_INVALID, "for_window", err_str);
28 free(err_str);
29 free_criteria(crit);
30 } else if (crit->tokens->length == 0) {
31 error = cmd_results_new(CMD_INVALID, "for_window", "Found no name/value pairs in criteria");
32 free_criteria(crit);
33 } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) {
34 sway_log(L_DEBUG, "for_window: Duplicate, skipping.");
35 free_criteria(crit);
36 } else {
37 sway_log(L_DEBUG, "for_window: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist);
38 list_add(config->criteria, crit);
39 }
40 return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
41}
diff --git a/sway/old/commands/force_focus_wrapping.c b/sway/old/commands/force_focus_wrapping.c
deleted file mode 100644
index f19dd163..00000000
--- a/sway/old/commands/force_focus_wrapping.c
+++ /dev/null
@@ -1,13 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "force_focus_wrapping", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10
11 config->force_focus_wrapping = !strcasecmp(argv[0], "yes");
12 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
13}
diff --git a/sway/old/commands/fullscreen.c b/sway/old/commands/fullscreen.c
deleted file mode 100644
index bfff82f9..00000000
--- a/sway/old/commands/fullscreen.c
+++ /dev/null
@@ -1,60 +0,0 @@
1#include <stdbool.h>
2#include <string.h>
3#include <wlc/wlc.h>
4#include "sway/commands.h"
5#include "sway/container.h"
6#include "sway/focus.h"
7#include "sway/ipc-server.h"
8#include "sway/layout.h"
9
10struct cmd_results *cmd_fullscreen(int argc, char **argv) {
11 struct cmd_results *error = NULL;
12 if (config->reading) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can't be used in config file.");
13 if (!config->active) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can only be used when sway is running.");
14 if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) {
15 return error;
16 }
17 swayc_t *container = current_container;
18 if(container->type != C_VIEW){
19 return cmd_results_new(CMD_INVALID, "fullscreen", "Only views can fullscreen");
20 }
21 swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE);
22 bool current = swayc_is_fullscreen(container);
23 wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current);
24
25 if (container->is_floating) {
26 if (current) {
27 // set dimensions back to what they were before we fullscreened this
28 container->x = container->cached_geometry.origin.x;
29 container->y = container->cached_geometry.origin.y;
30 container->width = container->cached_geometry.size.w;
31 container->height = container->cached_geometry.size.h;
32 } else {
33 // cache dimensions so we can reset them after we "unfullscreen" this
34 struct wlc_geometry geo = {
35 .origin = {
36 .x = container->x,
37 .y = container->y
38 },
39 .size = {
40 .w = container->width,
41 .h = container->height
42 }
43 };
44 container->cached_geometry = geo;
45 }
46 }
47
48 // Resize workspace if going from fullscreen -> notfullscreen
49 // otherwise just resize container
50 if (!current) {
51 arrange_windows(workspace, -1, -1);
52 workspace->fullscreen = container;
53 } else {
54 arrange_windows(container, -1, -1);
55 workspace->fullscreen = NULL;
56 }
57 ipc_event_window(container, "fullscreen_mode");
58
59 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
60}
diff --git a/sway/old/commands/gaps.c b/sway/old/commands/gaps.c
deleted file mode 100644
index 0a48592d..00000000
--- a/sway/old/commands/gaps.c
+++ /dev/null
@@ -1,166 +0,0 @@
1#include <ctype.h>
2#include <errno.h>
3#include <stdlib.h>
4#include <string.h>
5#include <strings.h>
6#include "sway/commands.h"
7#include "sway/container.h"
8#include "sway/focus.h"
9#include "sway/layout.h"
10
11struct cmd_results *cmd_gaps(int argc, char **argv) {
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1))) {
14 return error;
15 }
16 const char *expected_syntax =
17 "Expected 'gaps edge_gaps <on|off|toggle>' or "
18 "'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'";
19 const char *amount_str = argv[0];
20 // gaps amount
21 if (argc >= 1 && isdigit(*amount_str)) {
22 int amount = (int)strtol(amount_str, NULL, 10);
23 if (errno == ERANGE) {
24 errno = 0;
25 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
26 }
27 config->gaps_inner = config->gaps_outer = amount;
28 arrange_windows(&root_container, -1, -1);
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30 }
31 // gaps inner|outer n
32 else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) {
33 int amount = (int)strtol(amount_str, NULL, 10);
34 if (errno == ERANGE) {
35 errno = 0;
36 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
37 }
38 const char *target_str = argv[0];
39 if (strcasecmp(target_str, "inner") == 0) {
40 config->gaps_inner = amount;
41 } else if (strcasecmp(target_str, "outer") == 0) {
42 config->gaps_outer = amount;
43 }
44 arrange_windows(&root_container, -1, -1);
45 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
46 } else if (argc == 2 && strcasecmp(argv[0], "edge_gaps") == 0) {
47 // gaps edge_gaps <on|off|toggle>
48 if (strcasecmp(argv[1], "toggle") == 0) {
49 if (config->reading) {
50 return cmd_results_new(CMD_FAILURE, "gaps edge_gaps toggle",
51 "Can't be used in config file.");
52 }
53 config->edge_gaps = !config->edge_gaps;
54 } else {
55 config->edge_gaps =
56 (strcasecmp(argv[1], "yes") == 0 || strcasecmp(argv[1], "on") == 0);
57 }
58 arrange_windows(&root_container, -1, -1);
59 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
60 }
61 // gaps inner|outer current|all set|plus|minus n
62 if (argc < 4 || config->reading) {
63 return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
64 }
65 // gaps inner|outer ...
66 const char *inout_str = argv[0];
67 enum {INNER, OUTER} inout;
68 if (strcasecmp(inout_str, "inner") == 0) {
69 inout = INNER;
70 } else if (strcasecmp(inout_str, "outer") == 0) {
71 inout = OUTER;
72 } else {
73 return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
74 }
75
76 // gaps ... current|all ...
77 const char *target_str = argv[1];
78 enum {CURRENT, WORKSPACE, ALL} target;
79 if (strcasecmp(target_str, "current") == 0) {
80 target = CURRENT;
81 } else if (strcasecmp(target_str, "all") == 0) {
82 target = ALL;
83 } else if (strcasecmp(target_str, "workspace") == 0) {
84 if (inout == OUTER) {
85 target = CURRENT;
86 } else {
87 // Set gap for views in workspace
88 target = WORKSPACE;
89 }
90 } else {
91 return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
92 }
93
94 // gaps ... n
95 amount_str = argv[3];
96 int amount = (int)strtol(amount_str, NULL, 10);
97 if (errno == ERANGE) {
98 errno = 0;
99 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
100 }
101
102 // gaps ... set|plus|minus ...
103 const char *method_str = argv[2];
104 enum {SET, ADD} method;
105 if (strcasecmp(method_str, "set") == 0) {
106 method = SET;
107 } else if (strcasecmp(method_str, "plus") == 0) {
108 method = ADD;
109 } else if (strcasecmp(method_str, "minus") == 0) {
110 method = ADD;
111 amount *= -1;
112 } else {
113 return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
114 }
115
116 if (target == CURRENT) {
117 swayc_t *cont;
118 if (inout == OUTER) {
119 if ((cont = swayc_active_workspace()) == NULL) {
120 return cmd_results_new(CMD_FAILURE, "gaps", "There's no active workspace.");
121 }
122 } else {
123 if ((cont = get_focused_view(&root_container))->type != C_VIEW) {
124 return cmd_results_new(CMD_FAILURE, "gaps", "Currently focused item is not a view.");
125 }
126 }
127 cont->gaps = swayc_gap(cont);
128 if (method == SET) {
129 cont->gaps = amount;
130 } else if ((cont->gaps += amount) < 0) {
131 cont->gaps = 0;
132 }
133 arrange_windows(cont->parent, -1, -1);
134 } else if (inout == OUTER) {
135 //resize all workspace.
136 int i,j;
137 for (i = 0; i < root_container.children->length; ++i) {
138 swayc_t *op = root_container.children->items[i];
139 for (j = 0; j < op->children->length; ++j) {
140 swayc_t *ws = op->children->items[j];
141 if (method == SET) {
142 ws->gaps = amount;
143 } else if ((ws->gaps += amount) < 0) {
144 ws->gaps = 0;
145 }
146 }
147 }
148 arrange_windows(&root_container, -1, -1);
149 } else {
150 // Resize gaps for all views in workspace
151 swayc_t *top;
152 if (target == WORKSPACE) {
153 if ((top = swayc_active_workspace()) == NULL) {
154 return cmd_results_new(CMD_FAILURE, "gaps", "There's currently no active workspace.");
155 }
156 } else {
157 top = &root_container;
158 }
159 int top_gap = top->gaps;
160 container_map(top, method == SET ? set_gaps : add_gaps, &amount);
161 top->gaps = top_gap;
162 arrange_windows(top, -1, -1);
163 }
164
165 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
166}
diff --git a/sway/old/commands/hide_edge_borders.c b/sway/old/commands/hide_edge_borders.c
deleted file mode 100644
index ee2a2644..00000000
--- a/sway/old/commands/hide_edge_borders.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10
11 if (strcasecmp(argv[0], "none") == 0) {
12 config->hide_edge_borders = E_NONE;
13 } else if (strcasecmp(argv[0], "vertical") == 0) {
14 config->hide_edge_borders = E_VERTICAL;
15 } else if (strcasecmp(argv[0], "horizontal") == 0) {
16 config->hide_edge_borders = E_HORIZONTAL;
17 } else if (strcasecmp(argv[0], "both") == 0) {
18 config->hide_edge_borders = E_BOTH;
19 } else if (strcasecmp(argv[0], "smart") == 0) {
20 config->hide_edge_borders = E_SMART;
21 } else {
22 return cmd_results_new(CMD_INVALID, "hide_edge_borders",
23 "Expected 'hide_edge_borders <none|vertical|horizontal|both>'");
24 }
25
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
diff --git a/sway/old/commands/include.c b/sway/old/commands/include.c
deleted file mode 100644
index 1ba9a10d..00000000
--- a/sway/old/commands/include.c
+++ /dev/null
@@ -1,15 +0,0 @@
1#include "sway/commands.h"
2#include "sway/config.h"
3
4struct cmd_results *cmd_include(int argc, char **argv) {
5 struct cmd_results *error = NULL;
6 if ((error = checkarg(argc, "include", EXPECTED_EQUAL_TO, 1))) {
7 return error;
8 }
9
10 if (!load_include_configs(argv[0], config)) {
11 return cmd_results_new(CMD_INVALID, "include", "Failed to include sub configuration file: %s", argv[0]);
12 }
13
14 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
15}
diff --git a/sway/old/commands/input.c b/sway/old/commands/input.c
deleted file mode 100644
index ad53d272..00000000
--- a/sway/old/commands/input.c
+++ /dev/null
@@ -1,55 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5#include "log.h"
6
7struct cmd_results *cmd_input(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) {
10 return error;
11 }
12
13 if (config->reading && strcmp("{", argv[1]) == 0) {
14 current_input_config = new_input_config(argv[0]);
15 sway_log(L_DEBUG, "entering input block: %s", current_input_config->identifier);
16 return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
17 }
18
19 if (argc > 2) {
20 int argc_new = argc-2;
21 char **argv_new = argv+2;
22
23 struct cmd_results *res;
24 current_input_config = new_input_config(argv[0]);
25 if (strcasecmp("accel_profile", argv[1]) == 0) {
26 res = input_cmd_accel_profile(argc_new, argv_new);
27 } else if (strcasecmp("click_method", argv[1]) == 0) {
28 res = input_cmd_click_method(argc_new, argv_new);
29 } else if (strcasecmp("drag_lock", argv[1]) == 0) {
30 res = input_cmd_drag_lock(argc_new, argv_new);
31 } else if (strcasecmp("dwt", argv[1]) == 0) {
32 res = input_cmd_dwt(argc_new, argv_new);
33 } else if (strcasecmp("events", argv[1]) == 0) {
34 res = input_cmd_events(argc_new, argv_new);
35 } else if (strcasecmp("left_handed", argv[1]) == 0) {
36 res = input_cmd_left_handed(argc_new, argv_new);
37 } else if (strcasecmp("middle_emulation", argv[1]) == 0) {
38 res = input_cmd_middle_emulation(argc_new, argv_new);
39 } else if (strcasecmp("natural_scroll", argv[1]) == 0) {
40 res = input_cmd_natural_scroll(argc_new, argv_new);
41 } else if (strcasecmp("pointer_accel", argv[1]) == 0) {
42 res = input_cmd_pointer_accel(argc_new, argv_new);
43 } else if (strcasecmp("scroll_method", argv[1]) == 0) {
44 res = input_cmd_scroll_method(argc_new, argv_new);
45 } else if (strcasecmp("tap", argv[1]) == 0) {
46 res = input_cmd_tap(argc_new, argv_new);
47 } else {
48 res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
49 }
50 current_input_config = NULL;
51 return res;
52 }
53
54 return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
55}
diff --git a/sway/old/commands/input/accel_profile.c b/sway/old/commands/input/accel_profile.c
deleted file mode 100644
index 8288c1ad..00000000
--- a/sway/old/commands/input/accel_profile.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5
6struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "accel_profile", "No input device defined.");
13 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier);
15
16 if (strcasecmp(argv[0], "adaptive") == 0) {
17 new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
18 } else if (strcasecmp(argv[0], "flat") == 0) {
19 new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
20 } else {
21 return cmd_results_new(CMD_INVALID, "accel_profile",
22 "Expected 'accel_profile <adaptive|flat>'");
23 }
24
25 input_cmd_apply(new_config);
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
diff --git a/sway/old/commands/input/click_method.c b/sway/old/commands/input/click_method.c
deleted file mode 100644
index 5e9d3dcb..00000000
--- a/sway/old/commands/input/click_method.c
+++ /dev/null
@@ -1,30 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5#include "log.h"
6
7struct cmd_results *input_cmd_click_method(int argc, char **argv) {
8 sway_log(L_DEBUG, "click_method for device: %d %s", current_input_config==NULL, current_input_config->identifier);
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13 if (!current_input_config) {
14 return cmd_results_new(CMD_FAILURE, "click_method", "No input device defined.");
15 }
16 struct input_config *new_config = new_input_config(current_input_config->identifier);
17
18 if (strcasecmp(argv[0], "none") == 0) {
19 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
20 } else if (strcasecmp(argv[0], "button_areas") == 0) {
21 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
22 } else if (strcasecmp(argv[0], "clickfinger") == 0) {
23 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
24 } else {
25 return cmd_results_new(CMD_INVALID, "click_method", "Expected 'click_method <none|button_areas|clickfinger'");
26 }
27
28 input_cmd_apply(new_config);
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30}
diff --git a/sway/old/commands/input/drag_lock.c b/sway/old/commands/input/drag_lock.c
deleted file mode 100644
index f5a7beb4..00000000
--- a/sway/old/commands/input/drag_lock.c
+++ /dev/null
@@ -1,26 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5
6struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "drag_lock", "No input device defined.");
13 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier);
15
16 if (strcasecmp(argv[0], "enabled") == 0) {
17 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;
18 } else if (strcasecmp(argv[0], "disabled") == 0) {
19 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
20 } else {
21 return cmd_results_new(CMD_INVALID, "drag_lock", "Expected 'drag_lock <enabled|disabled>'");
22 }
23
24 input_cmd_apply(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/old/commands/input/dwt.c b/sway/old/commands/input/dwt.c
deleted file mode 100644
index 557b2207..00000000
--- a/sway/old/commands/input/dwt.c
+++ /dev/null
@@ -1,26 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5
6struct cmd_results *input_cmd_dwt(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined.");
13 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier);
15
16 if (strcasecmp(argv[0], "enabled") == 0) {
17 new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
18 } else if (strcasecmp(argv[0], "disabled") == 0) {
19 new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
20 } else {
21 return cmd_results_new(CMD_INVALID, "dwt", "Expected 'dwt <enabled|disabled>'");
22 }
23
24 input_cmd_apply(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/old/commands/input/events.c b/sway/old/commands/input/events.c
deleted file mode 100644
index 9d54287a..00000000
--- a/sway/old/commands/input/events.c
+++ /dev/null
@@ -1,30 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5#include "log.h"
6
7struct cmd_results *input_cmd_events(int argc, char **argv) {
8 sway_log(L_DEBUG, "events for device: %s", current_input_config->identifier);
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13 if (!current_input_config) {
14 return cmd_results_new(CMD_FAILURE, "events", "No input device defined.");
15 }
16 struct input_config *new_config = new_input_config(current_input_config->identifier);
17
18 if (strcasecmp(argv[0], "enabled") == 0) {
19 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
20 } else if (strcasecmp(argv[0], "disabled") == 0) {
21 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
22 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) {
23 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
24 } else {
25 return cmd_results_new(CMD_INVALID, "events", "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
26 }
27
28 input_cmd_apply(new_config);
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30}
diff --git a/sway/old/commands/input/left_handed.c b/sway/old/commands/input/left_handed.c
deleted file mode 100644
index 6c913e70..00000000
--- a/sway/old/commands/input/left_handed.c
+++ /dev/null
@@ -1,26 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5
6struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "left_handed", "No input device defined.");
13 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier);
15
16 if (strcasecmp(argv[0], "enabled") == 0) {
17 new_config->left_handed = 1;
18 } else if (strcasecmp(argv[0], "disabled") == 0) {
19 new_config->left_handed = 0;
20 } else {
21 return cmd_results_new(CMD_INVALID, "left_handed", "Expected 'left_handed <enabled|disabled>'");
22 }
23
24 input_cmd_apply(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/old/commands/input/middle_emulation.c b/sway/old/commands/input/middle_emulation.c
deleted file mode 100644
index 33cdd7d6..00000000
--- a/sway/old/commands/input/middle_emulation.c
+++ /dev/null
@@ -1,26 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5
6struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "middle_emulation", "No input device defined.");
13 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier);
15
16 if (strcasecmp(argv[0], "enabled") == 0) {
17 new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
18 } else if (strcasecmp(argv[0], "disabled") == 0) {
19 new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
20 } else {
21 return cmd_results_new(CMD_INVALID, "middle_emulation", "Expected 'middle_emulation <enabled|disabled>'");
22 }
23
24 input_cmd_apply(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/old/commands/input/natural_scroll.c b/sway/old/commands/input/natural_scroll.c
deleted file mode 100644
index 7bc8b8d0..00000000
--- a/sway/old/commands/input/natural_scroll.c
+++ /dev/null
@@ -1,26 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5
6struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "natural_scoll", "No input device defined.");
13 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier);
15
16 if (strcasecmp(argv[0], "enabled") == 0) {
17 new_config->natural_scroll = 1;
18 } else if (strcasecmp(argv[0], "disabled") == 0) {
19 new_config->natural_scroll = 0;
20 } else {
21 return cmd_results_new(CMD_INVALID, "natural_scroll", "Expected 'natural_scroll <enabled|disabled>'");
22 }
23
24 input_cmd_apply(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/old/commands/input/pointer_accel.c b/sway/old/commands/input/pointer_accel.c
deleted file mode 100644
index 94f595d1..00000000
--- a/sway/old/commands/input/pointer_accel.c
+++ /dev/null
@@ -1,24 +0,0 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5
6struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "pointer_accel", "No input device defined.");
13 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier);
15
16 float pointer_accel = atof(argv[0]);
17 if (pointer_accel < -1 || pointer_accel > 1) {
18 return cmd_results_new(CMD_INVALID, "pointer_accel", "Input out of range [-1, 1]");
19 }
20 new_config->pointer_accel = pointer_accel;
21
22 input_cmd_apply(new_config);
23 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
24}
diff --git a/sway/old/commands/input/scroll_method.c b/sway/old/commands/input/scroll_method.c
deleted file mode 100644
index 5c6c3d7a..00000000
--- a/sway/old/commands/input/scroll_method.c
+++ /dev/null
@@ -1,30 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5
6struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "scroll_method", "No input device defined.");
13 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier);
15
16 if (strcasecmp(argv[0], "none") == 0) {
17 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
18 } else if (strcasecmp(argv[0], "two_finger") == 0) {
19 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
20 } else if (strcasecmp(argv[0], "edge") == 0) {
21 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE;
22 } else if (strcasecmp(argv[0], "on_button_down") == 0) {
23 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
24 } else {
25 return cmd_results_new(CMD_INVALID, "scroll_method", "Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
26 }
27
28 input_cmd_apply(new_config);
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30}
diff --git a/sway/old/commands/input/tap.c b/sway/old/commands/input/tap.c
deleted file mode 100644
index 9e3ca2af..00000000
--- a/sway/old/commands/input/tap.c
+++ /dev/null
@@ -1,29 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input.h"
5#include "log.h"
6
7struct cmd_results *input_cmd_tap(int argc, char **argv) {
8 sway_log(L_DEBUG, "tap for device: %s", current_input_config->identifier);
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13 if (!current_input_config) {
14 return cmd_results_new(CMD_FAILURE, "tap", "No input device defined.");
15 }
16 struct input_config *new_config = new_input_config(current_input_config->identifier);
17
18 if (strcasecmp(argv[0], "enabled") == 0) {
19 new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED;
20 } else if (strcasecmp(argv[0], "disabled") == 0) {
21 new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED;
22 } else {
23 return cmd_results_new(CMD_INVALID, "tap", "Expected 'tap <enabled|disabled>'");
24 }
25
26 sway_log(L_DEBUG, "apply-tap for device: %s", current_input_config->identifier);
27 input_cmd_apply(new_config);
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29}
diff --git a/sway/old/commands/ipc.c b/sway/old/commands/ipc.c
deleted file mode 100644
index 0c678961..00000000
--- a/sway/old/commands/ipc.c
+++ /dev/null
@@ -1,162 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdio.h>
3#include <string.h>
4#include "sway/security.h"
5#include "sway/commands.h"
6#include "sway/config.h"
7#include "ipc.h"
8#include "log.h"
9#include "util.h"
10
11static struct ipc_policy *current_policy = NULL;
12
13struct cmd_results *cmd_ipc(int argc, char **argv) {
14 struct cmd_results *error = NULL;
15 if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 2))) {
16 return error;
17 }
18 if ((error = check_security_config())) {
19 return error;
20 }
21
22 char *program = NULL;
23
24 if (!strcmp(argv[0], "*")) {
25 program = strdup(argv[0]);
26 } else if (!(program = resolve_path(argv[0]))) {
27 return cmd_results_new(
28 CMD_INVALID, "ipc", "Unable to resolve IPC Policy target.");
29 }
30 if (config->reading && strcmp("{", argv[1]) != 0) {
31 return cmd_results_new(CMD_INVALID, "ipc",
32 "Expected '{' at start of IPC config definition.");
33 }
34
35 if (!config->reading) {
36 return cmd_results_new(CMD_FAILURE, "ipc", "Can only be used in config file.");
37 }
38
39 current_policy = alloc_ipc_policy(program);
40 list_add(config->ipc_policies, current_policy);
41
42 free(program);
43 return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL);
44}
45
46struct cmd_results *cmd_ipc_events(int argc, char **argv) {
47 struct cmd_results *error = NULL;
48 if ((error = checkarg(argc, "events", EXPECTED_EQUAL_TO, 1))) {
49 return error;
50 }
51
52 if (config->reading && strcmp("{", argv[0]) != 0) {
53 return cmd_results_new(CMD_INVALID, "events",
54 "Expected '{' at start of IPC event config definition.");
55 }
56
57 if (!config->reading) {
58 return cmd_results_new(CMD_FAILURE, "events", "Can only be used in config file.");
59 }
60
61 return cmd_results_new(CMD_BLOCK_IPC_EVENTS, NULL, NULL);
62}
63
64struct cmd_results *cmd_ipc_cmd(int argc, char **argv) {
65 struct cmd_results *error = NULL;
66 if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
67 return error;
68 }
69
70 bool enabled;
71 if (strcmp(argv[0], "enabled") == 0) {
72 enabled = true;
73 } else if (strcmp(argv[0], "disabled") == 0) {
74 enabled = false;
75 } else {
76 return cmd_results_new(CMD_INVALID, argv[-1],
77 "Argument must be one of 'enabled' or 'disabled'");
78 }
79
80 struct {
81 char *name;
82 enum ipc_feature type;
83 } types[] = {
84 { "*", IPC_FEATURE_ALL_COMMANDS },
85 { "command", IPC_FEATURE_COMMAND },
86 { "workspaces", IPC_FEATURE_GET_WORKSPACES },
87 { "outputs", IPC_FEATURE_GET_OUTPUTS },
88 { "tree", IPC_FEATURE_GET_TREE },
89 { "marks", IPC_FEATURE_GET_MARKS },
90 { "bar-config", IPC_FEATURE_GET_BAR_CONFIG },
91 { "inputs", IPC_FEATURE_GET_INPUTS },
92 { "clipboard", IPC_FEATURE_GET_CLIPBOARD },
93 };
94
95 uint32_t type = 0;
96
97 for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
98 if (strcmp(types[i].name, argv[-1]) == 0) {
99 type = types[i].type;
100 break;
101 }
102 }
103
104 if (enabled) {
105 current_policy->features |= type;
106 sway_log(L_DEBUG, "Enabled IPC %s feature", argv[-1]);
107 } else {
108 current_policy->features &= ~type;
109 sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]);
110 }
111
112 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
113}
114
115struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) {
116 struct cmd_results *error = NULL;
117 if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
118 return error;
119 }
120
121 bool enabled;
122 if (strcmp(argv[0], "enabled") == 0) {
123 enabled = true;
124 } else if (strcmp(argv[0], "disabled") == 0) {
125 enabled = false;
126 } else {
127 return cmd_results_new(CMD_INVALID, argv[-1],
128 "Argument must be one of 'enabled' or 'disabled'");
129 }
130
131 struct {
132 char *name;
133 enum ipc_feature type;
134 } types[] = {
135 { "*", IPC_FEATURE_ALL_EVENTS },
136 { "workspace", IPC_FEATURE_EVENT_WORKSPACE },
137 { "output", IPC_FEATURE_EVENT_OUTPUT },
138 { "mode", IPC_FEATURE_EVENT_MODE },
139 { "window", IPC_FEATURE_EVENT_WINDOW },
140 { "binding", IPC_FEATURE_EVENT_BINDING },
141 { "input", IPC_FEATURE_EVENT_INPUT },
142 };
143
144 uint32_t type = 0;
145
146 for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
147 if (strcmp(types[i].name, argv[-1]) == 0) {
148 type = types[i].type;
149 break;
150 }
151 }
152
153 if (enabled) {
154 current_policy->features |= type;
155 sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]);
156 } else {
157 current_policy->features &= ~type;
158 sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]);
159 }
160
161 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
162}
diff --git a/sway/old/commands/kill.c b/sway/old/commands/kill.c
deleted file mode 100644
index 742e2b86..00000000
--- a/sway/old/commands/kill.c
+++ /dev/null
@@ -1,12 +0,0 @@
1#include "sway/commands.h"
2#include "sway/container.h"
3#include "sway/focus.h"
4
5struct cmd_results *cmd_kill(int argc, char **argv) {
6 if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file.");
7 if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running.");
8
9 swayc_t *container = current_container;
10 close_views(container);
11 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
12}
diff --git a/sway/old/commands/layout.c b/sway/old/commands/layout.c
deleted file mode 100644
index 57a86565..00000000
--- a/sway/old/commands/layout.c
+++ /dev/null
@@ -1,196 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/container.h"
5#include "sway/layout.h"
6
7/**
8 * handle "layout auto" command group
9 */
10static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv);
11
12struct cmd_results *cmd_layout(int argc, char **argv) {
13 struct cmd_results *error = NULL;
14 if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file.");
15 if (!config->active) return cmd_results_new(CMD_FAILURE, "layout", "Can only be used when sway is running.");
16 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
17 return error;
18 }
19 swayc_t *parent = current_container;
20 if (parent->is_floating) {
21 return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows");
22 }
23
24 while (parent->type == C_VIEW) {
25 parent = parent->parent;
26 }
27
28 enum swayc_layouts old_layout = parent->layout;
29
30 if (strcasecmp(argv[0], "default") == 0) {
31 swayc_change_layout(parent, parent->prev_layout);
32 if (parent->layout == L_NONE) {
33 swayc_t *output = swayc_parent_by_type(parent, C_OUTPUT);
34 swayc_change_layout(parent, default_layout(output));
35 }
36 } else {
37 if (parent->layout != L_TABBED && parent->layout != L_STACKED) {
38 parent->prev_layout = parent->layout;
39 }
40
41 if (strcasecmp(argv[0], "tabbed") == 0) {
42 if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)){
43 parent = new_container(parent, L_TABBED);
44 }
45
46 swayc_change_layout(parent, L_TABBED);
47 } else if (strcasecmp(argv[0], "stacking") == 0) {
48 if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)) {
49 parent = new_container(parent, L_STACKED);
50 }
51
52 swayc_change_layout(parent, L_STACKED);
53 } else if (strcasecmp(argv[0], "splith") == 0) {
54 swayc_change_layout(parent, L_HORIZ);
55 } else if (strcasecmp(argv[0], "splitv") == 0) {
56 swayc_change_layout(parent, L_VERT);
57 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
58 if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE
59 || parent->workspace_layout == L_HORIZ)) {
60 swayc_change_layout(parent, L_VERT);
61 } else {
62 swayc_change_layout(parent, L_HORIZ);
63 }
64 } else if (strcasecmp(argv[0], "auto") == 0) {
65 return cmd_layout_auto(parent, argc, argv);
66 }
67 }
68
69 update_layout_geometry(parent, old_layout);
70 update_geometry(parent);
71
72 arrange_windows(parent, parent->width, parent->height);
73
74 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
75}
76
77static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv) {
78 // called after checking that argv[0] is auto, so just continue parsing from there
79 struct cmd_results *error = NULL;
80 const char *cmd_name = "layout auto";
81 const char *set_inc_cmd_name = "layout auto [master|ncol] [set|inc]";
82 const char *err_msg = "Allowed arguments are <right|left|top|bottom|next|prev|master|ncol>";
83
84 bool need_layout_update = false;
85 enum swayc_layouts old_layout = container->layout;
86 enum swayc_layouts layout = old_layout;
87
88 if ((error = checkarg(argc, "layout auto", EXPECTED_MORE_THAN, 1))) {
89 return error;
90 }
91
92 if (strcasecmp(argv[1], "left") == 0) {
93 layout = L_AUTO_LEFT;
94 } else if (strcasecmp(argv[1], "right") == 0) {
95 layout = L_AUTO_RIGHT;
96 } else if (strcasecmp(argv[1], "top") == 0) {
97 layout = L_AUTO_TOP;
98 } else if (strcasecmp(argv[1], "bottom") == 0) {
99 layout = L_AUTO_BOTTOM;
100 } else if (strcasecmp(argv[1], "next") == 0) {
101 if (is_auto_layout(container->layout) && container->layout < L_AUTO_LAST) {
102 layout = container->layout + 1;
103 } else {
104 layout = L_AUTO_FIRST;
105 }
106 } else if (strcasecmp(argv[1], "prev") == 0) {
107 if (is_auto_layout(container->layout) && container->layout > L_AUTO_FIRST) {
108 layout = container->layout - 1;
109 } else {
110 layout = L_AUTO_LAST;
111 }
112 } else {
113 bool is_nmaster;
114 bool is_set;
115 if (strcasecmp(argv[1], "master") == 0) {
116 is_nmaster = true;
117 } else if (strcasecmp(argv[1], "ncol") == 0) {
118 is_nmaster = false;
119 } else {
120 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command. %s",
121 cmd_name, err_msg);
122 }
123 if ((error = checkarg(argc, "auto <master|ncol>", EXPECTED_EQUAL_TO, 4))) {
124 return error;
125 }
126 if (strcasecmp(argv[2], "set") == 0) {
127 is_set = true;
128 } else if (strcasecmp(argv[2], "inc") == 0) {
129 is_set = false;
130 } else {
131 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command. %s, "
132 "Argument must be on of <set|inc>",
133 set_inc_cmd_name);
134 }
135 char *end;
136 int n = (int)strtol(argv[3], &end, 10);
137 if (*end) {
138 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
139 "(argument must be an integer)", set_inc_cmd_name);
140 }
141 if (is_auto_layout(container->layout)) {
142 int inc = 0; /* difference between current master/ncol and requested value */
143 if (is_nmaster) {
144 if (is_set) {
145 if (n < 0) {
146 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
147 "(master must be >= 0)", set_inc_cmd_name);
148 }
149 inc = n - (int)container->nb_master;
150 } else { /* inc command */
151 if ((int)container->nb_master + n >= 0) {
152 inc = n;
153 }
154 }
155 if (inc) {
156 for (int i = container->nb_master;
157 i >= 0 && i < container->children->length
158 && i != (int)container->nb_master + inc;) {
159 ((swayc_t *)container->children->items[i])->height = -1;
160 ((swayc_t *)container->children->items[i])->width = -1;
161 i += inc > 0 ? 1 : -1;
162 }
163 container->nb_master += inc;
164 need_layout_update = true;
165 }
166 } else { /* ncol modification */
167 if (is_set) {
168 if (n <= 0) {
169 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
170 "(ncol must be > 0)", set_inc_cmd_name);
171 }
172 inc = n - (int)container->nb_slave_groups;
173 } else { /* inc command */
174 if ((int)container->nb_slave_groups + n > 0) {
175 inc = n;
176 }
177 }
178 if (inc) {
179 container->nb_slave_groups += inc;
180 need_layout_update = true;
181 }
182 }
183 }
184 }
185
186 if (layout != old_layout) {
187 swayc_change_layout(container, layout);
188 update_layout_geometry(container, old_layout);
189 need_layout_update = true;
190 }
191 if (need_layout_update) {
192 update_geometry(container);
193 arrange_windows(container, container->width, container->height);
194 }
195 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
196}
diff --git a/sway/old/commands/log_colors.c b/sway/old/commands/log_colors.c
deleted file mode 100644
index 815d1942..00000000
--- a/sway/old/commands/log_colors.c
+++ /dev/null
@@ -1,22 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *cmd_log_colors(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if (!config->reading) return cmd_results_new(CMD_FAILURE, "log_colors", "Can only be used in config file.");
9 if ((error = checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12 if (strcasecmp(argv[0], "no") == 0) {
13 sway_log_colors(0);
14 } else if (strcasecmp(argv[0], "yes") == 0) {
15 sway_log_colors(1);
16 } else {
17 error = cmd_results_new(CMD_FAILURE, "log_colors",
18 "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]);
19 return error;
20 }
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22}
diff --git a/sway/old/commands/mark.c b/sway/old/commands/mark.c
deleted file mode 100644
index c1d959df..00000000
--- a/sway/old/commands/mark.c
+++ /dev/null
@@ -1,87 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include <stdbool.h>
4#include "sway/commands.h"
5#include "list.h"
6#include "stringop.h"
7
8static void find_marks_callback(swayc_t *container, void *_mark) {
9 char *mark = (char *)_mark;
10
11 int index;
12 if (container->marks && ((index = list_seq_find(container->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1)) {
13 list_del(container->marks, index);
14 }
15}
16
17struct cmd_results *cmd_mark(int argc, char **argv) {
18 struct cmd_results *error = NULL;
19 if (config->reading) return cmd_results_new(CMD_FAILURE, "mark", "Can't be used in config file.");
20 if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) {
21 return error;
22 }
23
24 swayc_t *view = current_container;
25 bool add = false;
26 bool toggle = false;
27
28 if (strcmp(argv[0], "--add") == 0) {
29 --argc; ++argv;
30 add = true;
31 } else if (strcmp(argv[0], "--replace") == 0) {
32 --argc; ++argv;
33 }
34
35 if (argc && strcmp(argv[0], "--toggle") == 0) {
36 --argc; ++argv;
37 toggle = true;
38 }
39
40 if (argc) {
41 char *mark = join_args(argv, argc);
42
43 // Remove all existing marks of this type
44 container_map(&root_container, find_marks_callback, mark);
45
46 if (view->marks) {
47 if (add) {
48 int index;
49 if ((index = list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1) {
50 if (toggle) {
51 free(view->marks->items[index]);
52 list_del(view->marks, index);
53
54 if (0 == view->marks->length) {
55 list_free(view->marks);
56 view->marks = NULL;
57 }
58 }
59 free(mark);
60 } else {
61 list_add(view->marks, mark);
62 }
63 } else {
64 if (toggle && list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark) != -1) {
65 // Delete the list
66 list_foreach(view->marks, free);
67 list_free(view->marks);
68 view->marks = NULL;
69 } else {
70 // Delete and replace with a new list
71 list_foreach(view->marks, free);
72 list_free(view->marks);
73
74 view->marks = create_list();
75 list_add(view->marks, mark);
76 }
77 }
78 } else {
79 view->marks = create_list();
80 list_add(view->marks, mark);
81 }
82 } else {
83 return cmd_results_new(CMD_FAILURE, "mark",
84 "Expected 'mark [--add|--replace] [--toggle] <mark>'");
85 }
86 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
87}
diff --git a/sway/old/commands/mode.c b/sway/old/commands/mode.c
deleted file mode 100644
index d2985c54..00000000
--- a/sway/old/commands/mode.c
+++ /dev/null
@@ -1,57 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdbool.h>
3#include <string.h>
4#include <strings.h>
5#include "sway/commands.h"
6#include "sway/config.h"
7#include "sway/ipc-server.h"
8#include "list.h"
9#include "log.h"
10
11struct cmd_results *cmd_mode(int argc, char **argv) {
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, "mode", EXPECTED_AT_LEAST, 1))) {
14 return error;
15 }
16
17 const char *mode_name = argv[0];
18 bool mode_make = (argc == 2 && strcmp(argv[1], "{") == 0);
19 if (mode_make) {
20 if (!config->reading)
21 return cmd_results_new(CMD_FAILURE, "mode", "Can only be used in config file.");
22 }
23 struct sway_mode *mode = NULL;
24 // Find mode
25 int i, len = config->modes->length;
26 for (i = 0; i < len; ++i) {
27 struct sway_mode *find = config->modes->items[i];
28 if (strcasecmp(find->name, mode_name) == 0) {
29 mode = find;
30 break;
31 }
32 }
33 // Create mode if it doesn't exist
34 if (!mode && mode_make) {
35 mode = malloc(sizeof(struct sway_mode));
36 if (!mode) {
37 return cmd_results_new(CMD_FAILURE, "mode", "Unable to allocate mode");
38 }
39 mode->name = strdup(mode_name);
40 mode->bindings = create_list();
41 list_add(config->modes, mode);
42 }
43 if (!mode) {
44 error = cmd_results_new(CMD_INVALID, "mode", "Unknown mode `%s'", mode_name);
45 return error;
46 }
47 if ((config->reading && mode_make) || (!config->reading && !mode_make)) {
48 sway_log(L_DEBUG, "Switching to mode `%s'",mode->name);
49 }
50 // Set current mode
51 config->current_mode = mode;
52 if (!mode_make) {
53 // trigger IPC mode event
54 ipc_event_mode(config->current_mode->name);
55 }
56 return cmd_results_new(mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL);
57}
diff --git a/sway/old/commands/mouse_warping.c b/sway/old/commands/mouse_warping.c
deleted file mode 100644
index 5596d483..00000000
--- a/sway/old/commands/mouse_warping.c
+++ /dev/null
@@ -1,17 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_mouse_warping(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "mouse_warping", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 } else if (strcasecmp(argv[0], "output") == 0) {
10 config->mouse_warping = true;
11 } else if (strcasecmp(argv[0], "none") == 0) {
12 config->mouse_warping = false;
13 } else {
14 return cmd_results_new(CMD_FAILURE, "mouse_warping", "Expected 'mouse_warping output|none'");
15 }
16 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
17}
diff --git a/sway/old/commands/move.c b/sway/old/commands/move.c
deleted file mode 100644
index 52c73e22..00000000
--- a/sway/old/commands/move.c
+++ /dev/null
@@ -1,189 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h>
3#include <strings.h>
4#include <wlc/wlc.h>
5#include "sway/commands.h"
6#include "sway/container.h"
7#include "sway/layout.h"
8#include "sway/output.h"
9#include "sway/workspace.h"
10#include "list.h"
11#include "stringop.h"
12
13struct cmd_results *cmd_move(int argc, char **argv) {
14 struct cmd_results *error = NULL;
15 int move_amt = 10;
16
17 if (config->reading) return cmd_results_new(CMD_FAILURE, "move", "Can't be used in config file.");
18 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
19 return error;
20 }
21 const char* expected_syntax = "Expected 'move <left|right|up|down|next|prev|first> <[px] px>' or "
22 "'move <container|window> to workspace <name>' or "
23 "'move <container|window|workspace> to output <name|direction>' or "
24 "'move position mouse'";
25 swayc_t *view = current_container;
26
27 if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0 )) {
28 char *inv;
29 move_amt = (int)strtol(argv[1], &inv, 10);
30 if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
31 move_amt = 10;
32 }
33 }
34
35 if (strcasecmp(argv[0], "left") == 0) {
36 move_container(view, MOVE_LEFT, move_amt);
37 } else if (strcasecmp(argv[0], "right") == 0) {
38 move_container(view, MOVE_RIGHT, move_amt);
39 } else if (strcasecmp(argv[0], "up") == 0) {
40 move_container(view, MOVE_UP, move_amt);
41 } else if (strcasecmp(argv[0], "down") == 0) {
42 move_container(view, MOVE_DOWN, move_amt);
43 } else if (strcasecmp(argv[0], "next") == 0) {
44 move_container(view, MOVE_NEXT, move_amt);
45 } else if (strcasecmp(argv[0], "prev") == 0) {
46 move_container(view, MOVE_PREV, move_amt);
47 } else if (strcasecmp(argv[0], "first") == 0) {
48 move_container(view, MOVE_FIRST, move_amt);
49 } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) {
50 // "move container ...
51 if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 4))) {
52 return error;
53 } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "workspace") == 0) {
54 // move container to workspace x
55 if (view->type == C_WORKSPACE) {
56 if (!view->children || view->children->length == 0) {
57 return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace");
58 }
59 view = new_container(view, view->workspace_layout);
60 } if (view->type != C_CONTAINER && view->type != C_VIEW) {
61 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
62 }
63
64 swayc_t *ws;
65 const char *num_name = NULL;
66 char *ws_name = NULL;
67 if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
68 // move "container to workspace number x"
69 num_name = argv[4];
70 ws = workspace_by_number(num_name);
71 } else {
72 ws_name = join_args(argv + 3, argc - 3);
73 ws = workspace_by_name(ws_name);
74 }
75
76 if (ws == NULL) {
77 ws = workspace_create(ws_name ? ws_name : num_name);
78 }
79 if (ws_name) {
80 free(ws_name);
81 }
82 move_container_to(view, get_focused_container(ws));
83 } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) {
84 // move container to output x
85 swayc_t *output = NULL;
86 struct wlc_point abs_pos;
87 get_absolute_center_position(view, &abs_pos);
88 if (view->type == C_WORKSPACE) {
89 if (!view->children || view->children->length == 0) {
90 return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace");
91 }
92 view = new_container(view, view->workspace_layout);
93 } else if (view->type != C_CONTAINER && view->type != C_VIEW) {
94 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
95 } else if (!(output = output_by_name(argv[3], &abs_pos))) {
96 return cmd_results_new(CMD_FAILURE, "move",
97 "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
98 }
99
100 swayc_t *container = get_focused_container(output);
101 if (container->is_floating) {
102 move_container_to(view, container->parent);
103 } else {
104 move_container_to(view, container);
105 }
106 } else {
107 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
108 }
109 } else if (strcasecmp(argv[0], "workspace") == 0) {
110 // move workspace (to output x)
111 swayc_t *output = NULL;
112 struct wlc_point abs_pos;
113 get_absolute_center_position(view, &abs_pos);
114 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
115 return error;
116 } else if (strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "output") != 0) {
117 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
118 } else if (!(output = output_by_name(argv[3], &abs_pos))) {
119 return cmd_results_new(CMD_FAILURE, "move workspace",
120 "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
121 }
122 if (view->type == C_WORKSPACE) {
123 // This probably means we're moving an empty workspace, but
124 // that's fine.
125 move_workspace_to(view, output);
126 } else {
127 swayc_t *workspace = swayc_parent_by_type(view, C_WORKSPACE);
128 move_workspace_to(workspace, output);
129 }
130 } else if (strcasecmp(argv[0], "scratchpad") == 0 || (strcasecmp(argv[0], "to") == 0 && strcasecmp(argv[1], "scratchpad") == 0)) {
131 // move scratchpad ...
132 if (view->type != C_CONTAINER && view->type != C_VIEW) {
133 return cmd_results_new(CMD_FAILURE, "move scratchpad", "Can only move containers and views.");
134 }
135 swayc_t *view = current_container;
136 int i;
137 for (i = 0; i < scratchpad->length; i++) {
138 if (scratchpad->items[i] == view) {
139 hide_view_in_scratchpad(view);
140 sp_view = NULL;
141 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
142 }
143 }
144 list_add(scratchpad, view);
145 if (!view->is_floating) {
146 destroy_container(remove_child(view));
147 } else {
148 remove_child(view);
149 }
150 wlc_view_set_mask(view->handle, 0);
151 arrange_windows(swayc_active_workspace(), -1, -1);
152 swayc_t *focused = container_under_pointer();
153 if (focused == NULL) {
154 focused = swayc_active_workspace();
155 }
156 set_focused_container(focused);
157 } else if (strcasecmp(argv[0], "position") == 0) {
158 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 2))) {
159 return error;
160 }
161 if (strcasecmp(argv[1], "mouse")) {
162 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
163 }
164
165 if (view->is_floating) {
166 swayc_t *output = swayc_parent_by_type(view, C_OUTPUT);
167 struct wlc_geometry g;
168 wlc_view_get_visible_geometry(view->handle, &g);
169 const struct wlc_size *size = wlc_output_get_resolution(output->handle);
170
171 double x_pos, y_pos;
172 wlc_pointer_get_position_v2(&x_pos, &y_pos);
173
174 int32_t x = x_pos - g.size.w / 2;
175 int32_t y = y_pos - g.size.h / 2;
176
177 uint32_t w = size->w - g.size.w;
178 uint32_t h = size->h - g.size.h;
179
180 view->x = g.origin.x = MIN((int32_t)w, MAX(x, 0));
181 view->y = g.origin.y = MIN((int32_t)h, MAX(y, 0));
182
183 wlc_view_set_geometry(view->handle, 0, &g);
184 }
185 } else {
186 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
187 }
188 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
189}
diff --git a/sway/old/commands/new_float.c b/sway/old/commands/new_float.c
deleted file mode 100644
index d0f96093..00000000
--- a/sway/old/commands/new_float.c
+++ /dev/null
@@ -1,8 +0,0 @@
1#include "log.h"
2#include "sway/commands.h"
3
4struct cmd_results *cmd_new_float(int argc, char **argv) {
5 sway_log(L_INFO, "`new_float` is deprecated and will be removed in the future. "
6 "Please use `default_floating_border` instead.");
7 return cmd_default_floating_border(argc, argv);
8}
diff --git a/sway/old/commands/new_window.c b/sway/old/commands/new_window.c
deleted file mode 100644
index 574a4527..00000000
--- a/sway/old/commands/new_window.c
+++ /dev/null
@@ -1,8 +0,0 @@
1#include "log.h"
2#include "sway/commands.h"
3
4struct cmd_results *cmd_new_window(int argc, char **argv) {
5 sway_log(L_INFO, "`new_window` is deprecated and will be removed in the future. "
6 "Please use `default_border` instead.");
7 return cmd_default_border(argc, argv);
8}
diff --git a/sway/old/commands/no_focus.c b/sway/old/commands/no_focus.c
deleted file mode 100644
index b3b88e5a..00000000
--- a/sway/old/commands/no_focus.c
+++ /dev/null
@@ -1,41 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/criteria.h"
5#include "list.h"
6#include "log.h"
7#include "stringop.h"
8
9struct cmd_results *cmd_no_focus(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "no_focus", EXPECTED_EQUAL_TO, 1))) {
12 return error;
13 }
14 // add command to a criteria/command pair that is run against views when they appear.
15 char *criteria = argv[0];
16
17 struct criteria *crit = malloc(sizeof(struct criteria));
18 if (!crit) {
19 return cmd_results_new(CMD_FAILURE, "no_focus", "Unable to allocate criteria");
20 }
21 crit->crit_raw = strdup(criteria);
22 crit->tokens = create_list();
23 crit->cmdlist = NULL;
24 char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
25
26 if (err_str) {
27 error = cmd_results_new(CMD_INVALID, "no_focus", err_str);
28 free(err_str);
29 free_criteria(crit);
30 } else if (crit->tokens->length == 0) {
31 error = cmd_results_new(CMD_INVALID, "no_focus", "Found no name/value pairs in criteria");
32 free_criteria(crit);
33 } else if (list_seq_find(config->no_focus, criteria_cmp, crit) != -1) {
34 sway_log(L_DEBUG, "no_focus: Duplicate, skipping.");
35 free_criteria(crit);
36 } else {
37 sway_log(L_DEBUG, "no_focus: '%s' added", crit->crit_raw);
38 list_add(config->no_focus, crit);
39 }
40 return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
41}
diff --git a/sway/old/commands/orientation.c b/sway/old/commands/orientation.c
deleted file mode 100644
index e54b60ee..00000000
--- a/sway/old/commands/orientation.c
+++ /dev/null
@@ -1,21 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_orientation(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if (!config->reading) return cmd_results_new(CMD_FAILURE, "orientation", "Can only be used in config file.");
8 if ((error = checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 if (strcasecmp(argv[0], "horizontal") == 0) {
12 config->default_orientation = L_HORIZ;
13 } else if (strcasecmp(argv[0], "vertical") == 0) {
14 config->default_orientation = L_VERT;
15 } else if (strcasecmp(argv[0], "auto") == 0) {
16 // Do nothing
17 } else {
18 return cmd_results_new(CMD_INVALID, "orientation", "Expected 'orientation <horizontal|vertical|auto>'");
19 }
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/old/commands/output.c b/sway/old/commands/output.c
deleted file mode 100644
index 911391d2..00000000
--- a/sway/old/commands/output.c
+++ /dev/null
@@ -1,219 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <ctype.h>
3#include <libgen.h>
4#include <stdlib.h>
5#include <string.h>
6#include <strings.h>
7#include <unistd.h>
8#include <wordexp.h>
9#include "sway/commands.h"
10#include "sway/config.h"
11#include "list.h"
12#include "log.h"
13#include "stringop.h"
14
15static char *bg_options[] = {
16 "stretch",
17 "center",
18 "fill",
19 "fit",
20 "tile"
21};
22
23struct cmd_results *cmd_output(int argc, char **argv) {
24 struct cmd_results *error = NULL;
25 if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) {
26 return error;
27 }
28 const char *name = argv[0];
29
30 struct output_config *output = calloc(1, sizeof(struct output_config));
31 if (!output) {
32 return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate output config");
33 }
34 output->x = output->y = output->width = output->height = -1;
35 output->name = strdup(name);
36 output->enabled = -1;
37 output->scale = 1;
38
39 // TODO: atoi doesn't handle invalid numbers
40
41 int i;
42 for (i = 1; i < argc; ++i) {
43 const char *command = argv[i];
44
45 if (strcasecmp(command, "disable") == 0) {
46 output->enabled = 0;
47 } else if (strcasecmp(command, "resolution") == 0 || strcasecmp(command, "res") == 0) {
48 if (++i >= argc) {
49 error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument.");
50 goto fail;
51 }
52 char *res = argv[i];
53 char *x = strchr(res, 'x');
54 int width = -1, height = -1;
55 if (x != NULL) {
56 // Format is 1234x4321
57 *x = '\0';
58 width = atoi(res);
59 height = atoi(x + 1);
60 *x = 'x';
61 } else {
62 // Format is 1234 4321
63 width = atoi(res);
64 if (++i >= argc) {
65 error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height).");
66 goto fail;
67 }
68 res = argv[i];
69 height = atoi(res);
70 }
71 output->width = width;
72 output->height = height;
73 } else if (strcasecmp(command, "position") == 0 || strcasecmp(command, "pos") == 0) {
74 if (++i >= argc) {
75 error = cmd_results_new(CMD_INVALID, "output", "Missing position argument.");
76 goto fail;
77 }
78 char *res = argv[i];
79 char *c = strchr(res, ',');
80 int x = -1, y = -1;
81 if (c != NULL) {
82 // Format is 1234,4321
83 *c = '\0';
84 x = atoi(res);
85 y = atoi(c + 1);
86 *c = ',';
87 } else {
88 // Format is 1234 4321
89 x = atoi(res);
90 if (++i >= argc) {
91 error = cmd_results_new(CMD_INVALID, "output", "Missing position argument (y).");
92 goto fail;
93 }
94 res = argv[i];
95 y = atoi(res);
96 }
97 output->x = x;
98 output->y = y;
99 } else if (strcasecmp(command, "scale") == 0) {
100 if (++i >= argc) {
101 error = cmd_results_new(CMD_INVALID, "output", "Missing scale parameter.");
102 goto fail;
103 }
104 output->scale = atoi(argv[i]);
105 } else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) {
106 wordexp_t p;
107 if (++i >= argc) {
108 error = cmd_results_new(CMD_INVALID, "output", "Missing background file or color specification.");
109 goto fail;
110 }
111 if (i + 1 >= argc) {
112 error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode or `solid_color`.");
113 goto fail;
114 }
115 if (strcasecmp(argv[i + 1], "solid_color") == 0) {
116 output->background = strdup(argv[argc - 2]);
117 output->background_option = strdup("solid_color");
118 } else {
119 // argv[i+j]=bg_option
120 bool valid = false;
121 char *mode;
122 size_t j;
123 for (j = 0; j < (size_t) (argc - i); ++j) {
124 mode = argv[i + j];
125 for (size_t k = 0; k < sizeof(bg_options) / sizeof(char *); ++k) {
126 if (strcasecmp(mode, bg_options[k]) == 0) {
127 valid = true;
128 break;
129 }
130 }
131 if (valid) {
132 break;
133 }
134 }
135 if (!valid) {
136 error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode.");
137 goto fail;
138 }
139
140 char *src = join_args(argv + i, j);
141 if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
142 error = cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src);
143 goto fail;
144 }
145 free(src);
146 src = p.we_wordv[0];
147 if (config->reading && *src != '/') {
148 char *conf = strdup(config->current_config);
149 if (conf) {
150 char *conf_path = dirname(conf);
151 src = malloc(strlen(conf_path) + strlen(src) + 2);
152 if (src) {
153 sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
154 } else {
155 sway_log(L_ERROR, "Unable to allocate background source");
156 }
157 free(conf);
158 } else {
159 sway_log(L_ERROR, "Unable to allocate background source");
160 }
161 }
162 if (!src || access(src, F_OK) == -1) {
163 error = cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src);
164 wordfree(&p);
165 goto fail;
166 }
167
168 output->background = strdup(src);
169 output->background_option = strdup(mode);
170 if (src != p.we_wordv[0]) {
171 free(src);
172 }
173 wordfree(&p);
174
175 i += j;
176 }
177 }
178 }
179
180 i = list_seq_find(config->output_configs, output_name_cmp, name);
181 if (i >= 0) {
182 // merge existing config
183 struct output_config *oc = config->output_configs->items[i];
184 merge_output_config(oc, output);
185 free_output_config(output);
186 output = oc;
187 } else {
188 list_add(config->output_configs, output);
189 }
190
191 sway_log(L_DEBUG, "Config stored for output %s (enabled:%d) (%d x %d @ %d, %d scale %d) (bg %s %s)",
192 output->name, output->enabled, output->width,
193 output->height, output->x, output->y, output->scale,
194 output->background, output->background_option);
195
196 if (output->name) {
197 // Try to find the output container and apply configuration now. If
198 // this is during startup then there will be no container and config
199 // will be applied during normal "new output" event from wlc.
200 swayc_t *cont = NULL;
201 for (int i = 0; i < root_container.children->length; ++i) {
202 cont = root_container.children->items[i];
203 if (cont->name && ((strcmp(cont->name, output->name) == 0) || (strcmp(output->name, "*") == 0))) {
204 apply_output_config(output, cont);
205
206 if (strcmp(output->name, "*") != 0) {
207 // stop looking if the output config isn't applicable to all outputs
208 break;
209 }
210 }
211 }
212 }
213
214 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
215
216fail:
217 free_output_config(output);
218 return error;
219}
diff --git a/sway/old/commands/permit.c b/sway/old/commands/permit.c
deleted file mode 100644
index 7a5e06f7..00000000
--- a/sway/old/commands/permit.c
+++ /dev/null
@@ -1,108 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5#include "sway/security.h"
6#include "util.h"
7#include "log.h"
8
9static enum secure_feature get_features(int argc, char **argv,
10 struct cmd_results **error) {
11 enum secure_feature features = 0;
12
13 struct {
14 char *name;
15 enum secure_feature feature;
16 } feature_names[] = {
17 { "lock", FEATURE_LOCK },
18 { "panel", FEATURE_PANEL },
19 { "background", FEATURE_BACKGROUND },
20 { "screenshot", FEATURE_SCREENSHOT },
21 { "fullscreen", FEATURE_FULLSCREEN },
22 { "keyboard", FEATURE_KEYBOARD },
23 { "mouse", FEATURE_MOUSE },
24 };
25
26 for (int i = 1; i < argc; ++i) {
27 size_t j;
28 for (j = 0; j < sizeof(feature_names) / sizeof(feature_names[0]); ++j) {
29 if (strcmp(feature_names[j].name, argv[i]) == 0) {
30 break;
31 }
32 }
33 if (j == sizeof(feature_names) / sizeof(feature_names[0])) {
34 *error = cmd_results_new(CMD_INVALID,
35 "permit", "Invalid feature grant %s", argv[i]);
36 return 0;
37 }
38 features |= feature_names[j].feature;
39 }
40 return features;
41}
42
43struct cmd_results *cmd_permit(int argc, char **argv) {
44 struct cmd_results *error = NULL;
45 if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) {
46 return error;
47 }
48 if ((error = check_security_config())) {
49 return error;
50 }
51
52 bool assign_perms = true;
53 char *program = NULL;
54
55 if (!strcmp(argv[0], "*")) {
56 program = strdup(argv[0]);
57 } else {
58 program = resolve_path(argv[0]);
59 }
60 if (!program) {
61 sway_assert(program, "Unable to resolve IPC permit target '%s'."
62 " will issue empty policy", argv[0]);
63 assign_perms = false;
64 program = strdup(argv[0]);
65 }
66
67 struct feature_policy *policy = get_feature_policy(program);
68 if (policy && assign_perms) {
69 policy->features |= get_features(argc, argv, &error);
70 sway_log(L_DEBUG, "Permissions granted to %s for features %d",
71 policy->program, policy->features);
72 }
73
74 free(program);
75 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
76}
77
78struct cmd_results *cmd_reject(int argc, char **argv) {
79 struct cmd_results *error = NULL;
80 if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) {
81 return error;
82 }
83 if ((error = check_security_config())) {
84 return error;
85 }
86
87 char *program = NULL;
88 if (!strcmp(argv[0], "*")) {
89 program = strdup(argv[0]);
90 } else {
91 program = resolve_path(argv[0]);
92 }
93 if (!program) {
94 // Punt
95 sway_log(L_INFO, "Unable to resolve IPC reject target '%s'."
96 " Will use provided path", argv[0]);
97 program = strdup(argv[0]);
98 }
99
100 struct feature_policy *policy = get_feature_policy(program);
101 policy->features &= ~get_features(argc, argv, &error);
102
103 sway_log(L_DEBUG, "Permissions granted to %s for features %d",
104 policy->program, policy->features);
105
106 free(program);
107 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
108}
diff --git a/sway/old/commands/reload.c b/sway/old/commands/reload.c
deleted file mode 100644
index 01fcc5ba..00000000
--- a/sway/old/commands/reload.c
+++ /dev/null
@@ -1,19 +0,0 @@
1#include "sway/commands.h"
2#include "sway/config.h"
3#include "sway/layout.h"
4
5struct cmd_results *cmd_reload(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if (config->reading) return cmd_results_new(CMD_FAILURE, "reload", "Can't be used in config file.");
8 if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
9 return error;
10 }
11 if (!load_main_config(config->current_config, true)) {
12 return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");
13 }
14
15 load_swaybars();
16
17 arrange_windows(&root_container, -1, -1);
18 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
19}
diff --git a/sway/old/commands/resize.c b/sway/old/commands/resize.c
deleted file mode 100644
index ef52bb07..00000000
--- a/sway/old/commands/resize.c
+++ /dev/null
@@ -1,375 +0,0 @@
1#include <errno.h>
2#include <math.h>
3#include <stdbool.h>
4#include <stdlib.h>
5#include <string.h>
6#include <strings.h>
7#include <wlc/wlc.h>
8#include "sway/commands.h"
9#include "sway/layout.h"
10#include "sway/focus.h"
11#include "sway/input_state.h"
12#include "sway/handlers.h"
13#include "log.h"
14
15enum resize_dim_types {
16 RESIZE_DIM_PX,
17 RESIZE_DIM_PPT,
18 RESIZE_DIM_DEFAULT,
19};
20
21static bool set_size_floating(int new_dimension, bool use_width) {
22 swayc_t *view = current_container;
23 if (view) {
24 if (use_width) {
25 int current_width = view->width;
26 view->desired_width = new_dimension;
27 floating_view_sane_size(view);
28
29 int new_x = view->x + (int)(((view->desired_width - current_width) / 2) * -1);
30 view->width = view->desired_width;
31 view->x = new_x;
32
33 update_geometry(view);
34 } else {
35 int current_height = view->height;
36 view->desired_height = new_dimension;
37 floating_view_sane_size(view);
38
39 int new_y = view->y + (int)(((view->desired_height - current_height) / 2) * -1);
40 view->height = view->desired_height;
41 view->y = new_y;
42
43 update_geometry(view);
44 }
45
46 return true;
47 }
48
49 return false;
50}
51
52static bool resize_floating(int amount, bool use_width) {
53 swayc_t *view = current_container;
54
55 if (view) {
56 if (use_width) {
57 return set_size_floating(view->width + amount, true);
58 } else {
59 return set_size_floating(view->height + amount, false);
60 }
61 }
62
63 return false;
64}
65
66static bool resize_tiled(int amount, bool use_width) {
67 swayc_t *container = current_container;
68 swayc_t *parent = container->parent;
69 int idx_focused = 0;
70 bool use_major = false;
71 size_t nb_before = 0;
72 size_t nb_after = 0;
73
74 // 1. Identify a container ancestor that will allow the focused child to grow in the requested
75 // direction.
76 while (container->parent) {
77 parent = container->parent;
78 if ((parent->children && parent->children->length > 1)
79 && (is_auto_layout(parent->layout)
80 || (use_width ? parent->layout == L_HORIZ : parent->layout == L_VERT))) {
81 // check if container has siblings that can provide/absorb the space needed for
82 // the resize operation.
83 use_major = use_width
84 ? parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
85 : parent->layout == L_AUTO_TOP || parent->layout == L_AUTO_BOTTOM;
86 // Note: use_major will be false for L_HORIZ and L_VERT
87
88 idx_focused = index_child(container);
89 if (idx_focused < 0) {
90 sway_log(L_ERROR, "Something weird is happening, child container not "
91 "present in its parent's children list.");
92 continue;
93 }
94 if (use_major) {
95 nb_before = auto_group_index(parent, idx_focused);
96 nb_after = auto_group_count(parent) - nb_before - 1;
97 } else {
98 nb_before = idx_focused - auto_group_start_index(parent, idx_focused);
99 nb_after = auto_group_end_index(parent, idx_focused) - idx_focused - 1;
100 sway_log(L_DEBUG, "+++ focused: %d, start: %d, end: %d, before: %d, after: %d",
101 idx_focused,
102 (int)auto_group_start_index(parent, idx_focused),
103 (int)auto_group_end_index(parent, idx_focused),
104 (int)nb_before, (int)nb_after);
105
106 }
107 if (nb_before || nb_after) {
108 break;
109 }
110 }
111 container = parent; /* continue up the tree to the next ancestor */
112 }
113 if (parent == &root_container) {
114 return true;
115 }
116 sway_log(L_DEBUG, "Found the proper parent: %p. It has %zu before conts, "
117 "and %zu after conts", parent, nb_before, nb_after);
118 // 2. Ensure that the resize operation will not make one of the resized containers drop
119 // below the "sane" size threshold.
120 bool valid = true;
121 swayc_t *focused = parent->children->items[idx_focused];
122 int start = use_major ? 0 : auto_group_start_index(parent, idx_focused);
123 int end = use_major ? parent->children->length : auto_group_end_index(parent, idx_focused);
124 sway_log(L_DEBUG, "Check children of container %p [%d,%d[", container, start, end);
125 for (int i = start; i < end; ) {
126 swayc_t *sibling = parent->children->items[i];
127 double pixels = amount;
128 bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y;
129 bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y;
130 if (is_before || is_after) {
131 pixels = -pixels;
132 pixels /= is_before ? nb_before : nb_after;
133 if (nb_after != 0 && nb_before != 0) {
134 pixels /= 2;
135 }
136 }
137 sway_log(L_DEBUG, "Check container %p: width %g vs %d, height %g vs %d", sibling, sibling->width + pixels, min_sane_w, sibling->height + pixels, min_sane_h);
138 if (use_width ?
139 sibling->width + pixels < min_sane_w :
140 sibling->height + pixels < min_sane_h) {
141 valid = false;
142 sway_log(L_DEBUG, "Container size no longer sane");
143 break;
144 }
145 i = use_major ? auto_group_end_index(parent, i) : (i + 1);
146 sway_log(L_DEBUG, "+++++ check %i", i);
147 }
148 // 3. Apply the size change
149 if (valid) {
150 for (int i = start; i < end; ) {
151 int next_i = use_major ? auto_group_end_index(parent, i) : (i + 1);
152 swayc_t *sibling = parent->children->items[i];
153 double pixels = amount;
154 bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y;
155 bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y;
156 if (is_before || is_after) {
157 pixels = -pixels;
158 pixels /= is_before ? nb_before : nb_after;
159 if (nb_after != 0 && nb_before != 0) {
160 pixels /= 2;
161 }
162 sway_log(L_DEBUG, "%p: %s", sibling, is_before ? "before" : "after");
163 if (use_major) {
164 for (int j = i; j < next_i; ++j) {
165 recursive_resize(parent->children->items[j], pixels,
166 use_width ?
167 (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
168 (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
169 }
170 } else {
171 recursive_resize(sibling, pixels,
172 use_width ?
173 (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
174 (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
175 }
176 } else {
177 if (use_major) {
178 for (int j = i; j < next_i; ++j) {
179 recursive_resize(parent->children->items[j], pixels / 2,
180 use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
181 recursive_resize(parent->children->items[j], pixels / 2,
182 use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
183 }
184 } else {
185 recursive_resize(sibling, pixels / 2,
186 use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
187 recursive_resize(sibling, pixels / 2,
188 use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
189 }
190 }
191 i = next_i;
192 }
193 // Recursive resize does not handle positions, let arrange_windows
194 // take care of that.
195 arrange_windows(swayc_active_workspace(), -1, -1);
196 }
197 return true;
198}
199
200static bool set_size_tiled(int amount, bool use_width) {
201 int desired;
202 swayc_t *focused = current_container;
203
204 if (use_width) {
205 desired = amount - focused->width;
206 } else {
207 desired = amount - focused->height;
208 }
209
210 return resize_tiled(desired, use_width);
211}
212
213static bool set_size(int dimension, bool use_width) {
214 swayc_t *focused = current_container;
215
216 if (focused) {
217 if (focused->is_floating) {
218 return set_size_floating(dimension, use_width);
219 } else {
220 return set_size_tiled(dimension, use_width);
221 }
222 }
223
224 return false;
225}
226
227static bool resize(int dimension, bool use_width, enum resize_dim_types dim_type) {
228 swayc_t *focused = current_container;
229
230 // translate "10 ppt" (10%) to appropriate # of pixels in case we need it
231 float ppt_dim = (float)dimension / 100;
232
233 if (use_width) {
234 ppt_dim = focused->width * ppt_dim;
235 } else {
236 ppt_dim = focused->height * ppt_dim;
237 }
238
239 if (focused) {
240 if (focused->is_floating) {
241 // floating view resize dimensions should default to px, so only
242 // use ppt if specified
243 if (dim_type == RESIZE_DIM_PPT) {
244 dimension = (int)ppt_dim;
245 }
246
247 return resize_floating(dimension, use_width);
248 } else {
249 // tiled view resize dimensions should default to ppt, so only use
250 // px if specified
251 if (dim_type != RESIZE_DIM_PX) {
252 dimension = (int)ppt_dim;
253 }
254
255 return resize_tiled(dimension, use_width);
256 }
257 }
258
259 return false;
260}
261
262static struct cmd_results *cmd_resize_set(int argc, char **argv) {
263 struct cmd_results *error = NULL;
264 if ((error = checkarg(argc, "resize set", EXPECTED_AT_LEAST, 2))) {
265 return error;
266 }
267
268 if (strcasecmp(argv[0], "width") == 0 || strcasecmp(argv[0], "height") == 0) {
269 // handle `reset set width 100 px height 100 px` syntax, also allows
270 // specifying only one dimension for a `resize set`
271 int cmd_num = 0;
272 int dim;
273
274 while ((cmd_num + 1) < argc) {
275 dim = (int)strtol(argv[cmd_num + 1], NULL, 10);
276 if (errno == ERANGE || dim == 0) {
277 errno = 0;
278 return cmd_results_new(CMD_INVALID, "resize set",
279 "Expected 'resize set <width|height> <amount> [px] [<width|height> <amount> [px]]'");
280 }
281
282 if (strcasecmp(argv[cmd_num], "width") == 0) {
283 set_size(dim, true);
284 } else if (strcasecmp(argv[cmd_num], "height") == 0) {
285 set_size(dim, false);
286 } else {
287 return cmd_results_new(CMD_INVALID, "resize set",
288 "Expected 'resize set <width|height> <amount> [px] [<width|height> <amount> [px]]'");
289 }
290
291 cmd_num += 2;
292
293 if (cmd_num < argc && strcasecmp(argv[cmd_num], "px") == 0) {
294 // if this was `resize set width 400 px height 300 px`, disregard the `px` arg
295 cmd_num++;
296 }
297 }
298 } else {
299 // handle `reset set 100 px 100 px` syntax
300 int width = (int)strtol(argv[0], NULL, 10);
301 if (errno == ERANGE || width == 0) {
302 errno = 0;
303 return cmd_results_new(CMD_INVALID, "resize set",
304 "Expected 'resize set <width> [px] <height> [px]'");
305 }
306
307 int height_arg = 1;
308 if (strcasecmp(argv[1], "px") == 0) {
309 height_arg = 2;
310 }
311
312 int height = (int)strtol(argv[height_arg], NULL, 10);
313 if (errno == ERANGE || height == 0) {
314 errno = 0;
315 return cmd_results_new(CMD_INVALID, "resize set",
316 "Expected 'resize set <width> [px] <height> [px]'");
317 }
318
319 set_size(width, true);
320 set_size(height, false);
321 }
322
323 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
324}
325
326struct cmd_results *cmd_resize(int argc, char **argv) {
327 struct cmd_results *error = NULL;
328 if (config->reading) return cmd_results_new(CMD_FAILURE, "resize", "Can't be used in config file.");
329 if (!config->active) return cmd_results_new(CMD_FAILURE, "resize", "Can only be used when sway is running.");
330
331 if (strcasecmp(argv[0], "set") == 0) {
332 return cmd_resize_set(argc - 1, &argv[1]);
333 }
334
335 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
336 return error;
337 }
338
339 int dim_arg = argc - 1;
340
341 enum resize_dim_types dim_type = RESIZE_DIM_DEFAULT;
342 if (strcasecmp(argv[dim_arg], "ppt") == 0) {
343 dim_type = RESIZE_DIM_PPT;
344 dim_arg--;
345 } else if (strcasecmp(argv[dim_arg], "px") == 0) {
346 dim_type = RESIZE_DIM_PX;
347 dim_arg--;
348 }
349
350 int amount = (int)strtol(argv[dim_arg], NULL, 10);
351 if (errno == ERANGE || amount == 0) {
352 errno = 0;
353 amount = 10; // this is the default resize dimension used by i3 for both px and ppt
354 sway_log(L_DEBUG, "Tried to get resize dimension out of '%s' but failed; setting dimension to default %d",
355 argv[dim_arg], amount);
356 }
357
358 bool use_width = false;
359 if (strcasecmp(argv[1], "width") == 0) {
360 use_width = true;
361 } else if (strcasecmp(argv[1], "height") != 0) {
362 return cmd_results_new(CMD_INVALID, "resize",
363 "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'");
364 }
365
366 if (strcasecmp(argv[0], "shrink") == 0) {
367 amount *= -1;
368 } else if (strcasecmp(argv[0], "grow") != 0) {
369 return cmd_results_new(CMD_INVALID, "resize",
370 "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'");
371 }
372
373 resize(amount, use_width, dim_type);
374 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
375}
diff --git a/sway/old/commands/scratchpad.c b/sway/old/commands/scratchpad.c
deleted file mode 100644
index 6c5c92df..00000000
--- a/sway/old/commands/scratchpad.c
+++ /dev/null
@@ -1,72 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include <wlc/wlc.h>
4#include "sway/commands.h"
5#include "sway/container.h"
6#include "sway/focus.h"
7#include "sway/layout.h"
8
9static swayc_t *fetch_view_from_scratchpad() {
10 sp_index = (sp_index + 1) % scratchpad->length;
11 swayc_t *view = scratchpad->items[sp_index];
12
13 if (wlc_view_get_output(view->handle) != swayc_active_output()->handle) {
14 wlc_view_set_output(view->handle, swayc_active_output()->handle);
15 }
16 if (!view->is_floating) {
17 view->width = swayc_active_workspace()->width * 0.5;
18 view->height = swayc_active_workspace()->height * 0.75;
19 view->x = (swayc_active_workspace()->width - view->width)/2;
20 view->y = (swayc_active_workspace()->height - view->height)/2;
21 }
22 if (swayc_active_workspace()->width < view->x + 20 || view->x + view->width < 20) {
23 view->x = (swayc_active_workspace()->width - view->width)/2;
24 }
25 if (swayc_active_workspace()->height < view->y + 20 || view->y + view->height < 20) {
26 view->y = (swayc_active_workspace()->height - view->height)/2;
27 }
28
29 add_floating(swayc_active_workspace(), view);
30 wlc_view_set_mask(view->handle, VISIBLE);
31 view->visible = true;
32 arrange_windows(swayc_active_workspace(), -1, -1);
33 set_focused_container(view);
34 return view;
35}
36
37struct cmd_results *cmd_scratchpad(int argc, char **argv) {
38 struct cmd_results *error = NULL;
39 if (config->reading) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can't be used in config file.");
40 if (!config->active) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can only be used when sway is running.");
41 if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) {
42 return error;
43 }
44
45 if (strcasecmp(argv[0], "show") == 0 && scratchpad->length > 0) {
46 if (!sp_view) {
47 if (current_container) {
48 // Haxor the scratchpad index if criteria'd
49 for (int i = 0; i < scratchpad->length; ++i) {
50 if (scratchpad->items[i] == current_container) {
51 sp_index = (i - 1) % scratchpad->length;
52 }
53 }
54 }
55 sp_view = fetch_view_from_scratchpad();
56 } else {
57 if (swayc_active_workspace() != sp_view->parent) {
58 hide_view_in_scratchpad(sp_view);
59 if (sp_index == 0) {
60 sp_index = scratchpad->length;
61 }
62 sp_index--;
63 sp_view = fetch_view_from_scratchpad();
64 } else {
65 hide_view_in_scratchpad(sp_view);
66 sp_view = NULL;
67 }
68 }
69 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
70 }
71 return cmd_results_new(CMD_FAILURE, "scratchpad", "Expected 'scratchpad show' when scratchpad is not empty.");
72}
diff --git a/sway/old/commands/seamless_mouse.c b/sway/old/commands/seamless_mouse.c
deleted file mode 100644
index 7760e88d..00000000
--- a/sway/old/commands/seamless_mouse.c
+++ /dev/null
@@ -1,13 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_seamless_mouse(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "seamless_mouse", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10
11 config->seamless_mouse = (strcasecmp(argv[0], "on") == 0 || strcasecmp(argv[0], "yes") == 0);
12 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
13}
diff --git a/sway/old/commands/set.c b/sway/old/commands/set.c
deleted file mode 100644
index 46fc6d38..00000000
--- a/sway/old/commands/set.c
+++ /dev/null
@@ -1,61 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <stdio.h>
3#include <string.h>
4#include <strings.h>
5#include "sway/commands.h"
6#include "sway/config.h"
7#include "list.h"
8#include "stringop.h"
9
10// sort in order of longest->shortest
11static int compare_set_qsort(const void *_l, const void *_r) {
12 struct sway_variable const *l = *(void **)_l;
13 struct sway_variable const *r = *(void **)_r;
14 return strlen(r->name) - strlen(l->name);
15}
16
17struct cmd_results *cmd_set(int argc, char **argv) {
18 char *tmp;
19 struct cmd_results *error = NULL;
20 if (!config->reading) return cmd_results_new(CMD_FAILURE, "set", "Can only be used in config file.");
21 if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) {
22 return error;
23 }
24
25 if (argv[0][0] != '$') {
26 sway_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]);
27
28 size_t size = snprintf(NULL, 0, "$%s", argv[0]);
29 tmp = malloc(size + 1);
30 if (!tmp) {
31 return cmd_results_new(CMD_FAILURE, "set", "Not possible to create variable $'%s'", argv[0]);
32 }
33 snprintf(tmp, size+1, "$%s", argv[0]);
34
35 argv[0] = tmp;
36 }
37
38 struct sway_variable *var = NULL;
39 // Find old variable if it exists
40 int i;
41 for (i = 0; i < config->symbols->length; ++i) {
42 var = config->symbols->items[i];
43 if (strcmp(var->name, argv[0]) == 0) {
44 break;
45 }
46 var = NULL;
47 }
48 if (var) {
49 free(var->value);
50 } else {
51 var = malloc(sizeof(struct sway_variable));
52 if (!var) {
53 return cmd_results_new(CMD_FAILURE, "set", "Unable to allocate variable");
54 }
55 var->name = strdup(argv[0]);
56 list_add(config->symbols, var);
57 list_qsort(config->symbols, compare_set_qsort);
58 }
59 var->value = join_args(argv + 1, argc - 1);
60 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
61}
diff --git a/sway/old/commands/show_marks.c b/sway/old/commands/show_marks.c
deleted file mode 100644
index ed56d9e5..00000000
--- a/sway/old/commands/show_marks.c
+++ /dev/null
@@ -1,13 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_show_marks(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "show_marks", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10
11 config->show_marks = !strcasecmp(argv[0], "on");
12 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
13}
diff --git a/sway/old/commands/smart_gaps.c b/sway/old/commands/smart_gaps.c
deleted file mode 100644
index 815fc501..00000000
--- a/sway/old/commands/smart_gaps.c
+++ /dev/null
@@ -1,20 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "smart_gaps", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10
11 if (strcasecmp(argv[0], "on") == 0) {
12 config->smart_gaps = true;
13 } else if (strcasecmp(argv[0], "off") == 0) {
14 config->smart_gaps = false;
15 } else {
16 return cmd_results_new(CMD_INVALID, "smart_gaps", "Expected 'smart_gaps <on|off>'");
17 }
18
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20}
diff --git a/sway/old/commands/split.c b/sway/old/commands/split.c
deleted file mode 100644
index e3045a4f..00000000
--- a/sway/old/commands/split.c
+++ /dev/null
@@ -1,98 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include <wlc/wlc-render.h>
4#include "sway/border.h"
5#include "sway/commands.h"
6#include "sway/container.h"
7#include "sway/focus.h"
8#include "sway/layout.h"
9#include "log.h"
10
11static struct cmd_results *_do_split(int argc, char **argv, int layout) {
12 char *name = layout == L_VERT ? "splitv" :
13 layout == L_HORIZ ? "splith" : "split";
14 struct cmd_results *error = NULL;
15 if (config->reading) return cmd_results_new(CMD_FAILURE, name, "Can't be used in config file.");
16 if (!config->active) return cmd_results_new(CMD_FAILURE, name, "Can only be used when sway is running.");
17 if ((error = checkarg(argc, name, EXPECTED_EQUAL_TO, 0))) {
18 return error;
19 }
20 swayc_t *focused = current_container;
21
22 // Case of floating window, don't split
23 if (focused->is_floating) {
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25 }
26 /* Case that focus is on an workspace with 0/1 children.change its layout */
27 if (focused->type == C_WORKSPACE && focused->children->length <= 1) {
28 sway_log(L_DEBUG, "changing workspace layout");
29 swayc_change_layout(focused, layout);
30 } else if (focused->type != C_WORKSPACE && focused->parent->children->length == 1) {
31 /* Case of no siblings. change parent layout */
32 sway_log(L_DEBUG, "changing container layout");
33 swayc_change_layout(focused->parent, layout);
34 } else {
35 /* regular case where new split container is build around focused container
36 * or in case of workspace, container inherits its children */
37 sway_log(L_DEBUG, "Adding new container around current focused container");
38 sway_log(L_INFO, "FOCUSED SIZE: %.f %.f", focused->width, focused->height);
39 swayc_t *parent = new_container(focused, layout);
40 set_focused_container(focused);
41 arrange_windows(parent, -1, -1);
42 }
43
44 // update container every time
45 // if it is tabbed/stacked then the title must change
46 // if the indicator color is different then the border must change
47 update_container_border(focused);
48 swayc_t *output = swayc_parent_by_type(focused, C_OUTPUT);
49 // schedule render to make changes take effect right away,
50 // otherwise we would have to wait for the view to render,
51 // which is unpredictable.
52 wlc_output_schedule_render(output->handle);
53
54 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
55}
56
57struct cmd_results *cmd_split(int argc, char **argv) {
58 struct cmd_results *error = NULL;
59 if (config->reading) return cmd_results_new(CMD_FAILURE, "split", "Can't be used in config file.");
60 if (!config->active) return cmd_results_new(CMD_FAILURE, "split", "Can only be used when sway is running.");
61 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
62 return error;
63 }
64 if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) {
65 _do_split(argc - 1, argv + 1, L_VERT);
66 } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) {
67 _do_split(argc - 1, argv + 1, L_HORIZ);
68 } else if (strcasecmp(argv[0], "t") == 0 || strcasecmp(argv[0], "toggle") == 0) {
69 swayc_t *focused = current_container;
70 if (focused->parent->layout == L_VERT) {
71 _do_split(argc - 1, argv + 1, L_HORIZ);
72 } else {
73 _do_split(argc - 1, argv + 1, L_VERT);
74 }
75 } else {
76 error = cmd_results_new(CMD_FAILURE, "split",
77 "Invalid split command (expected either horizontal or vertical).");
78 return error;
79 }
80 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
81}
82
83struct cmd_results *cmd_splitv(int argc, char **argv) {
84 return _do_split(argc, argv, L_VERT);
85}
86
87struct cmd_results *cmd_splith(int argc, char **argv) {
88 return _do_split(argc, argv, L_HORIZ);
89}
90
91struct cmd_results *cmd_splitt(int argc, char **argv) {
92 swayc_t *focused = current_container;
93 if (focused->parent->layout == L_VERT) {
94 return _do_split(argc, argv, L_HORIZ);
95 } else {
96 return _do_split(argc, argv, L_VERT);
97 }
98}
diff --git a/sway/old/commands/sticky.c b/sway/old/commands/sticky.c
deleted file mode 100644
index 4899e061..00000000
--- a/sway/old/commands/sticky.c
+++ /dev/null
@@ -1,25 +0,0 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "sway/focus.h"
4
5struct cmd_results *cmd_sticky(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if (config->reading) return cmd_results_new(CMD_FAILURE, "sticky", "Can't be used in config file.");
8 if (!config->active) return cmd_results_new(CMD_FAILURE, "sticky", "Can only be used when sway is running.");
9 if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12 char *action = argv[0];
13 swayc_t *cont = get_focused_view(&root_container);
14 if (strcmp(action, "toggle") == 0) {
15 cont->sticky = !cont->sticky;
16 } else if (strcmp(action, "enable") == 0) {
17 cont->sticky = true;
18 } else if (strcmp(action, "disable") == 0) {
19 cont->sticky = false;
20 } else {
21 return cmd_results_new(CMD_FAILURE, "sticky",
22 "Expected 'sticky enable|disable|toggle'");
23 }
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25}
diff --git a/sway/old/commands/unmark.c b/sway/old/commands/unmark.c
deleted file mode 100644
index ac213261..00000000
--- a/sway/old/commands/unmark.c
+++ /dev/null
@@ -1,31 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "list.h"
5#include "stringop.h"
6
7struct cmd_results *cmd_unmark(int argc, char **argv) {
8 swayc_t *view = current_container;
9
10 if (view->marks) {
11 if (argc) {
12 char *mark = join_args(argv, argc);
13 int index;
14 if ((index = list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1) {
15 free(view->marks->items[index]);
16 list_del(view->marks, index);
17
18 if (view->marks->length == 0) {
19 list_free(view->marks);
20 view->marks = NULL;
21 }
22 }
23 free(mark);
24 } else {
25 list_foreach(view->marks, free);
26 list_free(view->marks);
27 view->marks = NULL;
28 }
29 }
30 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
31}
diff --git a/sway/old/commands/workspace.c b/sway/old/commands/workspace.c
deleted file mode 100644
index a7839746..00000000
--- a/sway/old/commands/workspace.c
+++ /dev/null
@@ -1,92 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/config.h"
6#include "sway/input_state.h"
7#include "sway/workspace.h"
8#include "list.h"
9#include "log.h"
10#include "stringop.h"
11
12struct cmd_results *cmd_workspace(int argc, char **argv) {
13 struct cmd_results *error = NULL;
14 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) {
15 return error;
16 }
17
18 int output_location = -1;
19
20 for (int i = 0; i < argc; ++i) {
21 if (strcasecmp(argv[i], "output") == 0) {
22 output_location = i;
23 break;
24 }
25 }
26 if (output_location >= 0) {
27 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) {
28 return error;
29 }
30 struct workspace_output *wso = calloc(1, sizeof(struct workspace_output));
31 if (!wso) {
32 return cmd_results_new(CMD_FAILURE, "workspace output",
33 "Unable to allocate workspace output");
34 }
35 wso->workspace = join_args(argv, argc - 2);
36 wso->output = strdup(argv[output_location + 1]);
37 int i = -1;
38 if ((i = list_seq_find(config->workspace_outputs, workspace_output_cmp_workspace, wso)) != -1) {
39 struct workspace_output *old = config->workspace_outputs->items[i];
40 free(old); // workspaces can only be assigned to a single output
41 list_del(config->workspace_outputs, i);
42 }
43 sway_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output);
44 list_add(config->workspace_outputs, wso);
45 } else {
46 if (config->reading || !config->active) {
47 return cmd_results_new(CMD_DEFER, "workspace", NULL);
48 }
49 swayc_t *ws = NULL;
50 if (strcasecmp(argv[0], "number") == 0) {
51 if (!(ws = workspace_by_number(argv[1]))) {
52 char *name = join_args(argv + 1, argc - 1);
53 ws = workspace_create(name);
54 free(name);
55 }
56 } else if (strcasecmp(argv[0], "next") == 0) {
57 ws = workspace_next();
58 } else if (strcasecmp(argv[0], "prev") == 0) {
59 ws = workspace_prev();
60 } else if (strcasecmp(argv[0], "next_on_output") == 0) {
61 ws = workspace_output_next();
62 } else if (strcasecmp(argv[0], "prev_on_output") == 0) {
63 ws = workspace_output_prev();
64 } else if (strcasecmp(argv[0], "back_and_forth") == 0) {
65 // if auto_back_and_forth is enabled, workspace_switch will swap
66 // the workspaces. If we created prev_workspace here, workspace_switch
67 // would put us back on original workspace.
68 if (config->auto_back_and_forth) {
69 ws = swayc_active_workspace();
70 } else if (prev_workspace_name && !(ws = workspace_by_name(prev_workspace_name))) {
71 ws = workspace_create(prev_workspace_name);
72 }
73 } else {
74 char *name = join_args(argv, argc);
75 if (!(ws = workspace_by_name(name))) {
76 ws = workspace_create(name);
77 }
78 free(name);
79 }
80 swayc_t *old_output = swayc_active_output();
81 workspace_switch(ws);
82 swayc_t *new_output = swayc_active_output();
83
84 if (config->mouse_warping && old_output != new_output) {
85 swayc_t *focused = get_focused_view(ws);
86 if (focused && focused->type == C_VIEW) {
87 center_pointer_on(focused);
88 }
89 }
90 }
91 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
92}
diff --git a/sway/old/commands/workspace_auto_back_and_forth.c b/sway/old/commands/workspace_auto_back_and_forth.c
deleted file mode 100644
index d58ae5c8..00000000
--- a/sway/old/commands/workspace_auto_back_and_forth.c
+++ /dev/null
@@ -1,18 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10 if (strcasecmp(argv[0], "yes") == 0) {
11 config->auto_back_and_forth = true;
12 } else if (strcasecmp(argv[0], "no") == 0) {
13 config->auto_back_and_forth = false;
14 } else {
15 return cmd_results_new(CMD_INVALID, "workspace_auto_back_and_forth", "Expected 'workspace_auto_back_and_forth <yes|no>'");
16 }
17 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
18}
diff --git a/sway/old/commands/workspace_layout.c b/sway/old/commands/workspace_layout.c
deleted file mode 100644
index 9ac84be2..00000000
--- a/sway/old/commands/workspace_layout.c
+++ /dev/null
@@ -1,40 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_workspace_layout(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "workspace_layout", EXPECTED_AT_LEAST, 1))) {
8 return error;
9 }
10
11 if (strcasecmp(argv[0], "default") == 0) {
12 config->default_layout = L_NONE;
13 } else if (strcasecmp(argv[0], "stacking") == 0) {
14 config->default_layout = L_STACKED;
15 } else if (strcasecmp(argv[0], "tabbed") == 0) {
16 config->default_layout = L_TABBED;
17 } else if (strcasecmp(argv[0], "auto") == 0) {
18 if (argc == 1) {
19 config->default_layout = L_AUTO_FIRST;
20 } else {
21 if ((error = checkarg(argc, "workspace_layout auto", EXPECTED_EQUAL_TO, 2))) {
22 return error;
23 }
24 if (strcasecmp(argv[1], "left") == 0) {
25 config->default_layout = L_AUTO_LEFT;
26 } else if (strcasecmp(argv[1], "right") == 0) {
27 config->default_layout = L_AUTO_RIGHT;
28 } else if (strcasecmp(argv[1], "top") == 0) {
29 config->default_layout = L_AUTO_TOP;
30 } else if (strcasecmp(argv[1], "bottom") == 0) {
31 config->default_layout = L_AUTO_BOTTOM;
32 } else {
33 return cmd_results_new(CMD_INVALID, "workspace_layout auto", "Expected 'workspace_layout auto <left|right|top|bottom>'");
34 }
35 }
36 } else {
37 return cmd_results_new(CMD_INVALID, "workspace_layout", "Expected 'workspace_layout <default|stacking|tabbed|auto|auto left|auto right|auto top|auto bottom>'");
38 }
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40}
diff --git a/sway/old/config.c b/sway/old/config.c
deleted file mode 100644
index d14653e3..00000000
--- a/sway/old/config.c
+++ /dev/null
@@ -1,1313 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#define _XOPEN_SOURCE 700
3#include <stdio.h>
4#include <stdbool.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <libgen.h>
8#include <wordexp.h>
9#include <sys/types.h>
10#include <sys/wait.h>
11#include <sys/stat.h>
12#include <signal.h>
13#include <libinput.h>
14#include <limits.h>
15#include <float.h>
16#include <dirent.h>
17#include <strings.h>
18#include <wlr/types/wlr_output.h>
19#include "wayland-desktop-shell-server-protocol.h"
20#include "sway/commands.h"
21#include "sway/config.h"
22#include "sway/layout.h"
23#include "sway/input_state.h"
24#include "sway/criteria.h"
25#include "sway/input.h"
26#include "sway/border.h"
27#include "readline.h"
28#include "stringop.h"
29#include "list.h"
30#include "log.h"
31
32struct sway_config *config = NULL;
33
34static void terminate_swaybar(pid_t pid);
35
36static void free_variable(struct sway_variable *var) {
37 if (!var) {
38 return;
39 }
40 free(var->name);
41 free(var->value);
42 free(var);
43}
44
45static void free_binding(struct sway_binding *bind) {
46 if (!bind) {
47 return;
48 }
49 free_flat_list(bind->keys);
50 free(bind->command);
51 free(bind);
52}
53
54static void free_mode(struct sway_mode *mode) {
55 if (!mode) {
56 return;
57 }
58 free(mode->name);
59 int i;
60 for (i = 0; mode->bindings && i < mode->bindings->length; ++i) {
61 free_binding(mode->bindings->items[i]);
62 }
63 list_free(mode->bindings);
64 free(mode);
65}
66
67static void free_bar(struct bar_config *bar) {
68 if (!bar) {
69 return;
70 }
71 free(bar->mode);
72 free(bar->hidden_state);
73#ifdef ENABLE_TRAY
74 free(bar->tray_output);
75 free(bar->icon_theme);
76#endif
77 free(bar->status_command);
78 free(bar->font);
79 free(bar->separator_symbol);
80 int i;
81 for (i = 0; bar->bindings && i < bar->bindings->length; ++i) {
82 free_sway_mouse_binding(bar->bindings->items[i]);
83 }
84 list_free(bar->bindings);
85
86 if (bar->outputs) {
87 free_flat_list(bar->outputs);
88 }
89
90 if (bar->pid != 0) {
91 terminate_swaybar(bar->pid);
92 }
93
94 free(bar->colors.background);
95 free(bar->colors.statusline);
96 free(bar->colors.separator);
97 free(bar->colors.focused_background);
98 free(bar->colors.focused_statusline);
99 free(bar->colors.focused_separator);
100 free(bar->colors.focused_workspace_border);
101 free(bar->colors.focused_workspace_bg);
102 free(bar->colors.focused_workspace_text);
103 free(bar->colors.active_workspace_border);
104 free(bar->colors.active_workspace_bg);
105 free(bar->colors.active_workspace_text);
106 free(bar->colors.inactive_workspace_border);
107 free(bar->colors.inactive_workspace_bg);
108 free(bar->colors.inactive_workspace_text);
109 free(bar->colors.urgent_workspace_border);
110 free(bar->colors.urgent_workspace_bg);
111 free(bar->colors.urgent_workspace_text);
112 free(bar->colors.binding_mode_border);
113 free(bar->colors.binding_mode_bg);
114 free(bar->colors.binding_mode_text);
115
116 free(bar);
117}
118
119void free_input_config(struct input_config *ic) {
120 if (!ic) {
121 return;
122 }
123 free(ic->identifier);
124 free(ic);
125}
126
127void free_output_config(struct output_config *oc) {
128 if (!oc) {
129 return;
130 }
131 free(oc->name);
132 free(oc->background);
133 free(oc->background_option);
134 free(oc);
135}
136
137static void free_workspace_output(struct workspace_output *wo) {
138 if (!wo) {
139 return;
140 }
141 free(wo->output);
142 free(wo->workspace);
143 free(wo);
144}
145
146static void pid_workspace_cleanup() {
147 struct timespec ts;
148 struct pid_workspace *pw = NULL;
149
150 clock_gettime(CLOCK_MONOTONIC, &ts);
151
152 // work backwards through list and remove any entries
153 // older than PID_WORKSPACE_TIMEOUT
154 for (int i = config->pid_workspaces->length - 1; i > -1; i--) {
155 pw = config->pid_workspaces->items[i];
156
157 if (difftime(ts.tv_sec, *pw->time_added) >= PID_WORKSPACE_TIMEOUT) {
158 free_pid_workspace(config->pid_workspaces->items[i]);
159 list_del(config->pid_workspaces, i);
160 }
161 }
162}
163
164// de-dupe pid_workspaces to ensure pid uniqueness
165void pid_workspace_add(struct pid_workspace *pw) {
166 struct pid_workspace *list_pw = NULL;
167 struct timespec ts;
168 time_t *now = malloc(sizeof(time_t));
169 if (!now) {
170 sway_log(L_ERROR, "Allocating time for pid_workspace failed");
171 return;
172 }
173
174 pid_workspace_cleanup();
175
176 // add current time to pw
177 clock_gettime(CLOCK_MONOTONIC, &ts);
178 *now = ts.tv_sec;
179
180 pw->time_added = now;
181
182 // work backwards through list and delete any entries that
183 // have the same pid as that in our new pid_workspace
184 for (int i = config->pid_workspaces->length - 1; i > -1; i--) {
185 list_pw = config->pid_workspaces->items[i];
186
187 if (pw->pid == list_pw->pid) {
188 free_pid_workspace(config->pid_workspaces->items[i]);
189 list_del(config->pid_workspaces, i);
190 }
191 }
192
193 list_add(config->pid_workspaces, pw);
194}
195
196void free_pid_workspace(struct pid_workspace *pw) {
197 if (!pw) {
198 return;
199 }
200 free(pw->pid);
201 free(pw->workspace);
202 free(pw->time_added);
203 free(pw);
204}
205
206void free_command_policy(struct command_policy *policy) {
207 if (!policy) {
208 return;
209 }
210 free(policy->command);
211 free(policy);
212}
213
214void free_feature_policy(struct feature_policy *policy) {
215 if (!policy) {
216 return;
217 }
218 free(policy->program);
219 free(policy);
220}
221
222void free_config(struct sway_config *config) {
223 if (!config) {
224 return;
225 }
226 int i;
227 for (i = 0; config->symbols && i < config->symbols->length; ++i) {
228 free_variable(config->symbols->items[i]);
229 }
230 list_free(config->symbols);
231
232 for (i = 0; config->modes && i < config->modes->length; ++i) {
233 free_mode(config->modes->items[i]);
234 }
235 list_free(config->modes);
236
237 for (i = 0; config->bars && i < config->bars->length; ++i) {
238 free_bar(config->bars->items[i]);
239 }
240 list_free(config->bars);
241
242 free_flat_list(config->cmd_queue);
243
244 for (i = 0; config->workspace_outputs && i < config->workspace_outputs->length; ++i) {
245 free_workspace_output(config->workspace_outputs->items[i]);
246 }
247 list_free(config->workspace_outputs);
248
249 for (i = 0; config->pid_workspaces && i < config->pid_workspaces->length; ++i) {
250 free_pid_workspace(config->pid_workspaces->items[i]);
251 }
252 list_free(config->pid_workspaces);
253
254 for (i = 0; config->criteria && i < config->criteria->length; ++i) {
255 free_criteria(config->criteria->items[i]);
256 }
257 list_free(config->criteria);
258
259 for (i = 0; config->no_focus && i < config->no_focus->length; ++i) {
260 free_criteria(config->no_focus->items[i]);
261 }
262 list_free(config->no_focus);
263
264 for (i = 0; config->input_configs && i < config->input_configs->length; ++i) {
265 free_input_config(config->input_configs->items[i]);
266 }
267 list_free(config->input_configs);
268
269 for (i = 0; config->output_configs && i < config->output_configs->length; ++i) {
270 free_output_config(config->output_configs->items[i]);
271 }
272 list_free(config->output_configs);
273
274 for (i = 0; config->command_policies && i < config->command_policies->length; ++i) {
275 free_command_policy(config->command_policies->items[i]);
276 }
277 list_free(config->command_policies);
278
279 for (i = 0; config->feature_policies && i < config->feature_policies->length; ++i) {
280 free_feature_policy(config->feature_policies->items[i]);
281 }
282 list_free(config->feature_policies);
283
284 list_free(config->active_bar_modifiers);
285 free_flat_list(config->config_chain);
286 free(config->font);
287 free(config->floating_scroll_up_cmd);
288 free(config->floating_scroll_down_cmd);
289 free(config->floating_scroll_left_cmd);
290 free(config->floating_scroll_right_cmd);
291 free(config);
292}
293
294
295static bool file_exists(const char *path) {
296 return path && access(path, R_OK) != -1;
297}
298
299static void config_defaults(struct sway_config *config) {
300 if (!(config->symbols = create_list())) goto cleanup;
301 if (!(config->modes = create_list())) goto cleanup;
302 if (!(config->bars = create_list())) goto cleanup;
303 if (!(config->workspace_outputs = create_list())) goto cleanup;
304 if (!(config->pid_workspaces = create_list())) goto cleanup;
305 if (!(config->criteria = create_list())) goto cleanup;
306 if (!(config->no_focus = create_list())) goto cleanup;
307 if (!(config->input_configs = create_list())) goto cleanup;
308 if (!(config->output_configs = create_list())) goto cleanup;
309
310 if (!(config->cmd_queue = create_list())) goto cleanup;
311
312 if (!(config->current_mode = malloc(sizeof(struct sway_mode)))) goto cleanup;
313 if (!(config->current_mode->name = malloc(sizeof("default")))) goto cleanup;
314 strcpy(config->current_mode->name, "default");
315 if (!(config->current_mode->bindings = create_list())) goto cleanup;
316 list_add(config->modes, config->current_mode);
317
318 config->floating_mod = 0;
319 config->dragging_key = M_LEFT_CLICK;
320 config->resizing_key = M_RIGHT_CLICK;
321 if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup;
322 if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup;
323 if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup;
324 if (!(config->floating_scroll_right_cmd = strdup(""))) goto cleanup;
325 config->default_layout = L_NONE;
326 config->default_orientation = L_NONE;
327 if (!(config->font = strdup("monospace 10"))) goto cleanup;
328 config->font_height = get_font_text_height(config->font);
329
330 // floating view
331 config->floating_maximum_width = 0;
332 config->floating_maximum_height = 0;
333 config->floating_minimum_width = 75;
334 config->floating_minimum_height = 50;
335
336 // Flags
337 config->focus_follows_mouse = true;
338 config->mouse_warping = true;
339 config->reloading = false;
340 config->active = false;
341 config->failed = false;
342 config->auto_back_and_forth = false;
343 config->seamless_mouse = true;
344 config->reading = false;
345 config->show_marks = true;
346
347 config->edge_gaps = true;
348 config->smart_gaps = false;
349 config->gaps_inner = 0;
350 config->gaps_outer = 0;
351
352 if (!(config->active_bar_modifiers = create_list())) goto cleanup;
353
354 if (!(config->config_chain = create_list())) goto cleanup;
355 config->current_config = NULL;
356
357 // borders
358 config->border = B_NORMAL;
359 config->floating_border = B_NORMAL;
360 config->border_thickness = 2;
361 config->floating_border_thickness = 2;
362 config->hide_edge_borders = E_NONE;
363
364 // border colors
365 config->border_colors.focused.border = 0x4C7899FF;
366 config->border_colors.focused.background = 0x285577FF;
367 config->border_colors.focused.text = 0xFFFFFFFF;
368 config->border_colors.focused.indicator = 0x2E9EF4FF;
369 config->border_colors.focused.child_border = 0x285577FF;
370
371 config->border_colors.focused_inactive.border = 0x333333FF;
372 config->border_colors.focused_inactive.background = 0x5F676AFF;
373 config->border_colors.focused_inactive.text = 0xFFFFFFFF;
374 config->border_colors.focused_inactive.indicator = 0x484E50FF;
375 config->border_colors.focused_inactive.child_border = 0x5F676AFF;
376
377 config->border_colors.unfocused.border = 0x333333FF;
378 config->border_colors.unfocused.background = 0x222222FF;
379 config->border_colors.unfocused.text = 0x888888FF;
380 config->border_colors.unfocused.indicator = 0x292D2EFF;
381 config->border_colors.unfocused.child_border = 0x222222FF;
382
383 config->border_colors.urgent.border = 0x2F343AFF;
384 config->border_colors.urgent.background = 0x900000FF;
385 config->border_colors.urgent.text = 0xFFFFFFFF;
386 config->border_colors.urgent.indicator = 0x900000FF;
387 config->border_colors.urgent.child_border = 0x900000FF;
388
389 config->border_colors.placeholder.border = 0x000000FF;
390 config->border_colors.placeholder.background = 0x0C0C0CFF;
391 config->border_colors.placeholder.text = 0xFFFFFFFF;
392 config->border_colors.placeholder.indicator = 0x000000FF;
393 config->border_colors.placeholder.child_border = 0x0C0C0CFF;
394
395 config->border_colors.background = 0xFFFFFFFF;
396
397 // Security
398 if (!(config->command_policies = create_list())) goto cleanup;
399 if (!(config->feature_policies = create_list())) goto cleanup;
400 if (!(config->ipc_policies = create_list())) goto cleanup;
401
402 return;
403cleanup:
404 sway_abort("Unable to allocate config structures");
405}
406
407static int compare_modifiers(const void *left, const void *right) {
408 uint32_t a = *(uint32_t *)left;
409 uint32_t b = *(uint32_t *)right;
410
411 return a - b;
412}
413
414void update_active_bar_modifiers() {
415 if (config->active_bar_modifiers->length > 0) {
416 list_free(config->active_bar_modifiers);
417 config->active_bar_modifiers = create_list();
418 }
419
420 struct bar_config *bar;
421 int i;
422 for (i = 0; i < config->bars->length; ++i) {
423 bar = config->bars->items[i];
424 if (strcmp(bar->mode, "hide") == 0 && strcmp(bar->hidden_state, "hide") == 0) {
425 if (list_seq_find(config->active_bar_modifiers, compare_modifiers, &bar->modifier) < 0) {
426 list_add(config->active_bar_modifiers, &bar->modifier);
427 }
428 }
429 }
430}
431
432static char *get_config_path(void) {
433 static const char *config_paths[] = {
434 "$HOME/.sway/config",
435 "$XDG_CONFIG_HOME/sway/config",
436 "$HOME/.i3/config",
437 "$XDG_CONFIG_HOME/i3/config",
438 SYSCONFDIR "/sway/config",
439 SYSCONFDIR "/i3/config",
440 };
441
442 if (!getenv("XDG_CONFIG_HOME")) {
443 char *home = getenv("HOME");
444 char *config_home = malloc(strlen(home) + strlen("/.config") + 1);
445 if (!config_home) {
446 sway_log(L_ERROR, "Unable to allocate $HOME/.config");
447 } else {
448 strcpy(config_home, home);
449 strcat(config_home, "/.config");
450 setenv("XDG_CONFIG_HOME", config_home, 1);
451 sway_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);
452 free(config_home);
453 }
454 }
455
456 wordexp_t p;
457 char *path;
458
459 int i;
460 for (i = 0; i < (int)(sizeof(config_paths) / sizeof(char *)); ++i) {
461 if (wordexp(config_paths[i], &p, 0) == 0) {
462 path = strdup(p.we_wordv[0]);
463 wordfree(&p);
464 if (file_exists(path)) {
465 return path;
466 }
467 }
468 }
469
470 return NULL; // Not reached
471}
472
473const char *current_config_path;
474
475static bool load_config(const char *path, struct sway_config *config) {
476 sway_log(L_INFO, "Loading config from %s", path);
477 current_config_path = path;
478
479 struct stat sb;
480 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
481 return false;
482 }
483
484 if (path == NULL) {
485 sway_log(L_ERROR, "Unable to find a config file!");
486 return false;
487 }
488
489 FILE *f = fopen(path, "r");
490 if (!f) {
491 sway_log(L_ERROR, "Unable to open %s for reading", path);
492 return false;
493 }
494
495 bool config_load_success = read_config(f, config);
496 fclose(f);
497
498 if (!config_load_success) {
499 sway_log(L_ERROR, "Error(s) loading config!");
500 }
501
502 current_config_path = NULL;
503 return true;
504}
505
506static int qstrcmp(const void* a, const void* b) {
507 return strcmp(*((char**) a), *((char**) b));
508}
509
510bool load_main_config(const char *file, bool is_active) {
511 input_init();
512
513 char *path;
514 if (file != NULL) {
515 path = strdup(file);
516 } else {
517 path = get_config_path();
518 }
519
520 struct sway_config *old_config = config;
521 config = calloc(1, sizeof(struct sway_config));
522 if (!config) {
523 sway_abort("Unable to allocate config");
524 }
525
526 config_defaults(config);
527 if (is_active) {
528 sway_log(L_DEBUG, "Performing configuration file reload");
529 config->reloading = true;
530 config->active = true;
531 }
532
533 config->current_config = path;
534 list_add(config->config_chain, path);
535
536 config->reading = true;
537
538 // Read security configs
539 bool success = true;
540 DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
541 if (!dir) {
542 sway_log(L_ERROR, "%s does not exist, sway will have no security configuration"
543 " and will probably be broken", SYSCONFDIR "/sway/security.d");
544 } else {
545 list_t *secconfigs = create_list();
546 char *base = SYSCONFDIR "/sway/security.d/";
547 struct dirent *ent = readdir(dir);
548 struct stat s;
549 while (ent != NULL) {
550 char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1);
551 strcpy(_path, base);
552 strcat(_path, ent->d_name);
553 lstat(_path, &s);
554 if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') {
555 list_add(secconfigs, _path);
556 }
557 else {
558 free(_path);
559 }
560 ent = readdir(dir);
561 }
562 closedir(dir);
563
564 list_qsort(secconfigs, qstrcmp);
565 for (int i = 0; i < secconfigs->length; ++i) {
566 char *_path = secconfigs->items[i];
567 if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || (((s.st_mode & 0777) != 0644) && (s.st_mode & 0777) != 0444)) {
568 sway_log(L_ERROR, "Refusing to load %s - it must be owned by root and mode 644 or 444", _path);
569 success = false;
570 } else {
571 success = success && load_config(_path, config);
572 }
573 }
574
575 free_flat_list(secconfigs);
576 }
577
578 success = success && load_config(path, config);
579
580 if (is_active) {
581 config->reloading = false;
582 }
583
584 if (old_config) {
585 free_config(old_config);
586 }
587 config->reading = false;
588
589 if (success) {
590 update_active_bar_modifiers();
591 }
592
593 return success;
594}
595
596static bool load_include_config(const char *path, const char *parent_dir, struct sway_config *config) {
597 // save parent config
598 const char *parent_config = config->current_config;
599
600 char *full_path = strdup(path);
601 int len = strlen(path);
602 if (len >= 1 && path[0] != '/') {
603 len = len + strlen(parent_dir) + 2;
604 full_path = malloc(len * sizeof(char));
605 if (!full_path) {
606 sway_log(L_ERROR, "Unable to allocate full path to included config");
607 return false;
608 }
609 snprintf(full_path, len, "%s/%s", parent_dir, path);
610 }
611
612 char *real_path = realpath(full_path, NULL);
613 free(full_path);
614
615 if (real_path == NULL) {
616 sway_log(L_DEBUG, "%s not found.", path);
617 return false;
618 }
619
620 // check if config has already been included
621 int j;
622 for (j = 0; j < config->config_chain->length; ++j) {
623 char *old_path = config->config_chain->items[j];
624 if (strcmp(real_path, old_path) == 0) {
625 sway_log(L_DEBUG, "%s already included once, won't be included again.", real_path);
626 free(real_path);
627 return false;
628 }
629 }
630
631 config->current_config = real_path;
632 list_add(config->config_chain, real_path);
633 int index = config->config_chain->length - 1;
634
635 if (!load_config(real_path, config)) {
636 free(real_path);
637 config->current_config = parent_config;
638 list_del(config->config_chain, index);
639 return false;
640 }
641
642 // restore current_config
643 config->current_config = parent_config;
644 return true;
645}
646
647bool load_include_configs(const char *path, struct sway_config *config) {
648 char *wd = getcwd(NULL, 0);
649 char *parent_path = strdup(config->current_config);
650 const char *parent_dir = dirname(parent_path);
651
652 if (chdir(parent_dir) < 0) {
653 free(parent_path);
654 free(wd);
655 return false;
656 }
657
658 wordexp_t p;
659
660 if (wordexp(path, &p, 0) < 0) {
661 free(parent_path);
662 free(wd);
663 return false;
664 }
665
666 char **w = p.we_wordv;
667 size_t i;
668 for (i = 0; i < p.we_wordc; ++i) {
669 load_include_config(w[i], parent_dir, config);
670 }
671 free(parent_path);
672 wordfree(&p);
673
674 // restore wd
675 if (chdir(wd) < 0) {
676 free(wd);
677 sway_log(L_ERROR, "failed to restore working directory");
678 return false;
679 }
680
681 free(wd);
682 return true;
683}
684
685struct cmd_results *check_security_config() {
686 if (!current_config_path || strncmp(SYSCONFDIR "/sway/security.d/", current_config_path,
687 strlen(SYSCONFDIR "/sway/security.d/")) != 0) {
688 return cmd_results_new(CMD_INVALID, "permit",
689 "This command is only permitted to run from " SYSCONFDIR "/sway/security.d/*");
690 }
691 return NULL;
692}
693
694bool read_config(FILE *file, struct sway_config *config) {
695 bool success = true;
696 enum cmd_status block = CMD_BLOCK_END;
697
698 int line_number = 0;
699 char *line;
700 while (!feof(file)) {
701 line = read_line(file);
702 if (!line) {
703 continue;
704 }
705 line_number++;
706 line = strip_whitespace(line);
707 if (line[0] == '#') {
708 free(line);
709 continue;
710 }
711 struct cmd_results *res;
712 if (block == CMD_BLOCK_COMMANDS) {
713 // Special case
714 res = config_commands_command(line);
715 } else {
716 res = config_command(line, block);
717 }
718 switch(res->status) {
719 case CMD_FAILURE:
720 case CMD_INVALID:
721 sway_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, line,
722 res->error, config->current_config);
723 success = false;
724 break;
725
726 case CMD_DEFER:
727 sway_log(L_DEBUG, "Defferring command `%s'", line);
728 list_add(config->cmd_queue, strdup(line));
729 break;
730
731 case CMD_BLOCK_MODE:
732 if (block == CMD_BLOCK_END) {
733 block = CMD_BLOCK_MODE;
734 } else {
735 sway_log(L_ERROR, "Invalid block '%s'", line);
736 }
737 break;
738
739 case CMD_BLOCK_INPUT:
740 if (block == CMD_BLOCK_END) {
741 block = CMD_BLOCK_INPUT;
742 } else {
743 sway_log(L_ERROR, "Invalid block '%s'", line);
744 }
745 break;
746
747 case CMD_BLOCK_BAR:
748 if (block == CMD_BLOCK_END) {
749 block = CMD_BLOCK_BAR;
750 } else {
751 sway_log(L_ERROR, "Invalid block '%s'", line);
752 }
753 break;
754
755 case CMD_BLOCK_BAR_COLORS:
756 if (block == CMD_BLOCK_BAR) {
757 block = CMD_BLOCK_BAR_COLORS;
758 } else {
759 sway_log(L_ERROR, "Invalid block '%s'", line);
760 }
761 break;
762
763 case CMD_BLOCK_COMMANDS:
764 if (block == CMD_BLOCK_END) {
765 block = CMD_BLOCK_COMMANDS;
766 } else {
767 sway_log(L_ERROR, "Invalid block '%s'", line);
768 }
769 break;
770
771 case CMD_BLOCK_IPC:
772 if (block == CMD_BLOCK_END) {
773 block = CMD_BLOCK_IPC;
774 } else {
775 sway_log(L_ERROR, "Invalid block '%s'", line);
776 }
777 break;
778
779 case CMD_BLOCK_IPC_EVENTS:
780 if (block == CMD_BLOCK_IPC) {
781 block = CMD_BLOCK_IPC_EVENTS;
782 } else {
783 sway_log(L_ERROR, "Invalid block '%s'", line);
784 }
785 break;
786
787 case CMD_BLOCK_END:
788 switch(block) {
789 case CMD_BLOCK_MODE:
790 sway_log(L_DEBUG, "End of mode block");
791 config->current_mode = config->modes->items[0];
792 block = CMD_BLOCK_END;
793 break;
794
795 case CMD_BLOCK_INPUT:
796 sway_log(L_DEBUG, "End of input block");
797 current_input_config = NULL;
798 block = CMD_BLOCK_END;
799 break;
800
801 case CMD_BLOCK_BAR:
802 sway_log(L_DEBUG, "End of bar block");
803 config->current_bar = NULL;
804 block = CMD_BLOCK_END;
805 break;
806
807 case CMD_BLOCK_BAR_COLORS:
808 sway_log(L_DEBUG, "End of bar colors block");
809 block = CMD_BLOCK_BAR;
810 break;
811
812 case CMD_BLOCK_COMMANDS:
813 sway_log(L_DEBUG, "End of commands block");
814 block = CMD_BLOCK_END;
815 break;
816
817 case CMD_BLOCK_IPC:
818 sway_log(L_DEBUG, "End of IPC block");
819 block = CMD_BLOCK_END;
820 break;
821
822 case CMD_BLOCK_IPC_EVENTS:
823 sway_log(L_DEBUG, "End of IPC events block");
824 block = CMD_BLOCK_IPC;
825 break;
826
827 case CMD_BLOCK_END:
828 sway_log(L_ERROR, "Unmatched }");
829 break;
830
831 default:;
832 }
833 default:;
834 }
835 free(line);
836 free_cmd_results(res);
837 }
838
839 return success;
840}
841
842int input_identifier_cmp(const void *item, const void *data) {
843 const struct input_config *ic = item;
844 const char *identifier = data;
845 return strcmp(ic->identifier, identifier);
846}
847
848int output_name_cmp(const void *item, const void *data) {
849 const struct output_config *output = item;
850 const char *name = data;
851
852 return strcmp(output->name, name);
853}
854
855void merge_input_config(struct input_config *dst, struct input_config *src) {
856 if (src->identifier) {
857 if (dst->identifier) {
858 free(dst->identifier);
859 }
860 dst->identifier = strdup(src->identifier);
861 }
862 if (src->accel_profile != INT_MIN) {
863 dst->accel_profile = src->accel_profile;
864 }
865 if (src->click_method != INT_MIN) {
866 dst->click_method = src->click_method;
867 }
868 if (src->drag_lock != INT_MIN) {
869 dst->drag_lock = src->drag_lock;
870 }
871 if (src->dwt != INT_MIN) {
872 dst->dwt = src->dwt;
873 }
874 if (src->middle_emulation != INT_MIN) {
875 dst->middle_emulation = src->middle_emulation;
876 }
877 if (src->natural_scroll != INT_MIN) {
878 dst->natural_scroll = src->natural_scroll;
879 }
880 if (src->pointer_accel != FLT_MIN) {
881 dst->pointer_accel = src->pointer_accel;
882 }
883 if (src->scroll_method != INT_MIN) {
884 dst->scroll_method = src->scroll_method;
885 }
886 if (src->send_events != INT_MIN) {
887 dst->send_events = src->send_events;
888 }
889 if (src->tap != INT_MIN) {
890 dst->tap = src->tap;
891 }
892}
893
894void merge_output_config(struct output_config *dst, struct output_config *src) {
895 if (src->name) {
896 if (dst->name) {
897 free(dst->name);
898 }
899 dst->name = strdup(src->name);
900 }
901 if (src->enabled != -1) {
902 dst->enabled = src->enabled;
903 }
904 if (src->width != -1) {
905 dst->width = src->width;
906 }
907 if (src->height != -1) {
908 dst->height = src->height;
909 }
910 if (src->x != -1) {
911 dst->x = src->x;
912 }
913 if (src->y != -1) {
914 dst->y = src->y;
915 }
916 if (src->scale != -1) {
917 dst->scale = src->scale;
918 }
919 if (src->background) {
920 if (dst->background) {
921 free(dst->background);
922 }
923 dst->background = strdup(src->background);
924 }
925 if (src->background_option) {
926 if (dst->background_option) {
927 free(dst->background_option);
928 }
929 dst->background_option = strdup(src->background_option);
930 }
931}
932
933static void invoke_swaybar(struct bar_config *bar) {
934 return; // TODO WLR
935 sway_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
936 // Pipe to communicate errors
937 int filedes[2];
938 if (pipe(filedes) == -1) {
939 sway_log(L_ERROR, "Pipe setup failed! Cannot fork into bar");
940 return;
941 }
942
943 bar->pid = fork();
944 if (bar->pid == 0) {
945 close(filedes[0]);
946 if (!bar->swaybar_command) {
947 char *const cmd[] = {
948 "swaybar",
949 "-b",
950 bar->id,
951 NULL,
952 };
953
954 close(filedes[1]);
955 execvp(cmd[0], cmd);
956 _exit(EXIT_SUCCESS);
957 } else {
958 // run custom swaybar
959 int len = strlen(bar->swaybar_command) + strlen(bar->id) + 5;
960 char *command = malloc(len * sizeof(char));
961 if (!command) {
962 const char msg[] = "Unable to allocate swaybar command string";
963 int len = sizeof(msg);
964 if (write(filedes[1], &len, sizeof(int))) {};
965 if (write(filedes[1], msg, len)) {};
966 close(filedes[1]);
967 _exit(EXIT_FAILURE);
968 }
969 snprintf(command, len, "%s -b %s", bar->swaybar_command, bar->id);
970
971 char *const cmd[] = {
972 "sh",
973 "-c",
974 command,
975 NULL,
976 };
977
978 close(filedes[1]);
979 execvp(cmd[0], cmd);
980 free(command);
981 _exit(EXIT_SUCCESS);
982 }
983 }
984 close(filedes[0]);
985 int len;
986 if(read(filedes[1], &len, sizeof(int)) == sizeof(int)) {
987 char *buf = malloc(len);
988 if(!buf) {
989 sway_log(L_ERROR, "Cannot allocate error string");
990 return;
991 }
992 if(read(filedes[1], buf, len)) {
993 sway_log(L_ERROR, "%s", buf);
994 }
995 free(buf);
996 }
997 close(filedes[1]);
998}
999
1000static void terminate_swaybar(pid_t pid) {
1001 return; // TODO WLR
1002 int ret = kill(pid, SIGTERM);
1003 if (ret != 0) {
1004 sway_log(L_ERROR, "Unable to terminate swaybar [pid: %d]", pid);
1005 } else {
1006 int status;
1007 waitpid(pid, &status, 0);
1008 }
1009}
1010
1011void terminate_swaybg(pid_t pid) {
1012 int ret = kill(pid, SIGTERM);
1013 if (ret != 0) {
1014 sway_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid);
1015 } else {
1016 int status;
1017 waitpid(pid, &status, 0);
1018 }
1019}
1020
1021static bool active_output(const char *name) {
1022 int i;
1023 swayc_t *cont = NULL;
1024 for (i = 0; i < root_container.children->length; ++i) {
1025 cont = root_container.children->items[i];
1026 if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) {
1027 return true;
1028 }
1029 }
1030
1031 return false;
1032}
1033
1034void load_swaybars() {
1035 // Check for bars
1036 list_t *bars = create_list();
1037 struct bar_config *bar = NULL;
1038 int i;
1039 for (i = 0; i < config->bars->length; ++i) {
1040 bar = config->bars->items[i];
1041 bool apply = false;
1042 if (bar->outputs) {
1043 int j;
1044 for (j = 0; j < bar->outputs->length; ++j) {
1045 char *o = bar->outputs->items[j];
1046 if (!strcmp(o, "*") || active_output(o)) {
1047 apply = true;
1048 break;
1049 }
1050 }
1051 } else {
1052 apply = true;
1053 }
1054 if (apply) {
1055 list_add(bars, bar);
1056 }
1057 }
1058
1059 for (i = 0; i < bars->length; ++i) {
1060 bar = bars->items[i];
1061 if (bar->pid != 0) {
1062 terminate_swaybar(bar->pid);
1063 }
1064 invoke_swaybar(bar);
1065 }
1066
1067 list_free(bars);
1068}
1069
1070char *do_var_replacement(char *str) {
1071 int i;
1072 char *find = str;
1073 while ((find = strchr(find, '$'))) {
1074 // Skip if escaped.
1075 if (find > str && find[-1] == '\\') {
1076 if (find == str + 1 || !(find > str + 1 && find[-2] == '\\')) {
1077 ++find;
1078 continue;
1079 }
1080 }
1081 // Find matching variable
1082 for (i = 0; i < config->symbols->length; ++i) {
1083 struct sway_variable *var = config->symbols->items[i];
1084 int vnlen = strlen(var->name);
1085 if (strncmp(find, var->name, vnlen) == 0) {
1086 int vvlen = strlen(var->value);
1087 char *newstr = malloc(strlen(str) - vnlen + vvlen + 1);
1088 if (!newstr) {
1089 sway_log(L_ERROR,
1090 "Unable to allocate replacement during variable expansion");
1091 break;
1092 }
1093 char *newptr = newstr;
1094 int offset = find - str;
1095 strncpy(newptr, str, offset);
1096 newptr += offset;
1097 strncpy(newptr, var->value, vvlen);
1098 newptr += vvlen;
1099 strcpy(newptr, find + vnlen);
1100 free(str);
1101 str = newstr;
1102 find = str + offset + vvlen;
1103 break;
1104 }
1105 }
1106 if (i == config->symbols->length) {
1107 ++find;
1108 }
1109 }
1110 return str;
1111}
1112
1113// the naming is intentional (albeit long): a workspace_output_cmp function
1114// would compare two structs in full, while this method only compares the
1115// workspace.
1116int workspace_output_cmp_workspace(const void *a, const void *b) {
1117 const struct workspace_output *wsa = a, *wsb = b;
1118 return lenient_strcmp(wsa->workspace, wsb->workspace);
1119}
1120
1121int sway_binding_cmp_keys(const void *a, const void *b) {
1122 const struct sway_binding *binda = a, *bindb = b;
1123
1124 // Count keys pressed for this binding. important so we check long before
1125 // short ones. for example mod+a+b before mod+a
1126 unsigned int moda = 0, modb = 0, i;
1127
1128 // Count how any modifiers are pressed
1129 for (i = 0; i < 8 * sizeof(binda->modifiers); ++i) {
1130 moda += (binda->modifiers & 1 << i) != 0;
1131 modb += (bindb->modifiers & 1 << i) != 0;
1132 }
1133 if (bindb->keys->length + modb != binda->keys->length + moda) {
1134 return (bindb->keys->length + modb) - (binda->keys->length + moda);
1135 }
1136
1137 // Otherwise compare keys
1138 if (binda->modifiers > bindb->modifiers) {
1139 return 1;
1140 } else if (binda->modifiers < bindb->modifiers) {
1141 return -1;
1142 }
1143 struct wlc_modifiers no_mods = { 0, 0 };
1144 for (int i = 0; i < binda->keys->length; i++) {
1145 xkb_keysym_t ka = *(xkb_keysym_t *)binda->keys->items[i],
1146 kb = *(xkb_keysym_t *)bindb->keys->items[i];
1147 if (binda->bindcode) {
1148 uint32_t *keycode = binda->keys->items[i];
1149 ka = wlc_keyboard_get_keysym_for_key(*keycode, &no_mods);
1150 }
1151
1152 if (bindb->bindcode) {
1153 uint32_t *keycode = bindb->keys->items[i];
1154 kb = wlc_keyboard_get_keysym_for_key(*keycode, &no_mods);
1155 }
1156
1157 if (ka > kb) {
1158 return 1;
1159 } else if (ka < kb) {
1160 return -1;
1161 }
1162 }
1163
1164 return 0;
1165}
1166
1167int sway_binding_cmp(const void *a, const void *b) {
1168 int cmp = 0;
1169 if ((cmp = sway_binding_cmp_keys(a, b)) != 0) {
1170 return cmp;
1171 }
1172 const struct sway_binding *binda = a, *bindb = b;
1173 return lenient_strcmp(binda->command, bindb->command);
1174}
1175
1176int sway_binding_cmp_qsort(const void *a, const void *b) {
1177 return sway_binding_cmp(*(void **)a, *(void **)b);
1178}
1179
1180void free_sway_binding(struct sway_binding *binding) {
1181 if (binding->keys) {
1182 for (int i = 0; i < binding->keys->length; i++) {
1183 free(binding->keys->items[i]);
1184 }
1185 list_free(binding->keys);
1186 }
1187 if (binding->command) {
1188 free(binding->command);
1189 }
1190 free(binding);
1191}
1192
1193int sway_mouse_binding_cmp_buttons(const void *a, const void *b) {
1194 const struct sway_mouse_binding *binda = a, *bindb = b;
1195 if (binda->button > bindb->button) {
1196 return 1;
1197 }
1198 if (binda->button < bindb->button) {
1199 return -1;
1200 }
1201 return 0;
1202}
1203
1204int sway_mouse_binding_cmp(const void *a, const void *b) {
1205 int cmp = 0;
1206 if ((cmp = sway_binding_cmp_keys(a, b)) != 0) {
1207 return cmp;
1208 }
1209 const struct sway_mouse_binding *binda = a, *bindb = b;
1210 return lenient_strcmp(binda->command, bindb->command);
1211}
1212
1213int sway_mouse_binding_cmp_qsort(const void *a, const void *b) {
1214 return sway_mouse_binding_cmp(*(void **)a, *(void **)b);
1215}
1216
1217void free_sway_mouse_binding(struct sway_mouse_binding *binding) {
1218 if (binding->command) {
1219 free(binding->command);
1220 }
1221 free(binding);
1222}
1223
1224struct sway_binding *sway_binding_dup(struct sway_binding *sb) {
1225 struct sway_binding *new_sb = malloc(sizeof(struct sway_binding));
1226 if (!new_sb) {
1227 return NULL;
1228 }
1229
1230 new_sb->order = sb->order;
1231 new_sb->modifiers = sb->modifiers;
1232 new_sb->command = strdup(sb->command);
1233
1234 new_sb->keys = create_list();
1235 int i;
1236 for (i = 0; i < sb->keys->length; ++i) {
1237 xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
1238 if (!key) {
1239 free_sway_binding(new_sb);
1240 return NULL;
1241 }
1242 *key = *(xkb_keysym_t *)sb->keys->items[i];
1243 list_add(new_sb->keys, key);
1244 }
1245
1246 return new_sb;
1247}
1248
1249struct bar_config *default_bar_config(void) {
1250 struct bar_config *bar = NULL;
1251 bar = malloc(sizeof(struct bar_config));
1252 if (!bar) {
1253 return NULL;
1254 }
1255 if (!(bar->mode = strdup("dock"))) goto cleanup;
1256 if (!(bar->hidden_state = strdup("hide"))) goto cleanup;
1257 bar->modifier = WLC_BIT_MOD_LOGO;
1258 bar->outputs = NULL;
1259 bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
1260 if (!(bar->bindings = create_list())) goto cleanup;
1261 if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup;
1262 bar->pango_markup = false;
1263 bar->swaybar_command = NULL;
1264 bar->font = NULL;
1265 bar->height = -1;
1266 bar->workspace_buttons = true;
1267 bar->wrap_scroll = false;
1268 bar->separator_symbol = NULL;
1269 bar->strip_workspace_numbers = false;
1270 bar->binding_mode_indicator = true;
1271#ifdef ENABLE_TRAY
1272 bar->tray_output = NULL;
1273 bar->icon_theme = NULL;
1274 bar->tray_padding = 2;
1275 bar->activate_button = 0x110; /* BTN_LEFT */
1276 bar->context_button = 0x111; /* BTN_RIGHT */
1277 bar->secondary_button = 0x112; /* BTN_MIDDLE */
1278#endif
1279 bar->verbose = false;
1280 bar->pid = 0;
1281 // set default colors
1282 if (!(bar->colors.background = strndup("#000000ff", 9))) goto cleanup;
1283 if (!(bar->colors.statusline = strndup("#ffffffff", 9))) goto cleanup;
1284 if (!(bar->colors.separator = strndup("#666666ff", 9))) goto cleanup;
1285 if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) goto cleanup;
1286 if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) goto cleanup;
1287 if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
1288 if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) goto cleanup;
1289 if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) goto cleanup;
1290 if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
1291 if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) goto cleanup;
1292 if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) goto cleanup;
1293 if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) goto cleanup;
1294 if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) goto cleanup;
1295 if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) goto cleanup;
1296 if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
1297 // if the following colors stay undefined, they fall back to background,
1298 // statusline, separator and urgent_workspace_*.
1299 bar->colors.focused_background = NULL;
1300 bar->colors.focused_statusline = NULL;
1301 bar->colors.focused_separator = NULL;
1302 bar->colors.binding_mode_border = NULL;
1303 bar->colors.binding_mode_bg = NULL;
1304 bar->colors.binding_mode_text = NULL;
1305
1306 list_add(config->bars, bar);
1307
1308 return bar;
1309
1310cleanup:
1311 free_bar(bar);
1312 return NULL;
1313}
diff --git a/sway/old/container.c b/sway/old/container.c
deleted file mode 100644
index 82c0d877..00000000
--- a/sway/old/container.c
+++ /dev/null
@@ -1,1024 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <ctype.h>
3#include <stdlib.h>
4#include <stdbool.h>
5#include <strings.h>
6#include <string.h>
7#include <wlr/types/wlr_box.h>
8#include <wlr/types/wlr_output.h>
9#include "sway/config.h"
10#include "sway/container.h"
11#include "sway/workspace.h"
12#include "sway/focus.h"
13#include "sway/border.h"
14#include "sway/layout.h"
15#include "sway/input_state.h"
16#include "sway/ipc-server.h"
17#include "sway/output.h"
18#include "sway/view.h"
19#include "log.h"
20#include "stringop.h"
21
22#define ASSERT_NONNULL(PTR) \
23 sway_assert (PTR, #PTR "must be non-null")
24
25static swayc_t *new_swayc(enum swayc_types type) {
26 // next id starts at 1 because 0 is assigned to root_container in layout.c
27 static size_t next_id = 1;
28 swayc_t *c = calloc(1, sizeof(swayc_t));
29 if (!c) {
30 return NULL;
31 }
32 c->id = next_id++;
33 c->handle = -1;
34 c->gaps = -1;
35 c->layout = L_NONE;
36 c->workspace_layout = L_NONE;
37 c->type = type;
38 c->nb_master = 1;
39 c->nb_slave_groups = 1;
40 if (type != C_VIEW) {
41 c->children = create_list();
42 }
43 return c;
44}
45
46static void free_swayc(swayc_t *cont) {
47 if (!ASSERT_NONNULL(cont)) {
48 return;
49 }
50 if (cont->children) {
51 // remove children until there are no more, free_swayc calls
52 // remove_child, which removes child from this container
53 while (cont->children->length) {
54 free_swayc(cont->children->items[0]);
55 }
56 list_free(cont->children);
57 }
58 if (cont->unmanaged) {
59 list_free(cont->unmanaged);
60 }
61 if (cont->floating) {
62 while (cont->floating->length) {
63 free_swayc(cont->floating->items[0]);
64 }
65 list_free(cont->floating);
66 }
67 if (cont->marks) {
68 list_foreach(cont->marks, free);
69 list_free(cont->marks);
70 }
71 if (cont->parent) {
72 remove_child(cont);
73 }
74 if (cont->name) {
75 free(cont->name);
76 }
77 if (cont->class) {
78 free(cont->class);
79 }
80 if (cont->instance) {
81 free(cont->instance);
82 }
83 if (cont->app_id) {
84 free(cont->app_id);
85 }
86 if (cont->bg_pid != 0) {
87 terminate_swaybg(cont->bg_pid);
88 }
89 if (cont->border) {
90 if (cont->border->buffer) {
91 free(cont->border->buffer);
92 }
93 free(cont->border);
94 }
95 free(cont);
96}
97
98static void update_root_geometry() {
99 int width = 0;
100 int height = 0;
101 swayc_t *child;
102 int child_width;
103 int child_height;
104
105 for (int i = 0; i < root_container.children->length; ++i) {
106 child = root_container.children->items[i];
107 child_width = child->width + child->x;
108 child_height = child->height + child->y;
109 if (child_width > width) {
110 width = child_width;
111 }
112
113 if (child_height > height) {
114 height = child_height;
115 }
116 }
117
118 root_container.width = width;
119 root_container.height = height;
120}
121
122// New containers
123
124swayc_t *new_output(struct sway_output *sway_output) {
125 struct wlr_box size;
126 wlr_output_effective_resolution(sway_output->wlr_output,
127 &size.width, &size.height);
128 const char *name = sway_output->wlr_output->name;
129 // Find current outputs to see if this already exists
130 {
131 int i, len = root_container.children->length;
132 for (i = 0; i < len; ++i) {
133 swayc_t *op = root_container.children->items[i];
134 const char *op_name = op->name;
135 if (op_name && name && strcmp(op_name, name) == 0) {
136 sway_log(L_DEBUG, "restoring output %p: %s",
137 sway_output, op_name);
138 return op;
139 }
140 }
141 }
142
143 struct output_config *oc = NULL, *all = NULL;
144 int i;
145 for (i = 0; i < config->output_configs->length; ++i) {
146 struct output_config *cur = config->output_configs->items[i];
147 if (strcasecmp(name, cur->name) == 0) {
148 sway_log(L_DEBUG, "Matched output config for %s", name);
149 oc = cur;
150 }
151 if (strcasecmp("*", cur->name) == 0) {
152 sway_log(L_DEBUG, "Matched wildcard output config for %s", name);
153 all = cur;
154 }
155
156 if (oc && all) {
157 break;
158 }
159 }
160
161 if (!oc) {
162 oc = all;
163 }
164
165 if (oc && !oc->enabled) {
166 return NULL;
167 }
168
169 swayc_t *output = new_swayc(C_OUTPUT);
170 output->_handle.output = sway_output;
171 output->name = name ? strdup(name) : NULL;
172 output->width = size.width;
173 output->height = size.width;
174 output->unmanaged = create_list();
175 output->bg_pid = 0;
176
177 apply_output_config(oc, output);
178 add_child(&root_container, output);
179 load_swaybars();
180
181 // Create workspace
182 char *ws_name = NULL;
183 swayc_t *ws = NULL;
184
185 if (name) {
186 for (i = 0; i < config->workspace_outputs->length; ++i) {
187 struct workspace_output *wso = config->workspace_outputs->items[i];
188 if (strcasecmp(wso->output, name) == 0) {
189 sway_log(L_DEBUG, "Matched workspace to output: %s for %s", wso->workspace, wso->output);
190 // Check if any other workspaces are using this name
191 if ((ws = workspace_by_name(wso->workspace))) {
192 // if yes, move those to this output, because they should be here
193 move_workspace_to(ws, output);
194 } else if (!ws_name) {
195 // set a workspace name in case we need to create a default one
196 ws_name = strdup(wso->workspace);
197 }
198 }
199 }
200 }
201
202 if (output->children->length == 0) {
203 if (!ws_name) {
204 ws_name = workspace_next_name(output->name);
205 }
206 // create and initialize default workspace
207 sway_log(L_DEBUG, "Creating default workspace %s", ws_name);
208 ws = new_workspace(output, ws_name);
209 ws->is_focused = true;
210 } else {
211 sort_workspaces(output);
212 set_focused_container(output->children->items[0]);
213 }
214
215 free(ws_name);
216 update_root_geometry();
217 return output;
218}
219
220swayc_t *new_workspace(swayc_t *output, const char *name) {
221 if (!ASSERT_NONNULL(output)) {
222 return NULL;
223 }
224 sway_log(L_DEBUG, "Added workspace %s for output %u", name, (unsigned int)output->handle);
225 swayc_t *workspace = new_swayc(C_WORKSPACE);
226
227 workspace->prev_layout = L_NONE;
228 workspace->layout = default_layout(output);
229 workspace->workspace_layout = default_layout(output);
230
231 workspace->x = output->x;
232 workspace->y = output->y;
233 workspace->width = output->width;
234 workspace->height = output->height;
235 workspace->name = !name ? NULL : strdup(name);
236 workspace->visible = false;
237 workspace->floating = create_list();
238
239 add_child(output, workspace);
240 sort_workspaces(output);
241
242 return workspace;
243}
244
245swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) {
246 if (!ASSERT_NONNULL(child)
247 && !sway_assert(!child->is_floating, "cannot create container around floating window")) {
248 return NULL;
249 }
250 swayc_t *cont = new_swayc(C_CONTAINER);
251
252 sway_log(L_DEBUG, "creating container %p around %p", cont, child);
253
254 cont->prev_layout = L_NONE;
255 cont->layout = layout;
256 cont->width = child->width;
257 cont->height = child->height;
258 cont->x = child->x;
259 cont->y = child->y;
260 cont->visible = child->visible;
261 cont->cached_geometry = child->cached_geometry;
262 cont->gaps = child->gaps;
263
264 /* Container inherits all of workspaces children, layout and whatnot */
265 if (child->type == C_WORKSPACE) {
266 swayc_t *workspace = child;
267 // reorder focus
268 cont->focused = workspace->focused;
269 workspace->focused = cont;
270 // set all children focu to container
271 int i;
272 for (i = 0; i < workspace->children->length; ++i) {
273 ((swayc_t *)workspace->children->items[i])->parent = cont;
274 }
275 // Swap children
276 list_t *tmp_list = workspace->children;
277 workspace->children = cont->children;
278 cont->children = tmp_list;
279 // add container to workspace chidren
280 add_child(workspace, cont);
281 // give them proper layouts
282 cont->layout = workspace->workspace_layout;
283 cont->prev_layout = workspace->prev_layout;
284 /* TODO: might break shit in move_container!!! workspace->layout = layout; */
285 set_focused_container_for(workspace, get_focused_view(workspace));
286 } else { // Or is built around container
287 swayc_t *parent = replace_child(child, cont);
288 if (parent) {
289 add_child(cont, child);
290 }
291 }
292 return cont;
293}
294
295swayc_t *new_view(swayc_t *sibling, struct sway_view *view) {
296 if (!ASSERT_NONNULL(sibling)) {
297 return NULL;
298 }
299 const char *title = view->iface.get_prop(view, VIEW_PROP_TITLE);
300 swayc_t *swayc = new_swayc(C_VIEW);
301 sway_log(L_DEBUG, "Adding new view %p:%s to container %p %d",
302 swayc, title, sibling, sibling ? sibling->type : 0);
303 // Setup values
304 swayc->_handle.view = view;
305
306 swayc->name = title ? strdup(title) : NULL;
307
308 const char *class = view->iface.get_prop(view, VIEW_PROP_CLASS);
309 swayc->class = class ? strdup(class) : NULL;
310
311 const char *instance = view->iface.get_prop(view, VIEW_PROP_INSTANCE);
312 swayc->instance = instance ? strdup(instance) : NULL;
313
314 const char *app_id = view->iface.get_prop(view, VIEW_PROP_APP_ID);
315 swayc->app_id = app_id ? strdup(app_id) : NULL;
316
317 swayc->visible = true;
318 swayc->is_focused = true;
319 swayc->sticky = false;
320 swayc->width = 0;
321 swayc->height = 0;
322 swayc->desired_width = -1;
323 swayc->desired_height = -1;
324 // setup border
325 swayc->border_type = config->border;
326 swayc->border_thickness = config->border_thickness;
327
328 swayc->is_floating = false;
329
330 if (sibling->type == C_WORKSPACE) {
331 // Case of focused workspace, just create as child of it
332 add_child(sibling, swayc);
333 } else {
334 // Regular case, create as sibling of current container
335 add_sibling(sibling, swayc);
336 }
337 return swayc;
338}
339
340swayc_t *new_floating_view(wlc_handle handle) {
341 if (swayc_active_workspace() == NULL) {
342 return NULL;
343 }
344 const char *title = wlc_view_get_title(handle);
345 swayc_t *view = new_swayc(C_VIEW);
346 sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%x:%s as a floating view",
347 handle, wlc_view_get_type(handle), title);
348 // Setup values
349 view->handle = handle;
350 view->name = title ? strdup(title) : NULL;
351 const char *class = wlc_view_get_class(handle);
352 view->class = class ? strdup(class) : NULL;
353 const char *instance = wlc_view_get_instance(handle);
354 view->instance = instance ? strdup(instance) : NULL;
355 const char *app_id = wlc_view_get_app_id(handle);
356 view->app_id = app_id ? strdup(app_id) : NULL;
357 view->visible = true;
358 view->sticky = false;
359
360 // Set the geometry of the floating view
361 const struct wlc_geometry *geometry = wlc_view_get_geometry(handle);
362
363 // give it requested geometry, but place in center if possible
364 // in top left otherwise
365 if (geometry->size.w != 0) {
366 view->x = (swayc_active_workspace()->width - geometry->size.w) / 2;
367 } else {
368 view->x = 0;
369 }
370 if (geometry->size.h != 0) {
371 view->y = (swayc_active_workspace()->height - geometry->size.h) / 2;
372 } else {
373 view->y = 0;
374 }
375
376 view->width = geometry->size.w;
377 view->height = geometry->size.h;
378
379 view->desired_width = view->width;
380 view->desired_height = view->height;
381
382 // setup border
383 view->border_type = config->floating_border;
384 view->border_thickness = config->floating_border_thickness;
385
386 view->is_floating = true;
387
388 // Case of focused workspace, just create as child of it
389 list_add(swayc_active_workspace()->floating, view);
390 view->parent = swayc_active_workspace();
391 if (swayc_active_workspace()->focused == NULL) {
392 set_focused_container_for(swayc_active_workspace(), view);
393 }
394 return view;
395}
396
397void floating_view_sane_size(swayc_t *view) {
398 // floating_minimum is used as sane value.
399 // floating_maximum has priority in case of conflict
400 // TODO: implement total_outputs_dimensions()
401 if (config->floating_minimum_height != -1 &&
402 view->desired_height < config->floating_minimum_height) {
403 view->desired_height = config->floating_minimum_height;
404 }
405 if (config->floating_minimum_width != -1 &&
406 view->desired_width < config->floating_minimum_width) {
407 view->desired_width = config->floating_minimum_width;
408 }
409
410 // if 0 do not resize, only enforce max value
411 if (config->floating_maximum_height == 0) {
412 // Missing total_outputs_dimensions() using swayc_active_workspace()
413 config->floating_maximum_height = swayc_active_workspace()->height;
414
415 } else if (config->floating_maximum_height != -1 &&
416 view->desired_height > config->floating_maximum_height) {
417 view->desired_height = config->floating_maximum_height;
418 }
419
420 // if 0 do not resize, only enforce max value
421 if (config->floating_maximum_width == 0) {
422 // Missing total_outputs_dimensions() using swayc_active_workspace()
423 config->floating_maximum_width = swayc_active_workspace()->width;
424
425 } else if (config->floating_maximum_width != -1 &&
426 view->desired_width > config->floating_maximum_width) {
427 view->desired_width = config->floating_maximum_width;
428 }
429
430 sway_log(L_DEBUG, "Sane values for view to %d x %d @ %.f, %.f",
431 view->desired_width, view->desired_height, view->x, view->y);
432
433 return;
434}
435
436
437// Destroy container
438
439swayc_t *destroy_output(swayc_t *output) {
440 if (!ASSERT_NONNULL(output)) {
441 return NULL;
442 }
443 if (output->children->length > 0) {
444 // TODO save workspaces when there are no outputs.
445 // TODO also check if there will ever be no outputs except for exiting
446 // program
447 if (root_container.children->length > 1) {
448 int p = root_container.children->items[0] == output;
449 // Move workspace from this output to another output
450 while (output->children->length) {
451 swayc_t *child = output->children->items[0];
452 remove_child(child);
453 add_child(root_container.children->items[p], child);
454 }
455 sort_workspaces(root_container.children->items[p]);
456 update_visibility(root_container.children->items[p]);
457 arrange_windows(root_container.children->items[p], -1, -1);
458 }
459 }
460 sway_log(L_DEBUG, "OUTPUT: Destroying output '%" PRIuPTR "'", output->handle);
461 free_swayc(output);
462 update_root_geometry();
463 return &root_container;
464}
465
466swayc_t *destroy_workspace(swayc_t *workspace) {
467 if (!ASSERT_NONNULL(workspace)) {
468 return NULL;
469 }
470
471 // Do not destroy this if it's the last workspace on this output
472 swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
473 if (output && output->children->length == 1) {
474 return NULL;
475 }
476
477 swayc_t *parent = workspace->parent;
478 // destroy the WS if there are no children
479 if (workspace->children->length == 0 && workspace->floating->length == 0) {
480 sway_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
481 ipc_event_workspace(workspace, NULL, "empty");
482 } else {
483 // Move children to a different workspace on this output
484 swayc_t *new_workspace = NULL;
485 int i;
486 for(i = 0; i < output->children->length; i++) {
487 if(output->children->items[i] != workspace) {
488 break;
489 }
490 }
491 new_workspace = output->children->items[i];
492
493 sway_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'",
494 workspace->name, new_workspace->name);
495
496 for(i = 0; i < workspace->children->length; i++) {
497 move_container_to(workspace->children->items[i], new_workspace);
498 }
499
500 for(i = 0; i < workspace->floating->length; i++) {
501 move_container_to(workspace->floating->items[i], new_workspace);
502 }
503 }
504
505 free_swayc(workspace);
506 return parent;
507}
508
509swayc_t *destroy_container(swayc_t *container) {
510 if (!ASSERT_NONNULL(container)) {
511 return NULL;
512 }
513 while (container->children->length == 0 && container->type == C_CONTAINER) {
514 sway_log(L_DEBUG, "Container: Destroying container '%p'", container);
515 swayc_t *parent = container->parent;
516 free_swayc(container);
517 container = parent;
518 }
519 return container;
520}
521
522swayc_t *destroy_view(swayc_t *view) {
523 if (!ASSERT_NONNULL(view)) {
524 return NULL;
525 }
526 sway_log(L_DEBUG, "Destroying view '%p'", view);
527 swayc_t *parent = view->parent;
528 free_swayc(view);
529
530 // Destroy empty containers
531 if (parent && parent->type == C_CONTAINER) {
532 return destroy_container(parent);
533 }
534 return parent;
535}
536
537// Container lookup
538
539
540swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data) {
541 if (!container->children) {
542 return NULL;
543 }
544 // Special case for checking floating stuff
545 int i;
546 if (container->type == C_WORKSPACE) {
547 for (i = 0; i < container->floating->length; ++i) {
548 swayc_t *child = container->floating->items[i];
549 if (test(child, data)) {
550 return child;
551 }
552 }
553 }
554 for (i = 0; i < container->children->length; ++i) {
555 swayc_t *child = container->children->items[i];
556 if (test(child, data)) {
557 return child;
558 } else {
559 swayc_t *res = swayc_by_test(child, test, data);
560 if (res) {
561 return res;
562 }
563 }
564 }
565 return NULL;
566}
567
568static bool test_name(swayc_t *view, void *data) {
569 if (!view || !view->name) {
570 return false;
571 }
572 return strcmp(view->name, data) == 0;
573}
574
575swayc_t *swayc_by_name(const char *name) {
576 return swayc_by_test(&root_container, test_name, (void *)name);
577}
578
579swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) {
580 if (!ASSERT_NONNULL(container)) {
581 return NULL;
582 }
583 if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
584 return NULL;
585 }
586 do {
587 container = container->parent;
588 } while (container && container->type != type);
589 return container;
590}
591
592swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts layout) {
593 if (!ASSERT_NONNULL(container)) {
594 return NULL;
595 }
596 if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
597 return NULL;
598 }
599 do {
600 container = container->parent;
601 } while (container && container->layout != layout);
602 return container;
603}
604
605swayc_t *swayc_focus_by_type(swayc_t *container, enum swayc_types type) {
606 if (!ASSERT_NONNULL(container)) {
607 return NULL;
608 }
609 if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
610 return NULL;
611 }
612 do {
613 container = container->focused;
614 } while (container && container->type != type);
615 return container;
616}
617
618swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts layout) {
619 if (!ASSERT_NONNULL(container)) {
620 return NULL;
621 }
622 if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
623 return NULL;
624 }
625 do {
626 container = container->focused;
627 } while (container && container->layout != layout);
628 return container;
629}
630
631
632static swayc_t *_swayc_by_handle_helper(wlc_handle handle, swayc_t *parent) {
633 if (!parent || !parent->children) {
634 return NULL;
635 }
636 int i, len;
637 swayc_t **child;
638 if (parent->type == C_WORKSPACE) {
639 len = parent->floating->length;
640 child = (swayc_t **)parent->floating->items;
641 for (i = 0; i < len; ++i, ++child) {
642 if ((*child)->handle == handle) {
643 return *child;
644 }
645 }
646 }
647
648 len = parent->children->length;
649 child = (swayc_t**)parent->children->items;
650 for (i = 0; i < len; ++i, ++child) {
651 if ((*child)->handle == handle) {
652 return *child;
653 } else {
654 swayc_t *res;
655 if ((res = _swayc_by_handle_helper(handle, *child))) {
656 return res;
657 }
658 }
659 }
660 return NULL;
661}
662
663swayc_t *swayc_by_handle(wlc_handle handle) {
664 return _swayc_by_handle_helper(handle, &root_container);
665}
666
667swayc_t *swayc_active_output(void) {
668 return root_container.focused;
669}
670
671swayc_t *swayc_active_workspace(void) {
672 return root_container.focused ? root_container.focused->focused : NULL;
673}
674
675swayc_t *swayc_active_workspace_for(swayc_t *cont) {
676 if (!cont) {
677 return NULL;
678 }
679 switch (cont->type) {
680 case C_ROOT:
681 cont = cont->focused;
682 /* Fallthrough */
683
684 case C_OUTPUT:
685 cont = cont ? cont->focused : NULL;
686 /* Fallthrough */
687
688 case C_WORKSPACE:
689 return cont;
690
691 default:
692 return swayc_parent_by_type(cont, C_WORKSPACE);
693 }
694}
695
696static bool pointer_test(swayc_t *view, void *_origin) {
697 const struct wlc_point *origin = _origin;
698 // Determine the output that the view is under
699 swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT);
700 if (origin->x >= view->x && origin->y >= view->y
701 && origin->x < view->x + view->width && origin->y < view->y + view->height
702 && view->visible && parent == root_container.focused) {
703 return true;
704 }
705 return false;
706}
707
708swayc_t *container_under_pointer(void) {
709 // root.output->workspace
710 if (!root_container.focused) {
711 return NULL;
712 }
713 swayc_t *lookup = root_container.focused;
714 // Case of empty workspace
715 if (lookup->children && !lookup->unmanaged) {
716 return NULL;
717 }
718 double x, y;
719 wlc_pointer_get_position_v2(&x, &y);
720 struct wlc_point origin = { .x = x, .y = y };
721
722 while (lookup && lookup->type != C_VIEW) {
723 int i;
724 int len;
725 for (int _i = 0; lookup->unmanaged && _i < lookup->unmanaged->length; ++_i) {
726 wlc_handle *handle = lookup->unmanaged->items[_i];
727 const struct wlc_geometry *geo = wlc_view_get_geometry(*handle);
728 if (origin.x >= geo->origin.x && origin.y >= geo->origin.y
729 && origin.x < geo->origin.x + (int)geo->size.w
730 && origin.y < geo->origin.y + (int)geo->size.h) {
731 // Hack: we force focus upon unmanaged views here
732 wlc_view_focus(*handle);
733 return NULL;
734 }
735 }
736 // if tabbed/stacked go directly to focused container, otherwise search
737 // children
738 if (lookup->layout == L_TABBED || lookup->layout == L_STACKED) {
739 lookup = lookup->focused;
740 continue;
741 }
742 // if workspace, search floating
743 if (lookup->type == C_WORKSPACE) {
744 i = len = lookup->floating->length;
745 bool got_floating = false;
746 while (--i > -1) {
747 if (pointer_test(lookup->floating->items[i], &origin)) {
748 lookup = lookup->floating->items[i];
749 got_floating = true;
750 break;
751 }
752 }
753 if (got_floating) {
754 continue;
755 }
756 }
757 // search children
758 len = lookup->children->length;
759 for (i = 0; i < len; ++i) {
760 if (pointer_test(lookup->children->items[i], &origin)) {
761 lookup = lookup->children->items[i];
762 break;
763 }
764 }
765 // when border and titles are done, this could happen
766 if (i == len) {
767 break;
768 }
769 }
770 return lookup;
771}
772
773swayc_t *container_find(swayc_t *container, bool (*f)(swayc_t *, const void *), const void *data) {
774 if (container->children == NULL || container->children->length == 0) {
775 return NULL;
776 }
777
778 swayc_t *con;
779 if (container->type == C_WORKSPACE) {
780 for (int i = 0; i < container->floating->length; ++i) {
781 con = container->floating->items[i];
782 if (f(con, data)) {
783 return con;
784 }
785 con = container_find(con, f, data);
786 if (con != NULL) {
787 return con;
788 }
789 }
790 }
791
792 for (int i = 0; i < container->children->length; ++i) {
793 con = container->children->items[i];
794 if (f(con, data)) {
795 return con;
796 }
797
798 con = container_find(con, f, data);
799 if (con != NULL) {
800 return con;
801 }
802 }
803
804 return NULL;
805}
806
807// Container information
808
809bool swayc_is_fullscreen(swayc_t *view) {
810 return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN);
811}
812
813bool swayc_is_active(swayc_t *view) {
814 return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_ACTIVATED);
815}
816
817bool swayc_is_parent_of(swayc_t *parent, swayc_t *child) {
818 while (child != &root_container) {
819 child = child->parent;
820 if (child == parent) {
821 return true;
822 }
823 }
824 return false;
825}
826
827bool swayc_is_child_of(swayc_t *child, swayc_t *parent) {
828 return swayc_is_parent_of(parent, child);
829}
830
831bool swayc_is_empty_workspace(swayc_t *container) {
832 return container->type == C_WORKSPACE && container->children->length == 0;
833}
834
835int swayc_gap(swayc_t *container) {
836 if (container->type == C_VIEW || container->type == C_CONTAINER) {
837 return container->gaps >= 0 ? container->gaps : config->gaps_inner;
838 } else if (container->type == C_WORKSPACE) {
839 int base = container->gaps >= 0 ? container->gaps : config->gaps_outer;
840 if (config->edge_gaps && !(config->smart_gaps && container->children->length == 1)) {
841 // the inner gap is created via a margin around each window which
842 // is half the gap size, so the workspace also needs half a gap
843 // size to make the outermost gap the same size (excluding the
844 // actual "outer gap" size which is handled independently)
845 return base + config->gaps_inner / 2;
846 } else if (config->smart_gaps && container->children->length == 1) {
847 return 0;
848 } else {
849 return base;
850 }
851 } else {
852 return 0;
853 }
854}
855
856// Mapping
857
858void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) {
859 if (container) {
860 int i;
861 if (container->children) {
862 for (i = 0; i < container->children->length; ++i) {
863 swayc_t *child = container->children->items[i];
864 container_map(child, f, data);
865 }
866 }
867 if (container->floating) {
868 for (i = 0; i < container->floating->length; ++i) {
869 swayc_t *child = container->floating->items[i];
870 container_map(child, f, data);
871 }
872 }
873 f(container, data);
874 }
875}
876
877void update_visibility_output(swayc_t *container, wlc_handle output) {
878 // Inherit visibility
879 swayc_t *parent = container->parent;
880 container->visible = parent->visible;
881 // special cases where visibility depends on focus
882 if (parent->type == C_OUTPUT || parent->layout == L_TABBED ||
883 parent->layout == L_STACKED) {
884 container->visible = parent->focused == container && parent->visible;
885 }
886 // Set visibility and output for view
887 if (container->type == C_VIEW) {
888 wlc_view_set_output(container->handle, output);
889 wlc_view_set_mask(container->handle, container->visible ? VISIBLE : 0);
890 }
891 // Update visibility for children
892 else {
893 if (container->children) {
894 int i, len = container->children->length;
895 for (i = 0; i < len; ++i) {
896 update_visibility_output(container->children->items[i], output);
897 }
898 }
899 if (container->floating) {
900 int i, len = container->floating->length;
901 for (i = 0; i < len; ++i) {
902 update_visibility_output(container->floating->items[i], output);
903 }
904 }
905 }
906}
907
908void update_visibility(swayc_t *container) {
909 if (!container) return;
910 switch (container->type) {
911 case C_ROOT:
912 container->visible = true;
913 if (container->children) {
914 int i, len = container->children->length;
915 for (i = 0; i < len; ++i) {
916 update_visibility(container->children->items[i]);
917 }
918 }
919 return;
920
921 case C_OUTPUT:
922 container->visible = true;
923 if (container->children) {
924 int i, len = container->children->length;
925 for (i = 0; i < len; ++i) {
926 update_visibility_output(container->children->items[i], container->handle);
927 }
928 }
929 return;
930
931 default:
932 {
933 swayc_t *op = swayc_parent_by_type(container, C_OUTPUT);
934 update_visibility_output(container, op->handle);
935 }
936 }
937}
938
939void set_gaps(swayc_t *view, void *_data) {
940 int *data = _data;
941 if (!ASSERT_NONNULL(view)) {
942 return;
943 }
944 if (view->type == C_WORKSPACE || view->type == C_VIEW) {
945 view->gaps = *data;
946 }
947}
948
949void add_gaps(swayc_t *view, void *_data) {
950 int *data = _data;
951 if (!ASSERT_NONNULL(view)) {
952 return;
953 }
954 if (view->type == C_WORKSPACE || view->type == C_VIEW) {
955 if ((view->gaps += *data) < 0) {
956 view->gaps = 0;
957 }
958 }
959}
960
961static void close_view(swayc_t *container, void *data) {
962 if (container->type == C_VIEW) {
963 wlc_view_close(container->handle);
964 }
965}
966
967void close_views(swayc_t *container) {
968 container_map(container, close_view, NULL);
969}
970
971swayc_t *swayc_tabbed_stacked_ancestor(swayc_t *view) {
972 swayc_t *parent = NULL;
973 if (!ASSERT_NONNULL(view)) {
974 return NULL;
975 }
976 while (view->type != C_WORKSPACE && view->parent && view->parent->type != C_WORKSPACE) {
977 view = view->parent;
978 if (view->layout == L_TABBED || view->layout == L_STACKED) {
979 parent = view;
980 }
981 }
982
983 return parent;
984}
985
986swayc_t *swayc_tabbed_stacked_parent(swayc_t *con) {
987 if (!ASSERT_NONNULL(con)) {
988 return NULL;
989 }
990 if (con->parent && (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED)) {
991 return con->parent;
992 }
993 return NULL;
994}
995
996swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) {
997 // if layout change modifies the auto layout's major axis, swap width and height
998 // to preserve current ratios.
999 if (is_auto_layout(layout) && is_auto_layout(container->layout)) {
1000 enum swayc_layouts prev_major =
1001 container->layout == L_AUTO_LEFT || container->layout == L_AUTO_RIGHT
1002 ? L_HORIZ : L_VERT;
1003 enum swayc_layouts new_major =
1004 layout == L_AUTO_LEFT || layout == L_AUTO_RIGHT
1005 ? L_HORIZ : L_VERT;
1006 if (new_major != prev_major) {
1007 for (int i = 0; i < container->children->length; ++i) {
1008 swayc_t *child = container->children->items[i];
1009 double h = child->height;
1010 child->height = child->width;
1011 child->width = h;
1012 }
1013 }
1014 }
1015 if (container->type == C_WORKSPACE) {
1016 container->workspace_layout = layout;
1017 if (layout == L_HORIZ || layout == L_VERT || is_auto_layout(layout)) {
1018 container->layout = layout;
1019 }
1020 } else {
1021 container->layout = layout;
1022 }
1023 return container;
1024}
diff --git a/sway/old/criteria.c b/sway/old/criteria.c
deleted file mode 100644
index e8978ebe..00000000
--- a/sway/old/criteria.c
+++ /dev/null
@@ -1,451 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <stdlib.h>
3#include <stdio.h>
4#include <stdbool.h>
5#include <pcre.h>
6#include "sway/criteria.h"
7#include "sway/container.h"
8#include "sway/config.h"
9#include "stringop.h"
10#include "list.h"
11#include "log.h"
12
13enum criteria_type { // *must* keep in sync with criteria_strings[]
14 CRIT_CLASS,
15 CRIT_CON_ID,
16 CRIT_CON_MARK,
17 CRIT_FLOATING,
18 CRIT_ID,
19 CRIT_INSTANCE,
20 CRIT_TILING,
21 CRIT_TITLE,
22 CRIT_URGENT,
23 CRIT_WINDOW_ROLE,
24 CRIT_WINDOW_TYPE,
25 CRIT_WORKSPACE,
26 CRIT_LAST
27};
28
29static const char * const criteria_strings[CRIT_LAST] = {
30 [CRIT_CLASS] = "class",
31 [CRIT_CON_ID] = "con_id",
32 [CRIT_CON_MARK] = "con_mark",
33 [CRIT_FLOATING] = "floating",
34 [CRIT_ID] = "id",
35 [CRIT_INSTANCE] = "instance",
36 [CRIT_TILING] = "tiling",
37 [CRIT_TITLE] = "title",
38 [CRIT_URGENT] = "urgent", // either "latest" or "oldest" ...
39 [CRIT_WINDOW_ROLE] = "window_role",
40 [CRIT_WINDOW_TYPE] = "window_type",
41 [CRIT_WORKSPACE] = "workspace"
42};
43
44/**
45 * A single criteria token (ie. value/regex pair),
46 * e.g. 'class="some class regex"'.
47 */
48struct crit_token {
49 enum criteria_type type;
50 pcre *regex;
51 char *raw;
52};
53
54static void free_crit_token(struct crit_token *crit) {
55 pcre_free(crit->regex);
56 free(crit->raw);
57 free(crit);
58}
59
60static void free_crit_tokens(list_t *crit_tokens) {
61 for (int i = 0; i < crit_tokens->length; i++) {
62 free_crit_token(crit_tokens->items[i]);
63 }
64 list_free(crit_tokens);
65}
66
67// Extracts criteria string from its brackets. Returns new (duplicate)
68// substring.
69static char *criteria_from(const char *arg) {
70 char *criteria = NULL;
71 if (*arg == '[') {
72 criteria = strdup(arg + 1);
73 } else {
74 criteria = strdup(arg);
75 }
76
77 int last = strlen(criteria) - 1;
78 if (criteria[last] == ']') {
79 criteria[last] = '\0';
80 }
81 return criteria;
82}
83
84// Return instances of c found in str.
85static int countchr(char *str, char c) {
86 int found = 0;
87 for (int i = 0; str[i]; i++) {
88 if (str[i] == c) {
89 ++found;
90 }
91 }
92 return found;
93}
94
95// criteria_str is e.g. '[class="some class regex" instance="instance name"]'.
96//
97// Will create array of pointers in buf, where first is duplicate of given
98// string (must be freed) and the rest are pointers to names and values in the
99// base string (every other, naturally). argc will be populated with the length
100// of buf.
101//
102// Returns error string or NULL if successful.
103static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str) {
104 sway_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str);
105 char *base = criteria_from(criteria_str);
106 char *head = base;
107 char *namep = head; // start of criteria name
108 char *valp = NULL; // start of value
109
110 // We're going to place EOS markers where we need to and fill up an array
111 // of pointers to the start of each token (either name or value).
112 int pairs = countchr(base, '=');
113 int max_tokens = pairs * 2 + 1; // this gives us at least enough slots
114
115 char **argv = *buf = calloc(max_tokens, sizeof(char*));
116 argv[0] = base; // this needs to be freed by caller
117 bool quoted = true;
118
119 *argc = 1; // uneven = name, even = value
120 while (*head && *argc < max_tokens) {
121 if (namep != head && *(head - 1) == '\\') {
122 // escaped character: don't try to parse this
123 } else if (*head == '=' && namep != head) {
124 if (*argc % 2 != 1) {
125 // we're not expecting a name
126 return strdup("Unable to parse criteria: "
127 "Found out of place equal sign");
128 } else {
129 // name ends here
130 char *end = head; // don't want to rewind the head
131 while (*(end - 1) == ' ') {
132 --end;
133 }
134 *end = '\0';
135 if (*(namep) == ' ') {
136 namep = strrchr(namep, ' ') + 1;
137 }
138 argv[*argc] = namep;
139 *argc += 1;
140 }
141 } else if (*head == '"') {
142 if (*argc % 2 != 0) {
143 // we're not expecting a value
144 return strdup("Unable to parse criteria: "
145 "Found quoted value where it was not expected");
146 } else if (!valp) { // value starts here
147 valp = head + 1;
148 quoted = true;
149 } else {
150 // value ends here
151 argv[*argc] = valp;
152 *argc += 1;
153 *head = '\0';
154 valp = NULL;
155 namep = head + 1;
156 }
157 } else if (*argc % 2 == 0 && *head != ' ') {
158 // parse unquoted values
159 if (!valp) {
160 quoted = false;
161 valp = head; // value starts here
162 }
163 } else if (valp && !quoted && *head == ' ') {
164 // value ends here
165 argv[*argc] = valp;
166 *argc += 1;
167 *head = '\0';
168 valp = NULL;
169 namep = head + 1;
170 }
171 head++;
172 }
173
174 // catch last unquoted value if needed
175 if (valp && !quoted && !*head) {
176 argv[*argc] = valp;
177 *argc += 1;
178 }
179
180 return NULL;
181}
182
183// Returns error string on failure or NULL otherwise.
184static char *parse_criteria_name(enum criteria_type *type, char *name) {
185 *type = CRIT_LAST;
186 for (int i = 0; i < CRIT_LAST; i++) {
187 if (strcmp(criteria_strings[i], name) == 0) {
188 *type = (enum criteria_type) i;
189 break;
190 }
191 }
192 if (*type == CRIT_LAST) {
193 const char *fmt = "Criteria type '%s' is invalid or unsupported.";
194 int len = strlen(name) + strlen(fmt) - 1;
195 char *error = malloc(len);
196 snprintf(error, len, fmt, name);
197 return error;
198 } else if (*type == CRIT_URGENT || *type == CRIT_WINDOW_ROLE ||
199 *type == CRIT_WINDOW_TYPE) {
200 // (we're just being helpful here)
201 const char *fmt = "\"%s\" criteria currently unsupported, "
202 "no window will match this";
203 int len = strlen(fmt) + strlen(name) - 1;
204 char *error = malloc(len);
205 snprintf(error, len, fmt, name);
206 return error;
207 }
208 return NULL;
209}
210
211// Returns error string on failure or NULL otherwise.
212static char *generate_regex(pcre **regex, char *value) {
213 const char *reg_err;
214 int offset;
215
216 *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, &reg_err, &offset, NULL);
217
218 if (!*regex) {
219 const char *fmt = "Regex compilation (for '%s') failed: %s";
220 int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3;
221 char *error = malloc(len);
222 snprintf(error, len, fmt, value, reg_err);
223 return error;
224 }
225 return NULL;
226}
227
228// Test whether the criterion corresponds to the currently focused window
229static bool crit_is_focused(const char *value) {
230 return !strcmp(value, "focused") || !strcmp(value, "__focused__");
231}
232
233// Populate list with crit_tokens extracted from criteria string, returns error
234// string or NULL if successful.
235char *extract_crit_tokens(list_t *tokens, const char * const criteria) {
236 int argc;
237 char **argv = NULL, *error = NULL;
238 if ((error = crit_tokens(&argc, &argv, criteria))) {
239 goto ect_cleanup;
240 }
241 for (int i = 1; i + 1 < argc; i += 2) {
242 char* name = argv[i], *value = argv[i + 1];
243 struct crit_token *token = calloc(1, sizeof(struct crit_token));
244 token->raw = strdup(value);
245
246 if ((error = parse_criteria_name(&token->type, name))) {
247 free_crit_token(token);
248 goto ect_cleanup;
249 } else if (token->type == CRIT_URGENT || crit_is_focused(value)) {
250 sway_log(L_DEBUG, "%s -> \"%s\"", name, value);
251 list_add(tokens, token);
252 } else if((error = generate_regex(&token->regex, value))) {
253 free_crit_token(token);
254 goto ect_cleanup;
255 } else {
256 sway_log(L_DEBUG, "%s -> /%s/", name, value);
257 list_add(tokens, token);
258 }
259 }
260ect_cleanup:
261 free(argv[0]); // base string
262 free(argv);
263 return error;
264}
265
266static int regex_cmp(const char *item, const pcre *regex) {
267 return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0);
268}
269
270// test a single view if it matches list of criteria tokens (all of them).
271static bool criteria_test(swayc_t *cont, list_t *tokens) {
272 if (cont->type != C_VIEW) {
273 return false;
274 }
275 int matches = 0;
276 for (int i = 0; i < tokens->length; i++) {
277 struct crit_token *crit = tokens->items[i];
278 switch (crit->type) {
279 case CRIT_CLASS:
280 if (!cont->class) {
281 // ignore
282 } else if (crit_is_focused(crit->raw)) {
283 swayc_t *focused = get_focused_view(&root_container);
284 if (focused->class && strcmp(cont->class, focused->class) == 0) {
285 matches++;
286 }
287 } else if (crit->regex && regex_cmp(cont->class, crit->regex) == 0) {
288 matches++;
289 }
290 break;
291 case CRIT_CON_ID: {
292 char *endptr;
293 size_t crit_id = strtoul(crit->raw, &endptr, 10);
294
295 if (*endptr == 0 && cont->id == crit_id) {
296 ++matches;
297 }
298 break;
299 }
300 case CRIT_CON_MARK:
301 if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) {
302 // Make sure it isn't matching the NUL string
303 if ((strcmp(crit->raw, "") == 0) == (list_seq_find(cont->marks, (int (*)(const void *, const void *))strcmp, "") != -1)) {
304 ++matches;
305 }
306 }
307 break;
308 case CRIT_FLOATING:
309 if (cont->is_floating) {
310 matches++;
311 }
312 break;
313 case CRIT_ID:
314 if (!cont->app_id) {
315 // ignore
316 } else if (crit->regex && regex_cmp(cont->app_id, crit->regex) == 0) {
317 matches++;
318 }
319 break;
320 case CRIT_INSTANCE:
321 if (!cont->instance) {
322 // ignore
323 } else if (crit_is_focused(crit->raw)) {
324 swayc_t *focused = get_focused_view(&root_container);
325 if (focused->instance && strcmp(cont->instance, focused->instance) == 0) {
326 matches++;
327 }
328 } else if (crit->regex && regex_cmp(cont->instance, crit->regex) == 0) {
329 matches++;
330 }
331 break;
332 case CRIT_TILING:
333 if (!cont->is_floating) {
334 matches++;
335 }
336 break;
337 case CRIT_TITLE:
338 if (!cont->name) {
339 // ignore
340 } else if (crit_is_focused(crit->raw)) {
341 swayc_t *focused = get_focused_view(&root_container);
342 if (focused->name && strcmp(cont->name, focused->name) == 0) {
343 matches++;
344 }
345 } else if (crit->regex && regex_cmp(cont->name, crit->regex) == 0) {
346 matches++;
347 }
348 break;
349 case CRIT_URGENT: // "latest" or "oldest"
350 break;
351 case CRIT_WINDOW_ROLE:
352 break;
353 case CRIT_WINDOW_TYPE:
354 // TODO wlc indeed exposes this information
355 break;
356 case CRIT_WORKSPACE: ;
357 swayc_t *cont_ws = swayc_parent_by_type(cont, C_WORKSPACE);
358 if (!cont_ws || !cont_ws->name) {
359 // ignore
360 } else if (crit_is_focused(crit->raw)) {
361 swayc_t *focused_ws = swayc_active_workspace();
362 if (focused_ws->name && strcmp(cont_ws->name, focused_ws->name) == 0) {
363 matches++;
364 }
365 } else if (crit->regex && regex_cmp(cont_ws->name, crit->regex) == 0) {
366 matches++;
367 }
368 break;
369 default:
370 sway_abort("Invalid criteria type (%i)", crit->type);
371 break;
372 }
373 }
374 return matches == tokens->length;
375}
376
377int criteria_cmp(const void *a, const void *b) {
378 if (a == b) {
379 return 0;
380 } else if (!a) {
381 return -1;
382 } else if (!b) {
383 return 1;
384 }
385 const struct criteria *crit_a = a, *crit_b = b;
386 int cmp = lenient_strcmp(crit_a->cmdlist, crit_b->cmdlist);
387 if (cmp != 0) {
388 return cmp;
389 }
390 return lenient_strcmp(crit_a->crit_raw, crit_b->crit_raw);
391}
392
393void free_criteria(struct criteria *crit) {
394 if (crit->tokens) {
395 free_crit_tokens(crit->tokens);
396 }
397 if (crit->cmdlist) {
398 free(crit->cmdlist);
399 }
400 if (crit->crit_raw) {
401 free(crit->crit_raw);
402 }
403 free(crit);
404}
405
406bool criteria_any(swayc_t *cont, list_t *criteria) {
407 for (int i = 0; i < criteria->length; i++) {
408 struct criteria *bc = criteria->items[i];
409 if (criteria_test(cont, bc->tokens)) {
410 return true;
411 }
412 }
413 return false;
414}
415
416list_t *criteria_for(swayc_t *cont) {
417 list_t *criteria = config->criteria, *matches = create_list();
418 for (int i = 0; i < criteria->length; i++) {
419 struct criteria *bc = criteria->items[i];
420 if (criteria_test(cont, bc->tokens)) {
421 list_add(matches, bc);
422 }
423 }
424 return matches;
425}
426
427struct list_tokens {
428 list_t *list;
429 list_t *tokens;
430};
431
432static void container_match_add(swayc_t *container, struct list_tokens *list_tokens) {
433 if (criteria_test(container, list_tokens->tokens)) {
434 list_add(list_tokens->list, container);
435 }
436}
437
438list_t *container_for(list_t *tokens) {
439 struct list_tokens list_tokens = (struct list_tokens){create_list(), tokens};
440
441 container_map(&root_container, (void (*)(swayc_t *, void *))container_match_add, &list_tokens);
442
443 for (int i = 0; i < scratchpad->length; ++i) {
444 swayc_t *c = scratchpad->items[i];
445 if (criteria_test(c, tokens)) {
446 list_add(list_tokens.list, c);
447 }
448 }
449
450 return list_tokens.list;
451}
diff --git a/sway/old/focus.c b/sway/old/focus.c
deleted file mode 100644
index 66f7ee17..00000000
--- a/sway/old/focus.c
+++ /dev/null
@@ -1,278 +0,0 @@
1#include "stdbool.h"
2#include <wlc/wlc.h>
3#include "sway/focus.h"
4#include "sway/workspace.h"
5#include "sway/layout.h"
6#include "sway/config.h"
7#include "sway/input_state.h"
8#include "sway/ipc-server.h"
9#include "sway/border.h"
10#include "log.h"
11
12bool locked_container_focus = false;
13bool suspend_workspace_cleanup = false;
14
15// switches parent focus to c. will switch it accordingly
16static void update_focus(swayc_t *c) {
17 // Handle if focus switches
18 swayc_t *parent = c->parent;
19 if (!parent) return;
20 if (parent->focused != c) {
21 // Get previous focus
22 swayc_t *prev = parent->focused;
23 // Set new focus
24 parent->focused = c;
25
26 switch (c->type) {
27 // Shouldn't happen
28 case C_ROOT: return;
29
30 // Case where output changes
31 case C_OUTPUT:
32 wlc_output_focus(c->handle);
33 break;
34
35 // Case where workspace changes
36 case C_WORKSPACE:
37 if (prev) {
38 ipc_event_workspace(prev, c, "focus");
39
40 // if the old workspace has no children, destroy it
41 if(prev->children->length == 0 && prev->floating->length == 0 && !suspend_workspace_cleanup) {
42 destroy_workspace(prev);
43 } else {
44 // update visibility of old workspace
45 update_visibility(prev);
46 }
47 }
48 // Update visibility of newly focused workspace
49 update_visibility(c);
50 break;
51
52 default:
53 case C_VIEW:
54 case C_CONTAINER:
55 break;
56 }
57 }
58}
59
60bool move_focus(enum movement_direction direction) {
61 swayc_t *old_view = get_focused_container(&root_container);
62 swayc_t *new_view = get_swayc_in_direction(old_view, direction);
63 if (!new_view) {
64 return false;
65 } else if (new_view->type == C_ROOT) {
66 sway_log(L_DEBUG, "Not setting focus above the workspace level");
67 return false;
68 } else if (new_view->type == C_OUTPUT) {
69 return set_focused_container(swayc_active_workspace_for(new_view));
70 } else if (direction == MOVE_PARENT || direction == MOVE_CHILD) {
71 return set_focused_container(new_view);
72 } else if (config->mouse_warping) {
73 swayc_t *old_op = old_view->type == C_OUTPUT ?
74 old_view : swayc_parent_by_type(old_view, C_OUTPUT);
75 swayc_t *focused = get_focused_view(new_view);
76 if (set_focused_container(focused)) {
77 if (old_op != swayc_active_output() && focused && focused->type == C_VIEW) {
78 center_pointer_on(focused);
79 }
80 return true;
81 }
82 } else {
83 return set_focused_container(get_focused_view(new_view));
84 }
85 return false;
86}
87
88swayc_t *get_focused_container(swayc_t *parent) {
89 if (!parent) {
90 return swayc_active_workspace();
91 }
92 while (!parent->is_focused && parent->focused) {
93 parent = parent->focused;
94 }
95 return parent;
96}
97
98bool set_focused_container(swayc_t *c) {
99 if (locked_container_focus || !c || !c->parent) {
100 return false;
101 }
102
103 // current ("old") workspace for sending workspace change event later
104 swayc_t *old_ws = swayc_active_workspace();
105 // keep track of child count so we can determine if it gets destroyed
106 int old_ws_child_count = 0;
107 if (old_ws) {
108 old_ws_child_count = old_ws->children->length + old_ws->floating->length;
109 }
110
111 // current ("old") focused container
112 swayc_t *old_focus = get_focused_container(&root_container);
113 // if old_focus is a workspace, then it's the same workspace as
114 // old_ws, and we'll need to null its pointer too, since it will
115 // be destroyed in the update_focus() call
116 bool old_focus_was_ws = (old_focus->type == C_WORKSPACE);
117
118 // workspace of new focused container
119 swayc_t *workspace = swayc_active_workspace_for(c);
120
121 if (swayc_is_fullscreen(get_focused_container(workspace))) {
122 // if switching to a workspace with a fullscreen view,
123 // focus on the fullscreen view
124 c = get_focused_container(workspace);
125 }
126
127 swayc_log(L_DEBUG, c, "Setting focus to %p:%" PRIuPTR, c, c->handle);
128
129 if (c->type == C_VIEW) {
130 // dispatch a window event
131 ipc_event_window(c, "focus");
132 }
133
134 // update the global pointer
135 current_focus = c;
136
137 // update container focus from here to root, making necessary changes along
138 // the way
139 swayc_t *p = c;
140 if (p->type != C_OUTPUT && p->type != C_ROOT) {
141 p->is_focused = true;
142 }
143 while (p != &root_container) {
144 update_focus(p);
145 p = p->parent;
146 p->is_focused = false;
147 }
148
149 if (old_focus_was_ws && old_ws_child_count == 0) {
150 // this workspace was destroyed in update_focus(), so null the pointers
151 old_focus = NULL;
152 old_ws = NULL;
153 }
154
155 if (!(wlc_view_get_type(p->handle) & WLC_BIT_POPUP)) {
156 if (old_focus) {
157 if (old_focus->type == C_VIEW) {
158 wlc_view_set_state(old_focus->handle, WLC_BIT_ACTIVATED, false);
159 }
160 update_container_border(old_focus);
161 }
162 if (c->type == C_VIEW) {
163 wlc_view_set_state(c->handle, WLC_BIT_ACTIVATED, true);
164 }
165 /* TODO WLR
166 if (!desktop_shell.is_locked) {
167 // If the system is locked, we do everything _but_ actually setting
168 // focus. This includes making our internals think that this view is
169 // focused.
170 wlc_view_focus(c->handle);
171 }
172 */
173 if (c->parent->layout != L_TABBED && c->parent->layout != L_STACKED) {
174 update_container_border(c);
175 }
176
177 swayc_t *parent = swayc_tabbed_stacked_ancestor(c);
178 if (parent != NULL) {
179 arrange_backgrounds();
180 arrange_windows(parent, -1, -1);
181 }
182 }
183
184 if (old_ws != workspace) {
185 // old_ws might be NULL here but that's ok
186 ipc_event_workspace(old_ws, workspace, "focus");
187 }
188
189 return true;
190}
191
192bool set_focused_container_for(swayc_t *a, swayc_t *c) {
193 if (locked_container_focus || !c) {
194 return false;
195 }
196 swayc_t *find = c;
197 while (find != a && (find = find->parent)) {
198 if (find == &root_container) {
199 return false;
200 }
201 }
202
203 // Get workspace for c, get that workspaces current focused container.
204 swayc_t *workspace = swayc_active_workspace_for(c);
205 swayc_t *focused = get_focused_view(workspace);
206 // if the workspace we are changing focus to has a fullscreen view return
207 if (swayc_is_fullscreen(focused) && c != focused) {
208 return false;
209 }
210
211 // Check if we are changing a parent container that will see change
212 bool effective = true;
213 while (find != &root_container) {
214 if (find->parent->focused != find) {
215 effective = false;
216 }
217 find = find->parent;
218 }
219 if (effective) {
220 // Go to set_focused_container
221 return set_focused_container(c);
222 }
223
224 sway_log(L_DEBUG, "Setting focus for %p:%" PRIuPTR " to %p:%" PRIuPTR,
225 a, a->handle, c, c->handle);
226
227 c->is_focused = true;
228 swayc_t *p = c;
229 while (p != a) {
230 update_focus(p);
231 p = p->parent;
232 p->is_focused = false;
233 }
234 return true;
235}
236
237swayc_t *get_focused_view(swayc_t *parent) {
238 swayc_t *c = parent;
239 while (c && c->type != C_VIEW) {
240 if (c->type == C_WORKSPACE && c->focused == NULL) {
241 return c;
242 }
243 c = c->focused;
244 }
245 if (c == NULL) {
246 c = swayc_active_workspace_for(parent);
247 }
248 return c;
249}
250
251swayc_t *get_focused_float(swayc_t *ws) {
252 if(!sway_assert(ws->type == C_WORKSPACE, "must be of workspace type")) {
253 ws = swayc_active_workspace();
254 }
255 if (ws->floating->length) {
256 return ws->floating->items[ws->floating->length - 1];
257 }
258 return NULL;
259}
260
261swayc_t *get_focused_view_include_floating(swayc_t *parent) {
262 swayc_t *c = parent;
263 swayc_t *f = NULL;
264
265 while (c && c->type != C_VIEW) {
266 if (c->type == C_WORKSPACE && c->focused == NULL) {
267 return ((f = get_focused_float(c))) ? f : c;
268 }
269
270 c = c->focused;
271 }
272
273 if (c == NULL) {
274 c = swayc_active_workspace_for(parent);
275 }
276
277 return c;
278}
diff --git a/sway/old/handlers.c b/sway/old/handlers.c
deleted file mode 100644
index 33e75d6b..00000000
--- a/sway/old/handlers.c
+++ /dev/null
@@ -1,1143 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <xkbcommon/xkbcommon.h>
3#include <strings.h>
4#include <stdlib.h>
5#include <stdbool.h>
6#include <libinput.h>
7#include <math.h>
8#include <wlc/wlc.h>
9#include <wlc/wlc-render.h>
10#include <wlc/wlc-wayland.h>
11#include <ctype.h>
12#include "sway/handlers.h"
13#include "sway/border.h"
14#include "sway/layout.h"
15#include "sway/config.h"
16#include "sway/commands.h"
17#include "sway/workspace.h"
18#include "sway/container.h"
19#include "sway/output.h"
20#include "sway/focus.h"
21#include "sway/input_state.h"
22#include "sway/extensions.h"
23#include "sway/criteria.h"
24#include "sway/ipc-server.h"
25#include "sway/input.h"
26#include "sway/security.h"
27#include "list.h"
28#include "stringop.h"
29#include "log.h"
30
31// Event should be sent to client
32#define EVENT_PASSTHROUGH false
33
34// Event handled by sway and should not be sent to client
35#define EVENT_HANDLED true
36
37static struct panel_config *if_panel_find_config(struct wl_client *client) {
38 int i;
39 for (i = 0; i < desktop_shell.panels->length; i++) {
40 struct panel_config *config = desktop_shell.panels->items[i];
41 if (config->client == client) {
42 return config;
43 }
44 }
45 return NULL;
46}
47
48static struct background_config *if_background_find_config(struct wl_client *client) {
49 int i;
50 for (i = 0; i < desktop_shell.backgrounds->length; i++) {
51 struct background_config *config = desktop_shell.backgrounds->items[i];
52 if (config->client == client) {
53 return config;
54 }
55 }
56 return NULL;
57}
58
59static struct wlc_geometry compute_panel_geometry(struct panel_config *config) {
60 struct wlc_size resolution;
61 output_get_scaled_size(config->output, &resolution);
62 const struct wlc_geometry *old = wlc_view_get_geometry(config->handle);
63 struct wlc_geometry new;
64
65 switch (config->panel_position) {
66 case DESKTOP_SHELL_PANEL_POSITION_TOP:
67 new.origin.x = 0;
68 new.origin.y = 0;
69 new.size.w = resolution.w;
70 new.size.h = old->size.h;
71 break;
72 case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
73 new.origin.x = 0;
74 new.origin.y = resolution.h - old->size.h;
75 new.size.w = resolution.w;
76 new.size.h = old->size.h;
77 break;
78 case DESKTOP_SHELL_PANEL_POSITION_LEFT:
79 new.origin.x = 0;
80 new.origin.y = 0;
81 new.size.w = old->size.w;
82 new.size.h = resolution.h;
83 break;
84 case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
85 new.origin.x = resolution.w - old->size.w;
86 new.origin.y = 0;
87 new.size.w = old->size.w;
88 new.size.h = resolution.h;
89 break;
90 }
91
92 return new;
93}
94
95static void update_panel_geometry(struct panel_config *config) {
96 struct wlc_geometry geometry = compute_panel_geometry(config);
97 wlc_view_set_geometry(config->handle, 0, &geometry);
98}
99
100static void update_panel_geometries(wlc_handle output) {
101 for (int i = 0; i < desktop_shell.panels->length; i++) {
102 struct panel_config *config = desktop_shell.panels->items[i];
103 if (config->output == output) {
104 update_panel_geometry(config);
105 }
106 }
107}
108
109static void update_background_geometry(struct background_config *config) {
110 struct wlc_geometry geometry = wlc_geometry_zero;
111 output_get_scaled_size(config->output, &geometry.size);
112 wlc_view_set_geometry(config->handle, 0, &geometry);
113}
114
115static void update_background_geometries(wlc_handle output) {
116 for (int i = 0; i < desktop_shell.backgrounds->length; i++) {
117 struct background_config *config = desktop_shell.backgrounds->items[i];
118 if (config->output == output) {
119 update_background_geometry(config);
120 }
121 }
122}
123
124/* Handles */
125
126static bool handle_input_created(struct libinput_device *device) {
127 const char *identifier = libinput_dev_unique_id(device);
128 if (!identifier) {
129 sway_log(L_ERROR, "Unable to allocate unique name for input device %p",
130 device);
131 return true;
132 }
133 sway_log(L_INFO, "Found input device (%s)", identifier);
134
135 list_add(input_devices, device);
136
137 struct input_config *ic = NULL;
138 int i;
139 for (i = 0; i < config->input_configs->length; ++i) {
140 struct input_config *cur = config->input_configs->items[i];
141 if (strcasecmp(identifier, cur->identifier) == 0) {
142 sway_log(L_DEBUG, "Matched input config for %s",
143 identifier);
144 ic = cur;
145 break;
146 }
147 if (strcasecmp("*", cur->identifier) == 0) {
148 sway_log(L_DEBUG, "Matched wildcard input config for %s",
149 identifier);
150 ic = cur;
151 break;
152 }
153 }
154
155 apply_input_config(ic, device);
156 return true;
157}
158
159static void handle_input_destroyed(struct libinput_device *device) {
160 int i;
161 list_t *list = input_devices;
162 for (i = 0; i < list->length; ++i) {
163 if(((struct libinput_device *)list->items[i]) == device) {
164 list_del(list, i);
165 break;
166 }
167 }
168}
169
170static bool handle_output_created(wlc_handle output) {
171 swayc_t *op = new_output(output);
172
173 // Visibility mask to be able to make view invisible
174 wlc_output_set_mask(output, VISIBLE);
175
176 if (!op) {
177 return false;
178 }
179
180 // Switch to workspace if we need to
181 if (swayc_active_workspace() == NULL) {
182 swayc_t *ws = op->children->items[0];
183 workspace_switch(ws);
184 }
185
186 // Fixes issues with backgrounds and wlc
187 wlc_handle prev = wlc_get_focused_output();
188 wlc_output_focus(output);
189 wlc_output_focus(prev);
190 return true;
191}
192
193static void handle_output_destroyed(wlc_handle output) {
194 int i;
195 list_t *list = root_container.children;
196 for (i = 0; i < list->length; ++i) {
197 if (((swayc_t *)list->items[i])->handle == output) {
198 break;
199 }
200 }
201 if (i < list->length) {
202 destroy_output(list->items[i]);
203 } else {
204 return;
205 }
206 if (list->length > 0) {
207 // switch to other outputs active workspace
208 workspace_switch(((swayc_t *)root_container.children->items[0])->focused);
209 }
210}
211
212static void handle_output_post_render(wlc_handle output) {
213 ipc_get_pixels(output);
214}
215
216static void handle_view_pre_render(wlc_handle view) {
217 render_view_borders(view);
218}
219
220static void handle_output_resolution_change(wlc_handle output, const struct wlc_size *from, const struct wlc_size *to) {
221 sway_log(L_DEBUG, "Output %u resolution changed to %d x %d", (unsigned int)output, to->w, to->h);
222
223 swayc_t *c = swayc_by_handle(output);
224 if (!c) {
225 return;
226 }
227 c->width = to->w;
228 c->height = to->h;
229
230 update_panel_geometries(output);
231 update_background_geometries(output);
232
233 arrange_windows(&root_container, -1, -1);
234}
235
236static void handle_output_focused(wlc_handle output, bool focus) {
237 swayc_t *c = swayc_by_handle(output);
238 // if for some reason this output doesn't exist, create it.
239 if (!c) {
240 handle_output_created(output);
241 }
242 if (focus) {
243 set_focused_container(get_focused_container(c));
244 }
245}
246
247static void ws_cleanup() {
248 swayc_t *op, *ws;
249 int i = 0, j;
250 if (!root_container.children)
251 return;
252 while (i < root_container.children->length) {
253 op = root_container.children->items[i++];
254 if (!op->children)
255 continue;
256 j = 0;
257 while (j < op->children->length) {
258 ws = op->children->items[j++];
259 if (ws->children->length == 0 && ws->floating->length == 0 && ws != op->focused) {
260 if (destroy_workspace(ws)) {
261 j--;
262 }
263 }
264 }
265 }
266}
267
268static void positioner_place_window(wlc_handle handle) {
269 const struct wlc_geometry *anchor = wlc_view_positioner_get_anchor_rect(handle);
270 const struct wlc_size *sr = wlc_view_positioner_get_size(handle);
271 // a positioner is required to have a non-null anchor and a non-negative size
272 if (!anchor || !sr ||
273 sr->w <= 0 || sr->h <= 0 ||
274 anchor->size.w <= 0 || anchor->size.h <= 0) {
275 return;
276 }
277 const struct wlc_point offset = *wlc_view_positioner_get_offset(handle);
278 enum wlc_positioner_anchor_bit anchors = wlc_view_positioner_get_anchor(handle);
279 enum wlc_positioner_gravity_bit gravity = wlc_view_positioner_get_gravity(handle);
280 struct wlc_geometry geo = {
281 .origin = offset,
282 .size = *sr
283 };
284
285 if (anchors & WLC_BIT_ANCHOR_TOP) {
286 geo.origin.y += anchor->origin.y;
287 } else if (anchors & WLC_BIT_ANCHOR_BOTTOM) {
288 geo.origin.y += anchor->origin.y + anchor->size.h;
289 } else {
290 geo.origin.y += anchor->origin.y + anchor->size.h / 2;
291 }
292 if (anchors & WLC_BIT_ANCHOR_LEFT) {
293 geo.origin.x += anchor->origin.x;
294 } else if (anchors & WLC_BIT_ANCHOR_RIGHT) {
295 geo.origin.x += anchor->origin.x + anchor->size.w;
296 } else {
297 geo.origin.x += anchor->origin.x + anchor->size.w / 2;
298 }
299
300 if (gravity & WLC_BIT_GRAVITY_TOP) {
301 geo.origin.y -= geo.size.h;
302 } else if (gravity & WLC_BIT_GRAVITY_BOTTOM) {
303 /* default */
304 } else {
305 geo.origin.y -= geo.size.h / 2;
306 }
307 if (gravity & WLC_BIT_GRAVITY_LEFT) {
308 geo.origin.x -= geo.size.w;
309 } else if (gravity & WLC_BIT_GRAVITY_RIGHT) {
310 /* default */
311 } else {
312 geo.origin.x -= geo.size.w / 2;
313 }
314
315 sway_log(L_DEBUG, "xdg-positioner: placing window %" PRIuPTR " "
316 "sized (%u,%u) offset by (%d,%d), "
317 "anchor rectangle sized (%u,%u) at (%d,%d), "
318 "anchor edges: %s %s, gravity: %s %s",
319 handle,
320 sr->w, sr->h, offset.x, offset.y,
321 anchor->size.w, anchor->size.h, anchor->origin.x, anchor->origin.y,
322 anchors & WLC_BIT_ANCHOR_TOP ? "top" :
323 (anchors & WLC_BIT_ANCHOR_BOTTOM ? "bottom" : "middle"),
324 anchors & WLC_BIT_ANCHOR_LEFT ? "left" :
325 (anchors & WLC_BIT_ANCHOR_RIGHT ? "right" : "center"),
326 gravity & WLC_BIT_GRAVITY_TOP ? "top" :
327 (gravity & WLC_BIT_GRAVITY_BOTTOM ? "bottom" : "middle"),
328 gravity & WLC_BIT_GRAVITY_LEFT ? "left" :
329 (gravity & WLC_BIT_GRAVITY_RIGHT ? "right" : "center"));
330
331 wlc_handle parent = wlc_view_get_parent(handle);
332 if (parent) {
333 const struct wlc_geometry *pg = wlc_view_get_geometry(parent);
334 geo.origin.x += pg->origin.x;
335 geo.origin.y += pg->origin.y;
336 }
337 wlc_view_set_geometry(handle, 0, &geo);
338}
339
340static bool handle_view_created(wlc_handle handle) {
341 // if view is child of another view, the use that as focused container
342 wlc_handle parent = wlc_view_get_parent(handle);
343 swayc_t *focused = NULL;
344 swayc_t *newview = NULL;
345 swayc_t *current_ws = swayc_active_workspace();
346 bool return_to_workspace = false;
347 struct wl_client *client = wlc_view_get_wl_client(handle);
348 struct wl_resource *resource = wlc_surface_get_wl_resource(
349 wlc_view_get_surface(handle));
350 pid_t pid;
351 struct panel_config *panel_config = NULL;
352 struct background_config *background_config = NULL;
353
354 panel_config = if_panel_find_config(client);
355 if (panel_config) {
356 panel_config->handle = handle;
357 update_panel_geometry(panel_config);
358 wlc_view_set_mask(handle, VISIBLE);
359 wlc_view_set_output(handle, panel_config->output);
360 wlc_view_bring_to_front(handle);
361 arrange_windows(&root_container, -1, -1);
362 return true;
363 }
364
365 background_config = if_background_find_config(client);
366 if (background_config) {
367 background_config->handle = handle;
368 update_background_geometry(background_config);
369 wlc_view_set_mask(handle, VISIBLE);
370 wlc_view_set_output(handle, background_config->output);
371 wlc_view_send_to_back(handle);
372 return true;
373 }
374
375 // Get parent container, to add view in
376 if (parent) {
377 focused = swayc_by_handle(parent);
378 }
379
380 if (client) {
381 pid = wlc_view_get_pid(handle);
382
383 if (pid) {
384 // using newview as a temp storage location here,
385 // rather than adding yet another workspace var
386 newview = workspace_for_pid(pid);
387 if (newview) {
388 focused = get_focused_container(newview);
389 return_to_workspace = true;
390 }
391 newview = NULL;
392 }
393 }
394
395 swayc_t *prev_focus = get_focused_container(&root_container);
396
397 if (!focused || focused->type == C_OUTPUT) {
398 focused = prev_focus;
399 // Move focus from floating view
400 if (focused->is_floating) {
401 // To workspace if there are no children
402 if (focused->parent->children->length == 0) {
403 focused = focused->parent;
404 }
405 // TODO find a better way of doing this
406 // Or to focused container
407 else {
408 focused = get_focused_container(focused->parent->children->items[0]);
409 }
410 }
411 }
412
413 positioner_place_window(handle);
414
415 sway_log(L_DEBUG, "handle:%" PRIuPTR " type:%x state:%x parent:%" PRIuPTR " "
416 "mask:%d (x:%d y:%d w:%d h:%d) title:%s "
417 "class:%s appid:%s",
418 handle, wlc_view_get_type(handle), wlc_view_get_state(handle), parent,
419 wlc_view_get_mask(handle), wlc_view_get_geometry(handle)->origin.x,
420 wlc_view_get_geometry(handle)->origin.y,wlc_view_get_geometry(handle)->size.w,
421 wlc_view_get_geometry(handle)->size.h, wlc_view_get_title(handle),
422 wlc_view_get_class(handle), wlc_view_get_app_id(handle));
423
424 // TODO properly figure out how each window should be handled.
425 switch (wlc_view_get_type(handle)) {
426 // regular view created regularly
427 case 0:
428 if (parent) {
429 newview = new_floating_view(handle);
430 } else {
431 newview = new_view(focused, handle);
432 wlc_view_set_state(handle, WLC_BIT_MAXIMIZED, true);
433 }
434 break;
435
436 // Dmenu keeps viewfocus, but others with this flag don't, for now simulate
437 // dmenu
438 case WLC_BIT_OVERRIDE_REDIRECT:
439 wlc_view_focus(handle);
440 wlc_view_set_state(handle, WLC_BIT_ACTIVATED, true);
441 wlc_view_bring_to_front(handle);
442 break;
443
444 // Firefox popups have this flag set.
445 case WLC_BIT_OVERRIDE_REDIRECT|WLC_BIT_UNMANAGED:
446 wlc_view_bring_to_front(handle);
447 locked_container_focus = true;
448 break;
449
450 // Modals, get focus, popups do not
451 case WLC_BIT_MODAL:
452 wlc_view_focus(handle);
453 wlc_view_bring_to_front(handle);
454 newview = new_floating_view(handle);
455 /* fallthrough */
456 case WLC_BIT_POPUP:
457 wlc_view_bring_to_front(handle);
458 break;
459 }
460
461 // Prevent current ws from being destroyed, if empty
462 suspend_workspace_cleanup = true;
463
464 if (newview) {
465 ipc_event_window(newview, "new");
466 set_focused_container(newview);
467 wlc_view_set_mask(handle, VISIBLE);
468 swayc_t *output = swayc_parent_by_type(newview, C_OUTPUT);
469 arrange_windows(output, -1, -1);
470 // check if it matches for_window in config and execute if so
471 list_t *criteria = criteria_for(newview);
472 for (int i = 0; i < criteria->length; i++) {
473 struct criteria *crit = criteria->items[i];
474 sway_log(L_DEBUG, "for_window '%s' matches new view %p, cmd: '%s'",
475 crit->crit_raw, newview, crit->cmdlist);
476 struct cmd_results *res = handle_command(crit->cmdlist, CONTEXT_CRITERIA);
477 if (res->status != CMD_SUCCESS) {
478 sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
479 }
480 free_cmd_results(res);
481 // view must be focused for commands to affect it, so always
482 // refocus in-between command lists
483 set_focused_container(newview);
484 }
485 swayc_t *workspace = swayc_parent_by_type(focused, C_WORKSPACE);
486 if (workspace && workspace->fullscreen) {
487 set_focused_container(workspace->fullscreen);
488 }
489 for (int i = 0; i < decoration_state.csd_resources->length; ++i) {
490 struct wl_resource *res = decoration_state.csd_resources->items[i];
491 if (res == resource) {
492 list_del(decoration_state.csd_resources, i);
493 server_decoration_enable_csd(handle);
494 break;
495 }
496 }
497 } else {
498 swayc_t *output = swayc_parent_by_type(focused, C_OUTPUT);
499 wlc_handle *h = malloc(sizeof(wlc_handle));
500 if (!h) {
501 sway_log(L_ERROR, "Unable to allocate window handle, view handler bailing out");
502 return true;
503 }
504 *h = handle;
505 sway_log(L_DEBUG, "Adding unmanaged window %p to %p", h, output->unmanaged);
506 list_add(output->unmanaged, h);
507 wlc_view_set_mask(handle, VISIBLE);
508 }
509
510 if (return_to_workspace && current_ws != swayc_active_workspace()) {
511 // we were on one workspace, switched to another to add this view,
512 // now let's return to where we were
513 workspace_switch(current_ws);
514 set_focused_container(get_focused_container(current_ws));
515 }
516 if (prev_focus && prev_focus->type == C_VIEW
517 && newview && criteria_any(newview, config->no_focus)) {
518 // Restore focus
519 swayc_t *ws = swayc_parent_by_type(newview, C_WORKSPACE);
520 if (!ws || ws != newview->parent
521 || ws->children->length + ws->floating->length != 1) {
522 sway_log(L_DEBUG, "no_focus: restoring focus to %s", prev_focus->name);
523 set_focused_container(prev_focus);
524 }
525 }
526
527 suspend_workspace_cleanup = false;
528 ws_cleanup();
529 return true;
530}
531
532static void handle_view_destroyed(wlc_handle handle) {
533 sway_log(L_DEBUG, "Destroying window %" PRIuPTR, handle);
534 swayc_t *view = swayc_by_handle(handle);
535
536 // destroy views by type
537 switch (wlc_view_get_type(handle)) {
538 // regular view created regularly
539 case 0:
540 case WLC_BIT_MODAL:
541 case WLC_BIT_POPUP:
542 break;
543 // DMENU has this flag, and takes view_focus, but other things with this
544 // flag don't
545 case WLC_BIT_OVERRIDE_REDIRECT:
546 break;
547 case WLC_BIT_OVERRIDE_REDIRECT|WLC_BIT_UNMANAGED:
548 locked_container_focus = false;
549 break;
550 }
551
552 if (view) {
553 bool fullscreen = swayc_is_fullscreen(view);
554 remove_view_from_scratchpad(view);
555 swayc_t *parent = destroy_view(view), *iter = NULL;
556 if (parent) {
557 ipc_event_window(parent, "close");
558
559 // Destroy empty workspaces
560 if (parent->type == C_WORKSPACE &&
561 parent->children->length == 0 &&
562 parent->floating->length == 0 &&
563 swayc_active_workspace() != parent &&
564 !parent->visible) {
565 parent = destroy_workspace(parent);
566 }
567
568 if (fullscreen) {
569 iter = parent;
570 while (iter) {
571 if (iter->fullscreen) {
572 iter->fullscreen = NULL;
573 break;
574 }
575 iter = iter->parent;
576 }
577 }
578
579
580 arrange_windows(iter ? iter : parent, -1, -1);
581 }
582 } else {
583 // Is it unmanaged?
584 int i;
585 for (i = 0; i < root_container.children->length; ++i) {
586 swayc_t *output = root_container.children->items[i];
587 int j;
588 for (j = 0; j < output->unmanaged->length; ++j) {
589 wlc_handle *_handle = output->unmanaged->items[j];
590 if (*_handle == handle) {
591 list_del(output->unmanaged, j);
592 free(_handle);
593 break;
594 }
595 }
596 }
597 // Is it in the scratchpad?
598 for (i = 0; i < scratchpad->length; ++i) {
599 swayc_t *item = scratchpad->items[i];
600 if (item->handle == handle) {
601 list_del(scratchpad, i);
602 destroy_view(item);
603 break;
604 }
605 }
606 }
607 set_focused_container(get_focused_view(&root_container));
608}
609
610static void handle_view_focus(wlc_handle view, bool focus) {
611 return;
612}
613
614static void handle_view_geometry_request(wlc_handle handle, const struct wlc_geometry *geometry) {
615 sway_log(L_DEBUG, "geometry request for %" PRIuPTR " %dx%d @ %d,%d", handle,
616 geometry->size.w, geometry->size.h, geometry->origin.x, geometry->origin.y);
617 // If the view is floating, then apply the geometry.
618 // Otherwise save the desired width/height for the view.
619 // This will not do anything for the time being as WLC improperly sends geometry requests
620 swayc_t *view = swayc_by_handle(handle);
621 if (view) {
622 view->desired_width = geometry->size.w;
623 view->desired_height = geometry->size.h;
624
625 if (view->is_floating) {
626 floating_view_sane_size(view);
627 view->width = view->desired_width;
628 view->height = view->desired_height;
629 view->x = geometry->origin.x;
630 view->y = geometry->origin.y;
631 update_geometry(view);
632 }
633 } else {
634 wlc_view_set_geometry(handle, 0, geometry);
635 }
636}
637
638static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit state, bool toggle) {
639 swayc_t *c = swayc_by_handle(view);
640 pid_t pid = wlc_view_get_pid(view);
641 switch (state) {
642 case WLC_BIT_FULLSCREEN:
643 if (!(get_feature_policy_mask(pid) & FEATURE_FULLSCREEN)) {
644 sway_log(L_INFO, "Denying fullscreen to %d (%s)", pid, c->name);
645 break;
646 }
647 // i3 just lets it become fullscreen
648 wlc_view_set_state(view, state, toggle);
649 if (c) {
650 sway_log(L_DEBUG, "setting view %" PRIuPTR " %s, fullscreen %d", view, c->name, toggle);
651 arrange_windows(c->parent, -1, -1);
652 // Set it as focused window for that workspace if its going fullscreen
653 swayc_t *ws = swayc_parent_by_type(c, C_WORKSPACE);
654 if (toggle) {
655 // Set ws focus to c
656 set_focused_container_for(ws, c);
657 ws->fullscreen = c;
658 } else {
659 ws->fullscreen = NULL;
660 }
661 }
662 break;
663 case WLC_BIT_MAXIMIZED:
664 case WLC_BIT_RESIZING:
665 case WLC_BIT_MOVING:
666 break;
667 case WLC_BIT_ACTIVATED:
668 sway_log(L_DEBUG, "View %p requested to be activated", c);
669 break;
670 }
671 return;
672}
673
674static void handle_view_properties_updated(wlc_handle view, uint32_t mask) {
675 if (mask == WLC_BIT_PROPERTY_TITLE) {
676 swayc_t *c = swayc_by_handle(view);
677 if (!c) {
678 return;
679 }
680
681 // update window title
682 const char *new_name = wlc_view_get_title(view);
683
684 if (new_name) {
685 if (!c->name || strcmp(c->name, new_name) != 0) {
686 free(c->name);
687 c->name = strdup(new_name);
688 swayc_t *p = swayc_tabbed_stacked_ancestor(c);
689 if (p) {
690 // TODO: we only got the topmost tabbed/stacked container, update borders of all containers on the path
691 update_container_border(get_focused_view(p));
692 } else if (c->border_type == B_NORMAL) {
693 update_container_border(c);
694 }
695 ipc_event_window(c, "title");
696 }
697 }
698 }
699}
700
701static void handle_binding_command(struct sway_binding *binding) {
702 struct sway_binding *binding_copy = binding;
703 bool reload = false;
704 // if this is a reload command we need to make a duplicate of the
705 // binding since it will be gone after the reload has completed.
706 if (strcasecmp(binding->command, "reload") == 0) {
707 binding_copy = sway_binding_dup(binding);
708 if (!binding_copy) {
709 sway_log(L_ERROR, "Unable to duplicate binding during reload");
710 return;
711 }
712 reload = true;
713 }
714
715 struct cmd_results *res = handle_command(binding->command, CONTEXT_BINDING);
716 if (res->status != CMD_SUCCESS) {
717 sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
718 }
719 ipc_event_binding_keyboard(binding_copy);
720
721 if (reload) { // free the binding if we made a copy
722 free_sway_binding(binding_copy);
723 }
724
725 free_cmd_results(res);
726}
727
728static bool handle_bindsym(struct sway_binding *binding, uint32_t keysym, uint32_t keycode) {
729 int i;
730 for (i = 0; i < binding->keys->length; ++i) {
731 if (binding->bindcode) {
732 xkb_keycode_t *key = binding->keys->items[i];
733 if (keycode == *key) {
734 handle_binding_command(binding);
735 return true;
736 }
737 } else {
738 xkb_keysym_t *key = binding->keys->items[i];
739 if (keysym == *key) {
740 handle_binding_command(binding);
741 return true;
742 }
743 }
744 }
745
746 return false;
747}
748
749static bool valid_bindsym(struct sway_binding *binding) {
750 bool match = false;
751 int i;
752 for (i = 0; i < binding->keys->length; ++i) {
753 if (binding->bindcode) {
754 xkb_keycode_t *key = binding->keys->items[i];
755 if ((match = check_key(0, *key)) == false) {
756 break;
757 }
758 } else {
759 xkb_keysym_t *key = binding->keys->items[i];
760 if ((match = check_key(*key, 0)) == false) {
761 break;
762 }
763 }
764 }
765
766 return match;
767}
768
769static bool handle_bindsym_release(struct sway_binding *binding) {
770 if (binding->keys->length == 1) {
771 xkb_keysym_t *key = binding->keys->items[0];
772 if (check_released_key(*key)) {
773 handle_binding_command(binding);
774 return true;
775 }
776 }
777
778 return false;
779}
780
781static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifiers *modifiers,
782 uint32_t key, enum wlc_key_state state) {
783
784 if (desktop_shell.is_locked) {
785 return EVENT_PASSTHROUGH;
786 }
787
788 // reset pointer mode on keypress
789 if (state == WLC_KEY_STATE_PRESSED && pointer_state.mode) {
790 pointer_mode_reset();
791 }
792
793 struct sway_mode *mode = config->current_mode;
794
795 struct wlc_modifiers no_mods = { 0, 0 };
796 uint32_t sym = tolower(wlc_keyboard_get_keysym_for_key(key, &no_mods));
797
798 int i;
799
800 if (state == WLC_KEY_STATE_PRESSED) {
801 press_key(sym, key);
802 } else { // WLC_KEY_STATE_RELEASED
803 release_key(sym, key);
804 }
805
806 // handle bar modifiers pressed/released
807 uint32_t modifier;
808 for (i = 0; i < config->active_bar_modifiers->length; ++i) {
809 modifier = *(uint32_t *)config->active_bar_modifiers->items[i];
810
811 switch (modifier_state_changed(modifiers->mods, modifier)) {
812 case MOD_STATE_PRESSED:
813 ipc_event_modifier(modifier, "pressed");
814 break;
815 case MOD_STATE_RELEASED:
816 ipc_event_modifier(modifier, "released");
817 break;
818 }
819 }
820 // update modifiers state
821 modifiers_state_update(modifiers->mods);
822
823 // handle bindings
824 list_t *candidates = create_list();
825 for (i = 0; i < mode->bindings->length; ++i) {
826 struct sway_binding *binding = mode->bindings->items[i];
827 if ((modifiers->mods ^ binding->modifiers) == 0) {
828 switch (state) {
829 case WLC_KEY_STATE_PRESSED:
830 if (!binding->release && valid_bindsym(binding)) {
831 list_add(candidates, binding);
832 }
833 break;
834 case WLC_KEY_STATE_RELEASED:
835 if (binding->release && handle_bindsym_release(binding)) {
836 list_free(candidates);
837 return EVENT_HANDLED;
838 }
839 break;
840 }
841 }
842 }
843
844 for (i = 0; i < candidates->length; ++i) {
845 struct sway_binding *binding = candidates->items[i];
846 if (state == WLC_KEY_STATE_PRESSED) {
847 if (!binding->release && handle_bindsym(binding, sym, key)) {
848 list_free(candidates);
849 return EVENT_HANDLED;
850 }
851 }
852 }
853
854 list_free(candidates);
855
856 swayc_t *focused = get_focused_container(&root_container);
857 if (focused->type == C_VIEW) {
858 pid_t pid = wlc_view_get_pid(focused->handle);
859 if (!(get_feature_policy_mask(pid) & FEATURE_KEYBOARD)) {
860 return EVENT_HANDLED;
861 }
862 }
863 return EVENT_PASSTHROUGH;
864}
865
866static bool handle_pointer_motion(wlc_handle handle, uint32_t time, double x, double y) {
867 if (desktop_shell.is_locked) {
868 return EVENT_PASSTHROUGH;
869 }
870
871 double new_x = x;
872 double new_y = y;
873 // Switch to adjacent output if touching output edge.
874 //
875 // Since this doesn't currently support moving windows between outputs we
876 // don't do the switch if the pointer is in a mode.
877 if (config->seamless_mouse && !pointer_state.mode &&
878 !pointer_state.left.held && !pointer_state.right.held && !pointer_state.scroll.held) {
879
880 swayc_t *output = swayc_active_output(), *adjacent = NULL;
881 struct wlc_point abs_pos = { .x = x + output->x, .y = y + output->y };
882 if (x <= 0) { // Left edge
883 if ((adjacent = swayc_adjacent_output(output, MOVE_LEFT, &abs_pos, false))) {
884 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
885 new_x = adjacent->width;
886 // adjust for differently aligned outputs (well, this is
887 // only correct when the two outputs have the same
888 // resolution or the same dpi I guess, it should take
889 // physical attributes into account)
890 new_y += (output->y - adjacent->y);
891 }
892 }
893 } else if (x >= output->width) { // Right edge
894 if ((adjacent = swayc_adjacent_output(output, MOVE_RIGHT, &abs_pos, false))) {
895 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
896 new_x = 0;
897 new_y += (output->y - adjacent->y);
898 }
899 }
900 } else if (y <= 0) { // Top edge
901 if ((adjacent = swayc_adjacent_output(output, MOVE_UP, &abs_pos, false))) {
902 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
903 new_y = adjacent->height;
904 new_x += (output->x - adjacent->x);
905 }
906 }
907 } else if (y >= output->height) { // Bottom edge
908 if ((adjacent = swayc_adjacent_output(output, MOVE_DOWN, &abs_pos, false))) {
909 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
910 new_y = 0;
911 new_x += (output->x - adjacent->x);
912 }
913 }
914 }
915 }
916
917 pointer_position_set(new_x, new_y, false);
918
919 swayc_t *focused = get_focused_container(&root_container);
920 if (focused->type == C_VIEW) {
921 pid_t pid = wlc_view_get_pid(focused->handle);
922 if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
923 return EVENT_HANDLED;
924 }
925 }
926
927 return EVENT_PASSTHROUGH;
928}
929
930static bool swayc_border_check(swayc_t *c, const void *_origin) {
931 const struct wlc_point *origin = _origin;
932 const struct wlc_geometry title_bar = c->title_bar_geometry;
933
934 if (c->border_type != B_NORMAL) {
935 return false;
936 }
937
938 if (origin->x >= title_bar.origin.x && origin->y >= title_bar.origin.y
939 && origin->x < title_bar.origin.x + (int32_t)title_bar.size.w
940 && origin->y < title_bar.origin.y + (int32_t)title_bar.size.h) {
941 return true;
942 }
943 return false;
944}
945
946static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct wlc_modifiers *modifiers,
947 uint32_t button, enum wlc_button_state state, const struct wlc_point *origin) {
948
949 // Update view pointer is on
950 pointer_state.view = container_under_pointer();
951
952 struct sway_mode *mode = config->current_mode;
953 // handle bindings
954 for (int i = 0; i < mode->bindings->length; ++i) {
955 struct sway_binding *binding = mode->bindings->items[i];
956 if ((modifiers->mods ^ binding->modifiers) == 0) {
957 switch (state) {
958 case WLC_BUTTON_STATE_PRESSED:
959 if (!binding->release && handle_bindsym(binding, button, 0)) {
960 return EVENT_HANDLED;
961 }
962 break;
963 case WLC_BUTTON_STATE_RELEASED:
964 if (binding->release && handle_bindsym(binding, button, 0)) {
965 return EVENT_HANDLED;
966 }
967 break;
968 }
969 }
970 }
971
972 // Update pointer_state
973 switch (button) {
974 case M_LEFT_CLICK:
975 if (state == WLC_BUTTON_STATE_PRESSED) {
976 pointer_state.left.held = true;
977 pointer_state.left.x = origin->x;
978 pointer_state.left.y = origin->y;
979 pointer_state.left.view = pointer_state.view;
980 } else {
981 pointer_state.left.held = false;
982 }
983 break;
984
985 case M_RIGHT_CLICK:
986 if (state == WLC_BUTTON_STATE_PRESSED) {
987 pointer_state.right.held = true;
988 pointer_state.right.x = origin->x;
989 pointer_state.right.y = origin->y;
990 pointer_state.right.view = pointer_state.view;
991 } else {
992 pointer_state.right.held = false;
993 }
994 break;
995
996 case M_SCROLL_CLICK:
997 if (state == WLC_BUTTON_STATE_PRESSED) {
998 pointer_state.scroll.held = true;
999 pointer_state.scroll.x = origin->x;
1000 pointer_state.scroll.y = origin->y;
1001 pointer_state.scroll.view = pointer_state.view;
1002 } else {
1003 pointer_state.scroll.held = false;
1004 }
1005 break;
1006
1007 //TODO scrolling behavior
1008 case M_SCROLL_UP:
1009 case M_SCROLL_DOWN:
1010 break;
1011 }
1012
1013 // get focused window and check if to change focus on mouse click
1014 swayc_t *focused = get_focused_container(&root_container);
1015
1016 // don't change focus or mode if fullscreen
1017 if (swayc_is_fullscreen(focused)) {
1018 if (focused->type == C_VIEW) {
1019 pid_t pid = wlc_view_get_pid(focused->handle);
1020 if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
1021 return EVENT_HANDLED;
1022 }
1023 }
1024 return EVENT_PASSTHROUGH;
1025 }
1026
1027 // set pointer mode only if floating mod has been set
1028 if (config->floating_mod) {
1029 pointer_mode_set(button, !(modifiers->mods ^ config->floating_mod));
1030 }
1031
1032 // Check whether to change focus
1033 swayc_t *pointer = pointer_state.view;
1034 if (pointer) {
1035 swayc_t *ws = swayc_parent_by_type(focused, C_WORKSPACE);
1036 if (ws != NULL) {
1037 swayc_t *find = container_find(ws, &swayc_border_check, origin);
1038 if (find != NULL) {
1039 set_focused_container(find);
1040 return EVENT_HANDLED;
1041 }
1042 }
1043
1044 if (focused != pointer) {
1045 set_focused_container(pointer_state.view);
1046 }
1047 // Send to front if floating
1048 if (pointer->is_floating) {
1049 int i;
1050 for (i = 0; i < pointer->parent->floating->length; i++) {
1051 if (pointer->parent->floating->items[i] == pointer) {
1052 list_del(pointer->parent->floating, i);
1053 list_add(pointer->parent->floating, pointer);
1054 break;
1055 }
1056 }
1057 wlc_view_bring_to_front(pointer->handle);
1058 }
1059 }
1060
1061 // Return if mode has been set
1062 if (pointer_state.mode) {
1063 return EVENT_HANDLED;
1064 }
1065
1066 if (focused->type == C_VIEW) {
1067 pid_t pid = wlc_view_get_pid(focused->handle);
1068 if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
1069 return EVENT_HANDLED;
1070 }
1071 }
1072
1073 // Always send mouse release
1074 if (state == WLC_BUTTON_STATE_RELEASED) {
1075 return EVENT_PASSTHROUGH;
1076 }
1077
1078 // Finally send click
1079 return EVENT_PASSTHROUGH;
1080}
1081
1082bool handle_pointer_scroll(wlc_handle view, uint32_t time, const struct wlc_modifiers* modifiers,
1083 uint8_t axis_bits, double _amount[2]) {
1084 if (!(modifiers->mods ^ config->floating_mod)) {
1085 int x_amount = (int)_amount[0];
1086 int y_amount = (int)_amount[1];
1087
1088 if (x_amount > 0 && strcmp(config->floating_scroll_up_cmd, "")) {
1089 handle_command(config->floating_scroll_up_cmd, CONTEXT_BINDING);
1090 return EVENT_HANDLED;
1091 } else if (x_amount < 0 && strcmp(config->floating_scroll_down_cmd, "")) {
1092 handle_command(config->floating_scroll_down_cmd, CONTEXT_BINDING);
1093 return EVENT_HANDLED;
1094 }
1095
1096 if (y_amount > 0 && strcmp(config->floating_scroll_right_cmd, "")) {
1097 handle_command(config->floating_scroll_right_cmd, CONTEXT_BINDING);
1098 return EVENT_HANDLED;
1099 } else if (y_amount < 0 && strcmp(config->floating_scroll_left_cmd, "")) {
1100 handle_command(config->floating_scroll_left_cmd, CONTEXT_BINDING);
1101 return EVENT_HANDLED;
1102 }
1103 }
1104 return EVENT_PASSTHROUGH;
1105}
1106
1107static void handle_wlc_ready(void) {
1108 sway_log(L_DEBUG, "Compositor is ready, executing cmds in queue");
1109 // Execute commands until there are none left
1110 config->active = true;
1111 while (config->cmd_queue->length) {
1112 char *line = config->cmd_queue->items[0];
1113 struct cmd_results *res = handle_command(line, CONTEXT_CONFIG);
1114 if (res->status != CMD_SUCCESS) {
1115 sway_log(L_ERROR, "Error on line '%s': %s", line, res->error);
1116 }
1117 free_cmd_results(res);
1118 free(line);
1119 list_del(config->cmd_queue, 0);
1120 }
1121}
1122
1123void register_wlc_handlers() {
1124 wlc_set_output_created_cb(handle_output_created);
1125 wlc_set_output_destroyed_cb(handle_output_destroyed);
1126 wlc_set_output_resolution_cb(handle_output_resolution_change);
1127 wlc_set_output_focus_cb(handle_output_focused);
1128 wlc_set_output_render_post_cb(handle_output_post_render);
1129 wlc_set_view_created_cb(handle_view_created);
1130 wlc_set_view_destroyed_cb(handle_view_destroyed);
1131 wlc_set_view_focus_cb(handle_view_focus);
1132 wlc_set_view_render_pre_cb(handle_view_pre_render);
1133 wlc_set_view_request_geometry_cb(handle_view_geometry_request);
1134 wlc_set_view_request_state_cb(handle_view_state_request);
1135 wlc_set_view_properties_updated_cb(handle_view_properties_updated);
1136 wlc_set_keyboard_key_cb(handle_key);
1137 wlc_set_pointer_motion_cb_v2(handle_pointer_motion);
1138 wlc_set_pointer_button_cb(handle_pointer_button);
1139 wlc_set_pointer_scroll_cb(handle_pointer_scroll);
1140 wlc_set_compositor_ready_cb(handle_wlc_ready);
1141 wlc_set_input_created_cb(handle_input_created);
1142 wlc_set_input_destroyed_cb(handle_input_destroyed);
1143}
diff --git a/sway/old/input_state.c b/sway/old/input_state.c
deleted file mode 100644
index 04aafd37..00000000
--- a/sway/old/input_state.c
+++ /dev/null
@@ -1,490 +0,0 @@
1#include <string.h>
2#include <stdbool.h>
3#include <ctype.h>
4#include "sway/input_state.h"
5#include "sway/config.h"
6#include "log.h"
7
8#define KEY_STATE_MAX_LENGTH 64
9
10struct key_state {
11 /*
12 * Aims to store state regardless of modifiers.
13 * If you press a key, then hold shift, then release the key, we'll
14 * get two different key syms, but the same key code. This handles
15 * that scenario and makes sure we can use the right bindings.
16 */
17 uint32_t key_sym;
18 uint32_t alt_sym;
19 uint32_t key_code;
20};
21
22static struct key_state key_state_array[KEY_STATE_MAX_LENGTH];
23
24static struct key_state last_released;
25
26static uint32_t modifiers_state;
27
28void input_init(void) {
29 int i;
30 for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) {
31 struct key_state none = { 0, 0, 0 };
32 key_state_array[i] = none;
33 }
34
35 struct key_state none = { 0, 0, 0 };
36 last_released = none;
37
38 modifiers_state = 0;
39}
40
41uint32_t modifier_state_changed(uint32_t new_state, uint32_t mod) {
42 if ((new_state & mod) != 0) { // pressed
43 if ((modifiers_state & mod) != 0) { // already pressed
44 return MOD_STATE_UNCHANGED;
45 } else { // pressed
46 return MOD_STATE_PRESSED;
47 }
48 } else { // not pressed
49 if ((modifiers_state & mod) != 0) { // released
50 return MOD_STATE_RELEASED;
51 } else { // already released
52 return MOD_STATE_UNCHANGED;
53 }
54 }
55}
56
57void modifiers_state_update(uint32_t new_state) {
58 modifiers_state = new_state;
59}
60
61static uint8_t find_key(uint32_t key_sym, uint32_t key_code, bool update) {
62 int i;
63 for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) {
64 if (0 == key_sym && 0 == key_code && key_state_array[i].key_sym == 0) {
65 break;
66 }
67 if (key_sym != 0 && (key_state_array[i].key_sym == key_sym
68 || key_state_array[i].alt_sym == key_sym)) {
69 break;
70 }
71 if (update && key_state_array[i].key_code == key_code) {
72 key_state_array[i].alt_sym = key_sym;
73 break;
74 }
75 if (key_sym == 0 && key_code != 0 && key_state_array[i].key_code == key_code) {
76 break;
77 }
78 }
79 return i;
80}
81
82bool check_key(uint32_t key_sym, uint32_t key_code) {
83 return find_key(key_sym, key_code, false) < KEY_STATE_MAX_LENGTH;
84}
85
86bool check_released_key(uint32_t key_sym) {
87 return (key_sym != 0
88 && (last_released.key_sym == key_sym
89 || last_released.alt_sym == key_sym));
90}
91
92void press_key(uint32_t key_sym, uint32_t key_code) {
93 if (key_code == 0) {
94 return;
95 }
96 // Check if key exists
97 if (!check_key(key_sym, key_code)) {
98 // Check that we don't exceed buffer length
99 int insert = find_key(0, 0, true);
100 if (insert < KEY_STATE_MAX_LENGTH) {
101 key_state_array[insert].key_sym = key_sym;
102 key_state_array[insert].key_code = key_code;
103 }
104 }
105}
106
107void release_key(uint32_t key_sym, uint32_t key_code) {
108 uint8_t index = find_key(key_sym, key_code, true);
109 if (index < KEY_STATE_MAX_LENGTH) {
110 last_released.key_sym = key_state_array[index].key_sym;
111 last_released.alt_sym = key_state_array[index].alt_sym;
112 last_released.key_code = key_state_array[index].key_code;
113 struct key_state none = { 0, 0, 0 };
114 key_state_array[index] = none;
115 }
116}
117
118// Pointer state and mode
119
120struct pointer_state pointer_state;
121
122static struct mode_state {
123 // initial view state
124 double x, y, w, h;
125 swayc_t *ptr;
126 // Containers used for resizing horizontally
127 struct {
128 double w;
129 swayc_t *ptr;
130 struct {
131 double w;
132 swayc_t *ptr;
133 } parent;
134 } horiz;
135 // Containers used for resizing vertically
136 struct {
137 double h;
138 swayc_t *ptr;
139 struct {
140 double h;
141 swayc_t *ptr;
142 } parent;
143 } vert;
144} initial;
145
146static struct {
147 bool left;
148 bool top;
149} lock;
150
151// initial set/unset
152
153static void set_initial_view(swayc_t *view) {
154 initial.ptr = view;
155 initial.x = view->x;
156 initial.y = view->y;
157 initial.w = view->width;
158 initial.h = view->height;
159}
160
161static void set_initial_sibling(void) {
162 bool reset = true;
163 swayc_t *ws = swayc_active_workspace_for(initial.ptr);
164 if ((initial.horiz.ptr = get_swayc_in_direction_under(initial.ptr,
165 lock.left ? MOVE_RIGHT: MOVE_LEFT, ws))) {
166 initial.horiz.w = initial.horiz.ptr->width;
167 initial.horiz.parent.ptr = get_swayc_in_direction_under(initial.horiz.ptr,
168 lock.left ? MOVE_LEFT : MOVE_RIGHT, ws);
169 initial.horiz.parent.w = initial.horiz.parent.ptr->width;
170 reset = false;
171 }
172 if ((initial.vert.ptr = get_swayc_in_direction_under(initial.ptr,
173 lock.top ? MOVE_DOWN: MOVE_UP, ws))) {
174 initial.vert.h = initial.vert.ptr->height;
175 initial.vert.parent.ptr = get_swayc_in_direction_under(initial.vert.ptr,
176 lock.top ? MOVE_UP : MOVE_DOWN, ws);
177 initial.vert.parent.h = initial.vert.parent.ptr->height;
178 reset = false;
179 }
180 // If nothing will change just undo the mode
181 if (reset) {
182 pointer_state.mode = 0;
183 }
184}
185
186static void reset_initial_view(void) {
187 initial.ptr->x = initial.x;
188 initial.ptr->y = initial.y;
189 initial.ptr->width = initial.w;
190 initial.ptr->height = initial.h;
191 arrange_windows(initial.ptr, -1, -1);
192 pointer_state.mode = 0;
193}
194
195static void reset_initial_sibling(void) {
196 initial.horiz.ptr->width = initial.horiz.w;
197 initial.horiz.parent.ptr->width = initial.horiz.parent.w;
198 initial.vert.ptr->height = initial.vert.h;
199 initial.vert.parent.ptr->height = initial.vert.parent.h;
200 arrange_windows(initial.horiz.ptr->parent, -1, -1);
201 arrange_windows(initial.vert.ptr->parent, -1, -1);
202 pointer_state.mode = 0;
203}
204
205void pointer_position_set(double new_x, double new_y, bool force_focus) {
206 double x, y;
207 wlc_pointer_get_position_v2(&x, &y);
208 pointer_state.delta.x = new_x - x;
209 pointer_state.delta.y = new_y - y;
210
211 wlc_pointer_set_position_v2(new_x, new_y);
212
213 // Update view under pointer
214 swayc_t *prev_view = pointer_state.view;
215 pointer_state.view = container_under_pointer();
216
217 // If pointer is in a mode, update it
218 if (pointer_state.mode) {
219 pointer_mode_update();
220 // Otherwise change focus if config is set
221 } else if (force_focus || (prev_view != pointer_state.view && config->focus_follows_mouse)) {
222 if (pointer_state.view && pointer_state.view->type == C_VIEW) {
223 set_focused_container(pointer_state.view);
224 }
225 }
226}
227
228void center_pointer_on(swayc_t *view) {
229 pointer_position_set(view->x + view->width/2, view->y + view->height/2, true);
230}
231
232// Mode set left/right click
233
234static void pointer_mode_set_dragging(void) {
235 switch (config->dragging_key) {
236 case M_LEFT_CLICK:
237 set_initial_view(pointer_state.left.view);
238 break;
239 case M_RIGHT_CLICK:
240 set_initial_view(pointer_state.right.view);
241 break;
242 }
243
244 if (initial.ptr->is_floating) {
245 pointer_state.mode = M_DRAGGING | M_FLOATING;
246 } else {
247 pointer_state.mode = M_DRAGGING | M_TILING;
248 // unset mode if we can't drag tile
249 if (initial.ptr->parent->type == C_WORKSPACE &&
250 initial.ptr->parent->children->length == 1) {
251 pointer_state.mode = 0;
252 }
253 }
254}
255
256static void pointer_mode_set_resizing(void) {
257 switch (config->resizing_key) {
258 case M_LEFT_CLICK:
259 set_initial_view(pointer_state.left.view);
260 break;
261 case M_RIGHT_CLICK:
262 set_initial_view(pointer_state.right.view);
263 break;
264 }
265 // Setup locking information
266 int midway_x = initial.ptr->x + initial.ptr->width/2;
267 int midway_y = initial.ptr->y + initial.ptr->height/2;
268
269 double x, y;
270 wlc_pointer_get_position_v2(&x, &y);
271 lock.left = x > midway_x;
272 lock.top = y > midway_y;
273
274 if (initial.ptr->is_floating) {
275 pointer_state.mode = M_RESIZING | M_FLOATING;
276 } else {
277 pointer_state.mode = M_RESIZING | M_TILING;
278 set_initial_sibling();
279 }
280}
281
282// Mode set/update/reset
283
284void pointer_mode_set(uint32_t button, bool condition) {
285 // switch on drag/resize mode
286 switch (pointer_state.mode & (M_DRAGGING | M_RESIZING)) {
287 case M_DRAGGING:
288 // end drag mode when 'dragging' click is unpressed
289 if (config->dragging_key == M_LEFT_CLICK && !pointer_state.left.held) {
290 pointer_state.mode = 0;
291 } else if (config->dragging_key == M_RIGHT_CLICK && !pointer_state.right.held) {
292 pointer_state.mode = 0;
293 }
294 break;
295
296 case M_RESIZING:
297 // end resize mode when 'resizing' click is unpressed
298 if (config->resizing_key == M_LEFT_CLICK && !pointer_state.left.held) {
299 pointer_state.mode = 0;
300 } else if (config->resizing_key == M_RIGHT_CLICK && !pointer_state.right.held) {
301 pointer_state.mode = 0;
302 }
303 break;
304
305 // No mode case
306 default:
307 // return if failed condition, or no view
308 if (!condition || !pointer_state.view) {
309 break;
310 }
311
312 // Set mode depending on current button press
313 switch (button) {
314 // Start left-click mode
315 case M_LEFT_CLICK:
316 // if button release don't do anything
317 if (pointer_state.left.held) {
318 if (config->dragging_key == M_LEFT_CLICK) {
319 pointer_mode_set_dragging();
320 } else if (config->resizing_key == M_LEFT_CLICK) {
321 pointer_mode_set_resizing();
322 }
323 }
324 break;
325
326 // Start right-click mode
327 case M_RIGHT_CLICK:
328 // if button release don't do anyhting
329 if (pointer_state.right.held) {
330 if (config->dragging_key == M_RIGHT_CLICK) {
331 pointer_mode_set_dragging();
332 } else if (config->resizing_key == M_RIGHT_CLICK) {
333 pointer_mode_set_resizing();
334 }
335 }
336 break;
337 }
338 }
339}
340
341void pointer_mode_update(void) {
342 if (initial.ptr->type != C_VIEW) {
343 pointer_state.mode = 0;
344 return;
345 }
346 double x, y;
347 wlc_pointer_get_position_v2(&x, &y);
348 int dx = x;
349 int dy = y;
350
351 switch (pointer_state.mode) {
352 case M_FLOATING | M_DRAGGING:
353 // Update position
354 switch (config->dragging_key) {
355 case M_LEFT_CLICK:
356 dx -= pointer_state.left.x;
357 dy -= pointer_state.left.y;
358 break;
359 case M_RIGHT_CLICK:
360 dx -= pointer_state.right.x;
361 dy -= pointer_state.right.y;
362 break;
363 }
364
365 if (initial.x + dx != initial.ptr->x) {
366 initial.ptr->x = initial.x + dx;
367 }
368 if (initial.y + dy != initial.ptr->y) {
369 initial.ptr->y = initial.y + dy;
370 }
371 update_geometry(initial.ptr);
372 break;
373
374 case M_FLOATING | M_RESIZING:
375 switch (config->resizing_key) {
376 case M_LEFT_CLICK:
377 dx -= pointer_state.left.x;
378 dy -= pointer_state.left.y;
379 initial.ptr = pointer_state.left.view;
380 break;
381 case M_RIGHT_CLICK:
382 dx -= pointer_state.right.x;
383 dy -= pointer_state.right.y;
384 initial.ptr = pointer_state.right.view;
385 break;
386 }
387
388 if (lock.left) {
389 if (initial.w + dx > min_sane_w) {
390 initial.ptr->width = initial.w + dx;
391 }
392 } else { // lock.right
393 if (initial.w - dx > min_sane_w) {
394 initial.ptr->width = initial.w - dx;
395 initial.ptr->x = initial.x + dx;
396 }
397 }
398 if (lock.top) {
399 if (initial.h + dy > min_sane_h) {
400 initial.ptr->height = initial.h + dy;
401 }
402 } else { // lock.bottom
403 if (initial.h - dy > min_sane_h) {
404 initial.ptr->height = initial.h - dy;
405 initial.ptr->y = initial.y + dy;
406 }
407 }
408 update_geometry(initial.ptr);
409 break;
410
411 case M_TILING | M_DRAGGING:
412 // swap current view under pointer with dragged view
413 if (pointer_state.view && pointer_state.view->type == C_VIEW
414 && pointer_state.view != initial.ptr
415 && !pointer_state.view->is_floating) {
416 // Swap them around
417 swap_container(pointer_state.view, initial.ptr);
418 swap_geometry(pointer_state.view, initial.ptr);
419 update_geometry(pointer_state.view);
420 update_geometry(initial.ptr);
421 // Set focus back to initial view
422 set_focused_container(initial.ptr);
423 // Arrange the windows
424 arrange_windows(&root_container, -1, -1);
425 }
426 break;
427
428 case M_TILING | M_RESIZING:
429 switch (config->resizing_key) {
430 case M_LEFT_CLICK:
431 dx -= pointer_state.left.x;
432 dy -= pointer_state.left.y;
433 break;
434 case M_RIGHT_CLICK:
435 dx -= pointer_state.right.x;
436 dy -= pointer_state.right.y;
437 break;
438 }
439
440 // resize if we can
441 if (initial.horiz.ptr) {
442 if (lock.left) {
443 // Check whether its fine to resize
444 if (initial.w + dx > min_sane_w && initial.horiz.w - dx > min_sane_w) {
445 initial.horiz.ptr->width = initial.horiz.w - dx;
446 initial.horiz.parent.ptr->width = initial.horiz.parent.w + dx;
447 }
448 } else { // lock.right
449 if (initial.w - dx > min_sane_w && initial.horiz.w + dx > min_sane_w) {
450 initial.horiz.ptr->width = initial.horiz.w + dx;
451 initial.horiz.parent.ptr->width = initial.horiz.parent.w - dx;
452 }
453 }
454 arrange_windows(initial.horiz.ptr->parent, -1, -1);
455 }
456 if (initial.vert.ptr) {
457 if (lock.top) {
458 if (initial.h + dy > min_sane_h && initial.vert.h - dy > min_sane_h) {
459 initial.vert.ptr->height = initial.vert.h - dy;
460 initial.vert.parent.ptr->height = initial.vert.parent.h + dy;
461 }
462 } else { // lock.bottom
463 if (initial.h - dy > min_sane_h && initial.vert.h + dy > min_sane_h) {
464 initial.vert.ptr->height = initial.vert.h + dy;
465 initial.vert.parent.ptr->height = initial.vert.parent.h - dy;
466 }
467 }
468 arrange_windows(initial.vert.ptr->parent, -1, -1);
469 }
470 default:
471 return;
472 }
473}
474
475void pointer_mode_reset(void) {
476 switch (pointer_state.mode) {
477 case M_FLOATING | M_RESIZING:
478 case M_FLOATING | M_DRAGGING:
479 reset_initial_view();
480 break;
481
482 case M_TILING | M_RESIZING:
483 (void) reset_initial_sibling;
484 break;
485
486 case M_TILING | M_DRAGGING:
487 default:
488 break;
489 }
490}
diff --git a/sway/old/ipc-json.c b/sway/old/ipc-json.c
deleted file mode 100644
index 1579a2d9..00000000
--- a/sway/old/ipc-json.c
+++ /dev/null
@@ -1,521 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <json-c/json.h>
3#include <ctype.h>
4#include <string.h>
5#include <stdint.h>
6#include <libinput.h>
7#include <wlr/types/wlr_box.h>
8#include <wlr/types/wlr_output.h>
9#include "sway/output.h"
10#include "sway/container.h"
11#include "sway/input.h"
12#include "sway/ipc-json.h"
13#include "util.h"
14
15static json_object *ipc_json_create_rect(swayc_t *c) {
16 json_object *rect = json_object_new_object();
17
18 json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x));
19 json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y));
20
21 struct wlr_box box;
22 if (c->type == C_OUTPUT) {
23 wlr_output_effective_resolution(c->_handle.output->wlr_output,
24 &box.width, &box.height);
25 } else {
26 box.width = c->width;
27 box.width = c->height;
28 }
29
30 json_object_object_add(rect, "width",
31 json_object_new_int((int32_t)box.width));
32 json_object_object_add(rect, "height",
33 json_object_new_int((int32_t)box.height));
34
35 return rect;
36}
37
38static json_object *ipc_json_create_rect_from_geometry(struct wlc_geometry g) {
39 json_object *rect = json_object_new_object();
40
41 json_object_object_add(rect, "x", json_object_new_int(g.origin.x));
42 json_object_object_add(rect, "y", json_object_new_int(g.origin.y));
43 json_object_object_add(rect, "width", json_object_new_int(g.size.w));
44 json_object_object_add(rect, "height", json_object_new_int(g.size.h));
45
46 return rect;
47}
48
49static const char *ipc_json_border_description(swayc_t *c) {
50 const char *border;
51
52 switch (c->border_type) {
53 case B_PIXEL:
54 border = "1pixel";
55 break;
56
57 case B_NORMAL:
58 border = "normal";
59 break;
60
61 case B_NONE: // fallthrough
62 default:
63 border = "none";
64 break;
65 }
66
67 return border;
68}
69
70static const char *ipc_json_layout_description(enum swayc_layouts l) {
71 const char *layout;
72
73 switch (l) {
74 case L_VERT:
75 layout = "splitv";
76 break;
77
78 case L_HORIZ:
79 layout = "splith";
80 break;
81
82 case L_TABBED:
83 layout = "tabbed";
84 break;
85
86 case L_STACKED:
87 layout = "stacked";
88 break;
89
90 case L_FLOATING:
91 layout = "floating";
92 break;
93
94 case L_NONE: // fallthrough
95 case L_LAYOUTS: // fallthrough; this should never happen, I'm just trying to silence compiler warnings
96 default:
97 layout = "null";
98 break;
99 }
100
101 return layout;
102}
103
104static float ipc_json_child_percentage(swayc_t *c) {
105 float percent = 0;
106 swayc_t *parent = c->parent;
107
108 if (parent) {
109 switch (parent->layout) {
110 case L_VERT:
111 percent = c->height / parent->height;
112 break;
113
114 case L_HORIZ:
115 percent = c->width / parent->width;
116 break;
117
118 case L_STACKED: // fallthrough
119 case L_TABBED: // fallthrough
120 percent = 1.0;
121 break;
122
123 default:
124 break;
125 }
126 }
127
128 return percent;
129}
130
131static void ipc_json_describe_output(swayc_t *output, json_object *object) {
132 uint32_t scale = wlc_output_get_scale(output->handle);
133 json_object_object_add(object, "active", json_object_new_boolean(true));
134 json_object_object_add(object, "primary", json_object_new_boolean(false));
135 json_object_object_add(object, "layout", json_object_new_string("output"));
136 json_object_object_add(object, "type", json_object_new_string("output"));
137 json_object_object_add(object, "current_workspace",
138 (output->focused) ? json_object_new_string(output->focused->name) : NULL);
139 json_object_object_add(object, "scale", json_object_new_int(scale));
140}
141
142static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object) {
143 int num = (isdigit(workspace->name[0])) ? atoi(workspace->name) : -1;
144 const char *layout = ipc_json_layout_description(workspace->workspace_layout);
145
146 json_object_object_add(object, "num", json_object_new_int(num));
147 json_object_object_add(object, "output", (workspace->parent) ? json_object_new_string(workspace->parent->name) : NULL);
148 json_object_object_add(object, "type", json_object_new_string("workspace"));
149 json_object_object_add(object, "layout", (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout));
150}
151
152// window is in the scratchpad ? changed : none
153static const char *ipc_json_get_scratchpad_state(swayc_t *c) {
154 int i;
155 for (i = 0; i < scratchpad->length; i++) {
156 if (scratchpad->items[i] == c) {
157 return "changed";
158 }
159 }
160 return "none"; // we ignore the fresh value
161}
162
163static void ipc_json_describe_view(swayc_t *c, json_object *object) {
164 json_object *props = json_object_new_object();
165 json_object_object_add(object, "type", json_object_new_string((c->is_floating) ? "floating_con" : "con"));
166
167 wlc_handle parent = wlc_view_get_parent(c->handle);
168 json_object_object_add(object, "scratchpad_state",
169 json_object_new_string(ipc_json_get_scratchpad_state(c)));
170
171 json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL);
172
173 json_object_object_add(props, "class", c->class ? json_object_new_string(c->class) :
174 c->app_id ? json_object_new_string(c->app_id) : NULL);
175 json_object_object_add(props, "instance", c->instance ? json_object_new_string(c->instance) :
176 c->app_id ? json_object_new_string(c->app_id) : NULL);
177 json_object_object_add(props, "title", (c->name) ? json_object_new_string(c->name) : NULL);
178 json_object_object_add(props, "transient_for", parent ? json_object_new_int(parent) : NULL);
179 json_object_object_add(object, "window_properties", props);
180
181 json_object_object_add(object, "fullscreen_mode",
182 json_object_new_int(swayc_is_fullscreen(c) ? 1 : 0));
183 json_object_object_add(object, "sticky", json_object_new_boolean(c->sticky));
184 json_object_object_add(object, "floating", json_object_new_string(
185 c->is_floating ? "auto_on" : "auto_off")); // we can't state the cause
186
187 json_object_object_add(object, "app_id", c->app_id ? json_object_new_string(c->app_id) : NULL);
188
189 if (c->parent) {
190 const char *layout = (c->parent->type == C_CONTAINER) ?
191 ipc_json_layout_description(c->parent->layout) : "none";
192 const char *last_layout = (c->parent->type == C_CONTAINER) ?
193 ipc_json_layout_description(c->parent->prev_layout) : "none";
194 json_object_object_add(object, "layout",
195 (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout));
196 json_object_object_add(object, "last_split_layout",
197 (strcmp(last_layout, "null") == 0) ? NULL : json_object_new_string(last_layout));
198 json_object_object_add(object, "workspace_layout",
199 json_object_new_string(ipc_json_layout_description(swayc_parent_by_type(c, C_WORKSPACE)->workspace_layout)));
200 }
201}
202
203static void ipc_json_describe_root(swayc_t *c, json_object *object) {
204 json_object_object_add(object, "type", json_object_new_string("root"));
205 json_object_object_add(object, "layout", json_object_new_string("splith"));
206}
207
208json_object *ipc_json_describe_container(swayc_t *c) {
209 float percent = ipc_json_child_percentage(c);
210
211 if (!(sway_assert(c, "Container must not be null."))) {
212 return NULL;
213 }
214
215 json_object *object = json_object_new_object();
216
217 json_object_object_add(object, "id", json_object_new_int((int)c->id));
218 json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL);
219 json_object_object_add(object, "rect", ipc_json_create_rect(c));
220 json_object_object_add(object, "visible", json_object_new_boolean(c->visible));
221 json_object_object_add(object, "focused", json_object_new_boolean(c == current_focus));
222
223 json_object_object_add(object, "border", json_object_new_string(ipc_json_border_description(c)));
224 json_object_object_add(object, "window_rect", ipc_json_create_rect_from_geometry(c->actual_geometry));
225 json_object_object_add(object, "deco_rect", ipc_json_create_rect_from_geometry(c->title_bar_geometry));
226 json_object_object_add(object, "geometry", ipc_json_create_rect_from_geometry(c->cached_geometry));
227 json_object_object_add(object, "percent", (percent > 0) ? json_object_new_double(percent) : NULL);
228 json_object_object_add(object, "window", json_object_new_int(c->handle)); // for the sake of i3 compat
229 // TODO: make urgency actually work once Sway supports it
230 json_object_object_add(object, "urgent", json_object_new_boolean(false));
231 json_object_object_add(object, "current_border_width", json_object_new_int(c->border_thickness));
232
233 switch (c->type) {
234 case C_ROOT:
235 ipc_json_describe_root(c, object);
236 break;
237
238 case C_OUTPUT:
239 ipc_json_describe_output(c, object);
240 break;
241
242 case C_CONTAINER: // fallthrough
243 case C_VIEW:
244 ipc_json_describe_view(c, object);
245 break;
246
247 case C_WORKSPACE:
248 ipc_json_describe_workspace(c, object);
249 break;
250
251 case C_TYPES: // fallthrough; this should never happen, I'm just trying to silence compiler warnings
252 default:
253 break;
254 }
255
256 return object;
257}
258
259json_object *ipc_json_describe_input(struct libinput_device *device) {
260 char* identifier = libinput_dev_unique_id(device);
261 int vendor = libinput_device_get_id_vendor(device);
262 int product = libinput_device_get_id_product(device);
263 const char *name = libinput_device_get_name(device);
264 double width = -1, height = -1;
265 int has_size = libinput_device_get_size(device, &width, &height);
266
267 json_object *device_object = json_object_new_object();
268 json_object_object_add(device_object,"identifier",
269 identifier ? json_object_new_string(identifier) : NULL);
270 json_object_object_add(device_object,
271 "vendor", json_object_new_int(vendor));
272 json_object_object_add(device_object,
273 "product", json_object_new_int(product));
274 json_object_object_add(device_object,
275 "name", json_object_new_string(name));
276 if (has_size == 0) {
277 json_object *size_object = json_object_new_object();
278 json_object_object_add(size_object,
279 "width", json_object_new_double(width));
280 json_object_object_add(size_object,
281 "height", json_object_new_double(height));
282 } else {
283 json_object_object_add(device_object, "size", NULL);
284 }
285
286 struct {
287 enum libinput_device_capability cap;
288 const char *name;
289 // If anyone feels like implementing device-specific IPC output,
290 // be my guest
291 json_object *(*describe)(struct libinput_device *);
292 } caps[] = {
293 { LIBINPUT_DEVICE_CAP_KEYBOARD, "keyboard", NULL },
294 { LIBINPUT_DEVICE_CAP_POINTER, "pointer", NULL },
295 { LIBINPUT_DEVICE_CAP_TOUCH, "touch", NULL },
296 { LIBINPUT_DEVICE_CAP_TABLET_TOOL, "tablet_tool", NULL },
297 { LIBINPUT_DEVICE_CAP_TABLET_PAD, "tablet_pad", NULL },
298 { LIBINPUT_DEVICE_CAP_GESTURE, "gesture", NULL },
299#ifdef LIBINPUT_DEVICE_CAP_SWITCH // libinput 1.7.0+
300 { LIBINPUT_DEVICE_CAP_SWITCH, "switch", NULL },
301#endif
302 };
303
304 json_object *_caps = json_object_new_array();
305 for (size_t i = 0; i < sizeof(caps) / sizeof(caps[0]); ++i) {
306 if (libinput_device_has_capability(device, caps[i].cap)) {
307 json_object_array_add(_caps, json_object_new_string(caps[i].name));
308 if (caps[i].describe) {
309 json_object *desc = caps[i].describe(device);
310 json_object_object_add(device_object, caps[i].name, desc);
311 }
312 }
313 }
314 json_object_object_add(device_object, "capabilities", _caps);
315
316 free(identifier);
317 return device_object;
318}
319
320json_object *ipc_json_get_version() {
321 int major = 0, minor = 0, patch = 0;
322 json_object *version = json_object_new_object();
323
324 sscanf(SWAY_VERSION, "%u.%u.%u", &major, &minor, &patch);
325
326 json_object_object_add(version, "human_readable", json_object_new_string(SWAY_VERSION));
327 json_object_object_add(version, "variant", json_object_new_string("sway"));
328 json_object_object_add(version, "major", json_object_new_int(major));
329 json_object_object_add(version, "minor", json_object_new_int(minor));
330 json_object_object_add(version, "patch", json_object_new_int(patch));
331
332 return version;
333}
334
335json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
336 if (!sway_assert(bar, "Bar must not be NULL")) {
337 return NULL;
338 }
339
340 json_object *json = json_object_new_object();
341 json_object_object_add(json, "id", json_object_new_string(bar->id));
342#ifdef ENABLE_TRAY
343 if (bar->tray_output) {
344 json_object_object_add(json, "tray_output", json_object_new_string(bar->tray_output));
345 } else {
346 json_object_object_add(json, "tray_output", NULL);
347 }
348 if (bar->icon_theme) {
349 json_object_object_add(json, "icon_theme", json_object_new_string(bar->icon_theme));
350 } else {
351 json_object_object_add(json, "icon_theme", NULL);
352 }
353 json_object_object_add(json, "tray_padding", json_object_new_int(bar->tray_padding));
354 json_object_object_add(json, "activate_button", json_object_new_int(bar->activate_button));
355 json_object_object_add(json, "context_button", json_object_new_int(bar->context_button));
356 json_object_object_add(json, "secondary_button", json_object_new_int(bar->secondary_button));
357#endif
358 json_object_object_add(json, "mode", json_object_new_string(bar->mode));
359 json_object_object_add(json, "hidden_state", json_object_new_string(bar->hidden_state));
360 json_object_object_add(json, "modifier", json_object_new_string(get_modifier_name_by_mask(bar->modifier)));
361 switch (bar->position) {
362 case DESKTOP_SHELL_PANEL_POSITION_TOP:
363 json_object_object_add(json, "position", json_object_new_string("top"));
364 break;
365 case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
366 json_object_object_add(json, "position", json_object_new_string("bottom"));
367 break;
368 case DESKTOP_SHELL_PANEL_POSITION_LEFT:
369 json_object_object_add(json, "position", json_object_new_string("left"));
370 break;
371 case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
372 json_object_object_add(json, "position", json_object_new_string("right"));
373 break;
374 }
375 json_object_object_add(json, "status_command", json_object_new_string(bar->status_command));
376 json_object_object_add(json, "font", json_object_new_string((bar->font) ? bar->font : config->font));
377 if (bar->separator_symbol) {
378 json_object_object_add(json, "separator_symbol", json_object_new_string(bar->separator_symbol));
379 }
380 json_object_object_add(json, "bar_height", json_object_new_int(bar->height));
381 json_object_object_add(json, "wrap_scroll", json_object_new_boolean(bar->wrap_scroll));
382 json_object_object_add(json, "workspace_buttons", json_object_new_boolean(bar->workspace_buttons));
383 json_object_object_add(json, "strip_workspace_numbers", json_object_new_boolean(bar->strip_workspace_numbers));
384 json_object_object_add(json, "binding_mode_indicator", json_object_new_boolean(bar->binding_mode_indicator));
385 json_object_object_add(json, "verbose", json_object_new_boolean(bar->verbose));
386 json_object_object_add(json, "pango_markup", json_object_new_boolean(bar->pango_markup));
387
388 json_object *colors = json_object_new_object();
389 json_object_object_add(colors, "background", json_object_new_string(bar->colors.background));
390 json_object_object_add(colors, "statusline", json_object_new_string(bar->colors.statusline));
391 json_object_object_add(colors, "separator", json_object_new_string(bar->colors.separator));
392
393 if (bar->colors.focused_background) {
394 json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.focused_background));
395 } else {
396 json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.background));
397 }
398
399 if (bar->colors.focused_statusline) {
400 json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.focused_statusline));
401 } else {
402 json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.statusline));
403 }
404
405 if (bar->colors.focused_separator) {
406 json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.focused_separator));
407 } else {
408 json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.separator));
409 }
410
411 json_object_object_add(colors, "focused_workspace_border", json_object_new_string(bar->colors.focused_workspace_border));
412 json_object_object_add(colors, "focused_workspace_bg", json_object_new_string(bar->colors.focused_workspace_bg));
413 json_object_object_add(colors, "focused_workspace_text", json_object_new_string(bar->colors.focused_workspace_text));
414
415 json_object_object_add(colors, "inactive_workspace_border", json_object_new_string(bar->colors.inactive_workspace_border));
416 json_object_object_add(colors, "inactive_workspace_bg", json_object_new_string(bar->colors.inactive_workspace_bg));
417 json_object_object_add(colors, "inactive_workspace_text", json_object_new_string(bar->colors.inactive_workspace_text));
418
419 json_object_object_add(colors, "active_workspace_border", json_object_new_string(bar->colors.active_workspace_border));
420 json_object_object_add(colors, "active_workspace_bg", json_object_new_string(bar->colors.active_workspace_bg));
421 json_object_object_add(colors, "active_workspace_text", json_object_new_string(bar->colors.active_workspace_text));
422
423 json_object_object_add(colors, "urgent_workspace_border", json_object_new_string(bar->colors.urgent_workspace_border));
424 json_object_object_add(colors, "urgent_workspace_bg", json_object_new_string(bar->colors.urgent_workspace_bg));
425 json_object_object_add(colors, "urgent_workspace_text", json_object_new_string(bar->colors.urgent_workspace_text));
426
427 if (bar->colors.binding_mode_border) {
428 json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.binding_mode_border));
429 } else {
430 json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.urgent_workspace_border));
431 }
432
433 if (bar->colors.binding_mode_bg) {
434 json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.binding_mode_bg));
435 } else {
436 json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.urgent_workspace_bg));
437 }
438
439 if (bar->colors.binding_mode_text) {
440 json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.binding_mode_text));
441 } else {
442 json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.urgent_workspace_text));
443 }
444
445 json_object_object_add(json, "colors", colors);
446
447 // Add outputs if defined
448 if (bar->outputs && bar->outputs->length > 0) {
449 json_object *outputs = json_object_new_array();
450 int i;
451 for (i = 0; i < bar->outputs->length; ++i) {
452 const char *name = bar->outputs->items[i];
453 json_object_array_add(outputs, json_object_new_string(name));
454 }
455 json_object_object_add(json, "outputs", outputs);
456 }
457
458 return json;
459}
460
461json_object *ipc_json_describe_container_recursive(swayc_t *c) {
462 json_object *object = ipc_json_describe_container(c);
463 int i;
464
465 json_object *floating = json_object_new_array();
466 if (c->type != C_VIEW && c->floating) {
467 for (i = 0; i < c->floating->length; ++i) {
468 swayc_t *item = c->floating->items[i];
469 json_object_array_add(floating, ipc_json_describe_container_recursive(item));
470 }
471 }
472 json_object_object_add(object, "floating_nodes", floating);
473
474 json_object *children = json_object_new_array();
475 if (c->type != C_VIEW && c->children) {
476 for (i = 0; i < c->children->length; ++i) {
477 json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i]));
478 }
479 }
480 json_object_object_add(object, "nodes", children);
481
482 json_object *focus = json_object_new_array();
483 if (c->type != C_VIEW) {
484 if (c->focused) {
485 json_object_array_add(focus, json_object_new_double(c->focused->id));
486 }
487 if (c->floating) {
488 for (i = 0; i < c->floating->length; ++i) {
489 swayc_t *item = c->floating->items[i];
490 if (item == c->focused) {
491 continue;
492 }
493
494 json_object_array_add(focus, json_object_new_double(item->id));
495 }
496 }
497 if (c->children) {
498 for (i = 0; i < c->children->length; ++i) {
499 swayc_t *item = c->children->items[i];
500 if (item == c->focused) {
501 continue;
502 }
503
504 json_object_array_add(focus, json_object_new_double(item->id));
505 }
506 }
507 }
508 json_object_object_add(object, "focus", focus);
509
510 if (c->type == C_ROOT) {
511 json_object *scratchpad_json = json_object_new_array();
512 if (scratchpad->length > 0) {
513 for (i = 0; i < scratchpad->length; ++i) {
514 json_object_array_add(scratchpad_json, ipc_json_describe_container_recursive(scratchpad->items[i]));
515 }
516 }
517 json_object_object_add(object, "scratchpad", scratchpad_json);
518 }
519
520 return object;
521}
diff --git a/sway/old/ipc-server.c b/sway/old/ipc-server.c
deleted file mode 100644
index fd64e81b..00000000
--- a/sway/old/ipc-server.c
+++ /dev/null
@@ -1,840 +0,0 @@
1// See https://i3wm.org/docs/ipc.html for protocol information
2#ifndef __FreeBSD__
3// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0)
4#define _XOPEN_SOURCE 700
5#endif
6#include <errno.h>
7#include <string.h>
8#include <sys/socket.h>
9#include <sys/un.h>
10#include <stdbool.h>
11#include <unistd.h>
12#include <stdlib.h>
13#include <sys/ioctl.h>
14#include <fcntl.h>
15#include <json-c/json.h>
16#include <list.h>
17#include <libinput.h>
18#ifdef __linux__
19struct ucred {
20 pid_t pid;
21 uid_t uid;
22 gid_t gid;
23};
24#endif
25#include "sway/ipc-json.h"
26#include "sway/ipc-server.h"
27#include "sway/security.h"
28#include "sway/config.h"
29#include "sway/commands.h"
30#include "sway/input.h"
31#include "sway/server.h"
32#include "stringop.h"
33#include "log.h"
34#include "list.h"
35#include "util.h"
36
37static int ipc_socket = -1;
38static struct wl_event_source *ipc_event_source = NULL;
39static struct sockaddr_un *ipc_sockaddr = NULL;
40static list_t *ipc_client_list = NULL;
41
42static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
43
44struct ipc_client {
45 struct wl_event_source *event_source;
46 struct wl_event_source *writable_event_source;
47 int fd;
48 uint32_t payload_length;
49 uint32_t security_policy;
50 enum ipc_command_type current_command;
51 enum ipc_command_type subscribed_events;
52 size_t write_buffer_len;
53 size_t write_buffer_size;
54 char *write_buffer;
55};
56
57struct sockaddr_un *ipc_user_sockaddr(void);
58int ipc_handle_connection(int fd, uint32_t mask, void *data);
59int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data);
60int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data);
61void ipc_client_disconnect(struct ipc_client *client);
62void ipc_client_handle_command(struct ipc_client *client);
63bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length);
64void ipc_get_workspaces_callback(swayc_t *workspace, void *data);
65void ipc_get_outputs_callback(swayc_t *container, void *data);
66static void ipc_get_marks_callback(swayc_t *container, void *data);
67
68void ipc_init(void) {
69 ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
70 if (ipc_socket == -1) {
71 sway_abort("Unable to create IPC socket");
72 }
73
74 ipc_sockaddr = ipc_user_sockaddr();
75
76 // We want to use socket name set by user, not existing socket from another sway instance.
77 if (getenv("SWAYSOCK") != NULL && access(getenv("SWAYSOCK"), F_OK) == -1) {
78 strncpy(ipc_sockaddr->sun_path, getenv("SWAYSOCK"), sizeof(ipc_sockaddr->sun_path));
79 ipc_sockaddr->sun_path[sizeof(ipc_sockaddr->sun_path) - 1] = 0;
80 }
81
82 unlink(ipc_sockaddr->sun_path);
83 if (bind(ipc_socket, (struct sockaddr *)ipc_sockaddr, sizeof(*ipc_sockaddr)) == -1) {
84 sway_abort("Unable to bind IPC socket");
85 }
86
87 if (listen(ipc_socket, 3) == -1) {
88 sway_abort("Unable to listen on IPC socket");
89 }
90
91 // Set i3 IPC socket path so that i3-msg works out of the box
92 setenv("I3SOCK", ipc_sockaddr->sun_path, 1);
93 setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1);
94
95 ipc_client_list = create_list();
96
97 ipc_event_source = wl_event_loop_add_fd(server.wl_event_loop, ipc_socket,
98 WL_EVENT_READABLE, ipc_handle_connection, NULL);
99}
100
101void ipc_terminate(void) {
102 if (ipc_event_source) {
103 wl_event_source_remove(ipc_event_source);
104 }
105 close(ipc_socket);
106 unlink(ipc_sockaddr->sun_path);
107
108 list_free(ipc_client_list);
109
110 if (ipc_sockaddr) {
111 free(ipc_sockaddr);
112 }
113}
114
115struct sockaddr_un *ipc_user_sockaddr(void) {
116 struct sockaddr_un *ipc_sockaddr = malloc(sizeof(struct sockaddr_un));
117 if (ipc_sockaddr == NULL) {
118 sway_abort("Can't allocate ipc_sockaddr");
119 }
120
121 ipc_sockaddr->sun_family = AF_UNIX;
122 int path_size = sizeof(ipc_sockaddr->sun_path);
123
124 // Env var typically set by logind, e.g. "/run/user/<user-id>"
125 const char *dir = getenv("XDG_RUNTIME_DIR");
126 if (!dir) {
127 dir = "/tmp";
128 }
129 if (path_size <= snprintf(ipc_sockaddr->sun_path, path_size,
130 "%s/sway-ipc.%i.%i.sock", dir, getuid(), getpid())) {
131 sway_abort("Socket path won't fit into ipc_sockaddr->sun_path");
132 }
133
134 return ipc_sockaddr;
135}
136
137static pid_t get_client_pid(int client_fd) {
138// FreeBSD supports getting uid/gid, but not pid
139#ifdef __linux__
140 struct ucred ucred;
141 socklen_t len = sizeof(struct ucred);
142
143 if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
144 return -1;
145 }
146
147 return ucred.pid;
148#else
149 return -1;
150#endif
151}
152
153int ipc_handle_connection(int fd, uint32_t mask, void *data) {
154 (void) fd; (void) data;
155 sway_log(L_DEBUG, "Event on IPC listening socket");
156 assert(mask == WL_EVENT_READABLE);
157
158 int client_fd = accept(ipc_socket, NULL, NULL);
159 if (client_fd == -1) {
160 sway_log_errno(L_ERROR, "Unable to accept IPC client connection");
161 return 0;
162 }
163
164 int flags;
165 if ((flags = fcntl(client_fd, F_GETFD)) == -1
166 || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) {
167 sway_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket");
168 close(client_fd);
169 return 0;
170 }
171 if ((flags = fcntl(client_fd, F_GETFL)) == -1
172 || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) {
173 sway_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket");
174 close(client_fd);
175 return 0;
176 }
177
178 struct ipc_client* client = malloc(sizeof(struct ipc_client));
179 if (!client) {
180 sway_log(L_ERROR, "Unable to allocate ipc client");
181 close(client_fd);
182 return 0;
183 }
184 client->payload_length = 0;
185 client->fd = client_fd;
186 client->subscribed_events = 0;
187 client->event_source = wl_event_loop_add_fd(server.wl_event_loop, client_fd,
188 WL_EVENT_READABLE, ipc_client_handle_readable, client);
189 client->writable_event_source = NULL;
190
191 client->write_buffer_size = 128;
192 client->write_buffer_len = 0;
193 client->write_buffer = malloc(client->write_buffer_size);
194 if (!client->write_buffer) {
195 sway_log(L_ERROR, "Unable to allocate ipc client write buffer");
196 close(client_fd);
197 return 0;
198 }
199
200 pid_t pid = get_client_pid(client->fd);
201 client->security_policy = get_ipc_policy_mask(pid);
202
203 sway_log(L_DEBUG, "New client: fd %d, pid %d", client_fd, pid);
204
205 list_add(ipc_client_list, client);
206
207 return 0;
208}
209
210static const int ipc_header_size = sizeof(ipc_magic)+8;
211
212int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
213 struct ipc_client *client = data;
214
215 if (mask & WL_EVENT_ERROR) {
216 sway_log(L_ERROR, "IPC Client socket error, removing client");
217 ipc_client_disconnect(client);
218 return 0;
219 }
220
221 if (mask & WL_EVENT_HANGUP) {
222 sway_log(L_DEBUG, "Client %d hung up", client->fd);
223 ipc_client_disconnect(client);
224 return 0;
225 }
226
227 sway_log(L_DEBUG, "Client %d readable", client->fd);
228
229 int read_available;
230 if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
231 sway_log_errno(L_INFO, "Unable to read IPC socket buffer size");
232 ipc_client_disconnect(client);
233 return 0;
234 }
235
236 // Wait for the rest of the command payload in case the header has already been read
237 if (client->payload_length > 0) {
238 if ((uint32_t)read_available >= client->payload_length) {
239 ipc_client_handle_command(client);
240 }
241 return 0;
242 }
243
244 if (read_available < ipc_header_size) {
245 return 0;
246 }
247
248 uint8_t buf[ipc_header_size];
249 uint32_t *buf32 = (uint32_t*)(buf + sizeof(ipc_magic));
250 // Should be fully available, because read_available >= ipc_header_size
251 ssize_t received = recv(client_fd, buf, ipc_header_size, 0);
252 if (received == -1) {
253 sway_log_errno(L_INFO, "Unable to receive header from IPC client");
254 ipc_client_disconnect(client);
255 return 0;
256 }
257
258 if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) {
259 sway_log(L_DEBUG, "IPC header check failed");
260 ipc_client_disconnect(client);
261 return 0;
262 }
263
264 client->payload_length = buf32[0];
265 client->current_command = (enum ipc_command_type)buf32[1];
266
267 if (read_available - received >= (long)client->payload_length) {
268 ipc_client_handle_command(client);
269 }
270
271 return 0;
272}
273
274int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
275 struct ipc_client *client = data;
276
277 if (mask & WL_EVENT_ERROR) {
278 sway_log(L_ERROR, "IPC Client socket error, removing client");
279 ipc_client_disconnect(client);
280 return 0;
281 }
282
283 if (mask & WL_EVENT_HANGUP) {
284 sway_log(L_DEBUG, "Client %d hung up", client->fd);
285 ipc_client_disconnect(client);
286 return 0;
287 }
288
289 if (client->write_buffer_len <= 0) {
290 return 0;
291 }
292
293 sway_log(L_DEBUG, "Client %d writable", client->fd);
294
295 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
296
297 if (written == -1 && errno == EAGAIN) {
298 return 0;
299 } else if (written == -1) {
300 sway_log_errno(L_INFO, "Unable to send data from queue to IPC client");
301 ipc_client_disconnect(client);
302 return 0;
303 }
304
305 memmove(client->write_buffer, client->write_buffer + written, client->write_buffer_len - written);
306 client->write_buffer_len -= written;
307
308 if (client->write_buffer_len == 0 && client->writable_event_source) {
309 wl_event_source_remove(client->writable_event_source);
310 client->writable_event_source = NULL;
311 }
312
313 return 0;
314}
315
316void ipc_client_disconnect(struct ipc_client *client) {
317 if (!sway_assert(client != NULL, "client != NULL")) {
318 return;
319 }
320
321 if (client->fd != -1) {
322 shutdown(client->fd, SHUT_RDWR);
323 }
324
325 sway_log(L_INFO, "IPC Client %d disconnected", client->fd);
326 wl_event_source_remove(client->event_source);
327 if (client->writable_event_source) {
328 wl_event_source_remove(client->writable_event_source);
329 }
330 int i = 0;
331 while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++;
332 list_del(ipc_client_list, i);
333 free(client->write_buffer);
334 close(client->fd);
335 free(client);
336}
337
338bool output_by_name_test(swayc_t *view, void *data) {
339 char *name = (char *)data;
340 if (view->type != C_OUTPUT) {
341 return false;
342 }
343 return !strcmp(name, view->name);
344}
345
346// greedy wildcard (only "*") matching
347bool mime_type_matches(const char *mime_type, const char *pattern) {
348 const char *wildcard = NULL;
349 while (*mime_type && *pattern) {
350 if (*pattern == '*' && !wildcard) {
351 wildcard = pattern;
352 ++pattern;
353 }
354
355 if (*mime_type != *pattern) {
356 if (!wildcard)
357 return false;
358
359 pattern = wildcard;
360 ++mime_type;
361 continue;
362 }
363
364 ++mime_type;
365 ++pattern;
366 }
367
368 while (*pattern == '*') {
369 ++pattern;
370 }
371
372 return (*mime_type == *pattern);
373}
374
375void ipc_client_handle_command(struct ipc_client *client) {
376 if (!sway_assert(client != NULL, "client != NULL")) {
377 return;
378 }
379
380 char *buf = malloc(client->payload_length + 1);
381 if (!buf) {
382 sway_log_errno(L_INFO, "Unable to allocate IPC payload");
383 ipc_client_disconnect(client);
384 return;
385 }
386 if (client->payload_length > 0) {
387 // Payload should be fully available
388 ssize_t received = recv(client->fd, buf, client->payload_length, 0);
389 if (received == -1)
390 {
391 sway_log_errno(L_INFO, "Unable to receive payload from IPC client");
392 ipc_client_disconnect(client);
393 free(buf);
394 return;
395 }
396 }
397 buf[client->payload_length] = '\0';
398
399 const char *error_denied = "{ \"success\": false, \"error\": \"Permission denied\" }";
400
401 switch (client->current_command) {
402 case IPC_COMMAND:
403 {
404 if (!(client->security_policy & IPC_FEATURE_COMMAND)) {
405 goto exit_denied;
406 }
407 struct cmd_results *results = handle_command(buf, CONTEXT_IPC);
408 const char *json = cmd_results_to_json(results);
409 char reply[256];
410 int length = snprintf(reply, sizeof(reply), "%s", json);
411 ipc_send_reply(client, reply, (uint32_t) length);
412 free_cmd_results(results);
413 goto exit_cleanup;
414 }
415
416 case IPC_SUBSCRIBE:
417 {
418 // TODO: Check if they're permitted to use these events
419 struct json_object *request = json_tokener_parse(buf);
420 if (request == NULL) {
421 ipc_send_reply(client, "{\"success\": false}", 18);
422 sway_log_errno(L_INFO, "Failed to read request");
423 goto exit_cleanup;
424 }
425
426 // parse requested event types
427 for (int i = 0; i < json_object_array_length(request); i++) {
428 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
429 if (strcmp(event_type, "workspace") == 0) {
430 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE);
431 } else if (strcmp(event_type, "barconfig_update") == 0) {
432 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE);
433 } else if (strcmp(event_type, "mode") == 0) {
434 client->subscribed_events |= event_mask(IPC_EVENT_MODE);
435 } else if (strcmp(event_type, "window") == 0) {
436 client->subscribed_events |= event_mask(IPC_EVENT_WINDOW);
437 } else if (strcmp(event_type, "modifier") == 0) {
438 client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER);
439 } else if (strcmp(event_type, "binding") == 0) {
440 client->subscribed_events |= event_mask(IPC_EVENT_BINDING);
441 } else {
442 ipc_send_reply(client, "{\"success\": false}", 18);
443 json_object_put(request);
444 sway_log_errno(L_INFO, "Failed to parse request");
445 goto exit_cleanup;
446 }
447 }
448
449 json_object_put(request);
450
451 ipc_send_reply(client, "{\"success\": true}", 17);
452 goto exit_cleanup;
453 }
454
455 case IPC_GET_WORKSPACES:
456 {
457 if (!(client->security_policy & IPC_FEATURE_GET_WORKSPACES)) {
458 goto exit_denied;
459 }
460 json_object *workspaces = json_object_new_array();
461 container_map(&root_container, ipc_get_workspaces_callback, workspaces);
462 const char *json_string = json_object_to_json_string(workspaces);
463 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
464 json_object_put(workspaces); // free
465 goto exit_cleanup;
466 }
467
468 case IPC_GET_INPUTS:
469 {
470 if (!(client->security_policy & IPC_FEATURE_GET_INPUTS)) {
471 goto exit_denied;
472 }
473 json_object *inputs = json_object_new_array();
474 /* TODO WLR
475 if (input_devices) {
476 for(int i = 0; i<input_devices->length; i++) {
477 struct libinput_device *device = input_devices->items[i];
478 json_object_array_add(inputs, ipc_json_describe_input(device));
479 }
480 }
481 */
482 const char *json_string = json_object_to_json_string(inputs);
483 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
484 json_object_put(inputs);
485 goto exit_cleanup;
486 }
487
488 case IPC_GET_OUTPUTS:
489 {
490 if (!(client->security_policy & IPC_FEATURE_GET_OUTPUTS)) {
491 goto exit_denied;
492 }
493 json_object *outputs = json_object_new_array();
494 container_map(&root_container, ipc_get_outputs_callback, outputs);
495 const char *json_string = json_object_to_json_string(outputs);
496 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
497 json_object_put(outputs); // free
498 goto exit_cleanup;
499 }
500
501 case IPC_GET_TREE:
502 {
503 if (!(client->security_policy & IPC_FEATURE_GET_TREE)) {
504 goto exit_denied;
505 }
506 json_object *tree = ipc_json_describe_container_recursive(&root_container);
507 const char *json_string = json_object_to_json_string(tree);
508 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
509 json_object_put(tree);
510 goto exit_cleanup;
511 }
512
513 case IPC_GET_MARKS:
514 {
515 if (!(client->security_policy & IPC_FEATURE_GET_MARKS)) {
516 goto exit_denied;
517 }
518 json_object *marks = json_object_new_array();
519 container_map(&root_container, ipc_get_marks_callback, marks);
520 const char *json_string = json_object_to_json_string(marks);
521 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
522 json_object_put(marks);
523 goto exit_cleanup;
524 }
525
526 case IPC_GET_VERSION:
527 {
528 json_object *version = ipc_json_get_version();
529 const char *json_string = json_object_to_json_string(version);
530 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
531 json_object_put(version); // free
532 goto exit_cleanup;
533 }
534
535 case IPC_GET_BAR_CONFIG:
536 {
537 if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) {
538 goto exit_denied;
539 }
540 if (!buf[0]) {
541 // Send list of configured bar IDs
542 json_object *bars = json_object_new_array();
543 int i;
544 for (i = 0; i < config->bars->length; ++i) {
545 struct bar_config *bar = config->bars->items[i];
546 json_object_array_add(bars, json_object_new_string(bar->id));
547 }
548 const char *json_string = json_object_to_json_string(bars);
549 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
550 json_object_put(bars); // free
551 } else {
552 // Send particular bar's details
553 struct bar_config *bar = NULL;
554 int i;
555 for (i = 0; i < config->bars->length; ++i) {
556 bar = config->bars->items[i];
557 if (strcmp(buf, bar->id) == 0) {
558 break;
559 }
560 bar = NULL;
561 }
562 if (!bar) {
563 const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }";
564 ipc_send_reply(client, error, (uint32_t)strlen(error));
565 goto exit_cleanup;
566 }
567 json_object *json = ipc_json_describe_bar_config(bar);
568 const char *json_string = json_object_to_json_string(json);
569 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
570 json_object_put(json); // free
571 }
572 goto exit_cleanup;
573 }
574
575 case IPC_GET_CLIPBOARD:
576 // TODO WLR
577 break;
578
579 default:
580 sway_log(L_INFO, "Unknown IPC command type %i", client->current_command);
581 goto exit_cleanup;
582 }
583
584exit_denied:
585 ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied));
586 sway_log(L_DEBUG, "Denied IPC client access to %i", client->current_command);
587
588exit_cleanup:
589 client->payload_length = 0;
590 free(buf);
591 return;
592}
593
594bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length) {
595 assert(payload);
596
597 char data[ipc_header_size];
598 uint32_t *data32 = (uint32_t*)(data + sizeof(ipc_magic));
599
600 memcpy(data, ipc_magic, sizeof(ipc_magic));
601 data32[0] = payload_length;
602 data32[1] = client->current_command;
603
604 while (client->write_buffer_len + ipc_header_size + payload_length >=
605 client->write_buffer_size) {
606 client->write_buffer_size *= 2;
607 }
608
609 // TODO: reduce the limit back to 4 MB when screenshooter is implemented
610 if (client->write_buffer_size > (1 << 28)) { // 256 MB
611 sway_log(L_ERROR, "Client write buffer too big, disconnecting client");
612 ipc_client_disconnect(client);
613 return false;
614 }
615
616 char *new_buffer = realloc(client->write_buffer, client->write_buffer_size);
617 if (!new_buffer) {
618 sway_log(L_ERROR, "Unable to reallocate ipc client write buffer");
619 ipc_client_disconnect(client);
620 return false;
621 }
622 client->write_buffer = new_buffer;
623
624 memcpy(client->write_buffer + client->write_buffer_len, data, ipc_header_size);
625 client->write_buffer_len += ipc_header_size;
626 memcpy(client->write_buffer + client->write_buffer_len, payload, payload_length);
627 client->write_buffer_len += payload_length;
628
629 if (!client->writable_event_source) {
630 client->writable_event_source = wl_event_loop_add_fd(
631 server.wl_event_loop, client->fd, WL_EVENT_WRITABLE,
632 ipc_client_handle_writable, client);
633 }
634
635 sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload);
636
637 return true;
638}
639
640void ipc_get_workspaces_callback(swayc_t *workspace, void *data) {
641 if (workspace->type == C_WORKSPACE) {
642 json_object *workspace_json = ipc_json_describe_container(workspace);
643 // override the default focused indicator because
644 // it's set differently for the get_workspaces reply
645 bool focused = root_container.focused == workspace->parent && workspace->parent->focused == workspace;
646 json_object_object_del(workspace_json, "focused");
647 json_object_object_add(workspace_json, "focused", json_object_new_boolean(focused));
648 json_object_array_add((json_object *)data, workspace_json);
649 }
650}
651
652void ipc_get_outputs_callback(swayc_t *container, void *data) {
653 if (container->type == C_OUTPUT) {
654 json_object_array_add((json_object *)data, ipc_json_describe_container(container));
655 }
656}
657
658static void ipc_get_marks_callback(swayc_t *container, void *data) {
659 json_object *object = (json_object *)data;
660 if (container->marks) {
661 for (int i = 0; i < container->marks->length; ++i) {
662 char *mark = (char *)container->marks->items[i];
663 json_object_array_add(object, json_object_new_string(mark));
664 }
665 }
666}
667
668void ipc_send_event(const char *json_string, enum ipc_command_type event) {
669 static struct {
670 enum ipc_command_type event;
671 enum ipc_feature feature;
672 } security_mappings[] = {
673 { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE },
674 { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT },
675 { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE },
676 { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW },
677 { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING },
678 { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT }
679 };
680
681 uint32_t security_mask = 0;
682 for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) {
683 if (security_mappings[i].event == event) {
684 security_mask = security_mappings[i].feature;
685 break;
686 }
687 }
688
689 int i;
690 struct ipc_client *client;
691 for (i = 0; i < ipc_client_list->length; i++) {
692 client = ipc_client_list->items[i];
693 if (!(client->security_policy & security_mask)) {
694 continue;
695 }
696 if ((client->subscribed_events & event_mask(event)) == 0) {
697 continue;
698 }
699 client->current_command = event;
700 if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) {
701 sway_log_errno(L_INFO, "Unable to send reply to IPC client");
702 ipc_client_disconnect(client);
703 }
704 }
705}
706
707void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) {
708 sway_log(L_DEBUG, "Sending workspace::%s event", change);
709 json_object *obj = json_object_new_object();
710 json_object_object_add(obj, "change", json_object_new_string(change));
711 if (strcmp("focus", change) == 0) {
712 if (old) {
713 json_object_object_add(obj, "old", ipc_json_describe_container_recursive(old));
714 } else {
715 json_object_object_add(obj, "old", NULL);
716 }
717 }
718
719 if (new) {
720 json_object_object_add(obj, "current", ipc_json_describe_container_recursive(new));
721 } else {
722 json_object_object_add(obj, "current", NULL);
723 }
724
725 const char *json_string = json_object_to_json_string(obj);
726 ipc_send_event(json_string, IPC_EVENT_WORKSPACE);
727
728 json_object_put(obj); // free
729}
730
731void ipc_event_window(swayc_t *window, const char *change) {
732 sway_log(L_DEBUG, "Sending window::%s event", change);
733 json_object *obj = json_object_new_object();
734 json_object_object_add(obj, "change", json_object_new_string(change));
735 json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window));
736
737 const char *json_string = json_object_to_json_string(obj);
738 ipc_send_event(json_string, IPC_EVENT_WINDOW);
739
740 json_object_put(obj); // free
741}
742
743void ipc_event_barconfig_update(struct bar_config *bar) {
744 sway_log(L_DEBUG, "Sending barconfig_update event");
745 json_object *json = ipc_json_describe_bar_config(bar);
746 const char *json_string = json_object_to_json_string(json);
747 ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE);
748
749 json_object_put(json); // free
750}
751
752void ipc_event_mode(const char *mode) {
753 sway_log(L_DEBUG, "Sending mode::%s event", mode);
754 json_object *obj = json_object_new_object();
755 json_object_object_add(obj, "change", json_object_new_string(mode));
756
757 const char *json_string = json_object_to_json_string(obj);
758 ipc_send_event(json_string, IPC_EVENT_MODE);
759
760 json_object_put(obj); // free
761}
762
763void ipc_event_modifier(uint32_t modifier, const char *state) {
764 sway_log(L_DEBUG, "Sending modifier::%s event", state);
765 json_object *obj = json_object_new_object();
766 json_object_object_add(obj, "change", json_object_new_string(state));
767
768 const char *modifier_name = get_modifier_name_by_mask(modifier);
769 json_object_object_add(obj, "modifier", json_object_new_string(modifier_name));
770
771 const char *json_string = json_object_to_json_string(obj);
772 ipc_send_event(json_string, IPC_EVENT_MODIFIER);
773
774 json_object_put(obj); // free
775}
776
777static void ipc_event_binding(json_object *sb_obj) {
778 sway_log(L_DEBUG, "Sending binding::run event");
779 json_object *obj = json_object_new_object();
780 json_object_object_add(obj, "change", json_object_new_string("run"));
781 json_object_object_add(obj, "binding", sb_obj);
782
783 const char *json_string = json_object_to_json_string(obj);
784 ipc_send_event(json_string, IPC_EVENT_BINDING);
785
786 json_object_put(obj); // free
787}
788
789void ipc_event_binding_keyboard(struct sway_binding *sb) {
790 json_object *sb_obj = json_object_new_object();
791 json_object_object_add(sb_obj, "command", json_object_new_string(sb->command));
792
793 const char *names[10];
794
795 int len = get_modifier_names(names, sb->modifiers);
796 int i;
797 json_object *modifiers = json_object_new_array();
798 for (i = 0; i < len; ++i) {
799 json_object_array_add(modifiers, json_object_new_string(names[i]));
800 }
801
802 json_object_object_add(sb_obj, "event_state_mask", modifiers);
803
804 json_object *input_codes = json_object_new_array();
805 int input_code = 0;
806 json_object *symbols = json_object_new_array();
807 json_object *symbol = NULL;
808
809 if (sb->bindcode) { // bindcode: populate input_codes
810 uint32_t keycode;
811 for (i = 0; i < sb->keys->length; ++i) {
812 keycode = *(uint32_t *)sb->keys->items[i];
813 json_object_array_add(input_codes, json_object_new_int(keycode));
814 if (i == 0) {
815 input_code = keycode;
816 }
817 }
818 } else { // bindsym: populate symbols
819 uint32_t keysym;
820 char buffer[64];
821 for (i = 0; i < sb->keys->length; ++i) {
822 keysym = *(uint32_t *)sb->keys->items[i];
823 if (xkb_keysym_get_name(keysym, buffer, 64) > 0) {
824 json_object *str = json_object_new_string(buffer);
825 json_object_array_add(symbols, str);
826 if (i == 0) {
827 symbol = str;
828 }
829 }
830 }
831 }
832
833 json_object_object_add(sb_obj, "input_codes", input_codes);
834 json_object_object_add(sb_obj, "input_code", json_object_new_int(input_code));
835 json_object_object_add(sb_obj, "symbols", symbols);
836 json_object_object_add(sb_obj, "symbol", symbol);
837 json_object_object_add(sb_obj, "input_type", json_object_new_string("keyboard"));
838
839 ipc_event_binding(sb_obj);
840}
diff --git a/sway/old/layout.c b/sway/old/layout.c
deleted file mode 100644
index 22f81688..00000000
--- a/sway/old/layout.c
+++ /dev/null
@@ -1,1773 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdlib.h>
3#include <stdbool.h>
4#include <math.h>
5#include <wlc/wlc.h>
6#include "sway/config.h"
7#include "sway/container.h"
8#include "sway/workspace.h"
9#include "sway/focus.h"
10#include "sway/output.h"
11#include "sway/ipc-server.h"
12#include "sway/border.h"
13#include "sway/layout.h"
14#include "list.h"
15#include "log.h"
16
17swayc_t root_container;
18swayc_t *current_focus;
19list_t *scratchpad;
20
21int min_sane_h = 60;
22int min_sane_w = 100;
23
24void init_layout(void) {
25 root_container.id = 0; // normally assigned in new_swayc()
26 root_container.type = C_ROOT;
27 root_container.layout = L_NONE;
28 root_container.name = strdup("root");
29 root_container.children = create_list();
30 root_container.handle = -1;
31 root_container.visible = true;
32 current_focus = &root_container;
33 scratchpad = create_list();
34}
35
36int index_child(const swayc_t *child) {
37 swayc_t *parent = child->parent;
38 int i, len;
39 if (!child->is_floating) {
40 len = parent->children->length;
41 for (i = 0; i < len; ++i) {
42 if (parent->children->items[i] == child) {
43 break;
44 }
45 }
46 } else {
47 len = parent->floating->length;
48 for (i = 0; i < len; ++i) {
49 if (parent->floating->items[i] == child) {
50 break;
51 }
52 }
53 }
54 if (!sway_assert(i < len, "Stray container")) {
55 return -1;
56 }
57 return i;
58}
59
60void add_child(swayc_t *parent, swayc_t *child) {
61 sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
62 child->width, child->height, parent, parent->type, parent->width, parent->height);
63 list_add(parent->children, child);
64 child->parent = parent;
65 // set focus for this container
66 if (!parent->focused) {
67 parent->focused = child;
68 }
69 if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
70 child = new_container(child, parent->workspace_layout);
71 }
72}
73
74static double *get_height(swayc_t *cont) {
75 return &cont->height;
76}
77
78static double *get_width(swayc_t *cont) {
79 return &cont->width;
80}
81
82void insert_child(swayc_t *parent, swayc_t *child, int index) {
83 if (index > parent->children->length) {
84 index = parent->children->length;
85 }
86 if (index < 0) {
87 index = 0;
88 }
89 list_insert(parent->children, index, child);
90 child->parent = parent;
91 if (!parent->focused) {
92 parent->focused = child;
93 }
94 if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
95 child = new_container(child, parent->workspace_layout);
96 }
97 if (is_auto_layout(parent->layout)) {
98 /* go through each group, adjust the size of the first child of each group */
99 double *(*get_maj_dim)(swayc_t *cont);
100 double *(*get_min_dim)(swayc_t *cont);
101 if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
102 get_maj_dim = get_width;
103 get_min_dim = get_height;
104 } else {
105 get_maj_dim = get_height;
106 get_min_dim = get_width;
107 }
108 for (int i = index; i < parent->children->length;) {
109 int start = auto_group_start_index(parent, i);
110 int end = auto_group_end_index(parent, i);
111 swayc_t *first = parent->children->items[start];
112 if (start + 1 < parent->children->length) {
113 /* preserve the group's dimension along major axis */
114 *get_maj_dim(first) = *get_maj_dim(parent->children->items[start + 1]);
115 } else {
116 /* new group, let the apply_layout handle it */
117 first->height = first->width = 0;
118 break;
119 }
120 double remaining = *get_min_dim(parent);
121 for (int j = end - 1; j > start; --j) {
122 swayc_t *sibling = parent->children->items[j];
123 if (sibling == child) {
124 /* the inserted child won't yet have its minor
125 dimension set */
126 remaining -= *get_min_dim(parent) / (end - start);
127 } else {
128 remaining -= *get_min_dim(sibling);
129 }
130 }
131 *get_min_dim(first) = remaining;
132 i = end;
133 }
134 }
135}
136
137void add_floating(swayc_t *ws, swayc_t *child) {
138 sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
139 child->width, child->height, ws, ws->type, ws->width, ws->height);
140 if (!sway_assert(ws->type == C_WORKSPACE, "Must be of workspace type")) {
141 return;
142 }
143 list_add(ws->floating, child);
144 child->parent = ws;
145 child->is_floating = true;
146 if (!ws->focused) {
147 ws->focused = child;
148 }
149 ipc_event_window(child, "floating");
150}
151
152swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) {
153 swayc_t *parent = fixed->parent;
154 if (fixed->is_floating) {
155 if (active->is_floating) {
156 int i = index_child(fixed);
157 list_insert(parent->floating, i + 1, active);
158 } else {
159 list_add(parent->children, active);
160 }
161 } else {
162 if (active->is_floating) {
163 list_add(parent->floating, active);
164 } else {
165 int i = index_child(fixed);
166 if (is_auto_layout(parent->layout)) {
167 list_add(parent->children, active);
168 } else {
169 list_insert(parent->children, i + 1, active);
170 }
171 }
172 }
173 active->parent = parent;
174 // focus new child
175 parent->focused = active;
176 return active->parent;
177}
178
179swayc_t *replace_child(swayc_t *child, swayc_t *new_child) {
180 swayc_t *parent = child->parent;
181 if (parent == NULL) {
182 return NULL;
183 }
184 int i = index_child(child);
185 if (child->is_floating) {
186 parent->floating->items[i] = new_child;
187 } else {
188 parent->children->items[i] = new_child;
189 }
190 // Set parent and focus for new_child
191 new_child->parent = child->parent;
192 if (child->parent->focused == child) {
193 child->parent->focused = new_child;
194 }
195 child->parent = NULL;
196
197 // Set geometry for new child
198 new_child->x = child->x;
199 new_child->y = child->y;
200 new_child->width = child->width;
201 new_child->height = child->height;
202
203 // reset geometry for child
204 child->width = 0;
205 child->height = 0;
206
207 // deactivate child
208 if (child->type == C_VIEW) {
209 wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
210 }
211 return parent;
212}
213
214swayc_t *remove_child(swayc_t *child) {
215 int i;
216 swayc_t *parent = child->parent;
217 if (child->is_floating) {
218 // Special case for floating views
219 for (i = 0; i < parent->floating->length; ++i) {
220 if (parent->floating->items[i] == child) {
221 list_del(parent->floating, i);
222 break;
223 }
224 }
225 i = 0;
226 } else {
227 for (i = 0; i < parent->children->length; ++i) {
228 if (parent->children->items[i] == child) {
229 list_del(parent->children, i);
230 break;
231 }
232 }
233 if (is_auto_layout(parent->layout) && parent->children->length) {
234 /* go through each group, adjust the size of the last child of each group */
235 double *(*get_maj_dim)(swayc_t *cont);
236 double *(*get_min_dim)(swayc_t *cont);
237 if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
238 get_maj_dim = get_width;
239 get_min_dim = get_height;
240 } else {
241 get_maj_dim = get_height;
242 get_min_dim = get_width;
243 }
244 for (int j = parent->children->length - 1; j >= i;) {
245 int start = auto_group_start_index(parent, j);
246 int end = auto_group_end_index(parent, j);
247 swayc_t *first = parent->children->items[start];
248 if (i == start) {
249 /* removed element was first child in the current group,
250 use its size along the major axis */
251 *get_maj_dim(first) = *get_maj_dim(child);
252 } else if (start > i) {
253 /* preserve the group's dimension along major axis */
254 *get_maj_dim(first) = *get_maj_dim(parent->children->items[start - 1]);
255 }
256 if (end != parent->children->length) {
257 double remaining = *get_min_dim(parent);
258 for (int k = start; k < end - 1; ++k) {
259 swayc_t *sibling = parent->children->items[k];
260 remaining -= *get_min_dim(sibling);
261 }
262 /* last element of the group gets remaining size, elements
263 that don't change groups keep their ratio */
264 *get_min_dim((swayc_t *) parent->children->items[end - 1]) = remaining;
265 } /* else last group, let apply_layout handle it */
266 j = start - 1;
267 }
268 }
269 }
270 // Set focused to new container
271 if (parent->focused == child) {
272 if (parent->children->length > 0) {
273 parent->focused = parent->children->items[i ? i-1:0];
274 } else if (parent->floating && parent->floating->length) {
275 parent->focused = parent->floating->items[parent->floating->length - 1];
276 } else {
277 parent->focused = NULL;
278 }
279 }
280 child->parent = NULL;
281 // deactivate view
282 if (child->type == C_VIEW) {
283 wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
284 }
285 return parent;
286}
287
288void swap_container(swayc_t *a, swayc_t *b) {
289 if (!sway_assert(a&&b, "parameters must be non null") ||
290 !sway_assert(a->parent && b->parent, "containers must have parents")) {
291 return;
292 }
293 size_t a_index = index_child(a);
294 size_t b_index = index_child(b);
295 swayc_t *a_parent = a->parent;
296 swayc_t *b_parent = b->parent;
297 // Swap the pointers
298 if (a->is_floating) {
299 a_parent->floating->items[a_index] = b;
300 } else {
301 a_parent->children->items[a_index] = b;
302 }
303 if (b->is_floating) {
304 b_parent->floating->items[b_index] = a;
305 } else {
306 b_parent->children->items[b_index] = a;
307 }
308 a->parent = b_parent;
309 b->parent = a_parent;
310 if (a_parent->focused == a) {
311 a_parent->focused = b;
312 }
313 // don't want to double switch
314 if (b_parent->focused == b && a_parent != b_parent) {
315 b_parent->focused = a;
316 }
317}
318
319void swap_geometry(swayc_t *a, swayc_t *b) {
320 double x = a->x;
321 double y = a->y;
322 double w = a->width;
323 double h = a->height;
324 a->x = b->x;
325 a->y = b->y;
326 a->width = b->width;
327 a->height = b->height;
328 b->x = x;
329 b->y = y;
330 b->width = w;
331 b->height = h;
332}
333
334static void swap_children(swayc_t *container, int a, int b) {
335 if (a >= 0 && b >= 0 && a < container->children->length
336 && b < container->children->length
337 && a != b) {
338 swayc_t *pa = (swayc_t *)container->children->items[a];
339 swayc_t *pb = (swayc_t *)container->children->items[b];
340 container->children->items[a] = container->children->items[b];
341 container->children->items[b] = pa;
342 if (is_auto_layout(container->layout)) {
343 size_t ga = auto_group_index(container, a);
344 size_t gb = auto_group_index(container, b);
345 if (ga != gb) {
346 swap_geometry(pa, pb);
347 }
348 }
349 }
350}
351
352void move_container(swayc_t *container, enum movement_direction dir, int move_amt) {
353 enum swayc_layouts layout = L_NONE;
354 swayc_t *parent = container->parent;
355 if (container->is_floating) {
356 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
357 switch(dir) {
358 case MOVE_LEFT:
359 container->x = MAX(0, container->x - move_amt);
360 break;
361 case MOVE_RIGHT:
362 container->x = MIN(output->width - container->width, container->x + move_amt);
363 break;
364 case MOVE_UP:
365 container->y = MAX(0, container->y - move_amt);
366 break;
367 case MOVE_DOWN:
368 container->y = MIN(output->height - container->height, container->y + move_amt);
369 break;
370 default:
371 break;
372 }
373 update_geometry(container);
374 return;
375 }
376 if (container->type != C_VIEW && container->type != C_CONTAINER) {
377 return;
378 }
379 if (dir == MOVE_UP || dir == MOVE_DOWN) {
380 layout = L_VERT;
381 } else if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
382 layout = L_HORIZ;
383 } else if (dir == MOVE_FIRST) {
384 // swap first child in auto layout with currently focused child
385 if (is_auto_layout(parent->layout)) {
386 int focused_idx = index_child(container);
387 swayc_t *first = parent->children->items[0];
388 if (focused_idx > 0) {
389 list_swap(parent->children, 0, focused_idx);
390 swap_geometry(first, container);
391 }
392 arrange_windows(parent->parent, -1, -1);
393 ipc_event_window(container, "move");
394 set_focused_container_for(parent->parent, container);
395 }
396 return;
397 } else if (! (dir == MOVE_NEXT || dir == MOVE_PREV)) {
398 return;
399 }
400 swayc_t *child = container;
401 bool ascended = false;
402
403 // View is wrapped in intermediate container which is needed for displaying
404 // the titlebar. Moving only the view outside of its parent container would just
405 // wrap it again under worspace. There would effectively be no movement,
406 // just a change of wrapping container.
407 if (child->type == C_VIEW &&
408 parent->type == C_CONTAINER &&
409 parent->children->length == 1 &&
410 parent->parent->type == C_WORKSPACE) {
411 child = parent;
412 parent = parent->parent;
413 }
414
415 while (true) {
416 sway_log(L_DEBUG, "container:%p, parent:%p, child %p,",
417 container,parent,child);
418 if (parent->layout == layout
419 || (layout == L_NONE && (parent->type == C_CONTAINER || parent->type == C_WORKSPACE)) /* accept any layout for next/prev direction */
420 || (parent->layout == L_TABBED && layout == L_HORIZ)
421 || (parent->layout == L_STACKED && layout == L_VERT)
422 || is_auto_layout(parent->layout)) {
423 int diff;
424 // If it has ascended (parent has moved up), no container is removed
425 // so insert it at index, or index+1.
426 // if it has not, the moved container is removed, so it needs to be
427 // inserted at index-1, or index+1
428 if (ascended) {
429 diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? 0 : 1;
430 } else {
431 diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? -1 : 1;
432 }
433 int idx = index_child(child);
434 int desired = idx + diff;
435 if (dir == MOVE_NEXT || dir == MOVE_PREV) {
436 // Next/Prev always wrap.
437 if (desired < 0) {
438 desired += parent->children->length;
439 } else if (desired >= parent->children->length) {
440 desired = 0;
441 }
442 }
443 // when it has ascended, legal insertion position is 0:len
444 // when it has not, legal insertion position is 0:len-1
445 if (desired >= 0 && desired - ascended < parent->children->length) {
446 if (!ascended) {
447 child = parent->children->items[desired];
448 // Move container into sibling container
449 if (child->type == C_CONTAINER) {
450 parent = child;
451 // Insert it in first/last if matching layout, otherwise
452 // insert it next to focused container
453 if (parent->layout == layout
454 || (parent->layout == L_TABBED && layout == L_HORIZ)
455 || (parent->layout == L_STACKED && layout == L_VERT)
456 || is_auto_layout(parent->layout)) {
457 desired = (diff < 0) * parent->children->length;
458 } else {
459 desired = index_child(child->focused) + 1;
460 }
461 //reset geometry
462 container->width = container->height = 0;
463 }
464 }
465 if (container->parent == parent) {
466 swap_children(parent, idx, desired);
467 } else {
468 swayc_t *old_parent = remove_child(container);
469 insert_child(parent, container, desired);
470 destroy_container(old_parent);
471 sway_log(L_DEBUG,"Moving to %p %d", parent, desired);
472 }
473 break;
474 }
475 }
476 // Change parent layout if we need to
477 if (parent->children->length == 1 && parent->layout != layout && layout != L_NONE) {
478 /* swayc_change_layout(parent, layout); */
479 parent->layout = layout;
480 continue;
481 }
482 if (parent->type == C_WORKSPACE) {
483 // If moving to an adjacent output we need a starting position (since this
484 // output might border to multiple outputs).
485 struct wlc_point abs_pos;
486 get_absolute_center_position(container, &abs_pos);
487
488 swayc_t *output = swayc_adjacent_output(parent->parent, dir, &abs_pos, true);
489
490 if (output) {
491 sway_log(L_DEBUG, "Moving between outputs");
492 swayc_t *old_parent = remove_child(container);
493 destroy_container(old_parent);
494
495 swayc_t *dest = output->focused;
496 switch (dir) {
497 case MOVE_LEFT:
498 case MOVE_UP:
499 // reset container geometry
500 container->width = container->height = 0;
501 add_child(dest, container);
502 break;
503 case MOVE_RIGHT:
504 case MOVE_DOWN:
505 // reset container geometry
506 container->width = container->height = 0;
507 insert_child(dest, container, 0);
508 break;
509 default:
510 break;
511 }
512 // arrange new workspace
513 arrange_windows(dest, -1, -1);
514 set_focused_container(container);
515 break;
516 }
517
518 // We simply cannot move any further.
519 if (parent->layout == layout) {
520 break;
521 }
522 // Create container around workspace to insert child into
523 parent = new_container(parent, layout);
524 // Previous line set the resulting container's layout to
525 // workspace_layout. It should have been just layout.
526 parent->layout = parent->parent->layout;
527 }
528 ascended = true;
529 child = parent;
530 parent = child->parent;
531 }
532 arrange_windows(parent->parent, -1, -1);
533 ipc_event_window(container, "move");
534 set_focused_container_for(parent->parent, container);
535}
536
537void move_container_to(swayc_t* container, swayc_t* destination) {
538 if (container == destination || swayc_is_parent_of(container, destination)) {
539 return;
540 }
541 swayc_t *parent = remove_child(container);
542 // Send to new destination
543 if (container->is_floating) {
544 swayc_t *ws = swayc_active_workspace_for(destination);
545 add_floating(ws, container);
546
547 // If the workspace only has one child after adding one, it
548 // means that the workspace was just initialized.
549 if (ws->children->length + ws->floating->length == 1) {
550 ipc_event_workspace(NULL, ws, "init");
551 }
552 } else if (destination->type == C_WORKSPACE) {
553 // reset container geometry
554 container->width = container->height = 0;
555 add_child(destination, container);
556
557 // If the workspace only has one child after adding one, it
558 // means that the workspace was just initialized.
559 if (destination->children->length + destination->floating->length == 1) {
560 ipc_event_workspace(NULL, destination, "init");
561 }
562 } else {
563 // reset container geometry
564 container->width = container->height = 0;
565 add_sibling(destination, container);
566 }
567 // Destroy old container if we need to
568 parent = destroy_container(parent);
569 // Refocus
570 swayc_t *op1 = swayc_parent_by_type(destination, C_OUTPUT);
571 swayc_t *op2 = swayc_parent_by_type(parent, C_OUTPUT);
572 set_focused_container(get_focused_view(op1));
573 arrange_windows(op1, -1, -1);
574 update_visibility(op1);
575 if (op1 != op2) {
576 set_focused_container(get_focused_view(op2));
577 arrange_windows(op2, -1, -1);
578 update_visibility(op2);
579 }
580}
581
582void move_workspace_to(swayc_t* workspace, swayc_t* destination) {
583 if (workspace == destination || swayc_is_parent_of(workspace, destination)) {
584 return;
585 }
586 swayc_t *src_op = remove_child(workspace);
587 // reset container geometry
588 workspace->width = workspace->height = 0;
589 add_child(destination, workspace);
590 sort_workspaces(destination);
591 // Refocus destination (change to new workspace)
592 set_focused_container(get_focused_view(workspace));
593 arrange_windows(destination, -1, -1);
594 update_visibility(destination);
595
596 // make sure source output has a workspace
597 if (src_op->children->length == 0) {
598 char *ws_name = workspace_next_name(src_op->name);
599 swayc_t *ws = new_workspace(src_op, ws_name);
600 ws->is_focused = true;
601 free(ws_name);
602 }
603 set_focused_container(get_focused_view(src_op));
604 update_visibility(src_op);
605}
606
607static void adjust_border_geometry(swayc_t *c, struct wlc_geometry *g,
608 const struct wlc_size *res, int left, int right, int top, int bottom) {
609
610 g->size.w += left + right;
611 if (g->origin.x - left < 0) {
612 g->size.w += g->origin.x - left;
613 } else if (g->origin.x + g->size.w - right > res->w) {
614 g->size.w = res->w - g->origin.x + right;
615 }
616
617 g->size.h += top + bottom;
618 if (g->origin.y - top < 0) {
619 g->size.h += g->origin.y - top;
620 } else if (g->origin.y + g->size.h - top > res->h) {
621 g->size.h = res->h - g->origin.y + top;
622 }
623
624 g->origin.x = MIN((uint32_t)MAX(g->origin.x - left, 0), res->w);
625 g->origin.y = MIN((uint32_t)MAX(g->origin.y - top, 0), res->h);
626
627}
628
629static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geometry) {
630 struct wlc_geometry g = *geometry;
631 c->actual_geometry = g;
632
633 swayc_t *output = swayc_parent_by_type(c, C_OUTPUT);
634 struct wlc_size res;
635 output_get_scaled_size(output->handle, &res);
636
637 switch (c->border_type) {
638 case B_NONE:
639 break;
640 case B_PIXEL:
641 adjust_border_geometry(c, &g, &res, c->border_thickness,
642 c->border_thickness, c->border_thickness, c->border_thickness);
643 break;
644 case B_NORMAL:
645 {
646 int title_bar_height = config->font_height + 4; // borders + padding
647
648 adjust_border_geometry(c, &g, &res, c->border_thickness,
649 c->border_thickness, title_bar_height, c->border_thickness);
650
651 struct wlc_geometry title_bar = {
652 .origin = {
653 .x = c->actual_geometry.origin.x - c->border_thickness,
654 .y = c->actual_geometry.origin.y - title_bar_height
655 },
656 .size = {
657 .w = c->actual_geometry.size.w + (2 * c->border_thickness),
658 .h = title_bar_height
659 }
660 };
661 c->title_bar_geometry = title_bar;
662 break;
663 }
664 }
665
666 c->border_geometry = g;
667 *geometry = c->actual_geometry;
668
669 update_container_border(c);
670}
671
672void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout) {
673 switch (parent->layout) {
674 case L_TABBED:
675 case L_STACKED:
676 if (prev_layout != L_TABBED && prev_layout != L_STACKED) {
677 // cache current geometry for all non-float children
678 int i;
679 for (i = 0; i < parent->children->length; ++i) {
680 swayc_t *child = parent->children->items[i];
681 child->cached_geometry.origin.x = child->x;
682 child->cached_geometry.origin.y = child->y;
683 child->cached_geometry.size.w = child->width;
684 child->cached_geometry.size.h = child->height;
685 }
686 }
687 break;
688 default:
689 if (prev_layout == L_TABBED || prev_layout == L_STACKED) {
690 // recover cached geometry for all non-float children
691 int i;
692 for (i = 0; i < parent->children->length; ++i) {
693 swayc_t *child = parent->children->items[i];
694 // only recoverer cached geometry if non-zero
695 if (!wlc_geometry_equals(&child->cached_geometry, &wlc_geometry_zero)) {
696 child->x = child->cached_geometry.origin.x;
697 child->y = child->cached_geometry.origin.y;
698 child->width = child->cached_geometry.size.w;
699 child->height = child->cached_geometry.size.h;
700 }
701 }
702 }
703 break;
704 }
705}
706
707static int update_gap_geometry(swayc_t *container, struct wlc_geometry *g) {
708 swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE);
709 swayc_t *op = ws->parent;
710 int gap = container->is_floating ? 0 : swayc_gap(container);
711 if (gap % 2 != 0) {
712 // because gaps are implemented as "half sized margins" it's currently
713 // not possible to align views properly with odd sized gaps.
714 gap -= 1;
715 }
716
717 g->origin.x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1;
718 g->origin.y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1;
719 g->size.w = container->width > gap ? container->width - gap : 1;
720 g->size.h = container->height > gap ? container->height - gap : 1;
721
722 if ((!config->edge_gaps && gap > 0) || (config->smart_gaps && ws->children->length == 1)) {
723 // Remove gap against the workspace edges. Because a pixel is not
724 // divisable, depending on gap size and the number of siblings our view
725 // might be at the workspace edge without being exactly so (thus test
726 // with gap, and align correctly).
727 if (container->x - gap <= ws->x) {
728 g->origin.x = ws->x;
729 g->size.w = container->width - gap/2;
730 }
731 if (container->y - gap <= ws->y) {
732 g->origin.y = ws->y;
733 g->size.h = container->height - gap/2;
734 }
735 if (container->x + container->width + gap >= ws->x + ws->width) {
736 g->size.w = ws->x + ws->width - g->origin.x;
737 }
738 if (container->y + container->height + gap >= ws->y + ws->height) {
739 g->size.h = ws->y + ws->height - g->origin.y;
740 }
741 }
742
743 return gap;
744}
745
746void update_geometry(swayc_t *container) {
747 if (container->type != C_VIEW && container->type != C_CONTAINER) {
748 return;
749 }
750
751 swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE);
752 swayc_t *op = workspace->parent;
753 swayc_t *parent = container->parent;
754
755 struct wlc_geometry geometry = {
756 .origin = {
757 .x = container->x < op->width ? container->x : op->width-1,
758 .y = container->y < op->height ? container->y : op->height-1
759 },
760 .size = {
761 .w = container->width,
762 .h = container->height,
763 }
764 };
765
766 int gap = 0;
767
768 // apply inner gaps to non-tabbed/stacked containers
769 swayc_t *p = swayc_tabbed_stacked_ancestor(container);
770 if (p == NULL) {
771 gap = update_gap_geometry(container, &geometry);
772 }
773
774 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
775 struct wlc_size size;
776 output_get_scaled_size(output->handle, &size);
777
778 if (swayc_is_fullscreen(container)) {
779 geometry.origin.x = 0;
780 geometry.origin.y = 0;
781 geometry.size.w = size.w;
782 geometry.size.h = size.h;
783 if (op->focused == workspace) {
784 wlc_view_bring_to_front(container->handle);
785 }
786
787 container->border_geometry = wlc_geometry_zero;
788 container->title_bar_geometry = wlc_geometry_zero;
789 border_clear(container->border);
790 } else if (container->is_floating) { // allocate border for floating window
791 update_border_geometry_floating(container, &geometry);
792 } else if (!container->is_floating) { // allocate border for titled window
793 container->border_geometry = geometry;
794
795 int border_top = container->border_thickness;
796 int border_bottom = container->border_thickness;
797 int border_left = container->border_thickness;
798 int border_right = container->border_thickness;
799
800 // handle hide_edge_borders
801 if (config->hide_edge_borders != E_NONE && (gap <= 0 || (config->smart_gaps && workspace->children->length == 1))) {
802 if (config->hide_edge_borders == E_VERTICAL || config->hide_edge_borders == E_BOTH) {
803 if (geometry.origin.x == workspace->x) {
804 border_left = 0;
805 }
806
807 if (geometry.origin.x + geometry.size.w == workspace->x + workspace->width) {
808 border_right = 0;
809 }
810 }
811
812 if (config->hide_edge_borders == E_HORIZONTAL || config->hide_edge_borders == E_BOTH) {
813 if (geometry.origin.y == workspace->y || should_hide_top_border(container, geometry.origin.y)) {
814 border_top = 0;
815 }
816
817 if (geometry.origin.y + geometry.size.h == workspace->y + workspace->height) {
818 border_bottom = 0;
819 }
820 }
821
822 if (config->hide_edge_borders == E_SMART && workspace->children->length == 1) {
823 border_top = 0;
824 border_bottom = 0;
825 border_left = 0;
826 border_right = 0;
827 }
828 }
829
830 int title_bar_height = config->font_height + 4; //borders + padding
831
832 if (parent->layout == L_TABBED && parent->children->length > 1) {
833 int i, x = 0, w, l, r;
834 l = parent->children->length;
835 w = geometry.size.w / l;
836 r = geometry.size.w % l;
837 for (i = 0; i < parent->children->length; ++i) {
838 swayc_t *view = parent->children->items[i];
839 if (view == container) {
840 x = w * i;
841 if (i == l - 1) {
842 w += r;
843 }
844 break;
845 }
846 }
847
848 struct wlc_geometry title_bar = {
849 .origin = {
850 .x = container->border_geometry.origin.x + x,
851 .y = container->border_geometry.origin.y
852 },
853 .size = {
854 .w = w,
855 .h = title_bar_height
856 }
857 };
858 geometry.origin.x += border_left;
859 geometry.origin.y += title_bar.size.h;
860 geometry.size.w -= (border_left + border_right);
861 geometry.size.h -= (border_bottom + title_bar.size.h);
862 container->title_bar_geometry = title_bar;
863 } else if (parent->layout == L_STACKED && parent->children->length > 1) {
864 int i, y = 0;
865 for (i = 0; i < parent->children->length; ++i) {
866 swayc_t *view = parent->children->items[i];
867 if (view == container) {
868 y = title_bar_height * i;
869 }
870 }
871
872 struct wlc_geometry title_bar = {
873 .origin = {
874 .x = container->border_geometry.origin.x,
875 .y = container->border_geometry.origin.y + y
876 },
877 .size = {
878 .w = container->border_geometry.size.w,
879 .h = title_bar_height
880 }
881 };
882 title_bar_height = title_bar_height * parent->children->length;
883 geometry.origin.x += border_left;
884 geometry.origin.y += title_bar_height;
885 geometry.size.w -= (border_left + border_right);
886 geometry.size.h -= (border_bottom + title_bar_height);
887 container->title_bar_geometry = title_bar;
888 } else {
889 switch (container->border_type) {
890 case B_NONE:
891 break;
892 case B_PIXEL:
893 geometry.origin.x += border_left;
894 geometry.origin.y += border_top;
895 geometry.size.w -= (border_left + border_right);
896 geometry.size.h -= (border_top + border_bottom);
897 break;
898 case B_NORMAL:
899 {
900 struct wlc_geometry title_bar = {
901 .origin = {
902 .x = container->border_geometry.origin.x,
903 .y = container->border_geometry.origin.y
904 },
905 .size = {
906 .w = container->border_geometry.size.w,
907 .h = title_bar_height
908 }
909 };
910 geometry.origin.x += border_left;
911 geometry.origin.y += title_bar.size.h;
912 geometry.size.w -= (border_left + border_right);
913 geometry.size.h -= (border_bottom + title_bar.size.h);
914 container->title_bar_geometry = title_bar;
915 break;
916 }
917 }
918 }
919
920 container->actual_geometry = geometry;
921
922 if (container->type == C_VIEW) {
923 update_container_border(container);
924 }
925 }
926
927 if (container->type == C_VIEW) {
928 wlc_view_set_geometry(container->handle, 0, &geometry);
929 }
930}
931
932/**
933 * Layout application prototypes
934 */
935static void apply_horiz_layout(swayc_t *container, const double x,
936 const double y, const double width,
937 const double height, const int start,
938 const int end);
939static void apply_vert_layout(swayc_t *container, const double x,
940 const double y, const double width,
941 const double height, const int start,
942 const int end);
943static void apply_tabbed_or_stacked_layout(swayc_t *container, double x,
944 double y, double width,
945 double height);
946
947static void apply_auto_layout(swayc_t *container, const double x, const double y,
948 const double width, const double height,
949 enum swayc_layouts group_layout,
950 bool master_first);
951
952static void arrange_windows_r(swayc_t *container, double width, double height) {
953 int i;
954 if (width == -1 || height == -1) {
955 swayc_log(L_DEBUG, container, "Arranging layout for %p", container);
956 width = container->width;
957 height = container->height;
958 }
959 // pixels are indivisible. if we don't round the pixels, then the view
960 // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
961 // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
962 width = floor(width);
963 height = floor(height);
964
965 sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
966 container->name, container->width, container->height, container->x,
967 container->y);
968
969 double x = 0, y = 0;
970 switch (container->type) {
971 case C_ROOT:
972 for (i = 0; i < container->children->length; ++i) {
973 swayc_t *output = container->children->items[i];
974 sway_log(L_DEBUG, "Arranging output '%s' at %f,%f", output->name, output->x, output->y);
975 arrange_windows_r(output, -1, -1);
976 }
977 return;
978 case C_OUTPUT:
979 {
980 struct wlc_size resolution;
981 output_get_scaled_size(container->handle, &resolution);
982 width = resolution.w; height = resolution.h;
983 // output must have correct size due to e.g. seamless mouse,
984 // but a workspace might be smaller depending on panels.
985 container->width = width;
986 container->height = height;
987 }
988 // arrange all workspaces:
989 for (i = 0; i < container->children->length; ++i) {
990 swayc_t *child = container->children->items[i];
991 arrange_windows_r(child, -1, -1);
992 }
993 // Bring all unmanaged views to the front
994 for (i = 0; i < container->unmanaged->length; ++i) {
995 wlc_handle *handle = container->unmanaged->items[i];
996 wlc_view_bring_to_front(*handle);
997 }
998 return;
999 case C_WORKSPACE:
1000 {
1001 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
1002 width = output->width, height = output->height;
1003 /* TODO WLR
1004 for (i = 0; i < desktop_shell.panels->length; ++i) {
1005 struct panel_config *config = desktop_shell.panels->items[i];
1006 if (config->output == output->handle) {
1007 struct wlc_size size = *wlc_surface_get_size(config->surface);
1008 sway_log(L_DEBUG, "-> Found panel for this workspace: %ux%u, position: %u", size.w, size.h, config->panel_position);
1009 switch (config->panel_position) {
1010 case DESKTOP_SHELL_PANEL_POSITION_TOP:
1011 y += size.h; height -= size.h;
1012 break;
1013 case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
1014 height -= size.h;
1015 break;
1016 case DESKTOP_SHELL_PANEL_POSITION_LEFT:
1017 x += size.w; width -= size.w;
1018 break;
1019 case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
1020 width -= size.w;
1021 break;
1022 }
1023 }
1024 }
1025 */
1026 int gap = swayc_gap(container);
1027 x = container->x = x + gap;
1028 y = container->y = y + gap;
1029 width = container->width = width - gap * 2;
1030 height = container->height = height - gap * 2;
1031 sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y);
1032 }
1033 // children are properly handled below
1034 break;
1035 case C_VIEW:
1036 {
1037 container->width = width;
1038 container->height = height;
1039 update_geometry(container);
1040 sway_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f", container->width,
1041 container->height, container->x, container->y);
1042 }
1043 return;
1044 default:
1045 container->width = width;
1046 container->height = height;
1047 x = container->x;
1048 y = container->y;
1049
1050 // add gaps to top level tapped/stacked container
1051 if (container->parent->type == C_WORKSPACE &&
1052 (container->layout == L_TABBED || container->layout == L_STACKED)) {
1053 update_geometry(container);
1054 width = container->border_geometry.size.w;
1055 height = container->border_geometry.size.h;
1056 x = container->border_geometry.origin.x;
1057 y = container->border_geometry.origin.y;
1058 }
1059
1060 // update container size if it's a direct child in a tabbed/stacked layout
1061 // if parent is a workspace, its actual_geometry won't be initialized
1062 if (swayc_tabbed_stacked_parent(container) != NULL &&
1063 container->parent->type != C_WORKSPACE) {
1064 // Use parent actual_geometry as a base for calculating
1065 // container geometry
1066 container->width = container->parent->actual_geometry.size.w;
1067 container->height = container->parent->actual_geometry.size.h;
1068 container->x = container->parent->actual_geometry.origin.x;
1069 container->y = container->parent->actual_geometry.origin.y;
1070
1071 update_geometry(container);
1072 width = container->width = container->actual_geometry.size.w;
1073 height = container->height = container->actual_geometry.size.h;
1074 x = container->x = container->actual_geometry.origin.x;
1075 y = container->y = container->actual_geometry.origin.y;
1076 }
1077
1078 break;
1079 }
1080
1081 switch (container->layout) {
1082 case L_HORIZ:
1083 default:
1084 apply_horiz_layout(container, x, y, width, height, 0,
1085 container->children->length);
1086 break;
1087 case L_VERT:
1088 apply_vert_layout(container, x, y, width, height, 0,
1089 container->children->length);
1090 break;
1091 case L_TABBED:
1092 case L_STACKED:
1093 apply_tabbed_or_stacked_layout(container, x, y, width, height);
1094 break;
1095 case L_AUTO_LEFT:
1096 apply_auto_layout(container, x, y, width, height, L_VERT, true);
1097 break;
1098 case L_AUTO_RIGHT:
1099 apply_auto_layout(container, x, y, width, height, L_VERT, false);
1100 break;
1101 case L_AUTO_TOP:
1102 apply_auto_layout(container, x, y, width, height, L_HORIZ, true);
1103 break;
1104 case L_AUTO_BOTTOM:
1105 apply_auto_layout(container, x, y, width, height, L_HORIZ, false);
1106 break;
1107 }
1108
1109 // Arrage floating layouts for workspaces last
1110 if (container->type == C_WORKSPACE) {
1111 for (int i = 0; i < container->floating->length; ++i) {
1112 swayc_t *view = container->floating->items[i];
1113 if (view->type == C_VIEW) {
1114 update_geometry(view);
1115 sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f",
1116 view->width, view->height, view->x, view->y);
1117 if (swayc_is_fullscreen(view)) {
1118 wlc_view_bring_to_front(view->handle);
1119 } else if (!container->focused ||
1120 !swayc_is_fullscreen(container->focused)) {
1121 wlc_view_bring_to_front(view->handle);
1122 }
1123 }
1124 }
1125 }
1126}
1127
1128void apply_horiz_layout(swayc_t *container, const double x, const double y,
1129 const double width, const double height,
1130 const int start, const int end) {
1131 double scale = 0;
1132 // Calculate total width
1133 for (int i = start; i < end; ++i) {
1134 double *old_width = &((swayc_t *)container->children->items[i])->width;
1135 if (*old_width <= 0) {
1136 if (end - start > 1) {
1137 *old_width = width / (end - start - 1);
1138 } else {
1139 *old_width = width;
1140 }
1141 }
1142 scale += *old_width;
1143 }
1144 scale = width / scale;
1145
1146 // Resize windows
1147 double child_x = x;
1148 if (scale > 0.1) {
1149 sway_log(L_DEBUG, "Arranging %p horizontally", container);
1150 swayc_t *focused = NULL;
1151 for (int i = start; i < end; ++i) {
1152 swayc_t *child = container->children->items[i];
1153 sway_log(L_DEBUG,
1154 "Calculating arrangement for %p:%d (will scale %f by %f)", child,
1155 child->type, width, scale);
1156 child->x = child_x;
1157 child->y = y;
1158
1159 if (child == container->focused) {
1160 focused = child;
1161 }
1162
1163 if (i == end - 1) {
1164 double remaining_width = x + width - child_x;
1165 arrange_windows_r(child, remaining_width, height);
1166 } else {
1167 arrange_windows_r(child, child->width * scale, height);
1168 }
1169 child_x += child->width;
1170 }
1171
1172 // update focused view border last because it may
1173 // depend on the title bar geometry of its siblings.
1174 if (focused && container->children->length > 1) {
1175 update_container_border(focused);
1176 }
1177 }
1178}
1179
1180void apply_vert_layout(swayc_t *container, const double x, const double y,
1181 const double width, const double height, const int start,
1182 const int end) {
1183 int i;
1184 double scale = 0;
1185 // Calculate total height
1186 for (i = start; i < end; ++i) {
1187 double *old_height = &((swayc_t *)container->children->items[i])->height;
1188 if (*old_height <= 0) {
1189 if (end - start > 1) {
1190 *old_height = height / (end - start - 1);
1191 } else {
1192 *old_height = height;
1193 }
1194 }
1195 scale += *old_height;
1196 }
1197 scale = height / scale;
1198
1199 // Resize
1200 double child_y = y;
1201 if (scale > 0.1) {
1202 sway_log(L_DEBUG, "Arranging %p vertically", container);
1203 swayc_t *focused = NULL;
1204 for (i = start; i < end; ++i) {
1205 swayc_t *child = container->children->items[i];
1206 sway_log(L_DEBUG,
1207 "Calculating arrangement for %p:%d (will scale %f by %f)", child,
1208 child->type, height, scale);
1209 child->x = x;
1210 child->y = child_y;
1211
1212 if (child == container->focused) {
1213 focused = child;
1214 }
1215
1216 if (i == end - 1) {
1217 double remaining_height = y + height - child_y;
1218 arrange_windows_r(child, width, remaining_height);
1219 } else {
1220 arrange_windows_r(child, width, child->height * scale);
1221 }
1222 child_y += child->height;
1223 }
1224
1225 // update focused view border last because it may
1226 // depend on the title bar geometry of its siblings.
1227 if (focused && container->children->length > 1) {
1228 update_container_border(focused);
1229 }
1230 }
1231}
1232
1233void apply_tabbed_or_stacked_layout(swayc_t *container, double x, double y,
1234 double width, double height) {
1235 int i;
1236 swayc_t *focused = NULL;
1237 for (i = 0; i < container->children->length; ++i) {
1238 swayc_t *child = container->children->items[i];
1239 child->x = x;
1240 child->y = y;
1241 if (child == container->focused) {
1242 focused = child;
1243 } else {
1244 arrange_windows_r(child, width, height);
1245 }
1246 }
1247
1248 if (focused) {
1249 arrange_windows_r(focused, width, height);
1250 }
1251}
1252
1253void apply_auto_layout(swayc_t *container, const double x, const double y,
1254 const double width, const double height,
1255 enum swayc_layouts group_layout,
1256 bool master_first) {
1257 // Auto layout "container" in width x height @ x, y
1258 // using "group_layout" for each of the groups in the container.
1259 // There is one "master" group, plus container->nb_slave_groups.
1260 // Each group is layed out side by side following the "major" axis.
1261 // The direction of the layout used for groups is the "minor" axis.
1262 // Example:
1263 //
1264 // ---- major axis -->
1265 // +---------+-----------+
1266 // | | | |
1267 // | master | slave 1 | |
1268 // | +-----------+ | minor axis (direction of group_layout)
1269 // | | | |
1270 // | | slave 2 | V
1271 // +---------+-----------+
1272 //
1273 // container with three children (one master and two slaves) and
1274 // a single slave group (containing slave 1 and 2). The master
1275 // group and slave group are layed out using L_VERT.
1276
1277 size_t nb_groups = auto_group_count(container);
1278
1279 // the target dimension of the container along the "major" axis, each
1280 // group in the container will be layed out using "group_layout" along
1281 // the "minor" axis.
1282 double dim_maj;
1283 double pos_maj;
1284
1285 // x and y coords for the next group to be laid out.
1286 const double *group_x, *group_y;
1287
1288 // pos of the next group to layout along the major axis
1289 double pos;
1290
1291 // size of the next group along the major axis.
1292 double group_dim;
1293
1294 // height and width of next group to be laid out.
1295 const double *group_h, *group_w;
1296
1297 switch (group_layout) {
1298 default:
1299 sway_log(L_DEBUG, "Unknown layout type (%d) used in %s()",
1300 group_layout, __func__);
1301 /* fall through */
1302 case L_VERT:
1303 dim_maj = width;
1304 pos_maj = x;
1305
1306 group_x = &pos;
1307 group_y = &y;
1308 group_w = &group_dim;
1309 group_h = &height;
1310 break;
1311 case L_HORIZ:
1312 dim_maj = height;
1313 pos_maj = y;
1314
1315 group_x = &x;
1316 group_y = &pos;
1317 group_w = &width;
1318 group_h = &group_dim;
1319 break;
1320 }
1321
1322 /* Determine the dimension of each of the groups in the layout.
1323 * Dimension will be width for a VERT layout and height for a HORIZ
1324 * layout. */
1325 double old_group_dim[nb_groups];
1326 double old_dim = 0;
1327 for (size_t group = 0; group < nb_groups; ++group) {
1328 int idx;
1329 if (auto_group_bounds(container, group, &idx, NULL)) {
1330 swayc_t *child = container->children->items[idx];
1331 double *dim = group_layout == L_HORIZ ? &child->height : &child->width;
1332 if (*dim <= 0) {
1333 // New child with uninitialized dimension
1334 *dim = dim_maj;
1335 if (nb_groups > 1) {
1336 // child gets a dimension proportional to existing groups,
1337 // it will be later scaled based on to the available size
1338 // in the major axis.
1339 *dim /= (nb_groups - 1);
1340 }
1341 }
1342 old_dim += *dim;
1343 old_group_dim[group] = *dim;
1344 }
1345 }
1346 double scale = dim_maj / old_dim;
1347
1348 /* Apply layout to each group */
1349 pos = pos_maj;
1350
1351 for (size_t group = 0; group < nb_groups; ++group) {
1352 int start, end; // index of first (inclusive) and last (exclusive) child in the group
1353 if (auto_group_bounds(container, group, &start, &end)) {
1354 // adjusted size of the group
1355 group_dim = old_group_dim[group] * scale;
1356 if (group == nb_groups - 1) {
1357 group_dim = pos_maj + dim_maj - pos; // remaining width
1358 }
1359 sway_log(L_DEBUG, "Arranging container %p column %zu, children [%d,%d[ (%fx%f+%f,%f)",
1360 container, group, start, end, *group_w, *group_h, *group_x, *group_y);
1361 switch (group_layout) {
1362 default:
1363 case L_VERT:
1364 apply_vert_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
1365 break;
1366 case L_HORIZ:
1367 apply_horiz_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
1368 break;
1369 }
1370
1371 /* update position for next group */
1372 pos += group_dim;
1373 }
1374 }
1375}
1376
1377void arrange_windows(swayc_t *container, double width, double height) {
1378 update_visibility(container);
1379 arrange_windows_r(container, width, height);
1380 layout_log(&root_container, 0);
1381}
1382
1383void arrange_backgrounds(void) {
1384 /* TODO WLR
1385 struct background_config *bg;
1386 for (int i = 0; i < desktop_shell.backgrounds->length; ++i) {
1387 bg = desktop_shell.backgrounds->items[i];
1388 wlc_view_send_to_back(bg->handle);
1389 }
1390 */
1391}
1392
1393/**
1394 * Get swayc in the direction of newly entered output.
1395 */
1396static swayc_t *get_swayc_in_output_direction(swayc_t *output, enum movement_direction dir) {
1397 if (!output) {
1398 return NULL;
1399 }
1400
1401 swayc_t *ws = swayc_focus_by_type(output, C_WORKSPACE);
1402 if (ws && ws->children->length > 0) {
1403 switch (dir) {
1404 case MOVE_LEFT:
1405 // get most right child of new output
1406 return ws->children->items[ws->children->length-1];
1407 case MOVE_RIGHT:
1408 // get most left child of new output
1409 return ws->children->items[0];
1410 case MOVE_UP:
1411 case MOVE_DOWN:
1412 {
1413 swayc_t *focused_view = swayc_focus_by_type(ws, C_VIEW);
1414 if (focused_view && focused_view->parent) {
1415 swayc_t *parent = focused_view->parent;
1416 if (parent->layout == L_VERT) {
1417 if (dir == MOVE_UP) {
1418 // get child furthest down on new output
1419 return parent->children->items[parent->children->length-1];
1420 } else if (dir == MOVE_DOWN) {
1421 // get child furthest up on new output
1422 return parent->children->items[0];
1423 }
1424 }
1425 return focused_view;
1426 }
1427 break;
1428 }
1429 default:
1430 break;
1431 }
1432 }
1433
1434 return output;
1435}
1436
1437swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_direction dir, swayc_t *limit) {
1438 if (dir == MOVE_CHILD) {
1439 return container->focused;
1440 }
1441
1442 swayc_t *parent = container->parent;
1443 if (dir == MOVE_PARENT) {
1444 if (parent->type == C_OUTPUT) {
1445 return NULL;
1446 } else {
1447 return parent;
1448 }
1449 }
1450
1451 if (dir == MOVE_PREV || dir == MOVE_NEXT) {
1452 int focused_idx = index_child(container);
1453 if (focused_idx == -1) {
1454 return NULL;
1455 } else {
1456 int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) %
1457 parent->children->length;
1458 if (desired < 0) {
1459 desired += parent->children->length;
1460 }
1461 return parent->children->items[desired];
1462 }
1463 }
1464
1465 // If moving to an adjacent output we need a starting position (since this
1466 // output might border to multiple outputs).
1467 struct wlc_point abs_pos;
1468 get_absolute_center_position(container, &abs_pos);
1469
1470 if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
1471 sway_log(L_DEBUG, "Moving from fullscreen view, skipping to output");
1472 container = swayc_parent_by_type(container, C_OUTPUT);
1473 get_absolute_center_position(container, &abs_pos);
1474 swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
1475 return get_swayc_in_output_direction(output, dir);
1476 }
1477
1478 if (container->type == C_WORKSPACE && container->fullscreen) {
1479 sway_log(L_DEBUG, "Moving to fullscreen view");
1480 return container->fullscreen;
1481 }
1482
1483 swayc_t *wrap_candidate = NULL;
1484 while (true) {
1485 // Test if we can even make a difference here
1486 bool can_move = false;
1487 int desired;
1488 int idx = index_child(container);
1489 if (parent->type == C_ROOT) {
1490 swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
1491 if (!output || output == container) {
1492 return wrap_candidate;
1493 }
1494 sway_log(L_DEBUG, "Moving between outputs");
1495 return get_swayc_in_output_direction(output, dir);
1496 } else {
1497 if (is_auto_layout(parent->layout)) {
1498 bool is_major = parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
1499 ? dir == MOVE_LEFT || dir == MOVE_RIGHT
1500 : dir == MOVE_DOWN || dir == MOVE_UP;
1501 size_t gidx = auto_group_index(parent, idx);
1502 if (is_major) {
1503 size_t desired_grp = gidx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
1504 can_move = auto_group_bounds(parent, desired_grp, &desired, NULL);
1505 } else {
1506 desired = idx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
1507 int start, end;
1508 can_move = auto_group_bounds(parent, gidx, &start, &end)
1509 && desired >= start && desired < end;
1510 }
1511 } else {
1512 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
1513 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
1514 can_move = true;
1515 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
1516 }
1517 } else {
1518 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
1519 can_move = true;
1520 desired = idx + (dir == MOVE_UP ? -1 : 1);
1521 }
1522 }
1523 }
1524 }
1525
1526 if (can_move) {
1527 if (container->is_floating) {
1528 if (desired < 0) {
1529 wrap_candidate = parent->floating->items[parent->floating->length-1];
1530 } else if (desired >= parent->floating->length){
1531 wrap_candidate = parent->floating->items[0];
1532 } else {
1533 wrap_candidate = parent->floating->items[desired];
1534 }
1535 if (wrap_candidate) {
1536 wlc_view_bring_to_front(wrap_candidate->handle);
1537 }
1538 return wrap_candidate;
1539 } else if (desired < 0 || desired >= parent->children->length) {
1540 can_move = false;
1541 int len = parent->children->length;
1542 if (!wrap_candidate && len > 1) {
1543 if (desired < 0) {
1544 wrap_candidate = parent->children->items[len-1];
1545 } else {
1546 wrap_candidate = parent->children->items[0];
1547 }
1548 if (config->force_focus_wrapping) {
1549 return wrap_candidate;
1550 }
1551 }
1552 } else {
1553 sway_log(L_DEBUG, "%s cont %d-%p dir %i sibling %d: %p", __func__,
1554 idx, container, dir, desired, parent->children->items[desired]);
1555 return parent->children->items[desired];
1556 }
1557 }
1558 if (!can_move) {
1559 container = parent;
1560 parent = parent->parent;
1561 if (!parent || container == limit) {
1562 // wrapping is the last chance
1563 return wrap_candidate;
1564 }
1565 }
1566 }
1567}
1568
1569swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir) {
1570 return get_swayc_in_direction_under(container, dir, NULL);
1571}
1572
1573void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge edge) {
1574 int i;
1575 bool layout_match = true;
1576 sway_log(L_DEBUG, "Resizing %p with amount: %f", container, amount);
1577 if (edge == WLC_RESIZE_EDGE_LEFT || edge == WLC_RESIZE_EDGE_RIGHT) {
1578 container->width += amount;
1579 layout_match = container->layout == L_HORIZ;
1580 } else if (edge == WLC_RESIZE_EDGE_TOP || edge == WLC_RESIZE_EDGE_BOTTOM) {
1581 container->height += amount;
1582 layout_match = container->layout == L_VERT;
1583 }
1584 if (container->type == C_VIEW) {
1585 update_geometry(container);
1586 return;
1587 }
1588 if (layout_match) {
1589 for (i = 0; i < container->children->length; i++) {
1590 recursive_resize(container->children->items[i], amount/container->children->length, edge);
1591 }
1592 } else {
1593 for (i = 0; i < container->children->length; i++) {
1594 recursive_resize(container->children->items[i], amount, edge);
1595 }
1596 }
1597}
1598
1599enum swayc_layouts default_layout(swayc_t *output) {
1600 if (config->default_layout != L_NONE) {
1601 return config->default_layout;
1602 } else if (config->default_orientation != L_NONE) {
1603 return config->default_orientation;
1604 } else if (output->width >= output->height) {
1605 return L_HORIZ;
1606 } else {
1607 return L_VERT;
1608 }
1609}
1610
1611bool is_auto_layout(enum swayc_layouts layout) {
1612 return (layout >= L_AUTO_FIRST) && (layout <= L_AUTO_LAST);
1613}
1614
1615/**
1616 * Return the number of master elements in a container
1617 */
1618static inline size_t auto_master_count(const swayc_t *container) {
1619 sway_assert(container->children->length >= 0, "Container %p has (negative) children %d",
1620 container, container->children->length);
1621 return MIN(container->nb_master, (size_t)container->children->length);
1622}
1623
1624/**
1625 * Return the number of children in the slave groups. This corresponds to the children
1626 * that are not members of the master group.
1627 */
1628static inline size_t auto_slave_count(const swayc_t *container) {
1629 return container->children->length - auto_master_count(container);
1630}
1631
1632/**
1633 * Return the number of slave groups in the container.
1634 */
1635size_t auto_slave_group_count(const swayc_t *container) {
1636 return MIN(container->nb_slave_groups, auto_slave_count(container));
1637}
1638
1639/**
1640 * Return the combined number of master and slave groups in the container.
1641 */
1642size_t auto_group_count(const swayc_t *container) {
1643 return auto_slave_group_count(container)
1644 + (container->children->length && container->nb_master ? 1 : 0);
1645}
1646
1647/**
1648 * given the index of a container's child, return the index of the first child of the group
1649 * which index is a member of.
1650 */
1651int auto_group_start_index(const swayc_t *container, int index) {
1652 if (index < 0 || ! is_auto_layout(container->layout)
1653 || (size_t)index < container->nb_master) {
1654 return 0;
1655 } else {
1656 size_t nb_slaves = auto_slave_count(container);
1657 size_t nb_slave_grp = auto_slave_group_count(container);
1658 size_t grp_sz = nb_slaves / nb_slave_grp;
1659 size_t remainder = nb_slaves % nb_slave_grp;
1660 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1661 int start_idx;
1662 if (index < idx2) {
1663 start_idx = ((index - container->nb_master) / grp_sz) * grp_sz + container->nb_master;
1664 } else {
1665 start_idx = idx2 + ((index - idx2) / (grp_sz + 1)) * (grp_sz + 1);
1666 }
1667 return MIN(start_idx, container->children->length);
1668 }
1669}
1670
1671/**
1672 * given the index of a container's child, return the index of the first child of the group
1673 * that follows the one which index is a member of.
1674 * This makes the function usable to walk through the groups in a container.
1675 */
1676int auto_group_end_index(const swayc_t *container, int index) {
1677 if (index < 0 || ! is_auto_layout(container->layout)) {
1678 return container->children->length;
1679 } else {
1680 int nxt_idx;
1681 if ((size_t)index < container->nb_master) {
1682 nxt_idx = auto_master_count(container);
1683 } else {
1684 size_t nb_slaves = auto_slave_count(container);
1685 size_t nb_slave_grp = auto_slave_group_count(container);
1686 size_t grp_sz = nb_slaves / nb_slave_grp;
1687 size_t remainder = nb_slaves % nb_slave_grp;
1688 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1689 if (index < idx2) {
1690 nxt_idx = ((index - container->nb_master) / grp_sz + 1) * grp_sz + container->nb_master;
1691 } else {
1692 nxt_idx = idx2 + ((index - idx2) / (grp_sz + 1) + 1) * (grp_sz + 1);
1693 }
1694 }
1695 return MIN(nxt_idx, container->children->length);
1696 }
1697}
1698
1699/**
1700 * return the index of the Group containing <index>th child of <container>.
1701 * The index is the order of the group along the container's major axis (starting at 0).
1702 */
1703size_t auto_group_index(const swayc_t *container, int index) {
1704 if (index < 0) {
1705 return 0;
1706 }
1707 bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
1708 size_t nb_slaves = auto_slave_count(container);
1709 if ((size_t)index < container->nb_master) {
1710 if (master_first || nb_slaves <= 0) {
1711 return 0;
1712 } else {
1713 return auto_slave_group_count(container);
1714 }
1715 } else {
1716 size_t nb_slave_grp = auto_slave_group_count(container);
1717 size_t grp_sz = nb_slaves / nb_slave_grp;
1718 size_t remainder = nb_slaves % nb_slave_grp;
1719 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1720 size_t grp_idx;
1721 if (index < idx2) {
1722 grp_idx = (index - container->nb_master) / grp_sz;
1723 } else {
1724 grp_idx = (nb_slave_grp - remainder) + (index - idx2) / (grp_sz + 1) ;
1725 }
1726 return grp_idx + (master_first && container-> nb_master ? 1 : 0);
1727 }
1728}
1729
1730/**
1731 * Return the first index (inclusive) and last index (exclusive) of the elements of a group in
1732 * an auto layout.
1733 * If the bounds of the given group can be calculated, they are returned in the start/end
1734 * parameters (int pointers) and the return value will be true.
1735 * The indexes are passed by reference and can be NULL.
1736 */
1737bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end) {
1738 size_t nb_grp = auto_group_count(container);
1739 if (group_index >= nb_grp) {
1740 return false;
1741 }
1742 bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
1743 size_t nb_master = auto_master_count(container);
1744 size_t nb_slave_grp = auto_slave_group_count(container);
1745 int g_start, g_end;
1746 if (nb_master && (master_first ? group_index == 0 : group_index == nb_grp - 1)) {
1747 g_start = 0;
1748 g_end = nb_master;
1749 } else {
1750 size_t nb_slaves = auto_slave_count(container);
1751 size_t grp_sz = nb_slaves / nb_slave_grp;
1752 size_t remainder = nb_slaves % nb_slave_grp;
1753 size_t g0 = master_first && container->nb_master ? 1 : 0;
1754 size_t g1 = g0 + nb_slave_grp - remainder;
1755 if (group_index < g1) {
1756 g_start = container->nb_master + (group_index - g0) * grp_sz;
1757 g_end = g_start + grp_sz;
1758 } else {
1759 size_t g2 = group_index - g1;
1760 g_start = container->nb_master
1761 + (nb_slave_grp - remainder) * grp_sz
1762 + g2 * (grp_sz + 1);
1763 g_end = g_start + grp_sz + 1;
1764 }
1765 }
1766 if (start) {
1767 *start = g_start;
1768 }
1769 if (end) {
1770 *end = g_end;
1771 }
1772 return true;
1773}
diff --git a/sway/old/output.c b/sway/old/output.c
deleted file mode 100644
index edfcac98..00000000
--- a/sway/old/output.c
+++ /dev/null
@@ -1,278 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <strings.h>
3#include <ctype.h>
4#include <stdlib.h>
5#include "sway/output.h"
6#include "log.h"
7#include "list.h"
8
9void output_get_scaled_size(wlc_handle handle, struct wlc_size *size) {
10 *size = *wlc_output_get_resolution(handle);
11 uint32_t scale = wlc_output_get_scale(handle);
12 size->w /= scale;
13 size->h /= scale;
14}
15
16swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos) {
17 swayc_t *output = NULL;
18 // If there is no output directly next to the current one, use
19 // swayc_opposite_output to wrap.
20 if (strcasecmp(name, "left") == 0) {
21 output = swayc_adjacent_output(NULL, MOVE_LEFT, abs_pos, true);
22 if (!output) {
23 output = swayc_opposite_output(MOVE_RIGHT, abs_pos);
24 }
25 } else if (strcasecmp(name, "right") == 0) {
26 output = swayc_adjacent_output(NULL, MOVE_RIGHT, abs_pos, true);
27 if (!output) {
28 output = swayc_opposite_output(MOVE_LEFT, abs_pos);
29 }
30 } else if (strcasecmp(name, "up") == 0) {
31 output = swayc_adjacent_output(NULL, MOVE_UP, abs_pos, true);
32 if (!output) {
33 output = swayc_opposite_output(MOVE_DOWN, abs_pos);
34 }
35 } else if (strcasecmp(name, "down") == 0) {
36 output = swayc_adjacent_output(NULL, MOVE_DOWN, abs_pos, true);
37 if (!output) {
38 output = swayc_opposite_output(MOVE_UP, abs_pos);
39 }
40 } else {
41 for(int i = 0; i < root_container.children->length; ++i) {
42 swayc_t *c = root_container.children->items[i];
43 if (c->type == C_OUTPUT && strcasecmp(c->name, name) == 0) {
44 return c;
45 }
46 }
47 }
48 return output;
49}
50
51swayc_t *swayc_opposite_output(enum movement_direction dir,
52 const struct wlc_point *abs_pos) {
53
54 // Search through all the outputs and pick the output whose edge covers the
55 // given position, and is at leftmost/rightmost/upmost/downmost side of the
56 // screen (decided by the direction given).
57 swayc_t *opposite = NULL;
58 char *dir_text = NULL;
59 switch(dir) {
60 case MOVE_LEFT:
61 case MOVE_RIGHT: ;
62 for (int i = 0; i < root_container.children->length; ++i) {
63 swayc_t *c = root_container.children->items[i];
64 if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
65 if (!opposite) {
66 opposite = c;
67 } else if ((dir == MOVE_LEFT && c->x < opposite->x)
68 || (dir == MOVE_RIGHT && c->x > opposite->x)) {
69 opposite = c;
70 }
71 }
72 }
73 dir_text = dir == MOVE_LEFT ? "leftmost" : "rightmost";
74 break;
75 case MOVE_UP:
76 case MOVE_DOWN: ;
77 for (int i = 0; i < root_container.children->length; ++i) {
78 swayc_t *c = root_container.children->items[i];
79 if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
80 if (!opposite) {
81 opposite = c;
82 } else if ((dir == MOVE_UP && c->y < opposite->y)
83 || (dir == MOVE_DOWN && c->y > opposite->y)) {
84 opposite = c;
85 }
86 }
87 }
88 dir_text = dir == MOVE_UP ? "upmost" : "downmost";
89 break;
90 default:
91 sway_abort("Function called with invalid argument.");
92 break;
93 }
94 if (opposite) {
95 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s from y-position %i",
96 opposite->name, opposite->width, opposite->height, opposite->x, opposite->y,
97 dir_text, abs_pos->y);
98 }
99 return opposite;
100}
101
102// Position is where on the edge (as absolute position) the adjacent output should be searched for.
103swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir,
104 const struct wlc_point *abs_pos, bool pick_closest) {
105
106 if (!output) {
107 output = swayc_active_output();
108 }
109 // In order to find adjacent outputs we need to test that the outputs are
110 // aligned on one axis (decided by the direction given) and that the given
111 // position is within the edge of the adjacent output. If no such output
112 // exists we pick the adjacent output within the edge that is closest to
113 // the given position, if any.
114 swayc_t *adjacent = NULL;
115 char *dir_text = NULL;
116 switch(dir) {
117 case MOVE_LEFT:
118 case MOVE_RIGHT: ;
119 double delta_y = 0;
120 for(int i = 0; i < root_container.children->length; ++i) {
121 swayc_t *c = root_container.children->items[i];
122 if (c == output || c->type != C_OUTPUT) {
123 continue;
124 }
125 bool x_aligned = dir == MOVE_LEFT ?
126 c->x + c->width == output->x :
127 c->x == output->x + output->width;
128 if (!x_aligned) {
129 continue;
130 }
131 if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
132 delta_y = 0;
133 adjacent = c;
134 break;
135 } else if (pick_closest) {
136 // track closest adjacent output
137 double top_y = c->y, bottom_y = c->y + c->height;
138 if (top_y >= output->y && top_y <= output->y + output->height) {
139 double delta = top_y - abs_pos->y;
140 if (delta < 0) delta = -delta;
141 if (delta < delta_y || !adjacent) {
142 delta_y = delta;
143 adjacent = c;
144 }
145 }
146 // we check both points and pick the closest
147 if (bottom_y >= output->y && bottom_y <= output->y + output->height) {
148 double delta = bottom_y - abs_pos->y;
149 if (delta < 0) delta = -delta;
150 if (delta < delta_y || !adjacent) {
151 delta_y = delta;
152 adjacent = c;
153 }
154 }
155 }
156 }
157 dir_text = dir == MOVE_LEFT ? "left of" : "right of";
158 if (adjacent && delta_y == 0) {
159 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i)",
160 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
161 dir_text, output->name, abs_pos->y);
162 } else if (adjacent) {
163 // so we end up picking the closest adjacent output because
164 // there is no directly adjacent to the given position
165 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i, delta: %.0f)",
166 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
167 dir_text, output->name, abs_pos->y, delta_y);
168 }
169 break;
170 case MOVE_UP:
171 case MOVE_DOWN: ;
172 double delta_x = 0;
173 for(int i = 0; i < root_container.children->length; ++i) {
174 swayc_t *c = root_container.children->items[i];
175 if (c == output || c->type != C_OUTPUT) {
176 continue;
177 }
178 bool y_aligned = dir == MOVE_UP ?
179 c->y + c->height == output->y :
180 c->y == output->y + output->height;
181 if (!y_aligned) {
182 continue;
183 }
184 if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
185 delta_x = 0;
186 adjacent = c;
187 break;
188 } else if (pick_closest) {
189 // track closest adjacent output
190 double left_x = c->x, right_x = c->x + c->width;
191 if (left_x >= output->x && left_x <= output->x + output->width) {
192 double delta = left_x - abs_pos->x;
193 if (delta < 0) delta = -delta;
194 if (delta < delta_x || !adjacent) {
195 delta_x = delta;
196 adjacent = c;
197 }
198 }
199 // we check both points and pick the closest
200 if (right_x >= output->x && right_x <= output->x + output->width) {
201 double delta = right_x - abs_pos->x;
202 if (delta < 0) delta = -delta;
203 if (delta < delta_x || !adjacent) {
204 delta_x = delta;
205 adjacent = c;
206 }
207 }
208 }
209 }
210 dir_text = dir == MOVE_UP ? "above" : "below";
211 if (adjacent && delta_x == 0) {
212 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i)",
213 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
214 dir_text, output->name, abs_pos->x);
215 } else if (adjacent) {
216 // so we end up picking the closest adjacent output because
217 // there is no directly adjacent to the given position
218 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i, delta: %.0f)",
219 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
220 dir_text, output->name, abs_pos->x, delta_x);
221 }
222 break;
223 default:
224 sway_abort("Function called with invalid argument.");
225 break;
226 }
227 return adjacent;
228}
229
230void get_absolute_position(swayc_t *container, struct wlc_point *point) {
231 if (!container || !point)
232 sway_abort("Need container and wlc_point (was %p, %p).", container, point);
233
234 if (container->type == C_OUTPUT) {
235 // Coordinates are already absolute.
236 point->x = container->x;
237 point->y = container->y;
238 } else {
239 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
240 if (container->type == C_WORKSPACE) {
241 // Workspace coordinates are actually wrong/arbitrary, but should
242 // be same as output.
243 point->x = output->x;
244 point->y = output->y;
245 } else {
246 point->x = output->x + container->x;
247 point->y = output->y + container->y;
248 }
249 }
250}
251
252void get_absolute_center_position(swayc_t *container, struct wlc_point *point) {
253 get_absolute_position(container, point);
254 point->x += container->width/2;
255 point->y += container->height/2;
256}
257
258static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
259 swayc_t *a = *(void **)_a;
260 swayc_t *b = *(void **)_b;
261 int retval = 0;
262
263 if (isdigit(a->name[0]) && isdigit(b->name[0])) {
264 int a_num = strtol(a->name, NULL, 10);
265 int b_num = strtol(b->name, NULL, 10);
266 retval = (a_num < b_num) ? -1 : (a_num > b_num);
267 } else if (isdigit(a->name[0])) {
268 retval = -1;
269 } else if (isdigit(b->name[0])) {
270 retval = 1;
271 }
272
273 return retval;
274}
275
276void sort_workspaces(swayc_t *output) {
277 list_stable_sort(output->children, sort_workspace_cmp_qsort);
278}
diff --git a/sway/old/security.c b/sway/old/security.c
deleted file mode 100644
index fcd70f9d..00000000
--- a/sway/old/security.c
+++ /dev/null
@@ -1,228 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <sys/types.h>
3#include <sys/stat.h>
4#include <string.h>
5#include <unistd.h>
6#include <stdio.h>
7#include "sway/config.h"
8#include "sway/security.h"
9#include "log.h"
10
11static bool validate_ipc_target(const char *program) {
12 struct stat sb;
13
14 sway_log(L_DEBUG, "Validating IPC target '%s'", program);
15
16 if (!strcmp(program, "*")) {
17 return true;
18 }
19 if (lstat(program, &sb) == -1) {
20 return false;
21 }
22 if (!S_ISREG(sb.st_mode)) {
23 sway_log(L_ERROR,
24 "IPC target '%s' MUST be/point at an existing regular file",
25 program);
26 return false;
27 }
28 if (sb.st_uid != 0) {
29#ifdef NDEBUG
30 sway_log(L_ERROR, "IPC target '%s' MUST be owned by root", program);
31 return false;
32#else
33 sway_log(L_INFO, "IPC target '%s' MUST be owned by root (waived for debug build)", program);
34 return true;
35#endif
36 }
37 if (sb.st_mode & S_IWOTH) {
38 sway_log(L_ERROR, "IPC target '%s' MUST NOT be world writable", program);
39 return false;
40 }
41
42 return true;
43}
44
45struct feature_policy *alloc_feature_policy(const char *program) {
46 uint32_t default_policy = 0;
47
48 if (!validate_ipc_target(program)) {
49 return NULL;
50 }
51 for (int i = 0; i < config->feature_policies->length; ++i) {
52 struct feature_policy *policy = config->feature_policies->items[i];
53 if (strcmp(policy->program, "*") == 0) {
54 default_policy = policy->features;
55 break;
56 }
57 }
58
59 struct feature_policy *policy = malloc(sizeof(struct feature_policy));
60 if (!policy) {
61 return NULL;
62 }
63 policy->program = strdup(program);
64 if (!policy->program) {
65 free(policy);
66 return NULL;
67 }
68 policy->features = default_policy;
69
70 return policy;
71}
72
73struct ipc_policy *alloc_ipc_policy(const char *program) {
74 uint32_t default_policy = 0;
75
76 if (!validate_ipc_target(program)) {
77 return NULL;
78 }
79 for (int i = 0; i < config->ipc_policies->length; ++i) {
80 struct ipc_policy *policy = config->ipc_policies->items[i];
81 if (strcmp(policy->program, "*") == 0) {
82 default_policy = policy->features;
83 break;
84 }
85 }
86
87 struct ipc_policy *policy = malloc(sizeof(struct ipc_policy));
88 if (!policy) {
89 return NULL;
90 }
91 policy->program = strdup(program);
92 if (!policy->program) {
93 free(policy);
94 return NULL;
95 }
96 policy->features = default_policy;
97
98 return policy;
99}
100
101struct command_policy *alloc_command_policy(const char *command) {
102 struct command_policy *policy = malloc(sizeof(struct command_policy));
103 if (!policy) {
104 return NULL;
105 }
106 policy->command = strdup(command);
107 if (!policy->command) {
108 free(policy);
109 return NULL;
110 }
111 policy->context = 0;
112 return policy;
113}
114
115static const char *get_pid_exe(pid_t pid) {
116#ifdef __FreeBSD__
117 const char *fmt = "/proc/%d/file";
118#else
119 const char *fmt = "/proc/%d/exe";
120#endif
121 int pathlen = snprintf(NULL, 0, fmt, pid);
122 char *path = malloc(pathlen + 1);
123 if (path) {
124 snprintf(path, pathlen + 1, fmt, pid);
125 }
126
127 static char link[2048];
128
129 ssize_t len = !path ? -1 : readlink(path, link, sizeof(link));
130 if (len < 0) {
131 sway_log(L_INFO,
132 "WARNING: unable to read %s for security check. Using default policy.",
133 path);
134 strcpy(link, "*");
135 } else {
136 link[len] = '\0';
137 }
138 free(path);
139
140 return link;
141}
142
143struct feature_policy *get_feature_policy(const char *name) {
144 struct feature_policy *policy = NULL;
145
146 for (int i = 0; i < config->feature_policies->length; ++i) {
147 struct feature_policy *p = config->feature_policies->items[i];
148 if (strcmp(p->program, name) == 0) {
149 policy = p;
150 break;
151 }
152 }
153 if (!policy) {
154 policy = alloc_feature_policy(name);
155 sway_assert(policy, "Unable to allocate security policy");
156 if (policy) {
157 list_add(config->feature_policies, policy);
158 }
159 }
160 return policy;
161}
162
163uint32_t get_feature_policy_mask(pid_t pid) {
164 uint32_t default_policy = 0;
165 const char *link = get_pid_exe(pid);
166
167 for (int i = 0; i < config->feature_policies->length; ++i) {
168 struct feature_policy *policy = config->feature_policies->items[i];
169 if (strcmp(policy->program, "*") == 0) {
170 default_policy = policy->features;
171 }
172 if (strcmp(policy->program, link) == 0) {
173 return policy->features;
174 }
175 }
176
177 return default_policy;
178}
179
180uint32_t get_ipc_policy_mask(pid_t pid) {
181 uint32_t default_policy = 0;
182 const char *link = get_pid_exe(pid);
183
184 for (int i = 0; i < config->ipc_policies->length; ++i) {
185 struct ipc_policy *policy = config->ipc_policies->items[i];
186 if (strcmp(policy->program, "*") == 0) {
187 default_policy = policy->features;
188 }
189 if (strcmp(policy->program, link) == 0) {
190 return policy->features;
191 }
192 }
193
194 return default_policy;
195}
196
197uint32_t get_command_policy_mask(const char *cmd) {
198 uint32_t default_policy = 0;
199
200 for (int i = 0; i < config->command_policies->length; ++i) {
201 struct command_policy *policy = config->command_policies->items[i];
202 if (strcmp(policy->command, "*") == 0) {
203 default_policy = policy->context;
204 }
205 if (strcmp(policy->command, cmd) == 0) {
206 return policy->context;
207 }
208 }
209
210 return default_policy;
211}
212
213const char *command_policy_str(enum command_context context) {
214 switch (context) {
215 case CONTEXT_ALL:
216 return "all";
217 case CONTEXT_CONFIG:
218 return "config";
219 case CONTEXT_BINDING:
220 return "binding";
221 case CONTEXT_IPC:
222 return "IPC";
223 case CONTEXT_CRITERIA:
224 return "criteria";
225 default:
226 return "unknown";
227 }
228}
diff --git a/sway/old/workspace.c b/sway/old/workspace.c
deleted file mode 100644
index 14cde146..00000000
--- a/sway/old/workspace.c
+++ /dev/null
@@ -1,373 +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/workspace.h"
12#include "sway/layout.h"
13#include "sway/container.h"
14#include "sway/handlers.h"
15#include "sway/config.h"
16#include "sway/focus.h"
17#include "stringop.h"
18#include "util.h"
19#include "list.h"
20#include "log.h"
21#include "ipc.h"
22
23char *prev_workspace_name = NULL;
24struct workspace_by_number_data {
25 int len;
26 const char *cset;
27 const char *name;
28};
29
30static bool workspace_valid_on_output(const char *output_name, const char *ws_name) {
31 int i;
32 for (i = 0; i < config->workspace_outputs->length; ++i) {
33 struct workspace_output *wso = config->workspace_outputs->items[i];
34 if (strcasecmp(wso->workspace, ws_name) == 0) {
35 if (strcasecmp(wso->output, output_name) != 0) {
36 return false;
37 }
38 }
39 }
40
41 return true;
42}
43
44char *workspace_next_name(const char *output_name) {
45 sway_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", output_name);
46 int i;
47 int l = 1;
48 // Scan all workspace bindings to find the next available workspace name,
49 // if none are found/available then default to a number
50 struct sway_mode *mode = config->current_mode;
51
52 int order = INT_MAX;
53 char *target = NULL;
54 for (i = 0; i < mode->bindings->length; ++i) {
55 struct sway_binding *binding = mode->bindings->items[i];
56 char *cmdlist = strdup(binding->command);
57 char *dup = cmdlist;
58 char *name = NULL;
59
60 // workspace n
61 char *cmd = argsep(&cmdlist, " ");
62 if (cmdlist) {
63 name = argsep(&cmdlist, ",;");
64 }
65
66 if (strcmp("workspace", cmd) == 0 && name) {
67 sway_log(L_DEBUG, "Got valid workspace command for target: '%s'", name);
68 char *_target = strdup(name);
69 strip_quotes(_target);
70 while (isspace(*_target))
71 _target++;
72
73 // Make sure that the command references an actual workspace
74 // not a command about workspaces
75 if (strcmp(_target, "next") == 0 ||
76 strcmp(_target, "prev") == 0 ||
77 strcmp(_target, "next_on_output") == 0 ||
78 strcmp(_target, "prev_on_output") == 0 ||
79 strcmp(_target, "number") == 0 ||
80 strcmp(_target, "back_and_forth") == 0 ||
81 strcmp(_target, "current") == 0)
82 {
83 free(_target);
84 free(dup);
85 continue;
86 }
87
88 // Make sure that the workspace doesn't already exist
89 if (workspace_by_name(_target)) {
90 free(_target);
91 free(dup);
92 continue;
93 }
94
95 // make sure that the workspace can appear on the given
96 // output
97 if (!workspace_valid_on_output(output_name, _target)) {
98 free(_target);
99 free(dup);
100 continue;
101 }
102
103 if (binding->order < order) {
104 order = binding->order;
105 free(target);
106 target = _target;
107 sway_log(L_DEBUG, "Workspace: Found free name %s", _target);
108 }
109 }
110 free(dup);
111 }
112 if (target != NULL) {
113 return target;
114 }
115 // As a fall back, get the current number of active workspaces
116 // and return that + 1 for the next workspace's name
117 int ws_num = root_container.children->length;
118 if (ws_num >= 10) {
119 l = 2;
120 } else if (ws_num >= 100) {
121 l = 3;
122 }
123 char *name = malloc(l + 1);
124 if (!name) {
125 sway_log(L_ERROR, "Could not allocate workspace name");
126 return NULL;
127 }
128 sprintf(name, "%d", ws_num++);
129 return name;
130}
131
132swayc_t *workspace_create(const char* name) {
133 swayc_t *parent;
134 // Search for workspace<->output pair
135 int i, e = config->workspace_outputs->length;
136 for (i = 0; i < e; ++i) {
137 struct workspace_output *wso = config->workspace_outputs->items[i];
138 if (strcasecmp(wso->workspace, name) == 0)
139 {
140 // Find output to use if it exists
141 e = root_container.children->length;
142 for (i = 0; i < e; ++i) {
143 parent = root_container.children->items[i];
144 if (strcmp(parent->name, wso->output) == 0) {
145 return new_workspace(parent, name);
146 }
147 }
148 break;
149 }
150 }
151 // Otherwise create a new one
152 parent = get_focused_container(&root_container);
153 parent = swayc_parent_by_type(parent, C_OUTPUT);
154 return new_workspace(parent, name);
155}
156
157static bool _workspace_by_name(swayc_t *view, void *data) {
158 return (view->type == C_WORKSPACE) &&
159 (strcasecmp(view->name, (char *) data) == 0);
160}
161
162swayc_t *workspace_by_name(const char* name) {
163 if (strcmp(name, "prev") == 0) {
164 return workspace_prev();
165 }
166 else if (strcmp(name, "prev_on_output") == 0) {
167 return workspace_output_prev();
168 }
169 else if (strcmp(name, "next") == 0) {
170 return workspace_next();
171 }
172 else if (strcmp(name, "next_on_output") == 0) {
173 return workspace_output_next();
174 }
175 else if (strcmp(name, "current") == 0) {
176 return swayc_active_workspace();
177 }
178 else {
179 return swayc_by_test(&root_container, _workspace_by_name, (void *) name);
180 }
181}
182
183static bool _workspace_by_number(swayc_t *view, void *data) {
184 if (view->type != C_WORKSPACE) {
185 return false;
186 }
187 struct workspace_by_number_data *wbnd = data;
188 int a = strspn(view->name, wbnd->cset);
189 return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0;
190}
191swayc_t *workspace_by_number(const char* name) {
192 struct workspace_by_number_data wbnd = {0, "1234567890", name};
193 wbnd.len = strspn(name, wbnd.cset);
194 if (wbnd.len <= 0) {
195 return NULL;
196 }
197 return swayc_by_test(&root_container, _workspace_by_number, (void *) &wbnd);
198}
199
200/**
201 * Get the previous or next workspace on the specified output.
202 * Wraps around at the end and beginning.
203 * If next is false, the previous workspace is returned, otherwise the next one is returned.
204 */
205swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) {
206 if (!sway_assert(output->type == C_OUTPUT, "Argument must be an output, is %d", output->type)) {
207 return NULL;
208 }
209
210 int i;
211 for (i = 0; i < output->children->length; i++) {
212 if (output->children->items[i] == output->focused) {
213 return output->children->items[wrap(i + (next ? 1 : -1), output->children->length)];
214 }
215 }
216
217 // Doesn't happen, at worst the for loop returns the previously active workspace
218 return NULL;
219}
220
221/**
222 * Get the previous or next workspace. If the first/last workspace on an output is active,
223 * proceed to the previous/next output's previous/next workspace.
224 * If next is false, the previous workspace is returned, otherwise the next one is returned.
225 */
226swayc_t *workspace_prev_next_impl(swayc_t *workspace, bool next) {
227 if (!sway_assert(workspace->type == C_WORKSPACE, "Argument must be a workspace, is %d", workspace->type)) {
228 return NULL;
229 }
230
231 swayc_t *current_output = workspace->parent;
232 int offset = next ? 1 : -1;
233 int start = next ? 0 : 1;
234 int end = next ? (current_output->children->length) - 1 : current_output->children->length;
235 int i;
236 for (i = start; i < end; i++) {
237 if (current_output->children->items[i] == workspace) {
238 return current_output->children->items[i + offset];
239 }
240 }
241
242 // Given workspace is the first/last on the output, jump to the previous/next output
243 int num_outputs = root_container.children->length;
244 for (i = 0; i < num_outputs; i++) {
245 if (root_container.children->items[i] == current_output) {
246 swayc_t *next_output = root_container.children->items[wrap(i + offset, num_outputs)];
247 return workspace_output_prev_next_impl(next_output, next);
248 }
249 }
250
251 // Doesn't happen, at worst the for loop returns the previously active workspace on the active output
252 return NULL;
253}
254
255swayc_t *workspace_output_next() {
256 return workspace_output_prev_next_impl(swayc_active_output(), true);
257}
258
259swayc_t *workspace_next() {
260 return workspace_prev_next_impl(swayc_active_workspace(), true);
261}
262
263swayc_t *workspace_output_prev() {
264 return workspace_output_prev_next_impl(swayc_active_output(), false);
265}
266
267swayc_t *workspace_prev() {
268 return workspace_prev_next_impl(swayc_active_workspace(), false);
269}
270
271bool workspace_switch(swayc_t *workspace) {
272 if (!workspace) {
273 return false;
274 }
275 swayc_t *active_ws = swayc_active_workspace();
276 if (config->auto_back_and_forth && active_ws == workspace && prev_workspace_name) {
277 swayc_t *new_ws = workspace_by_name(prev_workspace_name);
278 workspace = new_ws ? new_ws : workspace_create(prev_workspace_name);
279 }
280
281 if (!prev_workspace_name
282 || (strcmp(prev_workspace_name, active_ws->name)
283 && active_ws != workspace)) {
284 free(prev_workspace_name);
285 prev_workspace_name = malloc(strlen(active_ws->name) + 1);
286 if (!prev_workspace_name) {
287 sway_log(L_ERROR, "Unable to allocate previous workspace name");
288 return false;
289 }
290 strcpy(prev_workspace_name, active_ws->name);
291 }
292
293 // move sticky containers
294 if (swayc_parent_by_type(active_ws, C_OUTPUT) == swayc_parent_by_type(workspace, C_OUTPUT)) {
295 // don't change list while traversing it, use intermediate list instead
296 list_t *stickies = create_list();
297 for (int i = 0; i < active_ws->floating->length; i++) {
298 swayc_t *cont = active_ws->floating->items[i];
299 if (cont->sticky) {
300 list_add(stickies, cont);
301 }
302 }
303 for (int i = 0; i < stickies->length; i++) {
304 swayc_t *cont = stickies->items[i];
305 sway_log(L_DEBUG, "Moving sticky container %p to %p:%s",
306 cont, workspace, workspace->name);
307 swayc_t *parent = remove_child(cont);
308 add_floating(workspace, cont);
309 // Destroy old container if we need to
310 destroy_container(parent);
311 }
312 list_free(stickies);
313 }
314 sway_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name);
315 if (!set_focused_container(get_focused_view(workspace))) {
316 return false;
317 }
318 swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
319 arrange_backgrounds();
320 arrange_windows(output, -1, -1);
321 return true;
322}
323
324swayc_t *workspace_for_pid(pid_t pid) {
325 int i;
326 swayc_t *ws = NULL;
327 struct pid_workspace *pw = NULL;
328
329 sway_log(L_DEBUG, "looking for workspace for pid %d", pid);
330
331 // leaving this here as it's useful for debugging
332 // sway_log(L_DEBUG, "all pid_workspaces");
333 // for (int k = 0; k < config->pid_workspaces->length; k++) {
334 // pw = config->pid_workspaces->items[k];
335 // sway_log(L_DEBUG, "pid %d workspace %s time_added %li", *pw->pid, pw->workspace, *pw->time_added);
336 // }
337
338 do {
339 for (i = 0; i < config->pid_workspaces->length; i++) {
340 pw = config->pid_workspaces->items[i];
341 pid_t *pw_pid = pw->pid;
342
343 if (pid == *pw_pid) {
344 sway_log(L_DEBUG, "found pid_workspace for pid %d, workspace %s", pid, pw->workspace);
345 break; // out of for loop
346 }
347
348 pw = NULL;
349 }
350
351 if (pw) {
352 break; // out of do-while loop
353 }
354
355 pid = get_parent_pid(pid);
356 // no sense in looking for matches for pid 0.
357 // also, if pid == getpid(), that is the compositor's
358 // pid, which definitely isn't helpful
359 } while (pid > 0 && pid != getpid());
360
361 if (pw) {
362 ws = workspace_by_name(pw->workspace);
363
364 if (!ws) {
365 sway_log(L_DEBUG, "Creating workspace %s for pid %d because it disappeared", pw->workspace, pid);
366 ws = workspace_create(pw->workspace);
367 }
368
369 list_del(config->pid_workspaces, i);
370 }
371
372 return ws;
373}