diff options
Diffstat (limited to 'sway')
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 | |||
15 | void 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 | |||
25 | void border_clear(struct border *border) { | ||
26 | if (border && border->buffer) { | ||
27 | free(border->buffer); | ||
28 | border->buffer = NULL; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | static 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 | */ | ||
73 | static 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 | |||
99 | int 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 | |||
109 | static 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 | |||
175 | static 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 | */ | ||
250 | static 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 | |||
321 | void 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 | |||
351 | static 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 | |||
457 | cleanup: | ||
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 | |||
469 | void 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 | |||
480 | void 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 | |||
498 | bool 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 | |||
39 | struct cmd_handler { | ||
40 | char *command; | ||
41 | sway_cmd *handle; | ||
42 | }; | ||
43 | |||
44 | int sp_index = 0; | ||
45 | |||
46 | swayc_t *current_container = NULL; | ||
47 | |||
48 | // Returns error object, or NULL if check succeeds. | ||
49 | struct 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 | |||
87 | void 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 | |||
104 | void 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 | |||
142 | void 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 */ | ||
158 | static 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 | |||
226 | static 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 | */ | ||
258 | struct 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 | |||
287 | static 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 | |||
301 | static 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 | |||
315 | static 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 | |||
327 | static 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 | |||
337 | static 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 | |||
343 | static 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 | |||
375 | struct 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) | ||
512 | struct 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 | |||
550 | cleanup: | ||
551 | free_argv(argc, argv); | ||
552 | return results; | ||
553 | } | ||
554 | |||
555 | struct 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 | |||
629 | cleanup: | ||
630 | free_argv(argc, argv); | ||
631 | return results; | ||
632 | } | ||
633 | |||
634 | struct 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 | |||
661 | void 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 | |||
671 | const 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 | |||
9 | struct 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 | |||
9 | struct 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 | |||
5 | struct 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 | |||
6 | struct 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 | |||
9 | struct 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 | |||
4 | static 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 | |||
25 | static 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 | |||
48 | struct 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 | |||
62 | struct 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 | |||
71 | struct 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 | |||
75 | struct 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 | |||
79 | struct 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 | |||
88 | struct 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 | |||
97 | struct 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 | |||
106 | struct 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 | |||
110 | struct 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 | |||
114 | struct 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 | |||
118 | struct 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 | |||
122 | struct 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 | |||
5 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
9 | static 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 | |||
37 | struct 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 | |||
5 | struct 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 | |||
6 | struct 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 | |||
9 | static 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 | |||
42 | struct 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 | |||
7 | struct 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 | |||
7 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
5 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
5 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
12 | int binding_order = 0; | ||
13 | |||
14 | struct 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 | |||
98 | struct 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 | |||
9 | struct 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 | |||
5 | static 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 | |||
35 | struct cmd_results *cmd_client_focused(int argc, char **argv) { | ||
36 | return parse_border_color(&config->border_colors.focused, "client.focused", argc, argv); | ||
37 | } | ||
38 | |||
39 | struct 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 | |||
43 | struct cmd_results *cmd_client_unfocused(int argc, char **argv) { | ||
44 | return parse_border_color(&config->border_colors.unfocused, "client.unfocused", argc, argv); | ||
45 | } | ||
46 | |||
47 | struct cmd_results *cmd_client_urgent(int argc, char **argv) { | ||
48 | return parse_border_color(&config->border_colors.urgent, "client.urgent", argc, argv); | ||
49 | } | ||
50 | |||
51 | struct cmd_results *cmd_client_placeholder(int argc, char **argv) { | ||
52 | return parse_border_color(&config->border_colors.placeholder, "client.placeholder", argc, argv); | ||
53 | } | ||
54 | |||
55 | struct 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 | |||
7 | static 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 | |||
19 | struct 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 | |||
8 | struct 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 | |||
6 | struct 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 | |||
7 | struct 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 | |||
7 | struct 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 | |||
6 | struct 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 | |||
10 | struct 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 | |||
4 | void sway_terminate(int exit_code); | ||
5 | |||
6 | struct 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 | |||
10 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
10 | struct 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 | |||
8 | struct 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 | |||
12 | struct 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 | |||
5 | struct 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 | |||
8 | struct 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 | |||
9 | struct 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 | |||
5 | struct 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 | |||
10 | struct 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 | |||
11 | struct 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 | |||
5 | struct 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 | |||
4 | struct 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 | |||
7 | struct 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 | |||
6 | struct 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 | |||
7 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
7 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
7 | struct 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 | |||
11 | static struct ipc_policy *current_policy = NULL; | ||
12 | |||
13 | struct 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 | |||
46 | struct 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 | |||
64 | struct 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 | |||
115 | struct 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 | |||
5 | struct 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 | */ | ||
10 | static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv); | ||
11 | |||
12 | struct 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 | |||
77 | static 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 | |||
6 | struct 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 | |||
8 | static 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 | |||
17 | struct 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 | |||
11 | struct 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 | |||
5 | struct 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 | |||
13 | struct 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 | |||
4 | struct 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 | |||
4 | struct 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 | |||
9 | struct 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 | |||
5 | struct 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 | |||
15 | static char *bg_options[] = { | ||
16 | "stretch", | ||
17 | "center", | ||
18 | "fill", | ||
19 | "fit", | ||
20 | "tile" | ||
21 | }; | ||
22 | |||
23 | struct 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 | |||
216 | fail: | ||
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 | |||
9 | static 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 | |||
43 | struct 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 | |||
78 | struct 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 | |||
5 | struct 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 | |||
15 | enum resize_dim_types { | ||
16 | RESIZE_DIM_PX, | ||
17 | RESIZE_DIM_PPT, | ||
18 | RESIZE_DIM_DEFAULT, | ||
19 | }; | ||
20 | |||
21 | static 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 | |||
52 | static 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 | |||
66 | static 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 | |||
200 | static 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 | |||
213 | static 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 | |||
227 | static 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 | |||
262 | static 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 | |||
326 | struct 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 | |||
9 | static 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 | |||
37 | struct 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 | |||
5 | struct 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 | ||
11 | static 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 | |||
17 | struct 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 | |||
5 | struct 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 | |||
5 | struct 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 | |||
11 | static 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 | |||
57 | struct 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 | |||
83 | struct cmd_results *cmd_splitv(int argc, char **argv) { | ||
84 | return _do_split(argc, argv, L_VERT); | ||
85 | } | ||
86 | |||
87 | struct cmd_results *cmd_splith(int argc, char **argv) { | ||
88 | return _do_split(argc, argv, L_HORIZ); | ||
89 | } | ||
90 | |||
91 | struct 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 | |||
5 | struct 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 | |||
7 | struct 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 | |||
12 | struct 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 | |||
5 | struct 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 | |||
5 | struct 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 | |||
32 | struct sway_config *config = NULL; | ||
33 | |||
34 | static void terminate_swaybar(pid_t pid); | ||
35 | |||
36 | static 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 | |||
45 | static 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 | |||
54 | static 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 | |||
67 | static 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 | |||
119 | void free_input_config(struct input_config *ic) { | ||
120 | if (!ic) { | ||
121 | return; | ||
122 | } | ||
123 | free(ic->identifier); | ||
124 | free(ic); | ||
125 | } | ||
126 | |||
127 | void 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 | |||
137 | static 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 | |||
146 | static 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 | ||
165 | void 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 | |||
196 | void 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 | |||
206 | void free_command_policy(struct command_policy *policy) { | ||
207 | if (!policy) { | ||
208 | return; | ||
209 | } | ||
210 | free(policy->command); | ||
211 | free(policy); | ||
212 | } | ||
213 | |||
214 | void free_feature_policy(struct feature_policy *policy) { | ||
215 | if (!policy) { | ||
216 | return; | ||
217 | } | ||
218 | free(policy->program); | ||
219 | free(policy); | ||
220 | } | ||
221 | |||
222 | void 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 | |||
295 | static bool file_exists(const char *path) { | ||
296 | return path && access(path, R_OK) != -1; | ||
297 | } | ||
298 | |||
299 | static 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; | ||
403 | cleanup: | ||
404 | sway_abort("Unable to allocate config structures"); | ||
405 | } | ||
406 | |||
407 | static 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 | |||
414 | void 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 | |||
432 | static 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 | |||
473 | const char *current_config_path; | ||
474 | |||
475 | static 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 | |||
506 | static int qstrcmp(const void* a, const void* b) { | ||
507 | return strcmp(*((char**) a), *((char**) b)); | ||
508 | } | ||
509 | |||
510 | bool 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 | |||
596 | static 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 | |||
647 | bool 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 | |||
685 | struct 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 | |||
694 | bool 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 | |||
842 | int 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 | |||
848 | int 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 | |||
855 | void 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 | |||
894 | void 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 | |||
933 | static 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 | |||
1000 | static 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 | |||
1011 | void 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 | |||
1021 | static 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 | |||
1034 | void 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 | |||
1070 | char *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. | ||
1116 | int 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 | |||
1121 | int 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 | |||
1167 | int 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 | |||
1176 | int sway_binding_cmp_qsort(const void *a, const void *b) { | ||
1177 | return sway_binding_cmp(*(void **)a, *(void **)b); | ||
1178 | } | ||
1179 | |||
1180 | void 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 | |||
1193 | int 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 | |||
1204 | int 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 | |||
1213 | int sway_mouse_binding_cmp_qsort(const void *a, const void *b) { | ||
1214 | return sway_mouse_binding_cmp(*(void **)a, *(void **)b); | ||
1215 | } | ||
1216 | |||
1217 | void free_sway_mouse_binding(struct sway_mouse_binding *binding) { | ||
1218 | if (binding->command) { | ||
1219 | free(binding->command); | ||
1220 | } | ||
1221 | free(binding); | ||
1222 | } | ||
1223 | |||
1224 | struct 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 | |||
1249 | struct 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 | |||
1310 | cleanup: | ||
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 | |||
25 | static 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 | |||
46 | static 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 | |||
98 | static 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 | |||
124 | swayc_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 | |||
220 | swayc_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 | |||
245 | swayc_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 | |||
295 | swayc_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 | |||
340 | swayc_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 | |||
397 | void 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 | |||
439 | swayc_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 | |||
466 | swayc_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 | |||
509 | swayc_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 | |||
522 | swayc_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 | |||
540 | swayc_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 | |||
568 | static 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 | |||
575 | swayc_t *swayc_by_name(const char *name) { | ||
576 | return swayc_by_test(&root_container, test_name, (void *)name); | ||
577 | } | ||
578 | |||
579 | swayc_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 | |||
592 | swayc_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 | |||
605 | swayc_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 | |||
618 | swayc_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 | |||
632 | static 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 | |||
663 | swayc_t *swayc_by_handle(wlc_handle handle) { | ||
664 | return _swayc_by_handle_helper(handle, &root_container); | ||
665 | } | ||
666 | |||
667 | swayc_t *swayc_active_output(void) { | ||
668 | return root_container.focused; | ||
669 | } | ||
670 | |||
671 | swayc_t *swayc_active_workspace(void) { | ||
672 | return root_container.focused ? root_container.focused->focused : NULL; | ||
673 | } | ||
674 | |||
675 | swayc_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 | |||
696 | static 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 | |||
708 | swayc_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 | |||
773 | swayc_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 | |||
809 | bool swayc_is_fullscreen(swayc_t *view) { | ||
810 | return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN); | ||
811 | } | ||
812 | |||
813 | bool swayc_is_active(swayc_t *view) { | ||
814 | return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_ACTIVATED); | ||
815 | } | ||
816 | |||
817 | bool 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 | |||
827 | bool swayc_is_child_of(swayc_t *child, swayc_t *parent) { | ||
828 | return swayc_is_parent_of(parent, child); | ||
829 | } | ||
830 | |||
831 | bool swayc_is_empty_workspace(swayc_t *container) { | ||
832 | return container->type == C_WORKSPACE && container->children->length == 0; | ||
833 | } | ||
834 | |||
835 | int 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 | |||
858 | void 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 | |||
877 | void 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 | |||
908 | void 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 | |||
939 | void 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 | |||
949 | void 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 | |||
961 | static void close_view(swayc_t *container, void *data) { | ||
962 | if (container->type == C_VIEW) { | ||
963 | wlc_view_close(container->handle); | ||
964 | } | ||
965 | } | ||
966 | |||
967 | void close_views(swayc_t *container) { | ||
968 | container_map(container, close_view, NULL); | ||
969 | } | ||
970 | |||
971 | swayc_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 | |||
986 | swayc_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 | |||
996 | swayc_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 | |||
13 | enum 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 | |||
29 | static 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 | */ | ||
48 | struct crit_token { | ||
49 | enum criteria_type type; | ||
50 | pcre *regex; | ||
51 | char *raw; | ||
52 | }; | ||
53 | |||
54 | static void free_crit_token(struct crit_token *crit) { | ||
55 | pcre_free(crit->regex); | ||
56 | free(crit->raw); | ||
57 | free(crit); | ||
58 | } | ||
59 | |||
60 | static 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. | ||
69 | static 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. | ||
85 | static 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. | ||
103 | static 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. | ||
184 | static 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. | ||
212 | static 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, ®_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 | ||
229 | static 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. | ||
235 | char *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 | } | ||
260 | ect_cleanup: | ||
261 | free(argv[0]); // base string | ||
262 | free(argv); | ||
263 | return error; | ||
264 | } | ||
265 | |||
266 | static 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). | ||
271 | static 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 | |||
377 | int 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 | |||
393 | void 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 | |||
406 | bool 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 | |||
416 | list_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 | |||
427 | struct list_tokens { | ||
428 | list_t *list; | ||
429 | list_t *tokens; | ||
430 | }; | ||
431 | |||
432 | static 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 | |||
438 | list_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 | |||
12 | bool locked_container_focus = false; | ||
13 | bool suspend_workspace_cleanup = false; | ||
14 | |||
15 | // switches parent focus to c. will switch it accordingly | ||
16 | static 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 | |||
60 | bool 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 | |||
88 | swayc_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 | |||
98 | bool 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 | |||
192 | bool 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 | |||
237 | swayc_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 | |||
251 | swayc_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 | |||
261 | swayc_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 | |||
37 | static 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 | |||
48 | static 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 | |||
59 | static 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 | |||
95 | static 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 | |||
100 | static 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 | |||
109 | static 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 | |||
115 | static 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 | |||
126 | static 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 | |||
159 | static 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 | |||
170 | static 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 | |||
193 | static 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 | |||
212 | static void handle_output_post_render(wlc_handle output) { | ||
213 | ipc_get_pixels(output); | ||
214 | } | ||
215 | |||
216 | static void handle_view_pre_render(wlc_handle view) { | ||
217 | render_view_borders(view); | ||
218 | } | ||
219 | |||
220 | static 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 | |||
236 | static 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 | |||
247 | static 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 | |||
268 | static 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 | |||
340 | static 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 | |||
532 | static 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 | |||
610 | static void handle_view_focus(wlc_handle view, bool focus) { | ||
611 | return; | ||
612 | } | ||
613 | |||
614 | static 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 | |||
638 | static 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 | |||
674 | static 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 | |||
701 | static 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 | |||
728 | static 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 | |||
749 | static 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 | |||
769 | static 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 | |||
781 | static 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 | |||
866 | static 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 | |||
930 | static 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 | |||
946 | static 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 | |||
1082 | bool 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 | |||
1107 | static 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 | |||
1123 | void 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 | |||
10 | struct 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 | |||
22 | static struct key_state key_state_array[KEY_STATE_MAX_LENGTH]; | ||
23 | |||
24 | static struct key_state last_released; | ||
25 | |||
26 | static uint32_t modifiers_state; | ||
27 | |||
28 | void 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 | |||
41 | uint32_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 | |||
57 | void modifiers_state_update(uint32_t new_state) { | ||
58 | modifiers_state = new_state; | ||
59 | } | ||
60 | |||
61 | static 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 | |||
82 | bool 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 | |||
86 | bool 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 | |||
92 | void 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 | |||
107 | void 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 | |||
120 | struct pointer_state pointer_state; | ||
121 | |||
122 | static 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 | |||
146 | static struct { | ||
147 | bool left; | ||
148 | bool top; | ||
149 | } lock; | ||
150 | |||
151 | // initial set/unset | ||
152 | |||
153 | static 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 | |||
161 | static 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 | |||
186 | static 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 | |||
195 | static 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 | |||
205 | void 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 | |||
228 | void 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 | |||
234 | static 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 | |||
256 | static 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 | |||
284 | void 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 | |||
341 | void 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 | |||
475 | void 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 | |||
15 | static 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 | |||
38 | static 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 | |||
49 | static 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 | |||
70 | static 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 | |||
104 | static 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 | |||
131 | static 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 | |||
142 | static 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 | ||
153 | static 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 | |||
163 | static 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 | |||
203 | static 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 | |||
208 | json_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 | |||
259 | json_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 | |||
320 | json_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 | |||
335 | json_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 | |||
461 | json_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__ | ||
19 | struct 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 | |||
37 | static int ipc_socket = -1; | ||
38 | static struct wl_event_source *ipc_event_source = NULL; | ||
39 | static struct sockaddr_un *ipc_sockaddr = NULL; | ||
40 | static list_t *ipc_client_list = NULL; | ||
41 | |||
42 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; | ||
43 | |||
44 | struct 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 | |||
57 | struct sockaddr_un *ipc_user_sockaddr(void); | ||
58 | int ipc_handle_connection(int fd, uint32_t mask, void *data); | ||
59 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); | ||
60 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); | ||
61 | void ipc_client_disconnect(struct ipc_client *client); | ||
62 | void ipc_client_handle_command(struct ipc_client *client); | ||
63 | bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); | ||
64 | void ipc_get_workspaces_callback(swayc_t *workspace, void *data); | ||
65 | void ipc_get_outputs_callback(swayc_t *container, void *data); | ||
66 | static void ipc_get_marks_callback(swayc_t *container, void *data); | ||
67 | |||
68 | void 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 | |||
101 | void 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 | |||
115 | struct 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 | |||
137 | static 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 | |||
153 | int 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 | |||
210 | static const int ipc_header_size = sizeof(ipc_magic)+8; | ||
211 | |||
212 | int 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 | |||
274 | int 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 | |||
316 | void 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 | |||
338 | bool 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 | ||
347 | bool 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 | |||
375 | void 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 | |||
584 | exit_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 | |||
588 | exit_cleanup: | ||
589 | client->payload_length = 0; | ||
590 | free(buf); | ||
591 | return; | ||
592 | } | ||
593 | |||
594 | bool 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 | |||
640 | void 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 | |||
652 | void 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 | |||
658 | static 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 | |||
668 | void 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 | |||
707 | void 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 | |||
731 | void 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 | |||
743 | void 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 | |||
752 | void 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 | |||
763 | void 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 | |||
777 | static 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 | |||
789 | void 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 | |||
17 | swayc_t root_container; | ||
18 | swayc_t *current_focus; | ||
19 | list_t *scratchpad; | ||
20 | |||
21 | int min_sane_h = 60; | ||
22 | int min_sane_w = 100; | ||
23 | |||
24 | void 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 | |||
36 | int 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 | |||
60 | void 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 | |||
74 | static double *get_height(swayc_t *cont) { | ||
75 | return &cont->height; | ||
76 | } | ||
77 | |||
78 | static double *get_width(swayc_t *cont) { | ||
79 | return &cont->width; | ||
80 | } | ||
81 | |||
82 | void 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 | |||
137 | void 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 | |||
152 | swayc_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 | |||
179 | swayc_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 | |||
214 | swayc_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 | |||
288 | void 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 | |||
319 | void 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 | |||
334 | static 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 | |||
352 | void 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 | |||
537 | void 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 | |||
582 | void 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 | |||
607 | static 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 | |||
629 | static 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 | |||
672 | void 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 | |||
707 | static 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 | |||
746 | void 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 | */ | ||
935 | static 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); | ||
939 | static 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); | ||
943 | static void apply_tabbed_or_stacked_layout(swayc_t *container, double x, | ||
944 | double y, double width, | ||
945 | double height); | ||
946 | |||
947 | static 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 | |||
952 | static 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 | |||
1128 | void 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 | |||
1180 | void 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 | |||
1233 | void 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 | |||
1253 | void 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 | |||
1377 | void 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 | |||
1383 | void 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 | */ | ||
1396 | static 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 | |||
1437 | swayc_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 | |||
1569 | swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir) { | ||
1570 | return get_swayc_in_direction_under(container, dir, NULL); | ||
1571 | } | ||
1572 | |||
1573 | void 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 | |||
1599 | enum 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 | |||
1611 | bool 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 | */ | ||
1618 | static 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 | */ | ||
1628 | static 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 | */ | ||
1635 | size_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 | */ | ||
1642 | size_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 | */ | ||
1651 | int 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 | */ | ||
1676 | int 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 | */ | ||
1703 | size_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 | */ | ||
1737 | bool 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 | |||
9 | void 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 | |||
16 | swayc_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 | |||
51 | swayc_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. | ||
103 | swayc_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 | |||
230 | void 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 | |||
252 | void 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 | |||
258 | static 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 | |||
276 | void 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 | |||
11 | static 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 | |||
45 | struct 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 | |||
73 | struct 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 | |||
101 | struct 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 | |||
115 | static 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 | |||
143 | struct 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 | |||
163 | uint32_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 | |||
180 | uint32_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 | |||
197 | uint32_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 | |||
213 | const 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 | |||
23 | char *prev_workspace_name = NULL; | ||
24 | struct workspace_by_number_data { | ||
25 | int len; | ||
26 | const char *cset; | ||
27 | const char *name; | ||
28 | }; | ||
29 | |||
30 | static 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 | |||
44 | char *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 | |||
132 | swayc_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 | |||
157 | static bool _workspace_by_name(swayc_t *view, void *data) { | ||
158 | return (view->type == C_WORKSPACE) && | ||
159 | (strcasecmp(view->name, (char *) data) == 0); | ||
160 | } | ||
161 | |||
162 | swayc_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 | |||
183 | static 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 | } | ||
191 | swayc_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 | */ | ||
205 | swayc_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 | */ | ||
226 | swayc_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 | |||
255 | swayc_t *workspace_output_next() { | ||
256 | return workspace_output_prev_next_impl(swayc_active_output(), true); | ||
257 | } | ||
258 | |||
259 | swayc_t *workspace_next() { | ||
260 | return workspace_prev_next_impl(swayc_active_workspace(), true); | ||
261 | } | ||
262 | |||
263 | swayc_t *workspace_output_prev() { | ||
264 | return workspace_output_prev_next_impl(swayc_active_output(), false); | ||
265 | } | ||
266 | |||
267 | swayc_t *workspace_prev() { | ||
268 | return workspace_prev_next_impl(swayc_active_workspace(), false); | ||
269 | } | ||
270 | |||
271 | bool 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 | |||
324 | swayc_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 | } | ||