diff options
Diffstat (limited to 'sway')
145 files changed, 9959 insertions, 12402 deletions
diff --git a/sway/CMakeLists.txt b/sway/CMakeLists.txt deleted file mode 100644 index 48f7a7a8..00000000 --- a/sway/CMakeLists.txt +++ /dev/null | |||
@@ -1,107 +0,0 @@ | |||
1 | include_directories( | ||
2 | ${PROTOCOLS_INCLUDE_DIRS} | ||
3 | ${WLC_INCLUDE_DIRS} | ||
4 | ${PCRE_INCLUDE_DIRS} | ||
5 | ${JSONC_INCLUDE_DIRS} | ||
6 | ${XKBCOMMON_INCLUDE_DIRS} | ||
7 | ${LIBINPUT_INCLUDE_DIRS} | ||
8 | ${CAIRO_INCLUDE_DIRS} | ||
9 | ${PANGO_INCLUDE_DIRS} | ||
10 | ${WAYLAND_INCLUDE_DIR} | ||
11 | ) | ||
12 | |||
13 | file(GLOB cmds | ||
14 | "commands/*.c" | ||
15 | "commands/bar/*.c" | ||
16 | "commands/input/*.c" | ||
17 | ) | ||
18 | |||
19 | add_executable(sway | ||
20 | commands.c | ||
21 | ${cmds} | ||
22 | base64.c | ||
23 | config.c | ||
24 | container.c | ||
25 | criteria.c | ||
26 | debug_log.c | ||
27 | extensions.c | ||
28 | focus.c | ||
29 | handlers.c | ||
30 | input.c | ||
31 | input_state.c | ||
32 | ipc-json.c | ||
33 | ipc-server.c | ||
34 | layout.c | ||
35 | main.c | ||
36 | output.c | ||
37 | workspace.c | ||
38 | border.c | ||
39 | security.c | ||
40 | ) | ||
41 | |||
42 | add_definitions( | ||
43 | -DSYSCONFDIR="${CMAKE_INSTALL_FULL_SYSCONFDIR}" | ||
44 | ) | ||
45 | |||
46 | target_link_libraries(sway | ||
47 | sway-common | ||
48 | sway-protocols | ||
49 | sway-wayland | ||
50 | ${WLC_LIBRARIES} | ||
51 | ${XKBCOMMON_LIBRARIES} | ||
52 | ${PCRE_LIBRARIES} | ||
53 | ${JSONC_LIBRARIES} | ||
54 | ${WAYLAND_SERVER_LIBRARIES} | ||
55 | ${LIBINPUT_LIBRARIES} | ||
56 | ${PANGO_LIBRARIES} | ||
57 | ${JSONC_LIBRARIES} | ||
58 | m | ||
59 | ) | ||
60 | |||
61 | if (CMAKE_SYSTEM_NAME STREQUAL Linux) | ||
62 | target_link_libraries(sway cap) | ||
63 | endif (CMAKE_SYSTEM_NAME STREQUAL Linux) | ||
64 | |||
65 | install( | ||
66 | TARGETS sway | ||
67 | RUNTIME | ||
68 | DESTINATION bin | ||
69 | COMPONENT runtime | ||
70 | ) | ||
71 | |||
72 | add_custom_target(configs ALL) | ||
73 | |||
74 | function(add_config name source destination) | ||
75 | add_custom_command( | ||
76 | OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name} | ||
77 | COMMAND sed -r | ||
78 | 's?__PREFIX__?${CMAKE_INSTALL_PREFIX}?g\; s?__SYSCONFDIR__?${CMAKE_INSTALL_FULL_SYSCONFDIR}?g\; s?__DATADIR__?${CMAKE_INSTALL_FULL_DATADIR}?g' | ||
79 | ${PROJECT_SOURCE_DIR}/${source}.in > ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name} | ||
80 | DEPENDS ${PROJECT_SOURCE_DIR}/${source}.in | ||
81 | COMMENT "Generating config file ${source}" | ||
82 | ) | ||
83 | |||
84 | install( | ||
85 | FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name} | ||
86 | DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/${destination} | ||
87 | COMPONENT configuration | ||
88 | ) | ||
89 | |||
90 | add_custom_target(config-${name} DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}) | ||
91 | add_dependencies(configs config-${name}) | ||
92 | endfunction() | ||
93 | |||
94 | add_config(config config sway) | ||
95 | add_config(00-defaults security.d/00-defaults sway/security.d) | ||
96 | |||
97 | if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) | ||
98 | add_config(10-freebsd security.d/10-freebsd sway/security.d) | ||
99 | endif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) | ||
100 | |||
101 | if (A2X_FOUND) | ||
102 | add_manpage(sway 1) | ||
103 | add_manpage(sway 5) | ||
104 | add_manpage(sway-input 5) | ||
105 | add_manpage(sway-bar 5) | ||
106 | add_manpage(sway-security 7) | ||
107 | endif() | ||
diff --git a/sway/border.c b/sway/border.c deleted file mode 100644 index df0022ce..00000000 --- a/sway/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/commands.c b/sway/commands.c index c7dbf731..54d84450 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -1,39 +1,18 @@ | |||
1 | #define _XOPEN_SOURCE 700 | 1 | #define _XOPEN_SOURCE 500 |
2 | #include <xkbcommon/xkbcommon.h> | 2 | #include <ctype.h> |
3 | #include <xkbcommon/xkbcommon-names.h> | 3 | #include <stdarg.h> |
4 | #include <wlc/wlc.h> | ||
5 | #include <wlc/wlc-render.h> | ||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | 4 | #include <stdlib.h> |
8 | #include <errno.h> | ||
9 | #include <string.h> | 5 | #include <string.h> |
10 | #include <strings.h> | 6 | #include <strings.h> |
11 | #include <unistd.h> | 7 | #include <stdio.h> |
12 | #include <ctype.h> | 8 | #include <json-c/json.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" | 9 | #include "sway/commands.h" |
24 | #include "sway/container.h" | 10 | #include "sway/config.h" |
25 | #include "sway/output.h" | ||
26 | #include "sway/handlers.h" | ||
27 | #include "sway/input_state.h" | ||
28 | #include "sway/criteria.h" | 11 | #include "sway/criteria.h" |
29 | #include "sway/ipc-server.h" | ||
30 | #include "sway/security.h" | 12 | #include "sway/security.h" |
31 | #include "sway/input.h" | 13 | #include "sway/input/input-manager.h" |
32 | #include "sway/border.h" | 14 | #include "sway/input/seat.h" |
33 | #include "stringop.h" | 15 | #include "stringop.h" |
34 | #include "sway.h" | ||
35 | #include "util.h" | ||
36 | #include "list.h" | ||
37 | #include "log.h" | 16 | #include "log.h" |
38 | 17 | ||
39 | struct cmd_handler { | 18 | struct cmd_handler { |
@@ -41,10 +20,6 @@ struct cmd_handler { | |||
41 | sway_cmd *handle; | 20 | sway_cmd *handle; |
42 | }; | 21 | }; |
43 | 22 | ||
44 | int sp_index = 0; | ||
45 | |||
46 | swayc_t *current_container = NULL; | ||
47 | |||
48 | // Returns error object, or NULL if check succeeds. | 23 | // Returns error object, or NULL if check succeeds. |
49 | struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) { | 24 | struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) { |
50 | struct cmd_results *error = NULL; | 25 | struct cmd_results *error = NULL; |
@@ -84,24 +59,7 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type | |||
84 | return error; | 59 | return error; |
85 | } | 60 | } |
86 | 61 | ||
87 | void hide_view_in_scratchpad(swayc_t *sp_view) { | 62 | void apply_input_config(struct input_config *input) { |
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; | 63 | int i; |
106 | i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier); | 64 | i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier); |
107 | if (i >= 0) { | 65 | if (i >= 0) { |
@@ -114,111 +72,41 @@ void input_cmd_apply(struct input_config *input) { | |||
114 | list_add(config->input_configs, input); | 72 | list_add(config->input_configs, input); |
115 | } | 73 | } |
116 | 74 | ||
117 | current_input_config = input; | 75 | input_manager_apply_input_config(input_manager, 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 | struct libinput_device *device = NULL; | ||
124 | for (int i = 0; i < input_devices->length; ++i) { | ||
125 | device = input_devices->items[i]; | ||
126 | char* dev_identifier = libinput_dev_unique_id(device); | ||
127 | if (!dev_identifier) { | ||
128 | break; | ||
129 | } | ||
130 | int match = dev_identifier && strcmp(dev_identifier, input->identifier) == 0; | ||
131 | free(dev_identifier); | ||
132 | if (match) { | ||
133 | apply_input_config(input, device); | ||
134 | break; | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | } | 76 | } |
139 | 77 | ||
140 | void remove_view_from_scratchpad(swayc_t *view) { | 78 | void apply_seat_config(struct seat_config *seat_config) { |
141 | int i; | 79 | int i; |
142 | for (i = 0; i < scratchpad->length; i++) { | 80 | i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name); |
143 | if (scratchpad->items[i] == view) { | 81 | if (i >= 0) { |
144 | if (sp_index == 0) { | 82 | // merge existing config |
145 | sp_index = scratchpad->length - 1; | 83 | struct seat_config *sc = config->seat_configs->items[i]; |
146 | } else { | 84 | merge_seat_config(sc, seat_config); |
147 | sp_index--; | 85 | free_seat_config(seat_config); |
148 | } | 86 | seat_config = sc; |
149 | list_del(scratchpad, sp_index); | 87 | } else { |
150 | sp_view = NULL; | 88 | list_add(config->seat_configs, seat_config); |
151 | } | ||
152 | } | 89 | } |
90 | |||
91 | input_manager_apply_seat_config(input_manager, seat_config); | ||
153 | } | 92 | } |
154 | 93 | ||
155 | /* Keep alphabetized */ | 94 | /* Keep alphabetized */ |
156 | static struct cmd_handler handlers[] = { | 95 | static struct cmd_handler handlers[] = { |
157 | { "assign", cmd_assign }, | ||
158 | { "bar", cmd_bar }, | 96 | { "bar", cmd_bar }, |
159 | { "bindcode", cmd_bindcode }, | 97 | { "bindcode", cmd_bindcode }, |
160 | { "bindsym", cmd_bindsym }, | 98 | { "bindsym", cmd_bindsym }, |
161 | { "border", cmd_border }, | ||
162 | { "client.background", cmd_client_background }, | ||
163 | { "client.focused", cmd_client_focused }, | ||
164 | { "client.focused_inactive", cmd_client_focused_inactive }, | ||
165 | { "client.placeholder", cmd_client_placeholder }, | ||
166 | { "client.unfocused", cmd_client_unfocused }, | ||
167 | { "client.urgent", cmd_client_urgent }, | ||
168 | { "clipboard", cmd_clipboard }, | ||
169 | { "commands", cmd_commands }, | ||
170 | { "debuglog", cmd_debuglog }, | ||
171 | { "default_border", cmd_default_border }, | ||
172 | { "default_floating_border", cmd_default_floating_border }, | ||
173 | { "default_orientation", cmd_orientation }, | ||
174 | { "exec", cmd_exec }, | 99 | { "exec", cmd_exec }, |
175 | { "exec_always", cmd_exec_always }, | 100 | { "exec_always", cmd_exec_always }, |
176 | { "exit", cmd_exit }, | ||
177 | { "floating", cmd_floating }, | ||
178 | { "floating_maximum_size", cmd_floating_maximum_size }, | ||
179 | { "floating_minimum_size", cmd_floating_minimum_size }, | ||
180 | { "floating_modifier", cmd_floating_mod }, | ||
181 | { "floating_scroll", cmd_floating_scroll }, | ||
182 | { "focus", cmd_focus }, | ||
183 | { "focus_follows_mouse", cmd_focus_follows_mouse }, | 101 | { "focus_follows_mouse", cmd_focus_follows_mouse }, |
184 | { "font", cmd_font }, | ||
185 | { "for_window", cmd_for_window }, | ||
186 | { "force_focus_wrapping", cmd_force_focus_wrapping }, | ||
187 | { "fullscreen", cmd_fullscreen }, | ||
188 | { "gaps", cmd_gaps }, | ||
189 | { "hide_edge_borders", cmd_hide_edge_borders }, | ||
190 | { "include", cmd_include }, | 102 | { "include", cmd_include }, |
191 | { "input", cmd_input }, | 103 | { "input", cmd_input }, |
192 | { "ipc", cmd_ipc }, | ||
193 | { "kill", cmd_kill }, | ||
194 | { "layout", cmd_layout }, | ||
195 | { "log_colors", cmd_log_colors }, | ||
196 | { "mark", cmd_mark }, | ||
197 | { "mode", cmd_mode }, | 104 | { "mode", cmd_mode }, |
198 | { "mouse_warping", cmd_mouse_warping }, | 105 | { "mouse_warping", cmd_mouse_warping }, |
199 | { "move", cmd_move }, | ||
200 | { "new_float", cmd_new_float }, | ||
201 | { "new_window", cmd_new_window }, | ||
202 | { "no_focus", cmd_no_focus }, | ||
203 | { "output", cmd_output }, | 106 | { "output", cmd_output }, |
204 | { "permit", cmd_permit }, | 107 | { "seat", cmd_seat }, |
205 | { "reject", cmd_reject }, | ||
206 | { "reload", cmd_reload }, | ||
207 | { "resize", cmd_resize }, | ||
208 | { "scratchpad", cmd_scratchpad }, | ||
209 | { "seamless_mouse", cmd_seamless_mouse }, | ||
210 | { "set", cmd_set }, | ||
211 | { "show_marks", cmd_show_marks }, | ||
212 | { "smart_gaps", cmd_smart_gaps }, | ||
213 | { "split", cmd_split }, | ||
214 | { "splith", cmd_splith }, | ||
215 | { "splitt", cmd_splitt }, | ||
216 | { "splitv", cmd_splitv }, | ||
217 | { "sticky", cmd_sticky }, | ||
218 | { "unmark", cmd_unmark }, | ||
219 | { "workspace", cmd_workspace }, | 108 | { "workspace", cmd_workspace }, |
220 | { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, | 109 | { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, |
221 | { "workspace_layout", cmd_workspace_layout }, | ||
222 | }; | 110 | }; |
223 | 111 | ||
224 | static struct cmd_handler bar_handlers[] = { | 112 | static struct cmd_handler bar_handlers[] = { |
@@ -248,54 +136,6 @@ static struct cmd_handler bar_handlers[] = { | |||
248 | { "wrap_scroll", bar_cmd_wrap_scroll }, | 136 | { "wrap_scroll", bar_cmd_wrap_scroll }, |
249 | }; | 137 | }; |
250 | 138 | ||
251 | /** | ||
252 | * Check and add color to buffer. | ||
253 | * | ||
254 | * return error object, or NULL if color is valid. | ||
255 | */ | ||
256 | struct cmd_results *add_color(const char *name, char *buffer, const char *color) { | ||
257 | int len = strlen(color); | ||
258 | if (len != 7 && len != 9) { | ||
259 | return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color); | ||
260 | } | ||
261 | |||
262 | if (color[0] != '#') { | ||
263 | return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color); | ||
264 | } | ||
265 | |||
266 | int i; | ||
267 | for (i = 1; i < len; ++i) { | ||
268 | if (!isxdigit(color[i])) { | ||
269 | return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | // copy color to buffer | ||
274 | strncpy(buffer, color, len); | ||
275 | // add default alpha channel if color was defined without it | ||
276 | if (len == 7) { | ||
277 | buffer[7] = 'f'; | ||
278 | buffer[8] = 'f'; | ||
279 | } | ||
280 | buffer[9] = '\0'; | ||
281 | |||
282 | return NULL; | ||
283 | } | ||
284 | |||
285 | static struct cmd_handler input_handlers[] = { | ||
286 | { "accel_profile", input_cmd_accel_profile }, | ||
287 | { "click_method", input_cmd_click_method }, | ||
288 | { "drag_lock", input_cmd_drag_lock }, | ||
289 | { "dwt", input_cmd_dwt }, | ||
290 | { "events", input_cmd_events }, | ||
291 | { "left_handed", input_cmd_left_handed }, | ||
292 | { "middle_emulation", input_cmd_middle_emulation }, | ||
293 | { "natural_scroll", input_cmd_natural_scroll }, | ||
294 | { "pointer_accel", input_cmd_pointer_accel }, | ||
295 | { "scroll_method", input_cmd_scroll_method }, | ||
296 | { "tap", input_cmd_tap }, | ||
297 | }; | ||
298 | |||
299 | static struct cmd_handler bar_colors_handlers[] = { | 139 | static struct cmd_handler bar_colors_handlers[] = { |
300 | { "active_workspace", bar_colors_cmd_active_workspace }, | 140 | { "active_workspace", bar_colors_cmd_active_workspace }, |
301 | { "background", bar_colors_cmd_background }, | 141 | { "background", bar_colors_cmd_background }, |
@@ -310,26 +150,27 @@ static struct cmd_handler bar_colors_handlers[] = { | |||
310 | { "urgent_workspace", bar_colors_cmd_urgent_workspace }, | 150 | { "urgent_workspace", bar_colors_cmd_urgent_workspace }, |
311 | }; | 151 | }; |
312 | 152 | ||
313 | static struct cmd_handler ipc_handlers[] = { | 153 | /* Config-time only commands. Keep alphabetized */ |
314 | { "*", cmd_ipc_cmd }, | 154 | static struct cmd_handler config_handlers[] = { |
315 | { "bar-config", cmd_ipc_cmd }, | 155 | { "default_orientation", cmd_default_orientation }, |
316 | { "command", cmd_ipc_cmd }, | 156 | { "set", cmd_set }, |
317 | { "events", cmd_ipc_events }, | 157 | { "swaybg_command", cmd_swaybg_command }, |
318 | { "inputs", cmd_ipc_cmd }, | ||
319 | { "marks", cmd_ipc_cmd }, | ||
320 | { "outputs", cmd_ipc_cmd }, | ||
321 | { "tree", cmd_ipc_cmd }, | ||
322 | { "workspaces", cmd_ipc_cmd }, | ||
323 | }; | 158 | }; |
324 | 159 | ||
325 | static struct cmd_handler ipc_event_handlers[] = { | 160 | /* Runtime-only commands. Keep alphabetized */ |
326 | { "*", cmd_ipc_event_cmd }, | 161 | static struct cmd_handler command_handlers[] = { |
327 | { "binding", cmd_ipc_event_cmd }, | 162 | { "exit", cmd_exit }, |
328 | { "input", cmd_ipc_event_cmd }, | 163 | { "focus", cmd_focus }, |
329 | { "mode", cmd_ipc_event_cmd }, | 164 | { "kill", cmd_kill }, |
330 | { "output", cmd_ipc_event_cmd }, | 165 | { "layout", cmd_layout }, |
331 | { "window", cmd_ipc_event_cmd }, | 166 | { "move", cmd_move }, |
332 | { "workspace", cmd_ipc_event_cmd }, | 167 | { "opacity", cmd_opacity }, |
168 | { "reload", cmd_reload }, | ||
169 | { "resize", cmd_resize }, | ||
170 | { "split", cmd_split }, | ||
171 | { "splith", cmd_splith }, | ||
172 | { "splitt", cmd_splitt }, | ||
173 | { "splitv", cmd_splitv }, | ||
333 | }; | 174 | }; |
334 | 175 | ||
335 | static int handler_compare(const void *_a, const void *_b) { | 176 | static int handler_compare(const void *_a, const void *_b) { |
@@ -338,42 +179,90 @@ static int handler_compare(const void *_a, const void *_b) { | |||
338 | return strcasecmp(a->command, b->command); | 179 | return strcasecmp(a->command, b->command); |
339 | } | 180 | } |
340 | 181 | ||
182 | // must be in order for the bsearch | ||
183 | static struct cmd_handler input_handlers[] = { | ||
184 | { "accel_profile", input_cmd_accel_profile }, | ||
185 | { "click_method", input_cmd_click_method }, | ||
186 | { "drag_lock", input_cmd_drag_lock }, | ||
187 | { "dwt", input_cmd_dwt }, | ||
188 | { "events", input_cmd_events }, | ||
189 | { "left_handed", input_cmd_left_handed }, | ||
190 | { "map_to_output", input_cmd_map_to_output }, | ||
191 | { "middle_emulation", input_cmd_middle_emulation }, | ||
192 | { "natural_scroll", input_cmd_natural_scroll }, | ||
193 | { "pointer_accel", input_cmd_pointer_accel }, | ||
194 | { "scroll_method", input_cmd_scroll_method }, | ||
195 | { "tap", input_cmd_tap }, | ||
196 | { "xkb_layout", input_cmd_xkb_layout }, | ||
197 | { "xkb_model", input_cmd_xkb_model }, | ||
198 | { "xkb_options", input_cmd_xkb_options }, | ||
199 | { "xkb_rules", input_cmd_xkb_rules }, | ||
200 | { "xkb_variant", input_cmd_xkb_variant }, | ||
201 | }; | ||
202 | |||
203 | // must be in order for the bsearch | ||
204 | static struct cmd_handler seat_handlers[] = { | ||
205 | { "attach", seat_cmd_attach }, | ||
206 | { "cursor", seat_cmd_cursor }, | ||
207 | { "fallback", seat_cmd_fallback }, | ||
208 | }; | ||
209 | |||
341 | static struct cmd_handler *find_handler(char *line, enum cmd_status block) { | 210 | static struct cmd_handler *find_handler(char *line, enum cmd_status block) { |
342 | struct cmd_handler d = { .command=line }; | 211 | struct cmd_handler d = { .command=line }; |
343 | struct cmd_handler *res = NULL; | 212 | struct cmd_handler *res = NULL; |
344 | sway_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_INPUT); | 213 | wlr_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_SEAT); |
214 | |||
215 | bool config_loading = config->reading || !config->active; | ||
216 | |||
345 | if (block == CMD_BLOCK_BAR) { | 217 | if (block == CMD_BLOCK_BAR) { |
346 | res = bsearch(&d, bar_handlers, | 218 | return bsearch(&d, bar_handlers, |
347 | sizeof(bar_handlers) / sizeof(struct cmd_handler), | 219 | sizeof(bar_handlers) / sizeof(struct cmd_handler), |
348 | sizeof(struct cmd_handler), handler_compare); | 220 | sizeof(struct cmd_handler), handler_compare); |
349 | } else if (block == CMD_BLOCK_BAR_COLORS){ | 221 | } else if (block == CMD_BLOCK_BAR_COLORS) { |
350 | res = bsearch(&d, bar_colors_handlers, | 222 | return bsearch(&d, bar_colors_handlers, |
351 | sizeof(bar_colors_handlers) / sizeof(struct cmd_handler), | 223 | sizeof(bar_colors_handlers) / sizeof(struct cmd_handler), |
352 | sizeof(struct cmd_handler), handler_compare); | 224 | sizeof(struct cmd_handler), handler_compare); |
353 | } else if (block == CMD_BLOCK_INPUT) { | 225 | } else if (block == CMD_BLOCK_INPUT) { |
354 | res = bsearch(&d, input_handlers, | 226 | return bsearch(&d, input_handlers, |
355 | sizeof(input_handlers) / sizeof(struct cmd_handler), | 227 | sizeof(input_handlers) / sizeof(struct cmd_handler), |
356 | sizeof(struct cmd_handler), handler_compare); | 228 | sizeof(struct cmd_handler), handler_compare); |
357 | } else if (block == CMD_BLOCK_IPC) { | 229 | } else if (block == CMD_BLOCK_SEAT) { |
358 | res = bsearch(&d, ipc_handlers, | 230 | return bsearch(&d, seat_handlers, |
359 | sizeof(ipc_handlers) / sizeof(struct cmd_handler), | 231 | sizeof(seat_handlers) / sizeof(struct cmd_handler), |
360 | sizeof(struct cmd_handler), handler_compare); | 232 | sizeof(struct cmd_handler), handler_compare); |
361 | } else if (block == CMD_BLOCK_IPC_EVENTS) { | 233 | } |
362 | res = bsearch(&d, ipc_event_handlers, | 234 | |
363 | sizeof(ipc_event_handlers) / sizeof(struct cmd_handler), | 235 | if (!config_loading) { |
364 | sizeof(struct cmd_handler), handler_compare); | 236 | res = bsearch(&d, command_handlers, |
365 | } else { | 237 | sizeof(command_handlers) / sizeof(struct cmd_handler), |
366 | res = bsearch(&d, handlers, | 238 | sizeof(struct cmd_handler), handler_compare); |
239 | |||
240 | if (res) { | ||
241 | return res; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | if (config->reading) { | ||
246 | res = bsearch(&d, config_handlers, | ||
247 | sizeof(config_handlers) / sizeof(struct cmd_handler), | ||
248 | sizeof(struct cmd_handler), handler_compare); | ||
249 | |||
250 | if (res) { | ||
251 | return res; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | res = bsearch(&d, handlers, | ||
367 | sizeof(handlers) / sizeof(struct cmd_handler), | 256 | sizeof(handlers) / sizeof(struct cmd_handler), |
368 | sizeof(struct cmd_handler), handler_compare); | 257 | sizeof(struct cmd_handler), handler_compare); |
369 | } | 258 | |
370 | return res; | 259 | return res; |
371 | } | 260 | } |
372 | 261 | ||
373 | struct cmd_results *handle_command(char *_exec, enum command_context context) { | 262 | struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { |
374 | // Even though this function will process multiple commands we will only | 263 | // Even though this function will process multiple commands we will only |
375 | // return the last error, if any (for now). (Since we have access to an | 264 | // return the last error, if any (for now). (Since we have access to an |
376 | // error string we could e.g. concatonate all errors there.) | 265 | // error string we could e.g. concatenate all errors there.) |
377 | struct cmd_results *results = NULL; | 266 | struct cmd_results *results = NULL; |
378 | char *exec = strdup(_exec); | 267 | char *exec = strdup(_exec); |
379 | char *head = exec; | 268 | char *head = exec; |
@@ -381,10 +270,22 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) { | |||
381 | char *cmd; | 270 | char *cmd; |
382 | list_t *containers = NULL; | 271 | list_t *containers = NULL; |
383 | 272 | ||
273 | if (seat == NULL) { | ||
274 | // passing a NULL seat means we just pick the default seat | ||
275 | seat = input_manager_get_default_seat(input_manager); | ||
276 | if (!sway_assert(seat, "could not find a seat to run the command on")) { | ||
277 | return NULL; | ||
278 | } | ||
279 | } | ||
280 | |||
281 | config->handler_context.seat = seat; | ||
282 | |||
384 | head = exec; | 283 | head = exec; |
385 | do { | 284 | do { |
386 | // Extract criteria (valid for this command list only). | 285 | // Extract criteria (valid for this command list only). |
286 | bool has_criteria = false; | ||
387 | if (*head == '[') { | 287 | if (*head == '[') { |
288 | has_criteria = true; | ||
388 | ++head; | 289 | ++head; |
389 | char *criteria_string = argsep(&head, "]"); | 290 | char *criteria_string = argsep(&head, "]"); |
390 | if (head) { | 291 | if (head) { |
@@ -393,13 +294,14 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) { | |||
393 | char *error; | 294 | char *error; |
394 | 295 | ||
395 | if ((error = extract_crit_tokens(tokens, criteria_string))) { | 296 | if ((error = extract_crit_tokens(tokens, criteria_string))) { |
297 | wlr_log(L_DEBUG, "criteria string parse error: %s", error); | ||
396 | results = cmd_results_new(CMD_INVALID, criteria_string, | 298 | results = cmd_results_new(CMD_INVALID, criteria_string, |
397 | "Can't parse criteria string: %s", error); | 299 | "Can't parse criteria string: %s", error); |
398 | free(error); | 300 | free(error); |
399 | free(tokens); | 301 | free(tokens); |
400 | goto cleanup; | 302 | goto cleanup; |
401 | } | 303 | } |
402 | containers = container_for(tokens); | 304 | containers = container_for_crit_tokens(tokens); |
403 | 305 | ||
404 | free(tokens); | 306 | free(tokens); |
405 | } else { | 307 | } else { |
@@ -419,10 +321,10 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) { | |||
419 | cmd = argsep(&cmdlist, ","); | 321 | cmd = argsep(&cmdlist, ","); |
420 | cmd += strspn(cmd, whitespace); | 322 | cmd += strspn(cmd, whitespace); |
421 | if (strcmp(cmd, "") == 0) { | 323 | if (strcmp(cmd, "") == 0) { |
422 | sway_log(L_INFO, "Ignoring empty command."); | 324 | wlr_log(L_INFO, "Ignoring empty command."); |
423 | continue; | 325 | continue; |
424 | } | 326 | } |
425 | sway_log(L_INFO, "Handling command '%s'", cmd); | 327 | wlr_log(L_INFO, "Handling command '%s'", cmd); |
426 | //TODO better handling of argv | 328 | //TODO better handling of argv |
427 | int argc; | 329 | int argc; |
428 | char **argv = split_args(cmd, &argc); | 330 | char **argv = split_args(cmd, &argc); |
@@ -443,31 +345,16 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) { | |||
443 | free_argv(argc, argv); | 345 | free_argv(argc, argv); |
444 | goto cleanup; | 346 | goto cleanup; |
445 | } | 347 | } |
446 | if (!(get_command_policy_mask(argv[0]) & context)) { | ||
447 | if (results) { | ||
448 | free_cmd_results(results); | ||
449 | } | ||
450 | results = cmd_results_new(CMD_INVALID, cmd, | ||
451 | "Permission denied for %s via %s", cmd, | ||
452 | command_policy_str(context)); | ||
453 | free_argv(argc, argv); | ||
454 | goto cleanup; | ||
455 | } | ||
456 | int i = 0; | ||
457 | do { | ||
458 | if (!containers) { | ||
459 | current_container = get_focused_container(&root_container); | ||
460 | } else if (containers->length == 0) { | ||
461 | if (results) { | ||
462 | free_cmd_results(results); | ||
463 | } | ||
464 | results = cmd_results_new(CMD_FAILURE, argv[0], "No matching container"); | ||
465 | goto cleanup; | ||
466 | } else { | ||
467 | current_container = (swayc_t *)containers->items[i]; | ||
468 | } | ||
469 | sway_log(L_INFO, "Running on container '%s'", current_container->name); | ||
470 | 348 | ||
349 | if (!has_criteria) { | ||
350 | // without criteria, the command acts upon the focused | ||
351 | // container | ||
352 | config->handler_context.current_container = | ||
353 | seat_get_focus_inactive(seat, &root_container); | ||
354 | if (!sway_assert(config->handler_context.current_container, | ||
355 | "could not get focus-inactive for root container")) { | ||
356 | return NULL; | ||
357 | } | ||
471 | struct cmd_results *res = handler->handle(argc-1, argv+1); | 358 | struct cmd_results *res = handler->handle(argc-1, argv+1); |
472 | if (res->status != CMD_SUCCESS) { | 359 | if (res->status != CMD_SUCCESS) { |
473 | free_argv(argc, argv); | 360 | free_argv(argc, argv); |
@@ -478,35 +365,39 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) { | |||
478 | goto cleanup; | 365 | goto cleanup; |
479 | } | 366 | } |
480 | free_cmd_results(res); | 367 | free_cmd_results(res); |
481 | ++i; | 368 | } else { |
482 | } while(containers && i < containers->length); | 369 | for (int i = 0; i < containers->length; ++i) { |
483 | 370 | config->handler_context.current_container = containers->items[i]; | |
371 | struct cmd_results *res = handler->handle(argc-1, argv+1); | ||
372 | if (res->status != CMD_SUCCESS) { | ||
373 | free_argv(argc, argv); | ||
374 | if (results) { | ||
375 | free_cmd_results(results); | ||
376 | } | ||
377 | results = res; | ||
378 | goto cleanup; | ||
379 | } | ||
380 | free_cmd_results(res); | ||
381 | } | ||
382 | } | ||
484 | free_argv(argc, argv); | 383 | free_argv(argc, argv); |
485 | } while(cmdlist); | 384 | } while(cmdlist); |
486 | |||
487 | if (containers) { | ||
488 | list_free(containers); | ||
489 | containers = NULL; | ||
490 | } | ||
491 | } while(head); | 385 | } while(head); |
492 | cleanup: | 386 | cleanup: |
493 | free(exec); | 387 | free(exec); |
494 | if (containers) { | ||
495 | free(containers); | ||
496 | } | ||
497 | if (!results) { | 388 | if (!results) { |
498 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); | 389 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); |
499 | } | 390 | } |
500 | return results; | 391 | return results; |
501 | } | 392 | } |
502 | 393 | ||
503 | // this is like handle_command above, except: | 394 | // this is like execute_command above, except: |
504 | // 1) it ignores empty commands (empty lines) | 395 | // 1) it ignores empty commands (empty lines) |
505 | // 2) it does variable substitution | 396 | // 2) it does variable substitution |
506 | // 3) it doesn't split commands (because the multiple commands are supposed to | 397 | // 3) it doesn't split commands (because the multiple commands are supposed to |
507 | // be chained together) | 398 | // be chained together) |
508 | // 4) handle_command handles all state internally while config_command has some | 399 | // 4) execute_command handles all state internally while config_command has |
509 | // state handled outside (notably the block mode, in read_config) | 400 | // some state handled outside (notably the block mode, in read_config) |
510 | struct cmd_results *config_command(char *exec, enum cmd_status block) { | 401 | struct cmd_results *config_command(char *exec, enum cmd_status block) { |
511 | struct cmd_results *results = NULL; | 402 | struct cmd_results *results = NULL; |
512 | int argc; | 403 | int argc; |
@@ -516,7 +407,7 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) { | |||
516 | goto cleanup; | 407 | goto cleanup; |
517 | } | 408 | } |
518 | 409 | ||
519 | sway_log(L_INFO, "handling config command '%s'", exec); | 410 | wlr_log(L_INFO, "handling config command '%s'", exec); |
520 | // Endblock | 411 | // Endblock |
521 | if (**argv == '}') { | 412 | if (**argv == '}') { |
522 | results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); | 413 | results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); |
@@ -530,12 +421,13 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) { | |||
530 | } | 421 | } |
531 | int i; | 422 | int i; |
532 | // Var replacement, for all but first argument of set | 423 | // Var replacement, for all but first argument of set |
424 | // TODO commands | ||
533 | for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { | 425 | for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { |
534 | argv[i] = do_var_replacement(argv[i]); | 426 | argv[i] = do_var_replacement(argv[i]); |
535 | unescape_string(argv[i]); | 427 | unescape_string(argv[i]); |
536 | } | 428 | } |
537 | /* Strip quotes for first argument. | 429 | // Strip quotes for first argument. |
538 | * TODO This part needs to be handled much better */ | 430 | // TODO This part needs to be handled much better |
539 | if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) { | 431 | if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) { |
540 | strip_quotes(argv[1]); | 432 | strip_quotes(argv[1]); |
541 | } | 433 | } |
@@ -619,7 +511,7 @@ struct cmd_results *config_commands_command(char *exec) { | |||
619 | } | 511 | } |
620 | policy->context = context; | 512 | policy->context = context; |
621 | 513 | ||
622 | sway_log(L_INFO, "Set command policy for %s to %d", | 514 | wlr_log(L_INFO, "Set command policy for %s to %d", |
623 | policy->command, policy->context); | 515 | policy->command, policy->context); |
624 | 516 | ||
625 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); | 517 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); |
@@ -629,10 +521,11 @@ cleanup: | |||
629 | return results; | 521 | return results; |
630 | } | 522 | } |
631 | 523 | ||
632 | struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *format, ...) { | 524 | struct cmd_results *cmd_results_new(enum cmd_status status, |
525 | const char *input, const char *format, ...) { | ||
633 | struct cmd_results *results = malloc(sizeof(struct cmd_results)); | 526 | struct cmd_results *results = malloc(sizeof(struct cmd_results)); |
634 | if (!results) { | 527 | if (!results) { |
635 | sway_log(L_ERROR, "Unable to allocate command results"); | 528 | wlr_log(L_ERROR, "Unable to allocate command results"); |
636 | return NULL; | 529 | return NULL; |
637 | } | 530 | } |
638 | results->status = status; | 531 | results->status = status; |
@@ -669,12 +562,15 @@ void free_cmd_results(struct cmd_results *results) { | |||
669 | const char *cmd_results_to_json(struct cmd_results *results) { | 562 | const char *cmd_results_to_json(struct cmd_results *results) { |
670 | json_object *result_array = json_object_new_array(); | 563 | json_object *result_array = json_object_new_array(); |
671 | json_object *root = json_object_new_object(); | 564 | json_object *root = json_object_new_object(); |
672 | json_object_object_add(root, "success", json_object_new_boolean(results->status == CMD_SUCCESS)); | 565 | json_object_object_add(root, "success", |
566 | json_object_new_boolean(results->status == CMD_SUCCESS)); | ||
673 | if (results->input) { | 567 | if (results->input) { |
674 | json_object_object_add(root, "input", json_object_new_string(results->input)); | 568 | json_object_object_add( |
569 | root, "input", json_object_new_string(results->input)); | ||
675 | } | 570 | } |
676 | if (results->error) { | 571 | if (results->error) { |
677 | json_object_object_add(root, "error", json_object_new_string(results->error)); | 572 | json_object_object_add( |
573 | root, "error", json_object_new_string(results->error)); | ||
678 | } | 574 | } |
679 | json_object_array_add(result_array, root); | 575 | json_object_array_add(result_array, root); |
680 | const char *json = json_object_to_json_string(result_array); | 576 | const char *json = json_object_to_json_string(result_array); |
@@ -682,3 +578,35 @@ const char *cmd_results_to_json(struct cmd_results *results) { | |||
682 | free(root); | 578 | free(root); |
683 | return json; | 579 | return json; |
684 | } | 580 | } |
581 | |||
582 | /** | ||
583 | * Check and add color to buffer. | ||
584 | * | ||
585 | * return error object, or NULL if color is valid. | ||
586 | */ | ||
587 | struct cmd_results *add_color(const char *name, | ||
588 | char *buffer, const char *color) { | ||
589 | int len = strlen(color); | ||
590 | if (len != 7 && len != 9) { | ||
591 | return cmd_results_new(CMD_INVALID, name, | ||
592 | "Invalid color definition %s", color); | ||
593 | } | ||
594 | if (color[0] != '#') { | ||
595 | return cmd_results_new(CMD_INVALID, name, | ||
596 | "Invalid color definition %s", color); | ||
597 | } | ||
598 | for (int i = 1; i < len; ++i) { | ||
599 | if (!isxdigit(color[i])) { | ||
600 | return cmd_results_new(CMD_INVALID, name, | ||
601 | "Invalid color definition %s", color); | ||
602 | } | ||
603 | } | ||
604 | strncpy(buffer, color, len); | ||
605 | // add default alpha channel if color was defined without it | ||
606 | if (len == 7) { | ||
607 | buffer[7] = 'f'; | ||
608 | buffer[8] = 'f'; | ||
609 | } | ||
610 | buffer[9] = '\0'; | ||
611 | return NULL; | ||
612 | } | ||
diff --git a/sway/commands/assign.c b/sway/commands/assign.c deleted file mode 100644 index c3b03bbc..00000000 --- a/sway/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/commands/bar.c b/sway/commands/bar.c index 04745a6e..ff111163 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c | |||
@@ -1,9 +1,8 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include <wlr/util/log.h> | ||
4 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
5 | #include "sway/config.h" | 5 | #include "sway/config.h" |
6 | #include "log.h" | ||
7 | #include "util.h" | 6 | #include "util.h" |
8 | 7 | ||
9 | struct cmd_results *cmd_bar(int argc, char **argv) { | 8 | struct cmd_results *cmd_bar(int argc, char **argv) { |
@@ -27,7 +26,6 @@ struct cmd_results *cmd_bar(int argc, char **argv) { | |||
27 | return bar_cmd_hidden_state(argc-1, argv + 1); | 26 | return bar_cmd_hidden_state(argc-1, argv + 1); |
28 | } | 27 | } |
29 | } | 28 | } |
30 | |||
31 | return cmd_results_new(CMD_FAILURE, "bar", "Can only be used in config file."); | 29 | return cmd_results_new(CMD_FAILURE, "bar", "Can only be used in config file."); |
32 | } | 30 | } |
33 | 31 | ||
@@ -38,15 +36,15 @@ struct cmd_results *cmd_bar(int argc, char **argv) { | |||
38 | } | 36 | } |
39 | 37 | ||
40 | // set bar id | 38 | // set bar id |
41 | int i; | 39 | for (int i = 0; i < config->bars->length; ++i) { |
42 | for (i = 0; i < config->bars->length; ++i) { | ||
43 | if (bar == config->bars->items[i]) { | 40 | if (bar == config->bars->items[i]) { |
44 | const int len = 5 + numlen(i); // "bar-" + i + \0 | 41 | const int len = 5 + numlen(i); // "bar-" + i + \0 |
45 | bar->id = malloc(len * sizeof(char)); | 42 | bar->id = malloc(len * sizeof(char)); |
46 | if (bar->id) { | 43 | if (bar->id) { |
47 | snprintf(bar->id, len, "bar-%d", i); | 44 | snprintf(bar->id, len, "bar-%d", i); |
48 | } else { | 45 | } else { |
49 | return cmd_results_new(CMD_FAILURE, "bar", "Unable to allocate bar ID"); | 46 | return cmd_results_new(CMD_FAILURE, |
47 | "bar", "Unable to allocate bar ID"); | ||
50 | } | 48 | } |
51 | break; | 49 | break; |
52 | } | 50 | } |
@@ -54,6 +52,6 @@ struct cmd_results *cmd_bar(int argc, char **argv) { | |||
54 | 52 | ||
55 | // Set current bar | 53 | // Set current bar |
56 | config->current_bar = bar; | 54 | config->current_bar = bar; |
57 | sway_log(L_DEBUG, "Configuring bar %s", bar->id); | 55 | wlr_log(L_DEBUG, "Configuring bar %s", bar->id); |
58 | return cmd_results_new(CMD_BLOCK_BAR, NULL, NULL); | 56 | return cmd_results_new(CMD_BLOCK_BAR, NULL, NULL); |
59 | } | 57 | } |
diff --git a/sway/commands/bar/activate_button.c b/sway/commands/bar/activate_button.c index 32a1d3e5..7310e7ec 100644 --- a/sway/commands/bar/activate_button.c +++ b/sway/commands/bar/activate_button.c | |||
@@ -3,24 +3,6 @@ | |||
3 | #include "log.h" | 3 | #include "log.h" |
4 | 4 | ||
5 | struct cmd_results *bar_cmd_activate_button(int argc, char **argv) { | 5 | struct cmd_results *bar_cmd_activate_button(int argc, char **argv) { |
6 | const char *cmd_name = "activate_button"; | 6 | // TODO TRAY |
7 | #ifndef ENABLE_TRAY | 7 | return cmd_results_new(CMD_INVALID, "activate_button", "TODO 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 | } | 8 | } |
diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c index 64f5b84f..3ba5f33f 100644 --- a/sway/commands/bar/binding_mode_indicator.c +++ b/sway/commands/bar/binding_mode_indicator.c | |||
@@ -5,23 +5,23 @@ | |||
5 | 5 | ||
6 | struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { | 6 | struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { |
7 | struct cmd_results *error = NULL; | 7 | struct cmd_results *error = NULL; |
8 | if ((error = checkarg(argc, "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) { | 8 | if ((error = checkarg(argc, |
9 | "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) { | ||
9 | return error; | 10 | return error; |
10 | } | 11 | } |
11 | |||
12 | if (!config->current_bar) { | 12 | if (!config->current_bar) { |
13 | return cmd_results_new(CMD_FAILURE, "binding_mode_indicator", "No bar defined."); | 13 | return cmd_results_new(CMD_FAILURE, |
14 | "binding_mode_indicator", "No bar defined."); | ||
14 | } | 15 | } |
15 | |||
16 | if (strcasecmp("yes", argv[0]) == 0) { | 16 | if (strcasecmp("yes", argv[0]) == 0) { |
17 | config->current_bar->binding_mode_indicator = true; | 17 | config->current_bar->binding_mode_indicator = true; |
18 | sway_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", config->current_bar->id); | 18 | wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", |
19 | config->current_bar->id); | ||
19 | } else if (strcasecmp("no", argv[0]) == 0) { | 20 | } else if (strcasecmp("no", argv[0]) == 0) { |
20 | config->current_bar->binding_mode_indicator = false; | 21 | config->current_bar->binding_mode_indicator = false; |
21 | sway_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", config->current_bar->id); | 22 | wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", |
22 | } else { | 23 | config->current_bar->id); |
23 | error = cmd_results_new(CMD_INVALID, "binding_mode_indicator", "Invalid value %s", argv[0]); | ||
24 | return error; | ||
25 | } | 24 | } |
26 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 25 | return cmd_results_new(CMD_INVALID, "binding_mode_indicator", |
26 | "Invalid value %s", argv[0]); | ||
27 | } | 27 | } |
diff --git a/sway/commands/bar/bindsym.c b/sway/commands/bar/bindsym.c index 5f90b51a..ac09a03f 100644 --- a/sway/commands/bar/bindsym.c +++ b/sway/commands/bar/bindsym.c | |||
@@ -7,42 +7,5 @@ | |||
7 | #include "stringop.h" | 7 | #include "stringop.h" |
8 | 8 | ||
9 | struct cmd_results *bar_cmd_bindsym(int argc, char **argv) { | 9 | struct cmd_results *bar_cmd_bindsym(int argc, char **argv) { |
10 | struct cmd_results *error = NULL; | 10 | return cmd_results_new(CMD_FAILURE, "bindsym", "TODO"); // TODO |
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 | } | 11 | } |
diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c index 8b3b0aac..17ba9b7c 100644 --- a/sway/commands/bar/colors.c +++ b/sway/commands/bar/colors.c | |||
@@ -1,47 +1,38 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
3 | 3 | ||
4 | static struct cmd_results *parse_single_color(char **color, const char *cmd_name, int argc, char **argv) { | 4 | static struct cmd_results *parse_single_color(char **color, |
5 | const char *cmd_name, int argc, char **argv) { | ||
5 | struct cmd_results *error = NULL; | 6 | struct cmd_results *error = NULL; |
6 | if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) { | 7 | if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) { |
7 | return error; | 8 | return error; |
8 | } | 9 | } |
9 | 10 | if (!*color && !(*color = malloc(10))) { | |
10 | if (!*color) { | 11 | return NULL; |
11 | *color = malloc(10); | ||
12 | if (!*color) { | ||
13 | return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color"); | ||
14 | } | ||
15 | } | 12 | } |
16 | |||
17 | error = add_color(cmd_name, *color, argv[0]); | 13 | error = add_color(cmd_name, *color, argv[0]); |
18 | if (error) { | 14 | if (error) { |
19 | return error; | 15 | return error; |
20 | } | 16 | } |
21 | |||
22 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 17 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
23 | } | 18 | } |
24 | 19 | ||
25 | static struct cmd_results *parse_three_colors(char ***colors, const char *cmd_name, int argc, char **argv) { | 20 | static struct cmd_results *parse_three_colors(char ***colors, |
21 | const char *cmd_name, int argc, char **argv) { | ||
26 | struct cmd_results *error = NULL; | 22 | struct cmd_results *error = NULL; |
27 | if (argc != 3) { | 23 | if (argc != 3) { |
28 | return cmd_results_new(CMD_INVALID, cmd_name, "Requires exactly three color values"); | 24 | return cmd_results_new(CMD_INVALID, |
25 | cmd_name, "Requires exactly three color values"); | ||
29 | } | 26 | } |
30 | 27 | for (size_t i = 0; i < 3; i++) { | |
31 | int i; | 28 | if (!*colors[i] && !(*(colors[i]) = malloc(10))) { |
32 | for (i = 0; i < 3; i++) { | 29 | return NULL; |
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 | } | 30 | } |
39 | error = add_color(cmd_name, *(colors[i]), argv[i]); | 31 | error = add_color(cmd_name, *(colors[i]), argv[i]); |
40 | if (error) { | 32 | if (error) { |
41 | return error; | 33 | return error; |
42 | } | 34 | } |
43 | } | 35 | } |
44 | |||
45 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 36 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
46 | } | 37 | } |
47 | 38 | ||
@@ -50,12 +41,10 @@ struct cmd_results *bar_cmd_colors(int argc, char **argv) { | |||
50 | if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) { | 41 | if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) { |
51 | return error; | 42 | return error; |
52 | } | 43 | } |
53 | |||
54 | if (strcmp("{", argv[0]) != 0) { | 44 | if (strcmp("{", argv[0]) != 0) { |
55 | return cmd_results_new(CMD_INVALID, "colors", | 45 | return cmd_results_new(CMD_INVALID, "colors", |
56 | "Expected '{' at the start of colors config definition."); | 46 | "Expected '{' at the start of colors config definition."); |
57 | } | 47 | } |
58 | |||
59 | return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL); | 48 | return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL); |
60 | } | 49 | } |
61 | 50 | ||
@@ -69,11 +58,13 @@ struct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) { | |||
69 | } | 58 | } |
70 | 59 | ||
71 | struct cmd_results *bar_colors_cmd_background(int argc, char **argv) { | 60 | struct cmd_results *bar_colors_cmd_background(int argc, char **argv) { |
72 | return parse_single_color(&(config->current_bar->colors.background), "background", argc, argv); | 61 | return parse_single_color(&(config->current_bar->colors.background), |
62 | "background", argc, argv); | ||
73 | } | 63 | } |
74 | 64 | ||
75 | struct cmd_results *bar_colors_cmd_focused_background(int argc, char **argv) { | 65 | 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); | 66 | return parse_single_color(&(config->current_bar->colors.focused_background), |
67 | "focused_background", argc, argv); | ||
77 | } | 68 | } |
78 | 69 | ||
79 | struct cmd_results *bar_colors_cmd_binding_mode(int argc, char **argv) { | 70 | struct cmd_results *bar_colors_cmd_binding_mode(int argc, char **argv) { |
@@ -104,19 +95,23 @@ struct cmd_results *bar_colors_cmd_inactive_workspace(int argc, char **argv) { | |||
104 | } | 95 | } |
105 | 96 | ||
106 | struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) { | 97 | struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) { |
107 | return parse_single_color(&(config->current_bar->colors.separator), "separator", argc, argv); | 98 | return parse_single_color(&(config->current_bar->colors.separator), |
99 | "separator", argc, argv); | ||
108 | } | 100 | } |
109 | 101 | ||
110 | struct cmd_results *bar_colors_cmd_focused_separator(int argc, char **argv) { | 102 | 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); | 103 | return parse_single_color(&(config->current_bar->colors.focused_separator), |
104 | "focused_separator", argc, argv); | ||
112 | } | 105 | } |
113 | 106 | ||
114 | struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) { | 107 | struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) { |
115 | return parse_single_color(&(config->current_bar->colors.statusline), "statusline", argc, argv); | 108 | return parse_single_color(&(config->current_bar->colors.statusline), |
109 | "statusline", argc, argv); | ||
116 | } | 110 | } |
117 | 111 | ||
118 | struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) { | 112 | 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); | 113 | return parse_single_color(&(config->current_bar->colors.focused_separator), |
114 | "focused_separator", argc, argv); | ||
120 | } | 115 | } |
121 | 116 | ||
122 | struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) { | 117 | struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) { |
diff --git a/sway/commands/bar/context_button.c b/sway/commands/bar/context_button.c index 6d7d7aec..3b76885a 100644 --- a/sway/commands/bar/context_button.c +++ b/sway/commands/bar/context_button.c | |||
@@ -3,24 +3,6 @@ | |||
3 | #include "log.h" | 3 | #include "log.h" |
4 | 4 | ||
5 | struct cmd_results *bar_cmd_context_button(int argc, char **argv) { | 5 | struct cmd_results *bar_cmd_context_button(int argc, char **argv) { |
6 | const char *cmd_name = "context_button"; | 6 | // TODO TRAY |
7 | #ifndef ENABLE_TRAY | 7 | return cmd_results_new(CMD_INVALID, "context_button", "TODO 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 | } | 8 | } |
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index c586c5bc..80b7a593 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
1 | #include <string.h> | 2 | #include <string.h> |
2 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
3 | #include "log.h" | 4 | #include "log.h" |
@@ -8,19 +9,13 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) { | |||
8 | if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) { | 9 | if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) { |
9 | return error; | 10 | return error; |
10 | } | 11 | } |
11 | |||
12 | if (!config->current_bar) { | 12 | if (!config->current_bar) { |
13 | return cmd_results_new(CMD_FAILURE, "font", "No bar defined."); | 13 | return cmd_results_new(CMD_FAILURE, "font", "No bar defined."); |
14 | } | 14 | } |
15 | |||
16 | char *font = join_args(argv, argc); | 15 | char *font = join_args(argv, argc); |
17 | free(config->current_bar->font); | 16 | free(config->current_bar->font); |
18 | if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) { | 17 | config->current_bar->font = strdup(font); |
19 | config->current_bar->font = font; | 18 | wlr_log(L_DEBUG, "Settings font '%s' for bar: %s", |
20 | } else { | 19 | config->current_bar->font, config->current_bar->id); |
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); | 20 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
26 | } | 21 | } |
diff --git a/sway/commands/bar/height.c b/sway/commands/bar/height.c index eb576ab3..3160caed 100644 --- a/sway/commands/bar/height.c +++ b/sway/commands/bar/height.c | |||
@@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_height(int argc, char **argv) { | |||
8 | if ((error = checkarg(argc, "height", EXPECTED_EQUAL_TO, 1))) { | 8 | if ((error = checkarg(argc, "height", EXPECTED_EQUAL_TO, 1))) { |
9 | return error; | 9 | return error; |
10 | } | 10 | } |
11 | |||
12 | int height = atoi(argv[0]); | 11 | int height = atoi(argv[0]); |
13 | if (height < 0) { | 12 | if (height < 0) { |
14 | return cmd_results_new(CMD_INVALID, "height", | 13 | return cmd_results_new(CMD_INVALID, "height", |
15 | "Invalid height value: %s", argv[0]); | 14 | "Invalid height value: %s", argv[0]); |
16 | } | 15 | } |
17 | |||
18 | config->current_bar->height = height; | 16 | config->current_bar->height = height; |
19 | sway_log(L_DEBUG, "Setting bar height to %d on bar: %s", height, config->current_bar->id); | 17 | wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s", |
18 | height, config->current_bar->id); | ||
20 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
21 | } | 20 | } |
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c index 0b49aa6b..6641f184 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c | |||
@@ -6,7 +6,8 @@ | |||
6 | #include "sway/ipc-server.h" | 6 | #include "sway/ipc-server.h" |
7 | #include "log.h" | 7 | #include "log.h" |
8 | 8 | ||
9 | static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, const char *hidden_state) { | 9 | static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, |
10 | const char *hidden_state) { | ||
10 | char *old_state = bar->hidden_state; | 11 | char *old_state = bar->hidden_state; |
11 | if (strcasecmp("toggle", hidden_state) == 0 && !config->reading) { | 12 | if (strcasecmp("toggle", hidden_state) == 0 && !config->reading) { |
12 | if (strcasecmp("hide", bar->hidden_state) == 0) { | 13 | if (strcasecmp("hide", bar->hidden_state) == 0) { |
@@ -19,16 +20,16 @@ static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, const ch | |||
19 | } else if (strcasecmp("show", hidden_state) == 0) { | 20 | } else if (strcasecmp("show", hidden_state) == 0) { |
20 | bar->hidden_state = strdup("show"); | 21 | bar->hidden_state = strdup("show"); |
21 | } else { | 22 | } else { |
22 | return cmd_results_new(CMD_INVALID, "hidden_state", "Invalid value %s", hidden_state); | 23 | return cmd_results_new(CMD_INVALID, "hidden_state", |
24 | "Invalid value %s", hidden_state); | ||
23 | } | 25 | } |
24 | |||
25 | if (strcmp(old_state, bar->hidden_state) != 0) { | 26 | if (strcmp(old_state, bar->hidden_state) != 0) { |
26 | if (!config->reading) { | 27 | if (!config->reading) { |
27 | ipc_event_barconfig_update(bar); | 28 | ipc_event_barconfig_update(bar); |
28 | } | 29 | } |
29 | sway_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", bar->hidden_state, bar->id); | 30 | wlr_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", |
31 | bar->hidden_state, bar->id); | ||
30 | } | 32 | } |
31 | |||
32 | // free old mode | 33 | // free old mode |
33 | free(old_state); | 34 | free(old_state); |
34 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 35 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
@@ -42,13 +43,12 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { | |||
42 | if ((error = checkarg(argc, "hidden_state", EXPECTED_LESS_THAN, 3))) { | 43 | if ((error = checkarg(argc, "hidden_state", EXPECTED_LESS_THAN, 3))) { |
43 | return error; | 44 | return error; |
44 | } | 45 | } |
45 | |||
46 | if (config->reading && argc > 1) { | 46 | if (config->reading && argc > 1) { |
47 | return cmd_results_new(CMD_INVALID, "hidden_state", "Unexpected value %s in config mode", argv[1]); | 47 | return cmd_results_new(CMD_INVALID, "hidden_state", |
48 | "Unexpected value %s in config mode", argv[1]); | ||
48 | } | 49 | } |
49 | 50 | ||
50 | const char *state = argv[0]; | 51 | const char *state = argv[0]; |
51 | |||
52 | if (config->reading) { | 52 | if (config->reading) { |
53 | return bar_set_hidden_state(config->current_bar, state); | 53 | return bar_set_hidden_state(config->current_bar, state); |
54 | } | 54 | } |
@@ -57,10 +57,8 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { | |||
57 | if (argc == 2) { | 57 | if (argc == 2) { |
58 | id = argv[1]; | 58 | id = argv[1]; |
59 | } | 59 | } |
60 | |||
61 | int i; | ||
62 | struct bar_config *bar; | 60 | struct bar_config *bar; |
63 | for (i = 0; i < config->bars->length; ++i) { | 61 | for (int i = 0; i < config->bars->length; ++i) { |
64 | bar = config->bars->items[i]; | 62 | bar = config->bars->items[i]; |
65 | if (id && strcmp(id, bar->id) == 0) { | 63 | if (id && strcmp(id, bar->id) == 0) { |
66 | return bar_set_hidden_state(bar, state); | 64 | return bar_set_hidden_state(bar, state); |
@@ -71,9 +69,5 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { | |||
71 | return error; | 69 | return error; |
72 | } | 70 | } |
73 | } | 71 | } |
74 | |||
75 | // active bar modifiers might have changed. | ||
76 | update_active_bar_modifiers(); | ||
77 | |||
78 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 72 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
79 | } | 73 | } |
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c index cbfc0be5..44cd3076 100644 --- a/sway/commands/bar/icon_theme.c +++ b/sway/commands/bar/icon_theme.c | |||
@@ -3,23 +3,6 @@ | |||
3 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
4 | 4 | ||
5 | struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) { | 5 | struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) { |
6 | const char *cmd_name = "tray_output"; | 6 | // TODO TRAY |
7 | #ifndef ENABLE_TRAY | 7 | return cmd_results_new(CMD_INVALID, "icon_theme", "TODO 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 | } | 8 | } |
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c index 1221ebf6..c1e56f03 100644 --- a/sway/commands/bar/id.c +++ b/sway/commands/bar/id.c | |||
@@ -11,10 +11,8 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) { | |||
11 | 11 | ||
12 | const char *name = argv[0]; | 12 | const char *name = argv[0]; |
13 | const char *oldname = config->current_bar->id; | 13 | const char *oldname = config->current_bar->id; |
14 | |||
15 | // check if id is used by a previously defined bar | 14 | // check if id is used by a previously defined bar |
16 | int i; | 15 | for (int i = 0; i < config->bars->length; ++i) { |
17 | for (i = 0; i < config->bars->length; ++i) { | ||
18 | struct bar_config *find = config->bars->items[i]; | 16 | struct bar_config *find = config->bars->items[i]; |
19 | if (strcmp(name, find->id) == 0 && config->current_bar != find) { | 17 | if (strcmp(name, find->id) == 0 && config->current_bar != find) { |
20 | return cmd_results_new(CMD_FAILURE, "id", | 18 | return cmd_results_new(CMD_FAILURE, "id", |
@@ -23,11 +21,10 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) { | |||
23 | } | 21 | } |
24 | } | 22 | } |
25 | 23 | ||
26 | sway_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name); | 24 | wlr_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name); |
27 | 25 | ||
28 | // free old bar id | 26 | // free old bar id |
29 | free(config->current_bar->id); | 27 | free(config->current_bar->id); |
30 | |||
31 | config->current_bar->id = strdup(name); | 28 | config->current_bar->id = strdup(name); |
32 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 29 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
33 | } | 30 | } |
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 36816b93..34bb0a4f 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c | |||
@@ -27,11 +27,8 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode | |||
27 | if (strcmp(old_mode, bar->mode) != 0) { | 27 | if (strcmp(old_mode, bar->mode) != 0) { |
28 | if (!config->reading) { | 28 | if (!config->reading) { |
29 | ipc_event_barconfig_update(bar); | 29 | ipc_event_barconfig_update(bar); |
30 | |||
31 | // active bar modifiers might have changed. | ||
32 | update_active_bar_modifiers(); | ||
33 | } | 30 | } |
34 | sway_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); | 31 | wlr_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); |
35 | } | 32 | } |
36 | 33 | ||
37 | // free old mode | 34 | // free old mode |
@@ -47,13 +44,12 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) { | |||
47 | if ((error = checkarg(argc, "mode", EXPECTED_LESS_THAN, 3))) { | 44 | if ((error = checkarg(argc, "mode", EXPECTED_LESS_THAN, 3))) { |
48 | return error; | 45 | return error; |
49 | } | 46 | } |
50 | |||
51 | if (config->reading && argc > 1) { | 47 | if (config->reading && argc > 1) { |
52 | return cmd_results_new(CMD_INVALID, "mode", "Unexpected value %s in config mode", argv[1]); | 48 | return cmd_results_new(CMD_INVALID, |
49 | "mode", "Unexpected value %s in config mode", argv[1]); | ||
53 | } | 50 | } |
54 | 51 | ||
55 | const char *mode = argv[0]; | 52 | const char *mode = argv[0]; |
56 | |||
57 | if (config->reading) { | 53 | if (config->reading) { |
58 | return bar_set_mode(config->current_bar, mode); | 54 | return bar_set_mode(config->current_bar, mode); |
59 | } | 55 | } |
@@ -63,19 +59,16 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) { | |||
63 | id = argv[1]; | 59 | id = argv[1]; |
64 | } | 60 | } |
65 | 61 | ||
66 | int i; | ||
67 | struct bar_config *bar; | 62 | struct bar_config *bar; |
68 | for (i = 0; i < config->bars->length; ++i) { | 63 | for (int i = 0; i < config->bars->length; ++i) { |
69 | bar = config->bars->items[i]; | 64 | bar = config->bars->items[i]; |
70 | if (id && strcmp(id, bar->id) == 0) { | 65 | if (id && strcmp(id, bar->id) == 0) { |
71 | return bar_set_mode(bar, mode); | 66 | return bar_set_mode(bar, mode); |
72 | } | 67 | } |
73 | |||
74 | error = bar_set_mode(bar, mode); | 68 | error = bar_set_mode(bar, mode); |
75 | if (error) { | 69 | if (error) { |
76 | return error; | 70 | return error; |
77 | } | 71 | } |
78 | } | 72 | } |
79 | |||
80 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 73 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
81 | } | 74 | } |
diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c index 153d87e6..7ba4b125 100644 --- a/sway/commands/bar/modifier.c +++ b/sway/commands/bar/modifier.c | |||
@@ -15,7 +15,6 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) { | |||
15 | } | 15 | } |
16 | 16 | ||
17 | uint32_t mod = 0; | 17 | uint32_t mod = 0; |
18 | |||
19 | list_t *split = split_string(argv[0], "+"); | 18 | list_t *split = split_string(argv[0], "+"); |
20 | for (int i = 0; i < split->length; ++i) { | 19 | for (int i = 0; i < split->length; ++i) { |
21 | uint32_t tmp_mod; | 20 | uint32_t tmp_mod; |
@@ -24,12 +23,13 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) { | |||
24 | continue; | 23 | continue; |
25 | } else { | 24 | } else { |
26 | free_flat_list(split); | 25 | free_flat_list(split); |
27 | return cmd_results_new(CMD_INVALID, "modifier", "Unknown modifier '%s'", split->items[i]); | 26 | return cmd_results_new(CMD_INVALID, "modifier", |
27 | "Unknown modifier '%s'", split->items[i]); | ||
28 | } | 28 | } |
29 | } | 29 | } |
30 | free_flat_list(split); | 30 | free_flat_list(split); |
31 | |||
32 | config->current_bar->modifier = mod; | 31 | config->current_bar->modifier = mod; |
33 | sway_log(L_DEBUG, "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); | 32 | wlr_log(L_DEBUG, |
33 | "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); | ||
34 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 34 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
35 | } | 35 | } |
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c index a5710bc0..f7ca0aa4 100644 --- a/sway/commands/bar/output.c +++ b/sway/commands/bar/output.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _XOPEN_SOURCE 500 | 1 | #define _XOPEN_SOURCE 500 |
2 | #include <stdbool.h> | ||
2 | #include <string.h> | 3 | #include <string.h> |
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "list.h" | 5 | #include "list.h" |
@@ -9,7 +10,6 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) { | |||
9 | if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) { | 10 | if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) { |
10 | return error; | 11 | return error; |
11 | } | 12 | } |
12 | |||
13 | if (!config->current_bar) { | 13 | if (!config->current_bar) { |
14 | return cmd_results_new(CMD_FAILURE, "output", "No bar defined."); | 14 | return cmd_results_new(CMD_FAILURE, "output", "No bar defined."); |
15 | } | 15 | } |
@@ -21,21 +21,20 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) { | |||
21 | config->current_bar->outputs = outputs; | 21 | config->current_bar->outputs = outputs; |
22 | } | 22 | } |
23 | 23 | ||
24 | int i; | 24 | bool add_output = true; |
25 | int add_output = 1; | ||
26 | if (strcmp("*", output) == 0) { | 25 | if (strcmp("*", output) == 0) { |
27 | // remove all previous defined outputs and replace with '*' | 26 | // remove all previous defined outputs and replace with '*' |
28 | for (i = 0; i < outputs->length; ++i) { | 27 | for (int i = 0; i < outputs->length; ++i) { |
29 | free(outputs->items[i]); | 28 | free(outputs->items[i]); |
30 | list_del(outputs, i); | 29 | list_del(outputs, i); |
31 | } | 30 | } |
32 | } else { | 31 | } else { |
33 | // only add output if not already defined with either the same | 32 | // only add output if not already defined with either the same |
34 | // name or as '*' | 33 | // name or as '*' |
35 | for (i = 0; i < outputs->length; ++i) { | 34 | for (int i = 0; i < outputs->length; ++i) { |
36 | const char *find = outputs->items[i]; | 35 | const char *find = outputs->items[i]; |
37 | if (strcmp("*", find) == 0 || strcmp(output, find) == 0) { | 36 | if (strcmp("*", find) == 0 || strcmp(output, find) == 0) { |
38 | add_output = 0; | 37 | add_output = false; |
39 | break; | 38 | break; |
40 | } | 39 | } |
41 | } | 40 | } |
@@ -43,8 +42,8 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) { | |||
43 | 42 | ||
44 | if (add_output) { | 43 | if (add_output) { |
45 | list_add(outputs, strdup(output)); | 44 | list_add(outputs, strdup(output)); |
46 | sway_log(L_DEBUG, "Adding bar: '%s' to output '%s'", config->current_bar->id, output); | 45 | wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'", |
46 | config->current_bar->id, output); | ||
47 | } | 47 | } |
48 | |||
49 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 48 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
50 | } | 49 | } |
diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c index f69e882f..480af724 100644 --- a/sway/commands/bar/pango_markup.c +++ b/sway/commands/bar/pango_markup.c | |||
@@ -8,19 +8,20 @@ struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) { | |||
8 | if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) { | 8 | if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) { |
9 | return error; | 9 | return error; |
10 | } | 10 | } |
11 | |||
12 | if (!config->current_bar) { | 11 | if (!config->current_bar) { |
13 | return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined."); | 12 | return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined."); |
14 | } | 13 | } |
15 | |||
16 | if (strcasecmp("enabled", argv[0]) == 0) { | 14 | if (strcasecmp("enabled", argv[0]) == 0) { |
17 | config->current_bar->pango_markup = true; | 15 | config->current_bar->pango_markup = true; |
18 | sway_log(L_DEBUG, "Enabling pango markup for bar: %s", config->current_bar->id); | 16 | wlr_log(L_DEBUG, "Enabling pango markup for bar: %s", |
17 | config->current_bar->id); | ||
19 | } else if (strcasecmp("disabled", argv[0]) == 0) { | 18 | } else if (strcasecmp("disabled", argv[0]) == 0) { |
20 | config->current_bar->pango_markup = false; | 19 | config->current_bar->pango_markup = false; |
21 | sway_log(L_DEBUG, "Disabling pango markup for bar: %s", config->current_bar->id); | 20 | wlr_log(L_DEBUG, "Disabling pango markup for bar: %s", |
21 | config->current_bar->id); | ||
22 | } else { | 22 | } else { |
23 | error = cmd_results_new(CMD_INVALID, "pango_markup", "Invalid value %s", argv[0]); | 23 | error = cmd_results_new(CMD_INVALID, "pango_markup", |
24 | "Invalid value %s", argv[0]); | ||
24 | return error; | 25 | return error; |
25 | } | 26 | } |
26 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 27 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c index 50de58e2..9c580483 100644 --- a/sway/commands/bar/position.c +++ b/sway/commands/bar/position.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
1 | #include <string.h> | 2 | #include <string.h> |
2 | #include <strings.h> | 3 | #include <strings.h> |
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
@@ -8,26 +9,18 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) { | |||
8 | if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) { | 9 | if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) { |
9 | return error; | 10 | return error; |
10 | } | 11 | } |
11 | |||
12 | if (!config->current_bar) { | 12 | if (!config->current_bar) { |
13 | return cmd_results_new(CMD_FAILURE, "position", "No bar defined."); | 13 | return cmd_results_new(CMD_FAILURE, "position", "No bar defined."); |
14 | } | 14 | } |
15 | 15 | char *valid[] = { "top", "bottom", "left", "right" }; | |
16 | if (strcasecmp("top", argv[0]) == 0) { | 16 | for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) { |
17 | config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_TOP; | 17 | if (strcasecmp(valid[i], argv[0]) == 0) { |
18 | } else if (strcasecmp("bottom", argv[0]) == 0) { | 18 | wlr_log(L_DEBUG, "Setting bar position '%s' for bar: %s", |
19 | config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM; | 19 | argv[0], config->current_bar->id); |
20 | } else if (strcasecmp("left", argv[0]) == 0) { | 20 | config->current_bar->position = strdup(argv[0]); |
21 | sway_log(L_INFO, "Warning: swaybar currently only supports top and bottom positioning. YMMV"); | 21 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
22 | config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_LEFT; | 22 | } |
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 | } | 23 | } |
30 | 24 | return cmd_results_new(CMD_INVALID, | |
31 | sway_log(L_DEBUG, "Setting bar position '%s' for bar: %s", argv[0], config->current_bar->id); | 25 | "position", "Invalid value %s", argv[0]); |
32 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
33 | } | 26 | } |
diff --git a/sway/commands/bar/secondary_button.c b/sway/commands/bar/secondary_button.c index 745045c5..449124cb 100644 --- a/sway/commands/bar/secondary_button.c +++ b/sway/commands/bar/secondary_button.c | |||
@@ -3,24 +3,6 @@ | |||
3 | #include "log.h" | 3 | #include "log.h" |
4 | 4 | ||
5 | struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) { | 5 | struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) { |
6 | const char *cmd_name = "secondary_button"; | 6 | // TODO TRAY |
7 | #ifndef ENABLE_TRAY | 7 | return cmd_results_new(CMD_INVALID, "secondary_button", "TODO 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 | } | 8 | } |
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c index 2766d8a2..1e08df6d 100644 --- a/sway/commands/bar/separator_symbol.c +++ b/sway/commands/bar/separator_symbol.c | |||
@@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) { | |||
8 | if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) { | 8 | if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) { |
9 | return error; | 9 | return error; |
10 | } | 10 | } |
11 | |||
12 | if (!config->current_bar) { | 11 | if (!config->current_bar) { |
13 | return cmd_results_new(CMD_FAILURE, "separator_symbol", "No bar defined."); | 12 | return cmd_results_new(CMD_FAILURE, |
13 | "separator_symbol", "No bar defined."); | ||
14 | } | 14 | } |
15 | |||
16 | free(config->current_bar->separator_symbol); | 15 | free(config->current_bar->separator_symbol); |
17 | config->current_bar->separator_symbol = strdup(argv[0]); | 16 | 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); | 17 | wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", |
19 | 18 | config->current_bar->separator_symbol, config->current_bar->id); | |
20 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
21 | } | 20 | } |
diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c index b227ac47..5e199cde 100644 --- a/sway/commands/bar/status_command.c +++ b/sway/commands/bar/status_command.c | |||
@@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) { | |||
8 | if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) { | 8 | if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) { |
9 | return error; | 9 | return error; |
10 | } | 10 | } |
11 | |||
12 | if (!config->current_bar) { | 11 | if (!config->current_bar) { |
13 | return cmd_results_new(CMD_FAILURE, "status_command", "No bar defined."); | 12 | return cmd_results_new(CMD_FAILURE, |
13 | "status_command", "No bar defined."); | ||
14 | } | 14 | } |
15 | |||
16 | free(config->current_bar->status_command); | 15 | free(config->current_bar->status_command); |
17 | config->current_bar->status_command = join_args(argv, argc); | 16 | 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); | 17 | wlr_log(L_DEBUG, "Feeding bar with status command: %s", |
19 | 18 | config->current_bar->status_command); | |
20 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
21 | } | 20 | } |
diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c index 9ac32482..4f24a356 100644 --- a/sway/commands/bar/strip_workspace_numbers.c +++ b/sway/commands/bar/strip_workspace_numbers.c | |||
@@ -5,23 +5,25 @@ | |||
5 | 5 | ||
6 | struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) { | 6 | struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) { |
7 | struct cmd_results *error = NULL; | 7 | struct cmd_results *error = NULL; |
8 | if ((error = checkarg(argc, "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) { | 8 | if ((error = checkarg(argc, |
9 | "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) { | ||
9 | return error; | 10 | return error; |
10 | } | 11 | } |
11 | |||
12 | if (!config->current_bar) { | 12 | if (!config->current_bar) { |
13 | return cmd_results_new(CMD_FAILURE, "strip_workspace_numbers", "No bar defined."); | 13 | return cmd_results_new(CMD_FAILURE, |
14 | "strip_workspace_numbers", "No bar defined."); | ||
14 | } | 15 | } |
15 | |||
16 | if (strcasecmp("yes", argv[0]) == 0) { | 16 | if (strcasecmp("yes", argv[0]) == 0) { |
17 | config->current_bar->strip_workspace_numbers = true; | 17 | config->current_bar->strip_workspace_numbers = true; |
18 | sway_log(L_DEBUG, "Stripping workspace numbers on bar: %s", config->current_bar->id); | 18 | wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s", |
19 | config->current_bar->id); | ||
19 | } else if (strcasecmp("no", argv[0]) == 0) { | 20 | } else if (strcasecmp("no", argv[0]) == 0) { |
20 | config->current_bar->strip_workspace_numbers = false; | 21 | config->current_bar->strip_workspace_numbers = false; |
21 | sway_log(L_DEBUG, "Enabling workspace numbers on bar: %s", config->current_bar->id); | 22 | wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s", |
23 | config->current_bar->id); | ||
22 | } else { | 24 | } else { |
23 | error = cmd_results_new(CMD_INVALID, "strip_workspace_numbers", "Invalid value %s", argv[0]); | 25 | return cmd_results_new(CMD_INVALID, |
24 | return error; | 26 | "strip_workspace_numbers", "Invalid value %s", argv[0]); |
25 | } | 27 | } |
26 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 28 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
27 | } | 29 | } |
diff --git a/sway/commands/bar/swaybar_command.c b/sway/commands/bar/swaybar_command.c index 452e2df5..520cdd11 100644 --- a/sway/commands/bar/swaybar_command.c +++ b/sway/commands/bar/swaybar_command.c | |||
@@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) { | |||
8 | if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) { | 8 | if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) { |
9 | return error; | 9 | return error; |
10 | } | 10 | } |
11 | |||
12 | if (!config->current_bar) { | 11 | if (!config->current_bar) { |
13 | return cmd_results_new(CMD_FAILURE, "swaybar_command", "No bar defined."); | 12 | return cmd_results_new(CMD_FAILURE, |
13 | "swaybar_command", "No bar defined."); | ||
14 | } | 14 | } |
15 | |||
16 | free(config->current_bar->swaybar_command); | 15 | free(config->current_bar->swaybar_command); |
17 | config->current_bar->swaybar_command = join_args(argv, argc); | 16 | config->current_bar->swaybar_command = join_args(argv, argc); |
18 | sway_log(L_DEBUG, "Using custom swaybar command: %s", config->current_bar->swaybar_command); | 17 | wlr_log(L_DEBUG, "Using custom swaybar command: %s", |
19 | 18 | config->current_bar->swaybar_command); | |
20 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
21 | } | 20 | } |
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c index 012304a9..6ab16731 100644 --- a/sway/commands/bar/tray_output.c +++ b/sway/commands/bar/tray_output.c | |||
@@ -3,27 +3,6 @@ | |||
3 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
4 | 4 | ||
5 | struct cmd_results *bar_cmd_tray_output(int argc, char **argv) { | 5 | struct cmd_results *bar_cmd_tray_output(int argc, char **argv) { |
6 | const char *cmd_name = "tray_output"; | 6 | // TODO TRAY |
7 | #ifndef ENABLE_TRAY | 7 | return cmd_results_new(CMD_INVALID, "tray_output", "TODO 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 | } | 8 | } |
diff --git a/sway/commands/bar/tray_padding.c b/sway/commands/bar/tray_padding.c index ac0572ce..91c56f19 100644 --- a/sway/commands/bar/tray_padding.c +++ b/sway/commands/bar/tray_padding.c | |||
@@ -4,31 +4,6 @@ | |||
4 | #include "log.h" | 4 | #include "log.h" |
5 | 5 | ||
6 | struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) { | 6 | struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) { |
7 | const char *cmd_name = "tray_padding"; | 7 | // TODO TRAY |
8 | #ifndef ENABLE_TRAY | 8 | return cmd_results_new(CMD_INVALID, "tray_padding", "TODO 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 | } | 9 | } |
diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c index 67dd2d31..6edc3a0d 100644 --- a/sway/commands/bar/workspace_buttons.c +++ b/sway/commands/bar/workspace_buttons.c | |||
@@ -8,20 +8,21 @@ struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) { | |||
8 | if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) { | 8 | if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) { |
9 | return error; | 9 | return error; |
10 | } | 10 | } |
11 | |||
12 | if (!config->current_bar) { | 11 | if (!config->current_bar) { |
13 | return cmd_results_new(CMD_FAILURE, "workspace_buttons", "No bar defined."); | 12 | return cmd_results_new(CMD_FAILURE, |
13 | "workspace_buttons", "No bar defined."); | ||
14 | } | 14 | } |
15 | |||
16 | if (strcasecmp("yes", argv[0]) == 0) { | 15 | if (strcasecmp("yes", argv[0]) == 0) { |
17 | config->current_bar->workspace_buttons = true; | 16 | config->current_bar->workspace_buttons = true; |
18 | sway_log(L_DEBUG, "Enabling workspace buttons on bar: %s", config->current_bar->id); | 17 | wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s", |
18 | config->current_bar->id); | ||
19 | } else if (strcasecmp("no", argv[0]) == 0) { | 19 | } else if (strcasecmp("no", argv[0]) == 0) { |
20 | config->current_bar->workspace_buttons = false; | 20 | config->current_bar->workspace_buttons = false; |
21 | sway_log(L_DEBUG, "Disabling workspace buttons on bar: %s", config->current_bar->id); | 21 | wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s", |
22 | config->current_bar->id); | ||
22 | } else { | 23 | } else { |
23 | error = cmd_results_new(CMD_INVALID, "workspace_buttons", "Invalid value %s", argv[0]); | 24 | return cmd_results_new(CMD_INVALID, "workspace_buttons", |
24 | return error; | 25 | "Invalid value %s", argv[0]); |
25 | } | 26 | } |
26 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 27 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
27 | } | 28 | } |
diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c index 4ed1f12a..7386f82c 100644 --- a/sway/commands/bar/wrap_scroll.c +++ b/sway/commands/bar/wrap_scroll.c | |||
@@ -8,20 +8,20 @@ struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) { | |||
8 | if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) { | 8 | if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) { |
9 | return error; | 9 | return error; |
10 | } | 10 | } |
11 | |||
12 | if (!config->current_bar) { | 11 | if (!config->current_bar) { |
13 | return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined."); | 12 | return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined."); |
14 | } | 13 | } |
15 | |||
16 | if (strcasecmp("yes", argv[0]) == 0) { | 14 | if (strcasecmp("yes", argv[0]) == 0) { |
17 | config->current_bar->wrap_scroll = true; | 15 | config->current_bar->wrap_scroll = true; |
18 | sway_log(L_DEBUG, "Enabling wrap scroll on bar: %s", config->current_bar->id); | 16 | wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s", |
17 | config->current_bar->id); | ||
19 | } else if (strcasecmp("no", argv[0]) == 0) { | 18 | } else if (strcasecmp("no", argv[0]) == 0) { |
20 | config->current_bar->wrap_scroll = false; | 19 | config->current_bar->wrap_scroll = false; |
21 | sway_log(L_DEBUG, "Disabling wrap scroll on bar: %s", config->current_bar->id); | 20 | wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s", |
21 | config->current_bar->id); | ||
22 | } else { | 22 | } else { |
23 | error = cmd_results_new(CMD_INVALID, "wrap_scroll", "Invalid value %s", argv[0]); | 23 | return cmd_results_new(CMD_INVALID, |
24 | return error; | 24 | "wrap_scroll", "Invalid value %s", argv[0]); |
25 | } | 25 | } |
26 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 26 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
27 | } | 27 | } |
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index d9ea37b7..cbabb07b 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c | |||
@@ -1,9 +1,13 @@ | |||
1 | #ifdef __linux__ | ||
2 | #include <linux/input-event-codes.h> | ||
3 | #elif __FreeBSD__ | ||
4 | #include <dev/evdev/input-event-codes.h> | ||
5 | #endif | ||
1 | #include <xkbcommon/xkbcommon.h> | 6 | #include <xkbcommon/xkbcommon.h> |
2 | #include <xkbcommon/xkbcommon-names.h> | 7 | #include <xkbcommon/xkbcommon-names.h> |
3 | #include <strings.h> | 8 | #include <strings.h> |
4 | #include "sway/commands.h" | 9 | #include "sway/commands.h" |
5 | #include "sway/config.h" | 10 | #include "sway/config.h" |
6 | #include "sway/input_state.h" | ||
7 | #include "list.h" | 11 | #include "list.h" |
8 | #include "log.h" | 12 | #include "log.h" |
9 | #include "stringop.h" | 13 | #include "stringop.h" |
@@ -11,13 +15,67 @@ | |||
11 | 15 | ||
12 | int binding_order = 0; | 16 | int binding_order = 0; |
13 | 17 | ||
18 | void free_sway_binding(struct sway_binding *binding) { | ||
19 | if (!binding) { | ||
20 | return; | ||
21 | } | ||
22 | |||
23 | if (binding->keys) { | ||
24 | free_flat_list(binding->keys); | ||
25 | } | ||
26 | free(binding->command); | ||
27 | free(binding); | ||
28 | } | ||
29 | |||
30 | /** | ||
31 | * Returns true if the bindings have the same key and modifier combinations. | ||
32 | * Note that keyboard layout is not considered, so the bindings might actually | ||
33 | * not be equivalent on some layouts. | ||
34 | */ | ||
35 | bool binding_key_compare(struct sway_binding *binding_a, | ||
36 | struct sway_binding *binding_b) { | ||
37 | if (binding_a->release != binding_b->release) { | ||
38 | return false; | ||
39 | } | ||
40 | |||
41 | if (binding_a->bindcode != binding_b->bindcode) { | ||
42 | return false; | ||
43 | } | ||
44 | |||
45 | if (binding_a->modifiers ^ binding_b->modifiers) { | ||
46 | return false; | ||
47 | } | ||
48 | |||
49 | if (binding_a->keys->length != binding_b->keys->length) { | ||
50 | return false; | ||
51 | } | ||
52 | |||
53 | int keys_len = binding_a->keys->length; | ||
54 | for (int i = 0; i < keys_len; ++i) { | ||
55 | uint32_t key_a = *(uint32_t*)binding_a->keys->items[i]; | ||
56 | bool found = false; | ||
57 | for (int j = 0; j < keys_len; ++j) { | ||
58 | uint32_t key_b = *(uint32_t*)binding_b->keys->items[j]; | ||
59 | if (key_b == key_a) { | ||
60 | found = true; | ||
61 | break; | ||
62 | } | ||
63 | } | ||
64 | if (!found) { | ||
65 | return false; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | return true; | ||
70 | } | ||
71 | |||
14 | struct cmd_results *cmd_bindsym(int argc, char **argv) { | 72 | struct cmd_results *cmd_bindsym(int argc, char **argv) { |
15 | struct cmd_results *error = NULL; | 73 | struct cmd_results *error = NULL; |
16 | if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) { | 74 | if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) { |
17 | return error; | 75 | return error; |
18 | } | 76 | } |
19 | 77 | ||
20 | struct sway_binding *binding = malloc(sizeof(struct sway_binding)); | 78 | struct sway_binding *binding = calloc(1, sizeof(struct sway_binding)); |
21 | if (!binding) { | 79 | if (!binding) { |
22 | return cmd_results_new(CMD_FAILURE, "bindsym", | 80 | return cmd_results_new(CMD_FAILURE, "bindsym", |
23 | "Unable to allocate binding"); | 81 | "Unable to allocate binding"); |
@@ -58,7 +116,7 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { | |||
58 | // Check for mouse binding | 116 | // Check for mouse binding |
59 | if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && | 117 | if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && |
60 | strlen(split->items[i]) == strlen("button0")) { | 118 | strlen(split->items[i]) == strlen("button0")) { |
61 | sym = ((char *)split->items[i])[strlen("button")] - '1' + M_LEFT_CLICK; | 119 | sym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT; |
62 | } | 120 | } |
63 | if (!sym) { | 121 | if (!sym) { |
64 | struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", | 122 | struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", |
@@ -67,7 +125,7 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { | |||
67 | free_flat_list(split); | 125 | free_flat_list(split); |
68 | return ret; | 126 | return ret; |
69 | } | 127 | } |
70 | xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t)); | 128 | xkb_keysym_t *key = calloc(1, sizeof(xkb_keysym_t)); |
71 | if (!key) { | 129 | if (!key) { |
72 | free_sway_binding(binding); | 130 | free_sway_binding(binding); |
73 | free_flat_list(split); | 131 | free_flat_list(split); |
@@ -78,20 +136,29 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { | |||
78 | list_add(binding->keys, key); | 136 | list_add(binding->keys, key); |
79 | } | 137 | } |
80 | free_flat_list(split); | 138 | free_flat_list(split); |
139 | binding->order = binding_order++; | ||
81 | 140 | ||
82 | struct sway_mode *mode = config->current_mode; | 141 | list_t *mode_bindings = config->current_mode->keysym_bindings; |
83 | int i = list_seq_find(mode->bindings, sway_binding_cmp_keys, binding); | 142 | |
84 | if (i > -1) { | 143 | // overwrite the binding if it already exists |
85 | sway_log(L_DEBUG, "bindsym - '%s' already exists, overwriting", argv[0]); | 144 | bool overwritten = false; |
86 | struct sway_binding *dup = mode->bindings->items[i]; | 145 | for (int i = 0; i < mode_bindings->length; ++i) { |
87 | free_sway_binding(dup); | 146 | struct sway_binding *config_binding = mode_bindings->items[i]; |
88 | list_del(mode->bindings, i); | 147 | if (binding_key_compare(binding, config_binding)) { |
148 | wlr_log(L_DEBUG, "overwriting old binding with command '%s'", | ||
149 | config_binding->command); | ||
150 | free_sway_binding(config_binding); | ||
151 | mode_bindings->items[i] = binding; | ||
152 | overwritten = true; | ||
153 | } | ||
89 | } | 154 | } |
90 | binding->order = binding_order++; | ||
91 | list_add(mode->bindings, binding); | ||
92 | list_qsort(mode->bindings, sway_binding_cmp_qsort); | ||
93 | 155 | ||
94 | sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command); | 156 | if (!overwritten) { |
157 | list_add(mode_bindings, binding); | ||
158 | } | ||
159 | |||
160 | wlr_log(L_DEBUG, "bindsym - Bound %s to command %s", | ||
161 | argv[0], binding->command); | ||
95 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 162 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
96 | } | 163 | } |
97 | 164 | ||
@@ -101,7 +168,7 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { | |||
101 | return error; | 168 | return error; |
102 | } | 169 | } |
103 | 170 | ||
104 | struct sway_binding *binding = malloc(sizeof(struct sway_binding)); | 171 | struct sway_binding *binding = calloc(1, sizeof(struct sway_binding)); |
105 | if (!binding) { | 172 | if (!binding) { |
106 | return cmd_results_new(CMD_FAILURE, "bindsym", | 173 | return cmd_results_new(CMD_FAILURE, "bindsym", |
107 | "Unable to allocate binding"); | 174 | "Unable to allocate binding"); |
@@ -138,33 +205,41 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { | |||
138 | // parse keycode | 205 | // parse keycode |
139 | xkb_keycode_t keycode = (int)strtol(split->items[i], NULL, 10); | 206 | xkb_keycode_t keycode = (int)strtol(split->items[i], NULL, 10); |
140 | if (!xkb_keycode_is_legal_ext(keycode)) { | 207 | if (!xkb_keycode_is_legal_ext(keycode)) { |
141 | error = cmd_results_new(CMD_INVALID, "bindcode", "Invalid keycode '%s'", (char *)split->items[i]); | 208 | error = |
209 | cmd_results_new(CMD_INVALID, "bindcode", | ||
210 | "Invalid keycode '%s'", (char *)split->items[i]); | ||
142 | free_sway_binding(binding); | 211 | free_sway_binding(binding); |
143 | list_free(split); | 212 | list_free(split); |
144 | return error; | 213 | return error; |
145 | } | 214 | } |
146 | xkb_keycode_t *key = malloc(sizeof(xkb_keycode_t)); | 215 | xkb_keycode_t *key = calloc(1, sizeof(xkb_keycode_t)); |
147 | *key = keycode - 8; | 216 | *key = keycode - 8; |
148 | list_add(binding->keys, key); | 217 | list_add(binding->keys, key); |
149 | } | 218 | } |
150 | free_flat_list(split); | 219 | free_flat_list(split); |
151 | 220 | ||
152 | struct sway_mode *mode = config->current_mode; | 221 | binding->order = binding_order++; |
153 | int i = list_seq_find(mode->bindings, sway_binding_cmp_keys, binding); | 222 | |
154 | if (i > -1) { | 223 | list_t *mode_bindings = config->current_mode->keycode_bindings; |
155 | struct sway_binding *dup = mode->bindings->items[i]; | 224 | |
156 | if (dup->bindcode) { | 225 | // overwrite the binding if it already exists |
157 | sway_log(L_DEBUG, "bindcode - '%s' already exists, overwriting", argv[0]); | 226 | bool overwritten = false; |
158 | } else { | 227 | for (int i = 0; i < mode_bindings->length; ++i) { |
159 | sway_log(L_DEBUG, "bindcode - '%s' already exists as bindsym, overwriting", argv[0]); | 228 | struct sway_binding *config_binding = mode_bindings->items[i]; |
229 | if (binding_key_compare(binding, config_binding)) { | ||
230 | wlr_log(L_DEBUG, "overwriting old binding with command '%s'", | ||
231 | config_binding->command); | ||
232 | free_sway_binding(config_binding); | ||
233 | mode_bindings->items[i] = binding; | ||
234 | overwritten = true; | ||
160 | } | 235 | } |
161 | free_sway_binding(dup); | ||
162 | list_del(mode->bindings, i); | ||
163 | } | 236 | } |
164 | binding->order = binding_order++; | ||
165 | list_add(mode->bindings, binding); | ||
166 | list_qsort(mode->bindings, sway_binding_cmp_qsort); | ||
167 | 237 | ||
168 | sway_log(L_DEBUG, "bindcode - Bound %s to command %s", argv[0], binding->command); | 238 | if (!overwritten) { |
239 | list_add(mode_bindings, binding); | ||
240 | } | ||
241 | |||
242 | wlr_log(L_DEBUG, "bindcode - Bound %s to command %s", | ||
243 | argv[0], binding->command); | ||
169 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 244 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
170 | } | 245 | } |
diff --git a/sway/commands/border.c b/sway/commands/border.c deleted file mode 100644 index c888622e..00000000 --- a/sway/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/commands/client.c b/sway/commands/client.c deleted file mode 100644 index f3d879cd..00000000 --- a/sway/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/commands/clipboard.c b/sway/commands/clipboard.c deleted file mode 100644 index 95514e78..00000000 --- a/sway/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/commands/commands.c b/sway/commands/commands.c deleted file mode 100644 index 0c64970c..00000000 --- a/sway/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/commands/debuglog.c b/sway/commands/debuglog.c deleted file mode 100644 index 658d6165..00000000 --- a/sway/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/commands/default_border.c b/sway/commands/default_border.c deleted file mode 100644 index 8fbe8d19..00000000 --- a/sway/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/commands/default_floating_border.c b/sway/commands/default_floating_border.c deleted file mode 100644 index fb48c1c0..00000000 --- a/sway/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/commands/orientation.c b/sway/commands/default_orientation.c index e54b60ee..a5347ce2 100644 --- a/sway/commands/orientation.c +++ b/sway/commands/default_orientation.c | |||
@@ -2,10 +2,9 @@ | |||
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
4 | 4 | ||
5 | struct cmd_results *cmd_orientation(int argc, char **argv) { | 5 | struct cmd_results *cmd_default_orientation(int argc, char **argv) { |
6 | struct cmd_results *error = NULL; | 6 | struct cmd_results *error = NULL; |
7 | if (!config->reading) return cmd_results_new(CMD_FAILURE, "orientation", "Can only be used in config file."); | 7 | if ((error = checkarg(argc, "default_orientation", EXPECTED_EQUAL_TO, 1))) { |
8 | if ((error = checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1))) { | ||
9 | return error; | 8 | return error; |
10 | } | 9 | } |
11 | if (strcasecmp(argv[0], "horizontal") == 0) { | 10 | if (strcasecmp(argv[0], "horizontal") == 0) { |
@@ -15,7 +14,8 @@ struct cmd_results *cmd_orientation(int argc, char **argv) { | |||
15 | } else if (strcasecmp(argv[0], "auto") == 0) { | 14 | } else if (strcasecmp(argv[0], "auto") == 0) { |
16 | // Do nothing | 15 | // Do nothing |
17 | } else { | 16 | } else { |
18 | return cmd_results_new(CMD_INVALID, "orientation", "Expected 'orientation <horizontal|vertical|auto>'"); | 17 | return cmd_results_new(CMD_INVALID, "default_orientation", |
18 | "Expected 'orientation <horizontal|vertical|auto>'"); | ||
19 | } | 19 | } |
20 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 20 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
21 | } | 21 | } |
diff --git a/sway/commands/exec.c b/sway/commands/exec.c index 58ef5f94..363d5bef 100644 --- a/sway/commands/exec.c +++ b/sway/commands/exec.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
3 | #include "sway/config.h" | ||
3 | #include "log.h" | 4 | #include "log.h" |
4 | #include "stringop.h" | 5 | #include "stringop.h" |
5 | 6 | ||
@@ -7,10 +8,9 @@ struct cmd_results *cmd_exec(int argc, char **argv) { | |||
7 | if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL); | 8 | if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL); |
8 | if (config->reloading) { | 9 | if (config->reloading) { |
9 | char *args = join_args(argv, argc); | 10 | char *args = join_args(argv, argc); |
10 | sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); | 11 | wlr_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); |
11 | free(args); | 12 | free(args); |
12 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 13 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
13 | } | 14 | } |
14 | return cmd_exec_always(argc, argv); | 15 | return cmd_exec_always(argc, argv); |
15 | } | 16 | } |
16 | |||
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index ab2d8622..954950e7 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c | |||
@@ -1,9 +1,13 @@ | |||
1 | #define _XOPEN_SOURCE 500 | 1 | #define _XOPEN_SOURCE 500 |
2 | #include <stdlib.h> | ||
3 | #include <stdint.h> | ||
2 | #include <string.h> | 4 | #include <string.h> |
3 | #include <sys/wait.h> | 5 | #include <sys/wait.h> |
4 | #include <unistd.h> | 6 | #include <unistd.h> |
5 | #include "sway/commands.h" | 7 | #include "sway/commands.h" |
6 | #include "sway/config.h" | 8 | #include "sway/config.h" |
9 | #include "sway/tree/container.h" | ||
10 | #include "sway/tree/workspace.h" | ||
7 | #include "log.h" | 11 | #include "log.h" |
8 | #include "stringop.h" | 12 | #include "stringop.h" |
9 | 13 | ||
@@ -16,7 +20,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { | |||
16 | 20 | ||
17 | char *tmp = NULL; | 21 | char *tmp = NULL; |
18 | if (strcmp((char*)*argv, "--no-startup-id") == 0) { | 22 | if (strcmp((char*)*argv, "--no-startup-id") == 0) { |
19 | sway_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored."); | 23 | wlr_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored."); |
20 | if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) { | 24 | if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) { |
21 | return error; | 25 | return error; |
22 | } | 26 | } |
@@ -31,11 +35,11 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { | |||
31 | strncpy(cmd, tmp, sizeof(cmd)); | 35 | strncpy(cmd, tmp, sizeof(cmd)); |
32 | cmd[sizeof(cmd) - 1] = 0; | 36 | cmd[sizeof(cmd) - 1] = 0; |
33 | free(tmp); | 37 | free(tmp); |
34 | sway_log(L_DEBUG, "Executing %s", cmd); | 38 | wlr_log(L_DEBUG, "Executing %s", cmd); |
35 | 39 | ||
36 | int fd[2]; | 40 | int fd[2]; |
37 | if (pipe(fd) != 0) { | 41 | if (pipe(fd) != 0) { |
38 | sway_log(L_ERROR, "Unable to create pipe for fork"); | 42 | wlr_log(L_ERROR, "Unable to create pipe for fork"); |
39 | } | 43 | } |
40 | 44 | ||
41 | pid_t pid; | 45 | pid_t pid; |
@@ -49,7 +53,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { | |||
49 | setsid(); | 53 | setsid(); |
50 | if ((*child = fork()) == 0) { | 54 | if ((*child = fork()) == 0) { |
51 | execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); | 55 | execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); |
52 | /* Not reached */ | 56 | // Not reached |
53 | } | 57 | } |
54 | close(fd[0]); | 58 | close(fd[0]); |
55 | ssize_t s = 0; | 59 | ssize_t s = 0; |
@@ -70,13 +74,9 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { | |||
70 | close(fd[0]); | 74 | close(fd[0]); |
71 | // cleanup child process | 75 | // cleanup child process |
72 | wait(0); | 76 | wait(0); |
73 | swayc_t *ws = swayc_active_workspace(); | 77 | if (*child > 0) { |
74 | if (*child > 0 && ws) { | 78 | wlr_log(L_DEBUG, "Child process created with pid %d", *child); |
75 | sway_log(L_DEBUG, "Child process created with pid %d for workspace %s", *child, ws->name); | 79 | // TODO: add PID to active workspace |
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 { | 80 | } else { |
81 | free(child); | 81 | free(child); |
82 | } | 82 | } |
diff --git a/sway/commands/exit.c b/sway/commands/exit.c index f192f86a..d5353c20 100644 --- a/sway/commands/exit.c +++ b/sway/commands/exit.c | |||
@@ -1,17 +1,14 @@ | |||
1 | #include <stddef.h> | ||
1 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
2 | #include "sway/container.h" | 3 | #include "sway/config.h" |
3 | 4 | ||
4 | void sway_terminate(int exit_code); | 5 | void sway_terminate(int exit_code); |
5 | 6 | ||
6 | struct cmd_results *cmd_exit(int argc, char **argv) { | 7 | struct cmd_results *cmd_exit(int argc, char **argv) { |
7 | struct cmd_results *error = NULL; | 8 | 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))) { | 9 | if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) { |
10 | return error; | 10 | return error; |
11 | } | 11 | } |
12 | // Close all views | 12 | sway_terminate(0); |
13 | close_views(&root_container); | ||
14 | sway_terminate(EXIT_SUCCESS); | ||
15 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 13 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
16 | } | 14 | } |
17 | |||
diff --git a/sway/commands/floating.c b/sway/commands/floating.c deleted file mode 100644 index ccfde532..00000000 --- a/sway/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/commands/floating_maximum_size.c b/sway/commands/floating_maximum_size.c deleted file mode 100644 index 5bca4d7c..00000000 --- a/sway/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/commands/floating_minimum_size.c b/sway/commands/floating_minimum_size.c deleted file mode 100644 index ba72454c..00000000 --- a/sway/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/commands/floating_mod.c b/sway/commands/floating_mod.c deleted file mode 100644 index b8e81ab9..00000000 --- a/sway/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/commands/floating_scroll.c b/sway/commands/floating_scroll.c deleted file mode 100644 index 8c50c5bd..00000000 --- a/sway/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/commands/focus.c b/sway/commands/focus.c index defaba29..74d9d535 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c | |||
@@ -1,99 +1,57 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | 1 | #include <strings.h> |
3 | #include <wlc/wlc.h> | 2 | #include <wlr/util/log.h> |
3 | #include "log.h" | ||
4 | #include "sway/input/input-manager.h" | ||
5 | #include "sway/input/seat.h" | ||
6 | #include "sway/tree/view.h" | ||
4 | #include "sway/commands.h" | 7 | #include "sway/commands.h" |
5 | #include "sway/container.h" | 8 | |
6 | #include "sway/focus.h" | 9 | static bool parse_movement_direction(const char *name, |
7 | #include "sway/input_state.h" | 10 | enum movement_direction *out) { |
8 | #include "sway/output.h" | 11 | if (strcasecmp(name, "left") == 0) { |
9 | #include "sway/workspace.h" | 12 | *out = MOVE_LEFT; |
13 | } else if (strcasecmp(name, "right") == 0) { | ||
14 | *out = MOVE_RIGHT; | ||
15 | } else if (strcasecmp(name, "up") == 0) { | ||
16 | *out = MOVE_UP; | ||
17 | } else if (strcasecmp(name, "down") == 0) { | ||
18 | *out = MOVE_DOWN; | ||
19 | } else if (strcasecmp(name, "parent") == 0) { | ||
20 | *out = MOVE_PARENT; | ||
21 | } else if (strcasecmp(name, "child") == 0) { | ||
22 | *out = MOVE_CHILD; | ||
23 | } else { | ||
24 | return false; | ||
25 | } | ||
26 | |||
27 | return true; | ||
28 | } | ||
10 | 29 | ||
11 | struct cmd_results *cmd_focus(int argc, char **argv) { | 30 | struct cmd_results *cmd_focus(int argc, char **argv) { |
12 | if (config->reading) return cmd_results_new(CMD_FAILURE, "focus", "Can't be used in config file."); | 31 | struct sway_container *con = config->handler_context.current_container; |
13 | struct cmd_results *error = NULL; | 32 | struct sway_seat *seat = config->handler_context.seat; |
14 | if (argc > 0 && strcasecmp(argv[0], "output") == 0) { | 33 | if (con->type < C_WORKSPACE) { |
15 | swayc_t *output = NULL; | 34 | return cmd_results_new(CMD_FAILURE, "focus", |
16 | struct wlc_point abs_pos; | 35 | "Command 'focus' cannot be used above the workspace level"); |
17 | get_absolute_center_position(get_focused_container(&root_container), &abs_pos); | 36 | } |
18 | if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 2))) { | 37 | |
19 | return error; | 38 | if (argc == 0) { |
20 | } else if (!(output = output_by_name(argv[1], &abs_pos))) { | 39 | seat_set_focus(seat, con); |
21 | return cmd_results_new(CMD_FAILURE, "focus output", | ||
22 | "Can't find output with name/at direction '%s' @ (%i,%i)", argv[1], abs_pos.x, abs_pos.y); | ||
23 | } else if (!workspace_switch(swayc_active_workspace_for(output))) { | ||
24 | return cmd_results_new(CMD_FAILURE, "focus output", | ||
25 | "Switching to workspace on output '%s' was blocked", argv[1]); | ||
26 | } else if (config->mouse_warping) { | ||
27 | swayc_t *focused = get_focused_view(output); | ||
28 | if (focused && focused->type == C_VIEW) { | ||
29 | center_pointer_on(focused); | ||
30 | } | ||
31 | } | ||
32 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
33 | } else if (argc == 0) { | ||
34 | set_focused_container(current_container); | ||
35 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 40 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
36 | } else if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) { | ||
37 | return error; | ||
38 | } | 41 | } |
39 | static int floating_toggled_index = 0; | 42 | |
40 | static int tiled_toggled_index = 0; | 43 | // TODO mode_toggle |
41 | if (strcasecmp(argv[0], "left") == 0) { | 44 | enum movement_direction direction = 0; |
42 | move_focus(MOVE_LEFT); | 45 | if (!parse_movement_direction(argv[0], &direction)) { |
43 | } else if (strcasecmp(argv[0], "right") == 0) { | ||
44 | move_focus(MOVE_RIGHT); | ||
45 | } else if (strcasecmp(argv[0], "up") == 0) { | ||
46 | move_focus(MOVE_UP); | ||
47 | } else if (strcasecmp(argv[0], "down") == 0) { | ||
48 | move_focus(MOVE_DOWN); | ||
49 | } else if (strcasecmp(argv[0], "parent") == 0) { | ||
50 | move_focus(MOVE_PARENT); | ||
51 | } else if (strcasecmp(argv[0], "child") == 0) { | ||
52 | move_focus(MOVE_CHILD); | ||
53 | } else if (strcasecmp(argv[0], "next") == 0) { | ||
54 | move_focus(MOVE_NEXT); | ||
55 | } else if (strcasecmp(argv[0], "prev") == 0) { | ||
56 | move_focus(MOVE_PREV); | ||
57 | } else if (strcasecmp(argv[0], "mode_toggle") == 0) { | ||
58 | int i; | ||
59 | swayc_t *workspace = swayc_active_workspace(); | ||
60 | swayc_t *focused = get_focused_view(workspace); | ||
61 | if (focused->is_floating) { | ||
62 | if (workspace->children->length > 0) { | ||
63 | for (i = 0;i < workspace->floating->length; i++) { | ||
64 | if (workspace->floating->items[i] == focused) { | ||
65 | floating_toggled_index = i; | ||
66 | break; | ||
67 | } | ||
68 | } | ||
69 | if (workspace->children->length > tiled_toggled_index) { | ||
70 | set_focused_container(get_focused_view(workspace->children->items[tiled_toggled_index])); | ||
71 | } else { | ||
72 | set_focused_container(get_focused_view(workspace->children->items[0])); | ||
73 | tiled_toggled_index = 0; | ||
74 | } | ||
75 | } | ||
76 | } else { | ||
77 | if (workspace->floating->length > 0) { | ||
78 | for (i = 0;i < workspace->children->length; i++) { | ||
79 | if (workspace->children->items[i] == focused) { | ||
80 | tiled_toggled_index = i; | ||
81 | break; | ||
82 | } | ||
83 | } | ||
84 | if (workspace->floating->length > floating_toggled_index) { | ||
85 | swayc_t *floating = workspace->floating->items[floating_toggled_index]; | ||
86 | set_focused_container(get_focused_view(floating)); | ||
87 | } else { | ||
88 | swayc_t *floating = workspace->floating->items[workspace->floating->length - 1]; | ||
89 | set_focused_container(get_focused_view(floating)); | ||
90 | tiled_toggled_index = workspace->floating->length - 1; | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | } else { | ||
95 | return cmd_results_new(CMD_INVALID, "focus", | 46 | return cmd_results_new(CMD_INVALID, "focus", |
96 | "Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'"); | 47 | "Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'"); |
97 | } | 48 | } |
49 | |||
50 | struct sway_container *next_focus = container_get_in_direction( | ||
51 | con, seat, direction); | ||
52 | if (next_focus) { | ||
53 | seat_set_focus(seat, next_focus); | ||
54 | } | ||
55 | |||
98 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 56 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
99 | } | 57 | } |
diff --git a/sway/commands/focus_follows_mouse.c b/sway/commands/focus_follows_mouse.c index 7c9c2b13..661e7852 100644 --- a/sway/commands/focus_follows_mouse.c +++ b/sway/commands/focus_follows_mouse.c | |||
@@ -7,7 +7,6 @@ struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) { | |||
7 | if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) { | 7 | if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) { |
8 | return error; | 8 | return error; |
9 | } | 9 | } |
10 | |||
11 | config->focus_follows_mouse = !strcasecmp(argv[0], "yes"); | 10 | config->focus_follows_mouse = !strcasecmp(argv[0], "yes"); |
12 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 11 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
13 | } | 12 | } |
diff --git a/sway/commands/font.c b/sway/commands/font.c deleted file mode 100644 index 32994f8a..00000000 --- a/sway/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/commands/for_window.c b/sway/commands/for_window.c deleted file mode 100644 index d1fd1641..00000000 --- a/sway/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/commands/force_focus_wrapping.c b/sway/commands/force_focus_wrapping.c deleted file mode 100644 index f19dd163..00000000 --- a/sway/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/commands/fullscreen.c b/sway/commands/fullscreen.c deleted file mode 100644 index bfff82f9..00000000 --- a/sway/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/commands/gaps.c b/sway/commands/gaps.c deleted file mode 100644 index 0a48592d..00000000 --- a/sway/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/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c deleted file mode 100644 index ee2a2644..00000000 --- a/sway/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/commands/input.c b/sway/commands/input.c index ad53d272..fa9cf05a 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c | |||
@@ -1,7 +1,7 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 4 | #include "sway/input/input-manager.h" |
5 | #include "log.h" | 5 | #include "log.h" |
6 | 6 | ||
7 | struct cmd_results *cmd_input(int argc, char **argv) { | 7 | struct cmd_results *cmd_input(int argc, char **argv) { |
@@ -11,45 +11,73 @@ struct cmd_results *cmd_input(int argc, char **argv) { | |||
11 | } | 11 | } |
12 | 12 | ||
13 | if (config->reading && strcmp("{", argv[1]) == 0) { | 13 | if (config->reading && strcmp("{", argv[1]) == 0) { |
14 | current_input_config = new_input_config(argv[0]); | 14 | free_input_config(config->handler_context.input_config); |
15 | sway_log(L_DEBUG, "entering input block: %s", current_input_config->identifier); | 15 | config->handler_context.input_config = new_input_config(argv[0]); |
16 | if (!config->handler_context.input_config) { | ||
17 | return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config"); | ||
18 | } | ||
19 | wlr_log(L_DEBUG, "entering input block: %s", argv[0]); | ||
16 | return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL); | 20 | return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL); |
17 | } | 21 | } |
18 | 22 | ||
19 | if (argc > 2) { | 23 | if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 3))) { |
20 | int argc_new = argc-2; | 24 | return error; |
21 | char **argv_new = argv+2; | 25 | } |
22 | 26 | ||
23 | struct cmd_results *res; | 27 | bool has_context = (config->handler_context.input_config != NULL); |
24 | current_input_config = new_input_config(argv[0]); | 28 | if (!has_context) { |
25 | if (strcasecmp("accel_profile", argv[1]) == 0) { | 29 | // caller did not give a context so create one just for this command |
26 | res = input_cmd_accel_profile(argc_new, argv_new); | 30 | config->handler_context.input_config = new_input_config(argv[0]); |
27 | } else if (strcasecmp("click_method", argv[1]) == 0) { | 31 | if (!config->handler_context.input_config) { |
28 | res = input_cmd_click_method(argc_new, argv_new); | 32 | return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config"); |
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 | } | 33 | } |
50 | current_input_config = NULL; | ||
51 | return res; | ||
52 | } | 34 | } |
53 | 35 | ||
54 | return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL); | 36 | int argc_new = argc-2; |
37 | char **argv_new = argv+2; | ||
38 | |||
39 | struct cmd_results *res; | ||
40 | if (strcasecmp("accel_profile", argv[1]) == 0) { | ||
41 | res = input_cmd_accel_profile(argc_new, argv_new); | ||
42 | } else if (strcasecmp("click_method", argv[1]) == 0) { | ||
43 | res = input_cmd_click_method(argc_new, argv_new); | ||
44 | } else if (strcasecmp("drag_lock", argv[1]) == 0) { | ||
45 | res = input_cmd_drag_lock(argc_new, argv_new); | ||
46 | } else if (strcasecmp("dwt", argv[1]) == 0) { | ||
47 | res = input_cmd_dwt(argc_new, argv_new); | ||
48 | } else if (strcasecmp("events", argv[1]) == 0) { | ||
49 | res = input_cmd_events(argc_new, argv_new); | ||
50 | } else if (strcasecmp("left_handed", argv[1]) == 0) { | ||
51 | res = input_cmd_left_handed(argc_new, argv_new); | ||
52 | } else if (strcasecmp("middle_emulation", argv[1]) == 0) { | ||
53 | res = input_cmd_middle_emulation(argc_new, argv_new); | ||
54 | } else if (strcasecmp("natural_scroll", argv[1]) == 0) { | ||
55 | res = input_cmd_natural_scroll(argc_new, argv_new); | ||
56 | } else if (strcasecmp("pointer_accel", argv[1]) == 0) { | ||
57 | res = input_cmd_pointer_accel(argc_new, argv_new); | ||
58 | } else if (strcasecmp("scroll_method", argv[1]) == 0) { | ||
59 | res = input_cmd_scroll_method(argc_new, argv_new); | ||
60 | } else if (strcasecmp("tap", argv[1]) == 0) { | ||
61 | res = input_cmd_tap(argc_new, argv_new); | ||
62 | } else if (strcasecmp("xkb_layout", argv[1]) == 0) { | ||
63 | res = input_cmd_xkb_layout(argc_new, argv_new); | ||
64 | } else if (strcasecmp("xkb_model", argv[1]) == 0) { | ||
65 | res = input_cmd_xkb_model(argc_new, argv_new); | ||
66 | } else if (strcasecmp("xkb_options", argv[1]) == 0) { | ||
67 | res = input_cmd_xkb_options(argc_new, argv_new); | ||
68 | } else if (strcasecmp("xkb_rules", argv[1]) == 0) { | ||
69 | res = input_cmd_xkb_rules(argc_new, argv_new); | ||
70 | } else if (strcasecmp("xkb_variant", argv[1]) == 0) { | ||
71 | res = input_cmd_xkb_variant(argc_new, argv_new); | ||
72 | } else { | ||
73 | res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]); | ||
74 | } | ||
75 | |||
76 | if (!has_context) { | ||
77 | // clean up the context we created earlier | ||
78 | free_input_config(config->handler_context.input_config); | ||
79 | config->handler_context.input_config = NULL; | ||
80 | } | ||
81 | |||
82 | return res; | ||
55 | } | 83 | } |
diff --git a/sway/commands/input/accel_profile.c b/sway/commands/input/accel_profile.c index 8288c1ad..37d6e133 100644 --- a/sway/commands/input/accel_profile.c +++ b/sway/commands/input/accel_profile.c | |||
@@ -1,17 +1,22 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 5 | #include "sway/input/input-manager.h" |
5 | 6 | ||
6 | struct cmd_results *input_cmd_accel_profile(int argc, char **argv) { | 7 | struct cmd_results *input_cmd_accel_profile(int argc, char **argv) { |
7 | struct cmd_results *error = NULL; | 8 | struct cmd_results *error = NULL; |
8 | if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) { | 9 | if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) { |
9 | return error; | 10 | return error; |
10 | } | 11 | } |
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
11 | if (!current_input_config) { | 14 | if (!current_input_config) { |
12 | return cmd_results_new(CMD_FAILURE, "accel_profile", "No input device defined."); | 15 | return cmd_results_new(CMD_FAILURE, "accel_profile", |
16 | "No input device defined."); | ||
13 | } | 17 | } |
14 | struct input_config *new_config = new_input_config(current_input_config->identifier); | 18 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | ||
15 | 20 | ||
16 | if (strcasecmp(argv[0], "adaptive") == 0) { | 21 | if (strcasecmp(argv[0], "adaptive") == 0) { |
17 | new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; | 22 | new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; |
@@ -22,6 +27,6 @@ struct cmd_results *input_cmd_accel_profile(int argc, char **argv) { | |||
22 | "Expected 'accel_profile <adaptive|flat>'"); | 27 | "Expected 'accel_profile <adaptive|flat>'"); |
23 | } | 28 | } |
24 | 29 | ||
25 | input_cmd_apply(new_config); | 30 | apply_input_config(new_config); |
26 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 31 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
27 | } | 32 | } |
diff --git a/sway/commands/input/click_method.c b/sway/commands/input/click_method.c index 5e9d3dcb..8f1f0aa7 100644 --- a/sway/commands/input/click_method.c +++ b/sway/commands/input/click_method.c | |||
@@ -1,19 +1,23 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 4 | #include "sway/config.h" |
5 | #include "sway/input/input-manager.h" | ||
5 | #include "log.h" | 6 | #include "log.h" |
6 | 7 | ||
7 | struct cmd_results *input_cmd_click_method(int argc, char **argv) { | 8 | 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; | 9 | struct cmd_results *error = NULL; |
10 | if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) { | 10 | if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) { |
11 | return error; | 11 | return error; |
12 | } | 12 | } |
13 | struct input_config *current_input_config = | ||
14 | config->handler_context.input_config; | ||
13 | if (!current_input_config) { | 15 | if (!current_input_config) { |
14 | return cmd_results_new(CMD_FAILURE, "click_method", "No input device defined."); | 16 | return cmd_results_new(CMD_FAILURE, "click_method", |
17 | "No input device defined."); | ||
15 | } | 18 | } |
16 | struct input_config *new_config = new_input_config(current_input_config->identifier); | 19 | struct input_config *new_config = |
20 | new_input_config(current_input_config->identifier); | ||
17 | 21 | ||
18 | if (strcasecmp(argv[0], "none") == 0) { | 22 | if (strcasecmp(argv[0], "none") == 0) { |
19 | new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE; | 23 | new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE; |
@@ -22,9 +26,10 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) { | |||
22 | } else if (strcasecmp(argv[0], "clickfinger") == 0) { | 26 | } else if (strcasecmp(argv[0], "clickfinger") == 0) { |
23 | new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; | 27 | new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; |
24 | } else { | 28 | } else { |
25 | return cmd_results_new(CMD_INVALID, "click_method", "Expected 'click_method <none|button_areas|clickfinger'"); | 29 | return cmd_results_new(CMD_INVALID, "click_method", |
30 | "Expected 'click_method <none|button_areas|clickfinger'"); | ||
26 | } | 31 | } |
27 | 32 | ||
28 | input_cmd_apply(new_config); | 33 | apply_input_config(new_config); |
29 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 34 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
30 | } | 35 | } |
diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c index f5a7beb4..8273a7d4 100644 --- a/sway/commands/input/drag_lock.c +++ b/sway/commands/input/drag_lock.c | |||
@@ -1,26 +1,32 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 5 | #include "sway/input/input-manager.h" |
5 | 6 | ||
6 | struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { | 7 | struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { |
7 | struct cmd_results *error = NULL; | 8 | struct cmd_results *error = NULL; |
8 | if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) { | 9 | if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) { |
9 | return error; | 10 | return error; |
10 | } | 11 | } |
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
11 | if (!current_input_config) { | 14 | if (!current_input_config) { |
12 | return cmd_results_new(CMD_FAILURE, "drag_lock", "No input device defined."); | 15 | return cmd_results_new(CMD_FAILURE, |
16 | "drag_lock", "No input device defined."); | ||
13 | } | 17 | } |
14 | struct input_config *new_config = new_input_config(current_input_config->identifier); | 18 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | ||
15 | 20 | ||
16 | if (strcasecmp(argv[0], "enabled") == 0) { | 21 | if (strcasecmp(argv[0], "enabled") == 0) { |
17 | new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; | 22 | new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; |
18 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 23 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
19 | new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; | 24 | new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; |
20 | } else { | 25 | } else { |
21 | return cmd_results_new(CMD_INVALID, "drag_lock", "Expected 'drag_lock <enabled|disabled>'"); | 26 | return cmd_results_new(CMD_INVALID, "drag_lock", |
27 | "Expected 'drag_lock <enabled|disabled>'"); | ||
22 | } | 28 | } |
23 | 29 | ||
24 | input_cmd_apply(new_config); | 30 | apply_input_config(new_config); |
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 31 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
26 | } | 32 | } |
diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c index 557b2207..995a2f47 100644 --- a/sway/commands/input/dwt.c +++ b/sway/commands/input/dwt.c | |||
@@ -1,26 +1,31 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 5 | #include "sway/input/input-manager.h" |
5 | 6 | ||
6 | struct cmd_results *input_cmd_dwt(int argc, char **argv) { | 7 | struct cmd_results *input_cmd_dwt(int argc, char **argv) { |
7 | struct cmd_results *error = NULL; | 8 | struct cmd_results *error = NULL; |
8 | if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) { | 9 | if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) { |
9 | return error; | 10 | return error; |
10 | } | 11 | } |
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
11 | if (!current_input_config) { | 14 | if (!current_input_config) { |
12 | return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined."); | 15 | return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined."); |
13 | } | 16 | } |
14 | struct input_config *new_config = new_input_config(current_input_config->identifier); | 17 | struct input_config *new_config = |
18 | new_input_config(current_input_config->identifier); | ||
15 | 19 | ||
16 | if (strcasecmp(argv[0], "enabled") == 0) { | 20 | if (strcasecmp(argv[0], "enabled") == 0) { |
17 | new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED; | 21 | new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED; |
18 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 22 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
19 | new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; | 23 | new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; |
20 | } else { | 24 | } else { |
21 | return cmd_results_new(CMD_INVALID, "dwt", "Expected 'dwt <enabled|disabled>'"); | 25 | return cmd_results_new(CMD_INVALID, "dwt", |
26 | "Expected 'dwt <enabled|disabled>'"); | ||
22 | } | 27 | } |
23 | 28 | ||
24 | input_cmd_apply(new_config); | 29 | apply_input_config(new_config); |
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 30 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
26 | } | 31 | } |
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 9d54287a..2217f5ce 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c | |||
@@ -1,30 +1,38 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 5 | #include "sway/input/input-manager.h" |
5 | #include "log.h" | 6 | #include "log.h" |
6 | 7 | ||
7 | struct cmd_results *input_cmd_events(int argc, char **argv) { | 8 | 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; | 9 | struct cmd_results *error = NULL; |
10 | if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) { | 10 | if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) { |
11 | return error; | 11 | return error; |
12 | } | 12 | } |
13 | struct input_config *current_input_config = | ||
14 | config->handler_context.input_config; | ||
13 | if (!current_input_config) { | 15 | if (!current_input_config) { |
14 | return cmd_results_new(CMD_FAILURE, "events", "No input device defined."); | 16 | return cmd_results_new(CMD_FAILURE, "events", |
17 | "No input device defined."); | ||
15 | } | 18 | } |
16 | struct input_config *new_config = new_input_config(current_input_config->identifier); | 19 | wlr_log(L_DEBUG, "events for device: %s", |
20 | current_input_config->identifier); | ||
21 | struct input_config *new_config = | ||
22 | new_input_config(current_input_config->identifier); | ||
17 | 23 | ||
18 | if (strcasecmp(argv[0], "enabled") == 0) { | 24 | if (strcasecmp(argv[0], "enabled") == 0) { |
19 | new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; | 25 | new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; |
20 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 26 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
21 | new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; | 27 | new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; |
22 | } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) { | 28 | } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) { |
23 | new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; | 29 | new_config->send_events = |
30 | LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; | ||
24 | } else { | 31 | } else { |
25 | return cmd_results_new(CMD_INVALID, "events", "Expected 'events <enabled|disabled|disabled_on_external_mouse>'"); | 32 | return cmd_results_new(CMD_INVALID, "events", |
33 | "Expected 'events <enabled|disabled|disabled_on_external_mouse>'"); | ||
26 | } | 34 | } |
27 | 35 | ||
28 | input_cmd_apply(new_config); | 36 | apply_input_config(new_config); |
29 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 37 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
30 | } | 38 | } |
diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c index 6c913e70..94b8e03e 100644 --- a/sway/commands/input/left_handed.c +++ b/sway/commands/input/left_handed.c | |||
@@ -1,26 +1,32 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 5 | #include "sway/input/input-manager.h" |
5 | 6 | ||
6 | struct cmd_results *input_cmd_left_handed(int argc, char **argv) { | 7 | struct cmd_results *input_cmd_left_handed(int argc, char **argv) { |
7 | struct cmd_results *error = NULL; | 8 | struct cmd_results *error = NULL; |
8 | if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) { | 9 | if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) { |
9 | return error; | 10 | return error; |
10 | } | 11 | } |
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
11 | if (!current_input_config) { | 14 | if (!current_input_config) { |
12 | return cmd_results_new(CMD_FAILURE, "left_handed", "No input device defined."); | 15 | return cmd_results_new(CMD_FAILURE, "left_handed", |
16 | "No input device defined."); | ||
13 | } | 17 | } |
14 | struct input_config *new_config = new_input_config(current_input_config->identifier); | 18 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | ||
15 | 20 | ||
16 | if (strcasecmp(argv[0], "enabled") == 0) { | 21 | if (strcasecmp(argv[0], "enabled") == 0) { |
17 | new_config->left_handed = 1; | 22 | new_config->left_handed = 1; |
18 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 23 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
19 | new_config->left_handed = 0; | 24 | new_config->left_handed = 0; |
20 | } else { | 25 | } else { |
21 | return cmd_results_new(CMD_INVALID, "left_handed", "Expected 'left_handed <enabled|disabled>'"); | 26 | return cmd_results_new(CMD_INVALID, "left_handed", |
27 | "Expected 'left_handed <enabled|disabled>'"); | ||
22 | } | 28 | } |
23 | 29 | ||
24 | input_cmd_apply(new_config); | 30 | apply_input_config(new_config); |
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 31 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
26 | } | 32 | } |
diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c new file mode 100644 index 00000000..60e4608e --- /dev/null +++ b/sway/commands/input/map_to_output.c | |||
@@ -0,0 +1,27 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | ||
3 | #include <strings.h> | ||
4 | #include "sway/config.h" | ||
5 | #include "sway/commands.h" | ||
6 | #include "sway/input/input-manager.h" | ||
7 | #include "log.h" | ||
8 | |||
9 | struct cmd_results *input_cmd_map_to_output(int argc, char **argv) { | ||
10 | struct cmd_results *error = NULL; | ||
11 | if ((error = checkarg(argc, "map_to_output", EXPECTED_EQUAL_TO, 1))) { | ||
12 | return error; | ||
13 | } | ||
14 | struct input_config *current_input_config = | ||
15 | config->handler_context.input_config; | ||
16 | if (!current_input_config) { | ||
17 | return cmd_results_new(CMD_FAILURE, "map_to_output", | ||
18 | "No input device defined."); | ||
19 | } | ||
20 | struct input_config *new_config = | ||
21 | new_input_config(current_input_config->identifier); | ||
22 | |||
23 | new_config->mapped_output = strdup(argv[0]); | ||
24 | apply_input_config(new_config); | ||
25 | |||
26 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
27 | } | ||
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c index 33cdd7d6..a551fd51 100644 --- a/sway/commands/input/middle_emulation.c +++ b/sway/commands/input/middle_emulation.c | |||
@@ -1,26 +1,33 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 5 | #include "sway/input/input-manager.h" |
5 | 6 | ||
6 | struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { | 7 | struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { |
7 | struct cmd_results *error = NULL; | 8 | struct cmd_results *error = NULL; |
8 | if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) { | 9 | if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) { |
9 | return error; | 10 | return error; |
10 | } | 11 | } |
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
11 | if (!current_input_config) { | 14 | if (!current_input_config) { |
12 | return cmd_results_new(CMD_FAILURE, "middle_emulation", "No input device defined."); | 15 | return cmd_results_new(CMD_FAILURE, "middle_emulation", |
16 | "No input device defined."); | ||
13 | } | 17 | } |
14 | struct input_config *new_config = new_input_config(current_input_config->identifier); | 18 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | ||
15 | 20 | ||
16 | if (strcasecmp(argv[0], "enabled") == 0) { | 21 | if (strcasecmp(argv[0], "enabled") == 0) { |
17 | new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; | 22 | new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; |
18 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 23 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
19 | new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; | 24 | new_config->middle_emulation = |
25 | LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; | ||
20 | } else { | 26 | } else { |
21 | return cmd_results_new(CMD_INVALID, "middle_emulation", "Expected 'middle_emulation <enabled|disabled>'"); | 27 | return cmd_results_new(CMD_INVALID, "middle_emulation", |
28 | "Expected 'middle_emulation <enabled|disabled>'"); | ||
22 | } | 29 | } |
23 | 30 | ||
24 | input_cmd_apply(new_config); | 31 | apply_input_config(new_config); |
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 32 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
26 | } | 33 | } |
diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c index 7bc8b8d0..c4e19b78 100644 --- a/sway/commands/input/natural_scroll.c +++ b/sway/commands/input/natural_scroll.c | |||
@@ -1,26 +1,32 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 5 | #include "sway/input/input-manager.h" |
5 | 6 | ||
6 | struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { | 7 | struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { |
7 | struct cmd_results *error = NULL; | 8 | struct cmd_results *error = NULL; |
8 | if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) { | 9 | if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) { |
9 | return error; | 10 | return error; |
10 | } | 11 | } |
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
11 | if (!current_input_config) { | 14 | if (!current_input_config) { |
12 | return cmd_results_new(CMD_FAILURE, "natural_scoll", "No input device defined."); | 15 | return cmd_results_new(CMD_FAILURE, "natural_scoll", |
16 | "No input device defined."); | ||
13 | } | 17 | } |
14 | struct input_config *new_config = new_input_config(current_input_config->identifier); | 18 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | ||
15 | 20 | ||
16 | if (strcasecmp(argv[0], "enabled") == 0) { | 21 | if (strcasecmp(argv[0], "enabled") == 0) { |
17 | new_config->natural_scroll = 1; | 22 | new_config->natural_scroll = 1; |
18 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 23 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
19 | new_config->natural_scroll = 0; | 24 | new_config->natural_scroll = 0; |
20 | } else { | 25 | } else { |
21 | return cmd_results_new(CMD_INVALID, "natural_scroll", "Expected 'natural_scroll <enabled|disabled>'"); | 26 | return cmd_results_new(CMD_INVALID, "natural_scroll", |
27 | "Expected 'natural_scroll <enabled|disabled>'"); | ||
22 | } | 28 | } |
23 | 29 | ||
24 | input_cmd_apply(new_config); | 30 | apply_input_config(new_config); |
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 31 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
26 | } | 32 | } |
diff --git a/sway/commands/input/pointer_accel.c b/sway/commands/input/pointer_accel.c index 94f595d1..171063aa 100644 --- a/sway/commands/input/pointer_accel.c +++ b/sway/commands/input/pointer_accel.c | |||
@@ -1,24 +1,30 @@ | |||
1 | #include <stdlib.h> | 1 | #include <stdlib.h> |
2 | #include <string.h> | 2 | #include <string.h> |
3 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 5 | #include "sway/input/input-manager.h" |
5 | 6 | ||
6 | struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) { | 7 | struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) { |
7 | struct cmd_results *error = NULL; | 8 | struct cmd_results *error = NULL; |
8 | if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) { | 9 | if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) { |
9 | return error; | 10 | return error; |
10 | } | 11 | } |
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
11 | if (!current_input_config) { | 14 | if (!current_input_config) { |
12 | return cmd_results_new(CMD_FAILURE, "pointer_accel", "No input device defined."); | 15 | return cmd_results_new(CMD_FAILURE, |
16 | "pointer_accel", "No input device defined."); | ||
13 | } | 17 | } |
14 | struct input_config *new_config = new_input_config(current_input_config->identifier); | 18 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | ||
15 | 20 | ||
16 | float pointer_accel = atof(argv[0]); | 21 | float pointer_accel = atof(argv[0]); |
17 | if (pointer_accel < -1 || pointer_accel > 1) { | 22 | if (pointer_accel < -1 || pointer_accel > 1) { |
18 | return cmd_results_new(CMD_INVALID, "pointer_accel", "Input out of range [-1, 1]"); | 23 | return cmd_results_new(CMD_INVALID, "pointer_accel", |
24 | "Input out of range [-1, 1]"); | ||
19 | } | 25 | } |
20 | new_config->pointer_accel = pointer_accel; | 26 | new_config->pointer_accel = pointer_accel; |
21 | 27 | ||
22 | input_cmd_apply(new_config); | 28 | apply_input_config(new_config); |
23 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 29 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
24 | } | 30 | } |
diff --git a/sway/commands/input/scroll_method.c b/sway/commands/input/scroll_method.c index 5c6c3d7a..0a1c57ac 100644 --- a/sway/commands/input/scroll_method.c +++ b/sway/commands/input/scroll_method.c | |||
@@ -1,17 +1,22 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 5 | #include "sway/input/input-manager.h" |
5 | 6 | ||
6 | struct cmd_results *input_cmd_scroll_method(int argc, char **argv) { | 7 | struct cmd_results *input_cmd_scroll_method(int argc, char **argv) { |
7 | struct cmd_results *error = NULL; | 8 | struct cmd_results *error = NULL; |
8 | if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) { | 9 | if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) { |
9 | return error; | 10 | return error; |
10 | } | 11 | } |
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
11 | if (!current_input_config) { | 14 | if (!current_input_config) { |
12 | return cmd_results_new(CMD_FAILURE, "scroll_method", "No input device defined."); | 15 | return cmd_results_new(CMD_FAILURE, "scroll_method", |
16 | "No input device defined."); | ||
13 | } | 17 | } |
14 | struct input_config *new_config = new_input_config(current_input_config->identifier); | 18 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | ||
15 | 20 | ||
16 | if (strcasecmp(argv[0], "none") == 0) { | 21 | if (strcasecmp(argv[0], "none") == 0) { |
17 | new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; | 22 | new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; |
@@ -22,9 +27,10 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) { | |||
22 | } else if (strcasecmp(argv[0], "on_button_down") == 0) { | 27 | } else if (strcasecmp(argv[0], "on_button_down") == 0) { |
23 | new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; | 28 | new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; |
24 | } else { | 29 | } else { |
25 | return cmd_results_new(CMD_INVALID, "scroll_method", "Expected 'scroll_method <none|two_finger|edge|on_button_down>'"); | 30 | return cmd_results_new(CMD_INVALID, "scroll_method", |
31 | "Expected 'scroll_method <none|two_finger|edge|on_button_down>'"); | ||
26 | } | 32 | } |
27 | 33 | ||
28 | input_cmd_apply(new_config); | 34 | apply_input_config(new_config); |
29 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 35 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
30 | } | 36 | } |
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c index 9e3ca2af..e7f03058 100644 --- a/sway/commands/input/tap.c +++ b/sway/commands/input/tap.c | |||
@@ -1,29 +1,34 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/input.h" | 5 | #include "sway/input/input-manager.h" |
5 | #include "log.h" | 6 | #include "log.h" |
6 | 7 | ||
7 | struct cmd_results *input_cmd_tap(int argc, char **argv) { | 8 | 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; | 9 | struct cmd_results *error = NULL; |
10 | if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) { | 10 | if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) { |
11 | return error; | 11 | return error; |
12 | } | 12 | } |
13 | struct input_config *current_input_config = | ||
14 | config->handler_context.input_config; | ||
13 | if (!current_input_config) { | 15 | if (!current_input_config) { |
14 | return cmd_results_new(CMD_FAILURE, "tap", "No input device defined."); | 16 | return cmd_results_new(CMD_FAILURE, "tap", "No input device defined."); |
15 | } | 17 | } |
16 | struct input_config *new_config = new_input_config(current_input_config->identifier); | 18 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | ||
17 | 20 | ||
18 | if (strcasecmp(argv[0], "enabled") == 0) { | 21 | if (strcasecmp(argv[0], "enabled") == 0) { |
19 | new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED; | 22 | new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED; |
20 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 23 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
21 | new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; | 24 | new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; |
22 | } else { | 25 | } else { |
23 | return cmd_results_new(CMD_INVALID, "tap", "Expected 'tap <enabled|disabled>'"); | 26 | return cmd_results_new(CMD_INVALID, "tap", |
27 | "Expected 'tap <enabled|disabled>'"); | ||
24 | } | 28 | } |
25 | 29 | ||
26 | sway_log(L_DEBUG, "apply-tap for device: %s", current_input_config->identifier); | 30 | wlr_log(L_DEBUG, "apply-tap for device: %s", |
27 | input_cmd_apply(new_config); | 31 | current_input_config->identifier); |
32 | apply_input_config(new_config); | ||
28 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 33 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
29 | } | 34 | } |
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c new file mode 100644 index 00000000..867e65d3 --- /dev/null +++ b/sway/commands/input/xkb_layout.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/input/input-manager.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "xkb_layout", EXPECTED_EQUAL_TO, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
14 | if (!current_input_config) { | ||
15 | return cmd_results_new(CMD_FAILURE, "xkb_layout", "No input device defined."); | ||
16 | } | ||
17 | struct input_config *new_config = | ||
18 | new_input_config(current_input_config->identifier); | ||
19 | |||
20 | new_config->xkb_layout = strdup(argv[0]); | ||
21 | |||
22 | wlr_log(L_DEBUG, "apply-xkb_layout for device: %s layout: %s", | ||
23 | current_input_config->identifier, new_config->xkb_layout); | ||
24 | apply_input_config(new_config); | ||
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
26 | } | ||
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c new file mode 100644 index 00000000..e8c8e04e --- /dev/null +++ b/sway/commands/input/xkb_model.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/input/input-manager.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | struct cmd_results *input_cmd_xkb_model(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "xkb_model", EXPECTED_EQUAL_TO, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
14 | if (!current_input_config) { | ||
15 | return cmd_results_new(CMD_FAILURE, "xkb_model", "No input device defined."); | ||
16 | } | ||
17 | struct input_config *new_config = | ||
18 | new_input_config(current_input_config->identifier); | ||
19 | |||
20 | new_config->xkb_model = strdup(argv[0]); | ||
21 | |||
22 | wlr_log(L_DEBUG, "apply-xkb_model for device: %s model: %s", | ||
23 | current_input_config->identifier, new_config->xkb_model); | ||
24 | apply_input_config(new_config); | ||
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
26 | } | ||
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c new file mode 100644 index 00000000..e9ddd6e3 --- /dev/null +++ b/sway/commands/input/xkb_options.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/input/input-manager.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | struct cmd_results *input_cmd_xkb_options(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "xkb_options", EXPECTED_EQUAL_TO, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
14 | if (!current_input_config) { | ||
15 | return cmd_results_new(CMD_FAILURE, "xkb_options", "No input device defined."); | ||
16 | } | ||
17 | struct input_config *new_config = | ||
18 | new_input_config(current_input_config->identifier); | ||
19 | |||
20 | new_config->xkb_options = strdup(argv[0]); | ||
21 | |||
22 | wlr_log(L_DEBUG, "apply-xkb_options for device: %s options: %s", | ||
23 | current_input_config->identifier, new_config->xkb_options); | ||
24 | apply_input_config(new_config); | ||
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
26 | } | ||
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c new file mode 100644 index 00000000..926d0ac1 --- /dev/null +++ b/sway/commands/input/xkb_rules.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/input/input-manager.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "xkb_rules", EXPECTED_EQUAL_TO, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
14 | if (!current_input_config) { | ||
15 | return cmd_results_new(CMD_FAILURE, "xkb_rules", "No input device defined."); | ||
16 | } | ||
17 | struct input_config *new_config = | ||
18 | new_input_config(current_input_config->identifier); | ||
19 | |||
20 | new_config->xkb_rules = strdup(argv[0]); | ||
21 | |||
22 | wlr_log(L_DEBUG, "apply-xkb_rules for device: %s rules: %s", | ||
23 | current_input_config->identifier, new_config->xkb_rules); | ||
24 | apply_input_config(new_config); | ||
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
26 | } | ||
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c new file mode 100644 index 00000000..0e3ffd41 --- /dev/null +++ b/sway/commands/input/xkb_variant.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include "sway/config.h" | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/input/input-manager.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "xkb_variant", EXPECTED_EQUAL_TO, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
14 | if (!current_input_config) { | ||
15 | return cmd_results_new(CMD_FAILURE, "xkb_variant", "No input device defined."); | ||
16 | } | ||
17 | struct input_config *new_config = | ||
18 | new_input_config(current_input_config->identifier); | ||
19 | |||
20 | new_config->xkb_variant = strdup(argv[0]); | ||
21 | |||
22 | wlr_log(L_DEBUG, "apply-xkb_variant for device: %s variant: %s", | ||
23 | current_input_config->identifier, new_config->xkb_variant); | ||
24 | apply_input_config(new_config); | ||
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
26 | } | ||
diff --git a/sway/commands/ipc.c b/sway/commands/ipc.c deleted file mode 100644 index 0c678961..00000000 --- a/sway/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/commands/kill.c b/sway/commands/kill.c index 742e2b86..f3fa52f1 100644 --- a/sway/commands/kill.c +++ b/sway/commands/kill.c | |||
@@ -1,12 +1,16 @@ | |||
1 | #include <wlr/util/log.h> | ||
2 | #include "log.h" | ||
3 | #include "sway/input/input-manager.h" | ||
4 | #include "sway/input/seat.h" | ||
5 | #include "sway/tree/view.h" | ||
6 | #include "sway/tree/container.h" | ||
1 | #include "sway/commands.h" | 7 | #include "sway/commands.h" |
2 | #include "sway/container.h" | ||
3 | #include "sway/focus.h" | ||
4 | 8 | ||
5 | struct cmd_results *cmd_kill(int argc, char **argv) { | 9 | 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."); | 10 | struct sway_container *con = |
7 | if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running."); | 11 | config->handler_context.current_container; |
12 | |||
13 | container_close(con); | ||
8 | 14 | ||
9 | swayc_t *container = current_container; | ||
10 | close_views(container); | ||
11 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 15 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
12 | } | 16 | } |
diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 57a86565..4c49a627 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c | |||
@@ -1,196 +1,55 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
4 | #include "sway/container.h" | 4 | #include "sway/tree/container.h" |
5 | #include "sway/layout.h" | 5 | #include "sway/tree/layout.h" |
6 | 6 | #include "log.h" | |
7 | /** | ||
8 | * handle "layout auto" command group | ||
9 | */ | ||
10 | static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv); | ||
11 | 7 | ||
12 | struct cmd_results *cmd_layout(int argc, char **argv) { | 8 | struct cmd_results *cmd_layout(int argc, char **argv) { |
13 | struct cmd_results *error = NULL; | 9 | 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))) { | 10 | if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { |
17 | return error; | 11 | return error; |
18 | } | 12 | } |
19 | swayc_t *parent = current_container; | 13 | struct sway_container *parent = config->handler_context.current_container; |
14 | |||
15 | // TODO: floating | ||
16 | /* | ||
20 | if (parent->is_floating) { | 17 | if (parent->is_floating) { |
21 | return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows"); | 18 | return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows"); |
22 | } | 19 | } |
20 | */ | ||
23 | 21 | ||
24 | while (parent->type == C_VIEW) { | 22 | while (parent->type == C_VIEW) { |
25 | parent = parent->parent; | 23 | parent = parent->parent; |
26 | } | 24 | } |
27 | 25 | ||
28 | enum swayc_layouts old_layout = parent->layout; | 26 | // TODO: stacks and tabs |
29 | 27 | ||
30 | if (strcasecmp(argv[0], "default") == 0) { | 28 | if (strcasecmp(argv[0], "default") == 0) { |
31 | swayc_change_layout(parent, parent->prev_layout); | 29 | container_set_layout(parent, parent->prev_layout); |
32 | if (parent->layout == L_NONE) { | 30 | if (parent->layout == L_NONE) { |
33 | swayc_t *output = swayc_parent_by_type(parent, C_OUTPUT); | 31 | container_set_layout(parent, container_get_default_layout(parent)); |
34 | swayc_change_layout(parent, default_layout(output)); | ||
35 | } | 32 | } |
36 | } else { | 33 | } else { |
37 | if (parent->layout != L_TABBED && parent->layout != L_STACKED) { | 34 | if (parent->layout != L_TABBED && parent->layout != L_STACKED) { |
38 | parent->prev_layout = parent->layout; | 35 | parent->prev_layout = parent->layout; |
39 | } | 36 | } |
40 | 37 | ||
41 | if (strcasecmp(argv[0], "tabbed") == 0) { | 38 | if (strcasecmp(argv[0], "splith") == 0) { |
42 | if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)){ | 39 | container_set_layout(parent, L_HORIZ); |
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) { | 40 | } else if (strcasecmp(argv[0], "splitv") == 0) { |
56 | swayc_change_layout(parent, L_VERT); | 41 | container_set_layout(parent, L_VERT); |
57 | } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { | 42 | } 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 | 43 | if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE |
59 | || parent->workspace_layout == L_HORIZ)) { | 44 | || parent->workspace_layout == L_HORIZ)) { |
60 | swayc_change_layout(parent, L_VERT); | 45 | container_set_layout(parent, L_VERT); |
61 | } else { | 46 | } else { |
62 | swayc_change_layout(parent, L_HORIZ); | 47 | container_set_layout(parent, L_HORIZ); |
63 | } | 48 | } |
64 | } else if (strcasecmp(argv[0], "auto") == 0) { | ||
65 | return cmd_layout_auto(parent, argc, argv); | ||
66 | } | 49 | } |
67 | } | 50 | } |
68 | 51 | ||
69 | update_layout_geometry(parent, old_layout); | ||
70 | update_geometry(parent); | ||
71 | |||
72 | arrange_windows(parent, parent->width, parent->height); | 52 | arrange_windows(parent, parent->width, parent->height); |
73 | 53 | ||
74 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 54 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
75 | } | 55 | } |
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/commands/log_colors.c b/sway/commands/log_colors.c deleted file mode 100644 index 815d1942..00000000 --- a/sway/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/commands/mark.c b/sway/commands/mark.c deleted file mode 100644 index c1d959df..00000000 --- a/sway/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/commands/mode.c b/sway/commands/mode.c index d2985c54..c30a8bac 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c | |||
@@ -15,43 +15,45 @@ struct cmd_results *cmd_mode(int argc, char **argv) { | |||
15 | } | 15 | } |
16 | 16 | ||
17 | const char *mode_name = argv[0]; | 17 | const char *mode_name = argv[0]; |
18 | bool mode_make = (argc == 2 && strcmp(argv[1], "{") == 0); | 18 | bool new_mode = (argc == 2 && strcmp(argv[1], "{") == 0); |
19 | if (mode_make) { | 19 | if (new_mode && !config->reading) { |
20 | if (!config->reading) | 20 | return cmd_results_new(CMD_FAILURE, |
21 | return cmd_results_new(CMD_FAILURE, "mode", "Can only be used in config file."); | 21 | "mode", "Can only be used in config file."); |
22 | } | 22 | } |
23 | struct sway_mode *mode = NULL; | 23 | struct sway_mode *mode = NULL; |
24 | // Find mode | 24 | // Find mode |
25 | int i, len = config->modes->length; | 25 | for (int i = 0; i < config->modes->length; ++i) { |
26 | for (i = 0; i < len; ++i) { | 26 | struct sway_mode *test = config->modes->items[i]; |
27 | struct sway_mode *find = config->modes->items[i]; | 27 | if (strcasecmp(test->name, mode_name) == 0) { |
28 | if (strcasecmp(find->name, mode_name) == 0) { | 28 | mode = test; |
29 | mode = find; | ||
30 | break; | 29 | break; |
31 | } | 30 | } |
32 | } | 31 | } |
33 | // Create mode if it doesn't exist | 32 | // Create mode if it doesn't exist |
34 | if (!mode && mode_make) { | 33 | if (!mode && new_mode) { |
35 | mode = malloc(sizeof(struct sway_mode)); | 34 | mode = calloc(1, sizeof(struct sway_mode)); |
36 | if (!mode) { | 35 | if (!mode) { |
37 | return cmd_results_new(CMD_FAILURE, "mode", "Unable to allocate mode"); | 36 | return cmd_results_new(CMD_FAILURE, |
37 | "mode", "Unable to allocate mode"); | ||
38 | } | 38 | } |
39 | mode->name = strdup(mode_name); | 39 | mode->name = strdup(mode_name); |
40 | mode->bindings = create_list(); | 40 | mode->keysym_bindings = create_list(); |
41 | mode->keycode_bindings = create_list(); | ||
41 | list_add(config->modes, mode); | 42 | list_add(config->modes, mode); |
42 | } | 43 | } |
43 | if (!mode) { | 44 | if (!mode) { |
44 | error = cmd_results_new(CMD_INVALID, "mode", "Unknown mode `%s'", mode_name); | 45 | error = cmd_results_new(CMD_INVALID, |
46 | "mode", "Unknown mode `%s'", mode_name); | ||
45 | return error; | 47 | return error; |
46 | } | 48 | } |
47 | if ((config->reading && mode_make) || (!config->reading && !mode_make)) { | 49 | if ((config->reading && new_mode) || (!config->reading && !new_mode)) { |
48 | sway_log(L_DEBUG, "Switching to mode `%s'",mode->name); | 50 | wlr_log(L_DEBUG, "Switching to mode `%s'",mode->name); |
49 | } | 51 | } |
50 | // Set current mode | 52 | // Set current mode |
51 | config->current_mode = mode; | 53 | config->current_mode = mode; |
52 | if (!mode_make) { | 54 | if (!new_mode) { |
53 | // trigger IPC mode event | 55 | // trigger IPC mode event |
54 | ipc_event_mode(config->current_mode->name); | 56 | ipc_event_mode(config->current_mode->name); |
55 | } | 57 | } |
56 | return cmd_results_new(mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL); | 58 | return cmd_results_new(new_mode ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL); |
57 | } | 59 | } |
diff --git a/sway/commands/mouse_warping.c b/sway/commands/mouse_warping.c index 5596d483..eef32ce7 100644 --- a/sway/commands/mouse_warping.c +++ b/sway/commands/mouse_warping.c | |||
@@ -11,7 +11,9 @@ struct cmd_results *cmd_mouse_warping(int argc, char **argv) { | |||
11 | } else if (strcasecmp(argv[0], "none") == 0) { | 11 | } else if (strcasecmp(argv[0], "none") == 0) { |
12 | config->mouse_warping = false; | 12 | config->mouse_warping = false; |
13 | } else { | 13 | } else { |
14 | return cmd_results_new(CMD_FAILURE, "mouse_warping", "Expected 'mouse_warping output|none'"); | 14 | return cmd_results_new(CMD_FAILURE, "mouse_warping", |
15 | "Expected 'mouse_warping output|none'"); | ||
15 | } | 16 | } |
16 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 17 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
17 | } | 18 | } |
19 | |||
diff --git a/sway/commands/move.c b/sway/commands/move.c index 8d89f2ef..a5273ba4 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c | |||
@@ -1,186 +1,198 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
1 | #include <string.h> | 2 | #include <string.h> |
2 | #include <strings.h> | 3 | #include <strings.h> |
3 | #include <wlc/wlc.h> | 4 | #include <wlr/types/wlr_output.h> |
5 | #include <wlr/types/wlr_output_layout.h> | ||
6 | #include <wlr/util/log.h> | ||
4 | #include "sway/commands.h" | 7 | #include "sway/commands.h" |
5 | #include "sway/container.h" | 8 | #include "sway/input/seat.h" |
6 | #include "sway/layout.h" | ||
7 | #include "sway/output.h" | 9 | #include "sway/output.h" |
8 | #include "sway/workspace.h" | 10 | #include "sway/tree/container.h" |
9 | #include "list.h" | 11 | #include "sway/tree/layout.h" |
12 | #include "sway/tree/workspace.h" | ||
10 | #include "stringop.h" | 13 | #include "stringop.h" |
14 | #include "list.h" | ||
15 | |||
16 | static const char* expected_syntax = | ||
17 | "Expected 'move <left|right|up|down> <[px] px>' or " | ||
18 | "'move <container|window> to workspace <name>' or " | ||
19 | "'move <container|window|workspace> to output <name|direction>' or " | ||
20 | "'move position mouse'"; | ||
21 | |||
22 | static struct sway_container *output_in_direction(const char *direction, | ||
23 | struct wlr_output *reference, int ref_ox, int ref_oy) { | ||
24 | int ref_lx = ref_ox + reference->lx, | ||
25 | ref_ly = ref_oy + reference->ly; | ||
26 | struct { | ||
27 | char *name; | ||
28 | enum wlr_direction direction; | ||
29 | } names[] = { | ||
30 | { "up", WLR_DIRECTION_UP }, | ||
31 | { "down", WLR_DIRECTION_DOWN }, | ||
32 | { "left", WLR_DIRECTION_LEFT }, | ||
33 | { "right", WLR_DIRECTION_RIGHT }, | ||
34 | }; | ||
35 | for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) { | ||
36 | if (strcasecmp(names[i].name, direction) == 0) { | ||
37 | struct wlr_output *adjacent = wlr_output_layout_adjacent_output( | ||
38 | root_container.sway_root->output_layout, | ||
39 | names[i].direction, reference, ref_lx, ref_ly); | ||
40 | if (adjacent) { | ||
41 | struct sway_output *sway_output = adjacent->data; | ||
42 | return sway_output->swayc; | ||
43 | } | ||
44 | break; | ||
45 | } | ||
46 | } | ||
47 | return output_by_name(direction); | ||
48 | } | ||
49 | |||
50 | static struct cmd_results *cmd_move_container(struct sway_container *current, | ||
51 | int argc, char **argv) { | ||
52 | struct cmd_results *error = NULL; | ||
53 | if ((error = checkarg(argc, "move container/window", | ||
54 | EXPECTED_AT_LEAST, 4))) { | ||
55 | return error; | ||
56 | } else if (strcasecmp(argv[1], "to") == 0 | ||
57 | && strcasecmp(argv[2], "workspace") == 0) { | ||
58 | // move container to workspace x | ||
59 | if (current->type == C_WORKSPACE) { | ||
60 | // TODO: Wrap children in a container and move that | ||
61 | return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); | ||
62 | } else if (current->type != C_CONTAINER && current->type != C_VIEW) { | ||
63 | return cmd_results_new(CMD_FAILURE, "move", | ||
64 | "Can only move containers and views."); | ||
65 | } | ||
66 | struct sway_container *ws; | ||
67 | char *ws_name = NULL; | ||
68 | if (argc == 5 && strcasecmp(argv[3], "number") == 0) { | ||
69 | // move "container to workspace number x" | ||
70 | ws_name = strdup(argv[4]); | ||
71 | ws = workspace_by_number(ws_name); | ||
72 | } else { | ||
73 | ws_name = join_args(argv + 3, argc - 3); | ||
74 | ws = workspace_by_name(ws_name); | ||
75 | } | ||
76 | |||
77 | if (config->auto_back_and_forth && prev_workspace_name) { | ||
78 | // auto back and forth move | ||
79 | struct sway_container *curr_ws = container_parent(current, C_WORKSPACE); | ||
80 | if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) { | ||
81 | // if target workspace is the current one | ||
82 | free(ws_name); | ||
83 | ws_name = strdup(prev_workspace_name); | ||
84 | ws = workspace_by_name(ws_name); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | if (!ws) { | ||
89 | ws = workspace_create(NULL, ws_name); | ||
90 | } | ||
91 | free(ws_name); | ||
92 | struct sway_container *old_parent = current->parent; | ||
93 | struct sway_container *focus = seat_get_focus_inactive( | ||
94 | config->handler_context.seat, ws); | ||
95 | container_move_to(current, focus); | ||
96 | seat_set_focus(config->handler_context.seat, old_parent); | ||
97 | container_reap_empty(old_parent); | ||
98 | container_reap_empty(focus->parent); | ||
99 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
100 | } else if (strcasecmp(argv[1], "to") == 0 | ||
101 | && strcasecmp(argv[2], "output") == 0) { | ||
102 | if (current->type == C_WORKSPACE) { | ||
103 | // TODO: Wrap children in a container and move that | ||
104 | return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); | ||
105 | } else if (current->type != C_CONTAINER | ||
106 | && current->type != C_VIEW) { | ||
107 | return cmd_results_new(CMD_FAILURE, "move", | ||
108 | "Can only move containers and views."); | ||
109 | } | ||
110 | struct sway_container *source = container_parent(current, C_OUTPUT); | ||
111 | struct sway_container *destination = output_in_direction(argv[3], | ||
112 | source->sway_output->wlr_output, current->x, current->y); | ||
113 | if (!destination) { | ||
114 | return cmd_results_new(CMD_FAILURE, "move workspace", | ||
115 | "Can't find output with name/direction '%s'", argv[3]); | ||
116 | } | ||
117 | struct sway_container *focus = seat_get_focus_inactive( | ||
118 | config->handler_context.seat, destination); | ||
119 | if (!focus) { | ||
120 | // We've never been to this output before | ||
121 | focus = destination->children->items[0]; | ||
122 | } | ||
123 | struct sway_container *old_parent = current->parent; | ||
124 | container_move_to(current, focus); | ||
125 | seat_set_focus(config->handler_context.seat, old_parent); | ||
126 | container_reap_empty(old_parent); | ||
127 | container_reap_empty(focus->parent); | ||
128 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
129 | } | ||
130 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
131 | } | ||
132 | |||
133 | static struct cmd_results *cmd_move_workspace(struct sway_container *current, | ||
134 | int argc, char **argv) { | ||
135 | struct cmd_results *error = NULL; | ||
136 | if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { | ||
137 | return error; | ||
138 | } else if (strcasecmp(argv[1], "to") != 0 | ||
139 | || strcasecmp(argv[2], "output") != 0) { | ||
140 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
141 | } | ||
142 | struct sway_container *source = container_parent(current, C_OUTPUT); | ||
143 | int center_x = current->width / 2 + current->x, | ||
144 | center_y = current->height / 2 + current->y; | ||
145 | struct sway_container *destination = output_in_direction(argv[3], | ||
146 | source->sway_output->wlr_output, center_x, center_y); | ||
147 | if (!destination) { | ||
148 | return cmd_results_new(CMD_FAILURE, "move workspace", | ||
149 | "Can't find output with name/direction '%s'", argv[3]); | ||
150 | } | ||
151 | if (current->type != C_WORKSPACE) { | ||
152 | current = container_parent(current, C_WORKSPACE); | ||
153 | } | ||
154 | container_move_to(current, destination); | ||
155 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
156 | } | ||
11 | 157 | ||
12 | struct cmd_results *cmd_move(int argc, char **argv) { | 158 | struct cmd_results *cmd_move(int argc, char **argv) { |
13 | struct cmd_results *error = NULL; | 159 | struct cmd_results *error = NULL; |
14 | int move_amt = 10; | 160 | int move_amt = 10; |
15 | |||
16 | if (config->reading) return cmd_results_new(CMD_FAILURE, "move", "Can't be used in config file."); | ||
17 | if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { | 161 | if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { |
18 | return error; | 162 | return error; |
19 | } | 163 | } |
20 | const char* expected_syntax = "Expected 'move <left|right|up|down|next|prev|first> <[px] px>' or " | 164 | struct sway_container *current = config->handler_context.current_container; |
21 | "'move <container|window> to workspace <name>' or " | ||
22 | "'move <container|window|workspace> to output <name|direction>' or " | ||
23 | "'move position mouse'"; | ||
24 | swayc_t *view = current_container; | ||
25 | 165 | ||
26 | if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0 )) { | 166 | if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) { |
27 | char *inv; | 167 | char *inv; |
28 | move_amt = (int)strtol(argv[1], &inv, 10); | 168 | move_amt = (int)strtol(argv[1], &inv, 10); |
29 | if (*inv != '\0' && strcasecmp(inv, "px") != 0) { | 169 | if (*inv != '\0' && strcasecmp(inv, "px") != 0) { |
30 | move_amt = 10; | 170 | return cmd_results_new(CMD_FAILURE, "move", |
171 | "Invalid distance specified"); | ||
31 | } | 172 | } |
32 | } | 173 | } |
33 | 174 | ||
34 | if (strcasecmp(argv[0], "left") == 0) { | 175 | if (strcasecmp(argv[0], "left") == 0) { |
35 | move_container(view, MOVE_LEFT, move_amt); | 176 | container_move(current, MOVE_LEFT, move_amt); |
36 | } else if (strcasecmp(argv[0], "right") == 0) { | 177 | } else if (strcasecmp(argv[0], "right") == 0) { |
37 | move_container(view, MOVE_RIGHT, move_amt); | 178 | container_move(current, MOVE_RIGHT, move_amt); |
38 | } else if (strcasecmp(argv[0], "up") == 0) { | 179 | } else if (strcasecmp(argv[0], "up") == 0) { |
39 | move_container(view, MOVE_UP, move_amt); | 180 | container_move(current, MOVE_UP, move_amt); |
40 | } else if (strcasecmp(argv[0], "down") == 0) { | 181 | } else if (strcasecmp(argv[0], "down") == 0) { |
41 | move_container(view, MOVE_DOWN, move_amt); | 182 | container_move(current, MOVE_DOWN, move_amt); |
42 | } else if (strcasecmp(argv[0], "next") == 0) { | 183 | } else if (strcasecmp(argv[0], "container") == 0 |
43 | move_container(view, MOVE_NEXT, move_amt); | 184 | || strcasecmp(argv[0], "window") == 0) { |
44 | } else if (strcasecmp(argv[0], "prev") == 0) { | 185 | return cmd_move_container(current, argc, argv); |
45 | move_container(view, MOVE_PREV, move_amt); | ||
46 | } else if (strcasecmp(argv[0], "first") == 0) { | ||
47 | move_container(view, MOVE_FIRST, move_amt); | ||
48 | } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) { | ||
49 | // "move container ... | ||
50 | if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 4))) { | ||
51 | return error; | ||
52 | } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "workspace") == 0) { | ||
53 | // move container to workspace x | ||
54 | if (view->type == C_WORKSPACE) { | ||
55 | if (!view->children || view->children->length == 0) { | ||
56 | return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace"); | ||
57 | } | ||
58 | view = new_container(view, view->workspace_layout); | ||
59 | } if (view->type != C_CONTAINER && view->type != C_VIEW) { | ||
60 | return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views."); | ||
61 | } | ||
62 | |||
63 | swayc_t *ws; | ||
64 | const char *num_name = NULL; | ||
65 | char *ws_name = NULL; | ||
66 | if (argc == 5 && strcasecmp(argv[3], "number") == 0) { | ||
67 | // move "container to workspace number x" | ||
68 | num_name = argv[4]; | ||
69 | ws = workspace_by_number(num_name); | ||
70 | } else { | ||
71 | ws_name = join_args(argv + 3, argc - 3); | ||
72 | ws = workspace_by_name(ws_name); | ||
73 | } | ||
74 | |||
75 | if (ws == NULL) { | ||
76 | ws = workspace_create(ws_name ? ws_name : num_name); | ||
77 | } | ||
78 | if (ws_name) { | ||
79 | free(ws_name); | ||
80 | } | ||
81 | move_container_to(view, get_focused_container(ws)); | ||
82 | } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) { | ||
83 | // move container to output x | ||
84 | swayc_t *output = NULL; | ||
85 | struct wlc_point abs_pos; | ||
86 | get_absolute_center_position(view, &abs_pos); | ||
87 | if (view->type == C_WORKSPACE) { | ||
88 | if (!view->children || view->children->length == 0) { | ||
89 | return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace"); | ||
90 | } | ||
91 | view = new_container(view, view->workspace_layout); | ||
92 | } else if (view->type != C_CONTAINER && view->type != C_VIEW) { | ||
93 | return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views."); | ||
94 | } else if (!(output = output_by_name(argv[3], &abs_pos))) { | ||
95 | return cmd_results_new(CMD_FAILURE, "move", | ||
96 | "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y); | ||
97 | } | ||
98 | |||
99 | swayc_t *container = get_focused_container(output); | ||
100 | if (container->is_floating) { | ||
101 | move_container_to(view, container->parent); | ||
102 | } else { | ||
103 | move_container_to(view, container); | ||
104 | } | ||
105 | } else { | ||
106 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
107 | } | ||
108 | } else if (strcasecmp(argv[0], "workspace") == 0) { | 186 | } else if (strcasecmp(argv[0], "workspace") == 0) { |
109 | // move workspace (to output x) | 187 | return cmd_move_workspace(current, argc, argv); |
110 | swayc_t *output = NULL; | 188 | } else if (strcasecmp(argv[0], "scratchpad") == 0 |
111 | struct wlc_point abs_pos; | 189 | || (strcasecmp(argv[0], "to") == 0 |
112 | get_absolute_center_position(view, &abs_pos); | 190 | && strcasecmp(argv[1], "scratchpad") == 0)) { |
113 | if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { | 191 | // TODO: scratchpad |
114 | return error; | 192 | return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); |
115 | } else if (strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "output") != 0) { | ||
116 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
117 | } else if (!(output = output_by_name(argv[3], &abs_pos))) { | ||
118 | return cmd_results_new(CMD_FAILURE, "move workspace", | ||
119 | "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y); | ||
120 | } | ||
121 | if (view->type == C_WORKSPACE) { | ||
122 | // This probably means we're moving an empty workspace, but | ||
123 | // that's fine. | ||
124 | move_workspace_to(view, output); | ||
125 | } else { | ||
126 | swayc_t *workspace = swayc_parent_by_type(view, C_WORKSPACE); | ||
127 | move_workspace_to(workspace, output); | ||
128 | } | ||
129 | } else if (strcasecmp(argv[0], "scratchpad") == 0 || (strcasecmp(argv[0], "to") == 0 && strcasecmp(argv[1], "scratchpad") == 0)) { | ||
130 | // move scratchpad ... | ||
131 | if (view->type != C_CONTAINER && view->type != C_VIEW) { | ||
132 | return cmd_results_new(CMD_FAILURE, "move scratchpad", "Can only move containers and views."); | ||
133 | } | ||
134 | swayc_t *view = current_container; | ||
135 | int i; | ||
136 | for (i = 0; i < scratchpad->length; i++) { | ||
137 | if (scratchpad->items[i] == view) { | ||
138 | hide_view_in_scratchpad(view); | ||
139 | sp_view = NULL; | ||
140 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
141 | } | ||
142 | } | ||
143 | list_add(scratchpad, view); | ||
144 | if (!view->is_floating) { | ||
145 | destroy_container(remove_child(view)); | ||
146 | } else { | ||
147 | remove_child(view); | ||
148 | } | ||
149 | wlc_view_set_mask(view->handle, 0); | ||
150 | arrange_windows(swayc_active_workspace(), -1, -1); | ||
151 | swayc_t *focused = container_under_pointer(); | ||
152 | if (focused == NULL) { | ||
153 | focused = swayc_active_workspace(); | ||
154 | } | ||
155 | set_focused_container(focused); | ||
156 | } else if (strcasecmp(argv[0], "position") == 0) { | 193 | } else if (strcasecmp(argv[0], "position") == 0) { |
157 | if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 2))) { | 194 | // TODO: floating |
158 | return error; | 195 | return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); |
159 | } | ||
160 | if (strcasecmp(argv[1], "mouse")) { | ||
161 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
162 | } | ||
163 | |||
164 | if (view->is_floating) { | ||
165 | swayc_t *output = swayc_parent_by_type(view, C_OUTPUT); | ||
166 | struct wlc_geometry g; | ||
167 | wlc_view_get_visible_geometry(view->handle, &g); | ||
168 | const struct wlc_size *size = wlc_output_get_resolution(output->handle); | ||
169 | |||
170 | double x_pos, y_pos; | ||
171 | wlc_pointer_get_position_v2(&x_pos, &y_pos); | ||
172 | |||
173 | int32_t x = x_pos - g.size.w / 2; | ||
174 | int32_t y = y_pos - g.size.h / 2; | ||
175 | |||
176 | uint32_t w = size->w - g.size.w; | ||
177 | uint32_t h = size->h - g.size.h; | ||
178 | |||
179 | view->x = g.origin.x = MIN((int32_t)w, MAX(x, 0)); | ||
180 | view->y = g.origin.y = MIN((int32_t)h, MAX(y, 0)); | ||
181 | |||
182 | wlc_view_set_geometry(view->handle, 0, &g); | ||
183 | } | ||
184 | } else { | 196 | } else { |
185 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | 197 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); |
186 | } | 198 | } |
diff --git a/sway/commands/new_float.c b/sway/commands/new_float.c deleted file mode 100644 index d0f96093..00000000 --- a/sway/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/commands/new_window.c b/sway/commands/new_window.c deleted file mode 100644 index 574a4527..00000000 --- a/sway/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/commands/no_focus.c b/sway/commands/no_focus.c deleted file mode 100644 index b3b88e5a..00000000 --- a/sway/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/commands/opacity.c b/sway/commands/opacity.c new file mode 100644 index 00000000..68fd9f42 --- /dev/null +++ b/sway/commands/opacity.c | |||
@@ -0,0 +1,36 @@ | |||
1 | #include <assert.h> | ||
2 | #include <stdlib.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/tree/view.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | static bool parse_opacity(const char *opacity, float *val) { | ||
8 | char *err; | ||
9 | *val = strtof(opacity, &err); | ||
10 | if (*val < 0 || *val > 1 || *err) { | ||
11 | return false; | ||
12 | } | ||
13 | return true; | ||
14 | } | ||
15 | |||
16 | struct cmd_results *cmd_opacity(int argc, char **argv) { | ||
17 | struct cmd_results *error = NULL; | ||
18 | if ((error = checkarg(argc, "layout", EXPECTED_EQUAL_TO, 1))) { | ||
19 | return error; | ||
20 | } | ||
21 | |||
22 | struct sway_container *con = | ||
23 | config->handler_context.current_container; | ||
24 | |||
25 | float opacity = 0.0f; | ||
26 | |||
27 | if (!parse_opacity(argv[0], &opacity)) { | ||
28 | return cmd_results_new(CMD_INVALID, "opacity <value>", | ||
29 | "Invalid value (expected 0..1): %s", argv[0]); | ||
30 | } | ||
31 | |||
32 | con->alpha = opacity; | ||
33 | container_damage_whole(con); | ||
34 | |||
35 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
36 | } | ||
diff --git a/sway/commands/output.c b/sway/commands/output.c index 911391d2..f7e3372c 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c | |||
@@ -17,196 +17,299 @@ static char *bg_options[] = { | |||
17 | "center", | 17 | "center", |
18 | "fill", | 18 | "fill", |
19 | "fit", | 19 | "fit", |
20 | "tile" | 20 | "tile", |
21 | }; | 21 | }; |
22 | 22 | ||
23 | struct cmd_results *cmd_output(int argc, char **argv) { | 23 | static struct cmd_results *cmd_output_mode(struct output_config *output, |
24 | struct cmd_results *error = NULL; | 24 | int *i, int argc, char **argv) { |
25 | if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) { | 25 | if (++*i >= argc) { |
26 | return error; | 26 | return cmd_results_new(CMD_INVALID, "output", "Missing mode argument."); |
27 | } | 27 | } |
28 | const char *name = argv[0]; | ||
29 | 28 | ||
30 | struct output_config *output = calloc(1, sizeof(struct output_config)); | 29 | char *end; |
31 | if (!output) { | 30 | output->width = strtol(argv[*i], &end, 10); |
32 | return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate output config"); | 31 | if (*end) { |
32 | // Format is 1234x4321 | ||
33 | if (*end != 'x') { | ||
34 | return cmd_results_new(CMD_INVALID, "output", | ||
35 | "Invalid mode width."); | ||
36 | } | ||
37 | ++end; | ||
38 | output->height = strtol(end, &end, 10); | ||
39 | if (*end) { | ||
40 | if (*end != '@') { | ||
41 | return cmd_results_new(CMD_INVALID, "output", | ||
42 | "Invalid mode height."); | ||
43 | } | ||
44 | ++end; | ||
45 | output->refresh_rate = strtof(end, &end); | ||
46 | if (strcasecmp("Hz", end) != 0) { | ||
47 | return cmd_results_new(CMD_INVALID, "output", | ||
48 | "Invalid mode refresh rate."); | ||
49 | } | ||
50 | } | ||
51 | } else { | ||
52 | // Format is 1234 4321 | ||
53 | if (++*i >= argc) { | ||
54 | return cmd_results_new(CMD_INVALID, "output", | ||
55 | "Missing mode argument (height)."); | ||
56 | } | ||
57 | output->height = strtol(argv[*i], &end, 10); | ||
58 | if (*end) { | ||
59 | return cmd_results_new(CMD_INVALID, "output", | ||
60 | "Invalid mode height."); | ||
61 | } | ||
33 | } | 62 | } |
34 | output->x = output->y = output->width = output->height = -1; | ||
35 | output->name = strdup(name); | ||
36 | output->enabled = -1; | ||
37 | output->scale = 1; | ||
38 | 63 | ||
39 | // TODO: atoi doesn't handle invalid numbers | 64 | return NULL; |
65 | } | ||
40 | 66 | ||
41 | int i; | 67 | static struct cmd_results *cmd_output_position(struct output_config *output, |
42 | for (i = 1; i < argc; ++i) { | 68 | int *i, int argc, char **argv) { |
43 | const char *command = argv[i]; | 69 | if (++*i >= argc) { |
70 | return cmd_results_new(CMD_INVALID, "output", | ||
71 | "Missing position argument."); | ||
72 | } | ||
44 | 73 | ||
45 | if (strcasecmp(command, "disable") == 0) { | 74 | char *end; |
46 | output->enabled = 0; | 75 | output->x = strtol(argv[*i], &end, 10); |
47 | } else if (strcasecmp(command, "resolution") == 0 || strcasecmp(command, "res") == 0) { | 76 | if (*end) { |
48 | if (++i >= argc) { | 77 | // Format is 1234,4321 |
49 | error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument."); | 78 | if (*end != ',') { |
50 | goto fail; | 79 | return cmd_results_new(CMD_INVALID, "output", |
51 | } | 80 | "Invalid position x."); |
52 | char *res = argv[i]; | 81 | } |
53 | char *x = strchr(res, 'x'); | 82 | ++end; |
54 | int width = -1, height = -1; | 83 | output->y = strtol(end, &end, 10); |
55 | if (x != NULL) { | 84 | if (*end) { |
56 | // Format is 1234x4321 | 85 | return cmd_results_new(CMD_INVALID, "output", |
57 | *x = '\0'; | 86 | "Invalid position y."); |
58 | width = atoi(res); | 87 | } |
59 | height = atoi(x + 1); | 88 | } else { |
60 | *x = 'x'; | 89 | // Format is 1234 4321 (legacy) |
61 | } else { | 90 | if (++*i >= argc) { |
62 | // Format is 1234 4321 | 91 | return cmd_results_new(CMD_INVALID, "output", |
63 | width = atoi(res); | 92 | "Missing position argument (y)."); |
64 | if (++i >= argc) { | 93 | } |
65 | error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height)."); | 94 | output->y = strtol(argv[*i], &end, 10); |
66 | goto fail; | 95 | if (*end) { |
96 | return cmd_results_new(CMD_INVALID, "output", | ||
97 | "Invalid position y."); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | return NULL; | ||
102 | } | ||
103 | |||
104 | static struct cmd_results *cmd_output_scale(struct output_config *output, | ||
105 | int *i, int argc, char **argv) { | ||
106 | if (++*i >= argc) { | ||
107 | return cmd_results_new(CMD_INVALID, "output", | ||
108 | "Missing scale argument."); | ||
109 | } | ||
110 | |||
111 | char *end; | ||
112 | output->scale = strtof(argv[*i], &end); | ||
113 | if (*end) { | ||
114 | return cmd_results_new(CMD_INVALID, "output", "Invalid scale."); | ||
115 | } | ||
116 | |||
117 | return NULL; | ||
118 | } | ||
119 | |||
120 | static struct cmd_results *cmd_output_transform(struct output_config *output, | ||
121 | int *i, int argc, char **argv) { | ||
122 | if (++*i >= argc) { | ||
123 | return cmd_results_new(CMD_INVALID, "output", | ||
124 | "Missing transform argument."); | ||
125 | } | ||
126 | |||
127 | char *value = argv[*i]; | ||
128 | if (strcmp(value, "normal") == 0) { | ||
129 | output->transform = WL_OUTPUT_TRANSFORM_NORMAL; | ||
130 | } else if (strcmp(value, "90") == 0) { | ||
131 | output->transform = WL_OUTPUT_TRANSFORM_90; | ||
132 | } else if (strcmp(value, "180") == 0) { | ||
133 | output->transform = WL_OUTPUT_TRANSFORM_180; | ||
134 | } else if (strcmp(value, "270") == 0) { | ||
135 | output->transform = WL_OUTPUT_TRANSFORM_270; | ||
136 | } else if (strcmp(value, "flipped") == 0) { | ||
137 | output->transform = WL_OUTPUT_TRANSFORM_FLIPPED; | ||
138 | } else if (strcmp(value, "flipped-90") == 0) { | ||
139 | output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90; | ||
140 | } else if (strcmp(value, "flipped-180") == 0) { | ||
141 | output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180; | ||
142 | } else if (strcmp(value, "flipped-270") == 0) { | ||
143 | output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270; | ||
144 | } else { | ||
145 | return cmd_results_new(CMD_INVALID, "output", | ||
146 | "Invalid output transform."); | ||
147 | } | ||
148 | |||
149 | return NULL; | ||
150 | } | ||
151 | |||
152 | static struct cmd_results *cmd_output_background(struct output_config *output, | ||
153 | int *i, int argc, char **argv) { | ||
154 | if (++*i >= argc) { | ||
155 | return cmd_results_new(CMD_INVALID, "output", | ||
156 | "Missing background file or color specification."); | ||
157 | } | ||
158 | const char *background = argv[*i]; | ||
159 | if (*i + 1 >= argc) { | ||
160 | return cmd_results_new(CMD_INVALID, "output", | ||
161 | "Missing background scaling mode or `solid_color`."); | ||
162 | } | ||
163 | const char *background_option = argv[*i]; | ||
164 | |||
165 | if (strcasecmp(background_option, "solid_color") == 0) { | ||
166 | output->background = strdup(background); | ||
167 | output->background_option = strdup("solid_color"); | ||
168 | } else { | ||
169 | bool valid = false; | ||
170 | char *mode; | ||
171 | size_t j; | ||
172 | for (j = 0; j < (size_t)(argc - *i); ++j) { | ||
173 | mode = argv[*i + j]; | ||
174 | size_t n = sizeof(bg_options) / sizeof(char *); | ||
175 | for (size_t k = 0; k < n; ++k) { | ||
176 | if (strcasecmp(mode, bg_options[k]) == 0) { | ||
177 | valid = true; | ||
178 | break; | ||
67 | } | 179 | } |
68 | res = argv[i]; | ||
69 | height = atoi(res); | ||
70 | } | 180 | } |
71 | output->width = width; | 181 | if (valid) { |
72 | output->height = height; | 182 | break; |
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 | } | 183 | } |
78 | char *res = argv[i]; | 184 | } |
79 | char *c = strchr(res, ','); | 185 | if (!valid) { |
80 | int x = -1, y = -1; | 186 | return cmd_results_new(CMD_INVALID, "output", |
81 | if (c != NULL) { | 187 | "Missing background scaling mode."); |
82 | // Format is 1234,4321 | 188 | } |
83 | *c = '\0'; | 189 | |
84 | x = atoi(res); | 190 | wordexp_t p; |
85 | y = atoi(c + 1); | 191 | char *src = join_args(argv + *i, j); |
86 | *c = ','; | 192 | if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { |
87 | } else { | 193 | return cmd_results_new(CMD_INVALID, "output", |
88 | // Format is 1234 4321 | 194 | "Invalid syntax (%s).", src); |
89 | x = atoi(res); | 195 | } |
90 | if (++i >= argc) { | 196 | free(src); |
91 | error = cmd_results_new(CMD_INVALID, "output", "Missing position argument (y)."); | 197 | src = p.we_wordv[0]; |
92 | goto fail; | 198 | if (config->reading && *src != '/') { |
199 | char *conf = strdup(config->current_config); | ||
200 | if (conf) { | ||
201 | char *conf_path = dirname(conf); | ||
202 | src = malloc(strlen(conf_path) + strlen(src) + 2); | ||
203 | if (src) { | ||
204 | sprintf(src, "%s/%s", conf_path, p.we_wordv[0]); | ||
205 | } else { | ||
206 | wlr_log(L_ERROR, | ||
207 | "Unable to allocate background source"); | ||
93 | } | 208 | } |
94 | res = argv[i]; | 209 | free(conf); |
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 { | 210 | } else { |
119 | // argv[i+j]=bg_option | 211 | wlr_log(L_ERROR, "Unable to allocate background source"); |
120 | bool valid = false; | 212 | } |
121 | char *mode; | 213 | } |
122 | size_t j; | 214 | if (!src || access(src, F_OK) == -1) { |
123 | for (j = 0; j < (size_t) (argc - i); ++j) { | 215 | wordfree(&p); |
124 | mode = argv[i + j]; | 216 | return cmd_results_new(CMD_INVALID, "output", |
125 | for (size_t k = 0; k < sizeof(bg_options) / sizeof(char *); ++k) { | 217 | "Background file unreadable (%s).", src); |
126 | if (strcasecmp(mode, bg_options[k]) == 0) { | 218 | } |
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 | 219 | ||
140 | char *src = join_args(argv + i, j); | 220 | output->background = strdup(src); |
141 | if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { | 221 | output->background_option = strdup(mode); |
142 | error = cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src); | 222 | if (src != p.we_wordv[0]) { |
143 | goto fail; | 223 | free(src); |
144 | } | 224 | } |
145 | free(src); | 225 | wordfree(&p); |
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 | 226 | ||
168 | output->background = strdup(src); | 227 | *i += j; |
169 | output->background_option = strdup(mode); | 228 | } |
170 | if (src != p.we_wordv[0]) { | ||
171 | free(src); | ||
172 | } | ||
173 | wordfree(&p); | ||
174 | 229 | ||
175 | i += j; | 230 | return NULL; |
176 | } | 231 | } |
232 | |||
233 | struct cmd_results *cmd_output(int argc, char **argv) { | ||
234 | struct cmd_results *error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1); | ||
235 | if (error != NULL) { | ||
236 | return error; | ||
237 | } | ||
238 | |||
239 | struct output_config *output = new_output_config(argv[0]); | ||
240 | if (!output) { | ||
241 | wlr_log(L_ERROR, "Failed to allocate output config"); | ||
242 | return NULL; | ||
243 | } | ||
244 | |||
245 | for (int i = 1; i < argc; ++i) { | ||
246 | const char *command = argv[i]; | ||
247 | |||
248 | if (strcasecmp(command, "enable") == 0) { | ||
249 | output->enabled = 1; | ||
250 | } else if (strcasecmp(command, "disable") == 0) { | ||
251 | output->enabled = 0; | ||
252 | } else if (strcasecmp(command, "mode") == 0 || | ||
253 | strcasecmp(command, "resolution") == 0 || | ||
254 | strcasecmp(command, "res") == 0) { | ||
255 | error = cmd_output_mode(output, &i, argc, argv); | ||
256 | } else if (strcasecmp(command, "position") == 0 || | ||
257 | strcasecmp(command, "pos") == 0) { | ||
258 | error = cmd_output_position(output, &i, argc, argv); | ||
259 | } else if (strcasecmp(command, "scale") == 0) { | ||
260 | error = cmd_output_scale(output, &i, argc, argv); | ||
261 | } else if (strcasecmp(command, "transform") == 0) { | ||
262 | error = cmd_output_transform(output, &i, argc, argv); | ||
263 | } else if (strcasecmp(command, "background") == 0 || | ||
264 | strcasecmp(command, "bg") == 0) { | ||
265 | error = cmd_output_background(output, &i, argc, argv); | ||
266 | } else { | ||
267 | error = cmd_results_new(CMD_INVALID, "output", | ||
268 | "Invalid output subcommand: %s.", command); | ||
269 | } | ||
270 | |||
271 | if (error != NULL) { | ||
272 | goto fail; | ||
177 | } | 273 | } |
178 | } | 274 | } |
179 | 275 | ||
180 | i = list_seq_find(config->output_configs, output_name_cmp, name); | 276 | int i = list_seq_find(config->output_configs, output_name_cmp, output->name); |
181 | if (i >= 0) { | 277 | if (i >= 0) { |
182 | // merge existing config | 278 | // Merge existing config |
183 | struct output_config *oc = config->output_configs->items[i]; | 279 | struct output_config *current = config->output_configs->items[i]; |
184 | merge_output_config(oc, output); | 280 | merge_output_config(current, output); |
185 | free_output_config(output); | 281 | free_output_config(output); |
186 | output = oc; | 282 | output = current; |
187 | } else { | 283 | } else { |
188 | list_add(config->output_configs, output); | 284 | list_add(config->output_configs, output); |
189 | } | 285 | } |
190 | 286 | ||
191 | sway_log(L_DEBUG, "Config stored for output %s (enabled:%d) (%d x %d @ %d, %d scale %d) (bg %s %s)", | 287 | wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " |
192 | output->name, output->enabled, output->width, | 288 | "position %d,%d scale %f transform %d) (bg %s %s)", |
193 | output->height, output->x, output->y, output->scale, | 289 | output->name, output->enabled, output->width, output->height, |
194 | output->background, output->background_option); | 290 | output->refresh_rate, output->x, output->y, output->scale, |
291 | output->transform, output->background, output->background_option); | ||
195 | 292 | ||
196 | if (output->name) { | 293 | // Try to find the output container and apply configuration now. If |
197 | // Try to find the output container and apply configuration now. If | 294 | // this is during startup then there will be no container and config |
198 | // this is during startup then there will be no container and config | 295 | // will be applied during normal "new output" event from wlroots. |
199 | // will be applied during normal "new output" event from wlc. | 296 | char identifier[128]; |
200 | swayc_t *cont = NULL; | 297 | bool all = strcmp(output->name, "*") == 0; |
201 | for (int i = 0; i < root_container.children->length; ++i) { | 298 | for (int i = 0; i < root_container.children->length; ++i) { |
202 | cont = root_container.children->items[i]; | 299 | struct sway_container *cont = root_container.children->items[i]; |
203 | if (cont->name && ((strcmp(cont->name, output->name) == 0) || (strcmp(output->name, "*") == 0))) { | 300 | if (cont->type != C_OUTPUT) { |
204 | apply_output_config(output, cont); | 301 | continue; |
302 | } | ||
205 | 303 | ||
206 | if (strcmp(output->name, "*") != 0) { | 304 | output_get_identifier(identifier, sizeof(identifier), cont->sway_output); |
207 | // stop looking if the output config isn't applicable to all outputs | 305 | if (all || strcmp(cont->name, output->name) == 0 || |
208 | break; | 306 | strcmp(identifier, output->name) == 0) { |
209 | } | 307 | apply_output_config(output, cont); |
308 | |||
309 | if (!all) { | ||
310 | // Stop looking if the output config isn't applicable to all | ||
311 | // outputs | ||
312 | break; | ||
210 | } | 313 | } |
211 | } | 314 | } |
212 | } | 315 | } |
diff --git a/sway/commands/permit.c b/sway/commands/permit.c deleted file mode 100644 index 7a5e06f7..00000000 --- a/sway/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/commands/reload.c b/sway/commands/reload.c index 01fcc5ba..5bca6cde 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c | |||
@@ -1,10 +1,9 @@ | |||
1 | #include "sway/commands.h" | 1 | #include "sway/commands.h" |
2 | #include "sway/config.h" | 2 | #include "sway/config.h" |
3 | #include "sway/layout.h" | 3 | #include "sway/tree/layout.h" |
4 | 4 | ||
5 | struct cmd_results *cmd_reload(int argc, char **argv) { | 5 | struct cmd_results *cmd_reload(int argc, char **argv) { |
6 | struct cmd_results *error = NULL; | 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))) { | 7 | if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { |
9 | return error; | 8 | return error; |
10 | } | 9 | } |
@@ -13,7 +12,6 @@ struct cmd_results *cmd_reload(int argc, char **argv) { | |||
13 | } | 12 | } |
14 | 13 | ||
15 | load_swaybars(); | 14 | load_swaybars(); |
16 | |||
17 | arrange_windows(&root_container, -1, -1); | 15 | arrange_windows(&root_container, -1, -1); |
18 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 16 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
19 | } | 17 | } |
diff --git a/sway/commands/resize.c b/sway/commands/resize.c index ef52bb07..93c1fe7d 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c | |||
@@ -4,372 +4,281 @@ | |||
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include <string.h> | 5 | #include <string.h> |
6 | #include <strings.h> | 6 | #include <strings.h> |
7 | #include <wlc/wlc.h> | 7 | #include <wlr/util/log.h> |
8 | #include "sway/commands.h" | 8 | #include "sway/commands.h" |
9 | #include "sway/layout.h" | 9 | #include "sway/tree/layout.h" |
10 | #include "sway/focus.h" | ||
11 | #include "sway/input_state.h" | ||
12 | #include "sway/handlers.h" | ||
13 | #include "log.h" | 10 | #include "log.h" |
14 | 11 | ||
15 | enum resize_dim_types { | 12 | static const int MIN_SANE_W = 100, MIN_SANE_H = 60; |
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 | 13 | ||
33 | update_geometry(view); | 14 | enum resize_unit { |
34 | } else { | 15 | RESIZE_UNIT_PX, |
35 | int current_height = view->height; | 16 | RESIZE_UNIT_PPT, |
36 | view->desired_height = new_dimension; | 17 | RESIZE_UNIT_DEFAULT, |
37 | floating_view_sane_size(view); | 18 | RESIZE_UNIT_INVALID, |
38 | 19 | }; | |
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 | 20 | ||
43 | update_geometry(view); | 21 | enum resize_axis { |
44 | } | 22 | RESIZE_AXIS_HORIZONTAL, |
23 | RESIZE_AXIS_VERTICAL, | ||
24 | RESIZE_AXIS_INVALID, | ||
25 | }; | ||
45 | 26 | ||
46 | return true; | 27 | static enum resize_unit parse_resize_unit(const char *unit) { |
28 | if (strcasecmp(unit, "px") == 0) { | ||
29 | return RESIZE_UNIT_PX; | ||
47 | } | 30 | } |
48 | 31 | if (strcasecmp(unit, "ppt") == 0) { | |
49 | return false; | 32 | return RESIZE_UNIT_PPT; |
33 | } | ||
34 | if (strcasecmp(unit, "default") == 0) { | ||
35 | return RESIZE_UNIT_DEFAULT; | ||
36 | } | ||
37 | return RESIZE_UNIT_INVALID; | ||
50 | } | 38 | } |
51 | 39 | ||
52 | static bool resize_floating(int amount, bool use_width) { | 40 | static enum resize_axis parse_resize_axis(const char *axis) { |
53 | swayc_t *view = current_container; | 41 | if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { |
54 | 42 | return RESIZE_AXIS_HORIZONTAL; | |
55 | if (view) { | 43 | } |
56 | if (use_width) { | 44 | if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) { |
57 | return set_size_floating(view->width + amount, true); | 45 | return RESIZE_AXIS_VERTICAL; |
58 | } else { | ||
59 | return set_size_floating(view->height + amount, false); | ||
60 | } | ||
61 | } | 46 | } |
47 | return RESIZE_AXIS_INVALID; | ||
48 | } | ||
62 | 49 | ||
63 | return false; | 50 | static int parallel_coord(struct sway_container *c, enum resize_axis a) { |
51 | return a == RESIZE_AXIS_HORIZONTAL ? c->x : c->y; | ||
64 | } | 52 | } |
65 | 53 | ||
66 | static bool resize_tiled(int amount, bool use_width) { | 54 | static int parallel_size(struct sway_container *c, enum resize_axis a) { |
67 | swayc_t *container = current_container; | 55 | return a == RESIZE_AXIS_HORIZONTAL ? c->width : c->height; |
68 | swayc_t *parent = container->parent; | 56 | } |
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 | 57 | ||
106 | } | 58 | static void resize_tiled(int amount, enum resize_axis axis) { |
107 | if (nb_before || nb_after) { | 59 | struct sway_container *parent = config->handler_context.current_container; |
108 | break; | 60 | struct sway_container *focused = parent; |
109 | } | 61 | if (!parent) { |
110 | } | 62 | return; |
111 | container = parent; /* continue up the tree to the next ancestor */ | ||
112 | } | ||
113 | if (parent == &root_container) { | ||
114 | return true; | ||
115 | } | 63 | } |
116 | sway_log(L_DEBUG, "Found the proper parent: %p. It has %zu before conts, " | 64 | |
117 | "and %zu after conts", parent, nb_before, nb_after); | 65 | enum sway_container_layout parallel_layout = |
118 | // 2. Ensure that the resize operation will not make one of the resized containers drop | 66 | axis == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT; |
119 | // below the "sane" size threshold. | 67 | int minor_weight = 0; |
120 | bool valid = true; | 68 | int major_weight = 0; |
121 | swayc_t *focused = parent->children->items[idx_focused]; | 69 | while (parent->parent) { |
122 | int start = use_major ? 0 : auto_group_start_index(parent, idx_focused); | 70 | struct sway_container *next = parent->parent; |
123 | int end = use_major ? parent->children->length : auto_group_end_index(parent, idx_focused); | 71 | if (next->layout == parallel_layout) { |
124 | sway_log(L_DEBUG, "Check children of container %p [%d,%d[", container, start, end); | 72 | for (int i = 0; i < next->children->length; i++) { |
125 | for (int i = start; i < end; ) { | 73 | struct sway_container *sibling = next->children->items[i]; |
126 | swayc_t *sibling = parent->children->items[i]; | 74 | |
127 | double pixels = amount; | 75 | int sibling_pos = parallel_coord(sibling, axis); |
128 | bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y; | 76 | int focused_pos = parallel_coord(focused, axis); |
129 | bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y; | 77 | int parent_pos = parallel_coord(parent, axis); |
130 | if (is_before || is_after) { | 78 | |
131 | pixels = -pixels; | 79 | if (sibling_pos != focused_pos) { |
132 | pixels /= is_before ? nb_before : nb_after; | 80 | if (sibling_pos < parent_pos) { |
133 | if (nb_after != 0 && nb_before != 0) { | 81 | minor_weight++; |
134 | pixels /= 2; | 82 | } else if (sibling_pos > parent_pos) { |
135 | } | 83 | major_weight++; |
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 | } | 84 | } |
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 | } | 85 | } |
190 | } | 86 | } |
191 | i = next_i; | 87 | if (major_weight || minor_weight) { |
88 | break; | ||
89 | } | ||
192 | } | 90 | } |
193 | // Recursive resize does not handle positions, let arrange_windows | 91 | parent = next; |
194 | // take care of that. | ||
195 | arrange_windows(swayc_active_workspace(), -1, -1); | ||
196 | } | 92 | } |
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 | 93 | ||
204 | if (use_width) { | 94 | if (parent->type == C_ROOT) { |
205 | desired = amount - focused->width; | 95 | return; |
206 | } else { | ||
207 | desired = amount - focused->height; | ||
208 | } | 96 | } |
209 | 97 | ||
210 | return resize_tiled(desired, use_width); | 98 | wlr_log(L_DEBUG, |
211 | } | 99 | "Found the proper parent: %p. It has %d l conts, and %d r conts", |
212 | 100 | parent->parent, minor_weight, major_weight); | |
213 | static bool set_size(int dimension, bool use_width) { | ||
214 | swayc_t *focused = current_container; | ||
215 | 101 | ||
216 | if (focused) { | 102 | int min_sane = axis == RESIZE_AXIS_HORIZONTAL ? MIN_SANE_W : MIN_SANE_H; |
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 | 103 | ||
224 | return false; | 104 | //TODO: Ensure rounding is done in such a way that there are NO pixel leaks |
225 | } | 105 | // ^ ????? |
226 | 106 | ||
227 | static bool resize(int dimension, bool use_width, enum resize_dim_types dim_type) { | 107 | for (int i = 0; i < parent->parent->children->length; i++) { |
228 | swayc_t *focused = current_container; | 108 | struct sway_container *sibling = parent->parent->children->items[i]; |
229 | 109 | ||
230 | // translate "10 ppt" (10%) to appropriate # of pixels in case we need it | 110 | int sibling_pos = parallel_coord(sibling, axis); |
231 | float ppt_dim = (float)dimension / 100; | 111 | int focused_pos = parallel_coord(focused, axis); |
112 | int parent_pos = parallel_coord(parent, axis); | ||
232 | 113 | ||
233 | if (use_width) { | 114 | int sibling_size = parallel_size(sibling, axis); |
234 | ppt_dim = focused->width * ppt_dim; | 115 | int parent_size = parallel_size(parent, axis); |
235 | } else { | ||
236 | ppt_dim = focused->height * ppt_dim; | ||
237 | } | ||
238 | 116 | ||
239 | if (focused) { | 117 | if (sibling_pos != focused_pos) { |
240 | if (focused->is_floating) { | 118 | if (sibling_pos < parent_pos) { |
241 | // floating view resize dimensions should default to px, so only | 119 | double pixels = -amount / minor_weight; |
242 | // use ppt if specified | 120 | if (major_weight && (sibling_size + pixels / 2) < min_sane) { |
243 | if (dim_type == RESIZE_DIM_PPT) { | 121 | return; // Too small |
244 | dimension = (int)ppt_dim; | 122 | } else if ((sibling_size + pixels) < min_sane) { |
123 | return; // Too small | ||
124 | } | ||
125 | } else if (sibling_pos > parent_pos) { | ||
126 | double pixels = -amount / major_weight; | ||
127 | if (minor_weight && (sibling_size + pixels / 2) < min_sane) { | ||
128 | return; // Too small | ||
129 | } else if ((sibling_size + pixels) < min_sane) { | ||
130 | return; // Too small | ||
131 | } | ||
245 | } | 132 | } |
246 | |||
247 | return resize_floating(dimension, use_width); | ||
248 | } else { | 133 | } else { |
249 | // tiled view resize dimensions should default to ppt, so only use | 134 | double pixels = amount; |
250 | // px if specified | 135 | if (parent_size + pixels < min_sane) { |
251 | if (dim_type != RESIZE_DIM_PX) { | 136 | return; // Too small |
252 | dimension = (int)ppt_dim; | ||
253 | } | 137 | } |
254 | |||
255 | return resize_tiled(dimension, use_width); | ||
256 | } | 138 | } |
257 | } | 139 | } |
258 | 140 | ||
259 | return false; | 141 | enum resize_edge minor_edge = axis == RESIZE_AXIS_HORIZONTAL ? |
260 | } | 142 | RESIZE_EDGE_LEFT : RESIZE_EDGE_TOP; |
143 | enum resize_edge major_edge = axis == RESIZE_AXIS_HORIZONTAL ? | ||
144 | RESIZE_EDGE_RIGHT : RESIZE_EDGE_BOTTOM; | ||
261 | 145 | ||
262 | static struct cmd_results *cmd_resize_set(int argc, char **argv) { | 146 | for (int i = 0; i < parent->parent->children->length; i++) { |
263 | struct cmd_results *error = NULL; | 147 | struct sway_container *sibling = parent->parent->children->items[i]; |
264 | if ((error = checkarg(argc, "resize set", EXPECTED_AT_LEAST, 2))) { | ||
265 | return error; | ||
266 | } | ||
267 | 148 | ||
268 | if (strcasecmp(argv[0], "width") == 0 || strcasecmp(argv[0], "height") == 0) { | 149 | int sibling_pos = parallel_coord(sibling, axis); |
269 | // handle `reset set width 100 px height 100 px` syntax, also allows | 150 | int focused_pos = parallel_coord(focused, axis); |
270 | // specifying only one dimension for a `resize set` | 151 | int parent_pos = parallel_coord(parent, axis); |
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 | 152 | ||
282 | if (strcasecmp(argv[cmd_num], "width") == 0) { | 153 | if (sibling_pos != focused_pos) { |
283 | set_size(dim, true); | 154 | if (sibling_pos < parent_pos) { |
284 | } else if (strcasecmp(argv[cmd_num], "height") == 0) { | 155 | double pixels = -1 * amount; |
285 | set_size(dim, false); | 156 | pixels /= minor_weight; |
286 | } else { | 157 | if (major_weight) { |
287 | return cmd_results_new(CMD_INVALID, "resize set", | 158 | container_recursive_resize(sibling, pixels / 2, major_edge); |
288 | "Expected 'resize set <width|height> <amount> [px] [<width|height> <amount> [px]]'"); | 159 | } else { |
160 | container_recursive_resize(sibling, pixels, major_edge); | ||
161 | } | ||
162 | } else if (sibling_pos > parent_pos) { | ||
163 | double pixels = -1 * amount; | ||
164 | pixels /= major_weight; | ||
165 | if (minor_weight) { | ||
166 | container_recursive_resize(sibling, pixels / 2, minor_edge); | ||
167 | } else { | ||
168 | container_recursive_resize(sibling, pixels, minor_edge); | ||
169 | } | ||
289 | } | 170 | } |
290 | 171 | } else { | |
291 | cmd_num += 2; | 172 | if (major_weight != 0 && minor_weight != 0) { |
292 | 173 | double pixels = amount; | |
293 | if (cmd_num < argc && strcasecmp(argv[cmd_num], "px") == 0) { | 174 | pixels /= 2; |
294 | // if this was `resize set width 400 px height 300 px`, disregard the `px` arg | 175 | container_recursive_resize(parent, pixels, minor_edge); |
295 | cmd_num++; | 176 | container_recursive_resize(parent, pixels, major_edge); |
177 | } else if (major_weight) { | ||
178 | container_recursive_resize(parent, amount, major_edge); | ||
179 | } else if (minor_weight) { | ||
180 | container_recursive_resize(parent, amount, minor_edge); | ||
296 | } | 181 | } |
297 | } | 182 | } |
298 | } else { | 183 | } |
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 | 184 | ||
307 | int height_arg = 1; | 185 | arrange_windows(parent->parent, -1, -1); |
308 | if (strcasecmp(argv[1], "px") == 0) { | 186 | } |
309 | height_arg = 2; | ||
310 | } | ||
311 | 187 | ||
312 | int height = (int)strtol(argv[height_arg], NULL, 10); | 188 | static void resize(int amount, enum resize_axis axis, enum resize_unit unit) { |
313 | if (errno == ERANGE || height == 0) { | 189 | struct sway_container *current = config->handler_context.current_container; |
314 | errno = 0; | 190 | if (unit == RESIZE_UNIT_DEFAULT) { |
315 | return cmd_results_new(CMD_INVALID, "resize set", | 191 | // Default for tiling; TODO floating should be px |
316 | "Expected 'resize set <width> [px] <height> [px]'"); | 192 | unit = RESIZE_UNIT_PPT; |
317 | } | 193 | } |
318 | 194 | ||
319 | set_size(width, true); | 195 | if (unit == RESIZE_UNIT_PPT) { |
320 | set_size(height, false); | 196 | float pct = amount / 100.0f; |
197 | switch (axis) { | ||
198 | case RESIZE_AXIS_HORIZONTAL: | ||
199 | amount = (float)current->width * pct; | ||
200 | break; | ||
201 | case RESIZE_AXIS_VERTICAL: | ||
202 | amount = (float)current->height * pct; | ||
203 | break; | ||
204 | default: | ||
205 | sway_assert(0, "invalid resize axis"); | ||
206 | return; | ||
207 | } | ||
321 | } | 208 | } |
322 | 209 | ||
323 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 210 | return resize_tiled(amount, axis); |
324 | } | 211 | } |
325 | 212 | ||
326 | struct cmd_results *cmd_resize(int argc, char **argv) { | 213 | struct cmd_results *cmd_resize(int argc, char **argv) { |
327 | struct cmd_results *error = NULL; | 214 | struct sway_container *current = config->handler_context.current_container; |
328 | if (config->reading) return cmd_results_new(CMD_FAILURE, "resize", "Can't be used in config file."); | 215 | if (!current) { |
329 | if (!config->active) return cmd_results_new(CMD_FAILURE, "resize", "Can only be used when sway is running."); | 216 | return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); |
330 | 217 | } | |
218 | if (current->type != C_VIEW && current->type != C_CONTAINER) { | ||
219 | return cmd_results_new(CMD_INVALID, "resize", | ||
220 | "Can only resize views/containers"); | ||
221 | } | ||
331 | if (strcasecmp(argv[0], "set") == 0) { | 222 | if (strcasecmp(argv[0], "set") == 0) { |
332 | return cmd_resize_set(argc - 1, &argv[1]); | 223 | // TODO |
224 | //return cmd_resize_set(argc - 1, &argv[1]); | ||
225 | return cmd_results_new(CMD_INVALID, "resize", "resize set unimplemented"); | ||
333 | } | 226 | } |
334 | 227 | struct cmd_results *error; | |
335 | if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { | 228 | if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { |
336 | return error; | 229 | return error; |
337 | } | 230 | } |
338 | 231 | ||
339 | int dim_arg = argc - 1; | 232 | // TODO: resize grow|shrink left|right|up|down |
340 | 233 | ||
341 | enum resize_dim_types dim_type = RESIZE_DIM_DEFAULT; | 234 | const char *usage = "Expected 'resize <shrink|grow> " |
342 | if (strcasecmp(argv[dim_arg], "ppt") == 0) { | 235 | "<width|height> [<amount>] [px|ppt]'"; |
343 | dim_type = RESIZE_DIM_PPT; | 236 | |
344 | dim_arg--; | 237 | int multiplier = 0; |
345 | } else if (strcasecmp(argv[dim_arg], "px") == 0) { | 238 | if (strcasecmp(*argv, "grow") == 0) { |
346 | dim_type = RESIZE_DIM_PX; | 239 | multiplier = 1; |
347 | dim_arg--; | 240 | } else if (strcasecmp(*argv, "shrink") == 0) { |
241 | multiplier = -1; | ||
242 | } else { | ||
243 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
348 | } | 244 | } |
245 | --argc; ++argv; | ||
349 | 246 | ||
350 | int amount = (int)strtol(argv[dim_arg], NULL, 10); | 247 | enum resize_axis axis = parse_resize_axis(*argv); |
351 | if (errno == ERANGE || amount == 0) { | 248 | if (axis == RESIZE_AXIS_INVALID) { |
352 | errno = 0; | 249 | return cmd_results_new(CMD_INVALID, "resize", usage); |
353 | amount = 10; // this is the default resize dimension used by i3 for both px and ppt | 250 | } |
354 | sway_log(L_DEBUG, "Tried to get resize dimension out of '%s' but failed; setting dimension to default %d", | 251 | --argc; ++argv; |
355 | argv[dim_arg], amount); | 252 | |
253 | int amount = 10; // Default amount | ||
254 | enum resize_unit unit = RESIZE_UNIT_DEFAULT; | ||
255 | |||
256 | if (argc) { | ||
257 | char *err; | ||
258 | amount = (int)strtol(*argv, &err, 10); | ||
259 | if (*err) { | ||
260 | // e.g. `resize grow width 10px` | ||
261 | unit = parse_resize_unit(err); | ||
262 | if (unit == RESIZE_UNIT_INVALID) { | ||
263 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
264 | } | ||
265 | } | ||
266 | --argc; ++argv; | ||
356 | } | 267 | } |
357 | 268 | ||
358 | bool use_width = false; | 269 | if (argc) { |
359 | if (strcasecmp(argv[1], "width") == 0) { | 270 | unit = parse_resize_unit(*argv); |
360 | use_width = true; | 271 | if (unit == RESIZE_UNIT_INVALID) { |
361 | } else if (strcasecmp(argv[1], "height") != 0) { | 272 | return cmd_results_new(CMD_INVALID, "resize", usage); |
362 | return cmd_results_new(CMD_INVALID, "resize", | 273 | } |
363 | "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'"); | 274 | --argc; ++argv; |
364 | } | 275 | } |
365 | 276 | ||
366 | if (strcasecmp(argv[0], "shrink") == 0) { | 277 | if (argc) { |
367 | amount *= -1; | 278 | // Provied too many args, the bastard |
368 | } else if (strcasecmp(argv[0], "grow") != 0) { | 279 | return cmd_results_new(CMD_INVALID, "resize", usage); |
369 | return cmd_results_new(CMD_INVALID, "resize", | ||
370 | "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'"); | ||
371 | } | 280 | } |
372 | 281 | ||
373 | resize(amount, use_width, dim_type); | 282 | resize(amount * multiplier, axis, unit); |
374 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 283 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
375 | } | 284 | } |
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c deleted file mode 100644 index 6c5c92df..00000000 --- a/sway/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/commands/seamless_mouse.c b/sway/commands/seamless_mouse.c deleted file mode 100644 index 7760e88d..00000000 --- a/sway/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/commands/seat.c b/sway/commands/seat.c new file mode 100644 index 00000000..5916015f --- /dev/null +++ b/sway/commands/seat.c | |||
@@ -0,0 +1,61 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/input/input-manager.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | struct cmd_results *cmd_seat(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 2))) { | ||
10 | return error; | ||
11 | } | ||
12 | |||
13 | if (config->reading && strcmp("{", argv[1]) == 0) { | ||
14 | free_seat_config(config->handler_context.seat_config); | ||
15 | config->handler_context.seat_config = new_seat_config(argv[0]); | ||
16 | if (!config->handler_context.seat_config) { | ||
17 | return cmd_results_new(CMD_FAILURE, NULL, | ||
18 | "Couldn't allocate config"); | ||
19 | } | ||
20 | wlr_log(L_DEBUG, "entering seat block: %s", argv[0]); | ||
21 | return cmd_results_new(CMD_BLOCK_SEAT, NULL, NULL); | ||
22 | } | ||
23 | |||
24 | if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 3))) { | ||
25 | return error; | ||
26 | } | ||
27 | |||
28 | bool has_context = (config->handler_context.seat_config != NULL); | ||
29 | if (!has_context) { | ||
30 | config->handler_context.seat_config = new_seat_config(argv[0]); | ||
31 | if (!config->handler_context.seat_config) { | ||
32 | return cmd_results_new(CMD_FAILURE, NULL, | ||
33 | "Couldn't allocate config"); | ||
34 | } | ||
35 | } | ||
36 | |||
37 | int argc_new = argc-2; | ||
38 | char **argv_new = argv+2; | ||
39 | |||
40 | struct cmd_results *res; | ||
41 | if (strcasecmp("attach", argv[1]) == 0) { | ||
42 | res = seat_cmd_attach(argc_new, argv_new); | ||
43 | } else if (strcasecmp("cursor", argv[1]) == 0) { | ||
44 | res = seat_cmd_cursor(argc_new, argv_new); | ||
45 | } else if (strcasecmp("fallback", argv[1]) == 0) { | ||
46 | res = seat_cmd_fallback(argc_new, argv_new); | ||
47 | } else { | ||
48 | res = | ||
49 | cmd_results_new(CMD_INVALID, | ||
50 | "seat <name>", "Unknown command %s", | ||
51 | argv[1]); | ||
52 | } | ||
53 | |||
54 | if (!has_context) { | ||
55 | // clean up the context we created earlier | ||
56 | free_seat_config(config->handler_context.seat_config); | ||
57 | config->handler_context.seat_config = NULL; | ||
58 | } | ||
59 | |||
60 | return res; | ||
61 | } | ||
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c new file mode 100644 index 00000000..3e771c00 --- /dev/null +++ b/sway/commands/seat/attach.c | |||
@@ -0,0 +1,28 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include <string.h> | ||
3 | #include <strings.h> | ||
4 | #include "sway/input/input-manager.h" | ||
5 | #include "sway/commands.h" | ||
6 | #include "sway/config.h" | ||
7 | #include "log.h" | ||
8 | #include "stringop.h" | ||
9 | |||
10 | struct cmd_results *seat_cmd_attach(int argc, char **argv) { | ||
11 | struct cmd_results *error = NULL; | ||
12 | if ((error = checkarg(argc, "attach", EXPECTED_AT_LEAST, 1))) { | ||
13 | return error; | ||
14 | } | ||
15 | struct seat_config *current_seat_config = | ||
16 | config->handler_context.seat_config; | ||
17 | if (!current_seat_config) { | ||
18 | return cmd_results_new(CMD_FAILURE, "attach", "No seat defined"); | ||
19 | } | ||
20 | |||
21 | struct seat_config *new_config = new_seat_config(current_seat_config->name); | ||
22 | struct seat_attachment_config *new_attachment = seat_attachment_config_new(); | ||
23 | new_attachment->identifier = strdup(argv[0]); | ||
24 | list_add(new_config->attachments, new_attachment); | ||
25 | |||
26 | apply_seat_config(new_config); | ||
27 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
28 | } | ||
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c new file mode 100644 index 00000000..5dad97f1 --- /dev/null +++ b/sway/commands/seat/cursor.c | |||
@@ -0,0 +1,89 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #ifdef __linux__ | ||
3 | #include <linux/input-event-codes.h> | ||
4 | #elif __FreeBSD__ | ||
5 | #include <dev/evdev/input-event-codes.h> | ||
6 | #endif | ||
7 | |||
8 | #include <strings.h> | ||
9 | #include <wlr/types/wlr_cursor.h> | ||
10 | #include "sway/commands.h" | ||
11 | #include "sway/input/cursor.h" | ||
12 | |||
13 | static struct cmd_results *press_or_release(struct sway_cursor *cursor, | ||
14 | char *action, char *button_str, uint32_t time); | ||
15 | |||
16 | static const char *expected_syntax = "Expected 'cursor <move> <x> <y>' or " | ||
17 | "'cursor <set> <x> <y>' or " | ||
18 | "'curor <press|release> <left|right|1|2|3...>'"; | ||
19 | |||
20 | struct cmd_results *seat_cmd_cursor(int argc, char **argv) { | ||
21 | struct cmd_results *error = NULL; | ||
22 | if ((error = checkarg(argc, "cursor", EXPECTED_AT_LEAST, 2))) { | ||
23 | return error; | ||
24 | } | ||
25 | struct sway_seat *seat = config->handler_context.seat; | ||
26 | if (!seat) { | ||
27 | return cmd_results_new(CMD_FAILURE, "cursor", "No seat defined"); | ||
28 | } | ||
29 | |||
30 | struct sway_cursor *cursor = seat->cursor; | ||
31 | |||
32 | struct timespec now; | ||
33 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
34 | uint32_t time = now.tv_nsec / 1000; | ||
35 | |||
36 | if (strcasecmp(argv[0], "move") == 0) { | ||
37 | if (argc < 3) { | ||
38 | return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); | ||
39 | } | ||
40 | int delta_x = strtol(argv[1], NULL, 10); | ||
41 | int delta_y = strtol(argv[2], NULL, 10); | ||
42 | wlr_cursor_move(cursor->cursor, NULL, delta_x, delta_y); | ||
43 | cursor_send_pointer_motion(cursor, time); | ||
44 | } else if (strcasecmp(argv[0], "set") == 0) { | ||
45 | if (argc < 3) { | ||
46 | return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); | ||
47 | } | ||
48 | // map absolute coords (0..1,0..1) to root container coords | ||
49 | float x = strtof(argv[1], NULL) / root_container.width; | ||
50 | float y = strtof(argv[2], NULL) / root_container.height; | ||
51 | wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y); | ||
52 | cursor_send_pointer_motion(cursor, time); | ||
53 | } else { | ||
54 | if (argc < 2) { | ||
55 | return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); | ||
56 | } | ||
57 | if ((error = press_or_release(cursor, argv[0], argv[1], time))) { | ||
58 | return error; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
63 | } | ||
64 | |||
65 | static struct cmd_results *press_or_release(struct sway_cursor *cursor, | ||
66 | char *action, char *button_str, uint32_t time) { | ||
67 | enum wlr_button_state state; | ||
68 | uint32_t button; | ||
69 | if (strcasecmp(action, "press") == 0) { | ||
70 | state = WLR_BUTTON_PRESSED; | ||
71 | } else if (strcasecmp(action, "release") == 0) { | ||
72 | state = WLR_BUTTON_RELEASED; | ||
73 | } else { | ||
74 | return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); | ||
75 | } | ||
76 | |||
77 | if (strcasecmp(button_str, "left") == 0) { | ||
78 | button = BTN_LEFT; | ||
79 | } else if (strcasecmp(button_str, "right") == 0) { | ||
80 | button = BTN_RIGHT; | ||
81 | } else { | ||
82 | button = strtol(button_str, NULL, 10); | ||
83 | if (button == 0) { | ||
84 | return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); | ||
85 | } | ||
86 | } | ||
87 | dispatch_cursor_button(cursor, time, button, state); | ||
88 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
89 | } | ||
diff --git a/sway/commands/seat/fallback.c b/sway/commands/seat/fallback.c new file mode 100644 index 00000000..56feaab5 --- /dev/null +++ b/sway/commands/seat/fallback.c | |||
@@ -0,0 +1,32 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/commands.h" | ||
5 | #include "sway/input/input-manager.h" | ||
6 | |||
7 | struct cmd_results *seat_cmd_fallback(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "fallback", EXPECTED_AT_LEAST, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | struct seat_config *current_seat_config = | ||
13 | config->handler_context.seat_config; | ||
14 | if (!current_seat_config) { | ||
15 | return cmd_results_new(CMD_FAILURE, "fallback", "No seat defined"); | ||
16 | } | ||
17 | struct seat_config *new_config = | ||
18 | new_seat_config(current_seat_config->name); | ||
19 | |||
20 | if (strcasecmp(argv[0], "true") == 0) { | ||
21 | new_config->fallback = 1; | ||
22 | } else if (strcasecmp(argv[0], "false") == 0) { | ||
23 | new_config->fallback = 0; | ||
24 | } else { | ||
25 | free_seat_config(new_config); | ||
26 | return cmd_results_new(CMD_INVALID, "fallback", | ||
27 | "Expected 'fallback <true|false>'"); | ||
28 | } | ||
29 | |||
30 | apply_seat_config(new_config); | ||
31 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
32 | } | ||
diff --git a/sway/commands/set.c b/sway/commands/set.c index 46fc6d38..84e9b792 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "sway/commands.h" | 5 | #include "sway/commands.h" |
6 | #include "sway/config.h" | 6 | #include "sway/config.h" |
7 | #include "list.h" | 7 | #include "list.h" |
8 | #include "log.h" | ||
8 | #include "stringop.h" | 9 | #include "stringop.h" |
9 | 10 | ||
10 | // sort in order of longest->shortest | 11 | // sort in order of longest->shortest |
@@ -14,16 +15,24 @@ static int compare_set_qsort(const void *_l, const void *_r) { | |||
14 | return strlen(r->name) - strlen(l->name); | 15 | return strlen(r->name) - strlen(l->name); |
15 | } | 16 | } |
16 | 17 | ||
18 | void free_sway_variable(struct sway_variable *var) { | ||
19 | if (!var) { | ||
20 | return; | ||
21 | } | ||
22 | free(var->name); | ||
23 | free(var->value); | ||
24 | free(var); | ||
25 | } | ||
26 | |||
17 | struct cmd_results *cmd_set(int argc, char **argv) { | 27 | struct cmd_results *cmd_set(int argc, char **argv) { |
18 | char *tmp; | 28 | char *tmp; |
19 | struct cmd_results *error = NULL; | 29 | 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))) { | 30 | if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) { |
22 | return error; | 31 | return error; |
23 | } | 32 | } |
24 | 33 | ||
25 | if (argv[0][0] != '$') { | 34 | if (argv[0][0] != '$') { |
26 | sway_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]); | 35 | wlr_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]); |
27 | 36 | ||
28 | size_t size = snprintf(NULL, 0, "$%s", argv[0]); | 37 | size_t size = snprintf(NULL, 0, "$%s", argv[0]); |
29 | tmp = malloc(size + 1); | 38 | tmp = malloc(size + 1); |
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c deleted file mode 100644 index ed56d9e5..00000000 --- a/sway/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/commands/smart_gaps.c b/sway/commands/smart_gaps.c deleted file mode 100644 index 815fc501..00000000 --- a/sway/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/commands/split.c b/sway/commands/split.c index e3045a4f..130ed31f 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c | |||
@@ -1,76 +1,41 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | 2 | #include <strings.h> |
3 | #include <wlc/wlc-render.h> | ||
4 | #include "sway/border.h" | ||
5 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
6 | #include "sway/container.h" | 4 | #include "sway/tree/container.h" |
7 | #include "sway/focus.h" | 5 | #include "sway/tree/layout.h" |
8 | #include "sway/layout.h" | 6 | #include "sway/tree/view.h" |
7 | #include "sway/input/input-manager.h" | ||
8 | #include "sway/input/seat.h" | ||
9 | #include "log.h" | 9 | #include "log.h" |
10 | 10 | ||
11 | static struct cmd_results *_do_split(int argc, char **argv, int layout) { | 11 | static struct cmd_results *do_split(int layout) { |
12 | char *name = layout == L_VERT ? "splitv" : | 12 | struct sway_container *con = config->handler_context.current_container; |
13 | layout == L_HORIZ ? "splith" : "split"; | 13 | struct sway_container *parent = container_split(con, layout); |
14 | struct cmd_results *error = NULL; | 14 | container_create_notify(parent); |
15 | if (config->reading) return cmd_results_new(CMD_FAILURE, name, "Can't be used in config file."); | 15 | arrange_windows(parent, -1, -1); |
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 | 16 | ||
54 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 17 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
55 | } | 18 | } |
56 | 19 | ||
57 | struct cmd_results *cmd_split(int argc, char **argv) { | 20 | struct cmd_results *cmd_split(int argc, char **argv) { |
58 | struct cmd_results *error = NULL; | 21 | 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))) { | 22 | if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { |
62 | return error; | 23 | return error; |
63 | } | 24 | } |
64 | if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) { | 25 | if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) { |
65 | _do_split(argc - 1, argv + 1, L_VERT); | 26 | do_split(L_VERT); |
66 | } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) { | 27 | } else if (strcasecmp(argv[0], "h") == 0 || |
67 | _do_split(argc - 1, argv + 1, L_HORIZ); | 28 | strcasecmp(argv[0], "horizontal") == 0) { |
68 | } else if (strcasecmp(argv[0], "t") == 0 || strcasecmp(argv[0], "toggle") == 0) { | 29 | do_split(L_HORIZ); |
69 | swayc_t *focused = current_container; | 30 | } else if (strcasecmp(argv[0], "t") == 0 || |
31 | strcasecmp(argv[0], "toggle") == 0) { | ||
32 | struct sway_container *focused = | ||
33 | config->handler_context.current_container; | ||
34 | |||
70 | if (focused->parent->layout == L_VERT) { | 35 | if (focused->parent->layout == L_VERT) { |
71 | _do_split(argc - 1, argv + 1, L_HORIZ); | 36 | do_split(L_HORIZ); |
72 | } else { | 37 | } else { |
73 | _do_split(argc - 1, argv + 1, L_VERT); | 38 | do_split(L_VERT); |
74 | } | 39 | } |
75 | } else { | 40 | } else { |
76 | error = cmd_results_new(CMD_FAILURE, "split", | 41 | error = cmd_results_new(CMD_FAILURE, "split", |
@@ -81,18 +46,32 @@ struct cmd_results *cmd_split(int argc, char **argv) { | |||
81 | } | 46 | } |
82 | 47 | ||
83 | struct cmd_results *cmd_splitv(int argc, char **argv) { | 48 | struct cmd_results *cmd_splitv(int argc, char **argv) { |
84 | return _do_split(argc, argv, L_VERT); | 49 | struct cmd_results *error = NULL; |
50 | if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) { | ||
51 | return error; | ||
52 | } | ||
53 | return do_split(L_VERT); | ||
85 | } | 54 | } |
86 | 55 | ||
87 | struct cmd_results *cmd_splith(int argc, char **argv) { | 56 | struct cmd_results *cmd_splith(int argc, char **argv) { |
88 | return _do_split(argc, argv, L_HORIZ); | 57 | struct cmd_results *error = NULL; |
58 | if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) { | ||
59 | return error; | ||
60 | } | ||
61 | return do_split(L_HORIZ); | ||
89 | } | 62 | } |
90 | 63 | ||
91 | struct cmd_results *cmd_splitt(int argc, char **argv) { | 64 | struct cmd_results *cmd_splitt(int argc, char **argv) { |
92 | swayc_t *focused = current_container; | 65 | struct cmd_results *error = NULL; |
93 | if (focused->parent->layout == L_VERT) { | 66 | if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) { |
94 | return _do_split(argc, argv, L_HORIZ); | 67 | return error; |
68 | } | ||
69 | |||
70 | struct sway_container *con = config->handler_context.current_container; | ||
71 | |||
72 | if (con->parent->layout == L_VERT) { | ||
73 | return do_split(L_HORIZ); | ||
95 | } else { | 74 | } else { |
96 | return _do_split(argc, argv, L_VERT); | 75 | return do_split(L_VERT); |
97 | } | 76 | } |
98 | } | 77 | } |
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c deleted file mode 100644 index 4899e061..00000000 --- a/sway/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/commands/swaybg_command.c b/sway/commands/swaybg_command.c new file mode 100644 index 00000000..770d4821 --- /dev/null +++ b/sway/commands/swaybg_command.c | |||
@@ -0,0 +1,20 @@ | |||
1 | #include <string.h> | ||
2 | #include "sway/commands.h" | ||
3 | #include "log.h" | ||
4 | #include "stringop.h" | ||
5 | |||
6 | struct cmd_results *cmd_swaybg_command(int argc, char **argv) { | ||
7 | struct cmd_results *error = NULL; | ||
8 | if ((error = checkarg(argc, "swaybg_command", EXPECTED_AT_LEAST, 1))) { | ||
9 | return error; | ||
10 | } | ||
11 | |||
12 | if (config->swaybg_command) { | ||
13 | free(config->swaybg_command); | ||
14 | } | ||
15 | config->swaybg_command = join_args(argv, argc); | ||
16 | wlr_log(L_DEBUG, "Using custom swaybg command: %s", | ||
17 | config->swaybg_command); | ||
18 | |||
19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
20 | } | ||
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c deleted file mode 100644 index ac213261..00000000 --- a/sway/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/commands/workspace.c b/sway/commands/workspace.c index a7839746..958b3222 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c | |||
@@ -3,8 +3,8 @@ | |||
3 | #include <strings.h> | 3 | #include <strings.h> |
4 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
5 | #include "sway/config.h" | 5 | #include "sway/config.h" |
6 | #include "sway/input_state.h" | 6 | #include "sway/input/seat.h" |
7 | #include "sway/workspace.h" | 7 | #include "sway/tree/workspace.h" |
8 | #include "list.h" | 8 | #include "list.h" |
9 | #include "log.h" | 9 | #include "log.h" |
10 | #include "stringop.h" | 10 | #include "stringop.h" |
@@ -17,6 +17,17 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
17 | 17 | ||
18 | int output_location = -1; | 18 | int output_location = -1; |
19 | 19 | ||
20 | struct sway_container *current_container = config->handler_context.current_container; | ||
21 | struct sway_container *old_workspace = NULL, *old_output = NULL; | ||
22 | if (current_container) { | ||
23 | if (current_container->type == C_WORKSPACE) { | ||
24 | old_workspace = current_container; | ||
25 | } else { | ||
26 | old_workspace = container_parent(current_container, C_WORKSPACE); | ||
27 | } | ||
28 | old_output = container_parent(current_container, C_OUTPUT); | ||
29 | } | ||
30 | |||
20 | for (int i = 0; i < argc; ++i) { | 31 | for (int i = 0; i < argc; ++i) { |
21 | if (strcasecmp(argv[i], "output") == 0) { | 32 | if (strcasecmp(argv[i], "output") == 0) { |
22 | output_location = i; | 33 | output_location = i; |
@@ -40,52 +51,51 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
40 | free(old); // workspaces can only be assigned to a single output | 51 | free(old); // workspaces can only be assigned to a single output |
41 | list_del(config->workspace_outputs, i); | 52 | list_del(config->workspace_outputs, i); |
42 | } | 53 | } |
43 | sway_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); | 54 | wlr_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); |
44 | list_add(config->workspace_outputs, wso); | 55 | list_add(config->workspace_outputs, wso); |
45 | } else { | 56 | } else { |
46 | if (config->reading || !config->active) { | 57 | if (config->reading || !config->active) { |
47 | return cmd_results_new(CMD_DEFER, "workspace", NULL); | 58 | return cmd_results_new(CMD_DEFER, "workspace", NULL); |
48 | } | 59 | } |
49 | swayc_t *ws = NULL; | 60 | struct sway_container *ws = NULL; |
50 | if (strcasecmp(argv[0], "number") == 0) { | 61 | if (strcasecmp(argv[0], "number") == 0) { |
51 | if (!(ws = workspace_by_number(argv[1]))) { | 62 | if (!(ws = workspace_by_number(argv[1]))) { |
52 | char *name = join_args(argv + 1, argc - 1); | 63 | char *name = join_args(argv + 1, argc - 1); |
53 | ws = workspace_create(name); | 64 | ws = workspace_create(NULL, name); |
54 | free(name); | 65 | free(name); |
55 | } | 66 | } |
56 | } else if (strcasecmp(argv[0], "next") == 0) { | 67 | } else if (strcasecmp(argv[0], "next") == 0) { |
57 | ws = workspace_next(); | 68 | ws = workspace_next(old_workspace); |
58 | } else if (strcasecmp(argv[0], "prev") == 0) { | 69 | } else if (strcasecmp(argv[0], "prev") == 0) { |
59 | ws = workspace_prev(); | 70 | ws = workspace_prev(old_workspace); |
60 | } else if (strcasecmp(argv[0], "next_on_output") == 0) { | 71 | } else if (strcasecmp(argv[0], "next_on_output") == 0) { |
61 | ws = workspace_output_next(); | 72 | ws = workspace_output_next(old_output); |
62 | } else if (strcasecmp(argv[0], "prev_on_output") == 0) { | 73 | } else if (strcasecmp(argv[0], "prev_on_output") == 0) { |
63 | ws = workspace_output_prev(); | 74 | ws = workspace_output_prev(old_output); |
64 | } else if (strcasecmp(argv[0], "back_and_forth") == 0) { | 75 | } else if (strcasecmp(argv[0], "back_and_forth") == 0) { |
65 | // if auto_back_and_forth is enabled, workspace_switch will swap | 76 | // if auto_back_and_forth is enabled, workspace_switch will swap |
66 | // the workspaces. If we created prev_workspace here, workspace_switch | 77 | // the workspaces. If we created prev_workspace here, workspace_switch |
67 | // would put us back on original workspace. | 78 | // would put us back on original workspace. |
68 | if (config->auto_back_and_forth) { | 79 | if (config->auto_back_and_forth) { |
69 | ws = swayc_active_workspace(); | 80 | ws = old_workspace; |
70 | } else if (prev_workspace_name && !(ws = workspace_by_name(prev_workspace_name))) { | 81 | } else if (prev_workspace_name |
71 | ws = workspace_create(prev_workspace_name); | 82 | && !(ws = workspace_by_name(prev_workspace_name))) { |
83 | ws = workspace_create(NULL, prev_workspace_name); | ||
72 | } | 84 | } |
73 | } else { | 85 | } else { |
74 | char *name = join_args(argv, argc); | 86 | char *name = join_args(argv, argc); |
75 | if (!(ws = workspace_by_name(name))) { | 87 | if (!(ws = workspace_by_name(name))) { |
76 | ws = workspace_create(name); | 88 | ws = workspace_create(NULL, name); |
77 | } | 89 | } |
78 | free(name); | 90 | free(name); |
79 | } | 91 | } |
80 | swayc_t *old_output = swayc_active_output(); | ||
81 | workspace_switch(ws); | 92 | workspace_switch(ws); |
82 | swayc_t *new_output = swayc_active_output(); | 93 | current_container = |
94 | seat_get_focus(config->handler_context.seat); | ||
95 | struct sway_container *new_output = container_parent(current_container, C_OUTPUT); | ||
83 | 96 | ||
84 | if (config->mouse_warping && old_output != new_output) { | 97 | if (config->mouse_warping && old_output != new_output) { |
85 | swayc_t *focused = get_focused_view(ws); | 98 | // TODO: Warp mouse |
86 | if (focused && focused->type == C_VIEW) { | ||
87 | center_pointer_on(focused); | ||
88 | } | ||
89 | } | 99 | } |
90 | } | 100 | } |
91 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 101 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/workspace_layout.c b/sway/commands/workspace_layout.c deleted file mode 100644 index 9ac84be2..00000000 --- a/sway/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/commands/workspace_auto_back_and_forth.c b/sway/commands/ws_auto_back_and_forth.c index d58ae5c8..2485db35 100644 --- a/sway/commands/workspace_auto_back_and_forth.c +++ b/sway/commands/ws_auto_back_and_forth.c | |||
@@ -7,12 +7,6 @@ struct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) { | |||
7 | if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) { | 7 | if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) { |
8 | return error; | 8 | return error; |
9 | } | 9 | } |
10 | if (strcasecmp(argv[0], "yes") == 0) { | 10 | config->auto_back_and_forth = !strcasecmp(argv[0], "yes"); |
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); | 11 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
18 | } | 12 | } |
diff --git a/sway/config.c b/sway/config.c index a33b8edc..90b833ab 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -12,17 +12,19 @@ | |||
12 | #include <signal.h> | 12 | #include <signal.h> |
13 | #include <libinput.h> | 13 | #include <libinput.h> |
14 | #include <limits.h> | 14 | #include <limits.h> |
15 | #include <float.h> | ||
16 | #include <dirent.h> | 15 | #include <dirent.h> |
17 | #include <strings.h> | 16 | #include <strings.h> |
18 | #include "wayland-desktop-shell-server-protocol.h" | 17 | #ifdef __linux__ |
18 | #include <linux/input-event-codes.h> | ||
19 | #elif __FreeBSD__ | ||
20 | #include <dev/evdev/input-event-codes.h> | ||
21 | #endif | ||
22 | #include <wlr/types/wlr_output.h> | ||
23 | #include "sway/input/input-manager.h" | ||
24 | #include "sway/input/seat.h" | ||
19 | #include "sway/commands.h" | 25 | #include "sway/commands.h" |
20 | #include "sway/config.h" | 26 | #include "sway/config.h" |
21 | #include "sway/layout.h" | 27 | #include "sway/tree/layout.h" |
22 | #include "sway/input_state.h" | ||
23 | #include "sway/criteria.h" | ||
24 | #include "sway/input.h" | ||
25 | #include "sway/border.h" | ||
26 | #include "readline.h" | 28 | #include "readline.h" |
27 | #include "stringop.h" | 29 | #include "stringop.h" |
28 | #include "list.h" | 30 | #include "list.h" |
@@ -30,269 +32,102 @@ | |||
30 | 32 | ||
31 | struct sway_config *config = NULL; | 33 | struct sway_config *config = NULL; |
32 | 34 | ||
33 | static void terminate_swaybar(pid_t pid); | ||
34 | |||
35 | static void free_variable(struct sway_variable *var) { | ||
36 | if (!var) { | ||
37 | return; | ||
38 | } | ||
39 | free(var->name); | ||
40 | free(var->value); | ||
41 | free(var); | ||
42 | } | ||
43 | |||
44 | static void free_binding(struct sway_binding *bind) { | ||
45 | if (!bind) { | ||
46 | return; | ||
47 | } | ||
48 | free_flat_list(bind->keys); | ||
49 | free(bind->command); | ||
50 | free(bind); | ||
51 | } | ||
52 | |||
53 | static void free_mode(struct sway_mode *mode) { | 35 | static void free_mode(struct sway_mode *mode) { |
54 | if (!mode) { | ||
55 | return; | ||
56 | } | ||
57 | free(mode->name); | ||
58 | int i; | ||
59 | for (i = 0; mode->bindings && i < mode->bindings->length; ++i) { | ||
60 | free_binding(mode->bindings->items[i]); | ||
61 | } | ||
62 | list_free(mode->bindings); | ||
63 | free(mode); | ||
64 | } | ||
65 | |||
66 | static void free_bar(struct bar_config *bar) { | ||
67 | if (!bar) { | ||
68 | return; | ||
69 | } | ||
70 | free(bar->mode); | ||
71 | free(bar->hidden_state); | ||
72 | #ifdef ENABLE_TRAY | ||
73 | free(bar->tray_output); | ||
74 | free(bar->icon_theme); | ||
75 | #endif | ||
76 | free(bar->status_command); | ||
77 | free(bar->font); | ||
78 | free(bar->separator_symbol); | ||
79 | int i; | 36 | int i; |
80 | for (i = 0; bar->bindings && i < bar->bindings->length; ++i) { | ||
81 | free_sway_mouse_binding(bar->bindings->items[i]); | ||
82 | } | ||
83 | list_free(bar->bindings); | ||
84 | |||
85 | if (bar->outputs) { | ||
86 | free_flat_list(bar->outputs); | ||
87 | } | ||
88 | |||
89 | if (bar->pid != 0) { | ||
90 | terminate_swaybar(bar->pid); | ||
91 | } | ||
92 | |||
93 | free(bar->colors.background); | ||
94 | free(bar->colors.statusline); | ||
95 | free(bar->colors.separator); | ||
96 | free(bar->colors.focused_background); | ||
97 | free(bar->colors.focused_statusline); | ||
98 | free(bar->colors.focused_separator); | ||
99 | free(bar->colors.focused_workspace_border); | ||
100 | free(bar->colors.focused_workspace_bg); | ||
101 | free(bar->colors.focused_workspace_text); | ||
102 | free(bar->colors.active_workspace_border); | ||
103 | free(bar->colors.active_workspace_bg); | ||
104 | free(bar->colors.active_workspace_text); | ||
105 | free(bar->colors.inactive_workspace_border); | ||
106 | free(bar->colors.inactive_workspace_bg); | ||
107 | free(bar->colors.inactive_workspace_text); | ||
108 | free(bar->colors.urgent_workspace_border); | ||
109 | free(bar->colors.urgent_workspace_bg); | ||
110 | free(bar->colors.urgent_workspace_text); | ||
111 | free(bar->colors.binding_mode_border); | ||
112 | free(bar->colors.binding_mode_bg); | ||
113 | free(bar->colors.binding_mode_text); | ||
114 | |||
115 | free(bar); | ||
116 | } | ||
117 | 37 | ||
118 | void free_input_config(struct input_config *ic) { | 38 | if (!mode) { |
119 | if (!ic) { | ||
120 | return; | ||
121 | } | ||
122 | free(ic->identifier); | ||
123 | free(ic); | ||
124 | } | ||
125 | |||
126 | void free_output_config(struct output_config *oc) { | ||
127 | if (!oc) { | ||
128 | return; | ||
129 | } | ||
130 | free(oc->name); | ||
131 | free(oc->background); | ||
132 | free(oc->background_option); | ||
133 | free(oc); | ||
134 | } | ||
135 | |||
136 | static void free_workspace_output(struct workspace_output *wo) { | ||
137 | if (!wo) { | ||
138 | return; | 39 | return; |
139 | } | 40 | } |
140 | free(wo->output); | 41 | free(mode->name); |
141 | free(wo->workspace); | 42 | if (mode->keysym_bindings) { |
142 | free(wo); | 43 | for (i = 0; i < mode->keysym_bindings->length; i++) { |
143 | } | 44 | free_sway_binding(mode->keysym_bindings->items[i]); |
144 | |||
145 | static void pid_workspace_cleanup() { | ||
146 | struct timespec ts; | ||
147 | struct pid_workspace *pw = NULL; | ||
148 | |||
149 | clock_gettime(CLOCK_MONOTONIC, &ts); | ||
150 | |||
151 | // work backwards through list and remove any entries | ||
152 | // older than PID_WORKSPACE_TIMEOUT | ||
153 | for (int i = config->pid_workspaces->length - 1; i > -1; i--) { | ||
154 | pw = config->pid_workspaces->items[i]; | ||
155 | |||
156 | if (difftime(ts.tv_sec, *pw->time_added) >= PID_WORKSPACE_TIMEOUT) { | ||
157 | free_pid_workspace(config->pid_workspaces->items[i]); | ||
158 | list_del(config->pid_workspaces, i); | ||
159 | } | 45 | } |
46 | list_free(mode->keysym_bindings); | ||
160 | } | 47 | } |
161 | } | 48 | if (mode->keycode_bindings) { |
162 | 49 | for (i = 0; i < mode->keycode_bindings->length; i++) { | |
163 | // de-dupe pid_workspaces to ensure pid uniqueness | 50 | free_sway_binding(mode->keycode_bindings->items[i]); |
164 | void pid_workspace_add(struct pid_workspace *pw) { | ||
165 | struct pid_workspace *list_pw = NULL; | ||
166 | struct timespec ts; | ||
167 | time_t *now = malloc(sizeof(time_t)); | ||
168 | if (!now) { | ||
169 | sway_log(L_ERROR, "Allocating time for pid_workspace failed"); | ||
170 | return; | ||
171 | } | ||
172 | |||
173 | pid_workspace_cleanup(); | ||
174 | |||
175 | // add current time to pw | ||
176 | clock_gettime(CLOCK_MONOTONIC, &ts); | ||
177 | *now = ts.tv_sec; | ||
178 | |||
179 | pw->time_added = now; | ||
180 | |||
181 | // work backwards through list and delete any entries that | ||
182 | // have the same pid as that in our new pid_workspace | ||
183 | for (int i = config->pid_workspaces->length - 1; i > -1; i--) { | ||
184 | list_pw = config->pid_workspaces->items[i]; | ||
185 | |||
186 | if (pw->pid == list_pw->pid) { | ||
187 | free_pid_workspace(config->pid_workspaces->items[i]); | ||
188 | list_del(config->pid_workspaces, i); | ||
189 | } | 51 | } |
52 | list_free(mode->keycode_bindings); | ||
190 | } | 53 | } |
191 | 54 | free(mode); | |
192 | list_add(config->pid_workspaces, pw); | ||
193 | } | ||
194 | |||
195 | void free_pid_workspace(struct pid_workspace *pw) { | ||
196 | if (!pw) { | ||
197 | return; | ||
198 | } | ||
199 | free(pw->pid); | ||
200 | free(pw->workspace); | ||
201 | free(pw->time_added); | ||
202 | free(pw); | ||
203 | } | ||
204 | |||
205 | void free_command_policy(struct command_policy *policy) { | ||
206 | if (!policy) { | ||
207 | return; | ||
208 | } | ||
209 | free(policy->command); | ||
210 | free(policy); | ||
211 | } | ||
212 | |||
213 | void free_feature_policy(struct feature_policy *policy) { | ||
214 | if (!policy) { | ||
215 | return; | ||
216 | } | ||
217 | free(policy->program); | ||
218 | free(policy); | ||
219 | } | 55 | } |
220 | 56 | ||
221 | void free_config(struct sway_config *config) { | 57 | void free_config(struct sway_config *config) { |
58 | config_clear_handler_context(config); | ||
59 | |||
222 | if (!config) { | 60 | if (!config) { |
223 | return; | 61 | return; |
224 | } | 62 | } |
225 | int i; | ||
226 | for (i = 0; config->symbols && i < config->symbols->length; ++i) { | ||
227 | free_variable(config->symbols->items[i]); | ||
228 | } | ||
229 | list_free(config->symbols); | ||
230 | 63 | ||
231 | for (i = 0; config->modes && i < config->modes->length; ++i) { | 64 | // TODO: handle all currently unhandled lists as we add implementations |
232 | free_mode(config->modes->items[i]); | 65 | if (config->symbols) { |
66 | for (int i = 0; i < config->symbols->length; ++i) { | ||
67 | free_sway_variable(config->symbols->items[i]); | ||
68 | } | ||
69 | list_free(config->symbols); | ||
233 | } | 70 | } |
234 | list_free(config->modes); | 71 | if (config->modes) { |
235 | 72 | for (int i = 0; i < config->modes->length; ++i) { | |
236 | for (i = 0; config->bars && i < config->bars->length; ++i) { | 73 | free_mode(config->modes->items[i]); |
237 | free_bar(config->bars->items[i]); | 74 | } |
75 | list_free(config->modes); | ||
238 | } | 76 | } |
239 | list_free(config->bars); | 77 | if (config->bars) { |
240 | 78 | for (int i = 0; i < config->bars->length; ++i) { | |
241 | free_flat_list(config->cmd_queue); | 79 | free_bar_config(config->bars->items[i]); |
242 | 80 | } | |
243 | for (i = 0; config->workspace_outputs && i < config->workspace_outputs->length; ++i) { | 81 | list_free(config->bars); |
244 | free_workspace_output(config->workspace_outputs->items[i]); | ||
245 | } | 82 | } |
83 | list_free(config->cmd_queue); | ||
246 | list_free(config->workspace_outputs); | 84 | list_free(config->workspace_outputs); |
247 | |||
248 | for (i = 0; config->pid_workspaces && i < config->pid_workspaces->length; ++i) { | ||
249 | free_pid_workspace(config->pid_workspaces->items[i]); | ||
250 | } | ||
251 | list_free(config->pid_workspaces); | 85 | list_free(config->pid_workspaces); |
252 | 86 | list_free(config->output_configs); | |
253 | for (i = 0; config->criteria && i < config->criteria->length; ++i) { | 87 | if (config->input_configs) { |
254 | free_criteria(config->criteria->items[i]); | 88 | for (int i = 0; i < config->input_configs->length; i++) { |
89 | free_input_config(config->input_configs->items[i]); | ||
90 | } | ||
91 | list_free(config->input_configs); | ||
255 | } | 92 | } |
256 | list_free(config->criteria); | 93 | if (config->seat_configs) { |
257 | 94 | for (int i = 0; i < config->seat_configs->length; i++) { | |
258 | for (i = 0; config->no_focus && i < config->no_focus->length; ++i) { | 95 | free_seat_config(config->seat_configs->items[i]); |
259 | free_criteria(config->no_focus->items[i]); | 96 | } |
97 | list_free(config->seat_configs); | ||
260 | } | 98 | } |
99 | list_free(config->criteria); | ||
261 | list_free(config->no_focus); | 100 | list_free(config->no_focus); |
262 | 101 | list_free(config->active_bar_modifiers); | |
263 | for (i = 0; config->input_configs && i < config->input_configs->length; ++i) { | 102 | list_free(config->config_chain); |
264 | free_input_config(config->input_configs->items[i]); | ||
265 | } | ||
266 | list_free(config->input_configs); | ||
267 | |||
268 | for (i = 0; config->output_configs && i < config->output_configs->length; ++i) { | ||
269 | free_output_config(config->output_configs->items[i]); | ||
270 | } | ||
271 | list_free(config->output_configs); | ||
272 | |||
273 | for (i = 0; config->command_policies && i < config->command_policies->length; ++i) { | ||
274 | free_command_policy(config->command_policies->items[i]); | ||
275 | } | ||
276 | list_free(config->command_policies); | 103 | list_free(config->command_policies); |
277 | |||
278 | for (i = 0; config->feature_policies && i < config->feature_policies->length; ++i) { | ||
279 | free_feature_policy(config->feature_policies->items[i]); | ||
280 | } | ||
281 | list_free(config->feature_policies); | 104 | list_free(config->feature_policies); |
282 | 105 | list_free(config->ipc_policies); | |
283 | list_free(config->active_bar_modifiers); | 106 | free(config->current_bar); |
284 | free_flat_list(config->config_chain); | ||
285 | free(config->font); | ||
286 | free(config->floating_scroll_up_cmd); | 107 | free(config->floating_scroll_up_cmd); |
287 | free(config->floating_scroll_down_cmd); | 108 | free(config->floating_scroll_down_cmd); |
288 | free(config->floating_scroll_left_cmd); | 109 | free(config->floating_scroll_left_cmd); |
289 | free(config->floating_scroll_right_cmd); | 110 | free(config->floating_scroll_right_cmd); |
111 | free(config->font); | ||
112 | free((char *)config->current_config); | ||
290 | free(config); | 113 | free(config); |
291 | } | 114 | } |
292 | 115 | ||
293 | 116 | static void destroy_removed_seats(struct sway_config *old_config, | |
294 | static bool file_exists(const char *path) { | 117 | struct sway_config *new_config) { |
295 | return path && access(path, R_OK) != -1; | 118 | struct seat_config *seat_config; |
119 | struct sway_seat *seat; | ||
120 | int i; | ||
121 | for (i = 0; i < old_config->seat_configs->length; i++) { | ||
122 | seat_config = old_config->seat_configs->items[i]; | ||
123 | /* Also destroy seats that aren't present in new config */ | ||
124 | if (new_config && list_seq_find(new_config->seat_configs, | ||
125 | seat_name_cmp, seat_config->name) < 0) { | ||
126 | seat = input_manager_get_seat(input_manager, | ||
127 | seat_config->name); | ||
128 | seat_destroy(seat); | ||
129 | } | ||
130 | } | ||
296 | } | 131 | } |
297 | 132 | ||
298 | static void config_defaults(struct sway_config *config) { | 133 | static void config_defaults(struct sway_config *config) { |
@@ -304,19 +139,22 @@ static void config_defaults(struct sway_config *config) { | |||
304 | if (!(config->criteria = create_list())) goto cleanup; | 139 | if (!(config->criteria = create_list())) goto cleanup; |
305 | if (!(config->no_focus = create_list())) goto cleanup; | 140 | if (!(config->no_focus = create_list())) goto cleanup; |
306 | if (!(config->input_configs = create_list())) goto cleanup; | 141 | if (!(config->input_configs = create_list())) goto cleanup; |
142 | if (!(config->seat_configs = create_list())) goto cleanup; | ||
307 | if (!(config->output_configs = create_list())) goto cleanup; | 143 | if (!(config->output_configs = create_list())) goto cleanup; |
308 | 144 | ||
309 | if (!(config->cmd_queue = create_list())) goto cleanup; | 145 | if (!(config->cmd_queue = create_list())) goto cleanup; |
310 | 146 | ||
311 | if (!(config->current_mode = malloc(sizeof(struct sway_mode)))) goto cleanup; | 147 | if (!(config->current_mode = malloc(sizeof(struct sway_mode)))) |
148 | goto cleanup; | ||
312 | if (!(config->current_mode->name = malloc(sizeof("default")))) goto cleanup; | 149 | if (!(config->current_mode->name = malloc(sizeof("default")))) goto cleanup; |
313 | strcpy(config->current_mode->name, "default"); | 150 | strcpy(config->current_mode->name, "default"); |
314 | if (!(config->current_mode->bindings = create_list())) goto cleanup; | 151 | if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; |
152 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; | ||
315 | list_add(config->modes, config->current_mode); | 153 | list_add(config->modes, config->current_mode); |
316 | 154 | ||
317 | config->floating_mod = 0; | 155 | config->floating_mod = 0; |
318 | config->dragging_key = M_LEFT_CLICK; | 156 | config->dragging_key = BTN_LEFT; |
319 | config->resizing_key = M_RIGHT_CLICK; | 157 | config->resizing_key = BTN_RIGHT; |
320 | if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup; | 158 | if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup; |
321 | if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup; | 159 | if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup; |
322 | if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup; | 160 | if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup; |
@@ -324,7 +162,8 @@ static void config_defaults(struct sway_config *config) { | |||
324 | config->default_layout = L_NONE; | 162 | config->default_layout = L_NONE; |
325 | config->default_orientation = L_NONE; | 163 | config->default_orientation = L_NONE; |
326 | if (!(config->font = strdup("monospace 10"))) goto cleanup; | 164 | if (!(config->font = strdup("monospace 10"))) goto cleanup; |
327 | config->font_height = get_font_text_height(config->font); | 165 | // TODO: border |
166 | //config->font_height = get_font_text_height(config->font); | ||
328 | 167 | ||
329 | // floating view | 168 | // floating view |
330 | config->floating_maximum_width = 0; | 169 | config->floating_maximum_width = 0; |
@@ -339,7 +178,6 @@ static void config_defaults(struct sway_config *config) { | |||
339 | config->active = false; | 178 | config->active = false; |
340 | config->failed = false; | 179 | config->failed = false; |
341 | config->auto_back_and_forth = false; | 180 | config->auto_back_and_forth = false; |
342 | config->seamless_mouse = true; | ||
343 | config->reading = false; | 181 | config->reading = false; |
344 | config->show_marks = true; | 182 | config->show_marks = true; |
345 | 183 | ||
@@ -403,29 +241,8 @@ cleanup: | |||
403 | sway_abort("Unable to allocate config structures"); | 241 | sway_abort("Unable to allocate config structures"); |
404 | } | 242 | } |
405 | 243 | ||
406 | static int compare_modifiers(const void *left, const void *right) { | 244 | static bool file_exists(const char *path) { |
407 | uint32_t a = *(uint32_t *)left; | 245 | return path && access(path, R_OK) != -1; |
408 | uint32_t b = *(uint32_t *)right; | ||
409 | |||
410 | return a - b; | ||
411 | } | ||
412 | |||
413 | void update_active_bar_modifiers() { | ||
414 | if (config->active_bar_modifiers->length > 0) { | ||
415 | list_free(config->active_bar_modifiers); | ||
416 | config->active_bar_modifiers = create_list(); | ||
417 | } | ||
418 | |||
419 | struct bar_config *bar; | ||
420 | int i; | ||
421 | for (i = 0; i < config->bars->length; ++i) { | ||
422 | bar = config->bars->items[i]; | ||
423 | if (strcmp(bar->mode, "hide") == 0 && strcmp(bar->hidden_state, "hide") == 0) { | ||
424 | if (list_seq_find(config->active_bar_modifiers, compare_modifiers, &bar->modifier) < 0) { | ||
425 | list_add(config->active_bar_modifiers, &bar->modifier); | ||
426 | } | ||
427 | } | ||
428 | } | ||
429 | } | 246 | } |
430 | 247 | ||
431 | static char *get_config_path(void) { | 248 | static char *get_config_path(void) { |
@@ -442,12 +259,12 @@ static char *get_config_path(void) { | |||
442 | char *home = getenv("HOME"); | 259 | char *home = getenv("HOME"); |
443 | char *config_home = malloc(strlen(home) + strlen("/.config") + 1); | 260 | char *config_home = malloc(strlen(home) + strlen("/.config") + 1); |
444 | if (!config_home) { | 261 | if (!config_home) { |
445 | sway_log(L_ERROR, "Unable to allocate $HOME/.config"); | 262 | wlr_log(L_ERROR, "Unable to allocate $HOME/.config"); |
446 | } else { | 263 | } else { |
447 | strcpy(config_home, home); | 264 | strcpy(config_home, home); |
448 | strcat(config_home, "/.config"); | 265 | strcat(config_home, "/.config"); |
449 | setenv("XDG_CONFIG_HOME", config_home, 1); | 266 | setenv("XDG_CONFIG_HOME", config_home, 1); |
450 | sway_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); | 267 | wlr_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); |
451 | free(config_home); | 268 | free(config_home); |
452 | } | 269 | } |
453 | } | 270 | } |
@@ -463,6 +280,7 @@ static char *get_config_path(void) { | |||
463 | if (file_exists(path)) { | 280 | if (file_exists(path)) { |
464 | return path; | 281 | return path; |
465 | } | 282 | } |
283 | free(path); | ||
466 | } | 284 | } |
467 | } | 285 | } |
468 | 286 | ||
@@ -472,7 +290,7 @@ static char *get_config_path(void) { | |||
472 | const char *current_config_path; | 290 | const char *current_config_path; |
473 | 291 | ||
474 | static bool load_config(const char *path, struct sway_config *config) { | 292 | static bool load_config(const char *path, struct sway_config *config) { |
475 | sway_log(L_INFO, "Loading config from %s", path); | 293 | wlr_log(L_INFO, "Loading config from %s", path); |
476 | current_config_path = path; | 294 | current_config_path = path; |
477 | 295 | ||
478 | struct stat sb; | 296 | struct stat sb; |
@@ -481,13 +299,13 @@ static bool load_config(const char *path, struct sway_config *config) { | |||
481 | } | 299 | } |
482 | 300 | ||
483 | if (path == NULL) { | 301 | if (path == NULL) { |
484 | sway_log(L_ERROR, "Unable to find a config file!"); | 302 | wlr_log(L_ERROR, "Unable to find a config file!"); |
485 | return false; | 303 | return false; |
486 | } | 304 | } |
487 | 305 | ||
488 | FILE *f = fopen(path, "r"); | 306 | FILE *f = fopen(path, "r"); |
489 | if (!f) { | 307 | if (!f) { |
490 | sway_log(L_ERROR, "Unable to open %s for reading", path); | 308 | wlr_log(L_ERROR, "Unable to open %s for reading", path); |
491 | return false; | 309 | return false; |
492 | } | 310 | } |
493 | 311 | ||
@@ -495,20 +313,14 @@ static bool load_config(const char *path, struct sway_config *config) { | |||
495 | fclose(f); | 313 | fclose(f); |
496 | 314 | ||
497 | if (!config_load_success) { | 315 | if (!config_load_success) { |
498 | sway_log(L_ERROR, "Error(s) loading config!"); | 316 | wlr_log(L_ERROR, "Error(s) loading config!"); |
499 | } | 317 | } |
500 | 318 | ||
501 | current_config_path = NULL; | 319 | current_config_path = NULL; |
502 | return true; | 320 | return true; |
503 | } | 321 | } |
504 | 322 | ||
505 | static int qstrcmp(const void* a, const void* b) { | ||
506 | return strcmp(*((char**) a), *((char**) b)); | ||
507 | } | ||
508 | |||
509 | bool load_main_config(const char *file, bool is_active) { | 323 | bool load_main_config(const char *file, bool is_active) { |
510 | input_init(); | ||
511 | |||
512 | char *path; | 324 | char *path; |
513 | if (file != NULL) { | 325 | if (file != NULL) { |
514 | path = strdup(file); | 326 | path = strdup(file); |
@@ -524,7 +336,7 @@ bool load_main_config(const char *file, bool is_active) { | |||
524 | 336 | ||
525 | config_defaults(config); | 337 | config_defaults(config); |
526 | if (is_active) { | 338 | if (is_active) { |
527 | sway_log(L_DEBUG, "Performing configuration file reload"); | 339 | wlr_log(L_DEBUG, "Performing configuration file reload"); |
528 | config->reloading = true; | 340 | config->reloading = true; |
529 | config->active = true; | 341 | config->active = true; |
530 | } | 342 | } |
@@ -535,11 +347,14 @@ bool load_main_config(const char *file, bool is_active) { | |||
535 | config->reading = true; | 347 | config->reading = true; |
536 | 348 | ||
537 | // Read security configs | 349 | // Read security configs |
350 | // TODO: Security | ||
538 | bool success = true; | 351 | bool success = true; |
352 | /* | ||
539 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); | 353 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); |
540 | if (!dir) { | 354 | if (!dir) { |
541 | sway_log(L_ERROR, "%s does not exist, sway will have no security configuration" | 355 | wlr_log(L_ERROR, |
542 | " and will probably be broken", SYSCONFDIR "/sway/security.d"); | 356 | "%s does not exist, sway will have no security configuration" |
357 | " and will probably be broken", SYSCONFDIR "/sway/security.d"); | ||
543 | } else { | 358 | } else { |
544 | list_t *secconfigs = create_list(); | 359 | list_t *secconfigs = create_list(); |
545 | char *base = SYSCONFDIR "/sway/security.d/"; | 360 | char *base = SYSCONFDIR "/sway/security.d/"; |
@@ -563,8 +378,12 @@ bool load_main_config(const char *file, bool is_active) { | |||
563 | list_qsort(secconfigs, qstrcmp); | 378 | list_qsort(secconfigs, qstrcmp); |
564 | for (int i = 0; i < secconfigs->length; ++i) { | 379 | for (int i = 0; i < secconfigs->length; ++i) { |
565 | char *_path = secconfigs->items[i]; | 380 | char *_path = secconfigs->items[i]; |
566 | if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || (((s.st_mode & 0777) != 0644) && (s.st_mode & 0777) != 0444)) { | 381 | if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || |
567 | sway_log(L_ERROR, "Refusing to load %s - it must be owned by root and mode 644 or 444", _path); | 382 | (((s.st_mode & 0777) != 0644) && |
383 | (s.st_mode & 0777) != 0444)) { | ||
384 | wlr_log(L_ERROR, | ||
385 | "Refusing to load %s - it must be owned by root " | ||
386 | "and mode 644 or 444", _path); | ||
568 | success = false; | 387 | success = false; |
569 | } else { | 388 | } else { |
570 | success = success && load_config(_path, config); | 389 | success = success && load_config(_path, config); |
@@ -573,6 +392,7 @@ bool load_main_config(const char *file, bool is_active) { | |||
573 | 392 | ||
574 | free_flat_list(secconfigs); | 393 | free_flat_list(secconfigs); |
575 | } | 394 | } |
395 | */ | ||
576 | 396 | ||
577 | success = success && load_config(path, config); | 397 | success = success && load_config(path, config); |
578 | 398 | ||
@@ -581,18 +401,15 @@ bool load_main_config(const char *file, bool is_active) { | |||
581 | } | 401 | } |
582 | 402 | ||
583 | if (old_config) { | 403 | if (old_config) { |
404 | destroy_removed_seats(old_config, config); | ||
584 | free_config(old_config); | 405 | free_config(old_config); |
585 | } | 406 | } |
586 | config->reading = false; | 407 | config->reading = false; |
587 | |||
588 | if (success) { | ||
589 | update_active_bar_modifiers(); | ||
590 | } | ||
591 | |||
592 | return success; | 408 | return success; |
593 | } | 409 | } |
594 | 410 | ||
595 | static bool load_include_config(const char *path, const char *parent_dir, struct sway_config *config) { | 411 | static bool load_include_config(const char *path, const char *parent_dir, |
412 | struct sway_config *config) { | ||
596 | // save parent config | 413 | // save parent config |
597 | const char *parent_config = config->current_config; | 414 | const char *parent_config = config->current_config; |
598 | 415 | ||
@@ -602,7 +419,8 @@ static bool load_include_config(const char *path, const char *parent_dir, struct | |||
602 | len = len + strlen(parent_dir) + 2; | 419 | len = len + strlen(parent_dir) + 2; |
603 | full_path = malloc(len * sizeof(char)); | 420 | full_path = malloc(len * sizeof(char)); |
604 | if (!full_path) { | 421 | if (!full_path) { |
605 | sway_log(L_ERROR, "Unable to allocate full path to included config"); | 422 | wlr_log(L_ERROR, |
423 | "Unable to allocate full path to included config"); | ||
606 | return false; | 424 | return false; |
607 | } | 425 | } |
608 | snprintf(full_path, len, "%s/%s", parent_dir, path); | 426 | snprintf(full_path, len, "%s/%s", parent_dir, path); |
@@ -612,7 +430,7 @@ static bool load_include_config(const char *path, const char *parent_dir, struct | |||
612 | free(full_path); | 430 | free(full_path); |
613 | 431 | ||
614 | if (real_path == NULL) { | 432 | if (real_path == NULL) { |
615 | sway_log(L_DEBUG, "%s not found.", path); | 433 | wlr_log(L_DEBUG, "%s not found.", path); |
616 | return false; | 434 | return false; |
617 | } | 435 | } |
618 | 436 | ||
@@ -621,7 +439,9 @@ static bool load_include_config(const char *path, const char *parent_dir, struct | |||
621 | for (j = 0; j < config->config_chain->length; ++j) { | 439 | for (j = 0; j < config->config_chain->length; ++j) { |
622 | char *old_path = config->config_chain->items[j]; | 440 | char *old_path = config->config_chain->items[j]; |
623 | if (strcmp(real_path, old_path) == 0) { | 441 | if (strcmp(real_path, old_path) == 0) { |
624 | sway_log(L_DEBUG, "%s already included once, won't be included again.", real_path); | 442 | wlr_log(L_DEBUG, |
443 | "%s already included once, won't be included again.", | ||
444 | real_path); | ||
625 | free(real_path); | 445 | free(real_path); |
626 | return false; | 446 | return false; |
627 | } | 447 | } |
@@ -673,7 +493,7 @@ bool load_include_configs(const char *path, struct sway_config *config) { | |||
673 | // restore wd | 493 | // restore wd |
674 | if (chdir(wd) < 0) { | 494 | if (chdir(wd) < 0) { |
675 | free(wd); | 495 | free(wd); |
676 | sway_log(L_ERROR, "failed to restore working directory"); | 496 | wlr_log(L_ERROR, "failed to restore working directory"); |
677 | return false; | 497 | return false; |
678 | } | 498 | } |
679 | 499 | ||
@@ -681,13 +501,11 @@ bool load_include_configs(const char *path, struct sway_config *config) { | |||
681 | return true; | 501 | return true; |
682 | } | 502 | } |
683 | 503 | ||
684 | struct cmd_results *check_security_config() { | 504 | void config_clear_handler_context(struct sway_config *config) { |
685 | if (!current_config_path || strncmp(SYSCONFDIR "/sway/security.d/", current_config_path, | 505 | free_input_config(config->handler_context.input_config); |
686 | strlen(SYSCONFDIR "/sway/security.d/")) != 0) { | 506 | free_seat_config(config->handler_context.seat_config); |
687 | return cmd_results_new(CMD_INVALID, "permit", | 507 | |
688 | "This command is only permitted to run from " SYSCONFDIR "/sway/security.d/*"); | 508 | memset(&config->handler_context, 0, sizeof(config->handler_context)); |
689 | } | ||
690 | return NULL; | ||
691 | } | 509 | } |
692 | 510 | ||
693 | bool read_config(FILE *file, struct sway_config *config) { | 511 | bool read_config(FILE *file, struct sway_config *config) { |
@@ -717,13 +535,13 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
717 | switch(res->status) { | 535 | switch(res->status) { |
718 | case CMD_FAILURE: | 536 | case CMD_FAILURE: |
719 | case CMD_INVALID: | 537 | case CMD_INVALID: |
720 | sway_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, line, | 538 | wlr_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, |
721 | res->error, config->current_config); | 539 | line, res->error, config->current_config); |
722 | success = false; | 540 | success = false; |
723 | break; | 541 | break; |
724 | 542 | ||
725 | case CMD_DEFER: | 543 | case CMD_DEFER: |
726 | sway_log(L_DEBUG, "Defferring command `%s'", line); | 544 | wlr_log(L_DEBUG, "Deferring command `%s'", line); |
727 | list_add(config->cmd_queue, strdup(line)); | 545 | list_add(config->cmd_queue, strdup(line)); |
728 | break; | 546 | break; |
729 | 547 | ||
@@ -731,7 +549,7 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
731 | if (block == CMD_BLOCK_END) { | 549 | if (block == CMD_BLOCK_END) { |
732 | block = CMD_BLOCK_MODE; | 550 | block = CMD_BLOCK_MODE; |
733 | } else { | 551 | } else { |
734 | sway_log(L_ERROR, "Invalid block '%s'", line); | 552 | wlr_log(L_ERROR, "Invalid block '%s'", line); |
735 | } | 553 | } |
736 | break; | 554 | break; |
737 | 555 | ||
@@ -739,7 +557,15 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
739 | if (block == CMD_BLOCK_END) { | 557 | if (block == CMD_BLOCK_END) { |
740 | block = CMD_BLOCK_INPUT; | 558 | block = CMD_BLOCK_INPUT; |
741 | } else { | 559 | } else { |
742 | sway_log(L_ERROR, "Invalid block '%s'", line); | 560 | wlr_log(L_ERROR, "Invalid block '%s'", line); |
561 | } | ||
562 | break; | ||
563 | |||
564 | case CMD_BLOCK_SEAT: | ||
565 | if (block == CMD_BLOCK_END) { | ||
566 | block = CMD_BLOCK_SEAT; | ||
567 | } else { | ||
568 | wlr_log(L_ERROR, "Invalid block '%s'", line); | ||
743 | } | 569 | } |
744 | break; | 570 | break; |
745 | 571 | ||
@@ -747,7 +573,7 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
747 | if (block == CMD_BLOCK_END) { | 573 | if (block == CMD_BLOCK_END) { |
748 | block = CMD_BLOCK_BAR; | 574 | block = CMD_BLOCK_BAR; |
749 | } else { | 575 | } else { |
750 | sway_log(L_ERROR, "Invalid block '%s'", line); | 576 | wlr_log(L_ERROR, "Invalid block '%s'", line); |
751 | } | 577 | } |
752 | break; | 578 | break; |
753 | 579 | ||
@@ -755,7 +581,7 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
755 | if (block == CMD_BLOCK_BAR) { | 581 | if (block == CMD_BLOCK_BAR) { |
756 | block = CMD_BLOCK_BAR_COLORS; | 582 | block = CMD_BLOCK_BAR_COLORS; |
757 | } else { | 583 | } else { |
758 | sway_log(L_ERROR, "Invalid block '%s'", line); | 584 | wlr_log(L_ERROR, "Invalid block '%s'", line); |
759 | } | 585 | } |
760 | break; | 586 | break; |
761 | 587 | ||
@@ -763,7 +589,7 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
763 | if (block == CMD_BLOCK_END) { | 589 | if (block == CMD_BLOCK_END) { |
764 | block = CMD_BLOCK_COMMANDS; | 590 | block = CMD_BLOCK_COMMANDS; |
765 | } else { | 591 | } else { |
766 | sway_log(L_ERROR, "Invalid block '%s'", line); | 592 | wlr_log(L_ERROR, "Invalid block '%s'", line); |
767 | } | 593 | } |
768 | break; | 594 | break; |
769 | 595 | ||
@@ -771,7 +597,7 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
771 | if (block == CMD_BLOCK_END) { | 597 | if (block == CMD_BLOCK_END) { |
772 | block = CMD_BLOCK_IPC; | 598 | block = CMD_BLOCK_IPC; |
773 | } else { | 599 | } else { |
774 | sway_log(L_ERROR, "Invalid block '%s'", line); | 600 | wlr_log(L_ERROR, "Invalid block '%s'", line); |
775 | } | 601 | } |
776 | break; | 602 | break; |
777 | 603 | ||
@@ -779,56 +605,61 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
779 | if (block == CMD_BLOCK_IPC) { | 605 | if (block == CMD_BLOCK_IPC) { |
780 | block = CMD_BLOCK_IPC_EVENTS; | 606 | block = CMD_BLOCK_IPC_EVENTS; |
781 | } else { | 607 | } else { |
782 | sway_log(L_ERROR, "Invalid block '%s'", line); | 608 | wlr_log(L_ERROR, "Invalid block '%s'", line); |
783 | } | 609 | } |
784 | break; | 610 | break; |
785 | 611 | ||
786 | case CMD_BLOCK_END: | 612 | case CMD_BLOCK_END: |
787 | switch(block) { | 613 | switch(block) { |
788 | case CMD_BLOCK_MODE: | 614 | case CMD_BLOCK_MODE: |
789 | sway_log(L_DEBUG, "End of mode block"); | 615 | wlr_log(L_DEBUG, "End of mode block"); |
790 | config->current_mode = config->modes->items[0]; | 616 | config->current_mode = config->modes->items[0]; |
791 | block = CMD_BLOCK_END; | 617 | block = CMD_BLOCK_END; |
792 | break; | 618 | break; |
793 | 619 | ||
794 | case CMD_BLOCK_INPUT: | 620 | case CMD_BLOCK_INPUT: |
795 | sway_log(L_DEBUG, "End of input block"); | 621 | wlr_log(L_DEBUG, "End of input block"); |
796 | current_input_config = NULL; | 622 | block = CMD_BLOCK_END; |
623 | break; | ||
624 | |||
625 | case CMD_BLOCK_SEAT: | ||
626 | wlr_log(L_DEBUG, "End of seat block"); | ||
797 | block = CMD_BLOCK_END; | 627 | block = CMD_BLOCK_END; |
798 | break; | 628 | break; |
799 | 629 | ||
800 | case CMD_BLOCK_BAR: | 630 | case CMD_BLOCK_BAR: |
801 | sway_log(L_DEBUG, "End of bar block"); | 631 | wlr_log(L_DEBUG, "End of bar block"); |
802 | config->current_bar = NULL; | 632 | config->current_bar = NULL; |
803 | block = CMD_BLOCK_END; | 633 | block = CMD_BLOCK_END; |
804 | break; | 634 | break; |
805 | 635 | ||
806 | case CMD_BLOCK_BAR_COLORS: | 636 | case CMD_BLOCK_BAR_COLORS: |
807 | sway_log(L_DEBUG, "End of bar colors block"); | 637 | wlr_log(L_DEBUG, "End of bar colors block"); |
808 | block = CMD_BLOCK_BAR; | 638 | block = CMD_BLOCK_BAR; |
809 | break; | 639 | break; |
810 | 640 | ||
811 | case CMD_BLOCK_COMMANDS: | 641 | case CMD_BLOCK_COMMANDS: |
812 | sway_log(L_DEBUG, "End of commands block"); | 642 | wlr_log(L_DEBUG, "End of commands block"); |
813 | block = CMD_BLOCK_END; | 643 | block = CMD_BLOCK_END; |
814 | break; | 644 | break; |
815 | 645 | ||
816 | case CMD_BLOCK_IPC: | 646 | case CMD_BLOCK_IPC: |
817 | sway_log(L_DEBUG, "End of IPC block"); | 647 | wlr_log(L_DEBUG, "End of IPC block"); |
818 | block = CMD_BLOCK_END; | 648 | block = CMD_BLOCK_END; |
819 | break; | 649 | break; |
820 | 650 | ||
821 | case CMD_BLOCK_IPC_EVENTS: | 651 | case CMD_BLOCK_IPC_EVENTS: |
822 | sway_log(L_DEBUG, "End of IPC events block"); | 652 | wlr_log(L_DEBUG, "End of IPC events block"); |
823 | block = CMD_BLOCK_IPC; | 653 | block = CMD_BLOCK_IPC; |
824 | break; | 654 | break; |
825 | 655 | ||
826 | case CMD_BLOCK_END: | 656 | case CMD_BLOCK_END: |
827 | sway_log(L_ERROR, "Unmatched }"); | 657 | wlr_log(L_ERROR, "Unmatched }"); |
828 | break; | 658 | break; |
829 | 659 | ||
830 | default:; | 660 | default:; |
831 | } | 661 | } |
662 | config_clear_handler_context(config); | ||
832 | default:; | 663 | default:; |
833 | } | 664 | } |
834 | free(line); | 665 | free(line); |
@@ -838,365 +669,6 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
838 | return success; | 669 | return success; |
839 | } | 670 | } |
840 | 671 | ||
841 | int input_identifier_cmp(const void *item, const void *data) { | ||
842 | const struct input_config *ic = item; | ||
843 | const char *identifier = data; | ||
844 | return strcmp(ic->identifier, identifier); | ||
845 | } | ||
846 | |||
847 | int output_name_cmp(const void *item, const void *data) { | ||
848 | const struct output_config *output = item; | ||
849 | const char *name = data; | ||
850 | |||
851 | return strcmp(output->name, name); | ||
852 | } | ||
853 | |||
854 | void merge_input_config(struct input_config *dst, struct input_config *src) { | ||
855 | if (src->identifier) { | ||
856 | if (dst->identifier) { | ||
857 | free(dst->identifier); | ||
858 | } | ||
859 | dst->identifier = strdup(src->identifier); | ||
860 | } | ||
861 | if (src->accel_profile != INT_MIN) { | ||
862 | dst->accel_profile = src->accel_profile; | ||
863 | } | ||
864 | if (src->click_method != INT_MIN) { | ||
865 | dst->click_method = src->click_method; | ||
866 | } | ||
867 | if (src->drag_lock != INT_MIN) { | ||
868 | dst->drag_lock = src->drag_lock; | ||
869 | } | ||
870 | if (src->dwt != INT_MIN) { | ||
871 | dst->dwt = src->dwt; | ||
872 | } | ||
873 | if (src->middle_emulation != INT_MIN) { | ||
874 | dst->middle_emulation = src->middle_emulation; | ||
875 | } | ||
876 | if (src->natural_scroll != INT_MIN) { | ||
877 | dst->natural_scroll = src->natural_scroll; | ||
878 | } | ||
879 | if (src->pointer_accel != FLT_MIN) { | ||
880 | dst->pointer_accel = src->pointer_accel; | ||
881 | } | ||
882 | if (src->scroll_method != INT_MIN) { | ||
883 | dst->scroll_method = src->scroll_method; | ||
884 | } | ||
885 | if (src->send_events != INT_MIN) { | ||
886 | dst->send_events = src->send_events; | ||
887 | } | ||
888 | if (src->tap != INT_MIN) { | ||
889 | dst->tap = src->tap; | ||
890 | } | ||
891 | } | ||
892 | |||
893 | void merge_output_config(struct output_config *dst, struct output_config *src) { | ||
894 | if (src->name) { | ||
895 | if (dst->name) { | ||
896 | free(dst->name); | ||
897 | } | ||
898 | dst->name = strdup(src->name); | ||
899 | } | ||
900 | if (src->enabled != -1) { | ||
901 | dst->enabled = src->enabled; | ||
902 | } | ||
903 | if (src->width != -1) { | ||
904 | dst->width = src->width; | ||
905 | } | ||
906 | if (src->height != -1) { | ||
907 | dst->height = src->height; | ||
908 | } | ||
909 | if (src->x != -1) { | ||
910 | dst->x = src->x; | ||
911 | } | ||
912 | if (src->y != -1) { | ||
913 | dst->y = src->y; | ||
914 | } | ||
915 | if (src->scale != -1) { | ||
916 | dst->scale = src->scale; | ||
917 | } | ||
918 | if (src->background) { | ||
919 | if (dst->background) { | ||
920 | free(dst->background); | ||
921 | } | ||
922 | dst->background = strdup(src->background); | ||
923 | } | ||
924 | if (src->background_option) { | ||
925 | if (dst->background_option) { | ||
926 | free(dst->background_option); | ||
927 | } | ||
928 | dst->background_option = strdup(src->background_option); | ||
929 | } | ||
930 | } | ||
931 | |||
932 | static void invoke_swaybar(struct bar_config *bar) { | ||
933 | // Pipe to communicate errors | ||
934 | int filedes[2]; | ||
935 | if (pipe(filedes) == -1) { | ||
936 | sway_log(L_ERROR, "Pipe setup failed! Cannot fork into bar"); | ||
937 | return; | ||
938 | } | ||
939 | |||
940 | bar->pid = fork(); | ||
941 | if (bar->pid == 0) { | ||
942 | close(filedes[0]); | ||
943 | if (!bar->swaybar_command) { | ||
944 | char *const cmd[] = { | ||
945 | "swaybar", | ||
946 | "-b", | ||
947 | bar->id, | ||
948 | NULL, | ||
949 | }; | ||
950 | |||
951 | close(filedes[1]); | ||
952 | execvp(cmd[0], cmd); | ||
953 | _exit(EXIT_SUCCESS); | ||
954 | } else { | ||
955 | // run custom swaybar | ||
956 | int len = strlen(bar->swaybar_command) + strlen(bar->id) + 5; | ||
957 | char *command = malloc(len * sizeof(char)); | ||
958 | if (!command) { | ||
959 | const char msg[] = "Unable to allocate swaybar command string"; | ||
960 | int len = sizeof(msg); | ||
961 | if (write(filedes[1], &len, sizeof(int))) {}; | ||
962 | if (write(filedes[1], msg, len)) {}; | ||
963 | close(filedes[1]); | ||
964 | _exit(EXIT_FAILURE); | ||
965 | } | ||
966 | snprintf(command, len, "%s -b %s", bar->swaybar_command, bar->id); | ||
967 | |||
968 | char *const cmd[] = { | ||
969 | "sh", | ||
970 | "-c", | ||
971 | command, | ||
972 | NULL, | ||
973 | }; | ||
974 | |||
975 | close(filedes[1]); | ||
976 | execvp(cmd[0], cmd); | ||
977 | free(command); | ||
978 | _exit(EXIT_SUCCESS); | ||
979 | } | ||
980 | } | ||
981 | close(filedes[0]); | ||
982 | int len; | ||
983 | if(read(filedes[1], &len, sizeof(int)) == sizeof(int)) { | ||
984 | char *buf = malloc(len); | ||
985 | if(!buf) { | ||
986 | sway_log(L_ERROR, "Cannot allocate error string"); | ||
987 | return; | ||
988 | } | ||
989 | if(read(filedes[1], buf, len)) { | ||
990 | sway_log(L_ERROR, "%s", buf); | ||
991 | } | ||
992 | free(buf); | ||
993 | } | ||
994 | close(filedes[1]); | ||
995 | } | ||
996 | |||
997 | static void terminate_swaybar(pid_t pid) { | ||
998 | int ret = kill(pid, SIGTERM); | ||
999 | if (ret != 0) { | ||
1000 | sway_log(L_ERROR, "Unable to terminate swaybar [pid: %d]", pid); | ||
1001 | } else { | ||
1002 | int status; | ||
1003 | waitpid(pid, &status, 0); | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | void terminate_swaybg(pid_t pid) { | ||
1008 | int ret = kill(pid, SIGTERM); | ||
1009 | if (ret != 0) { | ||
1010 | sway_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid); | ||
1011 | } else { | ||
1012 | int status; | ||
1013 | waitpid(pid, &status, 0); | ||
1014 | } | ||
1015 | } | ||
1016 | |||
1017 | static bool active_output(const char *name) { | ||
1018 | int i; | ||
1019 | swayc_t *cont = NULL; | ||
1020 | for (i = 0; i < root_container.children->length; ++i) { | ||
1021 | cont = root_container.children->items[i]; | ||
1022 | if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) { | ||
1023 | return true; | ||
1024 | } | ||
1025 | } | ||
1026 | |||
1027 | return false; | ||
1028 | } | ||
1029 | |||
1030 | void load_swaybars() { | ||
1031 | // Check for bars | ||
1032 | list_t *bars = create_list(); | ||
1033 | struct bar_config *bar = NULL; | ||
1034 | int i; | ||
1035 | for (i = 0; i < config->bars->length; ++i) { | ||
1036 | bar = config->bars->items[i]; | ||
1037 | bool apply = false; | ||
1038 | if (bar->outputs) { | ||
1039 | int j; | ||
1040 | for (j = 0; j < bar->outputs->length; ++j) { | ||
1041 | char *o = bar->outputs->items[j]; | ||
1042 | if (!strcmp(o, "*") || active_output(o)) { | ||
1043 | apply = true; | ||
1044 | break; | ||
1045 | } | ||
1046 | } | ||
1047 | } else { | ||
1048 | apply = true; | ||
1049 | } | ||
1050 | if (apply) { | ||
1051 | list_add(bars, bar); | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | for (i = 0; i < bars->length; ++i) { | ||
1056 | bar = bars->items[i]; | ||
1057 | if (bar->pid != 0) { | ||
1058 | terminate_swaybar(bar->pid); | ||
1059 | } | ||
1060 | sway_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); | ||
1061 | invoke_swaybar(bar); | ||
1062 | } | ||
1063 | |||
1064 | list_free(bars); | ||
1065 | } | ||
1066 | |||
1067 | void apply_input_config(struct input_config *ic, struct libinput_device *dev) { | ||
1068 | if (!ic) { | ||
1069 | return; | ||
1070 | } | ||
1071 | |||
1072 | sway_log(L_DEBUG, "apply_input_config(%s)", ic->identifier); | ||
1073 | |||
1074 | if (ic->accel_profile != INT_MIN) { | ||
1075 | sway_log(L_DEBUG, "apply_input_config(%s) accel_set_profile(%d)", ic->identifier, ic->accel_profile); | ||
1076 | libinput_device_config_accel_set_profile(dev, ic->accel_profile); | ||
1077 | } | ||
1078 | if (ic->click_method != INT_MIN) { | ||
1079 | sway_log(L_DEBUG, "apply_input_config(%s) click_set_method(%d)", ic->identifier, ic->click_method); | ||
1080 | libinput_device_config_click_set_method(dev, ic->click_method); | ||
1081 | } | ||
1082 | if (ic->drag_lock != INT_MIN) { | ||
1083 | sway_log(L_DEBUG, "apply_input_config(%s) tap_set_drag_lock_enabled(%d)", ic->identifier, ic->click_method); | ||
1084 | libinput_device_config_tap_set_drag_lock_enabled(dev, ic->drag_lock); | ||
1085 | } | ||
1086 | if (ic->dwt != INT_MIN) { | ||
1087 | sway_log(L_DEBUG, "apply_input_config(%s) dwt_set_enabled(%d)", ic->identifier, ic->dwt); | ||
1088 | libinput_device_config_dwt_set_enabled(dev, ic->dwt); | ||
1089 | } | ||
1090 | if (ic->left_handed != INT_MIN) { | ||
1091 | sway_log(L_DEBUG, "apply_input_config(%s) left_handed_set_enabled(%d)", ic->identifier, ic->left_handed); | ||
1092 | libinput_device_config_left_handed_set(dev, ic->left_handed); | ||
1093 | } | ||
1094 | if (ic->middle_emulation != INT_MIN) { | ||
1095 | sway_log(L_DEBUG, "apply_input_config(%s) middle_emulation_set_enabled(%d)", ic->identifier, ic->middle_emulation); | ||
1096 | libinput_device_config_middle_emulation_set_enabled(dev, ic->middle_emulation); | ||
1097 | } | ||
1098 | if (ic->natural_scroll != INT_MIN) { | ||
1099 | sway_log(L_DEBUG, "apply_input_config(%s) natural_scroll_set_enabled(%d)", ic->identifier, ic->natural_scroll); | ||
1100 | libinput_device_config_scroll_set_natural_scroll_enabled(dev, ic->natural_scroll); | ||
1101 | } | ||
1102 | if (ic->pointer_accel != FLT_MIN) { | ||
1103 | sway_log(L_DEBUG, "apply_input_config(%s) accel_set_speed(%f)", ic->identifier, ic->pointer_accel); | ||
1104 | libinput_device_config_accel_set_speed(dev, ic->pointer_accel); | ||
1105 | } | ||
1106 | if (ic->scroll_method != INT_MIN) { | ||
1107 | sway_log(L_DEBUG, "apply_input_config(%s) scroll_set_method(%d)", ic->identifier, ic->scroll_method); | ||
1108 | libinput_device_config_scroll_set_method(dev, ic->scroll_method); | ||
1109 | } | ||
1110 | if (ic->send_events != INT_MIN) { | ||
1111 | sway_log(L_DEBUG, "apply_input_config(%s) send_events_set_mode(%d)", ic->identifier, ic->send_events); | ||
1112 | libinput_device_config_send_events_set_mode(dev, ic->send_events); | ||
1113 | } | ||
1114 | if (ic->tap != INT_MIN) { | ||
1115 | sway_log(L_DEBUG, "apply_input_config(%s) tap_set_enabled(%d)", ic->identifier, ic->tap); | ||
1116 | libinput_device_config_tap_set_enabled(dev, ic->tap); | ||
1117 | } | ||
1118 | } | ||
1119 | |||
1120 | void apply_output_config(struct output_config *oc, swayc_t *output) { | ||
1121 | if (oc && oc->enabled == 0) { | ||
1122 | destroy_output(output); | ||
1123 | return; | ||
1124 | } | ||
1125 | |||
1126 | if (oc && oc->width > 0 && oc->height > 0) { | ||
1127 | output->width = oc->width; | ||
1128 | output->height = oc->height; | ||
1129 | |||
1130 | sway_log(L_DEBUG, "Set %s size to %ix%i (%d)", oc->name, oc->width, oc->height, oc->scale); | ||
1131 | struct wlc_size new_size = { .w = oc->width, .h = oc->height }; | ||
1132 | wlc_output_set_resolution(output->handle, &new_size, (uint32_t)oc->scale); | ||
1133 | } else if (oc) { | ||
1134 | const struct wlc_size *new_size = wlc_output_get_resolution(output->handle); | ||
1135 | wlc_output_set_resolution(output->handle, new_size, (uint32_t)oc->scale); | ||
1136 | } | ||
1137 | |||
1138 | // Find position for it | ||
1139 | if (oc && oc->x != -1 && oc->y != -1) { | ||
1140 | sway_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); | ||
1141 | output->x = oc->x; | ||
1142 | output->y = oc->y; | ||
1143 | } else { | ||
1144 | int x = 0; | ||
1145 | for (int i = 0; i < root_container.children->length; ++i) { | ||
1146 | swayc_t *c = root_container.children->items[i]; | ||
1147 | if (c->type == C_OUTPUT) { | ||
1148 | if (c->width + c->x > x) { | ||
1149 | x = c->width + c->x; | ||
1150 | } | ||
1151 | } | ||
1152 | } | ||
1153 | output->x = x; | ||
1154 | } | ||
1155 | |||
1156 | if (!oc || !oc->background) { | ||
1157 | // Look for a * config for background | ||
1158 | int i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
1159 | if (i >= 0) { | ||
1160 | oc = config->output_configs->items[i]; | ||
1161 | } else { | ||
1162 | oc = NULL; | ||
1163 | } | ||
1164 | } | ||
1165 | |||
1166 | int output_i; | ||
1167 | for (output_i = 0; output_i < root_container.children->length; ++output_i) { | ||
1168 | if (root_container.children->items[output_i] == output) { | ||
1169 | break; | ||
1170 | } | ||
1171 | } | ||
1172 | |||
1173 | if (oc && oc->background) { | ||
1174 | if (output->bg_pid != 0) { | ||
1175 | terminate_swaybg(output->bg_pid); | ||
1176 | } | ||
1177 | |||
1178 | sway_log(L_DEBUG, "Setting background for output %d to %s", output_i, oc->background); | ||
1179 | |||
1180 | size_t bufsize = 12; | ||
1181 | char output_id[bufsize]; | ||
1182 | snprintf(output_id, bufsize, "%d", output_i); | ||
1183 | output_id[bufsize-1] = 0; | ||
1184 | |||
1185 | char *const cmd[] = { | ||
1186 | "swaybg", | ||
1187 | output_id, | ||
1188 | oc->background, | ||
1189 | oc->background_option, | ||
1190 | NULL, | ||
1191 | }; | ||
1192 | |||
1193 | output->bg_pid = fork(); | ||
1194 | if (output->bg_pid == 0) { | ||
1195 | execvp(cmd[0], cmd); | ||
1196 | } | ||
1197 | } | ||
1198 | } | ||
1199 | |||
1200 | char *do_var_replacement(char *str) { | 672 | char *do_var_replacement(char *str) { |
1201 | int i; | 673 | int i; |
1202 | char *find = str; | 674 | char *find = str; |
@@ -1216,8 +688,9 @@ char *do_var_replacement(char *str) { | |||
1216 | int vvlen = strlen(var->value); | 688 | int vvlen = strlen(var->value); |
1217 | char *newstr = malloc(strlen(str) - vnlen + vvlen + 1); | 689 | char *newstr = malloc(strlen(str) - vnlen + vvlen + 1); |
1218 | if (!newstr) { | 690 | if (!newstr) { |
1219 | sway_log(L_ERROR, | 691 | wlr_log(L_ERROR, |
1220 | "Unable to allocate replacement during variable expansion"); | 692 | "Unable to allocate replacement " |
693 | "during variable expansion"); | ||
1221 | break; | 694 | break; |
1222 | } | 695 | } |
1223 | char *newptr = newstr; | 696 | char *newptr = newstr; |
@@ -1247,197 +720,3 @@ int workspace_output_cmp_workspace(const void *a, const void *b) { | |||
1247 | const struct workspace_output *wsa = a, *wsb = b; | 720 | const struct workspace_output *wsa = a, *wsb = b; |
1248 | return lenient_strcmp(wsa->workspace, wsb->workspace); | 721 | return lenient_strcmp(wsa->workspace, wsb->workspace); |
1249 | } | 722 | } |
1250 | |||
1251 | int sway_binding_cmp_keys(const void *a, const void *b) { | ||
1252 | const struct sway_binding *binda = a, *bindb = b; | ||
1253 | |||
1254 | // Count keys pressed for this binding. important so we check long before | ||
1255 | // short ones. for example mod+a+b before mod+a | ||
1256 | unsigned int moda = 0, modb = 0, i; | ||
1257 | |||
1258 | // Count how any modifiers are pressed | ||
1259 | for (i = 0; i < 8 * sizeof(binda->modifiers); ++i) { | ||
1260 | moda += (binda->modifiers & 1 << i) != 0; | ||
1261 | modb += (bindb->modifiers & 1 << i) != 0; | ||
1262 | } | ||
1263 | if (bindb->keys->length + modb != binda->keys->length + moda) { | ||
1264 | return (bindb->keys->length + modb) - (binda->keys->length + moda); | ||
1265 | } | ||
1266 | |||
1267 | // Otherwise compare keys | ||
1268 | if (binda->modifiers > bindb->modifiers) { | ||
1269 | return 1; | ||
1270 | } else if (binda->modifiers < bindb->modifiers) { | ||
1271 | return -1; | ||
1272 | } | ||
1273 | struct wlc_modifiers no_mods = { 0, 0 }; | ||
1274 | for (int i = 0; i < binda->keys->length; i++) { | ||
1275 | xkb_keysym_t ka = *(xkb_keysym_t *)binda->keys->items[i], | ||
1276 | kb = *(xkb_keysym_t *)bindb->keys->items[i]; | ||
1277 | if (binda->bindcode) { | ||
1278 | uint32_t *keycode = binda->keys->items[i]; | ||
1279 | ka = wlc_keyboard_get_keysym_for_key(*keycode, &no_mods); | ||
1280 | } | ||
1281 | |||
1282 | if (bindb->bindcode) { | ||
1283 | uint32_t *keycode = bindb->keys->items[i]; | ||
1284 | kb = wlc_keyboard_get_keysym_for_key(*keycode, &no_mods); | ||
1285 | } | ||
1286 | |||
1287 | if (ka > kb) { | ||
1288 | return 1; | ||
1289 | } else if (ka < kb) { | ||
1290 | return -1; | ||
1291 | } | ||
1292 | } | ||
1293 | |||
1294 | return 0; | ||
1295 | } | ||
1296 | |||
1297 | int sway_binding_cmp(const void *a, const void *b) { | ||
1298 | int cmp = 0; | ||
1299 | if ((cmp = sway_binding_cmp_keys(a, b)) != 0) { | ||
1300 | return cmp; | ||
1301 | } | ||
1302 | const struct sway_binding *binda = a, *bindb = b; | ||
1303 | return lenient_strcmp(binda->command, bindb->command); | ||
1304 | } | ||
1305 | |||
1306 | int sway_binding_cmp_qsort(const void *a, const void *b) { | ||
1307 | return sway_binding_cmp(*(void **)a, *(void **)b); | ||
1308 | } | ||
1309 | |||
1310 | void free_sway_binding(struct sway_binding *binding) { | ||
1311 | if (binding->keys) { | ||
1312 | for (int i = 0; i < binding->keys->length; i++) { | ||
1313 | free(binding->keys->items[i]); | ||
1314 | } | ||
1315 | list_free(binding->keys); | ||
1316 | } | ||
1317 | if (binding->command) { | ||
1318 | free(binding->command); | ||
1319 | } | ||
1320 | free(binding); | ||
1321 | } | ||
1322 | |||
1323 | int sway_mouse_binding_cmp_buttons(const void *a, const void *b) { | ||
1324 | const struct sway_mouse_binding *binda = a, *bindb = b; | ||
1325 | if (binda->button > bindb->button) { | ||
1326 | return 1; | ||
1327 | } | ||
1328 | if (binda->button < bindb->button) { | ||
1329 | return -1; | ||
1330 | } | ||
1331 | return 0; | ||
1332 | } | ||
1333 | |||
1334 | int sway_mouse_binding_cmp(const void *a, const void *b) { | ||
1335 | int cmp = 0; | ||
1336 | if ((cmp = sway_binding_cmp_keys(a, b)) != 0) { | ||
1337 | return cmp; | ||
1338 | } | ||
1339 | const struct sway_mouse_binding *binda = a, *bindb = b; | ||
1340 | return lenient_strcmp(binda->command, bindb->command); | ||
1341 | } | ||
1342 | |||
1343 | int sway_mouse_binding_cmp_qsort(const void *a, const void *b) { | ||
1344 | return sway_mouse_binding_cmp(*(void **)a, *(void **)b); | ||
1345 | } | ||
1346 | |||
1347 | void free_sway_mouse_binding(struct sway_mouse_binding *binding) { | ||
1348 | if (binding->command) { | ||
1349 | free(binding->command); | ||
1350 | } | ||
1351 | free(binding); | ||
1352 | } | ||
1353 | |||
1354 | struct sway_binding *sway_binding_dup(struct sway_binding *sb) { | ||
1355 | struct sway_binding *new_sb = malloc(sizeof(struct sway_binding)); | ||
1356 | if (!new_sb) { | ||
1357 | return NULL; | ||
1358 | } | ||
1359 | |||
1360 | new_sb->order = sb->order; | ||
1361 | new_sb->modifiers = sb->modifiers; | ||
1362 | new_sb->command = strdup(sb->command); | ||
1363 | |||
1364 | new_sb->keys = create_list(); | ||
1365 | int i; | ||
1366 | for (i = 0; i < sb->keys->length; ++i) { | ||
1367 | xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t)); | ||
1368 | if (!key) { | ||
1369 | free_sway_binding(new_sb); | ||
1370 | return NULL; | ||
1371 | } | ||
1372 | *key = *(xkb_keysym_t *)sb->keys->items[i]; | ||
1373 | list_add(new_sb->keys, key); | ||
1374 | } | ||
1375 | |||
1376 | return new_sb; | ||
1377 | } | ||
1378 | |||
1379 | struct bar_config *default_bar_config(void) { | ||
1380 | struct bar_config *bar = NULL; | ||
1381 | bar = malloc(sizeof(struct bar_config)); | ||
1382 | if (!bar) { | ||
1383 | return NULL; | ||
1384 | } | ||
1385 | if (!(bar->mode = strdup("dock"))) goto cleanup; | ||
1386 | if (!(bar->hidden_state = strdup("hide"))) goto cleanup; | ||
1387 | bar->modifier = WLC_BIT_MOD_LOGO; | ||
1388 | bar->outputs = NULL; | ||
1389 | bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM; | ||
1390 | if (!(bar->bindings = create_list())) goto cleanup; | ||
1391 | if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup; | ||
1392 | bar->pango_markup = false; | ||
1393 | bar->swaybar_command = NULL; | ||
1394 | bar->font = NULL; | ||
1395 | bar->height = -1; | ||
1396 | bar->workspace_buttons = true; | ||
1397 | bar->wrap_scroll = false; | ||
1398 | bar->separator_symbol = NULL; | ||
1399 | bar->strip_workspace_numbers = false; | ||
1400 | bar->binding_mode_indicator = true; | ||
1401 | #ifdef ENABLE_TRAY | ||
1402 | bar->tray_output = NULL; | ||
1403 | bar->icon_theme = NULL; | ||
1404 | bar->tray_padding = 2; | ||
1405 | bar->activate_button = 0x110; /* BTN_LEFT */ | ||
1406 | bar->context_button = 0x111; /* BTN_RIGHT */ | ||
1407 | bar->secondary_button = 0x112; /* BTN_MIDDLE */ | ||
1408 | #endif | ||
1409 | bar->verbose = false; | ||
1410 | bar->pid = 0; | ||
1411 | // set default colors | ||
1412 | if (!(bar->colors.background = strndup("#000000ff", 9))) goto cleanup; | ||
1413 | if (!(bar->colors.statusline = strndup("#ffffffff", 9))) goto cleanup; | ||
1414 | if (!(bar->colors.separator = strndup("#666666ff", 9))) goto cleanup; | ||
1415 | if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) goto cleanup; | ||
1416 | if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) goto cleanup; | ||
1417 | if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) goto cleanup; | ||
1418 | if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) goto cleanup; | ||
1419 | if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) goto cleanup; | ||
1420 | if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) goto cleanup; | ||
1421 | if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) goto cleanup; | ||
1422 | if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) goto cleanup; | ||
1423 | if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) goto cleanup; | ||
1424 | if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) goto cleanup; | ||
1425 | if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) goto cleanup; | ||
1426 | if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) goto cleanup; | ||
1427 | // if the following colors stay undefined, they fall back to background, | ||
1428 | // statusline, separator and urgent_workspace_*. | ||
1429 | bar->colors.focused_background = NULL; | ||
1430 | bar->colors.focused_statusline = NULL; | ||
1431 | bar->colors.focused_separator = NULL; | ||
1432 | bar->colors.binding_mode_border = NULL; | ||
1433 | bar->colors.binding_mode_bg = NULL; | ||
1434 | bar->colors.binding_mode_text = NULL; | ||
1435 | |||
1436 | list_add(config->bars, bar); | ||
1437 | |||
1438 | return bar; | ||
1439 | |||
1440 | cleanup: | ||
1441 | free_bar(bar); | ||
1442 | return NULL; | ||
1443 | } | ||
diff --git a/sway/config/bar.c b/sway/config/bar.c new file mode 100644 index 00000000..2913f059 --- /dev/null +++ b/sway/config/bar.c | |||
@@ -0,0 +1,240 @@ | |||
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 <wordexp.h> | ||
8 | #include <sys/types.h> | ||
9 | #include <sys/wait.h> | ||
10 | #include <sys/stat.h> | ||
11 | #include <signal.h> | ||
12 | #include <strings.h> | ||
13 | #include "sway/config.h" | ||
14 | #include "stringop.h" | ||
15 | #include "list.h" | ||
16 | #include "log.h" | ||
17 | |||
18 | static void terminate_swaybar(pid_t pid) { | ||
19 | wlr_log(L_DEBUG, "Terminating swaybar %d", pid); | ||
20 | int ret = kill(pid, SIGTERM); | ||
21 | if (ret != 0) { | ||
22 | wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid); | ||
23 | } else { | ||
24 | int status; | ||
25 | waitpid(pid, &status, 0); | ||
26 | } | ||
27 | } | ||
28 | |||
29 | void free_bar_config(struct bar_config *bar) { | ||
30 | if (!bar) { | ||
31 | return; | ||
32 | } | ||
33 | free(bar->mode); | ||
34 | free(bar->position); | ||
35 | free(bar->hidden_state); | ||
36 | free(bar->status_command); | ||
37 | free(bar->font); | ||
38 | free(bar->separator_symbol); | ||
39 | // TODO: Free mouse bindings | ||
40 | list_free(bar->bindings); | ||
41 | if (bar->outputs) { | ||
42 | free_flat_list(bar->outputs); | ||
43 | } | ||
44 | if (bar->pid != 0) { | ||
45 | terminate_swaybar(bar->pid); | ||
46 | } | ||
47 | free(bar->colors.background); | ||
48 | free(bar->colors.statusline); | ||
49 | free(bar->colors.separator); | ||
50 | free(bar->colors.focused_background); | ||
51 | free(bar->colors.focused_statusline); | ||
52 | free(bar->colors.focused_separator); | ||
53 | free(bar->colors.focused_workspace_border); | ||
54 | free(bar->colors.focused_workspace_bg); | ||
55 | free(bar->colors.focused_workspace_text); | ||
56 | free(bar->colors.active_workspace_border); | ||
57 | free(bar->colors.active_workspace_bg); | ||
58 | free(bar->colors.active_workspace_text); | ||
59 | free(bar->colors.inactive_workspace_border); | ||
60 | free(bar->colors.inactive_workspace_bg); | ||
61 | free(bar->colors.inactive_workspace_text); | ||
62 | free(bar->colors.urgent_workspace_border); | ||
63 | free(bar->colors.urgent_workspace_bg); | ||
64 | free(bar->colors.urgent_workspace_text); | ||
65 | free(bar->colors.binding_mode_border); | ||
66 | free(bar->colors.binding_mode_bg); | ||
67 | free(bar->colors.binding_mode_text); | ||
68 | free(bar); | ||
69 | } | ||
70 | |||
71 | struct bar_config *default_bar_config(void) { | ||
72 | struct bar_config *bar = NULL; | ||
73 | bar = malloc(sizeof(struct bar_config)); | ||
74 | if (!bar) { | ||
75 | return NULL; | ||
76 | } | ||
77 | if (!(bar->mode = strdup("dock"))) goto cleanup; | ||
78 | if (!(bar->hidden_state = strdup("hide"))) goto cleanup; | ||
79 | bar->outputs = NULL; | ||
80 | bar->position = strdup("bottom"); | ||
81 | if (!(bar->bindings = create_list())) goto cleanup; | ||
82 | if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup; | ||
83 | bar->pango_markup = false; | ||
84 | bar->swaybar_command = NULL; | ||
85 | bar->font = NULL; | ||
86 | bar->height = -1; | ||
87 | bar->workspace_buttons = true; | ||
88 | bar->wrap_scroll = false; | ||
89 | bar->separator_symbol = NULL; | ||
90 | bar->strip_workspace_numbers = false; | ||
91 | bar->binding_mode_indicator = true; | ||
92 | bar->verbose = false; | ||
93 | bar->pid = 0; | ||
94 | // set default colors | ||
95 | if (!(bar->colors.background = strndup("#000000ff", 9))) { | ||
96 | goto cleanup; | ||
97 | } | ||
98 | if (!(bar->colors.statusline = strndup("#ffffffff", 9))) { | ||
99 | goto cleanup; | ||
100 | } | ||
101 | if (!(bar->colors.separator = strndup("#666666ff", 9))) { | ||
102 | goto cleanup; | ||
103 | } | ||
104 | if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) { | ||
105 | goto cleanup; | ||
106 | } | ||
107 | if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) { | ||
108 | goto cleanup; | ||
109 | } | ||
110 | if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) { | ||
111 | goto cleanup; | ||
112 | } | ||
113 | if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) { | ||
114 | goto cleanup; | ||
115 | } | ||
116 | if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) { | ||
117 | goto cleanup; | ||
118 | } | ||
119 | if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) { | ||
120 | goto cleanup; | ||
121 | } | ||
122 | if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) { | ||
123 | goto cleanup; | ||
124 | } | ||
125 | if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) { | ||
126 | goto cleanup; | ||
127 | } | ||
128 | if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) { | ||
129 | goto cleanup; | ||
130 | } | ||
131 | if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) { | ||
132 | goto cleanup; | ||
133 | } | ||
134 | if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) { | ||
135 | goto cleanup; | ||
136 | } | ||
137 | if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) { | ||
138 | goto cleanup; | ||
139 | } | ||
140 | // if the following colors stay undefined, they fall back to background, | ||
141 | // statusline, separator and urgent_workspace_*. | ||
142 | bar->colors.focused_background = NULL; | ||
143 | bar->colors.focused_statusline = NULL; | ||
144 | bar->colors.focused_separator = NULL; | ||
145 | bar->colors.binding_mode_border = NULL; | ||
146 | bar->colors.binding_mode_bg = NULL; | ||
147 | bar->colors.binding_mode_text = NULL; | ||
148 | |||
149 | list_add(config->bars, bar); | ||
150 | return bar; | ||
151 | cleanup: | ||
152 | free_bar_config(bar); | ||
153 | return NULL; | ||
154 | } | ||
155 | |||
156 | void invoke_swaybar(struct bar_config *bar) { | ||
157 | // Pipe to communicate errors | ||
158 | int filedes[2]; | ||
159 | if (pipe(filedes) == -1) { | ||
160 | wlr_log(L_ERROR, "Pipe setup failed! Cannot fork into bar"); | ||
161 | return; | ||
162 | } | ||
163 | |||
164 | bar->pid = fork(); | ||
165 | if (bar->pid == 0) { | ||
166 | close(filedes[0]); | ||
167 | |||
168 | // run custom swaybar | ||
169 | size_t len = snprintf(NULL, 0, "%s -b %s", | ||
170 | bar->swaybar_command ? bar->swaybar_command : "swaybar", | ||
171 | bar->id); | ||
172 | char *command = malloc(len + 1); | ||
173 | if (!command) { | ||
174 | const char msg[] = "Unable to allocate swaybar command string"; | ||
175 | size_t len = sizeof(msg); | ||
176 | if (write(filedes[1], &len, sizeof(int))) {}; | ||
177 | if (write(filedes[1], msg, len)) {}; | ||
178 | close(filedes[1]); | ||
179 | exit(1); | ||
180 | } | ||
181 | snprintf(command, len + 1, "%s -b %s", | ||
182 | bar->swaybar_command ? bar->swaybar_command : "swaybar", | ||
183 | bar->id); | ||
184 | char *const cmd[] = { "sh", "-c", command, NULL, }; | ||
185 | close(filedes[1]); | ||
186 | execvp(cmd[0], cmd); | ||
187 | exit(1); | ||
188 | } | ||
189 | wlr_log(L_DEBUG, "Spawned swaybar %d", bar->pid); | ||
190 | close(filedes[0]); | ||
191 | ssize_t len; | ||
192 | if (read(filedes[1], &len, sizeof(int)) == sizeof(int)) { | ||
193 | char *buf = malloc(len); | ||
194 | if(!buf) { | ||
195 | wlr_log(L_ERROR, "Cannot allocate error string"); | ||
196 | return; | ||
197 | } | ||
198 | if (read(filedes[1], buf, len)) { | ||
199 | wlr_log(L_ERROR, "%s", buf); | ||
200 | } | ||
201 | free(buf); | ||
202 | } | ||
203 | close(filedes[1]); | ||
204 | } | ||
205 | |||
206 | static bool active_output(const char *name) { | ||
207 | struct sway_container *cont = NULL; | ||
208 | for (int i = 0; i < root_container.children->length; ++i) { | ||
209 | cont = root_container.children->items[i]; | ||
210 | if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) { | ||
211 | return true; | ||
212 | } | ||
213 | } | ||
214 | return false; | ||
215 | } | ||
216 | |||
217 | void load_swaybars() { | ||
218 | for (int i = 0; i < config->bars->length; ++i) { | ||
219 | struct bar_config *bar = config->bars->items[i]; | ||
220 | bool apply = false; | ||
221 | if (bar->outputs) { | ||
222 | for (int j = 0; j < bar->outputs->length; ++j) { | ||
223 | char *o = bar->outputs->items[j]; | ||
224 | if (!strcmp(o, "*") || active_output(o)) { | ||
225 | apply = true; | ||
226 | break; | ||
227 | } | ||
228 | } | ||
229 | } else { | ||
230 | apply = true; | ||
231 | } | ||
232 | if (apply) { | ||
233 | if (bar->pid != 0) { | ||
234 | terminate_swaybar(bar->pid); | ||
235 | } | ||
236 | wlr_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); | ||
237 | invoke_swaybar(bar); | ||
238 | } | ||
239 | } | ||
240 | } | ||
diff --git a/sway/config/input.c b/sway/config/input.c new file mode 100644 index 00000000..5e657c43 --- /dev/null +++ b/sway/config/input.c | |||
@@ -0,0 +1,119 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include <stdlib.h> | ||
3 | #include <limits.h> | ||
4 | #include <float.h> | ||
5 | #include "sway/config.h" | ||
6 | #include "log.h" | ||
7 | |||
8 | struct input_config *new_input_config(const char* identifier) { | ||
9 | struct input_config *input = calloc(1, sizeof(struct input_config)); | ||
10 | if (!input) { | ||
11 | wlr_log(L_DEBUG, "Unable to allocate input config"); | ||
12 | return NULL; | ||
13 | } | ||
14 | wlr_log(L_DEBUG, "new_input_config(%s)", identifier); | ||
15 | if (!(input->identifier = strdup(identifier))) { | ||
16 | free(input); | ||
17 | wlr_log(L_DEBUG, "Unable to allocate input config"); | ||
18 | return NULL; | ||
19 | } | ||
20 | |||
21 | input->tap = INT_MIN; | ||
22 | input->drag_lock = INT_MIN; | ||
23 | input->dwt = INT_MIN; | ||
24 | input->send_events = INT_MIN; | ||
25 | input->click_method = INT_MIN; | ||
26 | input->middle_emulation = INT_MIN; | ||
27 | input->natural_scroll = INT_MIN; | ||
28 | input->accel_profile = INT_MIN; | ||
29 | input->pointer_accel = FLT_MIN; | ||
30 | input->scroll_method = INT_MIN; | ||
31 | input->left_handed = INT_MIN; | ||
32 | |||
33 | return input; | ||
34 | } | ||
35 | |||
36 | void merge_input_config(struct input_config *dst, struct input_config *src) { | ||
37 | if (src->identifier) { | ||
38 | free(dst->identifier); | ||
39 | dst->identifier = strdup(src->identifier); | ||
40 | } | ||
41 | if (src->accel_profile != INT_MIN) { | ||
42 | dst->accel_profile = src->accel_profile; | ||
43 | } | ||
44 | if (src->click_method != INT_MIN) { | ||
45 | dst->click_method = src->click_method; | ||
46 | } | ||
47 | if (src->drag_lock != INT_MIN) { | ||
48 | dst->drag_lock = src->drag_lock; | ||
49 | } | ||
50 | if (src->dwt != INT_MIN) { | ||
51 | dst->dwt = src->dwt; | ||
52 | } | ||
53 | if (src->middle_emulation != INT_MIN) { | ||
54 | dst->middle_emulation = src->middle_emulation; | ||
55 | } | ||
56 | if (src->natural_scroll != INT_MIN) { | ||
57 | dst->natural_scroll = src->natural_scroll; | ||
58 | } | ||
59 | if (src->pointer_accel != FLT_MIN) { | ||
60 | dst->pointer_accel = src->pointer_accel; | ||
61 | } | ||
62 | if (src->scroll_method != INT_MIN) { | ||
63 | dst->scroll_method = src->scroll_method; | ||
64 | } | ||
65 | if (src->send_events != INT_MIN) { | ||
66 | dst->send_events = src->send_events; | ||
67 | } | ||
68 | if (src->tap != INT_MIN) { | ||
69 | dst->tap = src->tap; | ||
70 | } | ||
71 | if (src->xkb_layout) { | ||
72 | free(dst->xkb_layout); | ||
73 | dst->xkb_layout = strdup(src->xkb_layout); | ||
74 | } | ||
75 | if (src->xkb_model) { | ||
76 | free(dst->xkb_model); | ||
77 | dst->xkb_model = strdup(src->xkb_model); | ||
78 | } | ||
79 | if (src->xkb_options) { | ||
80 | free(dst->xkb_options); | ||
81 | dst->xkb_options = strdup(src->xkb_options); | ||
82 | } | ||
83 | if (src->xkb_rules) { | ||
84 | free(dst->xkb_rules); | ||
85 | dst->xkb_rules = strdup(src->xkb_rules); | ||
86 | } | ||
87 | if (src->xkb_variant) { | ||
88 | free(dst->xkb_variant); | ||
89 | dst->xkb_variant = strdup(src->xkb_variant); | ||
90 | } | ||
91 | if (src->mapped_output) { | ||
92 | free(dst->mapped_output); | ||
93 | dst->mapped_output = strdup(src->mapped_output); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | struct input_config *copy_input_config(struct input_config *ic) { | ||
98 | struct input_config *copy = calloc(1, sizeof(struct input_config)); | ||
99 | if (copy == NULL) { | ||
100 | wlr_log(L_ERROR, "could not allocate input config"); | ||
101 | return NULL; | ||
102 | } | ||
103 | merge_input_config(copy, ic); | ||
104 | return copy; | ||
105 | } | ||
106 | |||
107 | void free_input_config(struct input_config *ic) { | ||
108 | if (!ic) { | ||
109 | return; | ||
110 | } | ||
111 | free(ic->identifier); | ||
112 | free(ic); | ||
113 | } | ||
114 | |||
115 | int input_identifier_cmp(const void *item, const void *data) { | ||
116 | const struct input_config *ic = item; | ||
117 | const char *identifier = data; | ||
118 | return strcmp(ic->identifier, identifier); | ||
119 | } | ||
diff --git a/sway/config/output.c b/sway/config/output.c new file mode 100644 index 00000000..1c298d37 --- /dev/null +++ b/sway/config/output.c | |||
@@ -0,0 +1,213 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include <assert.h> | ||
3 | #include <stdbool.h> | ||
4 | #include <string.h> | ||
5 | #include <signal.h> | ||
6 | #include <sys/wait.h> | ||
7 | #include <unistd.h> | ||
8 | #include <wlr/types/wlr_output.h> | ||
9 | #include <wlr/types/wlr_output_layout.h> | ||
10 | #include "sway/config.h" | ||
11 | #include "sway/output.h" | ||
12 | #include "log.h" | ||
13 | |||
14 | int output_name_cmp(const void *item, const void *data) { | ||
15 | const struct output_config *output = item; | ||
16 | const char *name = data; | ||
17 | |||
18 | return strcmp(output->name, name); | ||
19 | } | ||
20 | |||
21 | void output_get_identifier(char *identifier, size_t len, | ||
22 | struct sway_output *output) { | ||
23 | struct wlr_output *wlr_output = output->wlr_output; | ||
24 | snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, | ||
25 | wlr_output->serial); | ||
26 | } | ||
27 | |||
28 | struct output_config *new_output_config(const char *name) { | ||
29 | struct output_config *oc = calloc(1, sizeof(struct output_config)); | ||
30 | if (oc == NULL) { | ||
31 | return NULL; | ||
32 | } | ||
33 | oc->name = strdup(name); | ||
34 | if (oc->name == NULL) { | ||
35 | free(oc); | ||
36 | return NULL; | ||
37 | } | ||
38 | oc->enabled = -1; | ||
39 | oc->width = oc->height = -1; | ||
40 | oc->refresh_rate = -1; | ||
41 | oc->x = oc->y = -1; | ||
42 | oc->scale = -1; | ||
43 | oc->transform = -1; | ||
44 | return oc; | ||
45 | } | ||
46 | |||
47 | void merge_output_config(struct output_config *dst, struct output_config *src) { | ||
48 | if (src->name) { | ||
49 | free(dst->name); | ||
50 | dst->name = strdup(src->name); | ||
51 | } | ||
52 | if (src->enabled != -1) { | ||
53 | dst->enabled = src->enabled; | ||
54 | } | ||
55 | if (src->width != -1) { | ||
56 | dst->width = src->width; | ||
57 | } | ||
58 | if (src->height != -1) { | ||
59 | dst->height = src->height; | ||
60 | } | ||
61 | if (src->x != -1) { | ||
62 | dst->x = src->x; | ||
63 | } | ||
64 | if (src->y != -1) { | ||
65 | dst->y = src->y; | ||
66 | } | ||
67 | if (src->scale != -1) { | ||
68 | dst->scale = src->scale; | ||
69 | } | ||
70 | if (src->refresh_rate != -1) { | ||
71 | dst->refresh_rate = src->refresh_rate; | ||
72 | } | ||
73 | if (src->transform != -1) { | ||
74 | dst->transform = src->transform; | ||
75 | } | ||
76 | if (src->background) { | ||
77 | free(dst->background); | ||
78 | dst->background = strdup(src->background); | ||
79 | } | ||
80 | if (src->background_option) { | ||
81 | free(dst->background_option); | ||
82 | dst->background_option = strdup(src->background_option); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | static void set_mode(struct wlr_output *output, int width, int height, | ||
87 | float refresh_rate) { | ||
88 | int mhz = (int)(refresh_rate * 1000); | ||
89 | if (wl_list_empty(&output->modes)) { | ||
90 | wlr_log(L_DEBUG, "Assigning custom mode to %s", output->name); | ||
91 | wlr_output_set_custom_mode(output, width, height, mhz); | ||
92 | return; | ||
93 | } | ||
94 | |||
95 | struct wlr_output_mode *mode, *best = NULL; | ||
96 | wl_list_for_each(mode, &output->modes, link) { | ||
97 | if (mode->width == width && mode->height == height) { | ||
98 | if (mode->refresh == mhz) { | ||
99 | best = mode; | ||
100 | break; | ||
101 | } | ||
102 | best = mode; | ||
103 | } | ||
104 | } | ||
105 | if (!best) { | ||
106 | wlr_log(L_ERROR, "Configured mode for %s not available", output->name); | ||
107 | } else { | ||
108 | wlr_log(L_DEBUG, "Assigning configured mode to %s", output->name); | ||
109 | wlr_output_set_mode(output, best); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | void terminate_swaybg(pid_t pid) { | ||
114 | int ret = kill(pid, SIGTERM); | ||
115 | if (ret != 0) { | ||
116 | wlr_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid); | ||
117 | } else { | ||
118 | int status; | ||
119 | waitpid(pid, &status, 0); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | void apply_output_config(struct output_config *oc, struct sway_container *output) { | ||
124 | assert(output->type == C_OUTPUT); | ||
125 | |||
126 | struct wlr_output_layout *output_layout = | ||
127 | root_container.sway_root->output_layout; | ||
128 | struct wlr_output *wlr_output = output->sway_output->wlr_output; | ||
129 | |||
130 | if (oc && oc->enabled == 0) { | ||
131 | container_destroy(output); | ||
132 | wlr_output_layout_remove(root_container.sway_root->output_layout, | ||
133 | wlr_output); | ||
134 | return; | ||
135 | } | ||
136 | |||
137 | if (oc && oc->width > 0 && oc->height > 0) { | ||
138 | wlr_log(L_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, | ||
139 | oc->height, oc->refresh_rate); | ||
140 | set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate); | ||
141 | } | ||
142 | if (oc && oc->scale > 0) { | ||
143 | wlr_log(L_DEBUG, "Set %s scale to %f", oc->name, oc->scale); | ||
144 | wlr_output_set_scale(wlr_output, oc->scale); | ||
145 | } | ||
146 | if (oc && oc->transform >= 0) { | ||
147 | wlr_log(L_DEBUG, "Set %s transform to %d", oc->name, oc->transform); | ||
148 | wlr_output_set_transform(wlr_output, oc->transform); | ||
149 | } | ||
150 | |||
151 | // Find position for it | ||
152 | if (oc && (oc->x != -1 || oc->y != -1)) { | ||
153 | wlr_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); | ||
154 | wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y); | ||
155 | } else { | ||
156 | wlr_output_layout_add_auto(output_layout, wlr_output); | ||
157 | } | ||
158 | |||
159 | if (!oc || !oc->background) { | ||
160 | // Look for a * config for background | ||
161 | int i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
162 | if (i >= 0) { | ||
163 | oc = config->output_configs->items[i]; | ||
164 | } else { | ||
165 | oc = NULL; | ||
166 | } | ||
167 | } | ||
168 | |||
169 | int output_i; | ||
170 | for (output_i = 0; output_i < root_container.children->length; ++output_i) { | ||
171 | if (root_container.children->items[output_i] == output) { | ||
172 | break; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | if (oc && oc->background) { | ||
177 | if (output->sway_output->bg_pid != 0) { | ||
178 | terminate_swaybg(output->sway_output->bg_pid); | ||
179 | } | ||
180 | |||
181 | wlr_log(L_DEBUG, "Setting background for output %d to %s", | ||
182 | output_i, oc->background); | ||
183 | |||
184 | size_t len = snprintf(NULL, 0, "%s %d %s %s", | ||
185 | config->swaybg_command ? config->swaybg_command : "swaybg", | ||
186 | output_i, oc->background, oc->background_option); | ||
187 | char *command = malloc(len + 1); | ||
188 | if (!command) { | ||
189 | wlr_log(L_DEBUG, "Unable to allocate swaybg command"); | ||
190 | return; | ||
191 | } | ||
192 | snprintf(command, len + 1, "%s %d %s %s", | ||
193 | config->swaybg_command ? config->swaybg_command : "swaybg", | ||
194 | output_i, oc->background, oc->background_option); | ||
195 | wlr_log(L_DEBUG, "-> %s", command); | ||
196 | |||
197 | char *const cmd[] = { "sh", "-c", command, NULL }; | ||
198 | output->sway_output->bg_pid = fork(); | ||
199 | if (output->sway_output->bg_pid == 0) { | ||
200 | execvp(cmd[0], cmd); | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | |||
205 | void free_output_config(struct output_config *oc) { | ||
206 | if (!oc) { | ||
207 | return; | ||
208 | } | ||
209 | free(oc->name); | ||
210 | free(oc->background); | ||
211 | free(oc->background_option); | ||
212 | free(oc); | ||
213 | } | ||
diff --git a/sway/config/seat.c b/sway/config/seat.c new file mode 100644 index 00000000..bd8b45c8 --- /dev/null +++ b/sway/config/seat.c | |||
@@ -0,0 +1,146 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | #include "sway/config.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | struct seat_config *new_seat_config(const char* name) { | ||
8 | struct seat_config *seat = calloc(1, sizeof(struct seat_config)); | ||
9 | if (!seat) { | ||
10 | wlr_log(L_DEBUG, "Unable to allocate seat config"); | ||
11 | return NULL; | ||
12 | } | ||
13 | |||
14 | wlr_log(L_DEBUG, "new_seat_config(%s)", name); | ||
15 | seat->name = strdup(name); | ||
16 | if (!sway_assert(seat->name, "could not allocate name for seat")) { | ||
17 | free(seat); | ||
18 | return NULL; | ||
19 | } | ||
20 | |||
21 | seat->fallback = -1; | ||
22 | seat->attachments = create_list(); | ||
23 | if (!sway_assert(seat->attachments, | ||
24 | "could not allocate seat attachments list")) { | ||
25 | free(seat->name); | ||
26 | free(seat); | ||
27 | return NULL; | ||
28 | } | ||
29 | |||
30 | return seat; | ||
31 | } | ||
32 | |||
33 | struct seat_attachment_config *seat_attachment_config_new() { | ||
34 | struct seat_attachment_config *attachment = | ||
35 | calloc(1, sizeof(struct seat_attachment_config)); | ||
36 | if (!attachment) { | ||
37 | wlr_log(L_DEBUG, "cannot allocate attachment config"); | ||
38 | return NULL; | ||
39 | } | ||
40 | return attachment; | ||
41 | } | ||
42 | |||
43 | static void seat_attachment_config_free( | ||
44 | struct seat_attachment_config *attachment) { | ||
45 | free(attachment->identifier); | ||
46 | free(attachment); | ||
47 | return; | ||
48 | } | ||
49 | |||
50 | static struct seat_attachment_config *seat_attachment_config_copy( | ||
51 | struct seat_attachment_config *attachment) { | ||
52 | struct seat_attachment_config *copy = seat_attachment_config_new(); | ||
53 | if (!copy) { | ||
54 | return NULL; | ||
55 | } | ||
56 | |||
57 | copy->identifier = strdup(attachment->identifier); | ||
58 | |||
59 | return copy; | ||
60 | } | ||
61 | |||
62 | static void merge_seat_attachment_config(struct seat_attachment_config *dest, | ||
63 | struct seat_attachment_config *source) { | ||
64 | // nothing to merge yet, but there will be some day | ||
65 | } | ||
66 | |||
67 | void merge_seat_config(struct seat_config *dest, struct seat_config *source) { | ||
68 | if (source->name) { | ||
69 | free(dest->name); | ||
70 | dest->name = strdup(source->name); | ||
71 | } | ||
72 | |||
73 | if (source->fallback != -1) { | ||
74 | dest->fallback = source->fallback; | ||
75 | } | ||
76 | |||
77 | for (int i = 0; i < source->attachments->length; ++i) { | ||
78 | struct seat_attachment_config *source_attachment = | ||
79 | source->attachments->items[i]; | ||
80 | bool found = false; | ||
81 | for (int j = 0; j < dest->attachments->length; ++j) { | ||
82 | struct seat_attachment_config *dest_attachment = | ||
83 | dest->attachments->items[j]; | ||
84 | if (strcmp(source_attachment->identifier, | ||
85 | dest_attachment->identifier) == 0) { | ||
86 | merge_seat_attachment_config(dest_attachment, | ||
87 | source_attachment); | ||
88 | found = true; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | if (!found) { | ||
93 | struct seat_attachment_config *copy = | ||
94 | seat_attachment_config_copy(source_attachment); | ||
95 | if (copy) { | ||
96 | list_add(dest->attachments, copy); | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | |||
102 | struct seat_config *copy_seat_config(struct seat_config *seat) { | ||
103 | struct seat_config *copy = new_seat_config(seat->name); | ||
104 | if (copy == NULL) { | ||
105 | return NULL; | ||
106 | } | ||
107 | |||
108 | merge_seat_config(copy, seat); | ||
109 | |||
110 | return copy; | ||
111 | } | ||
112 | |||
113 | void free_seat_config(struct seat_config *seat) { | ||
114 | if (!seat) { | ||
115 | return; | ||
116 | } | ||
117 | |||
118 | free(seat->name); | ||
119 | for (int i = 0; i < seat->attachments->length; ++i) { | ||
120 | struct seat_attachment_config *attachment = | ||
121 | seat->attachments->items[i]; | ||
122 | seat_attachment_config_free(attachment); | ||
123 | } | ||
124 | |||
125 | list_free(seat->attachments); | ||
126 | free(seat); | ||
127 | } | ||
128 | |||
129 | int seat_name_cmp(const void *item, const void *data) { | ||
130 | const struct seat_config *sc = item; | ||
131 | const char *name = data; | ||
132 | return strcmp(sc->name, name); | ||
133 | } | ||
134 | |||
135 | struct seat_attachment_config *seat_config_get_attachment( | ||
136 | struct seat_config *seat_config, char *identifier) { | ||
137 | for (int i = 0; i < seat_config->attachments->length; ++i) { | ||
138 | struct seat_attachment_config *attachment = | ||
139 | seat_config->attachments->items[i]; | ||
140 | if (strcmp(attachment->identifier, identifier) == 0) { | ||
141 | return attachment; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | return NULL; | ||
146 | } | ||
diff --git a/sway/container.c b/sway/container.c deleted file mode 100644 index 829fde69..00000000 --- a/sway/container.c +++ /dev/null | |||
@@ -1,1016 +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 "sway/config.h" | ||
8 | #include "sway/container.h" | ||
9 | #include "sway/workspace.h" | ||
10 | #include "sway/focus.h" | ||
11 | #include "sway/border.h" | ||
12 | #include "sway/layout.h" | ||
13 | #include "sway/input_state.h" | ||
14 | #include "sway/ipc-server.h" | ||
15 | #include "sway/output.h" | ||
16 | #include "log.h" | ||
17 | #include "stringop.h" | ||
18 | |||
19 | #define ASSERT_NONNULL(PTR) \ | ||
20 | sway_assert (PTR, #PTR "must be non-null") | ||
21 | |||
22 | static swayc_t *new_swayc(enum swayc_types type) { | ||
23 | // next id starts at 1 because 0 is assigned to root_container in layout.c | ||
24 | static size_t next_id = 1; | ||
25 | swayc_t *c = calloc(1, sizeof(swayc_t)); | ||
26 | if (!c) { | ||
27 | return NULL; | ||
28 | } | ||
29 | c->id = next_id++; | ||
30 | c->handle = -1; | ||
31 | c->gaps = -1; | ||
32 | c->layout = L_NONE; | ||
33 | c->workspace_layout = L_NONE; | ||
34 | c->type = type; | ||
35 | c->nb_master = 1; | ||
36 | c->nb_slave_groups = 1; | ||
37 | if (type != C_VIEW) { | ||
38 | c->children = create_list(); | ||
39 | } | ||
40 | return c; | ||
41 | } | ||
42 | |||
43 | static void free_swayc(swayc_t *cont) { | ||
44 | if (!ASSERT_NONNULL(cont)) { | ||
45 | return; | ||
46 | } | ||
47 | if (cont->children) { | ||
48 | // remove children until there are no more, free_swayc calls | ||
49 | // remove_child, which removes child from this container | ||
50 | while (cont->children->length) { | ||
51 | free_swayc(cont->children->items[0]); | ||
52 | } | ||
53 | list_free(cont->children); | ||
54 | } | ||
55 | if (cont->unmanaged) { | ||
56 | list_free(cont->unmanaged); | ||
57 | } | ||
58 | if (cont->floating) { | ||
59 | while (cont->floating->length) { | ||
60 | free_swayc(cont->floating->items[0]); | ||
61 | } | ||
62 | list_free(cont->floating); | ||
63 | } | ||
64 | if (cont->marks) { | ||
65 | list_foreach(cont->marks, free); | ||
66 | list_free(cont->marks); | ||
67 | } | ||
68 | if (cont->parent) { | ||
69 | remove_child(cont); | ||
70 | } | ||
71 | if (cont->name) { | ||
72 | free(cont->name); | ||
73 | } | ||
74 | if (cont->class) { | ||
75 | free(cont->class); | ||
76 | } | ||
77 | if (cont->instance) { | ||
78 | free(cont->instance); | ||
79 | } | ||
80 | if (cont->app_id) { | ||
81 | free(cont->app_id); | ||
82 | } | ||
83 | if (cont->bg_pid != 0) { | ||
84 | terminate_swaybg(cont->bg_pid); | ||
85 | } | ||
86 | if (cont->border) { | ||
87 | if (cont->border->buffer) { | ||
88 | free(cont->border->buffer); | ||
89 | } | ||
90 | free(cont->border); | ||
91 | } | ||
92 | free(cont); | ||
93 | } | ||
94 | |||
95 | static void update_root_geometry() { | ||
96 | int width = 0; | ||
97 | int height = 0; | ||
98 | swayc_t *child; | ||
99 | int child_width; | ||
100 | int child_height; | ||
101 | |||
102 | for (int i = 0; i < root_container.children->length; ++i) { | ||
103 | child = root_container.children->items[i]; | ||
104 | child_width = child->width + child->x; | ||
105 | child_height = child->height + child->y; | ||
106 | if (child_width > width) { | ||
107 | width = child_width; | ||
108 | } | ||
109 | |||
110 | if (child_height > height) { | ||
111 | height = child_height; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | root_container.width = width; | ||
116 | root_container.height = height; | ||
117 | } | ||
118 | |||
119 | // New containers | ||
120 | |||
121 | swayc_t *new_output(wlc_handle handle) { | ||
122 | struct wlc_size size; | ||
123 | output_get_scaled_size(handle, &size); | ||
124 | const char *name = wlc_output_get_name(handle); | ||
125 | // Find current outputs to see if this already exists | ||
126 | { | ||
127 | int i, len = root_container.children->length; | ||
128 | for (i = 0; i < len; ++i) { | ||
129 | swayc_t *op = root_container.children->items[i]; | ||
130 | const char *op_name = op->name; | ||
131 | if (op_name && name && strcmp(op_name, name) == 0) { | ||
132 | sway_log(L_DEBUG, "restoring output %" PRIuPTR ":%s", handle, op_name); | ||
133 | return op; | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | sway_log(L_DEBUG, "New output %" PRIuPTR ":%s", handle, name); | ||
139 | |||
140 | struct output_config *oc = NULL, *all = NULL; | ||
141 | int i; | ||
142 | for (i = 0; i < config->output_configs->length; ++i) { | ||
143 | struct output_config *cur = config->output_configs->items[i]; | ||
144 | if (strcasecmp(name, cur->name) == 0) { | ||
145 | sway_log(L_DEBUG, "Matched output config for %s", name); | ||
146 | oc = cur; | ||
147 | } | ||
148 | if (strcasecmp("*", cur->name) == 0) { | ||
149 | sway_log(L_DEBUG, "Matched wildcard output config for %s", name); | ||
150 | all = cur; | ||
151 | } | ||
152 | |||
153 | if (oc && all) { | ||
154 | break; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | if (!oc) { | ||
159 | oc = all; | ||
160 | } | ||
161 | |||
162 | if (oc && !oc->enabled) { | ||
163 | return NULL; | ||
164 | } | ||
165 | |||
166 | swayc_t *output = new_swayc(C_OUTPUT); | ||
167 | output->handle = handle; | ||
168 | output->name = name ? strdup(name) : NULL; | ||
169 | output->width = size.w; | ||
170 | output->height = size.h; | ||
171 | output->unmanaged = create_list(); | ||
172 | output->bg_pid = 0; | ||
173 | |||
174 | apply_output_config(oc, output); | ||
175 | add_child(&root_container, output); | ||
176 | load_swaybars(); | ||
177 | |||
178 | // Create workspace | ||
179 | char *ws_name = NULL; | ||
180 | swayc_t *ws = NULL; | ||
181 | |||
182 | if (name) { | ||
183 | for (i = 0; i < config->workspace_outputs->length; ++i) { | ||
184 | struct workspace_output *wso = config->workspace_outputs->items[i]; | ||
185 | if (strcasecmp(wso->output, name) == 0) { | ||
186 | sway_log(L_DEBUG, "Matched workspace to output: %s for %s", wso->workspace, wso->output); | ||
187 | // Check if any other workspaces are using this name | ||
188 | if ((ws = workspace_by_name(wso->workspace))) { | ||
189 | // if yes, move those to this output, because they should be here | ||
190 | move_workspace_to(ws, output); | ||
191 | } else if (!ws_name) { | ||
192 | // set a workspace name in case we need to create a default one | ||
193 | ws_name = strdup(wso->workspace); | ||
194 | } | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | |||
199 | if (output->children->length == 0) { | ||
200 | if (!ws_name) { | ||
201 | ws_name = workspace_next_name(output->name); | ||
202 | } | ||
203 | // create and initialize default workspace | ||
204 | sway_log(L_DEBUG, "Creating default workspace %s", ws_name); | ||
205 | ws = new_workspace(output, ws_name); | ||
206 | ws->is_focused = true; | ||
207 | } else { | ||
208 | sort_workspaces(output); | ||
209 | set_focused_container(output->children->items[0]); | ||
210 | } | ||
211 | |||
212 | free(ws_name); | ||
213 | update_root_geometry(); | ||
214 | return output; | ||
215 | } | ||
216 | |||
217 | swayc_t *new_workspace(swayc_t *output, const char *name) { | ||
218 | if (!ASSERT_NONNULL(output)) { | ||
219 | return NULL; | ||
220 | } | ||
221 | sway_log(L_DEBUG, "Added workspace %s for output %u", name, (unsigned int)output->handle); | ||
222 | swayc_t *workspace = new_swayc(C_WORKSPACE); | ||
223 | |||
224 | workspace->prev_layout = L_NONE; | ||
225 | workspace->layout = default_layout(output); | ||
226 | workspace->workspace_layout = default_layout(output); | ||
227 | |||
228 | workspace->x = output->x; | ||
229 | workspace->y = output->y; | ||
230 | workspace->width = output->width; | ||
231 | workspace->height = output->height; | ||
232 | workspace->name = !name ? NULL : strdup(name); | ||
233 | workspace->visible = false; | ||
234 | workspace->floating = create_list(); | ||
235 | |||
236 | add_child(output, workspace); | ||
237 | sort_workspaces(output); | ||
238 | |||
239 | return workspace; | ||
240 | } | ||
241 | |||
242 | swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) { | ||
243 | if (!ASSERT_NONNULL(child) | ||
244 | && !sway_assert(!child->is_floating, "cannot create container around floating window")) { | ||
245 | return NULL; | ||
246 | } | ||
247 | swayc_t *cont = new_swayc(C_CONTAINER); | ||
248 | |||
249 | sway_log(L_DEBUG, "creating container %p around %p", cont, child); | ||
250 | |||
251 | cont->prev_layout = L_NONE; | ||
252 | cont->layout = layout; | ||
253 | cont->width = child->width; | ||
254 | cont->height = child->height; | ||
255 | cont->x = child->x; | ||
256 | cont->y = child->y; | ||
257 | cont->visible = child->visible; | ||
258 | cont->cached_geometry = child->cached_geometry; | ||
259 | cont->gaps = child->gaps; | ||
260 | |||
261 | /* Container inherits all of workspaces children, layout and whatnot */ | ||
262 | if (child->type == C_WORKSPACE) { | ||
263 | swayc_t *workspace = child; | ||
264 | // reorder focus | ||
265 | cont->focused = workspace->focused; | ||
266 | workspace->focused = cont; | ||
267 | // set all children focu to container | ||
268 | int i; | ||
269 | for (i = 0; i < workspace->children->length; ++i) { | ||
270 | ((swayc_t *)workspace->children->items[i])->parent = cont; | ||
271 | } | ||
272 | // Swap children | ||
273 | list_t *tmp_list = workspace->children; | ||
274 | workspace->children = cont->children; | ||
275 | cont->children = tmp_list; | ||
276 | // add container to workspace chidren | ||
277 | add_child(workspace, cont); | ||
278 | // give them proper layouts | ||
279 | cont->layout = workspace->workspace_layout; | ||
280 | cont->prev_layout = workspace->prev_layout; | ||
281 | /* TODO: might break shit in move_container!!! workspace->layout = layout; */ | ||
282 | set_focused_container_for(workspace, get_focused_view(workspace)); | ||
283 | } else { // Or is built around container | ||
284 | swayc_t *parent = replace_child(child, cont); | ||
285 | if (parent) { | ||
286 | add_child(cont, child); | ||
287 | } | ||
288 | } | ||
289 | return cont; | ||
290 | } | ||
291 | |||
292 | swayc_t *new_view(swayc_t *sibling, wlc_handle handle) { | ||
293 | if (!ASSERT_NONNULL(sibling)) { | ||
294 | return NULL; | ||
295 | } | ||
296 | const char *title = wlc_view_get_title(handle); | ||
297 | swayc_t *view = new_swayc(C_VIEW); | ||
298 | sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%s to container %p %d", | ||
299 | handle, title, sibling, sibling ? sibling->type : 0); | ||
300 | // Setup values | ||
301 | view->handle = handle; | ||
302 | view->name = title ? strdup(title) : NULL; | ||
303 | const char *class = wlc_view_get_class(handle); | ||
304 | view->class = class ? strdup(class) : NULL; | ||
305 | const char *instance = wlc_view_get_instance(handle); | ||
306 | view->instance = instance ? strdup(instance) : NULL; | ||
307 | const char *app_id = wlc_view_get_app_id(handle); | ||
308 | view->app_id = app_id ? strdup(app_id) : NULL; | ||
309 | view->visible = true; | ||
310 | view->is_focused = true; | ||
311 | view->sticky = false; | ||
312 | view->width = 0; | ||
313 | view->height = 0; | ||
314 | view->desired_width = -1; | ||
315 | view->desired_height = -1; | ||
316 | // setup border | ||
317 | view->border_type = config->border; | ||
318 | view->border_thickness = config->border_thickness; | ||
319 | |||
320 | view->is_floating = false; | ||
321 | |||
322 | if (sibling->type == C_WORKSPACE) { | ||
323 | // Case of focused workspace, just create as child of it | ||
324 | add_child(sibling, view); | ||
325 | } else { | ||
326 | // Regular case, create as sibling of current container | ||
327 | add_sibling(sibling, view); | ||
328 | } | ||
329 | return view; | ||
330 | } | ||
331 | |||
332 | swayc_t *new_floating_view(wlc_handle handle) { | ||
333 | if (swayc_active_workspace() == NULL) { | ||
334 | return NULL; | ||
335 | } | ||
336 | const char *title = wlc_view_get_title(handle); | ||
337 | swayc_t *view = new_swayc(C_VIEW); | ||
338 | sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%x:%s as a floating view", | ||
339 | handle, wlc_view_get_type(handle), title); | ||
340 | // Setup values | ||
341 | view->handle = handle; | ||
342 | view->name = title ? strdup(title) : NULL; | ||
343 | const char *class = wlc_view_get_class(handle); | ||
344 | view->class = class ? strdup(class) : NULL; | ||
345 | const char *instance = wlc_view_get_instance(handle); | ||
346 | view->instance = instance ? strdup(instance) : NULL; | ||
347 | const char *app_id = wlc_view_get_app_id(handle); | ||
348 | view->app_id = app_id ? strdup(app_id) : NULL; | ||
349 | view->visible = true; | ||
350 | view->sticky = false; | ||
351 | |||
352 | // Set the geometry of the floating view | ||
353 | const struct wlc_geometry *geometry = wlc_view_get_geometry(handle); | ||
354 | |||
355 | // give it requested geometry, but place in center if possible | ||
356 | // in top left otherwise | ||
357 | if (geometry->size.w != 0) { | ||
358 | view->x = (swayc_active_workspace()->width - geometry->size.w) / 2; | ||
359 | } else { | ||
360 | view->x = 0; | ||
361 | } | ||
362 | if (geometry->size.h != 0) { | ||
363 | view->y = (swayc_active_workspace()->height - geometry->size.h) / 2; | ||
364 | } else { | ||
365 | view->y = 0; | ||
366 | } | ||
367 | |||
368 | view->width = geometry->size.w; | ||
369 | view->height = geometry->size.h; | ||
370 | |||
371 | view->desired_width = view->width; | ||
372 | view->desired_height = view->height; | ||
373 | |||
374 | // setup border | ||
375 | view->border_type = config->floating_border; | ||
376 | view->border_thickness = config->floating_border_thickness; | ||
377 | |||
378 | view->is_floating = true; | ||
379 | |||
380 | // Case of focused workspace, just create as child of it | ||
381 | list_add(swayc_active_workspace()->floating, view); | ||
382 | view->parent = swayc_active_workspace(); | ||
383 | if (swayc_active_workspace()->focused == NULL) { | ||
384 | set_focused_container_for(swayc_active_workspace(), view); | ||
385 | } | ||
386 | return view; | ||
387 | } | ||
388 | |||
389 | void floating_view_sane_size(swayc_t *view) { | ||
390 | // floating_minimum is used as sane value. | ||
391 | // floating_maximum has priority in case of conflict | ||
392 | // TODO: implement total_outputs_dimensions() | ||
393 | if (config->floating_minimum_height != -1 && | ||
394 | view->desired_height < config->floating_minimum_height) { | ||
395 | view->desired_height = config->floating_minimum_height; | ||
396 | } | ||
397 | if (config->floating_minimum_width != -1 && | ||
398 | view->desired_width < config->floating_minimum_width) { | ||
399 | view->desired_width = config->floating_minimum_width; | ||
400 | } | ||
401 | |||
402 | // if 0 do not resize, only enforce max value | ||
403 | if (config->floating_maximum_height == 0) { | ||
404 | // Missing total_outputs_dimensions() using swayc_active_workspace() | ||
405 | config->floating_maximum_height = swayc_active_workspace()->height; | ||
406 | |||
407 | } else if (config->floating_maximum_height != -1 && | ||
408 | view->desired_height > config->floating_maximum_height) { | ||
409 | view->desired_height = config->floating_maximum_height; | ||
410 | } | ||
411 | |||
412 | // if 0 do not resize, only enforce max value | ||
413 | if (config->floating_maximum_width == 0) { | ||
414 | // Missing total_outputs_dimensions() using swayc_active_workspace() | ||
415 | config->floating_maximum_width = swayc_active_workspace()->width; | ||
416 | |||
417 | } else if (config->floating_maximum_width != -1 && | ||
418 | view->desired_width > config->floating_maximum_width) { | ||
419 | view->desired_width = config->floating_maximum_width; | ||
420 | } | ||
421 | |||
422 | sway_log(L_DEBUG, "Sane values for view to %d x %d @ %.f, %.f", | ||
423 | view->desired_width, view->desired_height, view->x, view->y); | ||
424 | |||
425 | return; | ||
426 | } | ||
427 | |||
428 | |||
429 | // Destroy container | ||
430 | |||
431 | swayc_t *destroy_output(swayc_t *output) { | ||
432 | if (!ASSERT_NONNULL(output)) { | ||
433 | return NULL; | ||
434 | } | ||
435 | if (output->children->length > 0) { | ||
436 | // TODO save workspaces when there are no outputs. | ||
437 | // TODO also check if there will ever be no outputs except for exiting | ||
438 | // program | ||
439 | if (root_container.children->length > 1) { | ||
440 | int p = root_container.children->items[0] == output; | ||
441 | // Move workspace from this output to another output | ||
442 | while (output->children->length) { | ||
443 | swayc_t *child = output->children->items[0]; | ||
444 | remove_child(child); | ||
445 | add_child(root_container.children->items[p], child); | ||
446 | } | ||
447 | sort_workspaces(root_container.children->items[p]); | ||
448 | update_visibility(root_container.children->items[p]); | ||
449 | arrange_windows(root_container.children->items[p], -1, -1); | ||
450 | } | ||
451 | } | ||
452 | sway_log(L_DEBUG, "OUTPUT: Destroying output '%" PRIuPTR "'", output->handle); | ||
453 | free_swayc(output); | ||
454 | update_root_geometry(); | ||
455 | return &root_container; | ||
456 | } | ||
457 | |||
458 | swayc_t *destroy_workspace(swayc_t *workspace) { | ||
459 | if (!ASSERT_NONNULL(workspace)) { | ||
460 | return NULL; | ||
461 | } | ||
462 | |||
463 | // Do not destroy this if it's the last workspace on this output | ||
464 | swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT); | ||
465 | if (output && output->children->length == 1) { | ||
466 | return NULL; | ||
467 | } | ||
468 | |||
469 | swayc_t *parent = workspace->parent; | ||
470 | // destroy the WS if there are no children | ||
471 | if (workspace->children->length == 0 && workspace->floating->length == 0) { | ||
472 | sway_log(L_DEBUG, "destroying workspace '%s'", workspace->name); | ||
473 | ipc_event_workspace(workspace, NULL, "empty"); | ||
474 | } else { | ||
475 | // Move children to a different workspace on this output | ||
476 | swayc_t *new_workspace = NULL; | ||
477 | int i; | ||
478 | for(i = 0; i < output->children->length; i++) { | ||
479 | if(output->children->items[i] != workspace) { | ||
480 | break; | ||
481 | } | ||
482 | } | ||
483 | new_workspace = output->children->items[i]; | ||
484 | |||
485 | sway_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'", | ||
486 | workspace->name, new_workspace->name); | ||
487 | |||
488 | for(i = 0; i < workspace->children->length; i++) { | ||
489 | move_container_to(workspace->children->items[i], new_workspace); | ||
490 | } | ||
491 | |||
492 | for(i = 0; i < workspace->floating->length; i++) { | ||
493 | move_container_to(workspace->floating->items[i], new_workspace); | ||
494 | } | ||
495 | } | ||
496 | |||
497 | free_swayc(workspace); | ||
498 | return parent; | ||
499 | } | ||
500 | |||
501 | swayc_t *destroy_container(swayc_t *container) { | ||
502 | if (!ASSERT_NONNULL(container)) { | ||
503 | return NULL; | ||
504 | } | ||
505 | while (container->children->length == 0 && container->type == C_CONTAINER) { | ||
506 | sway_log(L_DEBUG, "Container: Destroying container '%p'", container); | ||
507 | swayc_t *parent = container->parent; | ||
508 | free_swayc(container); | ||
509 | container = parent; | ||
510 | } | ||
511 | return container; | ||
512 | } | ||
513 | |||
514 | swayc_t *destroy_view(swayc_t *view) { | ||
515 | if (!ASSERT_NONNULL(view)) { | ||
516 | return NULL; | ||
517 | } | ||
518 | sway_log(L_DEBUG, "Destroying view '%p'", view); | ||
519 | swayc_t *parent = view->parent; | ||
520 | free_swayc(view); | ||
521 | |||
522 | // Destroy empty containers | ||
523 | if (parent && parent->type == C_CONTAINER) { | ||
524 | return destroy_container(parent); | ||
525 | } | ||
526 | return parent; | ||
527 | } | ||
528 | |||
529 | // Container lookup | ||
530 | |||
531 | |||
532 | swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data) { | ||
533 | if (!container->children) { | ||
534 | return NULL; | ||
535 | } | ||
536 | // Special case for checking floating stuff | ||
537 | int i; | ||
538 | if (container->type == C_WORKSPACE) { | ||
539 | for (i = 0; i < container->floating->length; ++i) { | ||
540 | swayc_t *child = container->floating->items[i]; | ||
541 | if (test(child, data)) { | ||
542 | return child; | ||
543 | } | ||
544 | } | ||
545 | } | ||
546 | for (i = 0; i < container->children->length; ++i) { | ||
547 | swayc_t *child = container->children->items[i]; | ||
548 | if (test(child, data)) { | ||
549 | return child; | ||
550 | } else { | ||
551 | swayc_t *res = swayc_by_test(child, test, data); | ||
552 | if (res) { | ||
553 | return res; | ||
554 | } | ||
555 | } | ||
556 | } | ||
557 | return NULL; | ||
558 | } | ||
559 | |||
560 | static bool test_name(swayc_t *view, void *data) { | ||
561 | if (!view || !view->name) { | ||
562 | return false; | ||
563 | } | ||
564 | return strcmp(view->name, data) == 0; | ||
565 | } | ||
566 | |||
567 | swayc_t *swayc_by_name(const char *name) { | ||
568 | return swayc_by_test(&root_container, test_name, (void *)name); | ||
569 | } | ||
570 | |||
571 | swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) { | ||
572 | if (!ASSERT_NONNULL(container)) { | ||
573 | return NULL; | ||
574 | } | ||
575 | if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) { | ||
576 | return NULL; | ||
577 | } | ||
578 | do { | ||
579 | container = container->parent; | ||
580 | } while (container && container->type != type); | ||
581 | return container; | ||
582 | } | ||
583 | |||
584 | swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts layout) { | ||
585 | if (!ASSERT_NONNULL(container)) { | ||
586 | return NULL; | ||
587 | } | ||
588 | if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) { | ||
589 | return NULL; | ||
590 | } | ||
591 | do { | ||
592 | container = container->parent; | ||
593 | } while (container && container->layout != layout); | ||
594 | return container; | ||
595 | } | ||
596 | |||
597 | swayc_t *swayc_focus_by_type(swayc_t *container, enum swayc_types type) { | ||
598 | if (!ASSERT_NONNULL(container)) { | ||
599 | return NULL; | ||
600 | } | ||
601 | if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) { | ||
602 | return NULL; | ||
603 | } | ||
604 | do { | ||
605 | container = container->focused; | ||
606 | } while (container && container->type != type); | ||
607 | return container; | ||
608 | } | ||
609 | |||
610 | swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts layout) { | ||
611 | if (!ASSERT_NONNULL(container)) { | ||
612 | return NULL; | ||
613 | } | ||
614 | if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) { | ||
615 | return NULL; | ||
616 | } | ||
617 | do { | ||
618 | container = container->focused; | ||
619 | } while (container && container->layout != layout); | ||
620 | return container; | ||
621 | } | ||
622 | |||
623 | |||
624 | static swayc_t *_swayc_by_handle_helper(wlc_handle handle, swayc_t *parent) { | ||
625 | if (!parent || !parent->children) { | ||
626 | return NULL; | ||
627 | } | ||
628 | int i, len; | ||
629 | swayc_t **child; | ||
630 | if (parent->type == C_WORKSPACE) { | ||
631 | len = parent->floating->length; | ||
632 | child = (swayc_t **)parent->floating->items; | ||
633 | for (i = 0; i < len; ++i, ++child) { | ||
634 | if ((*child)->handle == handle) { | ||
635 | return *child; | ||
636 | } | ||
637 | } | ||
638 | } | ||
639 | |||
640 | len = parent->children->length; | ||
641 | child = (swayc_t**)parent->children->items; | ||
642 | for (i = 0; i < len; ++i, ++child) { | ||
643 | if ((*child)->handle == handle) { | ||
644 | return *child; | ||
645 | } else { | ||
646 | swayc_t *res; | ||
647 | if ((res = _swayc_by_handle_helper(handle, *child))) { | ||
648 | return res; | ||
649 | } | ||
650 | } | ||
651 | } | ||
652 | return NULL; | ||
653 | } | ||
654 | |||
655 | swayc_t *swayc_by_handle(wlc_handle handle) { | ||
656 | return _swayc_by_handle_helper(handle, &root_container); | ||
657 | } | ||
658 | |||
659 | swayc_t *swayc_active_output(void) { | ||
660 | return root_container.focused; | ||
661 | } | ||
662 | |||
663 | swayc_t *swayc_active_workspace(void) { | ||
664 | return root_container.focused ? root_container.focused->focused : NULL; | ||
665 | } | ||
666 | |||
667 | swayc_t *swayc_active_workspace_for(swayc_t *cont) { | ||
668 | if (!cont) { | ||
669 | return NULL; | ||
670 | } | ||
671 | switch (cont->type) { | ||
672 | case C_ROOT: | ||
673 | cont = cont->focused; | ||
674 | /* Fallthrough */ | ||
675 | |||
676 | case C_OUTPUT: | ||
677 | cont = cont ? cont->focused : NULL; | ||
678 | /* Fallthrough */ | ||
679 | |||
680 | case C_WORKSPACE: | ||
681 | return cont; | ||
682 | |||
683 | default: | ||
684 | return swayc_parent_by_type(cont, C_WORKSPACE); | ||
685 | } | ||
686 | } | ||
687 | |||
688 | static bool pointer_test(swayc_t *view, void *_origin) { | ||
689 | const struct wlc_point *origin = _origin; | ||
690 | // Determine the output that the view is under | ||
691 | swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT); | ||
692 | if (origin->x >= view->x && origin->y >= view->y | ||
693 | && origin->x < view->x + view->width && origin->y < view->y + view->height | ||
694 | && view->visible && parent == root_container.focused) { | ||
695 | return true; | ||
696 | } | ||
697 | return false; | ||
698 | } | ||
699 | |||
700 | swayc_t *container_under_pointer(void) { | ||
701 | // root.output->workspace | ||
702 | if (!root_container.focused) { | ||
703 | return NULL; | ||
704 | } | ||
705 | swayc_t *lookup = root_container.focused; | ||
706 | // Case of empty workspace | ||
707 | if (lookup->children && !lookup->unmanaged) { | ||
708 | return NULL; | ||
709 | } | ||
710 | double x, y; | ||
711 | wlc_pointer_get_position_v2(&x, &y); | ||
712 | struct wlc_point origin = { .x = x, .y = y }; | ||
713 | |||
714 | while (lookup && lookup->type != C_VIEW) { | ||
715 | int i; | ||
716 | int len; | ||
717 | for (int _i = 0; lookup->unmanaged && _i < lookup->unmanaged->length; ++_i) { | ||
718 | wlc_handle *handle = lookup->unmanaged->items[_i]; | ||
719 | const struct wlc_geometry *geo = wlc_view_get_geometry(*handle); | ||
720 | if (origin.x >= geo->origin.x && origin.y >= geo->origin.y | ||
721 | && origin.x < geo->origin.x + (int)geo->size.w | ||
722 | && origin.y < geo->origin.y + (int)geo->size.h) { | ||
723 | // Hack: we force focus upon unmanaged views here | ||
724 | wlc_view_focus(*handle); | ||
725 | return NULL; | ||
726 | } | ||
727 | } | ||
728 | // if tabbed/stacked go directly to focused container, otherwise search | ||
729 | // children | ||
730 | if (lookup->layout == L_TABBED || lookup->layout == L_STACKED) { | ||
731 | lookup = lookup->focused; | ||
732 | continue; | ||
733 | } | ||
734 | // if workspace, search floating | ||
735 | if (lookup->type == C_WORKSPACE) { | ||
736 | i = len = lookup->floating->length; | ||
737 | bool got_floating = false; | ||
738 | while (--i > -1) { | ||
739 | if (pointer_test(lookup->floating->items[i], &origin)) { | ||
740 | lookup = lookup->floating->items[i]; | ||
741 | got_floating = true; | ||
742 | break; | ||
743 | } | ||
744 | } | ||
745 | if (got_floating) { | ||
746 | continue; | ||
747 | } | ||
748 | } | ||
749 | // search children | ||
750 | len = lookup->children->length; | ||
751 | for (i = 0; i < len; ++i) { | ||
752 | if (pointer_test(lookup->children->items[i], &origin)) { | ||
753 | lookup = lookup->children->items[i]; | ||
754 | break; | ||
755 | } | ||
756 | } | ||
757 | // when border and titles are done, this could happen | ||
758 | if (i == len) { | ||
759 | break; | ||
760 | } | ||
761 | } | ||
762 | return lookup; | ||
763 | } | ||
764 | |||
765 | swayc_t *container_find(swayc_t *container, bool (*f)(swayc_t *, const void *), const void *data) { | ||
766 | if (container->children == NULL || container->children->length == 0) { | ||
767 | return NULL; | ||
768 | } | ||
769 | |||
770 | swayc_t *con; | ||
771 | if (container->type == C_WORKSPACE) { | ||
772 | for (int i = 0; i < container->floating->length; ++i) { | ||
773 | con = container->floating->items[i]; | ||
774 | if (f(con, data)) { | ||
775 | return con; | ||
776 | } | ||
777 | con = container_find(con, f, data); | ||
778 | if (con != NULL) { | ||
779 | return con; | ||
780 | } | ||
781 | } | ||
782 | } | ||
783 | |||
784 | for (int i = 0; i < container->children->length; ++i) { | ||
785 | con = container->children->items[i]; | ||
786 | if (f(con, data)) { | ||
787 | return con; | ||
788 | } | ||
789 | |||
790 | con = container_find(con, f, data); | ||
791 | if (con != NULL) { | ||
792 | return con; | ||
793 | } | ||
794 | } | ||
795 | |||
796 | return NULL; | ||
797 | } | ||
798 | |||
799 | // Container information | ||
800 | |||
801 | bool swayc_is_fullscreen(swayc_t *view) { | ||
802 | return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN); | ||
803 | } | ||
804 | |||
805 | bool swayc_is_active(swayc_t *view) { | ||
806 | return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_ACTIVATED); | ||
807 | } | ||
808 | |||
809 | bool swayc_is_parent_of(swayc_t *parent, swayc_t *child) { | ||
810 | while (child != &root_container) { | ||
811 | child = child->parent; | ||
812 | if (child == parent) { | ||
813 | return true; | ||
814 | } | ||
815 | } | ||
816 | return false; | ||
817 | } | ||
818 | |||
819 | bool swayc_is_child_of(swayc_t *child, swayc_t *parent) { | ||
820 | return swayc_is_parent_of(parent, child); | ||
821 | } | ||
822 | |||
823 | bool swayc_is_empty_workspace(swayc_t *container) { | ||
824 | return container->type == C_WORKSPACE && container->children->length == 0; | ||
825 | } | ||
826 | |||
827 | int swayc_gap(swayc_t *container) { | ||
828 | if (container->type == C_VIEW || container->type == C_CONTAINER) { | ||
829 | return container->gaps >= 0 ? container->gaps : config->gaps_inner; | ||
830 | } else if (container->type == C_WORKSPACE) { | ||
831 | int base = container->gaps >= 0 ? container->gaps : config->gaps_outer; | ||
832 | if (config->edge_gaps && !(config->smart_gaps && container->children->length == 1)) { | ||
833 | // the inner gap is created via a margin around each window which | ||
834 | // is half the gap size, so the workspace also needs half a gap | ||
835 | // size to make the outermost gap the same size (excluding the | ||
836 | // actual "outer gap" size which is handled independently) | ||
837 | return base + config->gaps_inner / 2; | ||
838 | } else if (config->smart_gaps && container->children->length == 1) { | ||
839 | return 0; | ||
840 | } else { | ||
841 | return base; | ||
842 | } | ||
843 | } else { | ||
844 | return 0; | ||
845 | } | ||
846 | } | ||
847 | |||
848 | // Mapping | ||
849 | |||
850 | void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) { | ||
851 | if (container) { | ||
852 | int i; | ||
853 | if (container->children) { | ||
854 | for (i = 0; i < container->children->length; ++i) { | ||
855 | swayc_t *child = container->children->items[i]; | ||
856 | container_map(child, f, data); | ||
857 | } | ||
858 | } | ||
859 | if (container->floating) { | ||
860 | for (i = 0; i < container->floating->length; ++i) { | ||
861 | swayc_t *child = container->floating->items[i]; | ||
862 | container_map(child, f, data); | ||
863 | } | ||
864 | } | ||
865 | f(container, data); | ||
866 | } | ||
867 | } | ||
868 | |||
869 | void update_visibility_output(swayc_t *container, wlc_handle output) { | ||
870 | // Inherit visibility | ||
871 | swayc_t *parent = container->parent; | ||
872 | container->visible = parent->visible; | ||
873 | // special cases where visibility depends on focus | ||
874 | if (parent->type == C_OUTPUT || parent->layout == L_TABBED || | ||
875 | parent->layout == L_STACKED) { | ||
876 | container->visible = parent->focused == container && parent->visible; | ||
877 | } | ||
878 | // Set visibility and output for view | ||
879 | if (container->type == C_VIEW) { | ||
880 | wlc_view_set_output(container->handle, output); | ||
881 | wlc_view_set_mask(container->handle, container->visible ? VISIBLE : 0); | ||
882 | } | ||
883 | // Update visibility for children | ||
884 | else { | ||
885 | if (container->children) { | ||
886 | int i, len = container->children->length; | ||
887 | for (i = 0; i < len; ++i) { | ||
888 | update_visibility_output(container->children->items[i], output); | ||
889 | } | ||
890 | } | ||
891 | if (container->floating) { | ||
892 | int i, len = container->floating->length; | ||
893 | for (i = 0; i < len; ++i) { | ||
894 | update_visibility_output(container->floating->items[i], output); | ||
895 | } | ||
896 | } | ||
897 | } | ||
898 | } | ||
899 | |||
900 | void update_visibility(swayc_t *container) { | ||
901 | if (!container) return; | ||
902 | switch (container->type) { | ||
903 | case C_ROOT: | ||
904 | container->visible = true; | ||
905 | if (container->children) { | ||
906 | int i, len = container->children->length; | ||
907 | for (i = 0; i < len; ++i) { | ||
908 | update_visibility(container->children->items[i]); | ||
909 | } | ||
910 | } | ||
911 | return; | ||
912 | |||
913 | case C_OUTPUT: | ||
914 | container->visible = true; | ||
915 | if (container->children) { | ||
916 | int i, len = container->children->length; | ||
917 | for (i = 0; i < len; ++i) { | ||
918 | update_visibility_output(container->children->items[i], container->handle); | ||
919 | } | ||
920 | } | ||
921 | return; | ||
922 | |||
923 | default: | ||
924 | { | ||
925 | swayc_t *op = swayc_parent_by_type(container, C_OUTPUT); | ||
926 | update_visibility_output(container, op->handle); | ||
927 | } | ||
928 | } | ||
929 | } | ||
930 | |||
931 | void set_gaps(swayc_t *view, void *_data) { | ||
932 | int *data = _data; | ||
933 | if (!ASSERT_NONNULL(view)) { | ||
934 | return; | ||
935 | } | ||
936 | if (view->type == C_WORKSPACE || view->type == C_VIEW) { | ||
937 | view->gaps = *data; | ||
938 | } | ||
939 | } | ||
940 | |||
941 | void add_gaps(swayc_t *view, void *_data) { | ||
942 | int *data = _data; | ||
943 | if (!ASSERT_NONNULL(view)) { | ||
944 | return; | ||
945 | } | ||
946 | if (view->type == C_WORKSPACE || view->type == C_VIEW) { | ||
947 | if ((view->gaps += *data) < 0) { | ||
948 | view->gaps = 0; | ||
949 | } | ||
950 | } | ||
951 | } | ||
952 | |||
953 | static void close_view(swayc_t *container, void *data) { | ||
954 | if (container->type == C_VIEW) { | ||
955 | wlc_view_close(container->handle); | ||
956 | } | ||
957 | } | ||
958 | |||
959 | void close_views(swayc_t *container) { | ||
960 | container_map(container, close_view, NULL); | ||
961 | } | ||
962 | |||
963 | swayc_t *swayc_tabbed_stacked_ancestor(swayc_t *view) { | ||
964 | swayc_t *parent = NULL; | ||
965 | if (!ASSERT_NONNULL(view)) { | ||
966 | return NULL; | ||
967 | } | ||
968 | while (view->type != C_WORKSPACE && view->parent && view->parent->type != C_WORKSPACE) { | ||
969 | view = view->parent; | ||
970 | if (view->layout == L_TABBED || view->layout == L_STACKED) { | ||
971 | parent = view; | ||
972 | } | ||
973 | } | ||
974 | |||
975 | return parent; | ||
976 | } | ||
977 | |||
978 | swayc_t *swayc_tabbed_stacked_parent(swayc_t *con) { | ||
979 | if (!ASSERT_NONNULL(con)) { | ||
980 | return NULL; | ||
981 | } | ||
982 | if (con->parent && (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED)) { | ||
983 | return con->parent; | ||
984 | } | ||
985 | return NULL; | ||
986 | } | ||
987 | |||
988 | swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) { | ||
989 | // if layout change modifies the auto layout's major axis, swap width and height | ||
990 | // to preserve current ratios. | ||
991 | if (is_auto_layout(layout) && is_auto_layout(container->layout)) { | ||
992 | enum swayc_layouts prev_major = | ||
993 | container->layout == L_AUTO_LEFT || container->layout == L_AUTO_RIGHT | ||
994 | ? L_HORIZ : L_VERT; | ||
995 | enum swayc_layouts new_major = | ||
996 | layout == L_AUTO_LEFT || layout == L_AUTO_RIGHT | ||
997 | ? L_HORIZ : L_VERT; | ||
998 | if (new_major != prev_major) { | ||
999 | for (int i = 0; i < container->children->length; ++i) { | ||
1000 | swayc_t *child = container->children->items[i]; | ||
1001 | double h = child->height; | ||
1002 | child->height = child->width; | ||
1003 | child->width = h; | ||
1004 | } | ||
1005 | } | ||
1006 | } | ||
1007 | if (container->type == C_WORKSPACE) { | ||
1008 | container->workspace_layout = layout; | ||
1009 | if (layout == L_HORIZ || layout == L_VERT || is_auto_layout(layout)) { | ||
1010 | container->layout = layout; | ||
1011 | } | ||
1012 | } else { | ||
1013 | container->layout = layout; | ||
1014 | } | ||
1015 | return container; | ||
1016 | } | ||
diff --git a/sway/criteria.c b/sway/criteria.c index e8978ebe..22e9a49b 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -4,13 +4,15 @@ | |||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <pcre.h> | 5 | #include <pcre.h> |
6 | #include "sway/criteria.h" | 6 | #include "sway/criteria.h" |
7 | #include "sway/container.h" | 7 | #include "sway/tree/container.h" |
8 | #include "sway/config.h" | 8 | #include "sway/config.h" |
9 | #include "sway/tree/view.h" | ||
9 | #include "stringop.h" | 10 | #include "stringop.h" |
10 | #include "list.h" | 11 | #include "list.h" |
11 | #include "log.h" | 12 | #include "log.h" |
12 | 13 | ||
13 | enum criteria_type { // *must* keep in sync with criteria_strings[] | 14 | enum criteria_type { // *must* keep in sync with criteria_strings[] |
15 | CRIT_APP_ID, | ||
14 | CRIT_CLASS, | 16 | CRIT_CLASS, |
15 | CRIT_CON_ID, | 17 | CRIT_CON_ID, |
16 | CRIT_CON_MARK, | 18 | CRIT_CON_MARK, |
@@ -27,6 +29,7 @@ enum criteria_type { // *must* keep in sync with criteria_strings[] | |||
27 | }; | 29 | }; |
28 | 30 | ||
29 | static const char * const criteria_strings[CRIT_LAST] = { | 31 | static const char * const criteria_strings[CRIT_LAST] = { |
32 | [CRIT_APP_ID] = "app_id", | ||
30 | [CRIT_CLASS] = "class", | 33 | [CRIT_CLASS] = "class", |
31 | [CRIT_CON_ID] = "con_id", | 34 | [CRIT_CON_ID] = "con_id", |
32 | [CRIT_CON_MARK] = "con_mark", | 35 | [CRIT_CON_MARK] = "con_mark", |
@@ -100,8 +103,9 @@ static int countchr(char *str, char c) { | |||
100 | // of buf. | 103 | // of buf. |
101 | // | 104 | // |
102 | // Returns error string or NULL if successful. | 105 | // Returns error string or NULL if successful. |
103 | static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str) { | 106 | static char *crit_tokens(int *argc, char ***buf, |
104 | sway_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str); | 107 | const char * const criteria_str) { |
108 | wlr_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str); | ||
105 | char *base = criteria_from(criteria_str); | 109 | char *base = criteria_from(criteria_str); |
106 | char *head = base; | 110 | char *head = base; |
107 | char *namep = head; // start of criteria name | 111 | char *namep = head; // start of criteria name |
@@ -247,13 +251,13 @@ char *extract_crit_tokens(list_t *tokens, const char * const criteria) { | |||
247 | free_crit_token(token); | 251 | free_crit_token(token); |
248 | goto ect_cleanup; | 252 | goto ect_cleanup; |
249 | } else if (token->type == CRIT_URGENT || crit_is_focused(value)) { | 253 | } else if (token->type == CRIT_URGENT || crit_is_focused(value)) { |
250 | sway_log(L_DEBUG, "%s -> \"%s\"", name, value); | 254 | wlr_log(L_DEBUG, "%s -> \"%s\"", name, value); |
251 | list_add(tokens, token); | 255 | list_add(tokens, token); |
252 | } else if((error = generate_regex(&token->regex, value))) { | 256 | } else if((error = generate_regex(&token->regex, value))) { |
253 | free_crit_token(token); | 257 | free_crit_token(token); |
254 | goto ect_cleanup; | 258 | goto ect_cleanup; |
255 | } else { | 259 | } else { |
256 | sway_log(L_DEBUG, "%s -> /%s/", name, value); | 260 | wlr_log(L_DEBUG, "%s -> /%s/", name, value); |
257 | list_add(tokens, token); | 261 | list_add(tokens, token); |
258 | } | 262 | } |
259 | } | 263 | } |
@@ -268,8 +272,8 @@ static int regex_cmp(const char *item, const pcre *regex) { | |||
268 | } | 272 | } |
269 | 273 | ||
270 | // test a single view if it matches list of criteria tokens (all of them). | 274 | // 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) { | 275 | static bool criteria_test(struct sway_container *cont, list_t *tokens) { |
272 | if (cont->type != C_VIEW) { | 276 | if (cont->type != C_CONTAINER && cont->type != C_VIEW) { |
273 | return false; | 277 | return false; |
274 | } | 278 | } |
275 | int matches = 0; | 279 | int matches = 0; |
@@ -277,94 +281,85 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) { | |||
277 | struct crit_token *crit = tokens->items[i]; | 281 | struct crit_token *crit = tokens->items[i]; |
278 | switch (crit->type) { | 282 | switch (crit->type) { |
279 | case CRIT_CLASS: | 283 | case CRIT_CLASS: |
280 | if (!cont->class) { | 284 | { |
281 | // ignore | 285 | const char *class = view_get_class(cont->sway_view); |
282 | } else if (crit_is_focused(crit->raw)) { | 286 | if (!class) { |
283 | swayc_t *focused = get_focused_view(&root_container); | 287 | break; |
284 | if (focused->class && strcmp(cont->class, focused->class) == 0) { | 288 | } |
289 | if (crit->regex && regex_cmp(class, crit->regex) == 0) { | ||
285 | matches++; | 290 | matches++; |
286 | } | 291 | } |
287 | } else if (crit->regex && regex_cmp(cont->class, crit->regex) == 0) { | 292 | break; |
288 | matches++; | ||
289 | } | 293 | } |
290 | break; | 294 | case CRIT_CON_ID: |
291 | case CRIT_CON_ID: { | 295 | { |
292 | char *endptr; | 296 | char *endptr; |
293 | size_t crit_id = strtoul(crit->raw, &endptr, 10); | 297 | size_t crit_id = strtoul(crit->raw, &endptr, 10); |
294 | 298 | ||
295 | if (*endptr == 0 && cont->id == crit_id) { | 299 | 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; | 300 | ++matches; |
305 | } | 301 | } |
302 | break; | ||
306 | } | 303 | } |
304 | case CRIT_CON_MARK: | ||
305 | // TODO | ||
307 | break; | 306 | break; |
308 | case CRIT_FLOATING: | 307 | case CRIT_FLOATING: |
309 | if (cont->is_floating) { | 308 | // TODO |
310 | matches++; | ||
311 | } | ||
312 | break; | 309 | break; |
313 | case CRIT_ID: | 310 | case CRIT_ID: |
314 | if (!cont->app_id) { | 311 | // TODO |
315 | // ignore | ||
316 | } else if (crit->regex && regex_cmp(cont->app_id, crit->regex) == 0) { | ||
317 | matches++; | ||
318 | } | ||
319 | break; | 312 | break; |
313 | case CRIT_APP_ID: | ||
314 | { | ||
315 | const char *app_id = view_get_app_id(cont->sway_view); | ||
316 | if (!app_id) { | ||
317 | break; | ||
318 | } | ||
319 | |||
320 | if (crit->regex && regex_cmp(app_id, crit->regex) == 0) { | ||
321 | matches++; | ||
322 | } | ||
323 | break; | ||
324 | } | ||
320 | case CRIT_INSTANCE: | 325 | case CRIT_INSTANCE: |
321 | if (!cont->instance) { | 326 | { |
322 | // ignore | 327 | const char *instance = view_get_instance(cont->sway_view); |
323 | } else if (crit_is_focused(crit->raw)) { | 328 | if (!instance) { |
324 | swayc_t *focused = get_focused_view(&root_container); | 329 | break; |
325 | if (focused->instance && strcmp(cont->instance, focused->instance) == 0) { | 330 | } |
331 | |||
332 | if (crit->regex && regex_cmp(instance, crit->regex) == 0) { | ||
326 | matches++; | 333 | matches++; |
327 | } | 334 | } |
328 | } else if (crit->regex && regex_cmp(cont->instance, crit->regex) == 0) { | 335 | break; |
329 | matches++; | ||
330 | } | 336 | } |
331 | break; | ||
332 | case CRIT_TILING: | 337 | case CRIT_TILING: |
333 | if (!cont->is_floating) { | 338 | // TODO |
334 | matches++; | ||
335 | } | ||
336 | break; | 339 | break; |
337 | case CRIT_TITLE: | 340 | case CRIT_TITLE: |
338 | if (!cont->name) { | 341 | { |
339 | // ignore | 342 | const char *title = view_get_title(cont->sway_view); |
340 | } else if (crit_is_focused(crit->raw)) { | 343 | if (!title) { |
341 | swayc_t *focused = get_focused_view(&root_container); | 344 | break; |
342 | if (focused->name && strcmp(cont->name, focused->name) == 0) { | 345 | } |
346 | |||
347 | if (crit->regex && regex_cmp(title, crit->regex) == 0) { | ||
343 | matches++; | 348 | matches++; |
344 | } | 349 | } |
345 | } else if (crit->regex && regex_cmp(cont->name, crit->regex) == 0) { | 350 | break; |
346 | matches++; | ||
347 | } | 351 | } |
348 | break; | 352 | case CRIT_URGENT: |
349 | case CRIT_URGENT: // "latest" or "oldest" | 353 | // TODO "latest" or "oldest" |
350 | break; | 354 | break; |
351 | case CRIT_WINDOW_ROLE: | 355 | case CRIT_WINDOW_ROLE: |
356 | // TODO | ||
352 | break; | 357 | break; |
353 | case CRIT_WINDOW_TYPE: | 358 | case CRIT_WINDOW_TYPE: |
354 | // TODO wlc indeed exposes this information | 359 | // TODO |
355 | break; | 360 | break; |
356 | case CRIT_WORKSPACE: ; | 361 | case CRIT_WORKSPACE: |
357 | swayc_t *cont_ws = swayc_parent_by_type(cont, C_WORKSPACE); | 362 | // TODO |
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; | 363 | break; |
369 | default: | 364 | default: |
370 | sway_abort("Invalid criteria type (%i)", crit->type); | 365 | sway_abort("Invalid criteria type (%i)", crit->type); |
@@ -403,7 +398,7 @@ void free_criteria(struct criteria *crit) { | |||
403 | free(crit); | 398 | free(crit); |
404 | } | 399 | } |
405 | 400 | ||
406 | bool criteria_any(swayc_t *cont, list_t *criteria) { | 401 | bool criteria_any(struct sway_container *cont, list_t *criteria) { |
407 | for (int i = 0; i < criteria->length; i++) { | 402 | for (int i = 0; i < criteria->length; i++) { |
408 | struct criteria *bc = criteria->items[i]; | 403 | struct criteria *bc = criteria->items[i]; |
409 | if (criteria_test(cont, bc->tokens)) { | 404 | if (criteria_test(cont, bc->tokens)) { |
@@ -413,7 +408,7 @@ bool criteria_any(swayc_t *cont, list_t *criteria) { | |||
413 | return false; | 408 | return false; |
414 | } | 409 | } |
415 | 410 | ||
416 | list_t *criteria_for(swayc_t *cont) { | 411 | list_t *criteria_for(struct sway_container *cont) { |
417 | list_t *criteria = config->criteria, *matches = create_list(); | 412 | list_t *criteria = config->criteria, *matches = create_list(); |
418 | for (int i = 0; i < criteria->length; i++) { | 413 | for (int i = 0; i < criteria->length; i++) { |
419 | struct criteria *bc = criteria->items[i]; | 414 | struct criteria *bc = criteria->items[i]; |
@@ -429,23 +424,22 @@ struct list_tokens { | |||
429 | list_t *tokens; | 424 | list_t *tokens; |
430 | }; | 425 | }; |
431 | 426 | ||
432 | static void container_match_add(swayc_t *container, struct list_tokens *list_tokens) { | 427 | static void container_match_add(struct sway_container *container, |
428 | struct list_tokens *list_tokens) { | ||
433 | if (criteria_test(container, list_tokens->tokens)) { | 429 | if (criteria_test(container, list_tokens->tokens)) { |
434 | list_add(list_tokens->list, container); | 430 | list_add(list_tokens->list, container); |
435 | } | 431 | } |
436 | } | 432 | } |
437 | 433 | ||
438 | list_t *container_for(list_t *tokens) { | 434 | list_t *container_for_crit_tokens(list_t *tokens) { |
439 | struct list_tokens list_tokens = (struct list_tokens){create_list(), tokens}; | 435 | struct list_tokens list_tokens = |
436 | (struct list_tokens){create_list(), tokens}; | ||
440 | 437 | ||
441 | container_map(&root_container, (void (*)(swayc_t *, void *))container_match_add, &list_tokens); | 438 | container_for_each_descendant_dfs(&root_container, |
442 | 439 | (void (*)(struct sway_container *, void *))container_match_add, | |
443 | for (int i = 0; i < scratchpad->length; ++i) { | 440 | &list_tokens); |
444 | swayc_t *c = scratchpad->items[i]; | ||
445 | if (criteria_test(c, tokens)) { | ||
446 | list_add(list_tokens.list, c); | ||
447 | } | ||
448 | } | ||
449 | 441 | ||
442 | // TODO look in the scratchpad | ||
443 | |||
450 | return list_tokens.list; | 444 | return list_tokens.list; |
451 | } | 445 | } |
diff --git a/sway/debug-tree.c b/sway/debug-tree.c new file mode 100644 index 00000000..ae0a1869 --- /dev/null +++ b/sway/debug-tree.c | |||
@@ -0,0 +1,119 @@ | |||
1 | #include <pango/pangocairo.h> | ||
2 | #include <wlr/backend.h> | ||
3 | #include <wlr/render/wlr_texture.h> | ||
4 | #include <wlr/util/log.h> | ||
5 | #include "config.h" | ||
6 | #include "sway/input/input-manager.h" | ||
7 | #include "sway/input/seat.h" | ||
8 | #include "sway/server.h" | ||
9 | #include "sway/tree/container.h" | ||
10 | #include "sway/tree/layout.h" | ||
11 | #include "cairo.h" | ||
12 | #include "config.h" | ||
13 | #include "pango.h" | ||
14 | |||
15 | static const char *layout_to_str(enum sway_container_layout layout) { | ||
16 | switch (layout) { | ||
17 | case L_HORIZ: | ||
18 | return "L_HORIZ"; | ||
19 | case L_VERT: | ||
20 | return "L_VERT"; | ||
21 | case L_STACKED: | ||
22 | return "L_STACKED"; | ||
23 | case L_TABBED: | ||
24 | return "L_TABBED"; | ||
25 | case L_FLOATING: | ||
26 | return "L_FLOATING"; | ||
27 | case L_NONE: | ||
28 | default: | ||
29 | return "L_NONE"; | ||
30 | } | ||
31 | } | ||
32 | |||
33 | static int draw_container(cairo_t *cairo, struct sway_container *container, | ||
34 | struct sway_container *focus, int x, int y) { | ||
35 | int text_width, text_height; | ||
36 | get_text_size(cairo, "monospace", &text_width, &text_height, | ||
37 | 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f", | ||
38 | container_type_to_str(container->type), container->id, container->name, | ||
39 | layout_to_str(container->layout), | ||
40 | container->width, container->height, container->x, container->y); | ||
41 | cairo_save(cairo); | ||
42 | cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height); | ||
43 | cairo_set_source_u32(cairo, 0xFFFFFFE0); | ||
44 | cairo_fill(cairo); | ||
45 | int height = text_height; | ||
46 | if (container->children) { | ||
47 | for (int i = 0; i < container->children->length; ++i) { | ||
48 | struct sway_container *child = container->children->items[i]; | ||
49 | if (child->parent == container) { | ||
50 | cairo_set_source_u32(cairo, 0x000000FF); | ||
51 | } else { | ||
52 | cairo_set_source_u32(cairo, 0xFF0000FF); | ||
53 | } | ||
54 | height += draw_container(cairo, child, focus, x + 10, y + height); | ||
55 | } | ||
56 | } | ||
57 | cairo_set_source_u32(cairo, 0xFFFFFFE0); | ||
58 | cairo_rectangle(cairo, x, y, 2, height); | ||
59 | cairo_fill(cairo); | ||
60 | cairo_restore(cairo); | ||
61 | cairo_move_to(cairo, x, y); | ||
62 | if (focus == container) { | ||
63 | cairo_set_source_u32(cairo, 0x0000FFFF); | ||
64 | } | ||
65 | pango_printf(cairo, "monospace", 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f", | ||
66 | container_type_to_str(container->type), container->id, container->name, | ||
67 | layout_to_str(container->layout), | ||
68 | container->width, container->height, container->x, container->y); | ||
69 | return height; | ||
70 | } | ||
71 | |||
72 | bool enable_debug_tree = false; | ||
73 | |||
74 | void update_debug_tree() { | ||
75 | if (!enable_debug_tree) { | ||
76 | return; | ||
77 | } | ||
78 | |||
79 | int width = 640, height = 480; | ||
80 | for (int i = 0; i < root_container.children->length; ++i) { | ||
81 | struct sway_container *container = root_container.children->items[i]; | ||
82 | if (container->width > width) { | ||
83 | width = container->width; | ||
84 | } | ||
85 | if (container->height > height) { | ||
86 | height = container->height; | ||
87 | } | ||
88 | } | ||
89 | cairo_surface_t *surface = | ||
90 | cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); | ||
91 | cairo_t *cairo = cairo_create(surface); | ||
92 | PangoContext *pango = pango_cairo_create_context(cairo); | ||
93 | |||
94 | struct sway_seat *seat = NULL; | ||
95 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
96 | break; | ||
97 | } | ||
98 | |||
99 | struct sway_container *focus = NULL; | ||
100 | if (seat != NULL) { | ||
101 | focus = seat_get_focus(seat); | ||
102 | } | ||
103 | cairo_set_source_u32(cairo, 0x000000FF); | ||
104 | draw_container(cairo, &root_container, focus, 0, 0); | ||
105 | |||
106 | cairo_surface_flush(surface); | ||
107 | struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend); | ||
108 | if (root_container.sway_root->debug_tree) { | ||
109 | wlr_texture_destroy(root_container.sway_root->debug_tree); | ||
110 | } | ||
111 | unsigned char *data = cairo_image_surface_get_data(surface); | ||
112 | int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); | ||
113 | struct wlr_texture *texture = wlr_texture_from_pixels(renderer, | ||
114 | WL_SHM_FORMAT_ARGB8888, stride, width, height, data); | ||
115 | root_container.sway_root->debug_tree = texture; | ||
116 | cairo_surface_destroy(surface); | ||
117 | g_object_unref(pango); | ||
118 | cairo_destroy(cairo); | ||
119 | } | ||
diff --git a/sway/debug_log.c b/sway/debug_log.c deleted file mode 100644 index d1eafae8..00000000 --- a/sway/debug_log.c +++ /dev/null | |||
@@ -1,103 +0,0 @@ | |||
1 | #include "log.h" | ||
2 | #include "sway.h" | ||
3 | #include <stdarg.h> | ||
4 | #include <stdio.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <libgen.h> | ||
7 | #include <fcntl.h> | ||
8 | #include <unistd.h> | ||
9 | #include <signal.h> | ||
10 | #include <errno.h> | ||
11 | #include <string.h> | ||
12 | #include <stringop.h> | ||
13 | #include "sway/workspace.h" | ||
14 | |||
15 | /* XXX:DEBUG:XXX */ | ||
16 | static void container_log(const swayc_t *c, int depth) { | ||
17 | fprintf(stderr, "focus:%c", | ||
18 | c == get_focused_view(&root_container) ? 'K': | ||
19 | c == get_focused_container(&root_container) ? 'F' : // Focused | ||
20 | c == swayc_active_workspace() ? 'W' : // active workspace | ||
21 | c == &root_container ? 'R' : // root | ||
22 | 'X');// not any others | ||
23 | for (int i = 6; i > depth; i--) { fprintf(stderr, " "); } | ||
24 | fprintf(stderr,"|(%p)",c); | ||
25 | fprintf(stderr,"(p:%-8p)",c->parent); | ||
26 | fprintf(stderr,"(f:%-8p)",c->focused); | ||
27 | fprintf(stderr,"(h:%2" PRIuPTR ")",c->handle); | ||
28 | fprintf(stderr,"Type:%-4s|", | ||
29 | c->type == C_ROOT ? "root" : | ||
30 | c->type == C_OUTPUT ? "op" : | ||
31 | c->type == C_WORKSPACE ? "ws" : | ||
32 | c->type == C_CONTAINER ? "cont" : | ||
33 | c->type == C_VIEW ? "view" : "?"); | ||
34 | fprintf(stderr,"layout:%-5s|", | ||
35 | c->layout == L_NONE ? "-" : | ||
36 | c->layout == L_HORIZ ? "Horiz": | ||
37 | c->layout == L_VERT ? "Vert": | ||
38 | c->layout == L_STACKED ? "Stack": | ||
39 | c->layout == L_TABBED ? "Tab": | ||
40 | c->layout == L_FLOATING ? "Float": | ||
41 | c->layout == L_AUTO_LEFT ? "A_lft": | ||
42 | c->layout == L_AUTO_RIGHT ? "A_rgt": | ||
43 | c->layout == L_AUTO_TOP ? "A_top": | ||
44 | c->layout == L_AUTO_BOTTOM ? "A_bot": | ||
45 | "Unknown"); | ||
46 | fprintf(stderr, "w:%4.f|h:%4.f|", c->width, c->height); | ||
47 | fprintf(stderr, "x:%4.f|y:%4.f|", c->x, c->y); | ||
48 | fprintf(stderr, "g:%3d|",c->gaps); | ||
49 | fprintf(stderr, "vis:%c|", c->visible?'t':'f'); | ||
50 | fprintf(stderr, "children:%2d|",c->children?c->children->length:0); | ||
51 | fprintf(stderr, "name:%.16s\n", c->name); | ||
52 | } | ||
53 | void layout_log(const swayc_t *c, int depth) { | ||
54 | if (L_DEBUG > get_log_level()) return; | ||
55 | int i, d; | ||
56 | int e = c->children ? c->children->length : 0; | ||
57 | container_log(c, depth); | ||
58 | if (e) { | ||
59 | for (i = 0; i < e; ++i) { | ||
60 | fputc('|',stderr); | ||
61 | for (d = 0; d < depth; ++d) fputc('-', stderr); | ||
62 | layout_log(c->children->items[i], depth + 1); | ||
63 | } | ||
64 | } | ||
65 | if (c->type == C_WORKSPACE) { | ||
66 | e = c->floating?c->floating->length:0; | ||
67 | if (e) { | ||
68 | for (i = 0; i < e; ++i) { | ||
69 | fputc('|',stderr); | ||
70 | for (d = 0; d < depth; ++d) fputc('=', stderr); | ||
71 | layout_log(c->floating->items[i], depth + 1); | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | |||
77 | const char *swayc_type_string(enum swayc_types type) { | ||
78 | return type == C_ROOT ? "ROOT" : | ||
79 | type == C_OUTPUT ? "OUTPUT" : | ||
80 | type == C_WORKSPACE ? "WORKSPACE" : | ||
81 | type == C_CONTAINER ? "CONTAINER" : | ||
82 | type == C_VIEW ? "VIEW" : | ||
83 | "UNKNOWN"; | ||
84 | } | ||
85 | |||
86 | // Like sway_log, but also appends some info about given container to log output. | ||
87 | void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) { | ||
88 | sway_assert(cont, "swayc_log: no container ..."); | ||
89 | va_list args; | ||
90 | va_start(args, format); | ||
91 | char *txt = malloc(128); | ||
92 | vsprintf(txt, format, args); | ||
93 | va_end(args); | ||
94 | |||
95 | char *debug_txt = malloc(32); | ||
96 | snprintf(debug_txt, 32, "%s '%s'", swayc_type_string(cont->type), cont->name); | ||
97 | |||
98 | sway_log(verbosity, "%s (%s)", txt, debug_txt); | ||
99 | free(txt); | ||
100 | free(debug_txt); | ||
101 | } | ||
102 | |||
103 | /* XXX:DEBUG:XXX */ | ||
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c new file mode 100644 index 00000000..66f33151 --- /dev/null +++ b/sway/desktop/desktop.c | |||
@@ -0,0 +1,14 @@ | |||
1 | #include "sway/tree/container.h" | ||
2 | #include "sway/desktop.h" | ||
3 | #include "sway/output.h" | ||
4 | |||
5 | void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, | ||
6 | bool whole) { | ||
7 | for (int i = 0; i < root_container.children->length; ++i) { | ||
8 | struct sway_container *cont = root_container.children->items[i]; | ||
9 | if (cont->type == C_OUTPUT) { | ||
10 | output_damage_surface(cont->sway_output, lx - cont->x, ly - cont->y, | ||
11 | surface, whole); | ||
12 | } | ||
13 | } | ||
14 | } | ||
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c new file mode 100644 index 00000000..f841e5f1 --- /dev/null +++ b/sway/desktop/layer_shell.c | |||
@@ -0,0 +1,347 @@ | |||
1 | #include <stdbool.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | #include <wayland-server.h> | ||
5 | #include <wlr/types/wlr_box.h> | ||
6 | #include <wlr/types/wlr_layer_shell.h> | ||
7 | #include <wlr/types/wlr_output_damage.h> | ||
8 | #include <wlr/types/wlr_output.h> | ||
9 | #include <wlr/util/log.h> | ||
10 | #include "sway/input/input-manager.h" | ||
11 | #include "sway/input/seat.h" | ||
12 | #include "sway/layers.h" | ||
13 | #include "sway/output.h" | ||
14 | #include "sway/server.h" | ||
15 | #include "sway/tree/layout.h" | ||
16 | |||
17 | static void apply_exclusive(struct wlr_box *usable_area, | ||
18 | uint32_t anchor, int32_t exclusive, | ||
19 | int32_t margin_top, int32_t margin_right, | ||
20 | int32_t margin_bottom, int32_t margin_left) { | ||
21 | if (exclusive <= 0) { | ||
22 | return; | ||
23 | } | ||
24 | struct { | ||
25 | uint32_t anchors; | ||
26 | int *positive_axis; | ||
27 | int *negative_axis; | ||
28 | int margin; | ||
29 | } edges[] = { | ||
30 | { | ||
31 | .anchors = | ||
32 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | ||
33 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
34 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, | ||
35 | .positive_axis = &usable_area->y, | ||
36 | .negative_axis = &usable_area->height, | ||
37 | .margin = margin_top, | ||
38 | }, | ||
39 | { | ||
40 | .anchors = | ||
41 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | ||
42 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
43 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
44 | .positive_axis = NULL, | ||
45 | .negative_axis = &usable_area->height, | ||
46 | .margin = margin_bottom, | ||
47 | }, | ||
48 | { | ||
49 | .anchors = | ||
50 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | ||
51 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
52 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
53 | .positive_axis = &usable_area->x, | ||
54 | .negative_axis = &usable_area->width, | ||
55 | .margin = margin_left, | ||
56 | }, | ||
57 | { | ||
58 | .anchors = | ||
59 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
60 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
61 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
62 | .positive_axis = NULL, | ||
63 | .negative_axis = &usable_area->width, | ||
64 | .margin = margin_right, | ||
65 | }, | ||
66 | }; | ||
67 | for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { | ||
68 | if ((anchor & edges[i].anchors) == edges[i].anchors) { | ||
69 | if (edges[i].positive_axis) { | ||
70 | *edges[i].positive_axis += exclusive + edges[i].margin; | ||
71 | } | ||
72 | if (edges[i].negative_axis) { | ||
73 | *edges[i].negative_axis -= exclusive + edges[i].margin; | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | static void arrange_layer(struct sway_output *output, struct wl_list *list, | ||
80 | struct wlr_box *usable_area, bool exclusive) { | ||
81 | struct sway_layer_surface *sway_layer; | ||
82 | struct wlr_box full_area = { 0 }; | ||
83 | wlr_output_effective_resolution(output->wlr_output, | ||
84 | &full_area.width, &full_area.height); | ||
85 | wl_list_for_each(sway_layer, list, link) { | ||
86 | struct wlr_layer_surface *layer = sway_layer->layer_surface; | ||
87 | struct wlr_layer_surface_state *state = &layer->current; | ||
88 | if (exclusive != (state->exclusive_zone > 0)) { | ||
89 | continue; | ||
90 | } | ||
91 | struct wlr_box bounds; | ||
92 | if (state->exclusive_zone == -1) { | ||
93 | bounds = full_area; | ||
94 | } else { | ||
95 | bounds = *usable_area; | ||
96 | } | ||
97 | struct wlr_box box = { | ||
98 | .width = state->desired_width, | ||
99 | .height = state->desired_height | ||
100 | }; | ||
101 | // Horizontal axis | ||
102 | const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ||
103 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; | ||
104 | if ((state->anchor & both_horiz) && box.width == 0) { | ||
105 | box.x = bounds.x; | ||
106 | box.width = bounds.width; | ||
107 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
108 | box.x = bounds.x; | ||
109 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
110 | box.x = bounds.x + (bounds.width - box.width); | ||
111 | } else { | ||
112 | box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); | ||
113 | } | ||
114 | // Vertical axis | ||
115 | const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ||
116 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; | ||
117 | if ((state->anchor & both_vert) && box.height == 0) { | ||
118 | box.y = bounds.y; | ||
119 | box.height = bounds.height; | ||
120 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
121 | box.y = bounds.y; | ||
122 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
123 | box.y = bounds.y + (bounds.height - box.height); | ||
124 | } else { | ||
125 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | ||
126 | } | ||
127 | // Margin | ||
128 | if ((state->anchor & both_horiz) == both_horiz) { | ||
129 | box.x += state->margin.left; | ||
130 | box.width -= state->margin.left + state->margin.right; | ||
131 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
132 | box.x += state->margin.left; | ||
133 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
134 | box.x -= state->margin.right; | ||
135 | } | ||
136 | if ((state->anchor & both_vert) == both_vert) { | ||
137 | box.y += state->margin.top; | ||
138 | box.height -= state->margin.top + state->margin.bottom; | ||
139 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
140 | box.y += state->margin.top; | ||
141 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
142 | box.y -= state->margin.bottom; | ||
143 | } | ||
144 | if (box.width < 0 || box.height < 0) { | ||
145 | // TODO: Bubble up a protocol error? | ||
146 | wlr_layer_surface_close(layer); | ||
147 | continue; | ||
148 | } | ||
149 | // Apply | ||
150 | sway_layer->geo = box; | ||
151 | apply_exclusive(usable_area, state->anchor, state->exclusive_zone, | ||
152 | state->margin.top, state->margin.right, | ||
153 | state->margin.bottom, state->margin.left); | ||
154 | wlr_layer_surface_configure(layer, box.width, box.height); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | void arrange_layers(struct sway_output *output) { | ||
159 | struct wlr_box usable_area = { 0 }; | ||
160 | wlr_output_effective_resolution(output->wlr_output, | ||
161 | &usable_area.width, &usable_area.height); | ||
162 | |||
163 | // Arrange exclusive surfaces from top->bottom | ||
164 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
165 | &usable_area, true); | ||
166 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
167 | &usable_area, true); | ||
168 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
169 | &usable_area, true); | ||
170 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
171 | &usable_area, true); | ||
172 | |||
173 | if (memcmp(&usable_area, &output->usable_area, | ||
174 | sizeof(struct wlr_box)) != 0) { | ||
175 | wlr_log(L_DEBUG, "Usable area changed, rearranging output"); | ||
176 | memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); | ||
177 | arrange_windows(output->swayc, -1, -1); | ||
178 | } | ||
179 | |||
180 | // Arrange non-exlusive surfaces from top->bottom | ||
181 | usable_area.x = usable_area.y = 0; | ||
182 | wlr_output_effective_resolution(output->wlr_output, | ||
183 | &usable_area.width, &usable_area.height); | ||
184 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
185 | &usable_area, false); | ||
186 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
187 | &usable_area, false); | ||
188 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
189 | &usable_area, false); | ||
190 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
191 | &usable_area, false); | ||
192 | |||
193 | // Find topmost keyboard interactive layer, if such a layer exists | ||
194 | uint32_t layers_above_shell[] = { | ||
195 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, | ||
196 | ZWLR_LAYER_SHELL_V1_LAYER_TOP, | ||
197 | }; | ||
198 | size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); | ||
199 | struct sway_layer_surface *layer, *topmost = NULL; | ||
200 | for (size_t i = 0; i < nlayers; ++i) { | ||
201 | wl_list_for_each_reverse(layer, | ||
202 | &output->layers[layers_above_shell[i]], link) { | ||
203 | if (layer->layer_surface->current.keyboard_interactive) { | ||
204 | topmost = layer; | ||
205 | break; | ||
206 | } | ||
207 | } | ||
208 | if (topmost != NULL) { | ||
209 | break; | ||
210 | } | ||
211 | } | ||
212 | |||
213 | struct sway_seat *seat; | ||
214 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
215 | seat_set_focus_layer(seat, topmost ? topmost->layer_surface : NULL); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | static void handle_output_destroy(struct wl_listener *listener, void *data) { | ||
220 | struct sway_layer_surface *sway_layer = | ||
221 | wl_container_of(listener, sway_layer, output_destroy); | ||
222 | wl_list_remove(&sway_layer->output_destroy.link); | ||
223 | sway_layer->layer_surface->output = NULL; | ||
224 | wlr_layer_surface_close(sway_layer->layer_surface); | ||
225 | } | ||
226 | |||
227 | static void handle_surface_commit(struct wl_listener *listener, void *data) { | ||
228 | struct sway_layer_surface *layer = | ||
229 | wl_container_of(listener, layer, surface_commit); | ||
230 | struct wlr_layer_surface *layer_surface = layer->layer_surface; | ||
231 | struct wlr_output *wlr_output = layer_surface->output; | ||
232 | if (wlr_output == NULL) { | ||
233 | return; | ||
234 | } | ||
235 | |||
236 | struct sway_output *output = wlr_output->data; | ||
237 | struct wlr_box old_geo = layer->geo; | ||
238 | arrange_layers(output); | ||
239 | if (memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0) { | ||
240 | output_damage_surface(output, old_geo.x, old_geo.y, | ||
241 | layer_surface->surface, true); | ||
242 | output_damage_surface(output, layer->geo.x, layer->geo.y, | ||
243 | layer_surface->surface, true); | ||
244 | } else { | ||
245 | output_damage_surface(output, layer->geo.x, layer->geo.y, | ||
246 | layer_surface->surface, false); | ||
247 | } | ||
248 | } | ||
249 | |||
250 | static void unmap(struct sway_layer_surface *sway_layer) { | ||
251 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | ||
252 | if (wlr_output == NULL) { | ||
253 | return; | ||
254 | } | ||
255 | struct sway_output *output = wlr_output->data; | ||
256 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | ||
257 | sway_layer->layer_surface->surface, true); | ||
258 | } | ||
259 | |||
260 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
261 | struct sway_layer_surface *sway_layer = | ||
262 | wl_container_of(listener, sway_layer, destroy); | ||
263 | wlr_log(L_DEBUG, "Layer surface destroyed (%s)", | ||
264 | sway_layer->layer_surface->namespace); | ||
265 | if (sway_layer->layer_surface->mapped) { | ||
266 | unmap(sway_layer); | ||
267 | } | ||
268 | wl_list_remove(&sway_layer->link); | ||
269 | wl_list_remove(&sway_layer->destroy.link); | ||
270 | wl_list_remove(&sway_layer->map.link); | ||
271 | wl_list_remove(&sway_layer->unmap.link); | ||
272 | wl_list_remove(&sway_layer->surface_commit.link); | ||
273 | if (sway_layer->layer_surface->output != NULL) { | ||
274 | struct sway_output *output = sway_layer->layer_surface->output->data; | ||
275 | arrange_layers(output); | ||
276 | |||
277 | wl_list_remove(&sway_layer->output_destroy.link); | ||
278 | } | ||
279 | free(sway_layer); | ||
280 | } | ||
281 | |||
282 | static void handle_map(struct wl_listener *listener, void *data) { | ||
283 | struct sway_layer_surface *sway_layer = wl_container_of(listener, | ||
284 | sway_layer, map); | ||
285 | struct sway_output *output = sway_layer->layer_surface->output->data; | ||
286 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | ||
287 | sway_layer->layer_surface->surface, true); | ||
288 | // TODO: send enter to subsurfaces and popups | ||
289 | wlr_surface_send_enter(sway_layer->layer_surface->surface, | ||
290 | sway_layer->layer_surface->output); | ||
291 | } | ||
292 | |||
293 | static void handle_unmap(struct wl_listener *listener, void *data) { | ||
294 | struct sway_layer_surface *sway_layer = wl_container_of( | ||
295 | listener, sway_layer, unmap); | ||
296 | unmap(sway_layer); | ||
297 | } | ||
298 | |||
299 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | ||
300 | struct wlr_layer_surface *layer_surface = data; | ||
301 | struct sway_server *server = | ||
302 | wl_container_of(listener, server, layer_shell_surface); | ||
303 | wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d " | ||
304 | "size %dx%d margin %d,%d,%d,%d", | ||
305 | layer_surface->namespace, layer_surface->layer, layer_surface->layer, | ||
306 | layer_surface->client_pending.desired_width, | ||
307 | layer_surface->client_pending.desired_height, | ||
308 | layer_surface->client_pending.margin.top, | ||
309 | layer_surface->client_pending.margin.right, | ||
310 | layer_surface->client_pending.margin.bottom, | ||
311 | layer_surface->client_pending.margin.left); | ||
312 | |||
313 | struct sway_layer_surface *sway_layer = | ||
314 | calloc(1, sizeof(struct sway_layer_surface)); | ||
315 | if (!sway_layer) { | ||
316 | return; | ||
317 | } | ||
318 | |||
319 | sway_layer->surface_commit.notify = handle_surface_commit; | ||
320 | wl_signal_add(&layer_surface->surface->events.commit, | ||
321 | &sway_layer->surface_commit); | ||
322 | |||
323 | sway_layer->output_destroy.notify = handle_output_destroy; | ||
324 | wl_signal_add(&layer_surface->output->events.destroy, | ||
325 | &sway_layer->output_destroy); | ||
326 | |||
327 | sway_layer->destroy.notify = handle_destroy; | ||
328 | wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); | ||
329 | sway_layer->map.notify = handle_map; | ||
330 | wl_signal_add(&layer_surface->events.map, &sway_layer->map); | ||
331 | sway_layer->unmap.notify = handle_unmap; | ||
332 | wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap); | ||
333 | // TODO: Listen for subsurfaces | ||
334 | |||
335 | sway_layer->layer_surface = layer_surface; | ||
336 | layer_surface->data = sway_layer; | ||
337 | |||
338 | struct sway_output *output = layer_surface->output->data; | ||
339 | wl_list_insert(&output->layers[layer_surface->layer], &sway_layer->link); | ||
340 | |||
341 | // Temporarily set the layer's current state to client_pending | ||
342 | // So that we can easily arrange it | ||
343 | struct wlr_layer_surface_state old_state = layer_surface->current; | ||
344 | layer_surface->current = layer_surface->client_pending; | ||
345 | arrange_layers(output); | ||
346 | layer_surface->current = old_state; | ||
347 | } | ||
diff --git a/sway/desktop/output.c b/sway/desktop/output.c new file mode 100644 index 00000000..1b3143d0 --- /dev/null +++ b/sway/desktop/output.c | |||
@@ -0,0 +1,579 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <strings.h> | ||
5 | #include <time.h> | ||
6 | #include <wayland-server.h> | ||
7 | #include <wlr/render/wlr_renderer.h> | ||
8 | #include <wlr/types/wlr_box.h> | ||
9 | #include <wlr/types/wlr_matrix.h> | ||
10 | #include <wlr/types/wlr_output_damage.h> | ||
11 | #include <wlr/types/wlr_output_layout.h> | ||
12 | #include <wlr/types/wlr_output.h> | ||
13 | #include <wlr/types/wlr_surface.h> | ||
14 | #include <wlr/types/wlr_wl_shell.h> | ||
15 | #include <wlr/util/region.h> | ||
16 | #include "log.h" | ||
17 | #include "sway/input/input-manager.h" | ||
18 | #include "sway/input/seat.h" | ||
19 | #include "sway/layers.h" | ||
20 | #include "sway/output.h" | ||
21 | #include "sway/server.h" | ||
22 | #include "sway/tree/container.h" | ||
23 | #include "sway/tree/layout.h" | ||
24 | #include "sway/tree/view.h" | ||
25 | |||
26 | struct sway_container *output_by_name(const char *name) { | ||
27 | for (int i = 0; i < root_container.children->length; ++i) { | ||
28 | struct sway_container *output = root_container.children->items[i]; | ||
29 | if (strcasecmp(output->name, name) == 0) { | ||
30 | return output; | ||
31 | } | ||
32 | } | ||
33 | return NULL; | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Rotate a child's position relative to a parent. The parent size is (pw, ph), | ||
38 | * the child position is (*sx, *sy) and its size is (sw, sh). | ||
39 | */ | ||
40 | static void rotate_child_position(double *sx, double *sy, double sw, double sh, | ||
41 | double pw, double ph, float rotation) { | ||
42 | if (rotation == 0.0f) { | ||
43 | return; | ||
44 | } | ||
45 | |||
46 | // Coordinates relative to the center of the subsurface | ||
47 | double ox = *sx - pw/2 + sw/2, | ||
48 | oy = *sy - ph/2 + sh/2; | ||
49 | // Rotated coordinates | ||
50 | double rx = cos(-rotation)*ox - sin(-rotation)*oy, | ||
51 | ry = cos(-rotation)*oy + sin(-rotation)*ox; | ||
52 | *sx = rx + pw/2 - sw/2; | ||
53 | *sy = ry + ph/2 - sh/2; | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * Contains a surface's root geometry information. For instance, when rendering | ||
58 | * a popup, this will contain the parent view's position and size. | ||
59 | */ | ||
60 | struct root_geometry { | ||
61 | double x, y; | ||
62 | int width, height; | ||
63 | float rotation; | ||
64 | }; | ||
65 | |||
66 | static bool get_surface_box(struct root_geometry *geo, | ||
67 | struct sway_output *output, struct wlr_surface *surface, int sx, int sy, | ||
68 | struct wlr_box *surface_box) { | ||
69 | if (!wlr_surface_has_buffer(surface)) { | ||
70 | return false; | ||
71 | } | ||
72 | |||
73 | int sw = surface->current->width; | ||
74 | int sh = surface->current->height; | ||
75 | |||
76 | double _sx = sx, _sy = sy; | ||
77 | rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, | ||
78 | geo->rotation); | ||
79 | |||
80 | struct wlr_box box = { | ||
81 | .x = geo->x + _sx, | ||
82 | .y = geo->y + _sy, | ||
83 | .width = sw, | ||
84 | .height = sh, | ||
85 | }; | ||
86 | if (surface_box != NULL) { | ||
87 | memcpy(surface_box, &box, sizeof(struct wlr_box)); | ||
88 | } | ||
89 | |||
90 | struct wlr_box rotated_box; | ||
91 | wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box); | ||
92 | |||
93 | struct wlr_box output_box = { | ||
94 | .width = output->swayc->width, | ||
95 | .height = output->swayc->height, | ||
96 | }; | ||
97 | |||
98 | struct wlr_box intersection; | ||
99 | return wlr_box_intersection(&output_box, &rotated_box, &intersection); | ||
100 | } | ||
101 | |||
102 | static void surface_for_each_surface(struct wlr_surface *surface, | ||
103 | double ox, double oy, struct root_geometry *geo, | ||
104 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
105 | geo->x = ox; | ||
106 | geo->y = oy; | ||
107 | geo->width = surface->current->width; | ||
108 | geo->height = surface->current->height; | ||
109 | geo->rotation = 0; | ||
110 | |||
111 | wlr_surface_for_each_surface(surface, iterator, user_data); | ||
112 | } | ||
113 | |||
114 | static void output_view_for_each_surface(struct sway_view *view, | ||
115 | struct root_geometry *geo, wlr_surface_iterator_func_t iterator, | ||
116 | void *user_data) { | ||
117 | geo->x = view->swayc->x; | ||
118 | geo->y = view->swayc->y; | ||
119 | geo->width = view->surface->current->width; | ||
120 | geo->height = view->surface->current->height; | ||
121 | geo->rotation = 0; // TODO | ||
122 | |||
123 | view_for_each_surface(view, iterator, user_data); | ||
124 | } | ||
125 | |||
126 | static void layer_for_each_surface(struct wl_list *layer_surfaces, | ||
127 | struct root_geometry *geo, wlr_surface_iterator_func_t iterator, | ||
128 | void *user_data) { | ||
129 | struct sway_layer_surface *layer_surface; | ||
130 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
131 | struct wlr_layer_surface *wlr_layer_surface = | ||
132 | layer_surface->layer_surface; | ||
133 | surface_for_each_surface(wlr_layer_surface->surface, | ||
134 | layer_surface->geo.x, layer_surface->geo.y, geo, iterator, | ||
135 | user_data); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | static void unmanaged_for_each_surface(struct wl_list *unmanaged, | ||
140 | struct sway_output *output, struct root_geometry *geo, | ||
141 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
142 | struct sway_xwayland_unmanaged *unmanaged_surface; | ||
143 | wl_list_for_each(unmanaged_surface, unmanaged, link) { | ||
144 | struct wlr_xwayland_surface *xsurface = | ||
145 | unmanaged_surface->wlr_xwayland_surface; | ||
146 | double ox = unmanaged_surface->lx - output->swayc->x; | ||
147 | double oy = unmanaged_surface->ly - output->swayc->y; | ||
148 | |||
149 | surface_for_each_surface(xsurface->surface, ox, oy, geo, | ||
150 | iterator, user_data); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | static void scale_box(struct wlr_box *box, float scale) { | ||
155 | box->x *= scale; | ||
156 | box->y *= scale; | ||
157 | box->width *= scale; | ||
158 | box->height *= scale; | ||
159 | } | ||
160 | |||
161 | struct render_data { | ||
162 | struct root_geometry root_geo; | ||
163 | struct sway_output *output; | ||
164 | float alpha; | ||
165 | }; | ||
166 | |||
167 | static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, | ||
168 | void *_data) { | ||
169 | struct render_data *data = _data; | ||
170 | struct wlr_output *wlr_output = data->output->wlr_output; | ||
171 | float rotation = data->root_geo.rotation; | ||
172 | float alpha = data->alpha; | ||
173 | |||
174 | if (!wlr_surface_has_buffer(surface)) { | ||
175 | return; | ||
176 | } | ||
177 | |||
178 | struct wlr_box box; | ||
179 | bool intersects = get_surface_box(&data->root_geo, data->output, surface, | ||
180 | sx, sy, &box); | ||
181 | if (!intersects) { | ||
182 | return; | ||
183 | } | ||
184 | |||
185 | struct wlr_renderer *renderer = | ||
186 | wlr_backend_get_renderer(wlr_output->backend); | ||
187 | if (!sway_assert(renderer != NULL, | ||
188 | "expected the output backend to have a renderer")) { | ||
189 | return; | ||
190 | } | ||
191 | |||
192 | scale_box(&box, wlr_output->scale); | ||
193 | |||
194 | float matrix[9]; | ||
195 | enum wl_output_transform transform = | ||
196 | wlr_output_transform_invert(surface->current->transform); | ||
197 | wlr_matrix_project_box(matrix, &box, transform, rotation, | ||
198 | wlr_output->transform_matrix); | ||
199 | |||
200 | wlr_render_texture_with_matrix(renderer, surface->texture, | ||
201 | matrix, alpha); | ||
202 | } | ||
203 | |||
204 | static void render_layer(struct sway_output *output, | ||
205 | struct wl_list *layer_surfaces) { | ||
206 | struct render_data data = { .output = output, .alpha = 1.0f }; | ||
207 | layer_for_each_surface(layer_surfaces, &data.root_geo, | ||
208 | render_surface_iterator, &data); | ||
209 | } | ||
210 | |||
211 | static void render_unmanaged(struct sway_output *output, | ||
212 | struct wl_list *unmanaged) { | ||
213 | struct render_data data = { .output = output, .alpha = 1.0f }; | ||
214 | unmanaged_for_each_surface(unmanaged, output, &data.root_geo, | ||
215 | render_surface_iterator, &data); | ||
216 | } | ||
217 | |||
218 | static void render_container_iterator(struct sway_container *con, | ||
219 | void *_data) { | ||
220 | struct sway_output *output = _data; | ||
221 | if (!sway_assert(con->type == C_VIEW, "expected a view")) { | ||
222 | return; | ||
223 | } | ||
224 | struct render_data data = { .output = output, .alpha = con->alpha }; | ||
225 | output_view_for_each_surface(con->sway_view, &data.root_geo, | ||
226 | render_surface_iterator, &data); | ||
227 | } | ||
228 | |||
229 | static void render_container(struct sway_output *output, | ||
230 | struct sway_container *con) { | ||
231 | container_descendants(con, C_VIEW, render_container_iterator, output); | ||
232 | } | ||
233 | |||
234 | static struct sway_container *output_get_active_workspace( | ||
235 | struct sway_output *output) { | ||
236 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
237 | struct sway_container *focus = | ||
238 | seat_get_focus_inactive(seat, output->swayc); | ||
239 | if (!focus) { | ||
240 | // We've never been to this output before | ||
241 | focus = output->swayc->children->items[0]; | ||
242 | } | ||
243 | struct sway_container *workspace = focus; | ||
244 | if (workspace->type != C_WORKSPACE) { | ||
245 | workspace = container_parent(workspace, C_WORKSPACE); | ||
246 | } | ||
247 | return workspace; | ||
248 | } | ||
249 | |||
250 | static void render_output(struct sway_output *output, struct timespec *when, | ||
251 | pixman_region32_t *damage) { | ||
252 | struct wlr_output *wlr_output = output->wlr_output; | ||
253 | |||
254 | struct wlr_renderer *renderer = | ||
255 | wlr_backend_get_renderer(wlr_output->backend); | ||
256 | if (!sway_assert(renderer != NULL, | ||
257 | "expected the output backend to have a renderer")) { | ||
258 | return; | ||
259 | } | ||
260 | |||
261 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
262 | |||
263 | if (!pixman_region32_not_empty(damage)) { | ||
264 | // Output isn't damaged but needs buffer swap | ||
265 | goto renderer_end; | ||
266 | } | ||
267 | |||
268 | // TODO: don't damage the whole output | ||
269 | int width, height; | ||
270 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
271 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
272 | |||
273 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | ||
274 | wlr_renderer_clear(renderer, clear_color); | ||
275 | |||
276 | render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
277 | render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
278 | |||
279 | struct sway_container *workspace = output_get_active_workspace(output); | ||
280 | render_container(output, workspace); | ||
281 | |||
282 | render_unmanaged(output, &root_container.sway_root->xwayland_unmanaged); | ||
283 | |||
284 | // TODO: consider revising this when fullscreen windows are supported | ||
285 | render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
286 | render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
287 | |||
288 | renderer_end: | ||
289 | if (root_container.sway_root->debug_tree) { | ||
290 | wlr_render_texture(renderer, root_container.sway_root->debug_tree, | ||
291 | wlr_output->transform_matrix, 0, 0, 1); | ||
292 | } | ||
293 | |||
294 | wlr_renderer_end(renderer); | ||
295 | if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { | ||
296 | return; | ||
297 | } | ||
298 | output->last_frame = *when; | ||
299 | } | ||
300 | |||
301 | struct send_frame_done_data { | ||
302 | struct root_geometry root_geo; | ||
303 | struct sway_output *output; | ||
304 | struct timespec *when; | ||
305 | }; | ||
306 | |||
307 | static void send_frame_done_iterator(struct wlr_surface *surface, | ||
308 | int sx, int sy, void *_data) { | ||
309 | struct send_frame_done_data *data = _data; | ||
310 | |||
311 | bool intersects = get_surface_box(&data->root_geo, data->output, surface, | ||
312 | sx, sy, NULL); | ||
313 | if (intersects) { | ||
314 | wlr_surface_send_frame_done(surface, data->when); | ||
315 | } | ||
316 | } | ||
317 | |||
318 | static void send_frame_done_layer(struct send_frame_done_data *data, | ||
319 | struct wl_list *layer_surfaces) { | ||
320 | layer_for_each_surface(layer_surfaces, &data->root_geo, | ||
321 | send_frame_done_iterator, data); | ||
322 | } | ||
323 | |||
324 | static void send_frame_done_unmanaged(struct send_frame_done_data *data, | ||
325 | struct wl_list *unmanaged) { | ||
326 | unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, | ||
327 | send_frame_done_iterator, data); | ||
328 | } | ||
329 | |||
330 | static void send_frame_done_container_iterator(struct sway_container *con, | ||
331 | void *_data) { | ||
332 | struct send_frame_done_data *data = _data; | ||
333 | if (!sway_assert(con->type == C_VIEW, "expected a view")) { | ||
334 | return; | ||
335 | } | ||
336 | output_view_for_each_surface(con->sway_view, &data->root_geo, | ||
337 | send_frame_done_iterator, data); | ||
338 | } | ||
339 | |||
340 | static void send_frame_done_container(struct send_frame_done_data *data, | ||
341 | struct sway_container *con) { | ||
342 | container_descendants(con, C_VIEW, | ||
343 | send_frame_done_container_iterator, data); | ||
344 | } | ||
345 | |||
346 | static void send_frame_done(struct sway_output *output, struct timespec *when) { | ||
347 | struct send_frame_done_data data = { | ||
348 | .output = output, | ||
349 | .when = when, | ||
350 | }; | ||
351 | |||
352 | send_frame_done_layer(&data, | ||
353 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
354 | send_frame_done_layer(&data, | ||
355 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
356 | |||
357 | struct sway_container *workspace = output_get_active_workspace(output); | ||
358 | send_frame_done_container(&data, workspace); | ||
359 | |||
360 | send_frame_done_unmanaged(&data, | ||
361 | &root_container.sway_root->xwayland_unmanaged); | ||
362 | |||
363 | send_frame_done_layer(&data, | ||
364 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
365 | send_frame_done_layer(&data, | ||
366 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
367 | } | ||
368 | |||
369 | static void damage_handle_frame(struct wl_listener *listener, void *data) { | ||
370 | struct sway_output *output = | ||
371 | wl_container_of(listener, output, damage_frame); | ||
372 | |||
373 | if (!output->wlr_output->enabled) { | ||
374 | return; | ||
375 | } | ||
376 | |||
377 | struct timespec now; | ||
378 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
379 | |||
380 | bool needs_swap; | ||
381 | pixman_region32_t damage; | ||
382 | pixman_region32_init(&damage); | ||
383 | if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) { | ||
384 | return; | ||
385 | } | ||
386 | |||
387 | if (needs_swap) { | ||
388 | render_output(output, &now, &damage); | ||
389 | } | ||
390 | |||
391 | pixman_region32_fini(&damage); | ||
392 | |||
393 | // Send frame done to all visible surfaces | ||
394 | send_frame_done(output, &now); | ||
395 | } | ||
396 | |||
397 | void output_damage_whole(struct sway_output *output) { | ||
398 | wlr_output_damage_add_whole(output->damage); | ||
399 | } | ||
400 | |||
401 | struct damage_data { | ||
402 | struct root_geometry root_geo; | ||
403 | struct sway_output *output; | ||
404 | bool whole; | ||
405 | }; | ||
406 | |||
407 | static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, | ||
408 | void *_data) { | ||
409 | struct damage_data *data = _data; | ||
410 | struct sway_output *output = data->output; | ||
411 | float rotation = data->root_geo.rotation; | ||
412 | bool whole = data->whole; | ||
413 | |||
414 | struct wlr_box box; | ||
415 | bool intersects = get_surface_box(&data->root_geo, data->output, surface, | ||
416 | sx, sy, &box); | ||
417 | if (!intersects) { | ||
418 | return; | ||
419 | } | ||
420 | |||
421 | scale_box(&box, output->wlr_output->scale); | ||
422 | |||
423 | if (whole) { | ||
424 | wlr_box_rotated_bounds(&box, rotation, &box); | ||
425 | wlr_output_damage_add_box(output->damage, &box); | ||
426 | } else { | ||
427 | int center_x = box.x + box.width/2; | ||
428 | int center_y = box.y + box.height/2; | ||
429 | |||
430 | pixman_region32_t damage; | ||
431 | pixman_region32_init(&damage); | ||
432 | pixman_region32_copy(&damage, &surface->current->surface_damage); | ||
433 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); | ||
434 | if (ceil(output->wlr_output->scale) > surface->current->scale) { | ||
435 | // When scaling up a surface, it'll become blurry so we need to | ||
436 | // expand the damage region | ||
437 | wlr_region_expand(&damage, &damage, | ||
438 | ceil(output->wlr_output->scale) - surface->current->scale); | ||
439 | } | ||
440 | pixman_region32_translate(&damage, box.x, box.y); | ||
441 | wlr_region_rotated_bounds(&damage, &damage, rotation, | ||
442 | center_x, center_y); | ||
443 | wlr_output_damage_add(output->damage, &damage); | ||
444 | pixman_region32_fini(&damage); | ||
445 | } | ||
446 | } | ||
447 | |||
448 | void output_damage_surface(struct sway_output *output, double ox, double oy, | ||
449 | struct wlr_surface *surface, bool whole) { | ||
450 | struct damage_data data = { | ||
451 | .output = output, | ||
452 | .whole = whole, | ||
453 | }; | ||
454 | |||
455 | surface_for_each_surface(surface, ox, oy, &data.root_geo, | ||
456 | damage_surface_iterator, &data); | ||
457 | } | ||
458 | |||
459 | void output_damage_view(struct sway_output *output, struct sway_view *view, | ||
460 | bool whole) { | ||
461 | if (!sway_assert(view->swayc != NULL, "expected a view in the tree")) { | ||
462 | return; | ||
463 | } | ||
464 | |||
465 | struct damage_data data = { | ||
466 | .output = output, | ||
467 | .whole = whole, | ||
468 | }; | ||
469 | |||
470 | output_view_for_each_surface(view, &data.root_geo, | ||
471 | damage_surface_iterator, &data); | ||
472 | } | ||
473 | |||
474 | static void output_damage_whole_container_iterator(struct sway_container *con, | ||
475 | void *data) { | ||
476 | struct sway_output *output = data; | ||
477 | |||
478 | if (!sway_assert(con->type == C_VIEW, "expected a view")) { | ||
479 | return; | ||
480 | } | ||
481 | |||
482 | output_damage_view(output, con->sway_view, true); | ||
483 | } | ||
484 | |||
485 | void output_damage_whole_container(struct sway_output *output, | ||
486 | struct sway_container *con) { | ||
487 | float scale = output->wlr_output->scale; | ||
488 | struct wlr_box box = { | ||
489 | .x = con->x * scale, | ||
490 | .y = con->y * scale, | ||
491 | .width = con->width * scale, | ||
492 | .height = con->height * scale, | ||
493 | }; | ||
494 | wlr_output_damage_add_box(output->damage, &box); | ||
495 | |||
496 | container_descendants(con, C_VIEW, output_damage_whole_container_iterator, | ||
497 | output); | ||
498 | } | ||
499 | |||
500 | static void damage_handle_destroy(struct wl_listener *listener, void *data) { | ||
501 | struct sway_output *output = | ||
502 | wl_container_of(listener, output, damage_destroy); | ||
503 | container_destroy(output->swayc); | ||
504 | } | ||
505 | |||
506 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
507 | struct sway_output *output = wl_container_of(listener, output, destroy); | ||
508 | container_destroy(output->swayc); | ||
509 | } | ||
510 | |||
511 | static void handle_mode(struct wl_listener *listener, void *data) { | ||
512 | struct sway_output *output = wl_container_of(listener, output, mode); | ||
513 | arrange_layers(output); | ||
514 | arrange_windows(output->swayc, -1, -1); | ||
515 | } | ||
516 | |||
517 | static void handle_transform(struct wl_listener *listener, void *data) { | ||
518 | struct sway_output *output = wl_container_of(listener, output, transform); | ||
519 | arrange_layers(output); | ||
520 | arrange_windows(output->swayc, -1, -1); | ||
521 | } | ||
522 | |||
523 | static void handle_scale(struct wl_listener *listener, void *data) { | ||
524 | struct sway_output *output = wl_container_of(listener, output, scale); | ||
525 | arrange_layers(output); | ||
526 | arrange_windows(output->swayc, -1, -1); | ||
527 | } | ||
528 | |||
529 | void handle_new_output(struct wl_listener *listener, void *data) { | ||
530 | struct sway_server *server = wl_container_of(listener, server, new_output); | ||
531 | struct wlr_output *wlr_output = data; | ||
532 | wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); | ||
533 | |||
534 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); | ||
535 | if (!output) { | ||
536 | return; | ||
537 | } | ||
538 | output->wlr_output = wlr_output; | ||
539 | wlr_output->data = output; | ||
540 | output->server = server; | ||
541 | |||
542 | if (!wl_list_empty(&wlr_output->modes)) { | ||
543 | struct wlr_output_mode *mode = | ||
544 | wl_container_of(wlr_output->modes.prev, mode, link); | ||
545 | wlr_output_set_mode(wlr_output, mode); | ||
546 | } | ||
547 | |||
548 | output->damage = wlr_output_damage_create(wlr_output); | ||
549 | |||
550 | output->swayc = output_create(output); | ||
551 | if (!output->swayc) { | ||
552 | free(output); | ||
553 | return; | ||
554 | } | ||
555 | |||
556 | size_t len = sizeof(output->layers) / sizeof(output->layers[0]); | ||
557 | for (size_t i = 0; i < len; ++i) { | ||
558 | wl_list_init(&output->layers[i]); | ||
559 | } | ||
560 | |||
561 | input_manager_configure_xcursor(input_manager); | ||
562 | |||
563 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | ||
564 | output->destroy.notify = handle_destroy; | ||
565 | wl_signal_add(&wlr_output->events.mode, &output->mode); | ||
566 | output->mode.notify = handle_mode; | ||
567 | wl_signal_add(&wlr_output->events.transform, &output->transform); | ||
568 | output->transform.notify = handle_transform; | ||
569 | wl_signal_add(&wlr_output->events.scale, &output->scale); | ||
570 | output->scale.notify = handle_scale; | ||
571 | |||
572 | wl_signal_add(&output->damage->events.frame, &output->damage_frame); | ||
573 | output->damage_frame.notify = damage_handle_frame; | ||
574 | wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); | ||
575 | output->damage_destroy.notify = damage_handle_destroy; | ||
576 | |||
577 | arrange_layers(output); | ||
578 | arrange_windows(&root_container, -1, -1); | ||
579 | } | ||
diff --git a/sway/desktop/wl_shell.c b/sway/desktop/wl_shell.c new file mode 100644 index 00000000..b63c220c --- /dev/null +++ b/sway/desktop/wl_shell.c | |||
@@ -0,0 +1,131 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <stdbool.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <wayland-server.h> | ||
5 | #include <wlr/types/wlr_wl_shell.h> | ||
6 | #include "sway/tree/container.h" | ||
7 | #include "sway/tree/layout.h" | ||
8 | #include "sway/server.h" | ||
9 | #include "sway/tree/view.h" | ||
10 | #include "sway/input/seat.h" | ||
11 | #include "sway/input/input-manager.h" | ||
12 | #include "log.h" | ||
13 | |||
14 | static struct sway_wl_shell_view *wl_shell_view_from_view( | ||
15 | struct sway_view *view) { | ||
16 | if (!sway_assert(view->type == SWAY_VIEW_WL_SHELL, | ||
17 | "Expected wl_shell view")) { | ||
18 | return NULL; | ||
19 | } | ||
20 | return (struct sway_wl_shell_view *)view; | ||
21 | } | ||
22 | |||
23 | static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { | ||
24 | if (wl_shell_view_from_view(view) == NULL) { | ||
25 | return NULL; | ||
26 | } | ||
27 | switch (prop) { | ||
28 | case VIEW_PROP_TITLE: | ||
29 | return view->wlr_wl_shell_surface->title; | ||
30 | case VIEW_PROP_CLASS: | ||
31 | return view->wlr_wl_shell_surface->class; | ||
32 | default: | ||
33 | return NULL; | ||
34 | } | ||
35 | } | ||
36 | |||
37 | static void configure(struct sway_view *view, double ox, double oy, int width, | ||
38 | int height) { | ||
39 | struct sway_wl_shell_view *wl_shell_view = wl_shell_view_from_view(view); | ||
40 | if (wl_shell_view == NULL) { | ||
41 | return; | ||
42 | } | ||
43 | view_update_position(view, ox, oy); | ||
44 | wl_shell_view->pending_width = width; | ||
45 | wl_shell_view->pending_height = height; | ||
46 | wlr_wl_shell_surface_configure(view->wlr_wl_shell_surface, 0, width, height); | ||
47 | } | ||
48 | |||
49 | static void _close(struct sway_view *view) { | ||
50 | if (wl_shell_view_from_view(view) == NULL) { | ||
51 | return; | ||
52 | } | ||
53 | |||
54 | wl_client_destroy(view->wlr_wl_shell_surface->client); | ||
55 | } | ||
56 | |||
57 | static void destroy(struct sway_view *view) { | ||
58 | struct sway_wl_shell_view *wl_shell_view = wl_shell_view_from_view(view); | ||
59 | if (wl_shell_view == NULL) { | ||
60 | return; | ||
61 | } | ||
62 | wl_list_remove(&wl_shell_view->commit.link); | ||
63 | wl_list_remove(&wl_shell_view->destroy.link); | ||
64 | free(wl_shell_view); | ||
65 | } | ||
66 | |||
67 | static const struct sway_view_impl view_impl = { | ||
68 | .get_prop = get_prop, | ||
69 | .configure = configure, | ||
70 | .close = _close, | ||
71 | .destroy = destroy, | ||
72 | }; | ||
73 | |||
74 | static void handle_commit(struct wl_listener *listener, void *data) { | ||
75 | struct sway_wl_shell_view *wl_shell_view = | ||
76 | wl_container_of(listener, wl_shell_view, commit); | ||
77 | struct sway_view *view = &wl_shell_view->view; | ||
78 | // NOTE: We intentionally discard the view's desired width here | ||
79 | // TODO: Let floating views do whatever | ||
80 | view_update_size(view, wl_shell_view->pending_width, | ||
81 | wl_shell_view->pending_height); | ||
82 | view_damage(view, false); | ||
83 | } | ||
84 | |||
85 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
86 | struct sway_wl_shell_view *wl_shell_view = | ||
87 | wl_container_of(listener, wl_shell_view, destroy); | ||
88 | view_destroy(&wl_shell_view->view); | ||
89 | } | ||
90 | |||
91 | void handle_wl_shell_surface(struct wl_listener *listener, void *data) { | ||
92 | struct sway_server *server = wl_container_of(listener, server, | ||
93 | wl_shell_surface); | ||
94 | struct wlr_wl_shell_surface *shell_surface = data; | ||
95 | |||
96 | if (shell_surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) { | ||
97 | // popups don't get views | ||
98 | wlr_log(L_DEBUG, "New wl_shell popup"); | ||
99 | return; | ||
100 | } | ||
101 | |||
102 | // TODO: make transient windows floating | ||
103 | |||
104 | wlr_log(L_DEBUG, "New wl_shell toplevel title='%s' app_id='%s'", | ||
105 | shell_surface->title, shell_surface->class); | ||
106 | wlr_wl_shell_surface_ping(shell_surface); | ||
107 | |||
108 | struct sway_wl_shell_view *wl_shell_view = | ||
109 | calloc(1, sizeof(struct sway_wl_shell_view)); | ||
110 | if (!sway_assert(wl_shell_view, "Failed to allocate view")) { | ||
111 | return; | ||
112 | } | ||
113 | |||
114 | view_init(&wl_shell_view->view, SWAY_VIEW_WL_SHELL, &view_impl); | ||
115 | wl_shell_view->view.wlr_wl_shell_surface = shell_surface; | ||
116 | |||
117 | // TODO: | ||
118 | // - Wire up listeners | ||
119 | // - Look up pid and open on appropriate workspace | ||
120 | // - Set new view to maximized so it behaves nicely | ||
121 | // - Criteria | ||
122 | |||
123 | wl_shell_view->commit.notify = handle_commit; | ||
124 | wl_signal_add(&shell_surface->surface->events.commit, | ||
125 | &wl_shell_view->commit); | ||
126 | |||
127 | wl_shell_view->destroy.notify = handle_destroy; | ||
128 | wl_signal_add(&shell_surface->events.destroy, &wl_shell_view->destroy); | ||
129 | |||
130 | view_map(&wl_shell_view->view, shell_surface->surface); | ||
131 | } | ||
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c new file mode 100644 index 00000000..e4703040 --- /dev/null +++ b/sway/desktop/xdg_shell_v6.c | |||
@@ -0,0 +1,249 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <stdbool.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <wayland-server.h> | ||
5 | #include <wlr/types/wlr_xdg_shell_v6.h> | ||
6 | #include "sway/tree/container.h" | ||
7 | #include "sway/tree/layout.h" | ||
8 | #include "sway/server.h" | ||
9 | #include "sway/tree/view.h" | ||
10 | #include "sway/input/seat.h" | ||
11 | #include "sway/input/input-manager.h" | ||
12 | #include "log.h" | ||
13 | |||
14 | static const struct sway_view_child_impl popup_impl; | ||
15 | |||
16 | static void popup_destroy(struct sway_view_child *child) { | ||
17 | if (!sway_assert(child->impl == &popup_impl, | ||
18 | "Expected an xdg_shell_v6 popup")) { | ||
19 | return; | ||
20 | } | ||
21 | struct sway_xdg_popup_v6 *popup = (struct sway_xdg_popup_v6 *)child; | ||
22 | wl_list_remove(&popup->new_popup.link); | ||
23 | wl_list_remove(&popup->unmap.link); | ||
24 | wl_list_remove(&popup->destroy.link); | ||
25 | free(popup); | ||
26 | } | ||
27 | |||
28 | static const struct sway_view_child_impl popup_impl = { | ||
29 | .destroy = popup_destroy, | ||
30 | }; | ||
31 | |||
32 | static struct sway_xdg_popup_v6 *popup_create( | ||
33 | struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view); | ||
34 | |||
35 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | ||
36 | struct sway_xdg_popup_v6 *popup = | ||
37 | wl_container_of(listener, popup, new_popup); | ||
38 | struct wlr_xdg_popup_v6 *wlr_popup = data; | ||
39 | popup_create(wlr_popup, popup->child.view); | ||
40 | } | ||
41 | |||
42 | static void popup_handle_unmap(struct wl_listener *listener, void *data) { | ||
43 | struct sway_xdg_popup_v6 *popup = wl_container_of(listener, popup, unmap); | ||
44 | view_child_destroy(&popup->child); | ||
45 | } | ||
46 | |||
47 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | ||
48 | struct sway_xdg_popup_v6 *popup = wl_container_of(listener, popup, destroy); | ||
49 | view_child_destroy(&popup->child); | ||
50 | } | ||
51 | |||
52 | static struct sway_xdg_popup_v6 *popup_create( | ||
53 | struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) { | ||
54 | struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base; | ||
55 | |||
56 | struct sway_xdg_popup_v6 *popup = | ||
57 | calloc(1, sizeof(struct sway_xdg_popup_v6)); | ||
58 | if (popup == NULL) { | ||
59 | return NULL; | ||
60 | } | ||
61 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | ||
62 | |||
63 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | ||
64 | popup->new_popup.notify = popup_handle_new_popup; | ||
65 | wl_signal_add(&xdg_surface->events.unmap, &popup->unmap); | ||
66 | popup->unmap.notify = popup_handle_unmap; | ||
67 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); | ||
68 | popup->destroy.notify = popup_handle_destroy; | ||
69 | |||
70 | return popup; | ||
71 | } | ||
72 | |||
73 | |||
74 | static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view( | ||
75 | struct sway_view *view) { | ||
76 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL_V6, | ||
77 | "Expected xdg_shell_v6 view")) { | ||
78 | return NULL; | ||
79 | } | ||
80 | return (struct sway_xdg_shell_v6_view *)view; | ||
81 | } | ||
82 | |||
83 | static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { | ||
84 | if (xdg_shell_v6_view_from_view(view) == NULL) { | ||
85 | return NULL; | ||
86 | } | ||
87 | switch (prop) { | ||
88 | case VIEW_PROP_TITLE: | ||
89 | return view->wlr_xdg_surface_v6->toplevel->title; | ||
90 | case VIEW_PROP_APP_ID: | ||
91 | return view->wlr_xdg_surface_v6->toplevel->app_id; | ||
92 | default: | ||
93 | return NULL; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | static void configure(struct sway_view *view, double ox, double oy, int width, | ||
98 | int height) { | ||
99 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
100 | xdg_shell_v6_view_from_view(view); | ||
101 | if (xdg_shell_v6_view == NULL) { | ||
102 | return; | ||
103 | } | ||
104 | |||
105 | view_update_position(view, ox, oy); | ||
106 | xdg_shell_v6_view->pending_width = width; | ||
107 | xdg_shell_v6_view->pending_height = height; | ||
108 | wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height); | ||
109 | } | ||
110 | |||
111 | static void set_activated(struct sway_view *view, bool activated) { | ||
112 | if (xdg_shell_v6_view_from_view(view) == NULL) { | ||
113 | return; | ||
114 | } | ||
115 | struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6; | ||
116 | if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { | ||
117 | wlr_xdg_toplevel_v6_set_activated(surface, activated); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static void for_each_surface(struct sway_view *view, | ||
122 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
123 | if (xdg_shell_v6_view_from_view(view) == NULL) { | ||
124 | return; | ||
125 | } | ||
126 | wlr_xdg_surface_v6_for_each_surface(view->wlr_xdg_surface_v6, iterator, | ||
127 | user_data); | ||
128 | } | ||
129 | |||
130 | static void _close(struct sway_view *view) { | ||
131 | if (xdg_shell_v6_view_from_view(view) == NULL) { | ||
132 | return; | ||
133 | } | ||
134 | struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6; | ||
135 | if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { | ||
136 | wlr_xdg_surface_v6_send_close(surface); | ||
137 | } | ||
138 | } | ||
139 | |||
140 | static void destroy(struct sway_view *view) { | ||
141 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
142 | xdg_shell_v6_view_from_view(view); | ||
143 | if (xdg_shell_v6_view == NULL) { | ||
144 | return; | ||
145 | } | ||
146 | wl_list_remove(&xdg_shell_v6_view->destroy.link); | ||
147 | wl_list_remove(&xdg_shell_v6_view->map.link); | ||
148 | wl_list_remove(&xdg_shell_v6_view->unmap.link); | ||
149 | free(xdg_shell_v6_view); | ||
150 | } | ||
151 | |||
152 | static const struct sway_view_impl view_impl = { | ||
153 | .get_prop = get_prop, | ||
154 | .configure = configure, | ||
155 | .set_activated = set_activated, | ||
156 | .for_each_surface = for_each_surface, | ||
157 | .close = _close, | ||
158 | .destroy = destroy, | ||
159 | }; | ||
160 | |||
161 | static void handle_commit(struct wl_listener *listener, void *data) { | ||
162 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
163 | wl_container_of(listener, xdg_shell_v6_view, commit); | ||
164 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
165 | // NOTE: We intentionally discard the view's desired width here | ||
166 | // TODO: Store this for restoration when moving to floating plane | ||
167 | // TODO: Let floating views do whatever | ||
168 | view_update_size(view, xdg_shell_v6_view->pending_width, | ||
169 | xdg_shell_v6_view->pending_height); | ||
170 | view_damage(view, false); | ||
171 | } | ||
172 | |||
173 | static void handle_new_popup(struct wl_listener *listener, void *data) { | ||
174 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
175 | wl_container_of(listener, xdg_shell_v6_view, new_popup); | ||
176 | struct wlr_xdg_popup_v6 *wlr_popup = data; | ||
177 | popup_create(wlr_popup, &xdg_shell_v6_view->view); | ||
178 | } | ||
179 | |||
180 | static void handle_unmap(struct wl_listener *listener, void *data) { | ||
181 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
182 | wl_container_of(listener, xdg_shell_v6_view, unmap); | ||
183 | |||
184 | view_unmap(&xdg_shell_v6_view->view); | ||
185 | |||
186 | wl_list_remove(&xdg_shell_v6_view->commit.link); | ||
187 | wl_list_remove(&xdg_shell_v6_view->new_popup.link); | ||
188 | } | ||
189 | |||
190 | static void handle_map(struct wl_listener *listener, void *data) { | ||
191 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
192 | wl_container_of(listener, xdg_shell_v6_view, map); | ||
193 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
194 | struct wlr_xdg_surface_v6 *xdg_surface = view->wlr_xdg_surface_v6; | ||
195 | |||
196 | view_map(view, view->wlr_xdg_surface_v6->surface); | ||
197 | |||
198 | xdg_shell_v6_view->commit.notify = handle_commit; | ||
199 | wl_signal_add(&xdg_surface->surface->events.commit, | ||
200 | &xdg_shell_v6_view->commit); | ||
201 | |||
202 | xdg_shell_v6_view->new_popup.notify = handle_new_popup; | ||
203 | wl_signal_add(&xdg_surface->events.new_popup, | ||
204 | &xdg_shell_v6_view->new_popup); | ||
205 | } | ||
206 | |||
207 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
208 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
209 | wl_container_of(listener, xdg_shell_v6_view, destroy); | ||
210 | view_destroy(&xdg_shell_v6_view->view); | ||
211 | } | ||
212 | |||
213 | void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { | ||
214 | struct sway_server *server = wl_container_of(listener, server, | ||
215 | xdg_shell_v6_surface); | ||
216 | struct wlr_xdg_surface_v6 *xdg_surface = data; | ||
217 | |||
218 | if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { | ||
219 | wlr_log(L_DEBUG, "New xdg_shell_v6 popup"); | ||
220 | return; | ||
221 | } | ||
222 | |||
223 | wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", | ||
224 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | ||
225 | wlr_xdg_surface_v6_ping(xdg_surface); | ||
226 | wlr_xdg_toplevel_v6_set_maximized(xdg_surface, true); | ||
227 | |||
228 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
229 | calloc(1, sizeof(struct sway_xdg_shell_v6_view)); | ||
230 | if (!sway_assert(xdg_shell_v6_view, "Failed to allocate view")) { | ||
231 | return; | ||
232 | } | ||
233 | |||
234 | view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl); | ||
235 | xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface; | ||
236 | |||
237 | // TODO: | ||
238 | // - Look up pid and open on appropriate workspace | ||
239 | // - Criteria | ||
240 | |||
241 | xdg_shell_v6_view->map.notify = handle_map; | ||
242 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map); | ||
243 | |||
244 | xdg_shell_v6_view->unmap.notify = handle_unmap; | ||
245 | wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_v6_view->unmap); | ||
246 | |||
247 | xdg_shell_v6_view->destroy.notify = handle_destroy; | ||
248 | wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_v6_view->destroy); | ||
249 | } | ||
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c new file mode 100644 index 00000000..413dbf8b --- /dev/null +++ b/sway/desktop/xwayland.c | |||
@@ -0,0 +1,310 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <stdbool.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <wayland-server.h> | ||
5 | #include <wlr/types/wlr_output_layout.h> | ||
6 | #include <wlr/types/wlr_output.h> | ||
7 | #include <wlr/xwayland.h> | ||
8 | #include "log.h" | ||
9 | #include "sway/desktop.h" | ||
10 | #include "sway/input/input-manager.h" | ||
11 | #include "sway/input/seat.h" | ||
12 | #include "sway/output.h" | ||
13 | #include "sway/server.h" | ||
14 | #include "sway/tree/container.h" | ||
15 | #include "sway/tree/layout.h" | ||
16 | #include "sway/tree/view.h" | ||
17 | |||
18 | static void unmanaged_handle_request_configure(struct wl_listener *listener, | ||
19 | void *data) { | ||
20 | struct sway_xwayland_unmanaged *surface = | ||
21 | wl_container_of(listener, surface, request_configure); | ||
22 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
23 | struct wlr_xwayland_surface_configure_event *ev = data; | ||
24 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, | ||
25 | ev->width, ev->height); | ||
26 | } | ||
27 | |||
28 | static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { | ||
29 | struct sway_xwayland_unmanaged *surface = | ||
30 | wl_container_of(listener, surface, commit); | ||
31 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
32 | |||
33 | if (xsurface->x != surface->lx || xsurface->y != surface->ly) { | ||
34 | // Surface has moved | ||
35 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
36 | true); | ||
37 | surface->lx = xsurface->x; | ||
38 | surface->ly = xsurface->y; | ||
39 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
40 | true); | ||
41 | } else { | ||
42 | desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, | ||
43 | false); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | ||
48 | struct sway_xwayland_unmanaged *surface = | ||
49 | wl_container_of(listener, surface, map); | ||
50 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
51 | |||
52 | wl_list_insert(&root_container.sway_root->xwayland_unmanaged, | ||
53 | &surface->link); | ||
54 | |||
55 | wl_signal_add(&xsurface->surface->events.commit, &surface->commit); | ||
56 | surface->commit.notify = unmanaged_handle_commit; | ||
57 | |||
58 | surface->lx = xsurface->x; | ||
59 | surface->ly = xsurface->y; | ||
60 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); | ||
61 | |||
62 | if (!wlr_xwayland_surface_is_unmanaged(xsurface)) { | ||
63 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
64 | struct wlr_xwayland *xwayland = seat->input->server->xwayland; | ||
65 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | ||
66 | seat_set_focus_surface(seat, xsurface->surface); | ||
67 | } | ||
68 | |||
69 | // TODO: we don't send surface enter/leave events to xwayland unmanaged | ||
70 | // surfaces, but xwayland doesn't support HiDPI anyway | ||
71 | } | ||
72 | |||
73 | static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | ||
74 | struct sway_xwayland_unmanaged *surface = | ||
75 | wl_container_of(listener, surface, unmap); | ||
76 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
77 | desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); | ||
78 | wl_list_remove(&surface->link); | ||
79 | wl_list_remove(&surface->commit.link); | ||
80 | } | ||
81 | |||
82 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { | ||
83 | struct sway_xwayland_unmanaged *surface = | ||
84 | wl_container_of(listener, surface, destroy); | ||
85 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
86 | if (xsurface->mapped) { | ||
87 | unmanaged_handle_unmap(&surface->unmap, xsurface); | ||
88 | } | ||
89 | wl_list_remove(&surface->map.link); | ||
90 | wl_list_remove(&surface->unmap.link); | ||
91 | wl_list_remove(&surface->destroy.link); | ||
92 | free(surface); | ||
93 | } | ||
94 | |||
95 | static struct sway_xwayland_unmanaged *create_unmanaged( | ||
96 | struct wlr_xwayland_surface *xsurface) { | ||
97 | struct sway_xwayland_unmanaged *surface = | ||
98 | calloc(1, sizeof(struct sway_xwayland_unmanaged)); | ||
99 | if (surface == NULL) { | ||
100 | wlr_log(L_ERROR, "Allocation failed"); | ||
101 | return NULL; | ||
102 | } | ||
103 | |||
104 | surface->wlr_xwayland_surface = xsurface; | ||
105 | |||
106 | wl_signal_add(&xsurface->events.request_configure, | ||
107 | &surface->request_configure); | ||
108 | surface->request_configure.notify = unmanaged_handle_request_configure; | ||
109 | wl_signal_add(&xsurface->events.map, &surface->map); | ||
110 | surface->map.notify = unmanaged_handle_map; | ||
111 | wl_signal_add(&xsurface->events.unmap, &surface->unmap); | ||
112 | surface->unmap.notify = unmanaged_handle_unmap; | ||
113 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); | ||
114 | surface->destroy.notify = unmanaged_handle_destroy; | ||
115 | |||
116 | unmanaged_handle_map(&surface->map, xsurface); | ||
117 | |||
118 | return surface; | ||
119 | } | ||
120 | |||
121 | |||
122 | static struct sway_xwayland_view *xwayland_view_from_view( | ||
123 | struct sway_view *view) { | ||
124 | if (!sway_assert(view->type == SWAY_VIEW_XWAYLAND, | ||
125 | "Expected xwayland view")) { | ||
126 | return NULL; | ||
127 | } | ||
128 | return (struct sway_xwayland_view *)view; | ||
129 | } | ||
130 | |||
131 | static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { | ||
132 | if (xwayland_view_from_view(view) == NULL) { | ||
133 | return NULL; | ||
134 | } | ||
135 | switch (prop) { | ||
136 | case VIEW_PROP_TITLE: | ||
137 | return view->wlr_xwayland_surface->title; | ||
138 | case VIEW_PROP_CLASS: | ||
139 | return view->wlr_xwayland_surface->class; | ||
140 | default: | ||
141 | return NULL; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | static void configure(struct sway_view *view, double ox, double oy, int width, | ||
146 | int height) { | ||
147 | struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); | ||
148 | if (xwayland_view == NULL) { | ||
149 | return; | ||
150 | } | ||
151 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
152 | |||
153 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | ||
154 | if (!sway_assert(output, "view must be within tree to set position")) { | ||
155 | return; | ||
156 | } | ||
157 | struct sway_container *root = container_parent(output, C_ROOT); | ||
158 | if (!sway_assert(root, "output must be within tree to set position")) { | ||
159 | return; | ||
160 | } | ||
161 | struct wlr_output_layout *layout = root->sway_root->output_layout; | ||
162 | struct wlr_output_layout_output *loutput = | ||
163 | wlr_output_layout_get(layout, output->sway_output->wlr_output); | ||
164 | if (!sway_assert(loutput, "output must be within layout to set position")) { | ||
165 | return; | ||
166 | } | ||
167 | |||
168 | view_update_position(view, ox, oy); | ||
169 | |||
170 | xwayland_view->pending_width = width; | ||
171 | xwayland_view->pending_height = height; | ||
172 | wlr_xwayland_surface_configure(xsurface, ox + loutput->x, oy + loutput->y, | ||
173 | width, height); | ||
174 | } | ||
175 | |||
176 | static void set_activated(struct sway_view *view, bool activated) { | ||
177 | if (xwayland_view_from_view(view) == NULL) { | ||
178 | return; | ||
179 | } | ||
180 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; | ||
181 | wlr_xwayland_surface_activate(surface, activated); | ||
182 | } | ||
183 | |||
184 | static void _close(struct sway_view *view) { | ||
185 | if (xwayland_view_from_view(view) == NULL) { | ||
186 | return; | ||
187 | } | ||
188 | wlr_xwayland_surface_close(view->wlr_xwayland_surface); | ||
189 | } | ||
190 | |||
191 | static void destroy(struct sway_view *view) { | ||
192 | struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); | ||
193 | if (xwayland_view == NULL) { | ||
194 | return; | ||
195 | } | ||
196 | wl_list_remove(&xwayland_view->destroy.link); | ||
197 | wl_list_remove(&xwayland_view->request_configure.link); | ||
198 | wl_list_remove(&xwayland_view->map.link); | ||
199 | wl_list_remove(&xwayland_view->unmap.link); | ||
200 | free(xwayland_view); | ||
201 | } | ||
202 | |||
203 | static const struct sway_view_impl view_impl = { | ||
204 | .get_prop = get_prop, | ||
205 | .configure = configure, | ||
206 | .set_activated = set_activated, | ||
207 | .close = _close, | ||
208 | .destroy = destroy, | ||
209 | }; | ||
210 | |||
211 | static void handle_commit(struct wl_listener *listener, void *data) { | ||
212 | struct sway_xwayland_view *xwayland_view = | ||
213 | wl_container_of(listener, xwayland_view, commit); | ||
214 | struct sway_view *view = &xwayland_view->view; | ||
215 | // NOTE: We intentionally discard the view's desired width here | ||
216 | // TODO: Let floating views do whatever | ||
217 | view_update_size(view, xwayland_view->pending_width, | ||
218 | xwayland_view->pending_height); | ||
219 | view_damage(view, false); | ||
220 | } | ||
221 | |||
222 | static void handle_unmap(struct wl_listener *listener, void *data) { | ||
223 | struct sway_xwayland_view *xwayland_view = | ||
224 | wl_container_of(listener, xwayland_view, unmap); | ||
225 | wl_list_remove(&xwayland_view->commit.link); | ||
226 | view_unmap(&xwayland_view->view); | ||
227 | } | ||
228 | |||
229 | static void handle_map(struct wl_listener *listener, void *data) { | ||
230 | struct sway_xwayland_view *xwayland_view = | ||
231 | wl_container_of(listener, xwayland_view, map); | ||
232 | struct wlr_xwayland_surface *xsurface = data; | ||
233 | struct sway_view *view = &xwayland_view->view; | ||
234 | |||
235 | // Wire up the commit listener here, because xwayland map/unmap can change | ||
236 | // the underlying wlr_surface | ||
237 | wl_signal_add(&xsurface->surface->events.commit, &xwayland_view->commit); | ||
238 | xwayland_view->commit.notify = handle_commit; | ||
239 | |||
240 | // Put it back into the tree | ||
241 | wlr_xwayland_surface_set_maximized(xsurface, true); | ||
242 | view_map(view, xsurface->surface); | ||
243 | } | ||
244 | |||
245 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
246 | struct sway_xwayland_view *xwayland_view = | ||
247 | wl_container_of(listener, xwayland_view, destroy); | ||
248 | struct sway_view *view = &xwayland_view->view; | ||
249 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
250 | if (xsurface->mapped) { | ||
251 | handle_unmap(&xwayland_view->unmap, xsurface); | ||
252 | } | ||
253 | view_destroy(&xwayland_view->view); | ||
254 | } | ||
255 | |||
256 | static void handle_request_configure(struct wl_listener *listener, void *data) { | ||
257 | struct sway_xwayland_view *xwayland_view = | ||
258 | wl_container_of(listener, xwayland_view, request_configure); | ||
259 | struct wlr_xwayland_surface_configure_event *ev = data; | ||
260 | struct sway_view *view = &xwayland_view->view; | ||
261 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
262 | // TODO: floating windows are allowed to move around like this, but make | ||
263 | // sure tiling windows always stay in place. | ||
264 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, | ||
265 | ev->width, ev->height); | ||
266 | } | ||
267 | |||
268 | void handle_xwayland_surface(struct wl_listener *listener, void *data) { | ||
269 | struct sway_server *server = wl_container_of(listener, server, | ||
270 | xwayland_surface); | ||
271 | struct wlr_xwayland_surface *xsurface = data; | ||
272 | |||
273 | if (wlr_xwayland_surface_is_unmanaged(xsurface) || | ||
274 | xsurface->override_redirect) { | ||
275 | wlr_log(L_DEBUG, "New xwayland unmanaged surface"); | ||
276 | create_unmanaged(xsurface); | ||
277 | return; | ||
278 | } | ||
279 | |||
280 | wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'", | ||
281 | xsurface->title, xsurface->class); | ||
282 | |||
283 | struct sway_xwayland_view *xwayland_view = | ||
284 | calloc(1, sizeof(struct sway_xwayland_view)); | ||
285 | if (!sway_assert(xwayland_view, "Failed to allocate view")) { | ||
286 | return; | ||
287 | } | ||
288 | |||
289 | view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); | ||
290 | xwayland_view->view.wlr_xwayland_surface = xsurface; | ||
291 | |||
292 | // TODO: | ||
293 | // - Look up pid and open on appropriate workspace | ||
294 | // - Criteria | ||
295 | |||
296 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); | ||
297 | xwayland_view->destroy.notify = handle_destroy; | ||
298 | |||
299 | wl_signal_add(&xsurface->events.request_configure, | ||
300 | &xwayland_view->request_configure); | ||
301 | xwayland_view->request_configure.notify = handle_request_configure; | ||
302 | |||
303 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); | ||
304 | xwayland_view->unmap.notify = handle_unmap; | ||
305 | |||
306 | wl_signal_add(&xsurface->events.map, &xwayland_view->map); | ||
307 | xwayland_view->map.notify = handle_map; | ||
308 | |||
309 | handle_map(&xwayland_view->map, xsurface); | ||
310 | } | ||
diff --git a/sway/extensions.c b/sway/extensions.c deleted file mode 100644 index 91746561..00000000 --- a/sway/extensions.c +++ /dev/null | |||
@@ -1,407 +0,0 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <wlc/wlc.h> | ||
3 | #include <wlc/wlc-wayland.h> | ||
4 | #include <wlc/wlc-render.h> | ||
5 | #include "wayland-desktop-shell-server-protocol.h" | ||
6 | #include "wayland-swaylock-server-protocol.h" | ||
7 | #include "wayland-gamma-control-server-protocol.h" | ||
8 | #include "wayland-server-decoration-server-protocol.h" | ||
9 | #include "sway/layout.h" | ||
10 | #include "sway/input_state.h" | ||
11 | #include "sway/extensions.h" | ||
12 | #include "sway/security.h" | ||
13 | #include "sway/ipc-server.h" | ||
14 | #include "log.h" | ||
15 | |||
16 | struct desktop_shell_state desktop_shell; | ||
17 | struct decoration_state decoration_state; | ||
18 | |||
19 | static struct panel_config *find_or_create_panel_config(struct wl_resource *resource) { | ||
20 | for (int i = 0; i < desktop_shell.panels->length; i++) { | ||
21 | struct panel_config *conf = desktop_shell.panels->items[i]; | ||
22 | if (conf->wl_resource == resource) { | ||
23 | sway_log(L_DEBUG, "Found existing panel config for resource %p", resource); | ||
24 | return conf; | ||
25 | } | ||
26 | } | ||
27 | sway_log(L_DEBUG, "Creating panel config for resource %p", resource); | ||
28 | struct panel_config *config = calloc(1, sizeof(struct panel_config)); | ||
29 | if (!config) { | ||
30 | sway_log(L_ERROR, "Unable to create panel config"); | ||
31 | return NULL; | ||
32 | } | ||
33 | list_add(desktop_shell.panels, config); | ||
34 | config->wl_resource = resource; | ||
35 | return config; | ||
36 | } | ||
37 | |||
38 | void background_surface_destructor(struct wl_resource *resource) { | ||
39 | sway_log(L_DEBUG, "Background surface killed"); | ||
40 | int i; | ||
41 | for (i = 0; i < desktop_shell.backgrounds->length; ++i) { | ||
42 | struct background_config *config = desktop_shell.backgrounds->items[i]; | ||
43 | if (config->wl_surface_res == resource) { | ||
44 | list_del(desktop_shell.backgrounds, i); | ||
45 | break; | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | |||
50 | void panel_surface_destructor(struct wl_resource *resource) { | ||
51 | sway_log(L_DEBUG, "Panel surface killed"); | ||
52 | int i; | ||
53 | for (i = 0; i < desktop_shell.panels->length; ++i) { | ||
54 | struct panel_config *config = desktop_shell.panels->items[i]; | ||
55 | if (config->wl_surface_res == resource) { | ||
56 | list_del(desktop_shell.panels, i); | ||
57 | arrange_windows(&root_container, -1, -1); | ||
58 | break; | ||
59 | } | ||
60 | } | ||
61 | } | ||
62 | |||
63 | void lock_surface_destructor(struct wl_resource *resource) { | ||
64 | sway_log(L_DEBUG, "Lock surface killed"); | ||
65 | int i; | ||
66 | for (i = 0; i < desktop_shell.lock_surfaces->length; ++i) { | ||
67 | struct wl_resource *surface = desktop_shell.lock_surfaces->items[i]; | ||
68 | if (surface == resource) { | ||
69 | list_del(desktop_shell.lock_surfaces, i); | ||
70 | arrange_windows(&root_container, -1, -1); | ||
71 | break; | ||
72 | } | ||
73 | } | ||
74 | if (desktop_shell.lock_surfaces->length == 0) { | ||
75 | sway_log(L_DEBUG, "Desktop shell unlocked"); | ||
76 | desktop_shell.is_locked = false; | ||
77 | |||
78 | // We need to now give focus back to the focus which we internally | ||
79 | // track, since when we lock sway we don't actually change our internal | ||
80 | // focus tracking. | ||
81 | swayc_t *focus = get_focused_container(swayc_active_workspace()); | ||
82 | set_focused_container(focus); | ||
83 | wlc_view_focus(focus->handle); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | static void set_background(struct wl_client *client, struct wl_resource *resource, | ||
88 | struct wl_resource *_output, struct wl_resource *surface) { | ||
89 | pid_t pid; | ||
90 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
91 | if (!(get_feature_policy_mask(pid) & FEATURE_BACKGROUND)) { | ||
92 | sway_log(L_INFO, "Denying background feature to %d", pid); | ||
93 | return; | ||
94 | } | ||
95 | wlc_handle output = wlc_handle_from_wl_output_resource(_output); | ||
96 | if (!output) { | ||
97 | return; | ||
98 | } | ||
99 | sway_log(L_DEBUG, "Setting surface %p as background for output %d", surface, (int)output); | ||
100 | struct background_config *config = malloc(sizeof(struct background_config)); | ||
101 | if (!config) { | ||
102 | sway_log(L_ERROR, "Unable to allocate background config"); | ||
103 | return; | ||
104 | } | ||
105 | config->client = client; | ||
106 | config->output = output; | ||
107 | config->surface = wlc_resource_from_wl_surface_resource(surface); | ||
108 | config->wl_surface_res = surface; | ||
109 | list_add(desktop_shell.backgrounds, config); | ||
110 | wl_resource_set_destructor(surface, background_surface_destructor); | ||
111 | arrange_windows(swayc_by_handle(output), -1, -1); | ||
112 | wlc_output_schedule_render(config->output); | ||
113 | } | ||
114 | |||
115 | static void set_panel(struct wl_client *client, struct wl_resource *resource, | ||
116 | struct wl_resource *_output, struct wl_resource *surface) { | ||
117 | pid_t pid; | ||
118 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
119 | if (!(get_feature_policy_mask(pid) & FEATURE_PANEL)) { | ||
120 | sway_log(L_INFO, "Denying panel feature to %d", pid); | ||
121 | return; | ||
122 | } | ||
123 | wlc_handle output = wlc_handle_from_wl_output_resource(_output); | ||
124 | if (!output) { | ||
125 | return; | ||
126 | } | ||
127 | sway_log(L_DEBUG, "Setting surface %p as panel for output %d (wl_resource: %p)", surface, (int)output, resource); | ||
128 | struct panel_config *config = find_or_create_panel_config(resource); | ||
129 | config->output = output; | ||
130 | config->client = client; | ||
131 | config->surface = wlc_resource_from_wl_surface_resource(surface); | ||
132 | config->wl_surface_res = surface; | ||
133 | wl_resource_set_destructor(surface, panel_surface_destructor); | ||
134 | arrange_windows(&root_container, -1, -1); | ||
135 | wlc_output_schedule_render(config->output); | ||
136 | } | ||
137 | |||
138 | static void desktop_set_lock_surface(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface) { | ||
139 | sway_log(L_ERROR, "desktop_set_lock_surface is not currently supported"); | ||
140 | } | ||
141 | |||
142 | static void desktop_unlock(struct wl_client *client, struct wl_resource *resource) { | ||
143 | sway_log(L_ERROR, "desktop_unlock is not currently supported"); | ||
144 | } | ||
145 | |||
146 | static void set_grab_surface(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface) { | ||
147 | sway_log(L_ERROR, "desktop_set_grab_surface is not currently supported"); | ||
148 | } | ||
149 | |||
150 | static void desktop_ready(struct wl_client *client, struct wl_resource *resource) { | ||
151 | // nop | ||
152 | } | ||
153 | |||
154 | static void set_panel_position(struct wl_client *client, struct wl_resource *resource, uint32_t position) { | ||
155 | pid_t pid; | ||
156 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
157 | if (!(get_feature_policy_mask(pid) & FEATURE_PANEL)) { | ||
158 | sway_log(L_INFO, "Denying panel feature to %d", pid); | ||
159 | return; | ||
160 | } | ||
161 | struct panel_config *config = find_or_create_panel_config(resource); | ||
162 | sway_log(L_DEBUG, "Panel position for wl_resource %p changed %d => %d", resource, config->panel_position, position); | ||
163 | config->panel_position = position; | ||
164 | arrange_windows(&root_container, -1, -1); | ||
165 | } | ||
166 | |||
167 | static struct desktop_shell_interface desktop_shell_implementation = { | ||
168 | .set_background = set_background, | ||
169 | .set_panel = set_panel, | ||
170 | .set_lock_surface = desktop_set_lock_surface, | ||
171 | .unlock = desktop_unlock, | ||
172 | .set_grab_surface = set_grab_surface, | ||
173 | .desktop_ready = desktop_ready, | ||
174 | .set_panel_position = set_panel_position | ||
175 | }; | ||
176 | |||
177 | static void desktop_shell_bind(struct wl_client *client, void *data, | ||
178 | uint32_t version, uint32_t id) { | ||
179 | if (version > 3) { | ||
180 | // Unsupported version | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | struct wl_resource *resource = wl_resource_create(client, &desktop_shell_interface, version, id); | ||
185 | if (!resource) { | ||
186 | wl_client_post_no_memory(client); | ||
187 | } | ||
188 | |||
189 | wl_resource_set_implementation(resource, &desktop_shell_implementation, NULL, NULL); | ||
190 | } | ||
191 | |||
192 | static void set_lock_surface(struct wl_client *client, struct wl_resource *resource, | ||
193 | struct wl_resource *_output, struct wl_resource *surface) { | ||
194 | pid_t pid; | ||
195 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
196 | if (!(get_feature_policy_mask(pid) & FEATURE_LOCK)) { | ||
197 | sway_log(L_INFO, "Denying lock feature to %d", pid); | ||
198 | return; | ||
199 | } | ||
200 | swayc_t *output = swayc_by_handle(wlc_handle_from_wl_output_resource(_output)); | ||
201 | swayc_t *view = swayc_by_handle(wlc_handle_from_wl_surface_resource(surface)); | ||
202 | sway_log(L_DEBUG, "Setting lock surface to %p", view); | ||
203 | if (view && output) { | ||
204 | swayc_t *workspace = output->focused; | ||
205 | if (!swayc_is_child_of(view, workspace)) { | ||
206 | move_container_to(view, workspace); | ||
207 | } | ||
208 | // make the view floating so it doesn't rearrange other siblings. | ||
209 | if (!view->is_floating) { | ||
210 | destroy_container(remove_child(view)); | ||
211 | add_floating(workspace, view); | ||
212 | } | ||
213 | wlc_view_set_state(view->handle, WLC_BIT_FULLSCREEN, true); | ||
214 | wlc_view_bring_to_front(view->handle); | ||
215 | wlc_view_focus(view->handle); | ||
216 | desktop_shell.is_locked = true; | ||
217 | input_init(); | ||
218 | arrange_windows(workspace, -1, -1); | ||
219 | list_add(desktop_shell.lock_surfaces, surface); | ||
220 | wl_resource_set_destructor(surface, lock_surface_destructor); | ||
221 | } else { | ||
222 | sway_log(L_ERROR, "Attempted to set lock surface to non-view"); | ||
223 | } | ||
224 | } | ||
225 | |||
226 | static void unlock(struct wl_client *client, struct wl_resource *resource) { | ||
227 | sway_log(L_ERROR, "unlock is not currently supported"); | ||
228 | // This isn't really necessary, we just unlock when the client exits. | ||
229 | } | ||
230 | |||
231 | static struct lock_interface swaylock_implementation = { | ||
232 | .set_lock_surface = set_lock_surface, | ||
233 | .unlock = unlock | ||
234 | }; | ||
235 | |||
236 | static void swaylock_bind(struct wl_client *client, void *data, | ||
237 | uint32_t version, uint32_t id) { | ||
238 | if (version > 1) { | ||
239 | // Unsupported version | ||
240 | return; | ||
241 | } | ||
242 | |||
243 | struct wl_resource *resource = wl_resource_create(client, &lock_interface, version, id); | ||
244 | if (!resource) { | ||
245 | wl_client_post_no_memory(client); | ||
246 | } | ||
247 | |||
248 | wl_resource_set_implementation(resource, &swaylock_implementation, NULL, NULL); | ||
249 | } | ||
250 | |||
251 | static void gamma_control_destroy(struct wl_client *client, struct wl_resource *res) { | ||
252 | wl_resource_destroy(res); | ||
253 | } | ||
254 | |||
255 | static void gamma_control_set_gamma(struct wl_client *client, | ||
256 | struct wl_resource *res, struct wl_array *red, | ||
257 | struct wl_array *green, struct wl_array *blue) { | ||
258 | if (red->size != green->size || red->size != blue->size) { | ||
259 | wl_resource_post_error(res, GAMMA_CONTROL_ERROR_INVALID_GAMMA, | ||
260 | "The gamma ramps don't have the same size"); | ||
261 | return; | ||
262 | } | ||
263 | uint16_t *r = (uint16_t *)red->data; | ||
264 | uint16_t *g = (uint16_t *)green->data; | ||
265 | uint16_t *b = (uint16_t *)blue->data; | ||
266 | wlc_handle output = wlc_handle_from_wl_output_resource( | ||
267 | wl_resource_get_user_data(res)); | ||
268 | if (!output) { | ||
269 | return; | ||
270 | } | ||
271 | sway_log(L_DEBUG, "Setting gamma for output"); | ||
272 | wlc_output_set_gamma(output, red->size / sizeof(uint16_t), r, g, b); | ||
273 | } | ||
274 | |||
275 | static void gamma_control_reset_gamma(struct wl_client *client, | ||
276 | struct wl_resource *resource) { | ||
277 | // This space intentionally left blank | ||
278 | } | ||
279 | |||
280 | static struct gamma_control_interface gamma_control_implementation = { | ||
281 | .destroy = gamma_control_destroy, | ||
282 | .set_gamma = gamma_control_set_gamma, | ||
283 | .reset_gamma = gamma_control_reset_gamma | ||
284 | }; | ||
285 | |||
286 | static void gamma_control_manager_destroy(struct wl_client *client, | ||
287 | struct wl_resource *res) { | ||
288 | wl_resource_destroy(res); | ||
289 | } | ||
290 | |||
291 | static void gamma_control_manager_get(struct wl_client *client, | ||
292 | struct wl_resource *res, uint32_t id, struct wl_resource *_output) { | ||
293 | struct wl_resource *manager_res = wl_resource_create(client, | ||
294 | &gamma_control_interface, wl_resource_get_version(res), id); | ||
295 | wlc_handle output = wlc_handle_from_wl_output_resource(_output); | ||
296 | if (!output) { | ||
297 | return; | ||
298 | } | ||
299 | wl_resource_set_implementation(manager_res, &gamma_control_implementation, | ||
300 | _output, NULL); | ||
301 | gamma_control_send_gamma_size(manager_res, wlc_output_get_gamma_size(output)); | ||
302 | } | ||
303 | |||
304 | static struct gamma_control_manager_interface gamma_manager_implementation = { | ||
305 | .destroy = gamma_control_manager_destroy, | ||
306 | .get_gamma_control = gamma_control_manager_get | ||
307 | }; | ||
308 | |||
309 | static void gamma_control_manager_bind(struct wl_client *client, void *data, | ||
310 | uint32_t version, uint32_t id) { | ||
311 | if (version > 1) { | ||
312 | // Unsupported version | ||
313 | return; | ||
314 | } | ||
315 | struct wl_resource *resource = wl_resource_create(client, | ||
316 | &gamma_control_manager_interface, version, id); | ||
317 | if (!resource) { | ||
318 | wl_client_post_no_memory(client); | ||
319 | } | ||
320 | wl_resource_set_implementation(resource, &gamma_manager_implementation, NULL, NULL); | ||
321 | } | ||
322 | |||
323 | static void server_decoration_release(struct wl_client *client, | ||
324 | struct wl_resource *resource) { | ||
325 | wl_resource_destroy(resource); | ||
326 | } | ||
327 | |||
328 | void server_decoration_enable_csd(wlc_handle handle) { | ||
329 | swayc_t *view = swayc_by_handle(handle); | ||
330 | if (!view) { | ||
331 | sway_log(L_DEBUG, "view invalid"); | ||
332 | return; | ||
333 | } | ||
334 | sway_log(L_DEBUG, "%s requested client side decorations", view->name); | ||
335 | view->border_type = B_NONE; | ||
336 | update_geometry(view); | ||
337 | } | ||
338 | |||
339 | static void server_decoration_request_mode(struct wl_client *client, | ||
340 | struct wl_resource *resource, uint32_t mode) { | ||
341 | sway_log(L_DEBUG, "Client requested server decoration mode %d", mode); | ||
342 | if (mode == ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER) { | ||
343 | return; | ||
344 | } | ||
345 | struct wl_resource *surface = wl_resource_get_user_data(resource); | ||
346 | if (!surface) { | ||
347 | sway_log(L_DEBUG, "surface invalid"); | ||
348 | return; | ||
349 | } | ||
350 | wlc_handle handle = wlc_handle_from_wl_surface_resource(surface); | ||
351 | if (!handle) { | ||
352 | list_add(decoration_state.csd_resources, surface); | ||
353 | return; | ||
354 | } | ||
355 | server_decoration_enable_csd(handle); | ||
356 | } | ||
357 | |||
358 | static struct org_kde_kwin_server_decoration_interface server_decoration_implementation = { | ||
359 | .release = server_decoration_release, | ||
360 | .request_mode = server_decoration_request_mode, | ||
361 | }; | ||
362 | |||
363 | static void server_decoration_manager_create(struct wl_client *client, | ||
364 | struct wl_resource *resource, uint32_t id, struct wl_resource *surface) { | ||
365 | sway_log(L_DEBUG, "Client requested server decoration manager"); | ||
366 | struct wl_resource *manager = wl_resource_create(client, | ||
367 | &org_kde_kwin_server_decoration_interface, 1, id); | ||
368 | if (!manager) { | ||
369 | wl_client_post_no_memory(client); | ||
370 | } | ||
371 | wl_resource_set_implementation(manager, &server_decoration_implementation, surface, NULL); | ||
372 | } | ||
373 | |||
374 | // Jesus christ KDE, these names are whack as hell | ||
375 | static struct org_kde_kwin_server_decoration_manager_interface server_decoration_manager_implementation = { | ||
376 | .create = server_decoration_manager_create, | ||
377 | }; | ||
378 | |||
379 | static void server_decoration_manager_bind(struct wl_client *client, void *data, | ||
380 | uint32_t version, uint32_t id) { | ||
381 | if (version > 1) { | ||
382 | // Unsupported version | ||
383 | return; | ||
384 | } | ||
385 | struct wl_resource *resource = wl_resource_create(client, | ||
386 | &org_kde_kwin_server_decoration_manager_interface, version, id); | ||
387 | if (!resource) { | ||
388 | wl_client_post_no_memory(client); | ||
389 | } | ||
390 | wl_resource_set_implementation(resource, &server_decoration_manager_implementation, NULL, NULL); | ||
391 | org_kde_kwin_server_decoration_manager_send_default_mode(resource, | ||
392 | ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER); | ||
393 | } | ||
394 | |||
395 | void register_extensions(void) { | ||
396 | wl_global_create(wlc_get_wl_display(), &desktop_shell_interface, 3, NULL, desktop_shell_bind); | ||
397 | desktop_shell.backgrounds = create_list(); | ||
398 | desktop_shell.panels = create_list(); | ||
399 | desktop_shell.lock_surfaces = create_list(); | ||
400 | desktop_shell.is_locked = false; | ||
401 | decoration_state.csd_resources = create_list(); | ||
402 | wl_global_create(wlc_get_wl_display(), &lock_interface, 1, NULL, swaylock_bind); | ||
403 | wl_global_create(wlc_get_wl_display(), &gamma_control_manager_interface, 1, | ||
404 | NULL, gamma_control_manager_bind); | ||
405 | wl_global_create(wlc_get_wl_display(), &org_kde_kwin_server_decoration_manager_interface , | ||
406 | 1, NULL, server_decoration_manager_bind); | ||
407 | } | ||
diff --git a/sway/focus.c b/sway/focus.c deleted file mode 100644 index e9b032f8..00000000 --- a/sway/focus.c +++ /dev/null | |||
@@ -1,277 +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/extensions.h" | ||
8 | #include "sway/input_state.h" | ||
9 | #include "sway/ipc-server.h" | ||
10 | #include "sway/border.h" | ||
11 | #include "log.h" | ||
12 | |||
13 | bool locked_container_focus = false; | ||
14 | bool suspend_workspace_cleanup = false; | ||
15 | |||
16 | // switches parent focus to c. will switch it accordingly | ||
17 | static void update_focus(swayc_t *c) { | ||
18 | // Handle if focus switches | ||
19 | swayc_t *parent = c->parent; | ||
20 | if (!parent) return; | ||
21 | if (parent->focused != c) { | ||
22 | // Get previous focus | ||
23 | swayc_t *prev = parent->focused; | ||
24 | // Set new focus | ||
25 | parent->focused = c; | ||
26 | |||
27 | switch (c->type) { | ||
28 | // Shouldn't happen | ||
29 | case C_ROOT: return; | ||
30 | |||
31 | // Case where output changes | ||
32 | case C_OUTPUT: | ||
33 | wlc_output_focus(c->handle); | ||
34 | break; | ||
35 | |||
36 | // Case where workspace changes | ||
37 | case C_WORKSPACE: | ||
38 | if (prev) { | ||
39 | ipc_event_workspace(prev, c, "focus"); | ||
40 | |||
41 | // if the old workspace has no children, destroy it | ||
42 | if(prev->children->length == 0 && prev->floating->length == 0 && !suspend_workspace_cleanup) { | ||
43 | destroy_workspace(prev); | ||
44 | } else { | ||
45 | // update visibility of old workspace | ||
46 | update_visibility(prev); | ||
47 | } | ||
48 | } | ||
49 | // Update visibility of newly focused workspace | ||
50 | update_visibility(c); | ||
51 | break; | ||
52 | |||
53 | default: | ||
54 | case C_VIEW: | ||
55 | case C_CONTAINER: | ||
56 | break; | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | bool move_focus(enum movement_direction direction) { | ||
62 | swayc_t *old_view = get_focused_container(&root_container); | ||
63 | swayc_t *new_view = get_swayc_in_direction(old_view, direction); | ||
64 | if (!new_view) { | ||
65 | return false; | ||
66 | } else if (new_view->type == C_ROOT) { | ||
67 | sway_log(L_DEBUG, "Not setting focus above the workspace level"); | ||
68 | return false; | ||
69 | } else if (new_view->type == C_OUTPUT) { | ||
70 | return set_focused_container(swayc_active_workspace_for(new_view)); | ||
71 | } else if (direction == MOVE_PARENT || direction == MOVE_CHILD) { | ||
72 | return set_focused_container(new_view); | ||
73 | } else if (config->mouse_warping) { | ||
74 | swayc_t *old_op = old_view->type == C_OUTPUT ? | ||
75 | old_view : swayc_parent_by_type(old_view, C_OUTPUT); | ||
76 | swayc_t *focused = get_focused_view(new_view); | ||
77 | if (set_focused_container(focused)) { | ||
78 | if (old_op != swayc_active_output() && focused && focused->type == C_VIEW) { | ||
79 | center_pointer_on(focused); | ||
80 | } | ||
81 | return true; | ||
82 | } | ||
83 | } else { | ||
84 | return set_focused_container(get_focused_view(new_view)); | ||
85 | } | ||
86 | return false; | ||
87 | } | ||
88 | |||
89 | swayc_t *get_focused_container(swayc_t *parent) { | ||
90 | if (!parent) { | ||
91 | return swayc_active_workspace(); | ||
92 | } | ||
93 | while (!parent->is_focused && parent->focused) { | ||
94 | parent = parent->focused; | ||
95 | } | ||
96 | return parent; | ||
97 | } | ||
98 | |||
99 | bool set_focused_container(swayc_t *c) { | ||
100 | if (locked_container_focus || !c || !c->parent) { | ||
101 | return false; | ||
102 | } | ||
103 | |||
104 | // current ("old") workspace for sending workspace change event later | ||
105 | swayc_t *old_ws = swayc_active_workspace(); | ||
106 | // keep track of child count so we can determine if it gets destroyed | ||
107 | int old_ws_child_count = 0; | ||
108 | if (old_ws) { | ||
109 | old_ws_child_count = old_ws->children->length + old_ws->floating->length; | ||
110 | } | ||
111 | |||
112 | // current ("old") focused container | ||
113 | swayc_t *old_focus = get_focused_container(&root_container); | ||
114 | // if old_focus is a workspace, then it's the same workspace as | ||
115 | // old_ws, and we'll need to null its pointer too, since it will | ||
116 | // be destroyed in the update_focus() call | ||
117 | bool old_focus_was_ws = (old_focus->type == C_WORKSPACE); | ||
118 | |||
119 | // workspace of new focused container | ||
120 | swayc_t *workspace = swayc_active_workspace_for(c); | ||
121 | |||
122 | if (swayc_is_fullscreen(get_focused_container(workspace))) { | ||
123 | // if switching to a workspace with a fullscreen view, | ||
124 | // focus on the fullscreen view | ||
125 | c = get_focused_container(workspace); | ||
126 | } | ||
127 | |||
128 | swayc_log(L_DEBUG, c, "Setting focus to %p:%" PRIuPTR, c, c->handle); | ||
129 | |||
130 | if (c->type == C_VIEW) { | ||
131 | // dispatch a window event | ||
132 | ipc_event_window(c, "focus"); | ||
133 | } | ||
134 | |||
135 | // update the global pointer | ||
136 | current_focus = c; | ||
137 | |||
138 | // update container focus from here to root, making necessary changes along | ||
139 | // the way | ||
140 | swayc_t *p = c; | ||
141 | if (p->type != C_OUTPUT && p->type != C_ROOT) { | ||
142 | p->is_focused = true; | ||
143 | } | ||
144 | while (p != &root_container) { | ||
145 | update_focus(p); | ||
146 | p = p->parent; | ||
147 | p->is_focused = false; | ||
148 | } | ||
149 | |||
150 | if (old_focus_was_ws && old_ws_child_count == 0) { | ||
151 | // this workspace was destroyed in update_focus(), so null the pointers | ||
152 | old_focus = NULL; | ||
153 | old_ws = NULL; | ||
154 | } | ||
155 | |||
156 | if (!(wlc_view_get_type(p->handle) & WLC_BIT_POPUP)) { | ||
157 | if (old_focus) { | ||
158 | if (old_focus->type == C_VIEW) { | ||
159 | wlc_view_set_state(old_focus->handle, WLC_BIT_ACTIVATED, false); | ||
160 | } | ||
161 | update_container_border(old_focus); | ||
162 | } | ||
163 | if (c->type == C_VIEW) { | ||
164 | wlc_view_set_state(c->handle, WLC_BIT_ACTIVATED, true); | ||
165 | } | ||
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 | if (c->parent->layout != L_TABBED && c->parent->layout != L_STACKED) { | ||
173 | update_container_border(c); | ||
174 | } | ||
175 | |||
176 | swayc_t *parent = swayc_tabbed_stacked_ancestor(c); | ||
177 | if (parent != NULL) { | ||
178 | arrange_backgrounds(); | ||
179 | arrange_windows(parent, -1, -1); | ||
180 | } | ||
181 | } | ||
182 | |||
183 | if (old_ws != workspace) { | ||
184 | // old_ws might be NULL here but that's ok | ||
185 | ipc_event_workspace(old_ws, workspace, "focus"); | ||
186 | } | ||
187 | |||
188 | return true; | ||
189 | } | ||
190 | |||
191 | bool set_focused_container_for(swayc_t *a, swayc_t *c) { | ||
192 | if (locked_container_focus || !c) { | ||
193 | return false; | ||
194 | } | ||
195 | swayc_t *find = c; | ||
196 | while (find != a && (find = find->parent)) { | ||
197 | if (find == &root_container) { | ||
198 | return false; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | // Get workspace for c, get that workspaces current focused container. | ||
203 | swayc_t *workspace = swayc_active_workspace_for(c); | ||
204 | swayc_t *focused = get_focused_view(workspace); | ||
205 | // if the workspace we are changing focus to has a fullscreen view return | ||
206 | if (swayc_is_fullscreen(focused) && c != focused) { | ||
207 | return false; | ||
208 | } | ||
209 | |||
210 | // Check if we are changing a parent container that will see change | ||
211 | bool effective = true; | ||
212 | while (find != &root_container) { | ||
213 | if (find->parent->focused != find) { | ||
214 | effective = false; | ||
215 | } | ||
216 | find = find->parent; | ||
217 | } | ||
218 | if (effective) { | ||
219 | // Go to set_focused_container | ||
220 | return set_focused_container(c); | ||
221 | } | ||
222 | |||
223 | sway_log(L_DEBUG, "Setting focus for %p:%" PRIuPTR " to %p:%" PRIuPTR, | ||
224 | a, a->handle, c, c->handle); | ||
225 | |||
226 | c->is_focused = true; | ||
227 | swayc_t *p = c; | ||
228 | while (p != a) { | ||
229 | update_focus(p); | ||
230 | p = p->parent; | ||
231 | p->is_focused = false; | ||
232 | } | ||
233 | return true; | ||
234 | } | ||
235 | |||
236 | swayc_t *get_focused_view(swayc_t *parent) { | ||
237 | swayc_t *c = parent; | ||
238 | while (c && c->type != C_VIEW) { | ||
239 | if (c->type == C_WORKSPACE && c->focused == NULL) { | ||
240 | return c; | ||
241 | } | ||
242 | c = c->focused; | ||
243 | } | ||
244 | if (c == NULL) { | ||
245 | c = swayc_active_workspace_for(parent); | ||
246 | } | ||
247 | return c; | ||
248 | } | ||
249 | |||
250 | swayc_t *get_focused_float(swayc_t *ws) { | ||
251 | if(!sway_assert(ws->type == C_WORKSPACE, "must be of workspace type")) { | ||
252 | ws = swayc_active_workspace(); | ||
253 | } | ||
254 | if (ws->floating->length) { | ||
255 | return ws->floating->items[ws->floating->length - 1]; | ||
256 | } | ||
257 | return NULL; | ||
258 | } | ||
259 | |||
260 | swayc_t *get_focused_view_include_floating(swayc_t *parent) { | ||
261 | swayc_t *c = parent; | ||
262 | swayc_t *f = NULL; | ||
263 | |||
264 | while (c && c->type != C_VIEW) { | ||
265 | if (c->type == C_WORKSPACE && c->focused == NULL) { | ||
266 | return ((f = get_focused_float(c))) ? f : c; | ||
267 | } | ||
268 | |||
269 | c = c->focused; | ||
270 | } | ||
271 | |||
272 | if (c == NULL) { | ||
273 | c = swayc_active_workspace_for(parent); | ||
274 | } | ||
275 | |||
276 | return c; | ||
277 | } | ||
diff --git a/sway/handlers.c b/sway/handlers.c deleted file mode 100644 index 33e75d6b..00000000 --- a/sway/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/input.c b/sway/input.c deleted file mode 100644 index 6263f79f..00000000 --- a/sway/input.c +++ /dev/null | |||
@@ -1,69 +0,0 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include <ctype.h> | ||
3 | #include <float.h> | ||
4 | #include <limits.h> | ||
5 | #include <stdio.h> | ||
6 | #include <string.h> | ||
7 | #include <libinput.h> | ||
8 | #include "sway/config.h" | ||
9 | #include "sway/input.h" | ||
10 | #include "list.h" | ||
11 | #include "log.h" | ||
12 | |||
13 | struct input_config *new_input_config(const char* identifier) { | ||
14 | struct input_config *input = calloc(1, sizeof(struct input_config)); | ||
15 | if (!input) { | ||
16 | sway_log(L_DEBUG, "Unable to allocate input config"); | ||
17 | return NULL; | ||
18 | } | ||
19 | sway_log(L_DEBUG, "new_input_config(%s)", identifier); | ||
20 | if (!(input->identifier = strdup(identifier))) { | ||
21 | free(input); | ||
22 | sway_log(L_DEBUG, "Unable to allocate input config"); | ||
23 | return NULL; | ||
24 | } | ||
25 | |||
26 | input->tap = INT_MIN; | ||
27 | input->drag_lock = INT_MIN; | ||
28 | input->dwt = INT_MIN; | ||
29 | input->send_events = INT_MIN; | ||
30 | input->click_method = INT_MIN; | ||
31 | input->middle_emulation = INT_MIN; | ||
32 | input->natural_scroll = INT_MIN; | ||
33 | input->accel_profile = INT_MIN; | ||
34 | input->pointer_accel = FLT_MIN; | ||
35 | input->scroll_method = INT_MIN; | ||
36 | input->left_handed = INT_MIN; | ||
37 | |||
38 | return input; | ||
39 | } | ||
40 | |||
41 | char *libinput_dev_unique_id(struct libinput_device *device) { | ||
42 | int vendor = libinput_device_get_id_vendor(device); | ||
43 | int product = libinput_device_get_id_product(device); | ||
44 | char *name = strdup(libinput_device_get_name(device)); | ||
45 | |||
46 | char *p = name; | ||
47 | for (; *p; ++p) { | ||
48 | if (*p == ' ') { | ||
49 | *p = '_'; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | sway_log(L_DEBUG, "rewritten name %s", name); | ||
54 | |||
55 | int len = strlen(name) + sizeof(char) * 6; | ||
56 | char *identifier = malloc(len); | ||
57 | if (!identifier) { | ||
58 | sway_log(L_ERROR, "Unable to allocate unique input device name"); | ||
59 | return NULL; | ||
60 | } | ||
61 | |||
62 | const char *fmt = "%d:%d:%s"; | ||
63 | snprintf(identifier, len, fmt, vendor, product, name); | ||
64 | free(name); | ||
65 | return identifier; | ||
66 | } | ||
67 | |||
68 | list_t *input_devices = NULL; | ||
69 | struct input_config *current_input_config = NULL; | ||
diff --git a/sway/input/cursor.c b/sway/input/cursor.c new file mode 100644 index 00000000..15a61cbf --- /dev/null +++ b/sway/input/cursor.c | |||
@@ -0,0 +1,384 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #ifdef __linux__ | ||
3 | #include <linux/input-event-codes.h> | ||
4 | #elif __FreeBSD__ | ||
5 | #include <dev/evdev/input-event-codes.h> | ||
6 | #endif | ||
7 | #include <wlr/types/wlr_cursor.h> | ||
8 | #include <wlr/types/wlr_xcursor_manager.h> | ||
9 | #include "list.h" | ||
10 | #include "log.h" | ||
11 | #include "sway/input/cursor.h" | ||
12 | #include "sway/layers.h" | ||
13 | #include "sway/output.h" | ||
14 | #include "sway/tree/view.h" | ||
15 | #include "wlr-layer-shell-unstable-v1-protocol.h" | ||
16 | |||
17 | static struct wlr_surface *layer_surface_at(struct sway_output *output, | ||
18 | struct wl_list *layer, double ox, double oy, double *sx, double *sy) { | ||
19 | struct sway_layer_surface *sway_layer; | ||
20 | wl_list_for_each_reverse(sway_layer, layer, link) { | ||
21 | struct wlr_surface *wlr_surface = | ||
22 | sway_layer->layer_surface->surface; | ||
23 | double _sx = ox - sway_layer->geo.x; | ||
24 | double _sy = oy - sway_layer->geo.y; | ||
25 | // TODO: Test popups/subsurfaces | ||
26 | if (wlr_surface_point_accepts_input(wlr_surface, _sx, _sy)) { | ||
27 | *sx = _sx; | ||
28 | *sy = _sy; | ||
29 | return wlr_surface; | ||
30 | } | ||
31 | } | ||
32 | return NULL; | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * Returns the container at the cursor's position. If there is a surface at that | ||
37 | * location, it is stored in **surface (it may not be a view). | ||
38 | */ | ||
39 | static struct sway_container *container_at_cursor(struct sway_cursor *cursor, | ||
40 | struct wlr_surface **surface, double *sx, double *sy) { | ||
41 | // check for unmanaged views first | ||
42 | struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged; | ||
43 | struct sway_xwayland_unmanaged *unmanaged_surface; | ||
44 | wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { | ||
45 | struct wlr_xwayland_surface *xsurface = | ||
46 | unmanaged_surface->wlr_xwayland_surface; | ||
47 | |||
48 | double _sx = cursor->cursor->x - unmanaged_surface->lx; | ||
49 | double _sy = cursor->cursor->y - unmanaged_surface->ly; | ||
50 | if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { | ||
51 | *surface = xsurface->surface; | ||
52 | *sx = _sx; | ||
53 | *sy = _sy; | ||
54 | return NULL; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | // find the output the cursor is on | ||
59 | struct wlr_output_layout *output_layout = | ||
60 | root_container.sway_root->output_layout; | ||
61 | struct wlr_output *wlr_output = | ||
62 | wlr_output_layout_output_at(output_layout, | ||
63 | cursor->cursor->x, cursor->cursor->y); | ||
64 | if (wlr_output == NULL) { | ||
65 | return NULL; | ||
66 | } | ||
67 | struct sway_output *output = wlr_output->data; | ||
68 | double ox = cursor->cursor->x, oy = cursor->cursor->y; | ||
69 | wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy); | ||
70 | |||
71 | // find the focused workspace on the output for this seat | ||
72 | struct sway_container *ws = | ||
73 | seat_get_focus_inactive(cursor->seat, output->swayc); | ||
74 | if (ws && ws->type != C_WORKSPACE) { | ||
75 | ws = container_parent(ws, C_WORKSPACE); | ||
76 | } | ||
77 | if (!ws) { | ||
78 | return output->swayc; | ||
79 | } | ||
80 | |||
81 | if ((*surface = layer_surface_at(output, | ||
82 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
83 | ox, oy, sx, sy))) { | ||
84 | return ws; | ||
85 | } | ||
86 | if ((*surface = layer_surface_at(output, | ||
87 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
88 | ox, oy, sx, sy))) { | ||
89 | return ws; | ||
90 | } | ||
91 | |||
92 | struct sway_container *c; | ||
93 | if ((c = container_at(ws, cursor->cursor->x, cursor->cursor->y, | ||
94 | surface, sx, sy))) { | ||
95 | return c; | ||
96 | } | ||
97 | |||
98 | if ((*surface = layer_surface_at(output, | ||
99 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
100 | ox, oy, sx, sy))) { | ||
101 | return ws; | ||
102 | } | ||
103 | if ((*surface = layer_surface_at(output, | ||
104 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
105 | ox, oy, sx, sy))) { | ||
106 | return ws; | ||
107 | } | ||
108 | |||
109 | c = seat_get_focus_inactive(cursor->seat, output->swayc); | ||
110 | if (c) { | ||
111 | return c; | ||
112 | } | ||
113 | if (!c && output->swayc->children->length) { | ||
114 | c = output->swayc->children->items[0]; | ||
115 | return c; | ||
116 | } | ||
117 | |||
118 | return output->swayc; | ||
119 | } | ||
120 | |||
121 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time) { | ||
122 | struct wlr_seat *seat = cursor->seat->wlr_seat; | ||
123 | struct wlr_surface *surface = NULL; | ||
124 | double sx, sy; | ||
125 | struct sway_container *c = container_at_cursor(cursor, &surface, &sx, &sy); | ||
126 | if (c && config->focus_follows_mouse) { | ||
127 | seat_set_focus_warp(cursor->seat, c, false); | ||
128 | } | ||
129 | |||
130 | // reset cursor if switching between clients | ||
131 | struct wl_client *client = NULL; | ||
132 | if (surface != NULL) { | ||
133 | client = wl_resource_get_client(surface->resource); | ||
134 | } | ||
135 | if (client != cursor->image_client) { | ||
136 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | ||
137 | "left_ptr", cursor->cursor); | ||
138 | cursor->image_client = client; | ||
139 | } | ||
140 | |||
141 | // send pointer enter/leave | ||
142 | if (surface != NULL) { | ||
143 | if (seat_is_input_allowed(cursor->seat, surface)) { | ||
144 | wlr_seat_pointer_notify_enter(seat, surface, sx, sy); | ||
145 | wlr_seat_pointer_notify_motion(seat, time, sx, sy); | ||
146 | } | ||
147 | } else { | ||
148 | wlr_seat_pointer_clear_focus(seat); | ||
149 | } | ||
150 | } | ||
151 | |||
152 | static void handle_cursor_motion(struct wl_listener *listener, void *data) { | ||
153 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); | ||
154 | struct wlr_event_pointer_motion *event = data; | ||
155 | wlr_cursor_move(cursor->cursor, event->device, | ||
156 | event->delta_x, event->delta_y); | ||
157 | cursor_send_pointer_motion(cursor, event->time_msec); | ||
158 | } | ||
159 | |||
160 | static void handle_cursor_motion_absolute( | ||
161 | struct wl_listener *listener, void *data) { | ||
162 | struct sway_cursor *cursor = | ||
163 | wl_container_of(listener, cursor, motion_absolute); | ||
164 | struct wlr_event_pointer_motion_absolute *event = data; | ||
165 | wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); | ||
166 | cursor_send_pointer_motion(cursor, event->time_msec); | ||
167 | } | ||
168 | |||
169 | void dispatch_cursor_button(struct sway_cursor *cursor, | ||
170 | uint32_t time_msec, uint32_t button, enum wlr_button_state state) { | ||
171 | struct wlr_surface *surface = NULL; | ||
172 | double sx, sy; | ||
173 | struct sway_container *cont = | ||
174 | container_at_cursor(cursor, &surface, &sx, &sy); | ||
175 | if (surface && wlr_surface_is_layer_surface(surface)) { | ||
176 | struct wlr_layer_surface *layer = | ||
177 | wlr_layer_surface_from_wlr_surface(surface); | ||
178 | if (layer->current.keyboard_interactive) { | ||
179 | seat_set_focus_layer(cursor->seat, layer); | ||
180 | return; | ||
181 | } | ||
182 | } | ||
183 | // Avoid moving keyboard focus from a surface that accepts it to one | ||
184 | // that does not unless the change would move us to a new workspace. | ||
185 | // | ||
186 | // This prevents, for example, losing focus when clicking on swaybar. | ||
187 | if (surface && cont && cont->type != C_VIEW) { | ||
188 | struct sway_container *new_ws = cont; | ||
189 | if (new_ws && new_ws->type != C_WORKSPACE) { | ||
190 | new_ws = container_parent(new_ws, C_WORKSPACE); | ||
191 | } | ||
192 | struct sway_container *old_ws = seat_get_focus(cursor->seat); | ||
193 | if (old_ws && old_ws->type != C_WORKSPACE) { | ||
194 | old_ws = container_parent(old_ws, C_WORKSPACE); | ||
195 | } | ||
196 | if (new_ws != old_ws) { | ||
197 | seat_set_focus(cursor->seat, cont); | ||
198 | } | ||
199 | } else { | ||
200 | seat_set_focus(cursor->seat, cont); | ||
201 | } | ||
202 | |||
203 | wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, | ||
204 | time_msec, button, state); | ||
205 | } | ||
206 | |||
207 | static void handle_cursor_button(struct wl_listener *listener, void *data) { | ||
208 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); | ||
209 | struct wlr_event_pointer_button *event = data; | ||
210 | dispatch_cursor_button(cursor, | ||
211 | event->time_msec, event->button, event->state); | ||
212 | } | ||
213 | |||
214 | static void handle_cursor_axis(struct wl_listener *listener, void *data) { | ||
215 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); | ||
216 | struct wlr_event_pointer_axis *event = data; | ||
217 | wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, | ||
218 | event->orientation, event->delta); | ||
219 | } | ||
220 | |||
221 | static void handle_touch_down(struct wl_listener *listener, void *data) { | ||
222 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); | ||
223 | struct wlr_event_touch_down *event = data; | ||
224 | wlr_log(L_DEBUG, "TODO: handle touch down event: %p", event); | ||
225 | } | ||
226 | |||
227 | static void handle_touch_up(struct wl_listener *listener, void *data) { | ||
228 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); | ||
229 | struct wlr_event_touch_up *event = data; | ||
230 | wlr_log(L_DEBUG, "TODO: handle touch up event: %p", event); | ||
231 | } | ||
232 | |||
233 | static void handle_touch_motion(struct wl_listener *listener, void *data) { | ||
234 | struct sway_cursor *cursor = | ||
235 | wl_container_of(listener, cursor, touch_motion); | ||
236 | struct wlr_event_touch_motion *event = data; | ||
237 | wlr_log(L_DEBUG, "TODO: handle touch motion event: %p", event); | ||
238 | } | ||
239 | |||
240 | static void handle_tool_axis(struct wl_listener *listener, void *data) { | ||
241 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); | ||
242 | struct wlr_event_tablet_tool_axis *event = data; | ||
243 | |||
244 | if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) && | ||
245 | (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { | ||
246 | wlr_cursor_warp_absolute(cursor->cursor, event->device, | ||
247 | event->x, event->y); | ||
248 | cursor_send_pointer_motion(cursor, event->time_msec); | ||
249 | } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) { | ||
250 | wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, -1); | ||
251 | cursor_send_pointer_motion(cursor, event->time_msec); | ||
252 | } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { | ||
253 | wlr_cursor_warp_absolute(cursor->cursor, event->device, -1, event->y); | ||
254 | cursor_send_pointer_motion(cursor, event->time_msec); | ||
255 | } | ||
256 | } | ||
257 | |||
258 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | ||
259 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); | ||
260 | struct wlr_event_tablet_tool_tip *event = data; | ||
261 | dispatch_cursor_button(cursor, event->time_msec, | ||
262 | BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? | ||
263 | WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED); | ||
264 | } | ||
265 | |||
266 | static void handle_tool_button(struct wl_listener *listener, void *data) { | ||
267 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); | ||
268 | struct wlr_event_tablet_tool_button *event = data; | ||
269 | // TODO: the user may want to configure which tool buttons are mapped to | ||
270 | // which simulated pointer buttons | ||
271 | switch (event->state) { | ||
272 | case WLR_BUTTON_PRESSED: | ||
273 | if (cursor->tool_buttons == 0) { | ||
274 | dispatch_cursor_button(cursor, | ||
275 | event->time_msec, BTN_RIGHT, event->state); | ||
276 | } | ||
277 | cursor->tool_buttons++; | ||
278 | break; | ||
279 | case WLR_BUTTON_RELEASED: | ||
280 | if (cursor->tool_buttons == 1) { | ||
281 | dispatch_cursor_button(cursor, | ||
282 | event->time_msec, BTN_RIGHT, event->state); | ||
283 | } | ||
284 | cursor->tool_buttons--; | ||
285 | break; | ||
286 | } | ||
287 | } | ||
288 | |||
289 | static void handle_request_set_cursor(struct wl_listener *listener, | ||
290 | void *data) { | ||
291 | struct sway_cursor *cursor = | ||
292 | wl_container_of(listener, cursor, request_set_cursor); | ||
293 | struct wlr_seat_pointer_request_set_cursor_event *event = data; | ||
294 | |||
295 | struct wl_client *focused_client = NULL; | ||
296 | struct wlr_surface *focused_surface = | ||
297 | cursor->seat->wlr_seat->pointer_state.focused_surface; | ||
298 | if (focused_surface != NULL) { | ||
299 | focused_client = wl_resource_get_client(focused_surface->resource); | ||
300 | } | ||
301 | |||
302 | // TODO: check cursor mode | ||
303 | if (focused_client == NULL || | ||
304 | event->seat_client->client != focused_client) { | ||
305 | wlr_log(L_DEBUG, "denying request to set cursor from unfocused client"); | ||
306 | return; | ||
307 | } | ||
308 | |||
309 | wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, | ||
310 | event->hotspot_y); | ||
311 | cursor->image_client = focused_client; | ||
312 | } | ||
313 | |||
314 | void sway_cursor_destroy(struct sway_cursor *cursor) { | ||
315 | if (!cursor) { | ||
316 | return; | ||
317 | } | ||
318 | |||
319 | wlr_xcursor_manager_destroy(cursor->xcursor_manager); | ||
320 | wlr_cursor_destroy(cursor->cursor); | ||
321 | free(cursor); | ||
322 | } | ||
323 | |||
324 | struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | ||
325 | struct sway_cursor *cursor = calloc(1, sizeof(struct sway_cursor)); | ||
326 | if (!sway_assert(cursor, "could not allocate sway cursor")) { | ||
327 | return NULL; | ||
328 | } | ||
329 | |||
330 | struct wlr_cursor *wlr_cursor = wlr_cursor_create(); | ||
331 | if (!sway_assert(wlr_cursor, "could not allocate wlr cursor")) { | ||
332 | free(cursor); | ||
333 | return NULL; | ||
334 | } | ||
335 | |||
336 | cursor->seat = seat; | ||
337 | wlr_cursor_attach_output_layout(wlr_cursor, | ||
338 | root_container.sway_root->output_layout); | ||
339 | |||
340 | // input events | ||
341 | wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); | ||
342 | cursor->motion.notify = handle_cursor_motion; | ||
343 | |||
344 | wl_signal_add(&wlr_cursor->events.motion_absolute, | ||
345 | &cursor->motion_absolute); | ||
346 | cursor->motion_absolute.notify = handle_cursor_motion_absolute; | ||
347 | |||
348 | wl_signal_add(&wlr_cursor->events.button, &cursor->button); | ||
349 | cursor->button.notify = handle_cursor_button; | ||
350 | |||
351 | wl_signal_add(&wlr_cursor->events.axis, &cursor->axis); | ||
352 | cursor->axis.notify = handle_cursor_axis; | ||
353 | |||
354 | wl_signal_add(&wlr_cursor->events.touch_down, &cursor->touch_down); | ||
355 | cursor->touch_down.notify = handle_touch_down; | ||
356 | |||
357 | wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); | ||
358 | cursor->touch_up.notify = handle_touch_up; | ||
359 | |||
360 | wl_signal_add(&wlr_cursor->events.touch_motion, | ||
361 | &cursor->touch_motion); | ||
362 | cursor->touch_motion.notify = handle_touch_motion; | ||
363 | |||
364 | // TODO: tablet protocol support | ||
365 | // Note: We should emulate pointer events for clients that don't support the | ||
366 | // tablet protocol when the time comes | ||
367 | wl_signal_add(&wlr_cursor->events.tablet_tool_axis, | ||
368 | &cursor->tool_axis); | ||
369 | cursor->tool_axis.notify = handle_tool_axis; | ||
370 | |||
371 | wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip); | ||
372 | cursor->tool_tip.notify = handle_tool_tip; | ||
373 | |||
374 | wl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tool_button); | ||
375 | cursor->tool_button.notify = handle_tool_button; | ||
376 | |||
377 | wl_signal_add(&seat->wlr_seat->events.request_set_cursor, | ||
378 | &cursor->request_set_cursor); | ||
379 | cursor->request_set_cursor.notify = handle_request_set_cursor; | ||
380 | |||
381 | cursor->cursor = wlr_cursor; | ||
382 | |||
383 | return cursor; | ||
384 | } | ||
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c new file mode 100644 index 00000000..ae55d2a1 --- /dev/null +++ b/sway/input/input-manager.c | |||
@@ -0,0 +1,445 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include <ctype.h> | ||
3 | #include <float.h> | ||
4 | #include <limits.h> | ||
5 | #include <stdio.h> | ||
6 | #include <string.h> | ||
7 | #include <libinput.h> | ||
8 | #include <math.h> | ||
9 | #include <wlr/backend/libinput.h> | ||
10 | #include <wlr/types/wlr_input_inhibitor.h> | ||
11 | #include "sway/config.h" | ||
12 | #include "sway/input/input-manager.h" | ||
13 | #include "sway/input/seat.h" | ||
14 | #include "sway/server.h" | ||
15 | #include "stringop.h" | ||
16 | #include "list.h" | ||
17 | #include "log.h" | ||
18 | |||
19 | static const char *default_seat = "seat0"; | ||
20 | |||
21 | // TODO make me not global | ||
22 | struct sway_input_manager *input_manager; | ||
23 | |||
24 | struct input_config *current_input_config = NULL; | ||
25 | struct seat_config *current_seat_config = NULL; | ||
26 | |||
27 | struct sway_seat *input_manager_current_seat(struct sway_input_manager *input) { | ||
28 | struct sway_seat *seat = config->handler_context.seat; | ||
29 | if (!seat) { | ||
30 | seat = input_manager_get_default_seat(input_manager); | ||
31 | } | ||
32 | return seat; | ||
33 | } | ||
34 | |||
35 | struct sway_seat *input_manager_get_seat( | ||
36 | struct sway_input_manager *input, const char *seat_name) { | ||
37 | struct sway_seat *seat = NULL; | ||
38 | wl_list_for_each(seat, &input->seats, link) { | ||
39 | if (strcmp(seat->wlr_seat->name, seat_name) == 0) { | ||
40 | return seat; | ||
41 | } | ||
42 | } | ||
43 | |||
44 | return seat_create(input, seat_name); | ||
45 | } | ||
46 | |||
47 | static char *get_device_identifier(struct wlr_input_device *device) { | ||
48 | int vendor = device->vendor; | ||
49 | int product = device->product; | ||
50 | char *name = strdup(device->name); | ||
51 | name = strip_whitespace(name); | ||
52 | |||
53 | char *p = name; | ||
54 | for (; *p; ++p) { | ||
55 | if (*p == ' ') { | ||
56 | *p = '_'; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | const char *fmt = "%d:%d:%s"; | ||
61 | int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; | ||
62 | char *identifier = malloc(len); | ||
63 | if (!identifier) { | ||
64 | wlr_log(L_ERROR, "Unable to allocate unique input device name"); | ||
65 | return NULL; | ||
66 | } | ||
67 | |||
68 | snprintf(identifier, len, fmt, vendor, product, name); | ||
69 | free(name); | ||
70 | return identifier; | ||
71 | } | ||
72 | |||
73 | static struct sway_input_device *input_sway_device_from_wlr( | ||
74 | struct sway_input_manager *input, struct wlr_input_device *device) { | ||
75 | struct sway_input_device *input_device = NULL; | ||
76 | wl_list_for_each(input_device, &input->devices, link) { | ||
77 | if (input_device->wlr_device == device) { | ||
78 | return input_device; | ||
79 | } | ||
80 | } | ||
81 | return NULL; | ||
82 | } | ||
83 | |||
84 | static bool input_has_seat_configuration(struct sway_input_manager *input) { | ||
85 | struct sway_seat *seat = NULL; | ||
86 | wl_list_for_each(seat, &input->seats, link) { | ||
87 | struct seat_config *seat_config = seat_get_config(seat); | ||
88 | if (seat_config) { | ||
89 | return true; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | return false; | ||
94 | } | ||
95 | |||
96 | static void input_manager_libinput_config_pointer( | ||
97 | struct sway_input_device *input_device) { | ||
98 | struct wlr_input_device *wlr_device = input_device->wlr_device; | ||
99 | struct input_config *ic = input_device_get_config(input_device); | ||
100 | struct libinput_device *libinput_device; | ||
101 | |||
102 | if (!ic || !wlr_input_device_is_libinput(wlr_device)) { | ||
103 | return; | ||
104 | } | ||
105 | |||
106 | libinput_device = wlr_libinput_get_device_handle(wlr_device); | ||
107 | wlr_log(L_DEBUG, "input_manager_libinput_config_pointer(%s)", | ||
108 | ic->identifier); | ||
109 | |||
110 | if (ic->accel_profile != INT_MIN) { | ||
111 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)", | ||
112 | ic->identifier, ic->accel_profile); | ||
113 | libinput_device_config_accel_set_profile(libinput_device, | ||
114 | ic->accel_profile); | ||
115 | } | ||
116 | if (ic->click_method != INT_MIN) { | ||
117 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)", | ||
118 | ic->identifier, ic->click_method); | ||
119 | libinput_device_config_click_set_method(libinput_device, | ||
120 | ic->click_method); | ||
121 | } | ||
122 | if (ic->drag_lock != INT_MIN) { | ||
123 | wlr_log(L_DEBUG, | ||
124 | "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", | ||
125 | ic->identifier, ic->click_method); | ||
126 | libinput_device_config_tap_set_drag_lock_enabled(libinput_device, | ||
127 | ic->drag_lock); | ||
128 | } | ||
129 | if (ic->dwt != INT_MIN) { | ||
130 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)", | ||
131 | ic->identifier, ic->dwt); | ||
132 | libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt); | ||
133 | } | ||
134 | if (ic->left_handed != INT_MIN) { | ||
135 | wlr_log(L_DEBUG, | ||
136 | "libinput_config_pointer(%s) left_handed_set_enabled(%d)", | ||
137 | ic->identifier, ic->left_handed); | ||
138 | libinput_device_config_left_handed_set(libinput_device, | ||
139 | ic->left_handed); | ||
140 | } | ||
141 | if (ic->middle_emulation != INT_MIN) { | ||
142 | wlr_log(L_DEBUG, | ||
143 | "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)", | ||
144 | ic->identifier, ic->middle_emulation); | ||
145 | libinput_device_config_middle_emulation_set_enabled(libinput_device, | ||
146 | ic->middle_emulation); | ||
147 | } | ||
148 | if (ic->natural_scroll != INT_MIN) { | ||
149 | wlr_log(L_DEBUG, | ||
150 | "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)", | ||
151 | ic->identifier, ic->natural_scroll); | ||
152 | libinput_device_config_scroll_set_natural_scroll_enabled( | ||
153 | libinput_device, ic->natural_scroll); | ||
154 | } | ||
155 | if (ic->pointer_accel != FLT_MIN) { | ||
156 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)", | ||
157 | ic->identifier, ic->pointer_accel); | ||
158 | libinput_device_config_accel_set_speed(libinput_device, | ||
159 | ic->pointer_accel); | ||
160 | } | ||
161 | if (ic->scroll_method != INT_MIN) { | ||
162 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)", | ||
163 | ic->identifier, ic->scroll_method); | ||
164 | libinput_device_config_scroll_set_method(libinput_device, | ||
165 | ic->scroll_method); | ||
166 | } | ||
167 | if (ic->send_events != INT_MIN) { | ||
168 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)", | ||
169 | ic->identifier, ic->send_events); | ||
170 | libinput_device_config_send_events_set_mode(libinput_device, | ||
171 | ic->send_events); | ||
172 | } | ||
173 | if (ic->tap != INT_MIN) { | ||
174 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)", | ||
175 | ic->identifier, ic->tap); | ||
176 | libinput_device_config_tap_set_enabled(libinput_device, ic->tap); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | static void handle_device_destroy(struct wl_listener *listener, void *data) { | ||
181 | struct wlr_input_device *device = data; | ||
182 | |||
183 | struct sway_input_device *input_device = | ||
184 | input_sway_device_from_wlr(input_manager, device); | ||
185 | |||
186 | if (!sway_assert(input_device, "could not find sway device")) { | ||
187 | return; | ||
188 | } | ||
189 | |||
190 | wlr_log(L_DEBUG, "removing device: '%s'", | ||
191 | input_device->identifier); | ||
192 | |||
193 | struct sway_seat *seat = NULL; | ||
194 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
195 | seat_remove_device(seat, input_device); | ||
196 | } | ||
197 | |||
198 | wl_list_remove(&input_device->link); | ||
199 | wl_list_remove(&input_device->device_destroy.link); | ||
200 | free(input_device->identifier); | ||
201 | free(input_device); | ||
202 | } | ||
203 | |||
204 | static void handle_new_input(struct wl_listener *listener, void *data) { | ||
205 | struct sway_input_manager *input = | ||
206 | wl_container_of(listener, input, new_input); | ||
207 | struct wlr_input_device *device = data; | ||
208 | |||
209 | struct sway_input_device *input_device = | ||
210 | calloc(1, sizeof(struct sway_input_device)); | ||
211 | if (!sway_assert(input_device, "could not allocate input device")) { | ||
212 | return; | ||
213 | } | ||
214 | |||
215 | input_device->wlr_device = device; | ||
216 | input_device->identifier = get_device_identifier(device); | ||
217 | wl_list_insert(&input->devices, &input_device->link); | ||
218 | |||
219 | wlr_log(L_DEBUG, "adding device: '%s'", | ||
220 | input_device->identifier); | ||
221 | |||
222 | if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { | ||
223 | input_manager_libinput_config_pointer(input_device); | ||
224 | } | ||
225 | |||
226 | struct sway_seat *seat = NULL; | ||
227 | if (!input_has_seat_configuration(input)) { | ||
228 | wlr_log(L_DEBUG, "no seat configuration, using default seat"); | ||
229 | seat = input_manager_get_seat(input, default_seat); | ||
230 | seat_add_device(seat, input_device); | ||
231 | return; | ||
232 | } | ||
233 | |||
234 | bool added = false; | ||
235 | wl_list_for_each(seat, &input->seats, link) { | ||
236 | struct seat_config *seat_config = seat_get_config(seat); | ||
237 | bool has_attachment = seat_config && | ||
238 | (seat_config_get_attachment(seat_config, input_device->identifier) || | ||
239 | seat_config_get_attachment(seat_config, "*")); | ||
240 | |||
241 | if (has_attachment) { | ||
242 | seat_add_device(seat, input_device); | ||
243 | added = true; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | if (!added) { | ||
248 | wl_list_for_each(seat, &input->seats, link) { | ||
249 | struct seat_config *seat_config = seat_get_config(seat); | ||
250 | if (seat_config && seat_config->fallback == 1) { | ||
251 | seat_add_device(seat, input_device); | ||
252 | added = true; | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | |||
257 | if (!added) { | ||
258 | wlr_log(L_DEBUG, | ||
259 | "device '%s' is not configured on any seats", | ||
260 | input_device->identifier); | ||
261 | } | ||
262 | |||
263 | wl_signal_add(&device->events.destroy, &input_device->device_destroy); | ||
264 | input_device->device_destroy.notify = handle_device_destroy; | ||
265 | } | ||
266 | |||
267 | static void handle_inhibit_activate(struct wl_listener *listener, void *data) { | ||
268 | struct sway_input_manager *input_manager = wl_container_of( | ||
269 | listener, input_manager, inhibit_activate); | ||
270 | struct sway_seat *seat; | ||
271 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
272 | seat_set_exclusive_client(seat, input_manager->inhibit->active_client); | ||
273 | } | ||
274 | } | ||
275 | |||
276 | static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) { | ||
277 | struct sway_input_manager *input_manager = wl_container_of( | ||
278 | listener, input_manager, inhibit_deactivate); | ||
279 | struct sway_seat *seat; | ||
280 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
281 | seat_set_exclusive_client(seat, NULL); | ||
282 | struct sway_container *previous = seat_get_focus(seat); | ||
283 | if (previous) { | ||
284 | wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, | ||
285 | container_type_to_str(previous->type), previous->name); | ||
286 | // Hack to get seat to re-focus the return value of get_focus | ||
287 | seat_set_focus(seat, previous->parent); | ||
288 | seat_set_focus(seat, previous); | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | struct sway_input_manager *input_manager_create( | ||
294 | struct sway_server *server) { | ||
295 | struct sway_input_manager *input = | ||
296 | calloc(1, sizeof(struct sway_input_manager)); | ||
297 | if (!input) { | ||
298 | return NULL; | ||
299 | } | ||
300 | input->server = server; | ||
301 | |||
302 | wl_list_init(&input->devices); | ||
303 | wl_list_init(&input->seats); | ||
304 | |||
305 | // create the default seat | ||
306 | input_manager_get_seat(input, default_seat); | ||
307 | |||
308 | input->new_input.notify = handle_new_input; | ||
309 | wl_signal_add(&server->backend->events.new_input, &input->new_input); | ||
310 | |||
311 | input->inhibit = wlr_input_inhibit_manager_create(server->wl_display); | ||
312 | input->inhibit_activate.notify = handle_inhibit_activate; | ||
313 | wl_signal_add(&input->inhibit->events.activate, | ||
314 | &input->inhibit_activate); | ||
315 | input->inhibit_deactivate.notify = handle_inhibit_deactivate; | ||
316 | wl_signal_add(&input->inhibit->events.deactivate, | ||
317 | &input->inhibit_deactivate); | ||
318 | |||
319 | return input; | ||
320 | } | ||
321 | |||
322 | bool input_manager_has_focus(struct sway_input_manager *input, | ||
323 | struct sway_container *container) { | ||
324 | struct sway_seat *seat = NULL; | ||
325 | wl_list_for_each(seat, &input->seats, link) { | ||
326 | if (seat_get_focus(seat) == container) { | ||
327 | return true; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | return false; | ||
332 | } | ||
333 | |||
334 | void input_manager_set_focus(struct sway_input_manager *input, | ||
335 | struct sway_container *container) { | ||
336 | struct sway_seat *seat; | ||
337 | wl_list_for_each(seat, &input->seats, link) { | ||
338 | seat_set_focus(seat, container); | ||
339 | } | ||
340 | } | ||
341 | |||
342 | void input_manager_apply_input_config(struct sway_input_manager *input, | ||
343 | struct input_config *input_config) { | ||
344 | struct sway_input_device *input_device = NULL; | ||
345 | wl_list_for_each(input_device, &input->devices, link) { | ||
346 | if (strcmp(input_device->identifier, input_config->identifier) == 0) { | ||
347 | if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { | ||
348 | input_manager_libinput_config_pointer(input_device); | ||
349 | } | ||
350 | |||
351 | struct sway_seat *seat = NULL; | ||
352 | wl_list_for_each(seat, &input->seats, link) { | ||
353 | seat_configure_device(seat, input_device); | ||
354 | } | ||
355 | } | ||
356 | } | ||
357 | } | ||
358 | |||
359 | void input_manager_apply_seat_config(struct sway_input_manager *input, | ||
360 | struct seat_config *seat_config) { | ||
361 | wlr_log(L_DEBUG, "applying new seat config for seat %s", | ||
362 | seat_config->name); | ||
363 | struct sway_seat *seat = input_manager_get_seat(input, seat_config->name); | ||
364 | if (!seat) { | ||
365 | return; | ||
366 | } | ||
367 | |||
368 | seat_apply_config(seat, seat_config); | ||
369 | |||
370 | // for every device, try to add it to a seat and if no seat has it | ||
371 | // attached, add it to the fallback seats. | ||
372 | struct sway_input_device *input_device = NULL; | ||
373 | wl_list_for_each(input_device, &input->devices, link) { | ||
374 | list_t *seat_list = create_list(); | ||
375 | struct sway_seat *seat = NULL; | ||
376 | wl_list_for_each(seat, &input->seats, link) { | ||
377 | struct seat_config *seat_config = seat_get_config(seat); | ||
378 | if (!seat_config) { | ||
379 | continue; | ||
380 | } | ||
381 | if (seat_config_get_attachment(seat_config, "*") || | ||
382 | seat_config_get_attachment(seat_config, | ||
383 | input_device->identifier)) { | ||
384 | list_add(seat_list, seat); | ||
385 | } | ||
386 | } | ||
387 | |||
388 | if (seat_list->length) { | ||
389 | wl_list_for_each(seat, &input->seats, link) { | ||
390 | bool attached = false; | ||
391 | for (int i = 0; i < seat_list->length; ++i) { | ||
392 | if (seat == seat_list->items[i]) { | ||
393 | attached = true; | ||
394 | break; | ||
395 | } | ||
396 | } | ||
397 | if (attached) { | ||
398 | seat_add_device(seat, input_device); | ||
399 | } else { | ||
400 | seat_remove_device(seat, input_device); | ||
401 | } | ||
402 | } | ||
403 | } else { | ||
404 | wl_list_for_each(seat, &input->seats, link) { | ||
405 | struct seat_config *seat_config = seat_get_config(seat); | ||
406 | if (seat_config && seat_config->fallback == 1) { | ||
407 | seat_add_device(seat, input_device); | ||
408 | } else { | ||
409 | seat_remove_device(seat, input_device); | ||
410 | } | ||
411 | } | ||
412 | } | ||
413 | list_free(seat_list); | ||
414 | } | ||
415 | } | ||
416 | |||
417 | void input_manager_configure_xcursor(struct sway_input_manager *input) { | ||
418 | struct sway_seat *seat = NULL; | ||
419 | wl_list_for_each(seat, &input->seats, link) { | ||
420 | seat_configure_xcursor(seat); | ||
421 | } | ||
422 | } | ||
423 | |||
424 | struct sway_seat *input_manager_get_default_seat( | ||
425 | struct sway_input_manager *input) { | ||
426 | struct sway_seat *seat = NULL; | ||
427 | wl_list_for_each(seat, &input->seats, link) { | ||
428 | if (strcmp(seat->wlr_seat->name, "seat0") == 0) { | ||
429 | return seat; | ||
430 | } | ||
431 | } | ||
432 | return seat; | ||
433 | } | ||
434 | |||
435 | struct input_config *input_device_get_config(struct sway_input_device *device) { | ||
436 | struct input_config *input_config = NULL; | ||
437 | for (int i = 0; i < config->input_configs->length; ++i) { | ||
438 | input_config = config->input_configs->items[i]; | ||
439 | if (strcmp(input_config->identifier, device->identifier) == 0) { | ||
440 | return input_config; | ||
441 | } | ||
442 | } | ||
443 | |||
444 | return NULL; | ||
445 | } | ||
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c new file mode 100644 index 00000000..dbb0c359 --- /dev/null +++ b/sway/input/keyboard.c | |||
@@ -0,0 +1,504 @@ | |||
1 | #include <assert.h> | ||
2 | #include <wlr/backend/multi.h> | ||
3 | #include <wlr/backend/session.h> | ||
4 | #include "sway/input/seat.h" | ||
5 | #include "sway/input/keyboard.h" | ||
6 | #include "sway/input/input-manager.h" | ||
7 | #include "sway/commands.h" | ||
8 | #include "log.h" | ||
9 | |||
10 | static bool keysym_is_modifier(xkb_keysym_t keysym) { | ||
11 | switch (keysym) { | ||
12 | case XKB_KEY_Shift_L: case XKB_KEY_Shift_R: | ||
13 | case XKB_KEY_Control_L: case XKB_KEY_Control_R: | ||
14 | case XKB_KEY_Caps_Lock: | ||
15 | case XKB_KEY_Shift_Lock: | ||
16 | case XKB_KEY_Meta_L: case XKB_KEY_Meta_R: | ||
17 | case XKB_KEY_Alt_L: case XKB_KEY_Alt_R: | ||
18 | case XKB_KEY_Super_L: case XKB_KEY_Super_R: | ||
19 | case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R: | ||
20 | return true; | ||
21 | default: | ||
22 | return false; | ||
23 | } | ||
24 | } | ||
25 | |||
26 | static size_t pressed_keysyms_length(xkb_keysym_t *pressed_keysyms) { | ||
27 | size_t n = 0; | ||
28 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) { | ||
29 | if (pressed_keysyms[i] != XKB_KEY_NoSymbol) { | ||
30 | ++n; | ||
31 | } | ||
32 | } | ||
33 | return n; | ||
34 | } | ||
35 | |||
36 | static ssize_t pressed_keysyms_index(xkb_keysym_t *pressed_keysyms, | ||
37 | xkb_keysym_t keysym) { | ||
38 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) { | ||
39 | if (pressed_keysyms[i] == keysym) { | ||
40 | return i; | ||
41 | } | ||
42 | } | ||
43 | return -1; | ||
44 | } | ||
45 | |||
46 | static void pressed_keysyms_add(xkb_keysym_t *pressed_keysyms, | ||
47 | xkb_keysym_t keysym) { | ||
48 | ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym); | ||
49 | if (i < 0) { | ||
50 | i = pressed_keysyms_index(pressed_keysyms, XKB_KEY_NoSymbol); | ||
51 | if (i >= 0) { | ||
52 | pressed_keysyms[i] = keysym; | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | |||
57 | static void pressed_keysyms_remove(xkb_keysym_t *pressed_keysyms, | ||
58 | xkb_keysym_t keysym) { | ||
59 | ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym); | ||
60 | if (i >= 0) { | ||
61 | pressed_keysyms[i] = XKB_KEY_NoSymbol; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | static void pressed_keysyms_update(xkb_keysym_t *pressed_keysyms, | ||
66 | const xkb_keysym_t *keysyms, size_t keysyms_len, | ||
67 | enum wlr_key_state state) { | ||
68 | for (size_t i = 0; i < keysyms_len; ++i) { | ||
69 | if (keysym_is_modifier(keysyms[i])) { | ||
70 | continue; | ||
71 | } | ||
72 | if (state == WLR_KEY_PRESSED) { | ||
73 | pressed_keysyms_add(pressed_keysyms, keysyms[i]); | ||
74 | } else { // WLR_KEY_RELEASED | ||
75 | pressed_keysyms_remove(pressed_keysyms, keysyms[i]); | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | static bool binding_matches_key_state(struct sway_binding *binding, | ||
81 | enum wlr_key_state key_state) { | ||
82 | if (key_state == WLR_KEY_PRESSED && !binding->release) { | ||
83 | return true; | ||
84 | } | ||
85 | if (key_state == WLR_KEY_RELEASED && binding->release) { | ||
86 | return true; | ||
87 | } | ||
88 | |||
89 | return false; | ||
90 | } | ||
91 | |||
92 | static void keyboard_execute_command(struct sway_keyboard *keyboard, | ||
93 | struct sway_binding *binding) { | ||
94 | wlr_log(L_DEBUG, "running command for binding: %s", | ||
95 | binding->command); | ||
96 | config_clear_handler_context(config); | ||
97 | config->handler_context.seat = keyboard->seat_device->sway_seat; | ||
98 | struct cmd_results *results = execute_command(binding->command, NULL); | ||
99 | if (results->status != CMD_SUCCESS) { | ||
100 | wlr_log(L_DEBUG, "could not run command for binding: %s (%s)", | ||
101 | binding->command, results->error); | ||
102 | } | ||
103 | free_cmd_results(results); | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * Execute a built-in, hardcoded compositor binding. These are triggered from a | ||
108 | * single keysym. | ||
109 | * | ||
110 | * Returns true if the keysym was handled by a binding and false if the event | ||
111 | * should be propagated to clients. | ||
112 | */ | ||
113 | static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | ||
114 | xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) { | ||
115 | for (size_t i = 0; i < keysyms_len; ++i) { | ||
116 | xkb_keysym_t keysym = pressed_keysyms[i]; | ||
117 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && | ||
118 | keysym <= XKB_KEY_XF86Switch_VT_12) { | ||
119 | if (wlr_backend_is_multi(server.backend)) { | ||
120 | struct wlr_session *session = | ||
121 | wlr_multi_get_session(server.backend); | ||
122 | if (session) { | ||
123 | unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; | ||
124 | wlr_session_change_vt(session, vt); | ||
125 | } | ||
126 | } | ||
127 | return true; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | return false; | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Execute keyboard bindings bound with `bindysm` for the given keyboard state. | ||
136 | * | ||
137 | * Returns true if the keysym was handled by a binding and false if the event | ||
138 | * should be propagated to clients. | ||
139 | */ | ||
140 | static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, | ||
141 | xkb_keysym_t *pressed_keysyms, uint32_t modifiers, | ||
142 | enum wlr_key_state key_state) { | ||
143 | // configured bindings | ||
144 | int n = pressed_keysyms_length(pressed_keysyms); | ||
145 | list_t *keysym_bindings = config->current_mode->keysym_bindings; | ||
146 | for (int i = 0; i < keysym_bindings->length; ++i) { | ||
147 | struct sway_binding *binding = keysym_bindings->items[i]; | ||
148 | if (!binding_matches_key_state(binding, key_state) || | ||
149 | modifiers ^ binding->modifiers || | ||
150 | n != binding->keys->length) { | ||
151 | continue; | ||
152 | } | ||
153 | |||
154 | bool match = true; | ||
155 | for (int j = 0; j < binding->keys->length; ++j) { | ||
156 | match = | ||
157 | pressed_keysyms_index(pressed_keysyms, | ||
158 | *(int*)binding->keys->items[j]) >= 0; | ||
159 | |||
160 | if (!match) { | ||
161 | break; | ||
162 | } | ||
163 | } | ||
164 | |||
165 | if (match) { | ||
166 | keyboard_execute_command(keyboard, binding); | ||
167 | return true; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | return false; | ||
172 | } | ||
173 | |||
174 | static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, | ||
175 | struct sway_binding *binding, struct wlr_event_keyboard_key *event) { | ||
176 | assert(binding->bindcode); | ||
177 | |||
178 | uint32_t keycode = event->keycode + 8; | ||
179 | |||
180 | if (!binding_matches_key_state(binding, event->state)) { | ||
181 | return false; | ||
182 | } | ||
183 | |||
184 | uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); | ||
185 | if (modifiers ^ binding->modifiers) { | ||
186 | return false; | ||
187 | } | ||
188 | |||
189 | // on release, the released key must be in the binding | ||
190 | if (event->state == WLR_KEY_RELEASED) { | ||
191 | bool found = false; | ||
192 | for (int i = 0; i < binding->keys->length; ++i) { | ||
193 | uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8; | ||
194 | if (binding_keycode == keycode) { | ||
195 | found = true; | ||
196 | break; | ||
197 | } | ||
198 | } | ||
199 | if (!found) { | ||
200 | return false; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | // every keycode in the binding must be present in the pressed keys on the | ||
205 | // keyboard | ||
206 | for (int i = 0; i < binding->keys->length; ++i) { | ||
207 | uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8; | ||
208 | if (event->state == WLR_KEY_RELEASED && keycode == binding_keycode) { | ||
209 | continue; | ||
210 | } | ||
211 | |||
212 | bool found = false; | ||
213 | for (size_t j = 0; j < keyboard->num_keycodes; ++j) { | ||
214 | xkb_keycode_t keycode = keyboard->keycodes[j] + 8; | ||
215 | if (keycode == binding_keycode) { | ||
216 | found = true; | ||
217 | break; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | if (!found) { | ||
222 | return false; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | // every keycode pressed on the keyboard must be present within the binding | ||
227 | // keys (unless it is a modifier) | ||
228 | for (size_t i = 0; i < keyboard->num_keycodes; ++i) { | ||
229 | xkb_keycode_t keycode = keyboard->keycodes[i] + 8; | ||
230 | bool found = false; | ||
231 | for (int j = 0; j < binding->keys->length; ++j) { | ||
232 | uint32_t binding_keycode = *(uint32_t*)binding->keys->items[j] + 8; | ||
233 | if (binding_keycode == keycode) { | ||
234 | found = true; | ||
235 | break; | ||
236 | } | ||
237 | } | ||
238 | |||
239 | if (!found) { | ||
240 | if (!binding->modifiers) { | ||
241 | return false; | ||
242 | } | ||
243 | |||
244 | // check if it is a modifier, which we know matched from the check | ||
245 | // above | ||
246 | const xkb_keysym_t *keysyms; | ||
247 | int num_keysyms = | ||
248 | xkb_state_key_get_syms(keyboard->xkb_state, | ||
249 | keycode, &keysyms); | ||
250 | if (num_keysyms != 1 || !keysym_is_modifier(keysyms[0])) { | ||
251 | return false; | ||
252 | } | ||
253 | } | ||
254 | } | ||
255 | |||
256 | return true; | ||
257 | } | ||
258 | |||
259 | /** | ||
260 | * Execute keyboard bindings bound with `bindcode` for the given keyboard state. | ||
261 | * | ||
262 | * Returns true if the keysym was handled by a binding and false if the event | ||
263 | * should be propagated to clients. | ||
264 | */ | ||
265 | static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard, | ||
266 | struct wlr_event_keyboard_key *event) { | ||
267 | struct wlr_keyboard *wlr_keyboard = | ||
268 | keyboard->seat_device->input_device->wlr_device->keyboard; | ||
269 | list_t *keycode_bindings = config->current_mode->keycode_bindings; | ||
270 | for (int i = 0; i < keycode_bindings->length; ++i) { | ||
271 | struct sway_binding *binding = keycode_bindings->items[i]; | ||
272 | if (binding_matches_keycodes(wlr_keyboard, binding, event)) { | ||
273 | keyboard_execute_command(keyboard, binding); | ||
274 | return true; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | return false; | ||
279 | } | ||
280 | |||
281 | /** | ||
282 | * Get keysyms and modifiers from the keyboard as xkb sees them. | ||
283 | * | ||
284 | * This uses the xkb keysyms translation based on pressed modifiers and clears | ||
285 | * the consumed modifiers from the list of modifiers passed to keybind | ||
286 | * detection. | ||
287 | * | ||
288 | * On US layout, pressing Alt+Shift+2 will trigger Alt+@. | ||
289 | */ | ||
290 | static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, | ||
291 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | ||
292 | uint32_t *modifiers) { | ||
293 | struct wlr_input_device *device = | ||
294 | keyboard->seat_device->input_device->wlr_device; | ||
295 | *modifiers = wlr_keyboard_get_modifiers(device->keyboard); | ||
296 | xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( | ||
297 | device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); | ||
298 | *modifiers = *modifiers & ~consumed; | ||
299 | |||
300 | return xkb_state_key_get_syms(device->keyboard->xkb_state, | ||
301 | keycode, keysyms); | ||
302 | } | ||
303 | |||
304 | /** | ||
305 | * Get keysyms and modifiers from the keyboard as if modifiers didn't change | ||
306 | * keysyms. | ||
307 | * | ||
308 | * This avoids the xkb keysym translation based on modifiers considered pressed | ||
309 | * in the state. | ||
310 | * | ||
311 | * This will trigger keybinds such as Alt+Shift+2. | ||
312 | */ | ||
313 | static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, | ||
314 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | ||
315 | uint32_t *modifiers) { | ||
316 | struct wlr_input_device *device = | ||
317 | keyboard->seat_device->input_device->wlr_device; | ||
318 | *modifiers = wlr_keyboard_get_modifiers(device->keyboard); | ||
319 | |||
320 | xkb_layout_index_t layout_index = xkb_state_key_get_layout( | ||
321 | device->keyboard->xkb_state, keycode); | ||
322 | return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, | ||
323 | keycode, layout_index, 0, keysyms); | ||
324 | } | ||
325 | |||
326 | static void handle_keyboard_key(struct wl_listener *listener, void *data) { | ||
327 | struct sway_keyboard *keyboard = | ||
328 | wl_container_of(listener, keyboard, keyboard_key); | ||
329 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | ||
330 | struct wlr_input_device *wlr_device = | ||
331 | keyboard->seat_device->input_device->wlr_device; | ||
332 | struct wlr_event_keyboard_key *event = data; | ||
333 | |||
334 | xkb_keycode_t keycode = event->keycode + 8; | ||
335 | bool handled = false; | ||
336 | |||
337 | // handle keycodes | ||
338 | handled = keyboard_execute_bindcode(keyboard, event); | ||
339 | |||
340 | // handle translated keysyms | ||
341 | if (!handled && event->state == WLR_KEY_RELEASED) { | ||
342 | handled = keyboard_execute_bindsym(keyboard, | ||
343 | keyboard->pressed_keysyms_translated, | ||
344 | keyboard->modifiers_translated, | ||
345 | event->state); | ||
346 | } | ||
347 | const xkb_keysym_t *translated_keysyms; | ||
348 | size_t translated_keysyms_len = | ||
349 | keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms, | ||
350 | &keyboard->modifiers_translated); | ||
351 | pressed_keysyms_update(keyboard->pressed_keysyms_translated, | ||
352 | translated_keysyms, translated_keysyms_len, event->state); | ||
353 | if (!handled && event->state == WLR_KEY_PRESSED) { | ||
354 | handled = keyboard_execute_bindsym(keyboard, | ||
355 | keyboard->pressed_keysyms_translated, | ||
356 | keyboard->modifiers_translated, | ||
357 | event->state); | ||
358 | } | ||
359 | |||
360 | // Handle raw keysyms | ||
361 | if (!handled && event->state == WLR_KEY_RELEASED) { | ||
362 | handled = keyboard_execute_bindsym(keyboard, | ||
363 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, | ||
364 | event->state); | ||
365 | } | ||
366 | const xkb_keysym_t *raw_keysyms; | ||
367 | size_t raw_keysyms_len = | ||
368 | keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &keyboard->modifiers_raw); | ||
369 | pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms, | ||
370 | raw_keysyms_len, event->state); | ||
371 | if (!handled && event->state == WLR_KEY_PRESSED) { | ||
372 | handled = keyboard_execute_bindsym(keyboard, | ||
373 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, | ||
374 | event->state); | ||
375 | } | ||
376 | |||
377 | // Compositor bindings | ||
378 | if (!handled && event->state == WLR_KEY_PRESSED) { | ||
379 | handled = | ||
380 | keyboard_execute_compositor_binding(keyboard, | ||
381 | keyboard->pressed_keysyms_translated, | ||
382 | keyboard->modifiers_translated, | ||
383 | translated_keysyms_len); | ||
384 | } | ||
385 | if (!handled && event->state == WLR_KEY_PRESSED) { | ||
386 | handled = | ||
387 | keyboard_execute_compositor_binding(keyboard, | ||
388 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, | ||
389 | raw_keysyms_len); | ||
390 | } | ||
391 | |||
392 | if (!handled || event->state == WLR_KEY_RELEASED) { | ||
393 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | ||
394 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | ||
395 | event->keycode, event->state); | ||
396 | } | ||
397 | } | ||
398 | |||
399 | static void handle_keyboard_modifiers(struct wl_listener *listener, | ||
400 | void *data) { | ||
401 | struct sway_keyboard *keyboard = | ||
402 | wl_container_of(listener, keyboard, keyboard_modifiers); | ||
403 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | ||
404 | struct wlr_input_device *wlr_device = | ||
405 | keyboard->seat_device->input_device->wlr_device; | ||
406 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | ||
407 | wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers); | ||
408 | } | ||
409 | |||
410 | struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | ||
411 | struct sway_seat_device *device) { | ||
412 | struct sway_keyboard *keyboard = | ||
413 | calloc(1, sizeof(struct sway_keyboard)); | ||
414 | if (!sway_assert(keyboard, "could not allocate sway keyboard")) { | ||
415 | return NULL; | ||
416 | } | ||
417 | |||
418 | keyboard->seat_device = device; | ||
419 | device->keyboard = keyboard; | ||
420 | |||
421 | wl_list_init(&keyboard->keyboard_key.link); | ||
422 | wl_list_init(&keyboard->keyboard_modifiers.link); | ||
423 | |||
424 | return keyboard; | ||
425 | } | ||
426 | |||
427 | void sway_keyboard_configure(struct sway_keyboard *keyboard) { | ||
428 | struct xkb_rule_names rules; | ||
429 | memset(&rules, 0, sizeof(rules)); | ||
430 | struct input_config *input_config = | ||
431 | input_device_get_config(keyboard->seat_device->input_device); | ||
432 | struct wlr_input_device *wlr_device = | ||
433 | keyboard->seat_device->input_device->wlr_device; | ||
434 | |||
435 | if (input_config && input_config->xkb_layout) { | ||
436 | rules.layout = input_config->xkb_layout; | ||
437 | } else { | ||
438 | rules.layout = getenv("XKB_DEFAULT_LAYOUT"); | ||
439 | } | ||
440 | if (input_config && input_config->xkb_model) { | ||
441 | rules.model = input_config->xkb_model; | ||
442 | } else { | ||
443 | rules.model = getenv("XKB_DEFAULT_MODEL"); | ||
444 | } | ||
445 | |||
446 | if (input_config && input_config->xkb_options) { | ||
447 | rules.options = input_config->xkb_options; | ||
448 | } else { | ||
449 | rules.options = getenv("XKB_DEFAULT_OPTIONS"); | ||
450 | } | ||
451 | |||
452 | if (input_config && input_config->xkb_rules) { | ||
453 | rules.rules = input_config->xkb_rules; | ||
454 | } else { | ||
455 | rules.rules = getenv("XKB_DEFAULT_RULES"); | ||
456 | } | ||
457 | |||
458 | if (input_config && input_config->xkb_variant) { | ||
459 | rules.variant = input_config->xkb_variant; | ||
460 | } else { | ||
461 | rules.variant = getenv("XKB_DEFAULT_VARIANT"); | ||
462 | } | ||
463 | |||
464 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | ||
465 | if (!sway_assert(context, "cannot create XKB context")) { | ||
466 | return; | ||
467 | } | ||
468 | |||
469 | struct xkb_keymap *keymap = | ||
470 | xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); | ||
471 | |||
472 | if (!keymap) { | ||
473 | wlr_log(L_DEBUG, "cannot configure keyboard: keymap does not exist"); | ||
474 | xkb_context_unref(context); | ||
475 | return; | ||
476 | } | ||
477 | |||
478 | xkb_keymap_unref(keyboard->keymap); | ||
479 | keyboard->keymap = keymap; | ||
480 | wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); | ||
481 | |||
482 | wlr_keyboard_set_repeat_info(wlr_device->keyboard, 25, 600); | ||
483 | xkb_context_unref(context); | ||
484 | struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; | ||
485 | wlr_seat_set_keyboard(seat, wlr_device); | ||
486 | |||
487 | wl_list_remove(&keyboard->keyboard_key.link); | ||
488 | wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); | ||
489 | keyboard->keyboard_key.notify = handle_keyboard_key; | ||
490 | |||
491 | wl_list_remove(&keyboard->keyboard_modifiers.link); | ||
492 | wl_signal_add( &wlr_device->keyboard->events.modifiers, | ||
493 | &keyboard->keyboard_modifiers); | ||
494 | keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; | ||
495 | } | ||
496 | |||
497 | void sway_keyboard_destroy(struct sway_keyboard *keyboard) { | ||
498 | if (!keyboard) { | ||
499 | return; | ||
500 | } | ||
501 | wl_list_remove(&keyboard->keyboard_key.link); | ||
502 | wl_list_remove(&keyboard->keyboard_modifiers.link); | ||
503 | free(keyboard); | ||
504 | } | ||
diff --git a/sway/input/seat.c b/sway/input/seat.c new file mode 100644 index 00000000..467e5087 --- /dev/null +++ b/sway/input/seat.c | |||
@@ -0,0 +1,675 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #define _POSIX_C_SOURCE 199309L | ||
3 | #include <assert.h> | ||
4 | #include <strings.h> | ||
5 | #include <time.h> | ||
6 | #include <wlr/types/wlr_cursor.h> | ||
7 | #include <wlr/types/wlr_output_layout.h> | ||
8 | #include <wlr/types/wlr_xcursor_manager.h> | ||
9 | #include "sway/debug.h" | ||
10 | #include "sway/tree/container.h" | ||
11 | #include "sway/tree/workspace.h" | ||
12 | #include "sway/input/seat.h" | ||
13 | #include "sway/input/cursor.h" | ||
14 | #include "sway/input/input-manager.h" | ||
15 | #include "sway/input/keyboard.h" | ||
16 | #include "sway/ipc-server.h" | ||
17 | #include "sway/layers.h" | ||
18 | #include "sway/output.h" | ||
19 | #include "sway/tree/container.h" | ||
20 | #include "sway/tree/view.h" | ||
21 | #include "log.h" | ||
22 | |||
23 | static void seat_device_destroy(struct sway_seat_device *seat_device) { | ||
24 | if (!seat_device) { | ||
25 | return; | ||
26 | } | ||
27 | |||
28 | sway_keyboard_destroy(seat_device->keyboard); | ||
29 | wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, | ||
30 | seat_device->input_device->wlr_device); | ||
31 | wl_list_remove(&seat_device->link); | ||
32 | free(seat_device); | ||
33 | } | ||
34 | |||
35 | void seat_destroy(struct sway_seat *seat) { | ||
36 | struct sway_seat_device *seat_device, *next; | ||
37 | wl_list_for_each_safe(seat_device, next, &seat->devices, link) { | ||
38 | seat_device_destroy(seat_device); | ||
39 | } | ||
40 | sway_cursor_destroy(seat->cursor); | ||
41 | wl_list_remove(&seat->link); | ||
42 | wlr_seat_destroy(seat->wlr_seat); | ||
43 | } | ||
44 | |||
45 | static struct sway_seat_container *seat_container_from_container( | ||
46 | struct sway_seat *seat, struct sway_container *con); | ||
47 | |||
48 | static void seat_container_destroy(struct sway_seat_container *seat_con) { | ||
49 | struct sway_container *con = seat_con->container; | ||
50 | struct sway_container *child = NULL; | ||
51 | |||
52 | if (con->children != NULL) { | ||
53 | for (int i = 0; i < con->children->length; ++i) { | ||
54 | child = con->children->items[i]; | ||
55 | struct sway_seat_container *seat_child = | ||
56 | seat_container_from_container(seat_con->seat, child); | ||
57 | seat_container_destroy(seat_child); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | wl_list_remove(&seat_con->destroy.link); | ||
62 | wl_list_remove(&seat_con->link); | ||
63 | free(seat_con); | ||
64 | } | ||
65 | |||
66 | static void seat_send_focus(struct sway_seat *seat, | ||
67 | struct sway_container *con) { | ||
68 | if (con->type != C_VIEW) { | ||
69 | return; | ||
70 | } | ||
71 | struct sway_view *view = con->sway_view; | ||
72 | if (view->type == SWAY_VIEW_XWAYLAND) { | ||
73 | struct wlr_xwayland *xwayland = | ||
74 | seat->input->server->xwayland; | ||
75 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | ||
76 | } | ||
77 | view_set_activated(view, true); | ||
78 | struct wlr_keyboard *keyboard = | ||
79 | wlr_seat_get_keyboard(seat->wlr_seat); | ||
80 | if (keyboard) { | ||
81 | wlr_seat_keyboard_notify_enter(seat->wlr_seat, | ||
82 | view->surface, keyboard->keycodes, | ||
83 | keyboard->num_keycodes, &keyboard->modifiers); | ||
84 | } else { | ||
85 | wlr_seat_keyboard_notify_enter( | ||
86 | seat->wlr_seat, view->surface, NULL, 0, NULL); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat, | ||
91 | struct sway_container *container, enum sway_container_type type) { | ||
92 | if (container->type == C_VIEW || container->children->length == 0) { | ||
93 | return container; | ||
94 | } | ||
95 | |||
96 | struct sway_seat_container *current = NULL; | ||
97 | wl_list_for_each(current, &seat->focus_stack, link) { | ||
98 | if (current->container->type != type && type != C_TYPES) { | ||
99 | continue; | ||
100 | } | ||
101 | |||
102 | if (container_has_child(container, current->container)) { | ||
103 | return current->container; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | void seat_focus_inactive_children_for_each(struct sway_seat *seat, | ||
111 | struct sway_container *container, | ||
112 | void (*f)(struct sway_container *container, void *data), void *data) { | ||
113 | struct sway_seat_container *current = NULL; | ||
114 | wl_list_for_each(current, &seat->focus_stack, link) { | ||
115 | if (current->container->parent == NULL) { | ||
116 | continue; | ||
117 | } | ||
118 | if (current->container->parent == container) { | ||
119 | f(current->container, data); | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | |||
124 | struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, | ||
125 | struct sway_container *container) { | ||
126 | return seat_get_focus_by_type(seat, container, C_VIEW); | ||
127 | } | ||
128 | |||
129 | static void handle_seat_container_destroy(struct wl_listener *listener, | ||
130 | void *data) { | ||
131 | struct sway_seat_container *seat_con = | ||
132 | wl_container_of(listener, seat_con, destroy); | ||
133 | struct sway_seat *seat = seat_con->seat; | ||
134 | struct sway_container *con = seat_con->container; | ||
135 | struct sway_container *parent = con->parent; | ||
136 | struct sway_container *focus = seat_get_focus(seat); | ||
137 | |||
138 | bool set_focus = | ||
139 | focus != NULL && | ||
140 | (focus == con || container_has_child(con, focus)) && | ||
141 | con->type != C_WORKSPACE; | ||
142 | |||
143 | seat_container_destroy(seat_con); | ||
144 | |||
145 | if (set_focus) { | ||
146 | struct sway_container *next_focus = NULL; | ||
147 | while (next_focus == NULL) { | ||
148 | next_focus = seat_get_focus_by_type(seat, parent, C_VIEW); | ||
149 | |||
150 | if (next_focus == NULL && parent->type == C_WORKSPACE) { | ||
151 | next_focus = parent; | ||
152 | break; | ||
153 | } | ||
154 | |||
155 | parent = parent->parent; | ||
156 | } | ||
157 | |||
158 | // the structure change might have caused it to move up to the top of | ||
159 | // the focus stack without sending focus notifications to the view | ||
160 | if (seat_get_focus(seat) == next_focus) { | ||
161 | seat_send_focus(seat, next_focus); | ||
162 | } else { | ||
163 | seat_set_focus(seat, next_focus); | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | |||
168 | static struct sway_seat_container *seat_container_from_container( | ||
169 | struct sway_seat *seat, struct sway_container *con) { | ||
170 | if (con->type == C_ROOT || con->type == C_OUTPUT) { | ||
171 | // these don't get seat containers ever | ||
172 | return NULL; | ||
173 | } | ||
174 | |||
175 | struct sway_seat_container *seat_con = NULL; | ||
176 | wl_list_for_each(seat_con, &seat->focus_stack, link) { | ||
177 | if (seat_con->container == con) { | ||
178 | return seat_con; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | seat_con = calloc(1, sizeof(struct sway_seat_container)); | ||
183 | if (seat_con == NULL) { | ||
184 | wlr_log(L_ERROR, "could not allocate seat container"); | ||
185 | return NULL; | ||
186 | } | ||
187 | |||
188 | seat_con->container = con; | ||
189 | seat_con->seat = seat; | ||
190 | wl_list_insert(seat->focus_stack.prev, &seat_con->link); | ||
191 | wl_signal_add(&con->events.destroy, &seat_con->destroy); | ||
192 | seat_con->destroy.notify = handle_seat_container_destroy; | ||
193 | |||
194 | return seat_con; | ||
195 | } | ||
196 | |||
197 | static void handle_new_container(struct wl_listener *listener, void *data) { | ||
198 | struct sway_seat *seat = wl_container_of(listener, seat, new_container); | ||
199 | struct sway_container *con = data; | ||
200 | seat_container_from_container(seat, con); | ||
201 | } | ||
202 | |||
203 | static void collect_focus_iter(struct sway_container *con, void *data) { | ||
204 | struct sway_seat *seat = data; | ||
205 | if (con->type > C_WORKSPACE) { | ||
206 | return; | ||
207 | } | ||
208 | struct sway_seat_container *seat_con = | ||
209 | seat_container_from_container(seat, con); | ||
210 | if (!seat_con) { | ||
211 | return; | ||
212 | } | ||
213 | wl_list_remove(&seat_con->link); | ||
214 | wl_list_insert(&seat->focus_stack, &seat_con->link); | ||
215 | } | ||
216 | |||
217 | struct sway_seat *seat_create(struct sway_input_manager *input, | ||
218 | const char *seat_name) { | ||
219 | struct sway_seat *seat = calloc(1, sizeof(struct sway_seat)); | ||
220 | if (!seat) { | ||
221 | return NULL; | ||
222 | } | ||
223 | |||
224 | seat->wlr_seat = wlr_seat_create(input->server->wl_display, seat_name); | ||
225 | if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { | ||
226 | free(seat); | ||
227 | return NULL; | ||
228 | } | ||
229 | |||
230 | seat->cursor = sway_cursor_create(seat); | ||
231 | if (!seat->cursor) { | ||
232 | wlr_seat_destroy(seat->wlr_seat); | ||
233 | free(seat); | ||
234 | return NULL; | ||
235 | } | ||
236 | |||
237 | // init the focus stack | ||
238 | wl_list_init(&seat->focus_stack); | ||
239 | |||
240 | container_for_each_descendant_dfs(&root_container, | ||
241 | collect_focus_iter, seat); | ||
242 | |||
243 | wl_signal_add(&root_container.sway_root->events.new_container, | ||
244 | &seat->new_container); | ||
245 | seat->new_container.notify = handle_new_container; | ||
246 | |||
247 | seat->input = input; | ||
248 | wl_list_init(&seat->devices); | ||
249 | |||
250 | wlr_seat_set_capabilities(seat->wlr_seat, | ||
251 | WL_SEAT_CAPABILITY_KEYBOARD | | ||
252 | WL_SEAT_CAPABILITY_POINTER | | ||
253 | WL_SEAT_CAPABILITY_TOUCH); | ||
254 | |||
255 | seat_configure_xcursor(seat); | ||
256 | |||
257 | wl_list_insert(&input->seats, &seat->link); | ||
258 | |||
259 | return seat; | ||
260 | } | ||
261 | |||
262 | static void seat_apply_input_config(struct sway_seat *seat, | ||
263 | struct sway_seat_device *sway_device) { | ||
264 | struct input_config *ic = input_device_get_config( | ||
265 | sway_device->input_device); | ||
266 | if (!ic) { | ||
267 | return; | ||
268 | } | ||
269 | wlr_log(L_DEBUG, "Applying input config to %s", | ||
270 | sway_device->input_device->identifier); | ||
271 | if (ic->mapped_output) { | ||
272 | struct sway_container *output = NULL; | ||
273 | for (int i = 0; i < root_container.children->length; ++i) { | ||
274 | struct sway_container *_output = root_container.children->items[i]; | ||
275 | if (strcasecmp(_output->name, ic->mapped_output) == 0) { | ||
276 | output = _output; | ||
277 | break; | ||
278 | } | ||
279 | } | ||
280 | if (output) { | ||
281 | wlr_cursor_map_input_to_output(seat->cursor->cursor, | ||
282 | sway_device->input_device->wlr_device, | ||
283 | output->sway_output->wlr_output); | ||
284 | wlr_log(L_DEBUG, "Mapped to output %s", output->name); | ||
285 | } | ||
286 | } | ||
287 | } | ||
288 | |||
289 | static void seat_configure_pointer(struct sway_seat *seat, | ||
290 | struct sway_seat_device *sway_device) { | ||
291 | wlr_cursor_attach_input_device(seat->cursor->cursor, | ||
292 | sway_device->input_device->wlr_device); | ||
293 | seat_apply_input_config(seat, sway_device); | ||
294 | } | ||
295 | |||
296 | static void seat_configure_keyboard(struct sway_seat *seat, | ||
297 | struct sway_seat_device *seat_device) { | ||
298 | if (!seat_device->keyboard) { | ||
299 | sway_keyboard_create(seat, seat_device); | ||
300 | } | ||
301 | struct wlr_keyboard *wlr_keyboard = | ||
302 | seat_device->input_device->wlr_device->keyboard; | ||
303 | sway_keyboard_configure(seat_device->keyboard); | ||
304 | wlr_seat_set_keyboard(seat->wlr_seat, | ||
305 | seat_device->input_device->wlr_device); | ||
306 | struct sway_container *focus = seat_get_focus(seat); | ||
307 | if (focus && focus->type == C_VIEW) { | ||
308 | // force notify reenter to pick up the new configuration | ||
309 | wlr_seat_keyboard_clear_focus(seat->wlr_seat); | ||
310 | wlr_seat_keyboard_notify_enter(seat->wlr_seat, | ||
311 | focus->sway_view->surface, wlr_keyboard->keycodes, | ||
312 | wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers); | ||
313 | } | ||
314 | } | ||
315 | |||
316 | static void seat_configure_tablet_tool(struct sway_seat *seat, | ||
317 | struct sway_seat_device *sway_device) { | ||
318 | wlr_cursor_attach_input_device(seat->cursor->cursor, | ||
319 | sway_device->input_device->wlr_device); | ||
320 | seat_apply_input_config(seat, sway_device); | ||
321 | } | ||
322 | |||
323 | static struct sway_seat_device *seat_get_device(struct sway_seat *seat, | ||
324 | struct sway_input_device *input_device) { | ||
325 | struct sway_seat_device *seat_device = NULL; | ||
326 | wl_list_for_each(seat_device, &seat->devices, link) { | ||
327 | if (seat_device->input_device == input_device) { | ||
328 | return seat_device; | ||
329 | } | ||
330 | } | ||
331 | |||
332 | return NULL; | ||
333 | } | ||
334 | |||
335 | void seat_configure_device(struct sway_seat *seat, | ||
336 | struct sway_input_device *input_device) { | ||
337 | struct sway_seat_device *seat_device = | ||
338 | seat_get_device(seat, input_device); | ||
339 | if (!seat_device) { | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | switch (input_device->wlr_device->type) { | ||
344 | case WLR_INPUT_DEVICE_POINTER: | ||
345 | seat_configure_pointer(seat, seat_device); | ||
346 | break; | ||
347 | case WLR_INPUT_DEVICE_KEYBOARD: | ||
348 | seat_configure_keyboard(seat, seat_device); | ||
349 | break; | ||
350 | case WLR_INPUT_DEVICE_TABLET_TOOL: | ||
351 | seat_configure_tablet_tool(seat, seat_device); | ||
352 | break; | ||
353 | case WLR_INPUT_DEVICE_TABLET_PAD: | ||
354 | case WLR_INPUT_DEVICE_TOUCH: | ||
355 | wlr_log(L_DEBUG, "TODO: configure other devices"); | ||
356 | break; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | void seat_add_device(struct sway_seat *seat, | ||
361 | struct sway_input_device *input_device) { | ||
362 | if (seat_get_device(seat, input_device)) { | ||
363 | seat_configure_device(seat, input_device); | ||
364 | return; | ||
365 | } | ||
366 | |||
367 | struct sway_seat_device *seat_device = | ||
368 | calloc(1, sizeof(struct sway_seat_device)); | ||
369 | if (!seat_device) { | ||
370 | wlr_log(L_DEBUG, "could not allocate seat device"); | ||
371 | return; | ||
372 | } | ||
373 | |||
374 | wlr_log(L_DEBUG, "adding device %s to seat %s", | ||
375 | input_device->identifier, seat->wlr_seat->name); | ||
376 | |||
377 | seat_device->sway_seat = seat; | ||
378 | seat_device->input_device = input_device; | ||
379 | wl_list_insert(&seat->devices, &seat_device->link); | ||
380 | |||
381 | seat_configure_device(seat, input_device); | ||
382 | } | ||
383 | |||
384 | void seat_remove_device(struct sway_seat *seat, | ||
385 | struct sway_input_device *input_device) { | ||
386 | struct sway_seat_device *seat_device = | ||
387 | seat_get_device(seat, input_device); | ||
388 | |||
389 | if (!seat_device) { | ||
390 | return; | ||
391 | } | ||
392 | |||
393 | wlr_log(L_DEBUG, "removing device %s from seat %s", | ||
394 | input_device->identifier, seat->wlr_seat->name); | ||
395 | |||
396 | seat_device_destroy(seat_device); | ||
397 | } | ||
398 | |||
399 | void seat_configure_xcursor(struct sway_seat *seat) { | ||
400 | // TODO configure theme and size | ||
401 | const char *cursor_theme = NULL; | ||
402 | |||
403 | if (!seat->cursor->xcursor_manager) { | ||
404 | seat->cursor->xcursor_manager = | ||
405 | wlr_xcursor_manager_create(cursor_theme, 24); | ||
406 | if (sway_assert(seat->cursor->xcursor_manager, | ||
407 | "Cannot create XCursor manager for theme %s", | ||
408 | cursor_theme)) { | ||
409 | return; | ||
410 | } | ||
411 | } | ||
412 | |||
413 | for (int i = 0; i < root_container.children->length; ++i) { | ||
414 | struct sway_container *output_container = | ||
415 | root_container.children->items[i]; | ||
416 | struct wlr_output *output = | ||
417 | output_container->sway_output->wlr_output; | ||
418 | bool result = | ||
419 | wlr_xcursor_manager_load(seat->cursor->xcursor_manager, | ||
420 | output->scale); | ||
421 | |||
422 | sway_assert(!result, | ||
423 | "Cannot load xcursor theme for output '%s' with scale %f", | ||
424 | // TODO: Fractional scaling | ||
425 | output->name, (double)output->scale); | ||
426 | } | ||
427 | |||
428 | wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager, | ||
429 | "left_ptr", seat->cursor->cursor); | ||
430 | wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, | ||
431 | seat->cursor->cursor->y); | ||
432 | } | ||
433 | |||
434 | bool seat_is_input_allowed(struct sway_seat *seat, | ||
435 | struct wlr_surface *surface) { | ||
436 | struct wl_client *client = wl_resource_get_client(surface->resource); | ||
437 | return !seat->exclusive_client || seat->exclusive_client == client; | ||
438 | } | ||
439 | |||
440 | void seat_set_focus_warp(struct sway_seat *seat, | ||
441 | struct sway_container *container, bool warp) { | ||
442 | if (seat->focused_layer) { | ||
443 | return; | ||
444 | } | ||
445 | |||
446 | struct sway_container *last_focus = seat_get_focus(seat); | ||
447 | if (container && last_focus == container) { | ||
448 | return; | ||
449 | } | ||
450 | |||
451 | if (container) { | ||
452 | struct sway_seat_container *seat_con = | ||
453 | seat_container_from_container(seat, container); | ||
454 | if (seat_con == NULL) { | ||
455 | return; | ||
456 | } | ||
457 | |||
458 | // put all the anscestors of this container on top of the focus stack | ||
459 | struct sway_seat_container *parent = | ||
460 | seat_container_from_container(seat, | ||
461 | seat_con->container->parent); | ||
462 | while (parent) { | ||
463 | wl_list_remove(&parent->link); | ||
464 | wl_list_insert(&seat->focus_stack, &parent->link); | ||
465 | |||
466 | parent = | ||
467 | seat_container_from_container(seat, | ||
468 | parent->container->parent); | ||
469 | } | ||
470 | |||
471 | wl_list_remove(&seat_con->link); | ||
472 | wl_list_insert(&seat->focus_stack, &seat_con->link); | ||
473 | |||
474 | if (container->type == C_VIEW && !seat_is_input_allowed( | ||
475 | seat, container->sway_view->surface)) { | ||
476 | wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited"); | ||
477 | return; | ||
478 | } | ||
479 | |||
480 | if (container->type == C_VIEW) { | ||
481 | seat_send_focus(seat, container); | ||
482 | } | ||
483 | } | ||
484 | |||
485 | if (last_focus) { | ||
486 | struct sway_container *last_ws = last_focus; | ||
487 | if (last_ws && last_ws->type != C_WORKSPACE) { | ||
488 | last_ws = container_parent(last_ws, C_WORKSPACE); | ||
489 | } | ||
490 | if (last_ws) { | ||
491 | ipc_event_workspace(last_ws, container, "focus"); | ||
492 | if (!workspace_is_visible(last_ws) | ||
493 | && last_ws->children->length == 0) { | ||
494 | container_destroy(last_ws); | ||
495 | } | ||
496 | } | ||
497 | |||
498 | if (config->mouse_warping && warp) { | ||
499 | struct sway_container *last_output = last_focus; | ||
500 | if (last_output && last_output->type != C_OUTPUT) { | ||
501 | last_output = container_parent(last_output, C_OUTPUT); | ||
502 | } | ||
503 | struct sway_container *new_output = container; | ||
504 | if (new_output && new_output->type != C_OUTPUT) { | ||
505 | new_output = container_parent(new_output, C_OUTPUT); | ||
506 | } | ||
507 | if (new_output && last_output && new_output != last_output) { | ||
508 | double x = new_output->x + container->x + | ||
509 | container->width / 2.0; | ||
510 | double y = new_output->y + container->y + | ||
511 | container->height / 2.0; | ||
512 | struct wlr_output *wlr_output = | ||
513 | new_output->sway_output->wlr_output; | ||
514 | if (!wlr_output_layout_contains_point( | ||
515 | root_container.sway_root->output_layout, | ||
516 | wlr_output, seat->cursor->cursor->x, | ||
517 | seat->cursor->cursor->y)) { | ||
518 | wlr_cursor_warp(seat->cursor->cursor, NULL, x, y); | ||
519 | struct timespec now; | ||
520 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
521 | cursor_send_pointer_motion(seat->cursor, now.tv_nsec / 1000); | ||
522 | } | ||
523 | } | ||
524 | } | ||
525 | } | ||
526 | |||
527 | if (last_focus && last_focus->type == C_VIEW && | ||
528 | !input_manager_has_focus(seat->input, last_focus)) { | ||
529 | struct sway_view *view = last_focus->sway_view; | ||
530 | view_set_activated(view, false); | ||
531 | } | ||
532 | |||
533 | seat->has_focus = (container != NULL); | ||
534 | |||
535 | update_debug_tree(); | ||
536 | } | ||
537 | |||
538 | void seat_set_focus(struct sway_seat *seat, | ||
539 | struct sway_container *container) { | ||
540 | seat_set_focus_warp(seat, container, true); | ||
541 | } | ||
542 | |||
543 | void seat_set_focus_surface(struct sway_seat *seat, | ||
544 | struct wlr_surface *surface) { | ||
545 | if (seat->focused_layer != NULL) { | ||
546 | return; | ||
547 | } | ||
548 | if (seat->has_focus) { | ||
549 | struct sway_container *focus = seat_get_focus(seat); | ||
550 | if (focus->type == C_VIEW) { | ||
551 | wlr_seat_keyboard_clear_focus(seat->wlr_seat); | ||
552 | view_set_activated(focus->sway_view, false); | ||
553 | } | ||
554 | seat->has_focus = false; | ||
555 | } | ||
556 | struct wlr_keyboard *keyboard = | ||
557 | wlr_seat_get_keyboard(seat->wlr_seat); | ||
558 | if (keyboard) { | ||
559 | wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, | ||
560 | keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); | ||
561 | } else { | ||
562 | wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, NULL, 0, NULL); | ||
563 | } | ||
564 | } | ||
565 | |||
566 | void seat_set_focus_layer(struct sway_seat *seat, | ||
567 | struct wlr_layer_surface *layer) { | ||
568 | if (!layer && seat->focused_layer) { | ||
569 | seat->focused_layer = NULL; | ||
570 | struct sway_container *previous = seat_get_focus(seat); | ||
571 | if (previous) { | ||
572 | wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, | ||
573 | container_type_to_str(previous->type), previous->name); | ||
574 | // Hack to get seat to re-focus the return value of get_focus | ||
575 | seat_set_focus(seat, previous->parent); | ||
576 | seat_set_focus(seat, previous); | ||
577 | } | ||
578 | return; | ||
579 | } else if (!layer || seat->focused_layer == layer) { | ||
580 | return; | ||
581 | } | ||
582 | seat_set_focus_surface(seat, layer->surface); | ||
583 | if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { | ||
584 | seat->focused_layer = layer; | ||
585 | } | ||
586 | } | ||
587 | |||
588 | void seat_set_exclusive_client(struct sway_seat *seat, | ||
589 | struct wl_client *client) { | ||
590 | if (!client) { | ||
591 | seat->exclusive_client = client; | ||
592 | // Triggers a refocus of the topmost surface layer if necessary | ||
593 | // TODO: Make layer surface focus per-output based on cursor position | ||
594 | for (int i = 0; i < root_container.children->length; ++i) { | ||
595 | struct sway_container *output = root_container.children->items[i]; | ||
596 | if (!sway_assert(output->type == C_OUTPUT, | ||
597 | "root container has non-output child")) { | ||
598 | continue; | ||
599 | } | ||
600 | arrange_layers(output->sway_output); | ||
601 | } | ||
602 | return; | ||
603 | } | ||
604 | if (seat->focused_layer) { | ||
605 | if (wl_resource_get_client(seat->focused_layer->resource) != client) { | ||
606 | seat_set_focus_layer(seat, NULL); | ||
607 | } | ||
608 | } | ||
609 | if (seat->has_focus) { | ||
610 | struct sway_container *focus = seat_get_focus(seat); | ||
611 | if (focus->type == C_VIEW && wl_resource_get_client( | ||
612 | focus->sway_view->surface->resource) != client) { | ||
613 | seat_set_focus(seat, NULL); | ||
614 | } | ||
615 | } | ||
616 | if (seat->wlr_seat->pointer_state.focused_client) { | ||
617 | if (seat->wlr_seat->pointer_state.focused_client->client != client) { | ||
618 | wlr_seat_pointer_clear_focus(seat->wlr_seat); | ||
619 | } | ||
620 | } | ||
621 | struct timespec now; | ||
622 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
623 | struct wlr_touch_point *point; | ||
624 | wl_list_for_each(point, &seat->wlr_seat->touch_state.touch_points, link) { | ||
625 | if (point->client->client != client) { | ||
626 | wlr_seat_touch_point_clear_focus(seat->wlr_seat, | ||
627 | now.tv_nsec / 1000, point->touch_id); | ||
628 | } | ||
629 | } | ||
630 | seat->exclusive_client = client; | ||
631 | } | ||
632 | |||
633 | struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, | ||
634 | struct sway_container *container) { | ||
635 | return seat_get_focus_by_type(seat, container, C_TYPES); | ||
636 | } | ||
637 | |||
638 | struct sway_container *sway_seat_get_focus(struct sway_seat *seat) { | ||
639 | if (!seat->has_focus) { | ||
640 | return NULL; | ||
641 | } | ||
642 | return seat_get_focus_inactive(seat, &root_container); | ||
643 | } | ||
644 | |||
645 | struct sway_container *seat_get_focus(struct sway_seat *seat) { | ||
646 | if (!seat->has_focus) { | ||
647 | return NULL; | ||
648 | } | ||
649 | return seat_get_focus_inactive(seat, &root_container); | ||
650 | } | ||
651 | |||
652 | void seat_apply_config(struct sway_seat *seat, | ||
653 | struct seat_config *seat_config) { | ||
654 | struct sway_seat_device *seat_device = NULL; | ||
655 | |||
656 | if (!seat_config) { | ||
657 | return; | ||
658 | } | ||
659 | |||
660 | wl_list_for_each(seat_device, &seat->devices, link) { | ||
661 | seat_configure_device(seat, seat_device->input_device); | ||
662 | } | ||
663 | } | ||
664 | |||
665 | struct seat_config *seat_get_config(struct sway_seat *seat) { | ||
666 | struct seat_config *seat_config = NULL; | ||
667 | for (int i = 0; i < config->seat_configs->length; ++i ) { | ||
668 | seat_config = config->seat_configs->items[i]; | ||
669 | if (strcmp(seat->wlr_seat->name, seat_config->name) == 0) { | ||
670 | return seat_config; | ||
671 | } | ||
672 | } | ||
673 | |||
674 | return NULL; | ||
675 | } | ||
diff --git a/sway/input_state.c b/sway/input_state.c deleted file mode 100644 index 04aafd37..00000000 --- a/sway/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/ipc-json.c b/sway/ipc-json.c index 6ab63c75..6158fc29 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c | |||
@@ -1,247 +1,213 @@ | |||
1 | #include <json-c/json.h> | 1 | #include <json-c/json.h> |
2 | #include <stdio.h> | ||
2 | #include <ctype.h> | 3 | #include <ctype.h> |
3 | #include <string.h> | 4 | #include "log.h" |
4 | #include <stdint.h> | ||
5 | #include <libinput.h> | ||
6 | #include "sway/container.h" | ||
7 | #include "sway/input.h" | ||
8 | #include "sway/ipc-json.h" | 5 | #include "sway/ipc-json.h" |
9 | #include "util.h" | 6 | #include "sway/tree/container.h" |
10 | 7 | #include "sway/output.h" | |
11 | static json_object *ipc_json_create_rect(swayc_t *c) { | 8 | #include "sway/input/input-manager.h" |
12 | json_object *rect = json_object_new_object(); | 9 | #include "sway/input/seat.h" |
10 | #include <wlr/types/wlr_box.h> | ||
11 | #include <wlr/types/wlr_output.h> | ||
12 | #include "wlr-layer-shell-unstable-v1-protocol.h" | ||
13 | |||
14 | static const char *ipc_json_layout_description(enum sway_container_layout l) { | ||
15 | switch (l) { | ||
16 | case L_VERT: | ||
17 | return "splitv"; | ||
18 | case L_HORIZ: | ||
19 | return "splith"; | ||
20 | case L_TABBED: | ||
21 | return "tabbed"; | ||
22 | case L_STACKED: | ||
23 | return "stacked"; | ||
24 | case L_FLOATING: | ||
25 | return "floating"; | ||
26 | case L_NONE: | ||
27 | break; | ||
28 | } | ||
29 | return "none"; | ||
30 | } | ||
13 | 31 | ||
14 | json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x)); | 32 | json_object *ipc_json_get_version() { |
15 | json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y)); | 33 | int major = 0, minor = 0, patch = 0; |
34 | json_object *version = json_object_new_object(); | ||
16 | 35 | ||
17 | struct wlc_size size; | 36 | sscanf(SWAY_VERSION, "%u.%u.%u", &major, &minor, &patch); |
18 | if (c->type == C_OUTPUT) { | ||
19 | size = *wlc_output_get_resolution(c->handle); | ||
20 | } else { | ||
21 | size.w = c->width; | ||
22 | size.h = c->height; | ||
23 | } | ||
24 | 37 | ||
25 | json_object_object_add(rect, "width", json_object_new_int((int32_t)size.w)); | 38 | json_object_object_add(version, "human_readable", json_object_new_string(SWAY_VERSION)); |
26 | json_object_object_add(rect, "height", json_object_new_int((int32_t)size.h)); | 39 | json_object_object_add(version, "variant", json_object_new_string("sway")); |
40 | json_object_object_add(version, "major", json_object_new_int(major)); | ||
41 | json_object_object_add(version, "minor", json_object_new_int(minor)); | ||
42 | json_object_object_add(version, "patch", json_object_new_int(patch)); | ||
27 | 43 | ||
28 | return rect; | 44 | return version; |
29 | } | 45 | } |
30 | 46 | ||
31 | static json_object *ipc_json_create_rect_from_geometry(struct wlc_geometry g) { | 47 | static json_object *ipc_json_create_rect(struct sway_container *c) { |
32 | json_object *rect = json_object_new_object(); | 48 | json_object *rect = json_object_new_object(); |
33 | 49 | ||
34 | json_object_object_add(rect, "x", json_object_new_int(g.origin.x)); | 50 | json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x)); |
35 | json_object_object_add(rect, "y", json_object_new_int(g.origin.y)); | 51 | json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y)); |
36 | json_object_object_add(rect, "width", json_object_new_int(g.size.w)); | 52 | json_object_object_add(rect, "width", json_object_new_int((int32_t)c->width)); |
37 | json_object_object_add(rect, "height", json_object_new_int(g.size.h)); | 53 | json_object_object_add(rect, "height", json_object_new_int((int32_t)c->height)); |
38 | 54 | ||
39 | return rect; | 55 | return rect; |
40 | } | 56 | } |
41 | 57 | ||
42 | static const char *ipc_json_border_description(swayc_t *c) { | 58 | static void ipc_json_describe_root(struct sway_container *root, json_object *object) { |
43 | const char *border; | 59 | json_object_object_add(object, "type", json_object_new_string("root")); |
44 | 60 | json_object_object_add(object, "layout", json_object_new_string("splith")); | |
45 | switch (c->border_type) { | ||
46 | case B_PIXEL: | ||
47 | border = "1pixel"; | ||
48 | break; | ||
49 | |||
50 | case B_NORMAL: | ||
51 | border = "normal"; | ||
52 | break; | ||
53 | |||
54 | case B_NONE: // fallthrough | ||
55 | default: | ||
56 | border = "none"; | ||
57 | break; | ||
58 | } | ||
59 | |||
60 | return border; | ||
61 | } | 61 | } |
62 | 62 | ||
63 | static const char *ipc_json_layout_description(enum swayc_layouts l) { | 63 | static const char *ipc_json_get_output_transform(enum wl_output_transform transform) { |
64 | const char *layout; | 64 | switch (transform) { |
65 | 65 | case WL_OUTPUT_TRANSFORM_NORMAL: | |
66 | switch (l) { | 66 | return "normal"; |
67 | case L_VERT: | 67 | case WL_OUTPUT_TRANSFORM_90: |
68 | layout = "splitv"; | 68 | return "90"; |
69 | break; | 69 | case WL_OUTPUT_TRANSFORM_180: |
70 | 70 | return "180"; | |
71 | case L_HORIZ: | 71 | case WL_OUTPUT_TRANSFORM_270: |
72 | layout = "splith"; | 72 | return "270"; |
73 | break; | 73 | case WL_OUTPUT_TRANSFORM_FLIPPED: |
74 | 74 | return "flipped"; | |
75 | case L_TABBED: | 75 | case WL_OUTPUT_TRANSFORM_FLIPPED_90: |
76 | layout = "tabbed"; | 76 | return "flipped-90"; |
77 | break; | 77 | case WL_OUTPUT_TRANSFORM_FLIPPED_180: |
78 | 78 | return "flipped-180"; | |
79 | case L_STACKED: | 79 | case WL_OUTPUT_TRANSFORM_FLIPPED_270: |
80 | layout = "stacked"; | 80 | return "flipped-270"; |
81 | break; | ||
82 | |||
83 | case L_FLOATING: | ||
84 | layout = "floating"; | ||
85 | break; | ||
86 | |||
87 | case L_NONE: // fallthrough | ||
88 | case L_LAYOUTS: // fallthrough; this should never happen, I'm just trying to silence compiler warnings | ||
89 | default: | ||
90 | layout = "null"; | ||
91 | break; | ||
92 | } | 81 | } |
93 | 82 | return NULL; | |
94 | return layout; | ||
95 | } | 83 | } |
96 | 84 | ||
97 | static float ipc_json_child_percentage(swayc_t *c) { | 85 | static void ipc_json_describe_output(struct sway_container *container, json_object *object) { |
98 | float percent = 0; | 86 | struct wlr_output *wlr_output = container->sway_output->wlr_output; |
99 | swayc_t *parent = c->parent; | 87 | json_object_object_add(object, "type", |
100 | 88 | json_object_new_string("output")); | |
101 | if (parent) { | 89 | json_object_object_add(object, "active", |
102 | switch (parent->layout) { | 90 | json_object_new_boolean(true)); |
103 | case L_VERT: | 91 | json_object_object_add(object, "primary", |
104 | percent = c->height / parent->height; | 92 | json_object_new_boolean(false)); |
105 | break; | 93 | json_object_object_add(object, "layout", |
106 | 94 | json_object_new_string("output")); | |
107 | case L_HORIZ: | 95 | json_object_object_add(object, "make", |
108 | percent = c->width / parent->width; | 96 | json_object_new_string(wlr_output->make)); |
109 | break; | 97 | json_object_object_add(object, "model", |
110 | 98 | json_object_new_string(wlr_output->model)); | |
111 | case L_STACKED: // fallthrough | 99 | json_object_object_add(object, "serial", |
112 | case L_TABBED: // fallthrough | 100 | json_object_new_string(wlr_output->serial)); |
113 | percent = 1.0; | 101 | json_object_object_add(object, "scale", |
114 | break; | 102 | json_object_new_double(wlr_output->scale)); |
115 | 103 | json_object_object_add(object, "refresh", | |
116 | default: | 104 | json_object_new_int(wlr_output->refresh)); |
117 | break; | 105 | json_object_object_add(object, "transform", |
106 | json_object_new_string( | ||
107 | ipc_json_get_output_transform(wlr_output->transform))); | ||
108 | |||
109 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | ||
110 | const char *ws = NULL; | ||
111 | if (seat) { | ||
112 | struct sway_container *focus = | ||
113 | seat_get_focus_inactive(seat, container); | ||
114 | if (focus && focus->type != C_WORKSPACE) { | ||
115 | focus = container_parent(focus, C_WORKSPACE); | ||
116 | } | ||
117 | if (focus) { | ||
118 | ws = focus->name; | ||
118 | } | 119 | } |
119 | } | 120 | } |
121 | json_object_object_add(object, "current_workspace", | ||
122 | json_object_new_string(ws)); | ||
123 | |||
124 | json_object *modes_array = json_object_new_array(); | ||
125 | struct wlr_output_mode *mode; | ||
126 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
127 | json_object *mode_object = json_object_new_object(); | ||
128 | json_object_object_add(mode_object, "width", | ||
129 | json_object_new_int(mode->width)); | ||
130 | json_object_object_add(mode_object, "height", | ||
131 | json_object_new_int(mode->height)); | ||
132 | json_object_object_add(mode_object, "refresh", | ||
133 | json_object_new_int(mode->refresh)); | ||
134 | json_object_array_add(modes_array, mode_object); | ||
135 | } | ||
120 | 136 | ||
121 | return percent; | 137 | json_object_object_add(object, "modes", modes_array); |
122 | } | ||
123 | |||
124 | static void ipc_json_describe_output(swayc_t *output, json_object *object) { | ||
125 | uint32_t scale = wlc_output_get_scale(output->handle); | ||
126 | json_object_object_add(object, "active", json_object_new_boolean(true)); | ||
127 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | ||
128 | json_object_object_add(object, "layout", json_object_new_string("output")); | 138 | json_object_object_add(object, "layout", json_object_new_string("output")); |
129 | json_object_object_add(object, "type", json_object_new_string("output")); | ||
130 | json_object_object_add(object, "current_workspace", | ||
131 | (output->focused) ? json_object_new_string(output->focused->name) : NULL); | ||
132 | json_object_object_add(object, "scale", json_object_new_int(scale)); | ||
133 | } | 139 | } |
134 | 140 | ||
135 | static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object) { | 141 | static void ipc_json_describe_workspace(struct sway_container *workspace, |
136 | int num = (isdigit(workspace->name[0])) ? atoi(workspace->name) : -1; | 142 | json_object *object) { |
137 | const char *layout = ipc_json_layout_description(workspace->workspace_layout); | 143 | int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1; |
138 | 144 | ||
139 | json_object_object_add(object, "num", json_object_new_int(num)); | 145 | json_object_object_add(object, "num", json_object_new_int(num)); |
140 | json_object_object_add(object, "output", (workspace->parent) ? json_object_new_string(workspace->parent->name) : NULL); | 146 | json_object_object_add(object, "output", workspace->parent ? |
147 | json_object_new_string(workspace->parent->name) : NULL); | ||
141 | json_object_object_add(object, "type", json_object_new_string("workspace")); | 148 | json_object_object_add(object, "type", json_object_new_string("workspace")); |
142 | json_object_object_add(object, "layout", (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout)); | 149 | json_object_object_add(object, "urgent", json_object_new_boolean(false)); |
143 | } | ||
144 | 150 | ||
145 | // window is in the scratchpad ? changed : none | 151 | const char *layout = ipc_json_layout_description(workspace->workspace_layout); |
146 | static const char *ipc_json_get_scratchpad_state(swayc_t *c) { | 152 | json_object_object_add(object, "layout", json_object_new_string(layout)); |
147 | int i; | ||
148 | for (i = 0; i < scratchpad->length; i++) { | ||
149 | if (scratchpad->items[i] == c) { | ||
150 | return "changed"; | ||
151 | } | ||
152 | } | ||
153 | return "none"; // we ignore the fresh value | ||
154 | } | 153 | } |
155 | 154 | ||
156 | static void ipc_json_describe_view(swayc_t *c, json_object *object) { | 155 | static void ipc_json_describe_view(struct sway_container *c, json_object *object) { |
157 | json_object *props = json_object_new_object(); | 156 | json_object_object_add(object, "name", |
158 | json_object_object_add(object, "type", json_object_new_string((c->is_floating) ? "floating_con" : "con")); | 157 | c->name ? json_object_new_string(c->name) : NULL); |
159 | 158 | json_object_object_add(object, "type", json_object_new_string("con")); | |
160 | wlc_handle parent = wlc_view_get_parent(c->handle); | ||
161 | json_object_object_add(object, "scratchpad_state", | ||
162 | json_object_new_string(ipc_json_get_scratchpad_state(c))); | ||
163 | |||
164 | json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL); | ||
165 | |||
166 | json_object_object_add(props, "class", c->class ? json_object_new_string(c->class) : | ||
167 | c->app_id ? json_object_new_string(c->app_id) : NULL); | ||
168 | json_object_object_add(props, "instance", c->instance ? json_object_new_string(c->instance) : | ||
169 | c->app_id ? json_object_new_string(c->app_id) : NULL); | ||
170 | json_object_object_add(props, "title", (c->name) ? json_object_new_string(c->name) : NULL); | ||
171 | json_object_object_add(props, "transient_for", parent ? json_object_new_int(parent) : NULL); | ||
172 | json_object_object_add(object, "window_properties", props); | ||
173 | |||
174 | json_object_object_add(object, "fullscreen_mode", | ||
175 | json_object_new_int(swayc_is_fullscreen(c) ? 1 : 0)); | ||
176 | json_object_object_add(object, "sticky", json_object_new_boolean(c->sticky)); | ||
177 | json_object_object_add(object, "floating", json_object_new_string( | ||
178 | c->is_floating ? "auto_on" : "auto_off")); // we can't state the cause | ||
179 | |||
180 | json_object_object_add(object, "app_id", c->app_id ? json_object_new_string(c->app_id) : NULL); | ||
181 | 159 | ||
182 | if (c->parent) { | 160 | if (c->parent) { |
183 | const char *layout = (c->parent->type == C_CONTAINER) ? | 161 | enum sway_container_layout layout = (c->parent->type == C_CONTAINER) ? |
184 | ipc_json_layout_description(c->parent->layout) : "none"; | 162 | c->parent->layout : c->layout; |
185 | const char *last_layout = (c->parent->type == C_CONTAINER) ? | 163 | |
186 | ipc_json_layout_description(c->parent->prev_layout) : "none"; | ||
187 | json_object_object_add(object, "layout", | 164 | json_object_object_add(object, "layout", |
188 | (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout)); | 165 | json_object_new_string(ipc_json_layout_description(layout))); |
189 | json_object_object_add(object, "last_split_layout", | ||
190 | (strcmp(last_layout, "null") == 0) ? NULL : json_object_new_string(last_layout)); | ||
191 | json_object_object_add(object, "workspace_layout", | ||
192 | json_object_new_string(ipc_json_layout_description(swayc_parent_by_type(c, C_WORKSPACE)->workspace_layout))); | ||
193 | } | 166 | } |
194 | } | 167 | } |
195 | 168 | ||
196 | static void ipc_json_describe_root(swayc_t *c, json_object *object) { | 169 | static void focus_inactive_children_iterator(struct sway_container *c, void *data) { |
197 | json_object_object_add(object, "type", json_object_new_string("root")); | 170 | json_object *focus = data; |
198 | json_object_object_add(object, "layout", json_object_new_string("splith")); | 171 | json_object_array_add(focus, json_object_new_int(c->id)); |
199 | } | 172 | } |
200 | 173 | ||
201 | json_object *ipc_json_describe_container(swayc_t *c) { | 174 | json_object *ipc_json_describe_container(struct sway_container *c) { |
202 | float percent = ipc_json_child_percentage(c); | ||
203 | |||
204 | if (!(sway_assert(c, "Container must not be null."))) { | 175 | if (!(sway_assert(c, "Container must not be null."))) { |
205 | return NULL; | 176 | return NULL; |
206 | } | 177 | } |
207 | 178 | ||
179 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | ||
180 | bool focused = seat_get_focus(seat) == c; | ||
181 | |||
208 | json_object *object = json_object_new_object(); | 182 | json_object *object = json_object_new_object(); |
209 | 183 | ||
210 | json_object_object_add(object, "id", json_object_new_int((int)c->id)); | 184 | json_object_object_add(object, "id", json_object_new_int((int)c->id)); |
211 | json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL); | 185 | json_object_object_add(object, "name", |
186 | c->name ? json_object_new_string(c->name) : NULL); | ||
212 | json_object_object_add(object, "rect", ipc_json_create_rect(c)); | 187 | json_object_object_add(object, "rect", ipc_json_create_rect(c)); |
213 | json_object_object_add(object, "visible", json_object_new_boolean(c->visible)); | 188 | json_object_object_add(object, "focused", |
214 | json_object_object_add(object, "focused", json_object_new_boolean(c == current_focus)); | 189 | json_object_new_boolean(focused)); |
215 | 190 | ||
216 | json_object_object_add(object, "border", json_object_new_string(ipc_json_border_description(c))); | 191 | json_object *focus = json_object_new_array(); |
217 | json_object_object_add(object, "window_rect", ipc_json_create_rect_from_geometry(c->actual_geometry)); | 192 | seat_focus_inactive_children_for_each(seat, c, |
218 | json_object_object_add(object, "deco_rect", ipc_json_create_rect_from_geometry(c->title_bar_geometry)); | 193 | focus_inactive_children_iterator, focus); |
219 | json_object_object_add(object, "geometry", ipc_json_create_rect_from_geometry(c->cached_geometry)); | 194 | json_object_object_add(object, "focus", focus); |
220 | json_object_object_add(object, "percent", (percent > 0) ? json_object_new_double(percent) : NULL); | ||
221 | json_object_object_add(object, "window", json_object_new_int(c->handle)); // for the sake of i3 compat | ||
222 | // TODO: make urgency actually work once Sway supports it | ||
223 | json_object_object_add(object, "urgent", json_object_new_boolean(false)); | ||
224 | json_object_object_add(object, "current_border_width", json_object_new_int(c->border_thickness)); | ||
225 | 195 | ||
226 | switch (c->type) { | 196 | switch (c->type) { |
227 | case C_ROOT: | 197 | case C_ROOT: |
228 | ipc_json_describe_root(c, object); | 198 | ipc_json_describe_root(c, object); |
229 | break; | 199 | break; |
230 | |||
231 | case C_OUTPUT: | 200 | case C_OUTPUT: |
232 | ipc_json_describe_output(c, object); | 201 | ipc_json_describe_output(c, object); |
233 | break; | 202 | break; |
234 | 203 | case C_CONTAINER: | |
235 | case C_CONTAINER: // fallthrough | ||
236 | case C_VIEW: | 204 | case C_VIEW: |
237 | ipc_json_describe_view(c, object); | 205 | ipc_json_describe_view(c, object); |
238 | break; | 206 | break; |
239 | |||
240 | case C_WORKSPACE: | 207 | case C_WORKSPACE: |
241 | ipc_json_describe_workspace(c, object); | 208 | ipc_json_describe_workspace(c, object); |
242 | break; | 209 | break; |
243 | 210 | case C_TYPES: | |
244 | case C_TYPES: // fallthrough; this should never happen, I'm just trying to silence compiler warnings | ||
245 | default: | 211 | default: |
246 | break; | 212 | break; |
247 | } | 213 | } |
@@ -249,80 +215,56 @@ json_object *ipc_json_describe_container(swayc_t *c) { | |||
249 | return object; | 215 | return object; |
250 | } | 216 | } |
251 | 217 | ||
252 | json_object *ipc_json_describe_input(struct libinput_device *device) { | 218 | json_object *ipc_json_describe_container_recursive(struct sway_container *c) { |
253 | char* identifier = libinput_dev_unique_id(device); | 219 | json_object *object = ipc_json_describe_container(c); |
254 | int vendor = libinput_device_get_id_vendor(device); | 220 | int i; |
255 | int product = libinput_device_get_id_product(device); | ||
256 | const char *name = libinput_device_get_name(device); | ||
257 | double width = -1, height = -1; | ||
258 | int has_size = libinput_device_get_size(device, &width, &height); | ||
259 | |||
260 | json_object *device_object = json_object_new_object(); | ||
261 | json_object_object_add(device_object,"identifier", | ||
262 | identifier ? json_object_new_string(identifier) : NULL); | ||
263 | json_object_object_add(device_object, | ||
264 | "vendor", json_object_new_int(vendor)); | ||
265 | json_object_object_add(device_object, | ||
266 | "product", json_object_new_int(product)); | ||
267 | json_object_object_add(device_object, | ||
268 | "name", json_object_new_string(name)); | ||
269 | if (has_size == 0) { | ||
270 | json_object *size_object = json_object_new_object(); | ||
271 | json_object_object_add(size_object, | ||
272 | "width", json_object_new_double(width)); | ||
273 | json_object_object_add(size_object, | ||
274 | "height", json_object_new_double(height)); | ||
275 | } else { | ||
276 | json_object_object_add(device_object, "size", NULL); | ||
277 | } | ||
278 | 221 | ||
279 | struct { | 222 | json_object *children = json_object_new_array(); |
280 | enum libinput_device_capability cap; | 223 | if (c->type != C_VIEW && c->children) { |
281 | const char *name; | 224 | for (i = 0; i < c->children->length; ++i) { |
282 | // If anyone feels like implementing device-specific IPC output, | 225 | json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i])); |
283 | // be my guest | ||
284 | json_object *(*describe)(struct libinput_device *); | ||
285 | } caps[] = { | ||
286 | { LIBINPUT_DEVICE_CAP_KEYBOARD, "keyboard", NULL }, | ||
287 | { LIBINPUT_DEVICE_CAP_POINTER, "pointer", NULL }, | ||
288 | { LIBINPUT_DEVICE_CAP_TOUCH, "touch", NULL }, | ||
289 | { LIBINPUT_DEVICE_CAP_TABLET_TOOL, "tablet_tool", NULL }, | ||
290 | { LIBINPUT_DEVICE_CAP_TABLET_PAD, "tablet_pad", NULL }, | ||
291 | { LIBINPUT_DEVICE_CAP_GESTURE, "gesture", NULL }, | ||
292 | #ifdef LIBINPUT_DEVICE_CAP_SWITCH // libinput 1.7.0+ | ||
293 | { LIBINPUT_DEVICE_CAP_SWITCH, "switch", NULL }, | ||
294 | #endif | ||
295 | }; | ||
296 | |||
297 | json_object *_caps = json_object_new_array(); | ||
298 | for (size_t i = 0; i < sizeof(caps) / sizeof(caps[0]); ++i) { | ||
299 | if (libinput_device_has_capability(device, caps[i].cap)) { | ||
300 | json_object_array_add(_caps, json_object_new_string(caps[i].name)); | ||
301 | if (caps[i].describe) { | ||
302 | json_object *desc = caps[i].describe(device); | ||
303 | json_object_object_add(device_object, caps[i].name, desc); | ||
304 | } | ||
305 | } | 226 | } |
306 | } | 227 | } |
307 | json_object_object_add(device_object, "capabilities", _caps); | 228 | json_object_object_add(object, "nodes", children); |
308 | 229 | ||
309 | free(identifier); | 230 | return object; |
310 | return device_object; | ||
311 | } | 231 | } |
312 | 232 | ||
313 | json_object *ipc_json_get_version() { | 233 | static const char *describe_device_type(struct sway_input_device *device) { |
314 | int major = 0, minor = 0, patch = 0; | 234 | switch (device->wlr_device->type) { |
315 | json_object *version = json_object_new_object(); | 235 | case WLR_INPUT_DEVICE_POINTER: |
236 | return "pointer"; | ||
237 | case WLR_INPUT_DEVICE_KEYBOARD: | ||
238 | return "keyboard"; | ||
239 | case WLR_INPUT_DEVICE_TOUCH: | ||
240 | return "touch"; | ||
241 | case WLR_INPUT_DEVICE_TABLET_TOOL: | ||
242 | return "tablet_tool"; | ||
243 | case WLR_INPUT_DEVICE_TABLET_PAD: | ||
244 | return "tablet_pad"; | ||
245 | } | ||
246 | return "unknown"; | ||
247 | } | ||
316 | 248 | ||
317 | sscanf(SWAY_VERSION, "%u.%u.%u", &major, &minor, &patch); | 249 | json_object *ipc_json_describe_input(struct sway_input_device *device) { |
250 | if (!(sway_assert(device, "Device must not be null"))) { | ||
251 | return NULL; | ||
252 | } | ||
318 | 253 | ||
319 | json_object_object_add(version, "human_readable", json_object_new_string(SWAY_VERSION)); | 254 | json_object *object = json_object_new_object(); |
320 | json_object_object_add(version, "variant", json_object_new_string("sway")); | ||
321 | json_object_object_add(version, "major", json_object_new_int(major)); | ||
322 | json_object_object_add(version, "minor", json_object_new_int(minor)); | ||
323 | json_object_object_add(version, "patch", json_object_new_int(patch)); | ||
324 | 255 | ||
325 | return version; | 256 | json_object_object_add(object, "identifier", |
257 | json_object_new_string(device->identifier)); | ||
258 | json_object_object_add(object, "name", | ||
259 | json_object_new_string(device->wlr_device->name)); | ||
260 | json_object_object_add(object, "vendor", | ||
261 | json_object_new_int(device->wlr_device->vendor)); | ||
262 | json_object_object_add(object, "product", | ||
263 | json_object_new_int(device->wlr_device->product)); | ||
264 | json_object_object_add(object, "type", | ||
265 | json_object_new_string(describe_device_type(device))); | ||
266 | |||
267 | return object; | ||
326 | } | 268 | } |
327 | 269 | ||
328 | json_object *ipc_json_describe_bar_config(struct bar_config *bar) { | 270 | json_object *ipc_json_describe_bar_config(struct bar_config *bar) { |
@@ -332,107 +274,116 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { | |||
332 | 274 | ||
333 | json_object *json = json_object_new_object(); | 275 | json_object *json = json_object_new_object(); |
334 | json_object_object_add(json, "id", json_object_new_string(bar->id)); | 276 | json_object_object_add(json, "id", json_object_new_string(bar->id)); |
335 | #ifdef ENABLE_TRAY | ||
336 | if (bar->tray_output) { | ||
337 | json_object_object_add(json, "tray_output", json_object_new_string(bar->tray_output)); | ||
338 | } else { | ||
339 | json_object_object_add(json, "tray_output", NULL); | ||
340 | } | ||
341 | if (bar->icon_theme) { | ||
342 | json_object_object_add(json, "icon_theme", json_object_new_string(bar->icon_theme)); | ||
343 | } else { | ||
344 | json_object_object_add(json, "icon_theme", NULL); | ||
345 | } | ||
346 | json_object_object_add(json, "tray_padding", json_object_new_int(bar->tray_padding)); | ||
347 | json_object_object_add(json, "activate_button", json_object_new_int(bar->activate_button)); | ||
348 | json_object_object_add(json, "context_button", json_object_new_int(bar->context_button)); | ||
349 | json_object_object_add(json, "secondary_button", json_object_new_int(bar->secondary_button)); | ||
350 | #endif | ||
351 | json_object_object_add(json, "mode", json_object_new_string(bar->mode)); | 277 | json_object_object_add(json, "mode", json_object_new_string(bar->mode)); |
352 | json_object_object_add(json, "hidden_state", json_object_new_string(bar->hidden_state)); | 278 | json_object_object_add(json, "hidden_state", |
353 | json_object_object_add(json, "modifier", json_object_new_string(get_modifier_name_by_mask(bar->modifier))); | 279 | json_object_new_string(bar->hidden_state)); |
354 | switch (bar->position) { | 280 | json_object_object_add(json, "position", |
355 | case DESKTOP_SHELL_PANEL_POSITION_TOP: | 281 | json_object_new_string(bar->position)); |
356 | json_object_object_add(json, "position", json_object_new_string("top")); | 282 | json_object_object_add(json, "status_command", |
357 | break; | 283 | json_object_new_string(bar->status_command)); |
358 | case DESKTOP_SHELL_PANEL_POSITION_BOTTOM: | 284 | json_object_object_add(json, "font", |
359 | json_object_object_add(json, "position", json_object_new_string("bottom")); | 285 | json_object_new_string((bar->font) ? bar->font : config->font)); |
360 | break; | ||
361 | case DESKTOP_SHELL_PANEL_POSITION_LEFT: | ||
362 | json_object_object_add(json, "position", json_object_new_string("left")); | ||
363 | break; | ||
364 | case DESKTOP_SHELL_PANEL_POSITION_RIGHT: | ||
365 | json_object_object_add(json, "position", json_object_new_string("right")); | ||
366 | break; | ||
367 | } | ||
368 | json_object_object_add(json, "status_command", json_object_new_string(bar->status_command)); | ||
369 | json_object_object_add(json, "font", json_object_new_string((bar->font) ? bar->font : config->font)); | ||
370 | if (bar->separator_symbol) { | 286 | if (bar->separator_symbol) { |
371 | json_object_object_add(json, "separator_symbol", json_object_new_string(bar->separator_symbol)); | 287 | json_object_object_add(json, "separator_symbol", |
288 | json_object_new_string(bar->separator_symbol)); | ||
372 | } | 289 | } |
373 | json_object_object_add(json, "bar_height", json_object_new_int(bar->height)); | 290 | json_object_object_add(json, "bar_height", |
374 | json_object_object_add(json, "wrap_scroll", json_object_new_boolean(bar->wrap_scroll)); | 291 | json_object_new_int(bar->height)); |
375 | json_object_object_add(json, "workspace_buttons", json_object_new_boolean(bar->workspace_buttons)); | 292 | json_object_object_add(json, "wrap_scroll", |
376 | json_object_object_add(json, "strip_workspace_numbers", json_object_new_boolean(bar->strip_workspace_numbers)); | 293 | json_object_new_boolean(bar->wrap_scroll)); |
377 | json_object_object_add(json, "binding_mode_indicator", json_object_new_boolean(bar->binding_mode_indicator)); | 294 | json_object_object_add(json, "workspace_buttons", |
378 | json_object_object_add(json, "verbose", json_object_new_boolean(bar->verbose)); | 295 | json_object_new_boolean(bar->workspace_buttons)); |
379 | json_object_object_add(json, "pango_markup", json_object_new_boolean(bar->pango_markup)); | 296 | json_object_object_add(json, "strip_workspace_numbers", |
297 | json_object_new_boolean(bar->strip_workspace_numbers)); | ||
298 | json_object_object_add(json, "binding_mode_indicator", | ||
299 | json_object_new_boolean(bar->binding_mode_indicator)); | ||
300 | json_object_object_add(json, "verbose", | ||
301 | json_object_new_boolean(bar->verbose)); | ||
302 | json_object_object_add(json, "pango_markup", | ||
303 | json_object_new_boolean(bar->pango_markup)); | ||
380 | 304 | ||
381 | json_object *colors = json_object_new_object(); | 305 | json_object *colors = json_object_new_object(); |
382 | json_object_object_add(colors, "background", json_object_new_string(bar->colors.background)); | 306 | json_object_object_add(colors, "background", |
383 | json_object_object_add(colors, "statusline", json_object_new_string(bar->colors.statusline)); | 307 | json_object_new_string(bar->colors.background)); |
384 | json_object_object_add(colors, "separator", json_object_new_string(bar->colors.separator)); | 308 | json_object_object_add(colors, "statusline", |
309 | json_object_new_string(bar->colors.statusline)); | ||
310 | json_object_object_add(colors, "separator", | ||
311 | json_object_new_string(bar->colors.separator)); | ||
385 | 312 | ||
386 | if (bar->colors.focused_background) { | 313 | if (bar->colors.focused_background) { |
387 | json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.focused_background)); | 314 | json_object_object_add(colors, "focused_background", |
315 | json_object_new_string(bar->colors.focused_background)); | ||
388 | } else { | 316 | } else { |
389 | json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.background)); | 317 | json_object_object_add(colors, "focused_background", |
318 | json_object_new_string(bar->colors.background)); | ||
390 | } | 319 | } |
391 | 320 | ||
392 | if (bar->colors.focused_statusline) { | 321 | if (bar->colors.focused_statusline) { |
393 | json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.focused_statusline)); | 322 | json_object_object_add(colors, "focused_statusline", |
323 | json_object_new_string(bar->colors.focused_statusline)); | ||
394 | } else { | 324 | } else { |
395 | json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.statusline)); | 325 | json_object_object_add(colors, "focused_statusline", |
326 | json_object_new_string(bar->colors.statusline)); | ||
396 | } | 327 | } |
397 | 328 | ||
398 | if (bar->colors.focused_separator) { | 329 | if (bar->colors.focused_separator) { |
399 | json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.focused_separator)); | 330 | json_object_object_add(colors, "focused_separator", |
331 | json_object_new_string(bar->colors.focused_separator)); | ||
400 | } else { | 332 | } else { |
401 | json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.separator)); | 333 | json_object_object_add(colors, "focused_separator", |
334 | json_object_new_string(bar->colors.separator)); | ||
402 | } | 335 | } |
403 | 336 | ||
404 | json_object_object_add(colors, "focused_workspace_border", json_object_new_string(bar->colors.focused_workspace_border)); | 337 | json_object_object_add(colors, "focused_workspace_border", |
405 | json_object_object_add(colors, "focused_workspace_bg", json_object_new_string(bar->colors.focused_workspace_bg)); | 338 | json_object_new_string(bar->colors.focused_workspace_border)); |
406 | json_object_object_add(colors, "focused_workspace_text", json_object_new_string(bar->colors.focused_workspace_text)); | 339 | json_object_object_add(colors, "focused_workspace_bg", |
407 | 340 | json_object_new_string(bar->colors.focused_workspace_bg)); | |
408 | json_object_object_add(colors, "inactive_workspace_border", json_object_new_string(bar->colors.inactive_workspace_border)); | 341 | json_object_object_add(colors, "focused_workspace_text", |
409 | json_object_object_add(colors, "inactive_workspace_bg", json_object_new_string(bar->colors.inactive_workspace_bg)); | 342 | json_object_new_string(bar->colors.focused_workspace_text)); |
410 | json_object_object_add(colors, "inactive_workspace_text", json_object_new_string(bar->colors.inactive_workspace_text)); | 343 | |
411 | 344 | json_object_object_add(colors, "inactive_workspace_border", | |
412 | json_object_object_add(colors, "active_workspace_border", json_object_new_string(bar->colors.active_workspace_border)); | 345 | json_object_new_string(bar->colors.inactive_workspace_border)); |
413 | json_object_object_add(colors, "active_workspace_bg", json_object_new_string(bar->colors.active_workspace_bg)); | 346 | json_object_object_add(colors, "inactive_workspace_bg", |
414 | json_object_object_add(colors, "active_workspace_text", json_object_new_string(bar->colors.active_workspace_text)); | 347 | json_object_new_string(bar->colors.inactive_workspace_bg)); |
415 | 348 | json_object_object_add(colors, "inactive_workspace_text", | |
416 | json_object_object_add(colors, "urgent_workspace_border", json_object_new_string(bar->colors.urgent_workspace_border)); | 349 | json_object_new_string(bar->colors.inactive_workspace_text)); |
417 | json_object_object_add(colors, "urgent_workspace_bg", json_object_new_string(bar->colors.urgent_workspace_bg)); | 350 | |
418 | json_object_object_add(colors, "urgent_workspace_text", json_object_new_string(bar->colors.urgent_workspace_text)); | 351 | json_object_object_add(colors, "active_workspace_border", |
352 | json_object_new_string(bar->colors.active_workspace_border)); | ||
353 | json_object_object_add(colors, "active_workspace_bg", | ||
354 | json_object_new_string(bar->colors.active_workspace_bg)); | ||
355 | json_object_object_add(colors, "active_workspace_text", | ||
356 | json_object_new_string(bar->colors.active_workspace_text)); | ||
357 | |||
358 | json_object_object_add(colors, "urgent_workspace_border", | ||
359 | json_object_new_string(bar->colors.urgent_workspace_border)); | ||
360 | json_object_object_add(colors, "urgent_workspace_bg", | ||
361 | json_object_new_string(bar->colors.urgent_workspace_bg)); | ||
362 | json_object_object_add(colors, "urgent_workspace_text", | ||
363 | json_object_new_string(bar->colors.urgent_workspace_text)); | ||
419 | 364 | ||
420 | if (bar->colors.binding_mode_border) { | 365 | if (bar->colors.binding_mode_border) { |
421 | json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.binding_mode_border)); | 366 | json_object_object_add(colors, "binding_mode_border", |
367 | json_object_new_string(bar->colors.binding_mode_border)); | ||
422 | } else { | 368 | } else { |
423 | json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.urgent_workspace_border)); | 369 | json_object_object_add(colors, "binding_mode_border", |
370 | json_object_new_string(bar->colors.urgent_workspace_border)); | ||
424 | } | 371 | } |
425 | 372 | ||
426 | if (bar->colors.binding_mode_bg) { | 373 | if (bar->colors.binding_mode_bg) { |
427 | json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.binding_mode_bg)); | 374 | json_object_object_add(colors, "binding_mode_bg", |
375 | json_object_new_string(bar->colors.binding_mode_bg)); | ||
428 | } else { | 376 | } else { |
429 | json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.urgent_workspace_bg)); | 377 | json_object_object_add(colors, "binding_mode_bg", |
378 | json_object_new_string(bar->colors.urgent_workspace_bg)); | ||
430 | } | 379 | } |
431 | 380 | ||
432 | if (bar->colors.binding_mode_text) { | 381 | if (bar->colors.binding_mode_text) { |
433 | json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.binding_mode_text)); | 382 | json_object_object_add(colors, "binding_mode_text", |
383 | json_object_new_string(bar->colors.binding_mode_text)); | ||
434 | } else { | 384 | } else { |
435 | json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.urgent_workspace_text)); | 385 | json_object_object_add(colors, "binding_mode_text", |
386 | json_object_new_string(bar->colors.urgent_workspace_text)); | ||
436 | } | 387 | } |
437 | 388 | ||
438 | json_object_object_add(json, "colors", colors); | 389 | json_object_object_add(json, "colors", colors); |
@@ -440,75 +391,11 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { | |||
440 | // Add outputs if defined | 391 | // Add outputs if defined |
441 | if (bar->outputs && bar->outputs->length > 0) { | 392 | if (bar->outputs && bar->outputs->length > 0) { |
442 | json_object *outputs = json_object_new_array(); | 393 | json_object *outputs = json_object_new_array(); |
443 | int i; | 394 | for (int i = 0; i < bar->outputs->length; ++i) { |
444 | for (i = 0; i < bar->outputs->length; ++i) { | ||
445 | const char *name = bar->outputs->items[i]; | 395 | const char *name = bar->outputs->items[i]; |
446 | json_object_array_add(outputs, json_object_new_string(name)); | 396 | json_object_array_add(outputs, json_object_new_string(name)); |
447 | } | 397 | } |
448 | json_object_object_add(json, "outputs", outputs); | 398 | json_object_object_add(json, "outputs", outputs); |
449 | } | 399 | } |
450 | |||
451 | return json; | 400 | return json; |
452 | } | 401 | } |
453 | |||
454 | json_object *ipc_json_describe_container_recursive(swayc_t *c) { | ||
455 | json_object *object = ipc_json_describe_container(c); | ||
456 | int i; | ||
457 | |||
458 | json_object *floating = json_object_new_array(); | ||
459 | if (c->type != C_VIEW && c->floating) { | ||
460 | for (i = 0; i < c->floating->length; ++i) { | ||
461 | swayc_t *item = c->floating->items[i]; | ||
462 | json_object_array_add(floating, ipc_json_describe_container_recursive(item)); | ||
463 | } | ||
464 | } | ||
465 | json_object_object_add(object, "floating_nodes", floating); | ||
466 | |||
467 | json_object *children = json_object_new_array(); | ||
468 | if (c->type != C_VIEW && c->children) { | ||
469 | for (i = 0; i < c->children->length; ++i) { | ||
470 | json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i])); | ||
471 | } | ||
472 | } | ||
473 | json_object_object_add(object, "nodes", children); | ||
474 | |||
475 | json_object *focus = json_object_new_array(); | ||
476 | if (c->type != C_VIEW) { | ||
477 | if (c->focused) { | ||
478 | json_object_array_add(focus, json_object_new_double(c->focused->id)); | ||
479 | } | ||
480 | if (c->floating) { | ||
481 | for (i = 0; i < c->floating->length; ++i) { | ||
482 | swayc_t *item = c->floating->items[i]; | ||
483 | if (item == c->focused) { | ||
484 | continue; | ||
485 | } | ||
486 | |||
487 | json_object_array_add(focus, json_object_new_double(item->id)); | ||
488 | } | ||
489 | } | ||
490 | if (c->children) { | ||
491 | for (i = 0; i < c->children->length; ++i) { | ||
492 | swayc_t *item = c->children->items[i]; | ||
493 | if (item == c->focused) { | ||
494 | continue; | ||
495 | } | ||
496 | |||
497 | json_object_array_add(focus, json_object_new_double(item->id)); | ||
498 | } | ||
499 | } | ||
500 | } | ||
501 | json_object_object_add(object, "focus", focus); | ||
502 | |||
503 | if (c->type == C_ROOT) { | ||
504 | json_object *scratchpad_json = json_object_new_array(); | ||
505 | if (scratchpad->length > 0) { | ||
506 | for (i = 0; i < scratchpad->length; ++i) { | ||
507 | json_object_array_add(scratchpad_json, ipc_json_describe_container_recursive(scratchpad->items[i])); | ||
508 | } | ||
509 | } | ||
510 | json_object_object_add(object, "scratchpad", scratchpad_json); | ||
511 | } | ||
512 | |||
513 | return object; | ||
514 | } | ||
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index b560b930..045802e1 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -1,51 +1,41 @@ | |||
1 | // See https://i3wm.org/docs/ipc.html for protocol information | 1 | // See https://i3wm.org/docs/ipc.html for protocol information |
2 | |||
3 | #ifndef __FreeBSD__ | 2 | #ifndef __FreeBSD__ |
4 | // Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0) | 3 | // Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0) |
5 | #define _XOPEN_SOURCE 700 | 4 | #define _XOPEN_SOURCE 700 |
6 | #endif | 5 | #endif |
7 | 6 | #include <assert.h> | |
8 | #include <errno.h> | 7 | #include <errno.h> |
8 | #include <fcntl.h> | ||
9 | #include <json-c/json.h> | ||
10 | #include <stdbool.h> | ||
11 | #include <stdint.h> | ||
12 | #include <stdlib.h> | ||
9 | #include <string.h> | 13 | #include <string.h> |
10 | #include <sys/socket.h> | 14 | #include <sys/socket.h> |
15 | #include <sys/ioctl.h> | ||
11 | #include <sys/un.h> | 16 | #include <sys/un.h> |
12 | #include <stdbool.h> | ||
13 | #include <wlc/wlc-render.h> | ||
14 | #include <unistd.h> | 17 | #include <unistd.h> |
15 | #include <stdlib.h> | 18 | #include <wayland-server.h> |
16 | #include <sys/ioctl.h> | 19 | #include "sway/commands.h" |
17 | #include <fcntl.h> | ||
18 | #include <json-c/json.h> | ||
19 | #include <list.h> | ||
20 | #include <libinput.h> | ||
21 | #ifdef __linux__ | ||
22 | struct ucred { | ||
23 | pid_t pid; | ||
24 | uid_t uid; | ||
25 | gid_t gid; | ||
26 | }; | ||
27 | #endif | ||
28 | #include "sway/ipc-json.h" | 20 | #include "sway/ipc-json.h" |
29 | #include "sway/ipc-server.h" | 21 | #include "sway/ipc-server.h" |
30 | #include "sway/security.h" | 22 | #include "sway/server.h" |
31 | #include "sway/config.h" | 23 | #include "sway/input/input-manager.h" |
32 | #include "sway/commands.h" | 24 | #include "sway/input/seat.h" |
33 | #include "sway/input.h" | ||
34 | #include "stringop.h" | ||
35 | #include "log.h" | ||
36 | #include "list.h" | 25 | #include "list.h" |
37 | #include "util.h" | 26 | #include "log.h" |
38 | 27 | ||
39 | static int ipc_socket = -1; | 28 | static int ipc_socket = -1; |
40 | static struct wlc_event_source *ipc_event_source = NULL; | 29 | static struct wl_event_source *ipc_event_source = NULL; |
41 | static struct sockaddr_un *ipc_sockaddr = NULL; | 30 | static struct sockaddr_un *ipc_sockaddr = NULL; |
42 | static list_t *ipc_client_list = NULL; | 31 | static list_t *ipc_client_list = NULL; |
43 | 32 | ||
44 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; | 33 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; |
45 | 34 | ||
46 | struct ipc_client { | 35 | struct ipc_client { |
47 | struct wlc_event_source *event_source; | 36 | struct wl_event_source *event_source; |
48 | struct wlc_event_source *writable_event_source; | 37 | struct wl_event_source *writable_event_source; |
38 | struct sway_server *server; | ||
49 | int fd; | 39 | int fd; |
50 | uint32_t payload_length; | 40 | uint32_t payload_length; |
51 | uint32_t security_policy; | 41 | uint32_t security_policy; |
@@ -56,27 +46,6 @@ struct ipc_client { | |||
56 | char *write_buffer; | 46 | char *write_buffer; |
57 | }; | 47 | }; |
58 | 48 | ||
59 | static list_t *ipc_get_pixel_requests = NULL; | ||
60 | |||
61 | struct get_pixels_request { | ||
62 | struct ipc_client *client; | ||
63 | wlc_handle output; | ||
64 | struct wlc_geometry geo; | ||
65 | }; | ||
66 | |||
67 | struct get_clipboard_request { | ||
68 | struct ipc_client *client; | ||
69 | json_object *json; | ||
70 | int fd; | ||
71 | struct wlc_event_source *fd_event_source; | ||
72 | struct wlc_event_source *timer_event_source; | ||
73 | char *type; | ||
74 | unsigned int *pending; | ||
75 | char *buf; | ||
76 | size_t buf_size; | ||
77 | size_t buf_position; | ||
78 | }; | ||
79 | |||
80 | struct sockaddr_un *ipc_user_sockaddr(void); | 49 | struct sockaddr_un *ipc_user_sockaddr(void); |
81 | int ipc_handle_connection(int fd, uint32_t mask, void *data); | 50 | int ipc_handle_connection(int fd, uint32_t mask, void *data); |
82 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); | 51 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); |
@@ -84,11 +53,8 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); | |||
84 | void ipc_client_disconnect(struct ipc_client *client); | 53 | void ipc_client_disconnect(struct ipc_client *client); |
85 | void ipc_client_handle_command(struct ipc_client *client); | 54 | void ipc_client_handle_command(struct ipc_client *client); |
86 | bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); | 55 | bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); |
87 | void ipc_get_workspaces_callback(swayc_t *workspace, void *data); | ||
88 | void ipc_get_outputs_callback(swayc_t *container, void *data); | ||
89 | static void ipc_get_marks_callback(swayc_t *container, void *data); | ||
90 | 56 | ||
91 | void ipc_init(void) { | 57 | void ipc_init(struct sway_server *server) { |
92 | ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); | 58 | ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); |
93 | if (ipc_socket == -1) { | 59 | if (ipc_socket == -1) { |
94 | sway_abort("Unable to create IPC socket"); | 60 | sway_abort("Unable to create IPC socket"); |
@@ -116,14 +82,14 @@ void ipc_init(void) { | |||
116 | setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1); | 82 | setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1); |
117 | 83 | ||
118 | ipc_client_list = create_list(); | 84 | ipc_client_list = create_list(); |
119 | ipc_get_pixel_requests = create_list(); | ||
120 | 85 | ||
121 | ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); | 86 | ipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket, |
87 | WL_EVENT_READABLE, ipc_handle_connection, server); | ||
122 | } | 88 | } |
123 | 89 | ||
124 | void ipc_terminate(void) { | 90 | void ipc_terminate(void) { |
125 | if (ipc_event_source) { | 91 | if (ipc_event_source) { |
126 | wlc_event_source_remove(ipc_event_source); | 92 | wl_event_source_remove(ipc_event_source); |
127 | } | 93 | } |
128 | close(ipc_socket); | 94 | close(ipc_socket); |
129 | unlink(ipc_sockaddr->sun_path); | 95 | unlink(ipc_sockaddr->sun_path); |
@@ -157,100 +123,82 @@ struct sockaddr_un *ipc_user_sockaddr(void) { | |||
157 | return ipc_sockaddr; | 123 | return ipc_sockaddr; |
158 | } | 124 | } |
159 | 125 | ||
160 | static pid_t get_client_pid(int client_fd) { | ||
161 | // FreeBSD supports getting uid/gid, but not pid | ||
162 | #ifdef __linux__ | ||
163 | struct ucred ucred; | ||
164 | socklen_t len = sizeof(struct ucred); | ||
165 | |||
166 | if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) { | ||
167 | return -1; | ||
168 | } | ||
169 | |||
170 | return ucred.pid; | ||
171 | #else | ||
172 | return -1; | ||
173 | #endif | ||
174 | } | ||
175 | |||
176 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { | 126 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { |
177 | (void) fd; (void) data; | 127 | (void) fd; |
178 | sway_log(L_DEBUG, "Event on IPC listening socket"); | 128 | struct sway_server *server = data; |
179 | assert(mask == WLC_EVENT_READABLE); | 129 | wlr_log(L_DEBUG, "Event on IPC listening socket"); |
130 | assert(mask == WL_EVENT_READABLE); | ||
180 | 131 | ||
181 | int client_fd = accept(ipc_socket, NULL, NULL); | 132 | int client_fd = accept(ipc_socket, NULL, NULL); |
182 | if (client_fd == -1) { | 133 | if (client_fd == -1) { |
183 | sway_log_errno(L_ERROR, "Unable to accept IPC client connection"); | 134 | wlr_log_errno(L_ERROR, "Unable to accept IPC client connection"); |
184 | return 0; | 135 | return 0; |
185 | } | 136 | } |
186 | 137 | ||
187 | int flags; | 138 | int flags; |
188 | if ((flags = fcntl(client_fd, F_GETFD)) == -1 | 139 | if ((flags = fcntl(client_fd, F_GETFD)) == -1 |
189 | || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { | 140 | || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { |
190 | sway_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket"); | 141 | wlr_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket"); |
191 | close(client_fd); | 142 | close(client_fd); |
192 | return 0; | 143 | return 0; |
193 | } | 144 | } |
194 | if ((flags = fcntl(client_fd, F_GETFL)) == -1 | 145 | if ((flags = fcntl(client_fd, F_GETFL)) == -1 |
195 | || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { | 146 | || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { |
196 | sway_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket"); | 147 | wlr_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket"); |
197 | close(client_fd); | 148 | close(client_fd); |
198 | return 0; | 149 | return 0; |
199 | } | 150 | } |
200 | 151 | ||
201 | struct ipc_client* client = malloc(sizeof(struct ipc_client)); | 152 | struct ipc_client *client = malloc(sizeof(struct ipc_client)); |
202 | if (!client) { | 153 | if (!client) { |
203 | sway_log(L_ERROR, "Unable to allocate ipc client"); | 154 | wlr_log(L_ERROR, "Unable to allocate ipc client"); |
204 | close(client_fd); | 155 | close(client_fd); |
205 | return 0; | 156 | return 0; |
206 | } | 157 | } |
158 | client->server = server; | ||
207 | client->payload_length = 0; | 159 | client->payload_length = 0; |
208 | client->fd = client_fd; | 160 | client->fd = client_fd; |
209 | client->subscribed_events = 0; | 161 | client->subscribed_events = 0; |
210 | client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); | 162 | client->event_source = wl_event_loop_add_fd(server->wl_event_loop, |
163 | client_fd, WL_EVENT_READABLE, ipc_client_handle_readable, client); | ||
211 | client->writable_event_source = NULL; | 164 | client->writable_event_source = NULL; |
212 | 165 | ||
213 | client->write_buffer_size = 128; | 166 | client->write_buffer_size = 128; |
214 | client->write_buffer_len = 0; | 167 | client->write_buffer_len = 0; |
215 | client->write_buffer = malloc(client->write_buffer_size); | 168 | client->write_buffer = malloc(client->write_buffer_size); |
216 | if (!client->write_buffer) { | 169 | if (!client->write_buffer) { |
217 | sway_log(L_ERROR, "Unable to allocate ipc client write buffer"); | 170 | wlr_log(L_ERROR, "Unable to allocate ipc client write buffer"); |
218 | close(client_fd); | 171 | close(client_fd); |
219 | return 0; | 172 | return 0; |
220 | } | 173 | } |
221 | 174 | ||
222 | pid_t pid = get_client_pid(client->fd); | 175 | wlr_log(L_DEBUG, "New client: fd %d", client_fd); |
223 | client->security_policy = get_ipc_policy_mask(pid); | ||
224 | |||
225 | sway_log(L_DEBUG, "New client: fd %d, pid %d", client_fd, pid); | ||
226 | |||
227 | list_add(ipc_client_list, client); | 176 | list_add(ipc_client_list, client); |
228 | |||
229 | return 0; | 177 | return 0; |
230 | } | 178 | } |
231 | 179 | ||
232 | static const int ipc_header_size = sizeof(ipc_magic)+8; | 180 | static const int ipc_header_size = sizeof(ipc_magic) + 8; |
233 | 181 | ||
234 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | 182 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { |
235 | struct ipc_client *client = data; | 183 | struct ipc_client *client = data; |
236 | 184 | ||
237 | if (mask & WLC_EVENT_ERROR) { | 185 | if (mask & WL_EVENT_ERROR) { |
238 | sway_log(L_ERROR, "IPC Client socket error, removing client"); | 186 | wlr_log(L_ERROR, "IPC Client socket error, removing client"); |
239 | ipc_client_disconnect(client); | 187 | ipc_client_disconnect(client); |
240 | return 0; | 188 | return 0; |
241 | } | 189 | } |
242 | 190 | ||
243 | if (mask & WLC_EVENT_HANGUP) { | 191 | if (mask & WL_EVENT_HANGUP) { |
244 | sway_log(L_DEBUG, "Client %d hung up", client->fd); | 192 | wlr_log(L_DEBUG, "Client %d hung up", client->fd); |
245 | ipc_client_disconnect(client); | 193 | ipc_client_disconnect(client); |
246 | return 0; | 194 | return 0; |
247 | } | 195 | } |
248 | 196 | ||
249 | sway_log(L_DEBUG, "Client %d readable", client->fd); | 197 | wlr_log(L_DEBUG, "Client %d readable", client->fd); |
250 | 198 | ||
251 | int read_available; | 199 | int read_available; |
252 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { | 200 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { |
253 | sway_log_errno(L_INFO, "Unable to read IPC socket buffer size"); | 201 | wlr_log_errno(L_INFO, "Unable to read IPC socket buffer size"); |
254 | ipc_client_disconnect(client); | 202 | ipc_client_disconnect(client); |
255 | return 0; | 203 | return 0; |
256 | } | 204 | } |
@@ -272,13 +220,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
272 | // Should be fully available, because read_available >= ipc_header_size | 220 | // Should be fully available, because read_available >= ipc_header_size |
273 | ssize_t received = recv(client_fd, buf, ipc_header_size, 0); | 221 | ssize_t received = recv(client_fd, buf, ipc_header_size, 0); |
274 | if (received == -1) { | 222 | if (received == -1) { |
275 | sway_log_errno(L_INFO, "Unable to receive header from IPC client"); | 223 | wlr_log_errno(L_INFO, "Unable to receive header from IPC client"); |
276 | ipc_client_disconnect(client); | 224 | ipc_client_disconnect(client); |
277 | return 0; | 225 | return 0; |
278 | } | 226 | } |
279 | 227 | ||
280 | if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { | 228 | if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { |
281 | sway_log(L_DEBUG, "IPC header check failed"); | 229 | wlr_log(L_DEBUG, "IPC header check failed"); |
282 | ipc_client_disconnect(client); | 230 | ipc_client_disconnect(client); |
283 | return 0; | 231 | return 0; |
284 | } | 232 | } |
@@ -293,17 +241,110 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
293 | return 0; | 241 | return 0; |
294 | } | 242 | } |
295 | 243 | ||
244 | static bool ipc_has_event_listeners(enum ipc_command_type event) { | ||
245 | for (int i = 0; i < ipc_client_list->length; i++) { | ||
246 | struct ipc_client *client = ipc_client_list->items[i]; | ||
247 | if ((client->subscribed_events & event_mask(event)) == 0) { | ||
248 | return true; | ||
249 | } | ||
250 | } | ||
251 | return false; | ||
252 | } | ||
253 | |||
254 | static void ipc_send_event(const char *json_string, enum ipc_command_type event) { | ||
255 | struct ipc_client *client; | ||
256 | for (int i = 0; i < ipc_client_list->length; i++) { | ||
257 | client = ipc_client_list->items[i]; | ||
258 | if ((client->subscribed_events & event_mask(event)) == 0) { | ||
259 | continue; | ||
260 | } | ||
261 | client->current_command = event; | ||
262 | if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { | ||
263 | wlr_log_errno(L_INFO, "Unable to send reply to IPC client"); | ||
264 | ipc_client_disconnect(client); | ||
265 | } | ||
266 | } | ||
267 | } | ||
268 | |||
269 | void ipc_event_workspace(struct sway_container *old, | ||
270 | struct sway_container *new, const char *change) { | ||
271 | if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) { | ||
272 | return; | ||
273 | } | ||
274 | wlr_log(L_DEBUG, "Sending workspace::%s event", change); | ||
275 | json_object *obj = json_object_new_object(); | ||
276 | json_object_object_add(obj, "change", json_object_new_string(change)); | ||
277 | if (strcmp("focus", change) == 0) { | ||
278 | if (old) { | ||
279 | json_object_object_add(obj, "old", | ||
280 | ipc_json_describe_container_recursive(old)); | ||
281 | } else { | ||
282 | json_object_object_add(obj, "old", NULL); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | if (new) { | ||
287 | json_object_object_add(obj, "current", | ||
288 | ipc_json_describe_container_recursive(new)); | ||
289 | } else { | ||
290 | json_object_object_add(obj, "current", NULL); | ||
291 | } | ||
292 | |||
293 | const char *json_string = json_object_to_json_string(obj); | ||
294 | ipc_send_event(json_string, IPC_EVENT_WORKSPACE); | ||
295 | json_object_put(obj); | ||
296 | } | ||
297 | |||
298 | void ipc_event_window(struct sway_container *window, const char *change) { | ||
299 | if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) { | ||
300 | return; | ||
301 | } | ||
302 | wlr_log(L_DEBUG, "Sending window::%s event", change); | ||
303 | json_object *obj = json_object_new_object(); | ||
304 | json_object_object_add(obj, "change", json_object_new_string(change)); | ||
305 | json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); | ||
306 | |||
307 | const char *json_string = json_object_to_json_string(obj); | ||
308 | ipc_send_event(json_string, IPC_EVENT_WINDOW); | ||
309 | json_object_put(obj); | ||
310 | } | ||
311 | |||
312 | void ipc_event_barconfig_update(struct bar_config *bar) { | ||
313 | if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) { | ||
314 | return; | ||
315 | } | ||
316 | wlr_log(L_DEBUG, "Sending barconfig_update event"); | ||
317 | json_object *json = ipc_json_describe_bar_config(bar); | ||
318 | |||
319 | const char *json_string = json_object_to_json_string(json); | ||
320 | ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE); | ||
321 | json_object_put(json); | ||
322 | } | ||
323 | |||
324 | void ipc_event_mode(const char *mode) { | ||
325 | if (!ipc_has_event_listeners(IPC_EVENT_MODE)) { | ||
326 | return; | ||
327 | } | ||
328 | wlr_log(L_DEBUG, "Sending mode::%s event", mode); | ||
329 | json_object *obj = json_object_new_object(); | ||
330 | json_object_object_add(obj, "change", json_object_new_string(mode)); | ||
331 | |||
332 | const char *json_string = json_object_to_json_string(obj); | ||
333 | ipc_send_event(json_string, IPC_EVENT_MODE); | ||
334 | json_object_put(obj); | ||
335 | } | ||
336 | |||
296 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | 337 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { |
297 | struct ipc_client *client = data; | 338 | struct ipc_client *client = data; |
298 | 339 | ||
299 | if (mask & WLC_EVENT_ERROR) { | 340 | if (mask & WL_EVENT_ERROR) { |
300 | sway_log(L_ERROR, "IPC Client socket error, removing client"); | 341 | wlr_log(L_ERROR, "IPC Client socket error, removing client"); |
301 | ipc_client_disconnect(client); | 342 | ipc_client_disconnect(client); |
302 | return 0; | 343 | return 0; |
303 | } | 344 | } |
304 | 345 | ||
305 | if (mask & WLC_EVENT_HANGUP) { | 346 | if (mask & WL_EVENT_HANGUP) { |
306 | sway_log(L_DEBUG, "Client %d hung up", client->fd); | 347 | wlr_log(L_DEBUG, "Client %d hung up", client->fd); |
307 | ipc_client_disconnect(client); | 348 | ipc_client_disconnect(client); |
308 | return 0; | 349 | return 0; |
309 | } | 350 | } |
@@ -312,14 +353,14 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
312 | return 0; | 353 | return 0; |
313 | } | 354 | } |
314 | 355 | ||
315 | sway_log(L_DEBUG, "Client %d writable", client->fd); | 356 | wlr_log(L_DEBUG, "Client %d writable", client->fd); |
316 | 357 | ||
317 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); | 358 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); |
318 | 359 | ||
319 | if (written == -1 && errno == EAGAIN) { | 360 | if (written == -1 && errno == EAGAIN) { |
320 | return 0; | 361 | return 0; |
321 | } else if (written == -1) { | 362 | } else if (written == -1) { |
322 | sway_log_errno(L_INFO, "Unable to send data from queue to IPC client"); | 363 | wlr_log_errno(L_INFO, "Unable to send data from queue to IPC client"); |
323 | ipc_client_disconnect(client); | 364 | ipc_client_disconnect(client); |
324 | return 0; | 365 | return 0; |
325 | } | 366 | } |
@@ -328,7 +369,7 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
328 | client->write_buffer_len -= written; | 369 | client->write_buffer_len -= written; |
329 | 370 | ||
330 | if (client->write_buffer_len == 0 && client->writable_event_source) { | 371 | if (client->write_buffer_len == 0 && client->writable_event_source) { |
331 | wlc_event_source_remove(client->writable_event_source); | 372 | wl_event_source_remove(client->writable_event_source); |
332 | client->writable_event_source = NULL; | 373 | client->writable_event_source = NULL; |
333 | } | 374 | } |
334 | 375 | ||
@@ -344,332 +385,48 @@ void ipc_client_disconnect(struct ipc_client *client) { | |||
344 | shutdown(client->fd, SHUT_RDWR); | 385 | shutdown(client->fd, SHUT_RDWR); |
345 | } | 386 | } |
346 | 387 | ||
347 | sway_log(L_INFO, "IPC Client %d disconnected", client->fd); | 388 | wlr_log(L_INFO, "IPC Client %d disconnected", client->fd); |
348 | wlc_event_source_remove(client->event_source); | 389 | wl_event_source_remove(client->event_source); |
349 | if (client->writable_event_source) { | 390 | if (client->writable_event_source) { |
350 | wlc_event_source_remove(client->writable_event_source); | 391 | wl_event_source_remove(client->writable_event_source); |
351 | } | 392 | } |
352 | int i = 0; | 393 | int i = 0; |
353 | while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++; | 394 | while (i < ipc_client_list->length && ipc_client_list->items[i] != client) { |
395 | i++; | ||
396 | } | ||
354 | list_del(ipc_client_list, i); | 397 | list_del(ipc_client_list, i); |
355 | free(client->write_buffer); | 398 | free(client->write_buffer); |
356 | close(client->fd); | 399 | close(client->fd); |
357 | free(client); | 400 | free(client); |
358 | } | 401 | } |
359 | 402 | ||
360 | bool output_by_name_test(swayc_t *view, void *data) { | 403 | static void ipc_get_workspaces_callback(struct sway_container *workspace, |
361 | char *name = (char *)data; | 404 | void *data) { |
362 | if (view->type != C_OUTPUT) { | 405 | if (workspace->type != C_WORKSPACE) { |
363 | return false; | ||
364 | } | ||
365 | return !strcmp(name, view->name); | ||
366 | } | ||
367 | |||
368 | void ipc_get_pixels(wlc_handle output) { | ||
369 | if (ipc_get_pixel_requests->length == 0) { | ||
370 | return; | 406 | return; |
371 | } | 407 | } |
372 | 408 | json_object *workspace_json = ipc_json_describe_container(workspace); | |
373 | list_t *unhandled = create_list(); | 409 | // override the default focused indicator because |
374 | 410 | // it's set differently for the get_workspaces reply | |
375 | struct get_pixels_request *req; | 411 | struct sway_seat *seat = |
376 | int i; | 412 | input_manager_get_default_seat(input_manager); |
377 | for (i = 0; i < ipc_get_pixel_requests->length; ++i) { | 413 | struct sway_container *focused_ws = seat_get_focus(seat); |
378 | req = ipc_get_pixel_requests->items[i]; | 414 | if (focused_ws != NULL && focused_ws->type != C_WORKSPACE) { |
379 | if (req->output != output) { | 415 | focused_ws = container_parent(focused_ws, C_WORKSPACE); |
380 | list_add(unhandled, req); | 416 | } |
381 | continue; | 417 | bool focused = workspace == focused_ws; |
382 | } | 418 | json_object_object_del(workspace_json, "focused"); |
383 | 419 | json_object_object_add(workspace_json, "focused", | |
384 | const struct wlc_size *size = &req->geo.size; | 420 | json_object_new_boolean(focused)); |
385 | struct wlc_geometry g_out; | 421 | json_object_array_add((json_object *)data, workspace_json); |
386 | char response_header[9]; | 422 | |
387 | memset(response_header, 0, sizeof(response_header)); | 423 | focused_ws = seat_get_focus_inactive(seat, workspace->parent); |
388 | char *data = malloc(sizeof(response_header) + size->w * size->h * 4); | 424 | if (focused_ws->type != C_WORKSPACE) { |
389 | if (!data) { | 425 | focused_ws = container_parent(focused_ws, C_WORKSPACE); |
390 | sway_log(L_ERROR, "Unable to allocate pixels for get_pixels"); | 426 | } |
391 | ipc_client_disconnect(req->client); | 427 | bool visible = workspace == focused_ws; |
392 | free(req); | 428 | json_object_object_add(workspace_json, "visible", |
393 | continue; | 429 | json_object_new_boolean(visible)); |
394 | } | ||
395 | wlc_pixels_read(WLC_RGBA8888, &req->geo, &g_out, data + sizeof(response_header)); | ||
396 | |||
397 | response_header[0] = 1; | ||
398 | uint32_t *_size = (uint32_t *)(response_header + 1); | ||
399 | _size[0] = g_out.size.w; | ||
400 | _size[1] = g_out.size.h; | ||
401 | size_t len = sizeof(response_header) + (g_out.size.w * g_out.size.h * 4); | ||
402 | memcpy(data, response_header, sizeof(response_header)); | ||
403 | ipc_send_reply(req->client, data, len); | ||
404 | free(data); | ||
405 | // free the request since it has been handled | ||
406 | free(req); | ||
407 | } | ||
408 | |||
409 | // free old list of pixel requests and set new list to all unhandled | ||
410 | // requests (request for another output). | ||
411 | list_free(ipc_get_pixel_requests); | ||
412 | ipc_get_pixel_requests = unhandled; | ||
413 | } | ||
414 | |||
415 | static bool is_text_target(const char *target) { | ||
416 | return (strncmp(target, "text/", 5) == 0 | ||
417 | || strcmp(target, "UTF8_STRING") == 0 | ||
418 | || strcmp(target, "STRING") == 0 | ||
419 | || strcmp(target, "TEXT") == 0 | ||
420 | || strcmp(target, "COMPOUND_TEXT") == 0); | ||
421 | } | ||
422 | |||
423 | static void release_clipboard_request(struct get_clipboard_request *req) { | ||
424 | if (--(*req->pending) == 0) { | ||
425 | const char *str = json_object_to_json_string(req->json); | ||
426 | ipc_send_reply(req->client, str, (uint32_t)strlen(str)); | ||
427 | json_object_put(req->json); | ||
428 | } | ||
429 | |||
430 | free(req->type); | ||
431 | free(req->buf); | ||
432 | wlc_event_source_remove(req->fd_event_source); | ||
433 | wlc_event_source_remove(req->timer_event_source); | ||
434 | close(req->fd); | ||
435 | free(req); | ||
436 | } | ||
437 | |||
438 | static int ipc_selection_data_cb(int fd, uint32_t mask, void *data) { | ||
439 | assert(data); | ||
440 | struct get_clipboard_request *req = (struct get_clipboard_request *)data; | ||
441 | |||
442 | if (mask & WLC_EVENT_ERROR) { | ||
443 | sway_log(L_ERROR, "Selection data fd error"); | ||
444 | goto error; | ||
445 | } | ||
446 | |||
447 | if (mask & WLC_EVENT_READABLE) { | ||
448 | static const unsigned int max_size = 8192 * 1024; | ||
449 | int amt = 0; | ||
450 | |||
451 | do { | ||
452 | int size = req->buf_size - req->buf_position; | ||
453 | int amt = read(fd, req->buf + req->buf_position, size - 1); | ||
454 | if (amt < 0) { | ||
455 | if (errno == EAGAIN) { | ||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | sway_log_errno(L_INFO, "Failed to read from clipboard data fd"); | ||
460 | goto release; | ||
461 | } | ||
462 | |||
463 | req->buf_position += amt; | ||
464 | if (req->buf_position >= req->buf_size - 1) { | ||
465 | if (req->buf_size >= max_size) { | ||
466 | sway_log(L_ERROR, "get_clipbard: selection data too large"); | ||
467 | goto error; | ||
468 | } | ||
469 | char *next = realloc(req->buf, req->buf_size *= 2); | ||
470 | if (!next) { | ||
471 | sway_log_errno(L_ERROR, "get_clipboard: realloc data buffer failed"); | ||
472 | goto error; | ||
473 | } | ||
474 | |||
475 | req->buf = next; | ||
476 | } | ||
477 | } while(amt != 0); | ||
478 | |||
479 | req->buf[req->buf_position] = '\0'; | ||
480 | |||
481 | json_object *obj = json_object_new_object(); | ||
482 | json_object_object_add(obj, "success", json_object_new_boolean(true)); | ||
483 | if (is_text_target(req->type)) { | ||
484 | json_object_object_add(obj, "content", json_object_new_string(req->buf)); | ||
485 | json_object_object_add(req->json, req->type, obj); | ||
486 | } else { | ||
487 | size_t outlen; | ||
488 | char *b64 = b64_encode(req->buf, req->buf_position, &outlen); | ||
489 | json_object_object_add(obj, "content", json_object_new_string(b64)); | ||
490 | free(b64); | ||
491 | |||
492 | char *type = malloc(strlen(req->type) + 8); | ||
493 | strcat(type, ";base64"); | ||
494 | json_object_object_add(req->json, type, obj); | ||
495 | free(type); | ||
496 | } | ||
497 | } | ||
498 | |||
499 | goto release; | ||
500 | |||
501 | error:; | ||
502 | json_object *obj = json_object_new_object(); | ||
503 | json_object_object_add(obj, "success", json_object_new_boolean(false)); | ||
504 | json_object_object_add(obj, "error", | ||
505 | json_object_new_string("Failed to retrieve data")); | ||
506 | json_object_object_add(req->json, req->type, obj); | ||
507 | |||
508 | release: | ||
509 | release_clipboard_request(req); | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static int ipc_selection_timer_cb(void *data) { | ||
514 | assert(data); | ||
515 | struct get_clipboard_request *req = (struct get_clipboard_request *)data; | ||
516 | |||
517 | sway_log(L_INFO, "get_clipbard: timeout for type %s", req->type); | ||
518 | json_object *obj = json_object_new_object(); | ||
519 | json_object_object_add(obj, "success", json_object_new_boolean(false)); | ||
520 | json_object_object_add(obj, "error", json_object_new_string("Timeout")); | ||
521 | json_object_object_add(req->json, req->type, obj); | ||
522 | |||
523 | release_clipboard_request(req); | ||
524 | return 0; | ||
525 | } | ||
526 | |||
527 | // greedy wildcard (only "*") matching | ||
528 | bool mime_type_matches(const char *mime_type, const char *pattern) { | ||
529 | const char *wildcard = NULL; | ||
530 | while (*mime_type && *pattern) { | ||
531 | if (*pattern == '*' && !wildcard) { | ||
532 | wildcard = pattern; | ||
533 | ++pattern; | ||
534 | } | ||
535 | |||
536 | if (*mime_type != *pattern) { | ||
537 | if (!wildcard) | ||
538 | return false; | ||
539 | |||
540 | pattern = wildcard; | ||
541 | ++mime_type; | ||
542 | continue; | ||
543 | } | ||
544 | |||
545 | ++mime_type; | ||
546 | ++pattern; | ||
547 | } | ||
548 | |||
549 | while (*pattern == '*') { | ||
550 | ++pattern; | ||
551 | } | ||
552 | |||
553 | return (*mime_type == *pattern); | ||
554 | } | ||
555 | |||
556 | void ipc_get_clipboard(struct ipc_client *client, char *buf) { | ||
557 | size_t size; | ||
558 | const char **types = wlc_get_selection_types(&size); | ||
559 | if (client->payload_length == 0) { | ||
560 | json_object *obj = json_object_new_array(); | ||
561 | for (size_t i = 0; i < size; ++i) { | ||
562 | json_object_array_add(obj, json_object_new_string(types[i])); | ||
563 | } | ||
564 | |||
565 | const char *str = json_object_to_json_string(obj); | ||
566 | ipc_send_reply(client, str, strlen(str)); | ||
567 | json_object_put(obj); | ||
568 | return; | ||
569 | } | ||
570 | |||
571 | unescape_string(buf); | ||
572 | strip_quotes(buf); | ||
573 | list_t *requested = split_string(buf, " "); | ||
574 | json_object *json = json_object_new_object(); | ||
575 | unsigned int *pending = malloc(sizeof(unsigned int)); | ||
576 | *pending = 0; | ||
577 | |||
578 | for (size_t l = 0; l < (size_t) requested->length; ++l) { | ||
579 | const char *pattern = requested->items[l]; | ||
580 | bool found = false; | ||
581 | for (size_t i = 0; i < size; ++i) { | ||
582 | if (!mime_type_matches(types[i], pattern)) { | ||
583 | continue; | ||
584 | } | ||
585 | |||
586 | found = true; | ||
587 | |||
588 | struct get_clipboard_request *req = malloc(sizeof(*req)); | ||
589 | if (!req) { | ||
590 | sway_log(L_ERROR, "get_clipboard: request malloc failed"); | ||
591 | goto data_error; | ||
592 | } | ||
593 | |||
594 | int pipes[2]; | ||
595 | if (pipe(pipes) == -1) { | ||
596 | sway_log_errno(L_ERROR, "get_clipboard: pipe call failed"); | ||
597 | free(req); | ||
598 | goto data_error; | ||
599 | } | ||
600 | |||
601 | fcntl(pipes[0], F_SETFD, FD_CLOEXEC | O_NONBLOCK); | ||
602 | fcntl(pipes[1], F_SETFD, FD_CLOEXEC | O_NONBLOCK); | ||
603 | |||
604 | if (!wlc_get_selection_data(types[i], pipes[1])) { | ||
605 | close(pipes[0]); | ||
606 | close(pipes[1]); | ||
607 | free(req); | ||
608 | sway_log(L_ERROR, "get_clipboard: failed to retrieve " | ||
609 | "selection data"); | ||
610 | goto data_error; | ||
611 | } | ||
612 | |||
613 | if (!(req->buf = malloc(512))) { | ||
614 | close(pipes[0]); | ||
615 | close(pipes[1]); | ||
616 | free(req); | ||
617 | sway_log_errno(L_ERROR, "get_clipboard: buf malloc failed"); | ||
618 | goto data_error; | ||
619 | } | ||
620 | |||
621 | (*pending)++; | ||
622 | |||
623 | req->client = client; | ||
624 | req->type = strdup(types[i]); | ||
625 | req->json = json; | ||
626 | req->pending = pending; | ||
627 | req->buf_position = 0; | ||
628 | req->buf_size = 512; | ||
629 | req->fd = pipes[0]; | ||
630 | req->timer_event_source = wlc_event_loop_add_timer(ipc_selection_timer_cb, req); | ||
631 | req->fd_event_source = wlc_event_loop_add_fd(pipes[0], | ||
632 | WLC_EVENT_READABLE | WLC_EVENT_ERROR | WLC_EVENT_HANGUP, | ||
633 | &ipc_selection_data_cb, req); | ||
634 | |||
635 | wlc_event_source_timer_update(req->timer_event_source, 30000); | ||
636 | |||
637 | // NOTE: remove this goto to enable retrieving multiple | ||
638 | // targets at once. The whole implementation is already | ||
639 | // made for it. The only reason it was disabled | ||
640 | // at the time of writing is that neither wlc's xselection | ||
641 | // implementation nor (apparently) gtk on wayland supports | ||
642 | // multiple send requests at the same time which makes | ||
643 | // every request except the last one fail (and therefore | ||
644 | // return empty data) | ||
645 | goto cleanup; | ||
646 | } | ||
647 | |||
648 | if (!found) { | ||
649 | sway_log(L_INFO, "Invalid clipboard type %s requested", pattern); | ||
650 | } | ||
651 | } | ||
652 | |||
653 | if (*pending == 0) { | ||
654 | static const char *error_empty = "{ \"success\": false, \"error\": " | ||
655 | "\"No matching types found\" }"; | ||
656 | ipc_send_reply(client, error_empty, (uint32_t)strlen(error_empty)); | ||
657 | free(json); | ||
658 | free(pending); | ||
659 | } | ||
660 | |||
661 | goto cleanup; | ||
662 | |||
663 | data_error:; | ||
664 | static const char *error_json = "{ \"success\": false, \"error\": " | ||
665 | "\"Failed to create clipboard data request\" }"; | ||
666 | ipc_send_reply(client, error_json, (uint32_t)strlen(error_json)); | ||
667 | free(json); | ||
668 | free(pending); | ||
669 | |||
670 | cleanup: | ||
671 | list_free(requested); | ||
672 | free(types); | ||
673 | } | 430 | } |
674 | 431 | ||
675 | void ipc_client_handle_command(struct ipc_client *client) { | 432 | void ipc_client_handle_command(struct ipc_client *client) { |
@@ -679,7 +436,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
679 | 436 | ||
680 | char *buf = malloc(client->payload_length + 1); | 437 | char *buf = malloc(client->payload_length + 1); |
681 | if (!buf) { | 438 | if (!buf) { |
682 | sway_log_errno(L_INFO, "Unable to allocate IPC payload"); | 439 | wlr_log_errno(L_INFO, "Unable to allocate IPC payload"); |
683 | ipc_client_disconnect(client); | 440 | ipc_client_disconnect(client); |
684 | return; | 441 | return; |
685 | } | 442 | } |
@@ -688,7 +445,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
688 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); | 445 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); |
689 | if (received == -1) | 446 | if (received == -1) |
690 | { | 447 | { |
691 | sway_log_errno(L_INFO, "Unable to receive payload from IPC client"); | 448 | wlr_log_errno(L_INFO, "Unable to receive payload from IPC client"); |
692 | ipc_client_disconnect(client); | 449 | ipc_client_disconnect(client); |
693 | free(buf); | 450 | free(buf); |
694 | return; | 451 | return; |
@@ -701,10 +458,8 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
701 | switch (client->current_command) { | 458 | switch (client->current_command) { |
702 | case IPC_COMMAND: | 459 | case IPC_COMMAND: |
703 | { | 460 | { |
704 | if (!(client->security_policy & IPC_FEATURE_COMMAND)) { | 461 | config_clear_handler_context(config); |
705 | goto exit_denied; | 462 | struct cmd_results *results = execute_command(buf, NULL); |
706 | } | ||
707 | struct cmd_results *results = handle_command(buf, CONTEXT_IPC); | ||
708 | const char *json = cmd_results_to_json(results); | 463 | const char *json = cmd_results_to_json(results); |
709 | char reply[256]; | 464 | char reply[256]; |
710 | int length = snprintf(reply, sizeof(reply), "%s", json); | 465 | int length = snprintf(reply, sizeof(reply), "%s", json); |
@@ -713,18 +468,45 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
713 | goto exit_cleanup; | 468 | goto exit_cleanup; |
714 | } | 469 | } |
715 | 470 | ||
471 | case IPC_GET_OUTPUTS: | ||
472 | { | ||
473 | json_object *outputs = json_object_new_array(); | ||
474 | for (int i = 0; i < root_container.children->length; ++i) { | ||
475 | struct sway_container *container = root_container.children->items[i]; | ||
476 | if (container->type == C_OUTPUT) { | ||
477 | json_object_array_add(outputs, | ||
478 | ipc_json_describe_container(container)); | ||
479 | } | ||
480 | } | ||
481 | const char *json_string = json_object_to_json_string(outputs); | ||
482 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
483 | json_object_put(outputs); // free | ||
484 | goto exit_cleanup; | ||
485 | } | ||
486 | |||
487 | case IPC_GET_WORKSPACES: | ||
488 | { | ||
489 | json_object *workspaces = json_object_new_array(); | ||
490 | container_for_each_descendant_dfs(&root_container, | ||
491 | ipc_get_workspaces_callback, workspaces); | ||
492 | const char *json_string = json_object_to_json_string(workspaces); | ||
493 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
494 | json_object_put(workspaces); // free | ||
495 | goto exit_cleanup; | ||
496 | } | ||
497 | |||
716 | case IPC_SUBSCRIBE: | 498 | case IPC_SUBSCRIBE: |
717 | { | 499 | { |
718 | // TODO: Check if they're permitted to use these events | 500 | // TODO: Check if they're permitted to use these events |
719 | struct json_object *request = json_tokener_parse(buf); | 501 | struct json_object *request = json_tokener_parse(buf); |
720 | if (request == NULL) { | 502 | if (request == NULL) { |
721 | ipc_send_reply(client, "{\"success\": false}", 18); | 503 | ipc_send_reply(client, "{\"success\": false}", 18); |
722 | sway_log_errno(L_INFO, "Failed to read request"); | 504 | wlr_log_errno(L_INFO, "Failed to read request"); |
723 | goto exit_cleanup; | 505 | goto exit_cleanup; |
724 | } | 506 | } |
725 | 507 | ||
726 | // parse requested event types | 508 | // parse requested event types |
727 | for (int i = 0; i < json_object_array_length(request); i++) { | 509 | for (size_t i = 0; i < json_object_array_length(request); i++) { |
728 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); | 510 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); |
729 | if (strcmp(event_type, "workspace") == 0) { | 511 | if (strcmp(event_type, "workspace") == 0) { |
730 | client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); | 512 | client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); |
@@ -741,86 +523,39 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
741 | } else { | 523 | } else { |
742 | ipc_send_reply(client, "{\"success\": false}", 18); | 524 | ipc_send_reply(client, "{\"success\": false}", 18); |
743 | json_object_put(request); | 525 | json_object_put(request); |
744 | sway_log_errno(L_INFO, "Failed to parse request"); | 526 | wlr_log_errno(L_INFO, "Failed to parse request"); |
745 | goto exit_cleanup; | 527 | goto exit_cleanup; |
746 | } | 528 | } |
747 | } | 529 | } |
748 | 530 | ||
749 | json_object_put(request); | 531 | json_object_put(request); |
750 | |||
751 | ipc_send_reply(client, "{\"success\": true}", 17); | 532 | ipc_send_reply(client, "{\"success\": true}", 17); |
752 | goto exit_cleanup; | 533 | goto exit_cleanup; |
753 | } | 534 | } |
754 | 535 | ||
755 | case IPC_GET_WORKSPACES: | ||
756 | { | ||
757 | if (!(client->security_policy & IPC_FEATURE_GET_WORKSPACES)) { | ||
758 | goto exit_denied; | ||
759 | } | ||
760 | json_object *workspaces = json_object_new_array(); | ||
761 | container_map(&root_container, ipc_get_workspaces_callback, workspaces); | ||
762 | const char *json_string = json_object_to_json_string(workspaces); | ||
763 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
764 | json_object_put(workspaces); // free | ||
765 | goto exit_cleanup; | ||
766 | } | ||
767 | |||
768 | case IPC_GET_INPUTS: | 536 | case IPC_GET_INPUTS: |
769 | { | 537 | { |
770 | if (!(client->security_policy & IPC_FEATURE_GET_INPUTS)) { | ||
771 | goto exit_denied; | ||
772 | } | ||
773 | json_object *inputs = json_object_new_array(); | 538 | json_object *inputs = json_object_new_array(); |
774 | if (input_devices) { | 539 | struct sway_input_device *device = NULL; |
775 | for(int i = 0; i<input_devices->length; i++) { | 540 | wl_list_for_each(device, &input_manager->devices, link) { |
776 | struct libinput_device *device = input_devices->items[i]; | 541 | json_object_array_add(inputs, ipc_json_describe_input(device)); |
777 | json_object_array_add(inputs, ipc_json_describe_input(device)); | ||
778 | } | ||
779 | } | 542 | } |
780 | const char *json_string = json_object_to_json_string(inputs); | 543 | const char *json_string = json_object_to_json_string(inputs); |
781 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | 544 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); |
782 | json_object_put(inputs); | 545 | json_object_put(inputs); // free |
783 | goto exit_cleanup; | ||
784 | } | ||
785 | |||
786 | case IPC_GET_OUTPUTS: | ||
787 | { | ||
788 | if (!(client->security_policy & IPC_FEATURE_GET_OUTPUTS)) { | ||
789 | goto exit_denied; | ||
790 | } | ||
791 | json_object *outputs = json_object_new_array(); | ||
792 | container_map(&root_container, ipc_get_outputs_callback, outputs); | ||
793 | const char *json_string = json_object_to_json_string(outputs); | ||
794 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
795 | json_object_put(outputs); // free | ||
796 | goto exit_cleanup; | 546 | goto exit_cleanup; |
797 | } | 547 | } |
798 | 548 | ||
799 | case IPC_GET_TREE: | 549 | case IPC_GET_TREE: |
800 | { | 550 | { |
801 | if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { | 551 | json_object *tree = |
802 | goto exit_denied; | 552 | ipc_json_describe_container_recursive(&root_container); |
803 | } | ||
804 | json_object *tree = ipc_json_describe_container_recursive(&root_container); | ||
805 | const char *json_string = json_object_to_json_string(tree); | 553 | const char *json_string = json_object_to_json_string(tree); |
806 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | 554 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); |
807 | json_object_put(tree); | 555 | json_object_put(tree); |
808 | goto exit_cleanup; | 556 | goto exit_cleanup; |
809 | } | 557 | } |
810 | 558 | ||
811 | case IPC_GET_MARKS: | ||
812 | { | ||
813 | if (!(client->security_policy & IPC_FEATURE_GET_MARKS)) { | ||
814 | goto exit_denied; | ||
815 | } | ||
816 | json_object *marks = json_object_new_array(); | ||
817 | container_map(&root_container, ipc_get_marks_callback, marks); | ||
818 | const char *json_string = json_object_to_json_string(marks); | ||
819 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
820 | json_object_put(marks); | ||
821 | goto exit_cleanup; | ||
822 | } | ||
823 | |||
824 | case IPC_GET_VERSION: | 559 | case IPC_GET_VERSION: |
825 | { | 560 | { |
826 | json_object *version = ipc_json_get_version(); | 561 | json_object *version = ipc_json_get_version(); |
@@ -830,62 +565,12 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
830 | goto exit_cleanup; | 565 | goto exit_cleanup; |
831 | } | 566 | } |
832 | 567 | ||
833 | case IPC_SWAY_GET_PIXELS: | ||
834 | { | ||
835 | char response_header[9]; | ||
836 | memset(response_header, 0, sizeof(response_header)); | ||
837 | |||
838 | json_object *obj = json_tokener_parse(buf); | ||
839 | json_object *o, *x, *y, *w, *h; | ||
840 | |||
841 | json_object_object_get_ex(obj, "output", &o); | ||
842 | json_object_object_get_ex(obj, "x", &x); | ||
843 | json_object_object_get_ex(obj, "y", &y); | ||
844 | json_object_object_get_ex(obj, "w", &w); | ||
845 | json_object_object_get_ex(obj, "h", &h); | ||
846 | |||
847 | struct wlc_geometry g = { | ||
848 | .origin = { | ||
849 | .x = json_object_get_int(x), | ||
850 | .y = json_object_get_int(y) | ||
851 | }, | ||
852 | .size = { | ||
853 | .w = json_object_get_int(w), | ||
854 | .h = json_object_get_int(h) | ||
855 | } | ||
856 | }; | ||
857 | |||
858 | swayc_t *output = swayc_by_test(&root_container, output_by_name_test, (void *)json_object_get_string(o)); | ||
859 | json_object_put(obj); | ||
860 | |||
861 | if (!output) { | ||
862 | sway_log(L_ERROR, "IPC GET_PIXELS request with unknown output name"); | ||
863 | ipc_send_reply(client, response_header, sizeof(response_header)); | ||
864 | goto exit_cleanup; | ||
865 | } | ||
866 | struct get_pixels_request *req = malloc(sizeof(struct get_pixels_request)); | ||
867 | if (!req) { | ||
868 | sway_log(L_ERROR, "Unable to allocate get_pixels request"); | ||
869 | goto exit_cleanup; | ||
870 | } | ||
871 | req->client = client; | ||
872 | req->output = output->handle; | ||
873 | req->geo = g; | ||
874 | list_add(ipc_get_pixel_requests, req); | ||
875 | wlc_output_schedule_render(output->handle); | ||
876 | goto exit_cleanup; | ||
877 | } | ||
878 | |||
879 | case IPC_GET_BAR_CONFIG: | 568 | case IPC_GET_BAR_CONFIG: |
880 | { | 569 | { |
881 | if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) { | ||
882 | goto exit_denied; | ||
883 | } | ||
884 | if (!buf[0]) { | 570 | if (!buf[0]) { |
885 | // Send list of configured bar IDs | 571 | // Send list of configured bar IDs |
886 | json_object *bars = json_object_new_array(); | 572 | json_object *bars = json_object_new_array(); |
887 | int i; | 573 | for (int i = 0; i < config->bars->length; ++i) { |
888 | for (i = 0; i < config->bars->length; ++i) { | ||
889 | struct bar_config *bar = config->bars->items[i]; | 574 | struct bar_config *bar = config->bars->items[i]; |
890 | json_object_array_add(bars, json_object_new_string(bar->id)); | 575 | json_object_array_add(bars, json_object_new_string(bar->id)); |
891 | } | 576 | } |
@@ -895,8 +580,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
895 | } else { | 580 | } else { |
896 | // Send particular bar's details | 581 | // Send particular bar's details |
897 | struct bar_config *bar = NULL; | 582 | struct bar_config *bar = NULL; |
898 | int i; | 583 | for (int i = 0; i < config->bars->length; ++i) { |
899 | for (i = 0; i < config->bars->length; ++i) { | ||
900 | bar = config->bars->items[i]; | 584 | bar = config->bars->items[i]; |
901 | if (strcmp(buf, bar->id) == 0) { | 585 | if (strcmp(buf, bar->id) == 0) { |
902 | break; | 586 | break; |
@@ -916,24 +600,13 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
916 | goto exit_cleanup; | 600 | goto exit_cleanup; |
917 | } | 601 | } |
918 | 602 | ||
919 | case IPC_GET_CLIPBOARD: | ||
920 | { | ||
921 | if (!(client->security_policy & IPC_FEATURE_GET_CLIPBOARD)) { | ||
922 | goto exit_denied; | ||
923 | } | ||
924 | |||
925 | ipc_get_clipboard(client, buf); | ||
926 | goto exit_cleanup; | ||
927 | } | ||
928 | |||
929 | default: | 603 | default: |
930 | sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); | 604 | wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command); |
931 | goto exit_cleanup; | 605 | goto exit_cleanup; |
932 | } | 606 | } |
933 | 607 | ||
934 | exit_denied: | ||
935 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); | 608 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); |
936 | sway_log(L_DEBUG, "Denied IPC client access to %i", client->current_command); | 609 | wlr_log(L_DEBUG, "Denied IPC client access to %i", client->current_command); |
937 | 610 | ||
938 | exit_cleanup: | 611 | exit_cleanup: |
939 | client->payload_length = 0; | 612 | client->payload_length = 0; |
@@ -956,16 +629,15 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay | |||
956 | client->write_buffer_size *= 2; | 629 | client->write_buffer_size *= 2; |
957 | } | 630 | } |
958 | 631 | ||
959 | // TODO: reduce the limit back to 4 MB when screenshooter is implemented | 632 | if (client->write_buffer_size > 4e6) { // 4 MB |
960 | if (client->write_buffer_size > (1 << 28)) { // 256 MB | 633 | wlr_log(L_ERROR, "Client write buffer too big, disconnecting client"); |
961 | sway_log(L_ERROR, "Client write buffer too big, disconnecting client"); | ||
962 | ipc_client_disconnect(client); | 634 | ipc_client_disconnect(client); |
963 | return false; | 635 | return false; |
964 | } | 636 | } |
965 | 637 | ||
966 | char *new_buffer = realloc(client->write_buffer, client->write_buffer_size); | 638 | char *new_buffer = realloc(client->write_buffer, client->write_buffer_size); |
967 | if (!new_buffer) { | 639 | if (!new_buffer) { |
968 | sway_log(L_ERROR, "Unable to reallocate ipc client write buffer"); | 640 | wlr_log(L_ERROR, "Unable to reallocate ipc client write buffer"); |
969 | ipc_client_disconnect(client); | 641 | ipc_client_disconnect(client); |
970 | return false; | 642 | return false; |
971 | } | 643 | } |
@@ -977,212 +649,11 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay | |||
977 | client->write_buffer_len += payload_length; | 649 | client->write_buffer_len += payload_length; |
978 | 650 | ||
979 | if (!client->writable_event_source) { | 651 | if (!client->writable_event_source) { |
980 | client->writable_event_source = wlc_event_loop_add_fd(client->fd, WLC_EVENT_WRITABLE, ipc_client_handle_writable, client); | 652 | client->writable_event_source = wl_event_loop_add_fd( |
653 | server.wl_event_loop, client->fd, WL_EVENT_WRITABLE, | ||
654 | ipc_client_handle_writable, client); | ||
981 | } | 655 | } |
982 | 656 | ||
983 | sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); | 657 | wlr_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); |
984 | |||
985 | return true; | 658 | return true; |
986 | } | 659 | } |
987 | |||
988 | void ipc_get_workspaces_callback(swayc_t *workspace, void *data) { | ||
989 | if (workspace->type == C_WORKSPACE) { | ||
990 | json_object *workspace_json = ipc_json_describe_container(workspace); | ||
991 | // override the default focused indicator because | ||
992 | // it's set differently for the get_workspaces reply | ||
993 | bool focused = root_container.focused == workspace->parent && workspace->parent->focused == workspace; | ||
994 | json_object_object_del(workspace_json, "focused"); | ||
995 | json_object_object_add(workspace_json, "focused", json_object_new_boolean(focused)); | ||
996 | json_object_array_add((json_object *)data, workspace_json); | ||
997 | } | ||
998 | } | ||
999 | |||
1000 | void ipc_get_outputs_callback(swayc_t *container, void *data) { | ||
1001 | if (container->type == C_OUTPUT) { | ||
1002 | json_object_array_add((json_object *)data, ipc_json_describe_container(container)); | ||
1003 | } | ||
1004 | } | ||
1005 | |||
1006 | static void ipc_get_marks_callback(swayc_t *container, void *data) { | ||
1007 | json_object *object = (json_object *)data; | ||
1008 | if (container->marks) { | ||
1009 | for (int i = 0; i < container->marks->length; ++i) { | ||
1010 | char *mark = (char *)container->marks->items[i]; | ||
1011 | json_object_array_add(object, json_object_new_string(mark)); | ||
1012 | } | ||
1013 | } | ||
1014 | } | ||
1015 | |||
1016 | void ipc_send_event(const char *json_string, enum ipc_command_type event) { | ||
1017 | static struct { | ||
1018 | enum ipc_command_type event; | ||
1019 | enum ipc_feature feature; | ||
1020 | } security_mappings[] = { | ||
1021 | { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE }, | ||
1022 | { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT }, | ||
1023 | { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE }, | ||
1024 | { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW }, | ||
1025 | { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING }, | ||
1026 | { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT } | ||
1027 | }; | ||
1028 | |||
1029 | uint32_t security_mask = 0; | ||
1030 | for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) { | ||
1031 | if (security_mappings[i].event == event) { | ||
1032 | security_mask = security_mappings[i].feature; | ||
1033 | break; | ||
1034 | } | ||
1035 | } | ||
1036 | |||
1037 | int i; | ||
1038 | struct ipc_client *client; | ||
1039 | for (i = 0; i < ipc_client_list->length; i++) { | ||
1040 | client = ipc_client_list->items[i]; | ||
1041 | if (!(client->security_policy & security_mask)) { | ||
1042 | continue; | ||
1043 | } | ||
1044 | if ((client->subscribed_events & event_mask(event)) == 0) { | ||
1045 | continue; | ||
1046 | } | ||
1047 | client->current_command = event; | ||
1048 | if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { | ||
1049 | sway_log_errno(L_INFO, "Unable to send reply to IPC client"); | ||
1050 | ipc_client_disconnect(client); | ||
1051 | } | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) { | ||
1056 | sway_log(L_DEBUG, "Sending workspace::%s event", change); | ||
1057 | json_object *obj = json_object_new_object(); | ||
1058 | json_object_object_add(obj, "change", json_object_new_string(change)); | ||
1059 | if (strcmp("focus", change) == 0) { | ||
1060 | if (old) { | ||
1061 | json_object_object_add(obj, "old", ipc_json_describe_container_recursive(old)); | ||
1062 | } else { | ||
1063 | json_object_object_add(obj, "old", NULL); | ||
1064 | } | ||
1065 | } | ||
1066 | |||
1067 | if (new) { | ||
1068 | json_object_object_add(obj, "current", ipc_json_describe_container_recursive(new)); | ||
1069 | } else { | ||
1070 | json_object_object_add(obj, "current", NULL); | ||
1071 | } | ||
1072 | |||
1073 | const char *json_string = json_object_to_json_string(obj); | ||
1074 | ipc_send_event(json_string, IPC_EVENT_WORKSPACE); | ||
1075 | |||
1076 | json_object_put(obj); // free | ||
1077 | } | ||
1078 | |||
1079 | void ipc_event_window(swayc_t *window, const char *change) { | ||
1080 | sway_log(L_DEBUG, "Sending window::%s event", change); | ||
1081 | json_object *obj = json_object_new_object(); | ||
1082 | json_object_object_add(obj, "change", json_object_new_string(change)); | ||
1083 | json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); | ||
1084 | |||
1085 | const char *json_string = json_object_to_json_string(obj); | ||
1086 | ipc_send_event(json_string, IPC_EVENT_WINDOW); | ||
1087 | |||
1088 | json_object_put(obj); // free | ||
1089 | } | ||
1090 | |||
1091 | void ipc_event_barconfig_update(struct bar_config *bar) { | ||
1092 | sway_log(L_DEBUG, "Sending barconfig_update event"); | ||
1093 | json_object *json = ipc_json_describe_bar_config(bar); | ||
1094 | const char *json_string = json_object_to_json_string(json); | ||
1095 | ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE); | ||
1096 | |||
1097 | json_object_put(json); // free | ||
1098 | } | ||
1099 | |||
1100 | void ipc_event_mode(const char *mode) { | ||
1101 | sway_log(L_DEBUG, "Sending mode::%s event", mode); | ||
1102 | json_object *obj = json_object_new_object(); | ||
1103 | json_object_object_add(obj, "change", json_object_new_string(mode)); | ||
1104 | |||
1105 | const char *json_string = json_object_to_json_string(obj); | ||
1106 | ipc_send_event(json_string, IPC_EVENT_MODE); | ||
1107 | |||
1108 | json_object_put(obj); // free | ||
1109 | } | ||
1110 | |||
1111 | void ipc_event_modifier(uint32_t modifier, const char *state) { | ||
1112 | sway_log(L_DEBUG, "Sending modifier::%s event", state); | ||
1113 | json_object *obj = json_object_new_object(); | ||
1114 | json_object_object_add(obj, "change", json_object_new_string(state)); | ||
1115 | |||
1116 | const char *modifier_name = get_modifier_name_by_mask(modifier); | ||
1117 | json_object_object_add(obj, "modifier", json_object_new_string(modifier_name)); | ||
1118 | |||
1119 | const char *json_string = json_object_to_json_string(obj); | ||
1120 | ipc_send_event(json_string, IPC_EVENT_MODIFIER); | ||
1121 | |||
1122 | json_object_put(obj); // free | ||
1123 | } | ||
1124 | |||
1125 | static void ipc_event_binding(json_object *sb_obj) { | ||
1126 | sway_log(L_DEBUG, "Sending binding::run event"); | ||
1127 | json_object *obj = json_object_new_object(); | ||
1128 | json_object_object_add(obj, "change", json_object_new_string("run")); | ||
1129 | json_object_object_add(obj, "binding", sb_obj); | ||
1130 | |||
1131 | const char *json_string = json_object_to_json_string(obj); | ||
1132 | ipc_send_event(json_string, IPC_EVENT_BINDING); | ||
1133 | |||
1134 | json_object_put(obj); // free | ||
1135 | } | ||
1136 | |||
1137 | void ipc_event_binding_keyboard(struct sway_binding *sb) { | ||
1138 | json_object *sb_obj = json_object_new_object(); | ||
1139 | json_object_object_add(sb_obj, "command", json_object_new_string(sb->command)); | ||
1140 | |||
1141 | const char *names[10]; | ||
1142 | |||
1143 | int len = get_modifier_names(names, sb->modifiers); | ||
1144 | int i; | ||
1145 | json_object *modifiers = json_object_new_array(); | ||
1146 | for (i = 0; i < len; ++i) { | ||
1147 | json_object_array_add(modifiers, json_object_new_string(names[i])); | ||
1148 | } | ||
1149 | |||
1150 | json_object_object_add(sb_obj, "event_state_mask", modifiers); | ||
1151 | |||
1152 | json_object *input_codes = json_object_new_array(); | ||
1153 | int input_code = 0; | ||
1154 | json_object *symbols = json_object_new_array(); | ||
1155 | json_object *symbol = NULL; | ||
1156 | |||
1157 | if (sb->bindcode) { // bindcode: populate input_codes | ||
1158 | uint32_t keycode; | ||
1159 | for (i = 0; i < sb->keys->length; ++i) { | ||
1160 | keycode = *(uint32_t *)sb->keys->items[i]; | ||
1161 | json_object_array_add(input_codes, json_object_new_int(keycode)); | ||
1162 | if (i == 0) { | ||
1163 | input_code = keycode; | ||
1164 | } | ||
1165 | } | ||
1166 | } else { // bindsym: populate symbols | ||
1167 | uint32_t keysym; | ||
1168 | char buffer[64]; | ||
1169 | for (i = 0; i < sb->keys->length; ++i) { | ||
1170 | keysym = *(uint32_t *)sb->keys->items[i]; | ||
1171 | if (xkb_keysym_get_name(keysym, buffer, 64) > 0) { | ||
1172 | json_object *str = json_object_new_string(buffer); | ||
1173 | json_object_array_add(symbols, str); | ||
1174 | if (i == 0) { | ||
1175 | symbol = str; | ||
1176 | } | ||
1177 | } | ||
1178 | } | ||
1179 | } | ||
1180 | |||
1181 | json_object_object_add(sb_obj, "input_codes", input_codes); | ||
1182 | json_object_object_add(sb_obj, "input_code", json_object_new_int(input_code)); | ||
1183 | json_object_object_add(sb_obj, "symbols", symbols); | ||
1184 | json_object_object_add(sb_obj, "symbol", symbol); | ||
1185 | json_object_object_add(sb_obj, "input_type", json_object_new_string("keyboard")); | ||
1186 | |||
1187 | ipc_event_binding(sb_obj); | ||
1188 | } | ||
diff --git a/sway/layout.c b/sway/layout.c deleted file mode 100644 index 69291daf..00000000 --- a/sway/layout.c +++ /dev/null | |||
@@ -1,1770 +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/extensions.h" | ||
7 | #include "sway/config.h" | ||
8 | #include "sway/container.h" | ||
9 | #include "sway/workspace.h" | ||
10 | #include "sway/focus.h" | ||
11 | #include "sway/output.h" | ||
12 | #include "sway/ipc-server.h" | ||
13 | #include "sway/border.h" | ||
14 | #include "sway/layout.h" | ||
15 | #include "list.h" | ||
16 | #include "log.h" | ||
17 | |||
18 | swayc_t root_container; | ||
19 | swayc_t *current_focus; | ||
20 | list_t *scratchpad; | ||
21 | |||
22 | int min_sane_h = 60; | ||
23 | int min_sane_w = 100; | ||
24 | |||
25 | void init_layout(void) { | ||
26 | root_container.id = 0; // normally assigned in new_swayc() | ||
27 | root_container.type = C_ROOT; | ||
28 | root_container.layout = L_NONE; | ||
29 | root_container.name = strdup("root"); | ||
30 | root_container.children = create_list(); | ||
31 | root_container.handle = -1; | ||
32 | root_container.visible = true; | ||
33 | current_focus = &root_container; | ||
34 | scratchpad = create_list(); | ||
35 | } | ||
36 | |||
37 | int index_child(const swayc_t *child) { | ||
38 | swayc_t *parent = child->parent; | ||
39 | int i, len; | ||
40 | if (!child->is_floating) { | ||
41 | len = parent->children->length; | ||
42 | for (i = 0; i < len; ++i) { | ||
43 | if (parent->children->items[i] == child) { | ||
44 | break; | ||
45 | } | ||
46 | } | ||
47 | } else { | ||
48 | len = parent->floating->length; | ||
49 | for (i = 0; i < len; ++i) { | ||
50 | if (parent->floating->items[i] == child) { | ||
51 | break; | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | if (!sway_assert(i < len, "Stray container")) { | ||
56 | return -1; | ||
57 | } | ||
58 | return i; | ||
59 | } | ||
60 | |||
61 | void add_child(swayc_t *parent, swayc_t *child) { | ||
62 | sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type, | ||
63 | child->width, child->height, parent, parent->type, parent->width, parent->height); | ||
64 | list_add(parent->children, child); | ||
65 | child->parent = parent; | ||
66 | // set focus for this container | ||
67 | if (!parent->focused) { | ||
68 | parent->focused = child; | ||
69 | } | ||
70 | if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) { | ||
71 | child = new_container(child, parent->workspace_layout); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | static double *get_height(swayc_t *cont) { | ||
76 | return &cont->height; | ||
77 | } | ||
78 | |||
79 | static double *get_width(swayc_t *cont) { | ||
80 | return &cont->width; | ||
81 | } | ||
82 | |||
83 | void insert_child(swayc_t *parent, swayc_t *child, int index) { | ||
84 | if (index > parent->children->length) { | ||
85 | index = parent->children->length; | ||
86 | } | ||
87 | if (index < 0) { | ||
88 | index = 0; | ||
89 | } | ||
90 | list_insert(parent->children, index, child); | ||
91 | child->parent = parent; | ||
92 | if (!parent->focused) { | ||
93 | parent->focused = child; | ||
94 | } | ||
95 | if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) { | ||
96 | child = new_container(child, parent->workspace_layout); | ||
97 | } | ||
98 | if (is_auto_layout(parent->layout)) { | ||
99 | /* go through each group, adjust the size of the first child of each group */ | ||
100 | double *(*get_maj_dim)(swayc_t *cont); | ||
101 | double *(*get_min_dim)(swayc_t *cont); | ||
102 | if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) { | ||
103 | get_maj_dim = get_width; | ||
104 | get_min_dim = get_height; | ||
105 | } else { | ||
106 | get_maj_dim = get_height; | ||
107 | get_min_dim = get_width; | ||
108 | } | ||
109 | for (int i = index; i < parent->children->length;) { | ||
110 | int start = auto_group_start_index(parent, i); | ||
111 | int end = auto_group_end_index(parent, i); | ||
112 | swayc_t *first = parent->children->items[start]; | ||
113 | if (start + 1 < parent->children->length) { | ||
114 | /* preserve the group's dimension along major axis */ | ||
115 | *get_maj_dim(first) = *get_maj_dim(parent->children->items[start + 1]); | ||
116 | } else { | ||
117 | /* new group, let the apply_layout handle it */ | ||
118 | first->height = first->width = 0; | ||
119 | break; | ||
120 | } | ||
121 | double remaining = *get_min_dim(parent); | ||
122 | for (int j = end - 1; j > start; --j) { | ||
123 | swayc_t *sibling = parent->children->items[j]; | ||
124 | if (sibling == child) { | ||
125 | /* the inserted child won't yet have its minor | ||
126 | dimension set */ | ||
127 | remaining -= *get_min_dim(parent) / (end - start); | ||
128 | } else { | ||
129 | remaining -= *get_min_dim(sibling); | ||
130 | } | ||
131 | } | ||
132 | *get_min_dim(first) = remaining; | ||
133 | i = end; | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | void add_floating(swayc_t *ws, swayc_t *child) { | ||
139 | sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type, | ||
140 | child->width, child->height, ws, ws->type, ws->width, ws->height); | ||
141 | if (!sway_assert(ws->type == C_WORKSPACE, "Must be of workspace type")) { | ||
142 | return; | ||
143 | } | ||
144 | list_add(ws->floating, child); | ||
145 | child->parent = ws; | ||
146 | child->is_floating = true; | ||
147 | if (!ws->focused) { | ||
148 | ws->focused = child; | ||
149 | } | ||
150 | ipc_event_window(child, "floating"); | ||
151 | } | ||
152 | |||
153 | swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) { | ||
154 | swayc_t *parent = fixed->parent; | ||
155 | if (fixed->is_floating) { | ||
156 | if (active->is_floating) { | ||
157 | int i = index_child(fixed); | ||
158 | list_insert(parent->floating, i + 1, active); | ||
159 | } else { | ||
160 | list_add(parent->children, active); | ||
161 | } | ||
162 | } else { | ||
163 | if (active->is_floating) { | ||
164 | list_add(parent->floating, active); | ||
165 | } else { | ||
166 | int i = index_child(fixed); | ||
167 | if (is_auto_layout(parent->layout)) { | ||
168 | list_add(parent->children, active); | ||
169 | } else { | ||
170 | list_insert(parent->children, i + 1, active); | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | active->parent = parent; | ||
175 | // focus new child | ||
176 | parent->focused = active; | ||
177 | return active->parent; | ||
178 | } | ||
179 | |||
180 | swayc_t *replace_child(swayc_t *child, swayc_t *new_child) { | ||
181 | swayc_t *parent = child->parent; | ||
182 | if (parent == NULL) { | ||
183 | return NULL; | ||
184 | } | ||
185 | int i = index_child(child); | ||
186 | if (child->is_floating) { | ||
187 | parent->floating->items[i] = new_child; | ||
188 | } else { | ||
189 | parent->children->items[i] = new_child; | ||
190 | } | ||
191 | // Set parent and focus for new_child | ||
192 | new_child->parent = child->parent; | ||
193 | if (child->parent->focused == child) { | ||
194 | child->parent->focused = new_child; | ||
195 | } | ||
196 | child->parent = NULL; | ||
197 | |||
198 | // Set geometry for new child | ||
199 | new_child->x = child->x; | ||
200 | new_child->y = child->y; | ||
201 | new_child->width = child->width; | ||
202 | new_child->height = child->height; | ||
203 | |||
204 | // reset geometry for child | ||
205 | child->width = 0; | ||
206 | child->height = 0; | ||
207 | |||
208 | // deactivate child | ||
209 | if (child->type == C_VIEW) { | ||
210 | wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false); | ||
211 | } | ||
212 | return parent; | ||
213 | } | ||
214 | |||
215 | swayc_t *remove_child(swayc_t *child) { | ||
216 | int i; | ||
217 | swayc_t *parent = child->parent; | ||
218 | if (child->is_floating) { | ||
219 | // Special case for floating views | ||
220 | for (i = 0; i < parent->floating->length; ++i) { | ||
221 | if (parent->floating->items[i] == child) { | ||
222 | list_del(parent->floating, i); | ||
223 | break; | ||
224 | } | ||
225 | } | ||
226 | i = 0; | ||
227 | } else { | ||
228 | for (i = 0; i < parent->children->length; ++i) { | ||
229 | if (parent->children->items[i] == child) { | ||
230 | list_del(parent->children, i); | ||
231 | break; | ||
232 | } | ||
233 | } | ||
234 | if (is_auto_layout(parent->layout) && parent->children->length) { | ||
235 | /* go through each group, adjust the size of the last child of each group */ | ||
236 | double *(*get_maj_dim)(swayc_t *cont); | ||
237 | double *(*get_min_dim)(swayc_t *cont); | ||
238 | if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) { | ||
239 | get_maj_dim = get_width; | ||
240 | get_min_dim = get_height; | ||
241 | } else { | ||
242 | get_maj_dim = get_height; | ||
243 | get_min_dim = get_width; | ||
244 | } | ||
245 | for (int j = parent->children->length - 1; j >= i;) { | ||
246 | int start = auto_group_start_index(parent, j); | ||
247 | int end = auto_group_end_index(parent, j); | ||
248 | swayc_t *first = parent->children->items[start]; | ||
249 | if (i == start) { | ||
250 | /* removed element was first child in the current group, | ||
251 | use its size along the major axis */ | ||
252 | *get_maj_dim(first) = *get_maj_dim(child); | ||
253 | } else if (start > i) { | ||
254 | /* preserve the group's dimension along major axis */ | ||
255 | *get_maj_dim(first) = *get_maj_dim(parent->children->items[start - 1]); | ||
256 | } | ||
257 | if (end != parent->children->length) { | ||
258 | double remaining = *get_min_dim(parent); | ||
259 | for (int k = start; k < end - 1; ++k) { | ||
260 | swayc_t *sibling = parent->children->items[k]; | ||
261 | remaining -= *get_min_dim(sibling); | ||
262 | } | ||
263 | /* last element of the group gets remaining size, elements | ||
264 | that don't change groups keep their ratio */ | ||
265 | *get_min_dim((swayc_t *) parent->children->items[end - 1]) = remaining; | ||
266 | } /* else last group, let apply_layout handle it */ | ||
267 | j = start - 1; | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | // Set focused to new container | ||
272 | if (parent->focused == child) { | ||
273 | if (parent->children->length > 0) { | ||
274 | parent->focused = parent->children->items[i ? i-1:0]; | ||
275 | } else if (parent->floating && parent->floating->length) { | ||
276 | parent->focused = parent->floating->items[parent->floating->length - 1]; | ||
277 | } else { | ||
278 | parent->focused = NULL; | ||
279 | } | ||
280 | } | ||
281 | child->parent = NULL; | ||
282 | // deactivate view | ||
283 | if (child->type == C_VIEW) { | ||
284 | wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false); | ||
285 | } | ||
286 | return parent; | ||
287 | } | ||
288 | |||
289 | void swap_container(swayc_t *a, swayc_t *b) { | ||
290 | if (!sway_assert(a&&b, "parameters must be non null") || | ||
291 | !sway_assert(a->parent && b->parent, "containers must have parents")) { | ||
292 | return; | ||
293 | } | ||
294 | size_t a_index = index_child(a); | ||
295 | size_t b_index = index_child(b); | ||
296 | swayc_t *a_parent = a->parent; | ||
297 | swayc_t *b_parent = b->parent; | ||
298 | // Swap the pointers | ||
299 | if (a->is_floating) { | ||
300 | a_parent->floating->items[a_index] = b; | ||
301 | } else { | ||
302 | a_parent->children->items[a_index] = b; | ||
303 | } | ||
304 | if (b->is_floating) { | ||
305 | b_parent->floating->items[b_index] = a; | ||
306 | } else { | ||
307 | b_parent->children->items[b_index] = a; | ||
308 | } | ||
309 | a->parent = b_parent; | ||
310 | b->parent = a_parent; | ||
311 | if (a_parent->focused == a) { | ||
312 | a_parent->focused = b; | ||
313 | } | ||
314 | // don't want to double switch | ||
315 | if (b_parent->focused == b && a_parent != b_parent) { | ||
316 | b_parent->focused = a; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | void swap_geometry(swayc_t *a, swayc_t *b) { | ||
321 | double x = a->x; | ||
322 | double y = a->y; | ||
323 | double w = a->width; | ||
324 | double h = a->height; | ||
325 | a->x = b->x; | ||
326 | a->y = b->y; | ||
327 | a->width = b->width; | ||
328 | a->height = b->height; | ||
329 | b->x = x; | ||
330 | b->y = y; | ||
331 | b->width = w; | ||
332 | b->height = h; | ||
333 | } | ||
334 | |||
335 | static void swap_children(swayc_t *container, int a, int b) { | ||
336 | if (a >= 0 && b >= 0 && a < container->children->length | ||
337 | && b < container->children->length | ||
338 | && a != b) { | ||
339 | swayc_t *pa = (swayc_t *)container->children->items[a]; | ||
340 | swayc_t *pb = (swayc_t *)container->children->items[b]; | ||
341 | container->children->items[a] = container->children->items[b]; | ||
342 | container->children->items[b] = pa; | ||
343 | if (is_auto_layout(container->layout)) { | ||
344 | size_t ga = auto_group_index(container, a); | ||
345 | size_t gb = auto_group_index(container, b); | ||
346 | if (ga != gb) { | ||
347 | swap_geometry(pa, pb); | ||
348 | } | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | |||
353 | void move_container(swayc_t *container, enum movement_direction dir, int move_amt) { | ||
354 | enum swayc_layouts layout = L_NONE; | ||
355 | swayc_t *parent = container->parent; | ||
356 | if (container->is_floating) { | ||
357 | swayc_t *output = swayc_parent_by_type(container, C_OUTPUT); | ||
358 | switch(dir) { | ||
359 | case MOVE_LEFT: | ||
360 | container->x = MAX(0, container->x - move_amt); | ||
361 | break; | ||
362 | case MOVE_RIGHT: | ||
363 | container->x = MIN(output->width - container->width, container->x + move_amt); | ||
364 | break; | ||
365 | case MOVE_UP: | ||
366 | container->y = MAX(0, container->y - move_amt); | ||
367 | break; | ||
368 | case MOVE_DOWN: | ||
369 | container->y = MIN(output->height - container->height, container->y + move_amt); | ||
370 | break; | ||
371 | default: | ||
372 | break; | ||
373 | } | ||
374 | update_geometry(container); | ||
375 | return; | ||
376 | } | ||
377 | if (container->type != C_VIEW && container->type != C_CONTAINER) { | ||
378 | return; | ||
379 | } | ||
380 | if (dir == MOVE_UP || dir == MOVE_DOWN) { | ||
381 | layout = L_VERT; | ||
382 | } else if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { | ||
383 | layout = L_HORIZ; | ||
384 | } else if (dir == MOVE_FIRST) { | ||
385 | // swap first child in auto layout with currently focused child | ||
386 | if (is_auto_layout(parent->layout)) { | ||
387 | int focused_idx = index_child(container); | ||
388 | swayc_t *first = parent->children->items[0]; | ||
389 | if (focused_idx > 0) { | ||
390 | list_swap(parent->children, 0, focused_idx); | ||
391 | swap_geometry(first, container); | ||
392 | } | ||
393 | arrange_windows(parent->parent, -1, -1); | ||
394 | ipc_event_window(container, "move"); | ||
395 | set_focused_container_for(parent->parent, container); | ||
396 | } | ||
397 | return; | ||
398 | } else if (! (dir == MOVE_NEXT || dir == MOVE_PREV)) { | ||
399 | return; | ||
400 | } | ||
401 | swayc_t *child = container; | ||
402 | bool ascended = false; | ||
403 | |||
404 | // View is wrapped in intermediate container which is needed for displaying | ||
405 | // the titlebar. Moving only the view outside of its parent container would just | ||
406 | // wrap it again under worspace. There would effectively be no movement, | ||
407 | // just a change of wrapping container. | ||
408 | if (child->type == C_VIEW && | ||
409 | parent->type == C_CONTAINER && | ||
410 | parent->children->length == 1 && | ||
411 | parent->parent->type == C_WORKSPACE) { | ||
412 | child = parent; | ||
413 | parent = parent->parent; | ||
414 | } | ||
415 | |||
416 | while (true) { | ||
417 | sway_log(L_DEBUG, "container:%p, parent:%p, child %p,", | ||
418 | container,parent,child); | ||
419 | if (parent->layout == layout | ||
420 | || (layout == L_NONE && (parent->type == C_CONTAINER || parent->type == C_WORKSPACE)) /* accept any layout for next/prev direction */ | ||
421 | || (parent->layout == L_TABBED && layout == L_HORIZ) | ||
422 | || (parent->layout == L_STACKED && layout == L_VERT) | ||
423 | || is_auto_layout(parent->layout)) { | ||
424 | int diff; | ||
425 | // If it has ascended (parent has moved up), no container is removed | ||
426 | // so insert it at index, or index+1. | ||
427 | // if it has not, the moved container is removed, so it needs to be | ||
428 | // inserted at index-1, or index+1 | ||
429 | if (ascended) { | ||
430 | diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? 0 : 1; | ||
431 | } else { | ||
432 | diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? -1 : 1; | ||
433 | } | ||
434 | int idx = index_child(child); | ||
435 | int desired = idx + diff; | ||
436 | if (dir == MOVE_NEXT || dir == MOVE_PREV) { | ||
437 | // Next/Prev always wrap. | ||
438 | if (desired < 0) { | ||
439 | desired += parent->children->length; | ||
440 | } else if (desired >= parent->children->length) { | ||
441 | desired = 0; | ||
442 | } | ||
443 | } | ||
444 | // when it has ascended, legal insertion position is 0:len | ||
445 | // when it has not, legal insertion position is 0:len-1 | ||
446 | if (desired >= 0 && desired - ascended < parent->children->length) { | ||
447 | if (!ascended) { | ||
448 | child = parent->children->items[desired]; | ||
449 | // Move container into sibling container | ||
450 | if (child->type == C_CONTAINER) { | ||
451 | parent = child; | ||
452 | // Insert it in first/last if matching layout, otherwise | ||
453 | // insert it next to focused container | ||
454 | if (parent->layout == layout | ||
455 | || (parent->layout == L_TABBED && layout == L_HORIZ) | ||
456 | || (parent->layout == L_STACKED && layout == L_VERT) | ||
457 | || is_auto_layout(parent->layout)) { | ||
458 | desired = (diff < 0) * parent->children->length; | ||
459 | } else { | ||
460 | desired = index_child(child->focused) + 1; | ||
461 | } | ||
462 | //reset geometry | ||
463 | container->width = container->height = 0; | ||
464 | } | ||
465 | } | ||
466 | if (container->parent == parent) { | ||
467 | swap_children(parent, idx, desired); | ||
468 | } else { | ||
469 | swayc_t *old_parent = remove_child(container); | ||
470 | insert_child(parent, container, desired); | ||
471 | destroy_container(old_parent); | ||
472 | sway_log(L_DEBUG,"Moving to %p %d", parent, desired); | ||
473 | } | ||
474 | break; | ||
475 | } | ||
476 | } | ||
477 | // Change parent layout if we need to | ||
478 | if (parent->children->length == 1 && parent->layout != layout && layout != L_NONE) { | ||
479 | /* swayc_change_layout(parent, layout); */ | ||
480 | parent->layout = layout; | ||
481 | continue; | ||
482 | } | ||
483 | if (parent->type == C_WORKSPACE) { | ||
484 | // If moving to an adjacent output we need a starting position (since this | ||
485 | // output might border to multiple outputs). | ||
486 | struct wlc_point abs_pos; | ||
487 | get_absolute_center_position(container, &abs_pos); | ||
488 | |||
489 | swayc_t *output = swayc_adjacent_output(parent->parent, dir, &abs_pos, true); | ||
490 | |||
491 | if (output) { | ||
492 | sway_log(L_DEBUG, "Moving between outputs"); | ||
493 | swayc_t *old_parent = remove_child(container); | ||
494 | destroy_container(old_parent); | ||
495 | |||
496 | swayc_t *dest = output->focused; | ||
497 | switch (dir) { | ||
498 | case MOVE_LEFT: | ||
499 | case MOVE_UP: | ||
500 | // reset container geometry | ||
501 | container->width = container->height = 0; | ||
502 | add_child(dest, container); | ||
503 | break; | ||
504 | case MOVE_RIGHT: | ||
505 | case MOVE_DOWN: | ||
506 | // reset container geometry | ||
507 | container->width = container->height = 0; | ||
508 | insert_child(dest, container, 0); | ||
509 | break; | ||
510 | default: | ||
511 | break; | ||
512 | } | ||
513 | // arrange new workspace | ||
514 | arrange_windows(dest, -1, -1); | ||
515 | set_focused_container(container); | ||
516 | break; | ||
517 | } | ||
518 | |||
519 | // We simply cannot move any further. | ||
520 | if (parent->layout == layout) { | ||
521 | break; | ||
522 | } | ||
523 | // Create container around workspace to insert child into | ||
524 | parent = new_container(parent, layout); | ||
525 | // Previous line set the resulting container's layout to | ||
526 | // workspace_layout. It should have been just layout. | ||
527 | parent->layout = parent->parent->layout; | ||
528 | } | ||
529 | ascended = true; | ||
530 | child = parent; | ||
531 | parent = child->parent; | ||
532 | } | ||
533 | arrange_windows(parent->parent, -1, -1); | ||
534 | ipc_event_window(container, "move"); | ||
535 | set_focused_container_for(parent->parent, container); | ||
536 | } | ||
537 | |||
538 | void move_container_to(swayc_t* container, swayc_t* destination) { | ||
539 | if (container == destination || swayc_is_parent_of(container, destination)) { | ||
540 | return; | ||
541 | } | ||
542 | swayc_t *parent = remove_child(container); | ||
543 | // Send to new destination | ||
544 | if (container->is_floating) { | ||
545 | swayc_t *ws = swayc_active_workspace_for(destination); | ||
546 | add_floating(ws, container); | ||
547 | |||
548 | // If the workspace only has one child after adding one, it | ||
549 | // means that the workspace was just initialized. | ||
550 | if (ws->children->length + ws->floating->length == 1) { | ||
551 | ipc_event_workspace(NULL, ws, "init"); | ||
552 | } | ||
553 | } else if (destination->type == C_WORKSPACE) { | ||
554 | // reset container geometry | ||
555 | container->width = container->height = 0; | ||
556 | add_child(destination, container); | ||
557 | |||
558 | // If the workspace only has one child after adding one, it | ||
559 | // means that the workspace was just initialized. | ||
560 | if (destination->children->length + destination->floating->length == 1) { | ||
561 | ipc_event_workspace(NULL, destination, "init"); | ||
562 | } | ||
563 | } else { | ||
564 | // reset container geometry | ||
565 | container->width = container->height = 0; | ||
566 | add_sibling(destination, container); | ||
567 | } | ||
568 | // Destroy old container if we need to | ||
569 | parent = destroy_container(parent); | ||
570 | // Refocus | ||
571 | swayc_t *op1 = swayc_parent_by_type(destination, C_OUTPUT); | ||
572 | swayc_t *op2 = swayc_parent_by_type(parent, C_OUTPUT); | ||
573 | set_focused_container(get_focused_view(op1)); | ||
574 | arrange_windows(op1, -1, -1); | ||
575 | update_visibility(op1); | ||
576 | if (op1 != op2) { | ||
577 | set_focused_container(get_focused_view(op2)); | ||
578 | arrange_windows(op2, -1, -1); | ||
579 | update_visibility(op2); | ||
580 | } | ||
581 | } | ||
582 | |||
583 | void move_workspace_to(swayc_t* workspace, swayc_t* destination) { | ||
584 | if (workspace == destination || swayc_is_parent_of(workspace, destination)) { | ||
585 | return; | ||
586 | } | ||
587 | swayc_t *src_op = remove_child(workspace); | ||
588 | // reset container geometry | ||
589 | workspace->width = workspace->height = 0; | ||
590 | add_child(destination, workspace); | ||
591 | sort_workspaces(destination); | ||
592 | // Refocus destination (change to new workspace) | ||
593 | set_focused_container(get_focused_view(workspace)); | ||
594 | arrange_windows(destination, -1, -1); | ||
595 | update_visibility(destination); | ||
596 | |||
597 | // make sure source output has a workspace | ||
598 | if (src_op->children->length == 0) { | ||
599 | char *ws_name = workspace_next_name(src_op->name); | ||
600 | swayc_t *ws = new_workspace(src_op, ws_name); | ||
601 | ws->is_focused = true; | ||
602 | free(ws_name); | ||
603 | } | ||
604 | set_focused_container(get_focused_view(src_op)); | ||
605 | update_visibility(src_op); | ||
606 | } | ||
607 | |||
608 | static void adjust_border_geometry(swayc_t *c, struct wlc_geometry *g, | ||
609 | const struct wlc_size *res, int left, int right, int top, int bottom) { | ||
610 | |||
611 | g->size.w += left + right; | ||
612 | if (g->origin.x - left < 0) { | ||
613 | g->size.w += g->origin.x - left; | ||
614 | } else if (g->origin.x + g->size.w - right > res->w) { | ||
615 | g->size.w = res->w - g->origin.x + right; | ||
616 | } | ||
617 | |||
618 | g->size.h += top + bottom; | ||
619 | if (g->origin.y - top < 0) { | ||
620 | g->size.h += g->origin.y - top; | ||
621 | } else if (g->origin.y + g->size.h - top > res->h) { | ||
622 | g->size.h = res->h - g->origin.y + top; | ||
623 | } | ||
624 | |||
625 | g->origin.x = MIN((uint32_t)MAX(g->origin.x - left, 0), res->w); | ||
626 | g->origin.y = MIN((uint32_t)MAX(g->origin.y - top, 0), res->h); | ||
627 | |||
628 | } | ||
629 | |||
630 | static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geometry) { | ||
631 | struct wlc_geometry g = *geometry; | ||
632 | c->actual_geometry = g; | ||
633 | |||
634 | swayc_t *output = swayc_parent_by_type(c, C_OUTPUT); | ||
635 | struct wlc_size res; | ||
636 | output_get_scaled_size(output->handle, &res); | ||
637 | |||
638 | switch (c->border_type) { | ||
639 | case B_NONE: | ||
640 | break; | ||
641 | case B_PIXEL: | ||
642 | adjust_border_geometry(c, &g, &res, c->border_thickness, | ||
643 | c->border_thickness, c->border_thickness, c->border_thickness); | ||
644 | break; | ||
645 | case B_NORMAL: | ||
646 | { | ||
647 | int title_bar_height = config->font_height + 4; // borders + padding | ||
648 | |||
649 | adjust_border_geometry(c, &g, &res, c->border_thickness, | ||
650 | c->border_thickness, title_bar_height, c->border_thickness); | ||
651 | |||
652 | struct wlc_geometry title_bar = { | ||
653 | .origin = { | ||
654 | .x = c->actual_geometry.origin.x - c->border_thickness, | ||
655 | .y = c->actual_geometry.origin.y - title_bar_height | ||
656 | }, | ||
657 | .size = { | ||
658 | .w = c->actual_geometry.size.w + (2 * c->border_thickness), | ||
659 | .h = title_bar_height | ||
660 | } | ||
661 | }; | ||
662 | c->title_bar_geometry = title_bar; | ||
663 | break; | ||
664 | } | ||
665 | } | ||
666 | |||
667 | c->border_geometry = g; | ||
668 | *geometry = c->actual_geometry; | ||
669 | |||
670 | update_container_border(c); | ||
671 | } | ||
672 | |||
673 | void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout) { | ||
674 | switch (parent->layout) { | ||
675 | case L_TABBED: | ||
676 | case L_STACKED: | ||
677 | if (prev_layout != L_TABBED && prev_layout != L_STACKED) { | ||
678 | // cache current geometry for all non-float children | ||
679 | int i; | ||
680 | for (i = 0; i < parent->children->length; ++i) { | ||
681 | swayc_t *child = parent->children->items[i]; | ||
682 | child->cached_geometry.origin.x = child->x; | ||
683 | child->cached_geometry.origin.y = child->y; | ||
684 | child->cached_geometry.size.w = child->width; | ||
685 | child->cached_geometry.size.h = child->height; | ||
686 | } | ||
687 | } | ||
688 | break; | ||
689 | default: | ||
690 | if (prev_layout == L_TABBED || prev_layout == L_STACKED) { | ||
691 | // recover cached geometry for all non-float children | ||
692 | int i; | ||
693 | for (i = 0; i < parent->children->length; ++i) { | ||
694 | swayc_t *child = parent->children->items[i]; | ||
695 | // only recoverer cached geometry if non-zero | ||
696 | if (!wlc_geometry_equals(&child->cached_geometry, &wlc_geometry_zero)) { | ||
697 | child->x = child->cached_geometry.origin.x; | ||
698 | child->y = child->cached_geometry.origin.y; | ||
699 | child->width = child->cached_geometry.size.w; | ||
700 | child->height = child->cached_geometry.size.h; | ||
701 | } | ||
702 | } | ||
703 | } | ||
704 | break; | ||
705 | } | ||
706 | } | ||
707 | |||
708 | static int update_gap_geometry(swayc_t *container, struct wlc_geometry *g) { | ||
709 | swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE); | ||
710 | swayc_t *op = ws->parent; | ||
711 | int gap = container->is_floating ? 0 : swayc_gap(container); | ||
712 | if (gap % 2 != 0) { | ||
713 | // because gaps are implemented as "half sized margins" it's currently | ||
714 | // not possible to align views properly with odd sized gaps. | ||
715 | gap -= 1; | ||
716 | } | ||
717 | |||
718 | g->origin.x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1; | ||
719 | g->origin.y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1; | ||
720 | g->size.w = container->width > gap ? container->width - gap : 1; | ||
721 | g->size.h = container->height > gap ? container->height - gap : 1; | ||
722 | |||
723 | if ((!config->edge_gaps && gap > 0) || (config->smart_gaps && ws->children->length == 1)) { | ||
724 | // Remove gap against the workspace edges. Because a pixel is not | ||
725 | // divisable, depending on gap size and the number of siblings our view | ||
726 | // might be at the workspace edge without being exactly so (thus test | ||
727 | // with gap, and align correctly). | ||
728 | if (container->x - gap <= ws->x) { | ||
729 | g->origin.x = ws->x; | ||
730 | g->size.w = container->width - gap/2; | ||
731 | } | ||
732 | if (container->y - gap <= ws->y) { | ||
733 | g->origin.y = ws->y; | ||
734 | g->size.h = container->height - gap/2; | ||
735 | } | ||
736 | if (container->x + container->width + gap >= ws->x + ws->width) { | ||
737 | g->size.w = ws->x + ws->width - g->origin.x; | ||
738 | } | ||
739 | if (container->y + container->height + gap >= ws->y + ws->height) { | ||
740 | g->size.h = ws->y + ws->height - g->origin.y; | ||
741 | } | ||
742 | } | ||
743 | |||
744 | return gap; | ||
745 | } | ||
746 | |||
747 | void update_geometry(swayc_t *container) { | ||
748 | if (container->type != C_VIEW && container->type != C_CONTAINER) { | ||
749 | return; | ||
750 | } | ||
751 | |||
752 | swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE); | ||
753 | swayc_t *op = workspace->parent; | ||
754 | swayc_t *parent = container->parent; | ||
755 | |||
756 | struct wlc_geometry geometry = { | ||
757 | .origin = { | ||
758 | .x = container->x < op->width ? container->x : op->width-1, | ||
759 | .y = container->y < op->height ? container->y : op->height-1 | ||
760 | }, | ||
761 | .size = { | ||
762 | .w = container->width, | ||
763 | .h = container->height, | ||
764 | } | ||
765 | }; | ||
766 | |||
767 | int gap = 0; | ||
768 | |||
769 | // apply inner gaps to non-tabbed/stacked containers | ||
770 | swayc_t *p = swayc_tabbed_stacked_ancestor(container); | ||
771 | if (p == NULL) { | ||
772 | gap = update_gap_geometry(container, &geometry); | ||
773 | } | ||
774 | |||
775 | swayc_t *output = swayc_parent_by_type(container, C_OUTPUT); | ||
776 | struct wlc_size size; | ||
777 | output_get_scaled_size(output->handle, &size); | ||
778 | |||
779 | if (swayc_is_fullscreen(container)) { | ||
780 | geometry.origin.x = 0; | ||
781 | geometry.origin.y = 0; | ||
782 | geometry.size.w = size.w; | ||
783 | geometry.size.h = size.h; | ||
784 | if (op->focused == workspace) { | ||
785 | wlc_view_bring_to_front(container->handle); | ||
786 | } | ||
787 | |||
788 | container->border_geometry = wlc_geometry_zero; | ||
789 | container->title_bar_geometry = wlc_geometry_zero; | ||
790 | border_clear(container->border); | ||
791 | } else if (container->is_floating) { // allocate border for floating window | ||
792 | update_border_geometry_floating(container, &geometry); | ||
793 | } else if (!container->is_floating) { // allocate border for titled window | ||
794 | container->border_geometry = geometry; | ||
795 | |||
796 | int border_top = container->border_thickness; | ||
797 | int border_bottom = container->border_thickness; | ||
798 | int border_left = container->border_thickness; | ||
799 | int border_right = container->border_thickness; | ||
800 | |||
801 | // handle hide_edge_borders | ||
802 | if (config->hide_edge_borders != E_NONE && (gap <= 0 || (config->smart_gaps && workspace->children->length == 1))) { | ||
803 | if (config->hide_edge_borders == E_VERTICAL || config->hide_edge_borders == E_BOTH) { | ||
804 | if (geometry.origin.x == workspace->x) { | ||
805 | border_left = 0; | ||
806 | } | ||
807 | |||
808 | if (geometry.origin.x + geometry.size.w == workspace->x + workspace->width) { | ||
809 | border_right = 0; | ||
810 | } | ||
811 | } | ||
812 | |||
813 | if (config->hide_edge_borders == E_HORIZONTAL || config->hide_edge_borders == E_BOTH) { | ||
814 | if (geometry.origin.y == workspace->y || should_hide_top_border(container, geometry.origin.y)) { | ||
815 | border_top = 0; | ||
816 | } | ||
817 | |||
818 | if (geometry.origin.y + geometry.size.h == workspace->y + workspace->height) { | ||
819 | border_bottom = 0; | ||
820 | } | ||
821 | } | ||
822 | |||
823 | if (config->hide_edge_borders == E_SMART && workspace->children->length == 1) { | ||
824 | border_top = 0; | ||
825 | border_bottom = 0; | ||
826 | border_left = 0; | ||
827 | border_right = 0; | ||
828 | } | ||
829 | } | ||
830 | |||
831 | int title_bar_height = config->font_height + 4; //borders + padding | ||
832 | |||
833 | if (parent->layout == L_TABBED && parent->children->length > 1) { | ||
834 | int i, x = 0, w, l, r; | ||
835 | l = parent->children->length; | ||
836 | w = geometry.size.w / l; | ||
837 | r = geometry.size.w % l; | ||
838 | for (i = 0; i < parent->children->length; ++i) { | ||
839 | swayc_t *view = parent->children->items[i]; | ||
840 | if (view == container) { | ||
841 | x = w * i; | ||
842 | if (i == l - 1) { | ||
843 | w += r; | ||
844 | } | ||
845 | break; | ||
846 | } | ||
847 | } | ||
848 | |||
849 | struct wlc_geometry title_bar = { | ||
850 | .origin = { | ||
851 | .x = container->border_geometry.origin.x + x, | ||
852 | .y = container->border_geometry.origin.y | ||
853 | }, | ||
854 | .size = { | ||
855 | .w = w, | ||
856 | .h = title_bar_height | ||
857 | } | ||
858 | }; | ||
859 | geometry.origin.x += border_left; | ||
860 | geometry.origin.y += title_bar.size.h; | ||
861 | geometry.size.w -= (border_left + border_right); | ||
862 | geometry.size.h -= (border_bottom + title_bar.size.h); | ||
863 | container->title_bar_geometry = title_bar; | ||
864 | } else if (parent->layout == L_STACKED && parent->children->length > 1) { | ||
865 | int i, y = 0; | ||
866 | for (i = 0; i < parent->children->length; ++i) { | ||
867 | swayc_t *view = parent->children->items[i]; | ||
868 | if (view == container) { | ||
869 | y = title_bar_height * i; | ||
870 | } | ||
871 | } | ||
872 | |||
873 | struct wlc_geometry title_bar = { | ||
874 | .origin = { | ||
875 | .x = container->border_geometry.origin.x, | ||
876 | .y = container->border_geometry.origin.y + y | ||
877 | }, | ||
878 | .size = { | ||
879 | .w = container->border_geometry.size.w, | ||
880 | .h = title_bar_height | ||
881 | } | ||
882 | }; | ||
883 | title_bar_height = title_bar_height * parent->children->length; | ||
884 | geometry.origin.x += border_left; | ||
885 | geometry.origin.y += title_bar_height; | ||
886 | geometry.size.w -= (border_left + border_right); | ||
887 | geometry.size.h -= (border_bottom + title_bar_height); | ||
888 | container->title_bar_geometry = title_bar; | ||
889 | } else { | ||
890 | switch (container->border_type) { | ||
891 | case B_NONE: | ||
892 | break; | ||
893 | case B_PIXEL: | ||
894 | geometry.origin.x += border_left; | ||
895 | geometry.origin.y += border_top; | ||
896 | geometry.size.w -= (border_left + border_right); | ||
897 | geometry.size.h -= (border_top + border_bottom); | ||
898 | break; | ||
899 | case B_NORMAL: | ||
900 | { | ||
901 | struct wlc_geometry title_bar = { | ||
902 | .origin = { | ||
903 | .x = container->border_geometry.origin.x, | ||
904 | .y = container->border_geometry.origin.y | ||
905 | }, | ||
906 | .size = { | ||
907 | .w = container->border_geometry.size.w, | ||
908 | .h = title_bar_height | ||
909 | } | ||
910 | }; | ||
911 | geometry.origin.x += border_left; | ||
912 | geometry.origin.y += title_bar.size.h; | ||
913 | geometry.size.w -= (border_left + border_right); | ||
914 | geometry.size.h -= (border_bottom + title_bar.size.h); | ||
915 | container->title_bar_geometry = title_bar; | ||
916 | break; | ||
917 | } | ||
918 | } | ||
919 | } | ||
920 | |||
921 | container->actual_geometry = geometry; | ||
922 | |||
923 | if (container->type == C_VIEW) { | ||
924 | update_container_border(container); | ||
925 | } | ||
926 | } | ||
927 | |||
928 | if (container->type == C_VIEW) { | ||
929 | wlc_view_set_geometry(container->handle, 0, &geometry); | ||
930 | } | ||
931 | } | ||
932 | |||
933 | /** | ||
934 | * Layout application prototypes | ||
935 | */ | ||
936 | static void apply_horiz_layout(swayc_t *container, const double x, | ||
937 | const double y, const double width, | ||
938 | const double height, const int start, | ||
939 | const int end); | ||
940 | static void apply_vert_layout(swayc_t *container, const double x, | ||
941 | const double y, const double width, | ||
942 | const double height, const int start, | ||
943 | const int end); | ||
944 | static void apply_tabbed_or_stacked_layout(swayc_t *container, double x, | ||
945 | double y, double width, | ||
946 | double height); | ||
947 | |||
948 | static void apply_auto_layout(swayc_t *container, const double x, const double y, | ||
949 | const double width, const double height, | ||
950 | enum swayc_layouts group_layout, | ||
951 | bool master_first); | ||
952 | |||
953 | static void arrange_windows_r(swayc_t *container, double width, double height) { | ||
954 | int i; | ||
955 | if (width == -1 || height == -1) { | ||
956 | swayc_log(L_DEBUG, container, "Arranging layout for %p", container); | ||
957 | width = container->width; | ||
958 | height = container->height; | ||
959 | } | ||
960 | // pixels are indivisible. if we don't round the pixels, then the view | ||
961 | // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's | ||
962 | // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y. | ||
963 | width = floor(width); | ||
964 | height = floor(height); | ||
965 | |||
966 | sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container, | ||
967 | container->name, container->width, container->height, container->x, | ||
968 | container->y); | ||
969 | |||
970 | double x = 0, y = 0; | ||
971 | switch (container->type) { | ||
972 | case C_ROOT: | ||
973 | for (i = 0; i < container->children->length; ++i) { | ||
974 | swayc_t *output = container->children->items[i]; | ||
975 | sway_log(L_DEBUG, "Arranging output '%s' at %f,%f", output->name, output->x, output->y); | ||
976 | arrange_windows_r(output, -1, -1); | ||
977 | } | ||
978 | return; | ||
979 | case C_OUTPUT: | ||
980 | { | ||
981 | struct wlc_size resolution; | ||
982 | output_get_scaled_size(container->handle, &resolution); | ||
983 | width = resolution.w; height = resolution.h; | ||
984 | // output must have correct size due to e.g. seamless mouse, | ||
985 | // but a workspace might be smaller depending on panels. | ||
986 | container->width = width; | ||
987 | container->height = height; | ||
988 | } | ||
989 | // arrange all workspaces: | ||
990 | for (i = 0; i < container->children->length; ++i) { | ||
991 | swayc_t *child = container->children->items[i]; | ||
992 | arrange_windows_r(child, -1, -1); | ||
993 | } | ||
994 | // Bring all unmanaged views to the front | ||
995 | for (i = 0; i < container->unmanaged->length; ++i) { | ||
996 | wlc_handle *handle = container->unmanaged->items[i]; | ||
997 | wlc_view_bring_to_front(*handle); | ||
998 | } | ||
999 | return; | ||
1000 | case C_WORKSPACE: | ||
1001 | { | ||
1002 | swayc_t *output = swayc_parent_by_type(container, C_OUTPUT); | ||
1003 | width = output->width, height = output->height; | ||
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 | int gap = swayc_gap(container); | ||
1026 | x = container->x = x + gap; | ||
1027 | y = container->y = y + gap; | ||
1028 | width = container->width = width - gap * 2; | ||
1029 | height = container->height = height - gap * 2; | ||
1030 | sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y); | ||
1031 | } | ||
1032 | // children are properly handled below | ||
1033 | break; | ||
1034 | case C_VIEW: | ||
1035 | { | ||
1036 | container->width = width; | ||
1037 | container->height = height; | ||
1038 | update_geometry(container); | ||
1039 | sway_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f", container->width, | ||
1040 | container->height, container->x, container->y); | ||
1041 | } | ||
1042 | return; | ||
1043 | default: | ||
1044 | container->width = width; | ||
1045 | container->height = height; | ||
1046 | x = container->x; | ||
1047 | y = container->y; | ||
1048 | |||
1049 | // add gaps to top level tapped/stacked container | ||
1050 | if (container->parent->type == C_WORKSPACE && | ||
1051 | (container->layout == L_TABBED || container->layout == L_STACKED)) { | ||
1052 | update_geometry(container); | ||
1053 | width = container->border_geometry.size.w; | ||
1054 | height = container->border_geometry.size.h; | ||
1055 | x = container->border_geometry.origin.x; | ||
1056 | y = container->border_geometry.origin.y; | ||
1057 | } | ||
1058 | |||
1059 | // update container size if it's a direct child in a tabbed/stacked layout | ||
1060 | // if parent is a workspace, its actual_geometry won't be initialized | ||
1061 | if (swayc_tabbed_stacked_parent(container) != NULL && | ||
1062 | container->parent->type != C_WORKSPACE) { | ||
1063 | // Use parent actual_geometry as a base for calculating | ||
1064 | // container geometry | ||
1065 | container->width = container->parent->actual_geometry.size.w; | ||
1066 | container->height = container->parent->actual_geometry.size.h; | ||
1067 | container->x = container->parent->actual_geometry.origin.x; | ||
1068 | container->y = container->parent->actual_geometry.origin.y; | ||
1069 | |||
1070 | update_geometry(container); | ||
1071 | width = container->width = container->actual_geometry.size.w; | ||
1072 | height = container->height = container->actual_geometry.size.h; | ||
1073 | x = container->x = container->actual_geometry.origin.x; | ||
1074 | y = container->y = container->actual_geometry.origin.y; | ||
1075 | } | ||
1076 | |||
1077 | break; | ||
1078 | } | ||
1079 | |||
1080 | switch (container->layout) { | ||
1081 | case L_HORIZ: | ||
1082 | default: | ||
1083 | apply_horiz_layout(container, x, y, width, height, 0, | ||
1084 | container->children->length); | ||
1085 | break; | ||
1086 | case L_VERT: | ||
1087 | apply_vert_layout(container, x, y, width, height, 0, | ||
1088 | container->children->length); | ||
1089 | break; | ||
1090 | case L_TABBED: | ||
1091 | case L_STACKED: | ||
1092 | apply_tabbed_or_stacked_layout(container, x, y, width, height); | ||
1093 | break; | ||
1094 | case L_AUTO_LEFT: | ||
1095 | apply_auto_layout(container, x, y, width, height, L_VERT, true); | ||
1096 | break; | ||
1097 | case L_AUTO_RIGHT: | ||
1098 | apply_auto_layout(container, x, y, width, height, L_VERT, false); | ||
1099 | break; | ||
1100 | case L_AUTO_TOP: | ||
1101 | apply_auto_layout(container, x, y, width, height, L_HORIZ, true); | ||
1102 | break; | ||
1103 | case L_AUTO_BOTTOM: | ||
1104 | apply_auto_layout(container, x, y, width, height, L_HORIZ, false); | ||
1105 | break; | ||
1106 | } | ||
1107 | |||
1108 | // Arrage floating layouts for workspaces last | ||
1109 | if (container->type == C_WORKSPACE) { | ||
1110 | for (int i = 0; i < container->floating->length; ++i) { | ||
1111 | swayc_t *view = container->floating->items[i]; | ||
1112 | if (view->type == C_VIEW) { | ||
1113 | update_geometry(view); | ||
1114 | sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f", | ||
1115 | view->width, view->height, view->x, view->y); | ||
1116 | if (swayc_is_fullscreen(view)) { | ||
1117 | wlc_view_bring_to_front(view->handle); | ||
1118 | } else if (!container->focused || | ||
1119 | !swayc_is_fullscreen(container->focused)) { | ||
1120 | wlc_view_bring_to_front(view->handle); | ||
1121 | } | ||
1122 | } | ||
1123 | } | ||
1124 | } | ||
1125 | } | ||
1126 | |||
1127 | void apply_horiz_layout(swayc_t *container, const double x, const double y, | ||
1128 | const double width, const double height, | ||
1129 | const int start, const int end) { | ||
1130 | double scale = 0; | ||
1131 | // Calculate total width | ||
1132 | for (int i = start; i < end; ++i) { | ||
1133 | double *old_width = &((swayc_t *)container->children->items[i])->width; | ||
1134 | if (*old_width <= 0) { | ||
1135 | if (end - start > 1) { | ||
1136 | *old_width = width / (end - start - 1); | ||
1137 | } else { | ||
1138 | *old_width = width; | ||
1139 | } | ||
1140 | } | ||
1141 | scale += *old_width; | ||
1142 | } | ||
1143 | scale = width / scale; | ||
1144 | |||
1145 | // Resize windows | ||
1146 | double child_x = x; | ||
1147 | if (scale > 0.1) { | ||
1148 | sway_log(L_DEBUG, "Arranging %p horizontally", container); | ||
1149 | swayc_t *focused = NULL; | ||
1150 | for (int i = start; i < end; ++i) { | ||
1151 | swayc_t *child = container->children->items[i]; | ||
1152 | sway_log(L_DEBUG, | ||
1153 | "Calculating arrangement for %p:%d (will scale %f by %f)", child, | ||
1154 | child->type, width, scale); | ||
1155 | child->x = child_x; | ||
1156 | child->y = y; | ||
1157 | |||
1158 | if (child == container->focused) { | ||
1159 | focused = child; | ||
1160 | } | ||
1161 | |||
1162 | if (i == end - 1) { | ||
1163 | double remaining_width = x + width - child_x; | ||
1164 | arrange_windows_r(child, remaining_width, height); | ||
1165 | } else { | ||
1166 | arrange_windows_r(child, child->width * scale, height); | ||
1167 | } | ||
1168 | child_x += child->width; | ||
1169 | } | ||
1170 | |||
1171 | // update focused view border last because it may | ||
1172 | // depend on the title bar geometry of its siblings. | ||
1173 | if (focused && container->children->length > 1) { | ||
1174 | update_container_border(focused); | ||
1175 | } | ||
1176 | } | ||
1177 | } | ||
1178 | |||
1179 | void apply_vert_layout(swayc_t *container, const double x, const double y, | ||
1180 | const double width, const double height, const int start, | ||
1181 | const int end) { | ||
1182 | int i; | ||
1183 | double scale = 0; | ||
1184 | // Calculate total height | ||
1185 | for (i = start; i < end; ++i) { | ||
1186 | double *old_height = &((swayc_t *)container->children->items[i])->height; | ||
1187 | if (*old_height <= 0) { | ||
1188 | if (end - start > 1) { | ||
1189 | *old_height = height / (end - start - 1); | ||
1190 | } else { | ||
1191 | *old_height = height; | ||
1192 | } | ||
1193 | } | ||
1194 | scale += *old_height; | ||
1195 | } | ||
1196 | scale = height / scale; | ||
1197 | |||
1198 | // Resize | ||
1199 | double child_y = y; | ||
1200 | if (scale > 0.1) { | ||
1201 | sway_log(L_DEBUG, "Arranging %p vertically", container); | ||
1202 | swayc_t *focused = NULL; | ||
1203 | for (i = start; i < end; ++i) { | ||
1204 | swayc_t *child = container->children->items[i]; | ||
1205 | sway_log(L_DEBUG, | ||
1206 | "Calculating arrangement for %p:%d (will scale %f by %f)", child, | ||
1207 | child->type, height, scale); | ||
1208 | child->x = x; | ||
1209 | child->y = child_y; | ||
1210 | |||
1211 | if (child == container->focused) { | ||
1212 | focused = child; | ||
1213 | } | ||
1214 | |||
1215 | if (i == end - 1) { | ||
1216 | double remaining_height = y + height - child_y; | ||
1217 | arrange_windows_r(child, width, remaining_height); | ||
1218 | } else { | ||
1219 | arrange_windows_r(child, width, child->height * scale); | ||
1220 | } | ||
1221 | child_y += child->height; | ||
1222 | } | ||
1223 | |||
1224 | // update focused view border last because it may | ||
1225 | // depend on the title bar geometry of its siblings. | ||
1226 | if (focused && container->children->length > 1) { | ||
1227 | update_container_border(focused); | ||
1228 | } | ||
1229 | } | ||
1230 | } | ||
1231 | |||
1232 | void apply_tabbed_or_stacked_layout(swayc_t *container, double x, double y, | ||
1233 | double width, double height) { | ||
1234 | int i; | ||
1235 | swayc_t *focused = NULL; | ||
1236 | for (i = 0; i < container->children->length; ++i) { | ||
1237 | swayc_t *child = container->children->items[i]; | ||
1238 | child->x = x; | ||
1239 | child->y = y; | ||
1240 | if (child == container->focused) { | ||
1241 | focused = child; | ||
1242 | } else { | ||
1243 | arrange_windows_r(child, width, height); | ||
1244 | } | ||
1245 | } | ||
1246 | |||
1247 | if (focused) { | ||
1248 | arrange_windows_r(focused, width, height); | ||
1249 | } | ||
1250 | } | ||
1251 | |||
1252 | void apply_auto_layout(swayc_t *container, const double x, const double y, | ||
1253 | const double width, const double height, | ||
1254 | enum swayc_layouts group_layout, | ||
1255 | bool master_first) { | ||
1256 | // Auto layout "container" in width x height @ x, y | ||
1257 | // using "group_layout" for each of the groups in the container. | ||
1258 | // There is one "master" group, plus container->nb_slave_groups. | ||
1259 | // Each group is layed out side by side following the "major" axis. | ||
1260 | // The direction of the layout used for groups is the "minor" axis. | ||
1261 | // Example: | ||
1262 | // | ||
1263 | // ---- major axis --> | ||
1264 | // +---------+-----------+ | ||
1265 | // | | | | | ||
1266 | // | master | slave 1 | | | ||
1267 | // | +-----------+ | minor axis (direction of group_layout) | ||
1268 | // | | | | | ||
1269 | // | | slave 2 | V | ||
1270 | // +---------+-----------+ | ||
1271 | // | ||
1272 | // container with three children (one master and two slaves) and | ||
1273 | // a single slave group (containing slave 1 and 2). The master | ||
1274 | // group and slave group are layed out using L_VERT. | ||
1275 | |||
1276 | size_t nb_groups = auto_group_count(container); | ||
1277 | |||
1278 | // the target dimension of the container along the "major" axis, each | ||
1279 | // group in the container will be layed out using "group_layout" along | ||
1280 | // the "minor" axis. | ||
1281 | double dim_maj; | ||
1282 | double pos_maj; | ||
1283 | |||
1284 | // x and y coords for the next group to be laid out. | ||
1285 | const double *group_x, *group_y; | ||
1286 | |||
1287 | // pos of the next group to layout along the major axis | ||
1288 | double pos; | ||
1289 | |||
1290 | // size of the next group along the major axis. | ||
1291 | double group_dim; | ||
1292 | |||
1293 | // height and width of next group to be laid out. | ||
1294 | const double *group_h, *group_w; | ||
1295 | |||
1296 | switch (group_layout) { | ||
1297 | default: | ||
1298 | sway_log(L_DEBUG, "Unknown layout type (%d) used in %s()", | ||
1299 | group_layout, __func__); | ||
1300 | /* fall through */ | ||
1301 | case L_VERT: | ||
1302 | dim_maj = width; | ||
1303 | pos_maj = x; | ||
1304 | |||
1305 | group_x = &pos; | ||
1306 | group_y = &y; | ||
1307 | group_w = &group_dim; | ||
1308 | group_h = &height; | ||
1309 | break; | ||
1310 | case L_HORIZ: | ||
1311 | dim_maj = height; | ||
1312 | pos_maj = y; | ||
1313 | |||
1314 | group_x = &x; | ||
1315 | group_y = &pos; | ||
1316 | group_w = &width; | ||
1317 | group_h = &group_dim; | ||
1318 | break; | ||
1319 | } | ||
1320 | |||
1321 | /* Determine the dimension of each of the groups in the layout. | ||
1322 | * Dimension will be width for a VERT layout and height for a HORIZ | ||
1323 | * layout. */ | ||
1324 | double old_group_dim[nb_groups]; | ||
1325 | double old_dim = 0; | ||
1326 | for (size_t group = 0; group < nb_groups; ++group) { | ||
1327 | int idx; | ||
1328 | if (auto_group_bounds(container, group, &idx, NULL)) { | ||
1329 | swayc_t *child = container->children->items[idx]; | ||
1330 | double *dim = group_layout == L_HORIZ ? &child->height : &child->width; | ||
1331 | if (*dim <= 0) { | ||
1332 | // New child with uninitialized dimension | ||
1333 | *dim = dim_maj; | ||
1334 | if (nb_groups > 1) { | ||
1335 | // child gets a dimension proportional to existing groups, | ||
1336 | // it will be later scaled based on to the available size | ||
1337 | // in the major axis. | ||
1338 | *dim /= (nb_groups - 1); | ||
1339 | } | ||
1340 | } | ||
1341 | old_dim += *dim; | ||
1342 | old_group_dim[group] = *dim; | ||
1343 | } | ||
1344 | } | ||
1345 | double scale = dim_maj / old_dim; | ||
1346 | |||
1347 | /* Apply layout to each group */ | ||
1348 | pos = pos_maj; | ||
1349 | |||
1350 | for (size_t group = 0; group < nb_groups; ++group) { | ||
1351 | int start, end; // index of first (inclusive) and last (exclusive) child in the group | ||
1352 | if (auto_group_bounds(container, group, &start, &end)) { | ||
1353 | // adjusted size of the group | ||
1354 | group_dim = old_group_dim[group] * scale; | ||
1355 | if (group == nb_groups - 1) { | ||
1356 | group_dim = pos_maj + dim_maj - pos; // remaining width | ||
1357 | } | ||
1358 | sway_log(L_DEBUG, "Arranging container %p column %zu, children [%d,%d[ (%fx%f+%f,%f)", | ||
1359 | container, group, start, end, *group_w, *group_h, *group_x, *group_y); | ||
1360 | switch (group_layout) { | ||
1361 | default: | ||
1362 | case L_VERT: | ||
1363 | apply_vert_layout(container, *group_x, *group_y, *group_w, *group_h, start, end); | ||
1364 | break; | ||
1365 | case L_HORIZ: | ||
1366 | apply_horiz_layout(container, *group_x, *group_y, *group_w, *group_h, start, end); | ||
1367 | break; | ||
1368 | } | ||
1369 | |||
1370 | /* update position for next group */ | ||
1371 | pos += group_dim; | ||
1372 | } | ||
1373 | } | ||
1374 | } | ||
1375 | |||
1376 | void arrange_windows(swayc_t *container, double width, double height) { | ||
1377 | update_visibility(container); | ||
1378 | arrange_windows_r(container, width, height); | ||
1379 | layout_log(&root_container, 0); | ||
1380 | } | ||
1381 | |||
1382 | void arrange_backgrounds(void) { | ||
1383 | struct background_config *bg; | ||
1384 | for (int i = 0; i < desktop_shell.backgrounds->length; ++i) { | ||
1385 | bg = desktop_shell.backgrounds->items[i]; | ||
1386 | wlc_view_send_to_back(bg->handle); | ||
1387 | } | ||
1388 | } | ||
1389 | |||
1390 | /** | ||
1391 | * Get swayc in the direction of newly entered output. | ||
1392 | */ | ||
1393 | static swayc_t *get_swayc_in_output_direction(swayc_t *output, enum movement_direction dir) { | ||
1394 | if (!output) { | ||
1395 | return NULL; | ||
1396 | } | ||
1397 | |||
1398 | swayc_t *ws = swayc_focus_by_type(output, C_WORKSPACE); | ||
1399 | if (ws && ws->children->length > 0) { | ||
1400 | switch (dir) { | ||
1401 | case MOVE_LEFT: | ||
1402 | // get most right child of new output | ||
1403 | return ws->children->items[ws->children->length-1]; | ||
1404 | case MOVE_RIGHT: | ||
1405 | // get most left child of new output | ||
1406 | return ws->children->items[0]; | ||
1407 | case MOVE_UP: | ||
1408 | case MOVE_DOWN: | ||
1409 | { | ||
1410 | swayc_t *focused_view = swayc_focus_by_type(ws, C_VIEW); | ||
1411 | if (focused_view && focused_view->parent) { | ||
1412 | swayc_t *parent = focused_view->parent; | ||
1413 | if (parent->layout == L_VERT) { | ||
1414 | if (dir == MOVE_UP) { | ||
1415 | // get child furthest down on new output | ||
1416 | return parent->children->items[parent->children->length-1]; | ||
1417 | } else if (dir == MOVE_DOWN) { | ||
1418 | // get child furthest up on new output | ||
1419 | return parent->children->items[0]; | ||
1420 | } | ||
1421 | } | ||
1422 | return focused_view; | ||
1423 | } | ||
1424 | break; | ||
1425 | } | ||
1426 | default: | ||
1427 | break; | ||
1428 | } | ||
1429 | } | ||
1430 | |||
1431 | return output; | ||
1432 | } | ||
1433 | |||
1434 | swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_direction dir, swayc_t *limit) { | ||
1435 | if (dir == MOVE_CHILD) { | ||
1436 | return container->focused; | ||
1437 | } | ||
1438 | |||
1439 | swayc_t *parent = container->parent; | ||
1440 | if (dir == MOVE_PARENT) { | ||
1441 | if (parent->type == C_OUTPUT) { | ||
1442 | return NULL; | ||
1443 | } else { | ||
1444 | return parent; | ||
1445 | } | ||
1446 | } | ||
1447 | |||
1448 | if (dir == MOVE_PREV || dir == MOVE_NEXT) { | ||
1449 | int focused_idx = index_child(container); | ||
1450 | if (focused_idx == -1) { | ||
1451 | return NULL; | ||
1452 | } else { | ||
1453 | int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) % | ||
1454 | parent->children->length; | ||
1455 | if (desired < 0) { | ||
1456 | desired += parent->children->length; | ||
1457 | } | ||
1458 | return parent->children->items[desired]; | ||
1459 | } | ||
1460 | } | ||
1461 | |||
1462 | // If moving to an adjacent output we need a starting position (since this | ||
1463 | // output might border to multiple outputs). | ||
1464 | struct wlc_point abs_pos; | ||
1465 | get_absolute_center_position(container, &abs_pos); | ||
1466 | |||
1467 | if (container->type == C_VIEW && swayc_is_fullscreen(container)) { | ||
1468 | sway_log(L_DEBUG, "Moving from fullscreen view, skipping to output"); | ||
1469 | container = swayc_parent_by_type(container, C_OUTPUT); | ||
1470 | get_absolute_center_position(container, &abs_pos); | ||
1471 | swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true); | ||
1472 | return get_swayc_in_output_direction(output, dir); | ||
1473 | } | ||
1474 | |||
1475 | if (container->type == C_WORKSPACE && container->fullscreen) { | ||
1476 | sway_log(L_DEBUG, "Moving to fullscreen view"); | ||
1477 | return container->fullscreen; | ||
1478 | } | ||
1479 | |||
1480 | swayc_t *wrap_candidate = NULL; | ||
1481 | while (true) { | ||
1482 | // Test if we can even make a difference here | ||
1483 | bool can_move = false; | ||
1484 | int desired; | ||
1485 | int idx = index_child(container); | ||
1486 | if (parent->type == C_ROOT) { | ||
1487 | swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true); | ||
1488 | if (!output || output == container) { | ||
1489 | return wrap_candidate; | ||
1490 | } | ||
1491 | sway_log(L_DEBUG, "Moving between outputs"); | ||
1492 | return get_swayc_in_output_direction(output, dir); | ||
1493 | } else { | ||
1494 | if (is_auto_layout(parent->layout)) { | ||
1495 | bool is_major = parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT | ||
1496 | ? dir == MOVE_LEFT || dir == MOVE_RIGHT | ||
1497 | : dir == MOVE_DOWN || dir == MOVE_UP; | ||
1498 | size_t gidx = auto_group_index(parent, idx); | ||
1499 | if (is_major) { | ||
1500 | size_t desired_grp = gidx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1); | ||
1501 | can_move = auto_group_bounds(parent, desired_grp, &desired, NULL); | ||
1502 | } else { | ||
1503 | desired = idx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1); | ||
1504 | int start, end; | ||
1505 | can_move = auto_group_bounds(parent, gidx, &start, &end) | ||
1506 | && desired >= start && desired < end; | ||
1507 | } | ||
1508 | } else { | ||
1509 | if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { | ||
1510 | if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { | ||
1511 | can_move = true; | ||
1512 | desired = idx + (dir == MOVE_LEFT ? -1 : 1); | ||
1513 | } | ||
1514 | } else { | ||
1515 | if (parent->layout == L_VERT || parent->layout == L_STACKED) { | ||
1516 | can_move = true; | ||
1517 | desired = idx + (dir == MOVE_UP ? -1 : 1); | ||
1518 | } | ||
1519 | } | ||
1520 | } | ||
1521 | } | ||
1522 | |||
1523 | if (can_move) { | ||
1524 | if (container->is_floating) { | ||
1525 | if (desired < 0) { | ||
1526 | wrap_candidate = parent->floating->items[parent->floating->length-1]; | ||
1527 | } else if (desired >= parent->floating->length){ | ||
1528 | wrap_candidate = parent->floating->items[0]; | ||
1529 | } else { | ||
1530 | wrap_candidate = parent->floating->items[desired]; | ||
1531 | } | ||
1532 | if (wrap_candidate) { | ||
1533 | wlc_view_bring_to_front(wrap_candidate->handle); | ||
1534 | } | ||
1535 | return wrap_candidate; | ||
1536 | } else if (desired < 0 || desired >= parent->children->length) { | ||
1537 | can_move = false; | ||
1538 | int len = parent->children->length; | ||
1539 | if (!wrap_candidate && len > 1) { | ||
1540 | if (desired < 0) { | ||
1541 | wrap_candidate = parent->children->items[len-1]; | ||
1542 | } else { | ||
1543 | wrap_candidate = parent->children->items[0]; | ||
1544 | } | ||
1545 | if (config->force_focus_wrapping) { | ||
1546 | return wrap_candidate; | ||
1547 | } | ||
1548 | } | ||
1549 | } else { | ||
1550 | sway_log(L_DEBUG, "%s cont %d-%p dir %i sibling %d: %p", __func__, | ||
1551 | idx, container, dir, desired, parent->children->items[desired]); | ||
1552 | return parent->children->items[desired]; | ||
1553 | } | ||
1554 | } | ||
1555 | if (!can_move) { | ||
1556 | container = parent; | ||
1557 | parent = parent->parent; | ||
1558 | if (!parent || container == limit) { | ||
1559 | // wrapping is the last chance | ||
1560 | return wrap_candidate; | ||
1561 | } | ||
1562 | } | ||
1563 | } | ||
1564 | } | ||
1565 | |||
1566 | swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir) { | ||
1567 | return get_swayc_in_direction_under(container, dir, NULL); | ||
1568 | } | ||
1569 | |||
1570 | void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge edge) { | ||
1571 | int i; | ||
1572 | bool layout_match = true; | ||
1573 | sway_log(L_DEBUG, "Resizing %p with amount: %f", container, amount); | ||
1574 | if (edge == WLC_RESIZE_EDGE_LEFT || edge == WLC_RESIZE_EDGE_RIGHT) { | ||
1575 | container->width += amount; | ||
1576 | layout_match = container->layout == L_HORIZ; | ||
1577 | } else if (edge == WLC_RESIZE_EDGE_TOP || edge == WLC_RESIZE_EDGE_BOTTOM) { | ||
1578 | container->height += amount; | ||
1579 | layout_match = container->layout == L_VERT; | ||
1580 | } | ||
1581 | if (container->type == C_VIEW) { | ||
1582 | update_geometry(container); | ||
1583 | return; | ||
1584 | } | ||
1585 | if (layout_match) { | ||
1586 | for (i = 0; i < container->children->length; i++) { | ||
1587 | recursive_resize(container->children->items[i], amount/container->children->length, edge); | ||
1588 | } | ||
1589 | } else { | ||
1590 | for (i = 0; i < container->children->length; i++) { | ||
1591 | recursive_resize(container->children->items[i], amount, edge); | ||
1592 | } | ||
1593 | } | ||
1594 | } | ||
1595 | |||
1596 | enum swayc_layouts default_layout(swayc_t *output) { | ||
1597 | if (config->default_layout != L_NONE) { | ||
1598 | return config->default_layout; | ||
1599 | } else if (config->default_orientation != L_NONE) { | ||
1600 | return config->default_orientation; | ||
1601 | } else if (output->width >= output->height) { | ||
1602 | return L_HORIZ; | ||
1603 | } else { | ||
1604 | return L_VERT; | ||
1605 | } | ||
1606 | } | ||
1607 | |||
1608 | bool is_auto_layout(enum swayc_layouts layout) { | ||
1609 | return (layout >= L_AUTO_FIRST) && (layout <= L_AUTO_LAST); | ||
1610 | } | ||
1611 | |||
1612 | /** | ||
1613 | * Return the number of master elements in a container | ||
1614 | */ | ||
1615 | static inline size_t auto_master_count(const swayc_t *container) { | ||
1616 | sway_assert(container->children->length >= 0, "Container %p has (negative) children %d", | ||
1617 | container, container->children->length); | ||
1618 | return MIN(container->nb_master, (size_t)container->children->length); | ||
1619 | } | ||
1620 | |||
1621 | /** | ||
1622 | * Return the number of children in the slave groups. This corresponds to the children | ||
1623 | * that are not members of the master group. | ||
1624 | */ | ||
1625 | static inline size_t auto_slave_count(const swayc_t *container) { | ||
1626 | return container->children->length - auto_master_count(container); | ||
1627 | } | ||
1628 | |||
1629 | /** | ||
1630 | * Return the number of slave groups in the container. | ||
1631 | */ | ||
1632 | size_t auto_slave_group_count(const swayc_t *container) { | ||
1633 | return MIN(container->nb_slave_groups, auto_slave_count(container)); | ||
1634 | } | ||
1635 | |||
1636 | /** | ||
1637 | * Return the combined number of master and slave groups in the container. | ||
1638 | */ | ||
1639 | size_t auto_group_count(const swayc_t *container) { | ||
1640 | return auto_slave_group_count(container) | ||
1641 | + (container->children->length && container->nb_master ? 1 : 0); | ||
1642 | } | ||
1643 | |||
1644 | /** | ||
1645 | * given the index of a container's child, return the index of the first child of the group | ||
1646 | * which index is a member of. | ||
1647 | */ | ||
1648 | int auto_group_start_index(const swayc_t *container, int index) { | ||
1649 | if (index < 0 || ! is_auto_layout(container->layout) | ||
1650 | || (size_t)index < container->nb_master) { | ||
1651 | return 0; | ||
1652 | } else { | ||
1653 | size_t nb_slaves = auto_slave_count(container); | ||
1654 | size_t nb_slave_grp = auto_slave_group_count(container); | ||
1655 | size_t grp_sz = nb_slaves / nb_slave_grp; | ||
1656 | size_t remainder = nb_slaves % nb_slave_grp; | ||
1657 | int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master; | ||
1658 | int start_idx; | ||
1659 | if (index < idx2) { | ||
1660 | start_idx = ((index - container->nb_master) / grp_sz) * grp_sz + container->nb_master; | ||
1661 | } else { | ||
1662 | start_idx = idx2 + ((index - idx2) / (grp_sz + 1)) * (grp_sz + 1); | ||
1663 | } | ||
1664 | return MIN(start_idx, container->children->length); | ||
1665 | } | ||
1666 | } | ||
1667 | |||
1668 | /** | ||
1669 | * given the index of a container's child, return the index of the first child of the group | ||
1670 | * that follows the one which index is a member of. | ||
1671 | * This makes the function usable to walk through the groups in a container. | ||
1672 | */ | ||
1673 | int auto_group_end_index(const swayc_t *container, int index) { | ||
1674 | if (index < 0 || ! is_auto_layout(container->layout)) { | ||
1675 | return container->children->length; | ||
1676 | } else { | ||
1677 | int nxt_idx; | ||
1678 | if ((size_t)index < container->nb_master) { | ||
1679 | nxt_idx = auto_master_count(container); | ||
1680 | } else { | ||
1681 | size_t nb_slaves = auto_slave_count(container); | ||
1682 | size_t nb_slave_grp = auto_slave_group_count(container); | ||
1683 | size_t grp_sz = nb_slaves / nb_slave_grp; | ||
1684 | size_t remainder = nb_slaves % nb_slave_grp; | ||
1685 | int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master; | ||
1686 | if (index < idx2) { | ||
1687 | nxt_idx = ((index - container->nb_master) / grp_sz + 1) * grp_sz + container->nb_master; | ||
1688 | } else { | ||
1689 | nxt_idx = idx2 + ((index - idx2) / (grp_sz + 1) + 1) * (grp_sz + 1); | ||
1690 | } | ||
1691 | } | ||
1692 | return MIN(nxt_idx, container->children->length); | ||
1693 | } | ||
1694 | } | ||
1695 | |||
1696 | /** | ||
1697 | * return the index of the Group containing <index>th child of <container>. | ||
1698 | * The index is the order of the group along the container's major axis (starting at 0). | ||
1699 | */ | ||
1700 | size_t auto_group_index(const swayc_t *container, int index) { | ||
1701 | if (index < 0) { | ||
1702 | return 0; | ||
1703 | } | ||
1704 | bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP); | ||
1705 | size_t nb_slaves = auto_slave_count(container); | ||
1706 | if ((size_t)index < container->nb_master) { | ||
1707 | if (master_first || nb_slaves <= 0) { | ||
1708 | return 0; | ||
1709 | } else { | ||
1710 | return auto_slave_group_count(container); | ||
1711 | } | ||
1712 | } else { | ||
1713 | size_t nb_slave_grp = auto_slave_group_count(container); | ||
1714 | size_t grp_sz = nb_slaves / nb_slave_grp; | ||
1715 | size_t remainder = nb_slaves % nb_slave_grp; | ||
1716 | int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master; | ||
1717 | size_t grp_idx; | ||
1718 | if (index < idx2) { | ||
1719 | grp_idx = (index - container->nb_master) / grp_sz; | ||
1720 | } else { | ||
1721 | grp_idx = (nb_slave_grp - remainder) + (index - idx2) / (grp_sz + 1) ; | ||
1722 | } | ||
1723 | return grp_idx + (master_first && container-> nb_master ? 1 : 0); | ||
1724 | } | ||
1725 | } | ||
1726 | |||
1727 | /** | ||
1728 | * Return the first index (inclusive) and last index (exclusive) of the elements of a group in | ||
1729 | * an auto layout. | ||
1730 | * If the bounds of the given group can be calculated, they are returned in the start/end | ||
1731 | * parameters (int pointers) and the return value will be true. | ||
1732 | * The indexes are passed by reference and can be NULL. | ||
1733 | */ | ||
1734 | bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end) { | ||
1735 | size_t nb_grp = auto_group_count(container); | ||
1736 | if (group_index >= nb_grp) { | ||
1737 | return false; | ||
1738 | } | ||
1739 | bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP); | ||
1740 | size_t nb_master = auto_master_count(container); | ||
1741 | size_t nb_slave_grp = auto_slave_group_count(container); | ||
1742 | int g_start, g_end; | ||
1743 | if (nb_master && (master_first ? group_index == 0 : group_index == nb_grp - 1)) { | ||
1744 | g_start = 0; | ||
1745 | g_end = nb_master; | ||
1746 | } else { | ||
1747 | size_t nb_slaves = auto_slave_count(container); | ||
1748 | size_t grp_sz = nb_slaves / nb_slave_grp; | ||
1749 | size_t remainder = nb_slaves % nb_slave_grp; | ||
1750 | size_t g0 = master_first && container->nb_master ? 1 : 0; | ||
1751 | size_t g1 = g0 + nb_slave_grp - remainder; | ||
1752 | if (group_index < g1) { | ||
1753 | g_start = container->nb_master + (group_index - g0) * grp_sz; | ||
1754 | g_end = g_start + grp_sz; | ||
1755 | } else { | ||
1756 | size_t g2 = group_index - g1; | ||
1757 | g_start = container->nb_master | ||
1758 | + (nb_slave_grp - remainder) * grp_sz | ||
1759 | + g2 * (grp_sz + 1); | ||
1760 | g_end = g_start + grp_sz + 1; | ||
1761 | } | ||
1762 | } | ||
1763 | if (start) { | ||
1764 | *start = g_start; | ||
1765 | } | ||
1766 | if (end) { | ||
1767 | *end = g_end; | ||
1768 | } | ||
1769 | return true; | ||
1770 | } | ||
diff --git a/sway/main.c b/sway/main.c index cc9147b8..efb674b6 100644 --- a/sway/main.c +++ b/sway/main.c | |||
@@ -1,58 +1,46 @@ | |||
1 | #define _XOPEN_SOURCE 700 | 1 | #define _XOPEN_SOURCE 700 |
2 | #define _POSIX_C_SOURCE 200112L | 2 | #define _POSIX_C_SOURCE 200112L |
3 | #include <stdio.h> | 3 | #include <getopt.h> |
4 | #include <stdlib.h> | 4 | #include <signal.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <wlc/wlc.h> | 6 | #include <stdlib.h> |
7 | #include <sys/wait.h> | 7 | #include <stdio.h> |
8 | #include <sys/types.h> | 8 | #include <string.h> |
9 | #include <sys/stat.h> | 9 | #include <sys/stat.h> |
10 | #include <sys/types.h> | ||
11 | #include <sys/wait.h> | ||
10 | #include <sys/un.h> | 12 | #include <sys/un.h> |
11 | #include <signal.h> | ||
12 | #include <unistd.h> | 13 | #include <unistd.h> |
13 | #include <getopt.h> | ||
14 | #ifdef __linux__ | 14 | #ifdef __linux__ |
15 | #include <sys/capability.h> | 15 | #include <sys/capability.h> |
16 | #include <sys/prctl.h> | 16 | #include <sys/prctl.h> |
17 | #endif | 17 | #endif |
18 | #include "sway/extensions.h" | 18 | #include <wlr/util/log.h> |
19 | #include "sway/layout.h" | ||
20 | #include "sway/config.h" | 19 | #include "sway/config.h" |
21 | #include "sway/security.h" | 20 | #include "sway/debug.h" |
22 | #include "sway/handlers.h" | 21 | #include "sway/server.h" |
23 | #include "sway/input.h" | 22 | #include "sway/tree/layout.h" |
24 | #include "sway/ipc-server.h" | 23 | #include "sway/ipc-server.h" |
25 | #include "ipc-client.h" | 24 | #include "ipc-client.h" |
26 | #include "readline.h" | 25 | #include "readline.h" |
27 | #include "stringop.h" | 26 | #include "stringop.h" |
28 | #include "sway.h" | ||
29 | #include "log.h" | ||
30 | #include "util.h" | 27 | #include "util.h" |
31 | 28 | ||
32 | static bool terminate_request = false; | 29 | static bool terminate_request = false; |
33 | static int exit_value = 0; | 30 | static int exit_value = 0; |
31 | struct sway_server server; | ||
34 | 32 | ||
35 | void sway_terminate(int exit_code) { | 33 | void sway_terminate(int exit_code) { |
36 | terminate_request = true; | 34 | terminate_request = true; |
37 | exit_value = exit_code; | 35 | exit_value = exit_code; |
38 | wlc_terminate(); | 36 | wl_display_terminate(server.wl_display); |
39 | } | 37 | } |
40 | 38 | ||
41 | void sig_handler(int signal) { | 39 | void sig_handler(int signal) { |
42 | close_views(&root_container); | 40 | //close_views(&root_container); |
43 | sway_terminate(EXIT_SUCCESS); | 41 | sway_terminate(EXIT_SUCCESS); |
44 | } | 42 | } |
45 | 43 | ||
46 | static void wlc_log_handler(enum wlc_log_type type, const char *str) { | ||
47 | if (type == WLC_LOG_ERROR) { | ||
48 | sway_log(L_ERROR, "[wlc] %s", str); | ||
49 | } else if (type == WLC_LOG_WARN) { | ||
50 | sway_log(L_INFO, "[wlc] %s", str); | ||
51 | } else { | ||
52 | sway_log(L_DEBUG, "[wlc] %s", str); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | void detect_raspi() { | 44 | void detect_raspi() { |
57 | bool raspi = false; | 45 | bool raspi = false; |
58 | FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); | 46 | FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); |
@@ -98,23 +86,16 @@ void detect_proprietary() { | |||
98 | if (!f) { | 86 | if (!f) { |
99 | return; | 87 | return; |
100 | } | 88 | } |
101 | bool nvidia = false, nvidia_modeset = false, nvidia_uvm = false, nvidia_drm = false; | ||
102 | while (!feof(f)) { | 89 | while (!feof(f)) { |
103 | char *line; | 90 | char *line; |
104 | if (!(line = read_line(f))) { | 91 | if (!(line = read_line(f))) { |
105 | break; | 92 | break; |
106 | } | 93 | } |
107 | if (strstr(line, "nvidia")) { | 94 | if (strstr(line, "nvidia")) { |
108 | nvidia = true; | 95 | fprintf(stderr, "\x1B[1;31mWarning: Proprietary Nvidia drivers are " |
109 | } | 96 | "NOT supported. Use Nouveau.\x1B[0m\n"); |
110 | if (strstr(line, "nvidia_modeset")) { | 97 | free(line); |
111 | nvidia_modeset = true; | 98 | break; |
112 | } | ||
113 | if (strstr(line, "nvidia_uvm")) { | ||
114 | nvidia_uvm = true; | ||
115 | } | ||
116 | if (strstr(line, "nvidia_drm")) { | ||
117 | nvidia_drm = true; | ||
118 | } | 99 | } |
119 | if (strstr(line, "fglrx")) { | 100 | if (strstr(line, "fglrx")) { |
120 | fprintf(stderr, "\x1B[1;31mWarning: Proprietary AMD drivers do " | 101 | fprintf(stderr, "\x1B[1;31mWarning: Proprietary AMD drivers do " |
@@ -125,52 +106,6 @@ void detect_proprietary() { | |||
125 | free(line); | 106 | free(line); |
126 | } | 107 | } |
127 | fclose(f); | 108 | fclose(f); |
128 | if (nvidia) { | ||
129 | fprintf(stderr, "\x1B[1;31mWarning: Proprietary nvidia driver support " | ||
130 | "is considered experimental. Nouveau is strongly recommended." | ||
131 | "\x1B[0m\n"); | ||
132 | if (!nvidia_modeset || !nvidia_uvm || !nvidia_drm) { | ||
133 | fprintf(stderr, "\x1B[1;31mWarning: You do not have all of the " | ||
134 | "necessary kernel modules loaded for nvidia support. " | ||
135 | "You need nvidia, nvidia_modeset, nvidia_uvm, and nvidia_drm." | ||
136 | "\x1B[0m\n"); | ||
137 | } | ||
138 | #ifdef __linux__ | ||
139 | f = fopen("/sys/module/nvidia_drm/parameters/modeset", "r"); | ||
140 | if (f) { | ||
141 | char *line = read_line(f); | ||
142 | if (line && strstr(line, "Y")) { | ||
143 | // nvidia-drm.modeset is set to 0 | ||
144 | fprintf(stderr, "\x1B[1;31mWarning: You must load " | ||
145 | "nvidia-drm with the modeset option on to use " | ||
146 | "the proprietary driver. Consider adding " | ||
147 | "nvidia-drm.modeset=1 to your kernel command line " | ||
148 | "parameters.\x1B[0m\n"); | ||
149 | } | ||
150 | fclose(f); | ||
151 | free(line); | ||
152 | } else { | ||
153 | // nvidia-drm.modeset is not set | ||
154 | fprintf(stderr, "\x1B[1;31mWarning: You must load " | ||
155 | "nvidia-drm with the modeset option on to use " | ||
156 | "the proprietary driver. Consider adding " | ||
157 | "nvidia-drm.modeset=1 to your kernel command line " | ||
158 | "parameters.\x1B[0m\n"); | ||
159 | } | ||
160 | #else | ||
161 | f = fopen("/proc/cmdline", "r"); | ||
162 | if (f) { | ||
163 | char *line = read_line(f); | ||
164 | if (line && !strstr(line, "nvidia-drm.modeset=1")) { | ||
165 | fprintf(stderr, "\x1B[1;31mWarning: You must add " | ||
166 | "nvidia-drm.modeset=1 to your kernel command line to use " | ||
167 | "the proprietary driver.\x1B[0m\n"); | ||
168 | } | ||
169 | fclose(f); | ||
170 | free(line); | ||
171 | } | ||
172 | #endif | ||
173 | } | ||
174 | } | 109 | } |
175 | 110 | ||
176 | void run_as_ipc_client(char *command, char *socket_path) { | 111 | void run_as_ipc_client(char *command, char *socket_path) { |
@@ -184,27 +119,15 @@ void run_as_ipc_client(char *command, char *socket_path) { | |||
184 | static void log_env() { | 119 | static void log_env() { |
185 | const char *log_vars[] = { | 120 | const char *log_vars[] = { |
186 | "PATH", | 121 | "PATH", |
187 | "LD_LOAD_PATH", | 122 | "LD_LIBRARY_PATH", |
188 | "LD_PRELOAD_PATH", | 123 | "LD_PRELOAD_PATH", |
189 | "LD_LIBRARY_PATH", | 124 | "LD_LIBRARY_PATH", |
190 | "SWAY_CURSOR_THEME", | 125 | "SWAY_CURSOR_THEME", |
191 | "SWAY_CURSOR_SIZE", | 126 | "SWAY_CURSOR_SIZE", |
192 | "SWAYSOCK", | 127 | "SWAYSOCK" |
193 | "WLC_DRM_DEVICE", | ||
194 | "WLC_SHM", | ||
195 | "WLC_OUTPUTS", | ||
196 | "WLC_XWAYLAND", | ||
197 | "WLC_LIBINPUT", | ||
198 | "WLC_REPEAT_DELAY", | ||
199 | "WLC_REPEAT_RATE", | ||
200 | "XKB_DEFAULT_RULES", | ||
201 | "XKB_DEFAULT_MODEL", | ||
202 | "XKB_DEFAULT_LAYOUT", | ||
203 | "XKB_DEFAULT_VARIANT", | ||
204 | "XKB_DEFAULT_OPTIONS", | ||
205 | }; | 128 | }; |
206 | for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) { | 129 | for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) { |
207 | sway_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i])); | 130 | wlr_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i])); |
208 | } | 131 | } |
209 | } | 132 | } |
210 | 133 | ||
@@ -219,14 +142,14 @@ static void log_distro() { | |||
219 | for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) { | 142 | for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) { |
220 | FILE *f = fopen(paths[i], "r"); | 143 | FILE *f = fopen(paths[i], "r"); |
221 | if (f) { | 144 | if (f) { |
222 | sway_log(L_INFO, "Contents of %s:", paths[i]); | 145 | wlr_log(L_INFO, "Contents of %s:", paths[i]); |
223 | while (!feof(f)) { | 146 | while (!feof(f)) { |
224 | char *line; | 147 | char *line; |
225 | if (!(line = read_line(f))) { | 148 | if (!(line = read_line(f))) { |
226 | break; | 149 | break; |
227 | } | 150 | } |
228 | if (*line) { | 151 | if (*line) { |
229 | sway_log(L_INFO, "%s", line); | 152 | wlr_log(L_INFO, "%s", line); |
230 | } | 153 | } |
231 | free(line); | 154 | free(line); |
232 | } | 155 | } |
@@ -236,9 +159,10 @@ static void log_distro() { | |||
236 | } | 159 | } |
237 | 160 | ||
238 | static void log_kernel() { | 161 | static void log_kernel() { |
162 | return; | ||
239 | FILE *f = popen("uname -a", "r"); | 163 | FILE *f = popen("uname -a", "r"); |
240 | if (!f) { | 164 | if (!f) { |
241 | sway_log(L_INFO, "Unable to determine kernel version"); | 165 | wlr_log(L_INFO, "Unable to determine kernel version"); |
242 | return; | 166 | return; |
243 | } | 167 | } |
244 | while (!feof(f)) { | 168 | while (!feof(f)) { |
@@ -247,7 +171,7 @@ static void log_kernel() { | |||
247 | break; | 171 | break; |
248 | } | 172 | } |
249 | if (*line) { | 173 | if (*line) { |
250 | sway_log(L_INFO, "%s", line); | 174 | wlr_log(L_INFO, "%s", line); |
251 | } | 175 | } |
252 | free(line); | 176 | free(line); |
253 | } | 177 | } |
@@ -258,14 +182,14 @@ static void security_sanity_check() { | |||
258 | // TODO: Notify users visually if this has issues | 182 | // TODO: Notify users visually if this has issues |
259 | struct stat s; | 183 | struct stat s; |
260 | if (stat("/proc", &s)) { | 184 | if (stat("/proc", &s)) { |
261 | sway_log(L_ERROR, | 185 | wlr_log(L_ERROR, |
262 | "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!"); | 186 | "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!"); |
263 | } | 187 | } |
264 | #ifdef __linux__ | 188 | #ifdef __linux__ |
265 | cap_flag_value_t v; | 189 | cap_flag_value_t v; |
266 | cap_t cap = cap_get_proc(); | 190 | cap_t cap = cap_get_proc(); |
267 | if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) { | 191 | if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) { |
268 | sway_log(L_ERROR, | 192 | wlr_log(L_ERROR, |
269 | "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users."); | 193 | "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users."); |
270 | } | 194 | } |
271 | if (cap) { | 195 | if (cap) { |
@@ -281,13 +205,13 @@ static void executable_sanity_check() { | |||
281 | stat(exe, &sb); | 205 | stat(exe, &sb); |
282 | // We assume that cap_get_file returning NULL implies ENODATA | 206 | // We assume that cap_get_file returning NULL implies ENODATA |
283 | if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) { | 207 | if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) { |
284 | sway_log(L_ERROR, | 208 | wlr_log(L_ERROR, |
285 | "sway executable has both the s(g)uid bit AND file caps set."); | 209 | "sway executable has both the s(g)uid bit AND file caps set."); |
286 | sway_log(L_ERROR, | 210 | wlr_log(L_ERROR, |
287 | "This is strongly discouraged (and completely broken)."); | 211 | "This is strongly discouraged (and completely broken)."); |
288 | sway_log(L_ERROR, | 212 | wlr_log(L_ERROR, |
289 | "Please clear one of them (either the suid bit, or the file caps)."); | 213 | "Please clear one of them (either the suid bit, or the file caps)."); |
290 | sway_log(L_ERROR, | 214 | wlr_log(L_ERROR, |
291 | "If unsure, strip the file caps."); | 215 | "If unsure, strip the file caps."); |
292 | exit(EXIT_FAILURE); | 216 | exit(EXIT_FAILURE); |
293 | } | 217 | } |
@@ -295,6 +219,37 @@ static void executable_sanity_check() { | |||
295 | #endif | 219 | #endif |
296 | } | 220 | } |
297 | 221 | ||
222 | static void drop_permissions(bool keep_caps) { | ||
223 | if (getuid() != geteuid() || getgid() != getegid()) { | ||
224 | if (setgid(getgid()) != 0) { | ||
225 | wlr_log(L_ERROR, "Unable to drop root"); | ||
226 | exit(EXIT_FAILURE); | ||
227 | } | ||
228 | if (setuid(getuid()) != 0) { | ||
229 | wlr_log(L_ERROR, "Unable to drop root"); | ||
230 | exit(EXIT_FAILURE); | ||
231 | } | ||
232 | } | ||
233 | if (setuid(0) != -1) { | ||
234 | wlr_log(L_ERROR, "Root privileges can be restored."); | ||
235 | exit(EXIT_FAILURE); | ||
236 | } | ||
237 | #ifdef __linux__ | ||
238 | if (keep_caps) { | ||
239 | // Drop every cap except CAP_SYS_PTRACE | ||
240 | cap_t caps = cap_init(); | ||
241 | cap_value_t keep = CAP_SYS_PTRACE; | ||
242 | wlr_log(L_INFO, "Dropping extra capabilities"); | ||
243 | if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) || | ||
244 | cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) || | ||
245 | cap_set_proc(caps)) { | ||
246 | wlr_log(L_ERROR, "Failed to drop extra capabilities"); | ||
247 | exit(EXIT_FAILURE); | ||
248 | } | ||
249 | } | ||
250 | #endif | ||
251 | } | ||
252 | |||
298 | int main(int argc, char **argv) { | 253 | int main(int argc, char **argv) { |
299 | static int verbose = 0, debug = 0, validate = 0; | 254 | static int verbose = 0, debug = 0, validate = 0; |
300 | 255 | ||
@@ -334,7 +289,7 @@ int main(int argc, char **argv) { | |||
334 | int c; | 289 | int c; |
335 | while (1) { | 290 | while (1) { |
336 | int option_index = 0; | 291 | int option_index = 0; |
337 | c = getopt_long(argc, argv, "hCdvVc:", long_options, &option_index); | 292 | c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index); |
338 | if (c == -1) { | 293 | if (c == -1) { |
339 | break; | 294 | break; |
340 | } | 295 | } |
@@ -352,6 +307,9 @@ int main(int argc, char **argv) { | |||
352 | case 'd': // debug | 307 | case 'd': // debug |
353 | debug = 1; | 308 | debug = 1; |
354 | break; | 309 | break; |
310 | case 'D': // extended debug options | ||
311 | enable_debug_tree = true; | ||
312 | break; | ||
355 | case 'v': // version | 313 | case 'v': // version |
356 | fprintf(stdout, "sway version " SWAY_VERSION "\n"); | 314 | fprintf(stdout, "sway version " SWAY_VERSION "\n"); |
357 | exit(EXIT_SUCCESS); | 315 | exit(EXIT_SUCCESS); |
@@ -374,37 +332,24 @@ int main(int argc, char **argv) { | |||
374 | } | 332 | } |
375 | } | 333 | } |
376 | 334 | ||
377 | // we need to setup logging before wlc_init in case it fails. | 335 | // TODO: switch logging over to wlroots? |
378 | if (debug) { | 336 | if (debug) { |
379 | init_log(L_DEBUG); | 337 | wlr_log_init(L_DEBUG, NULL); |
380 | } else if (verbose || validate) { | 338 | } else if (verbose || validate) { |
381 | init_log(L_INFO); | 339 | wlr_log_init(L_INFO, NULL); |
382 | } else { | 340 | } else { |
383 | init_log(L_ERROR); | 341 | wlr_log_init(L_ERROR, NULL); |
384 | } | 342 | } |
385 | 343 | ||
386 | if (optind < argc) { // Behave as IPC client | 344 | if (optind < argc) { // Behave as IPC client |
387 | if(optind != 1) { | 345 | if(optind != 1) { |
388 | sway_log(L_ERROR, "Don't use options with the IPC client"); | 346 | wlr_log(L_ERROR, "Don't use options with the IPC client"); |
389 | exit(EXIT_FAILURE); | ||
390 | } | ||
391 | if (getuid() != geteuid() || getgid() != getegid()) { | ||
392 | if (setgid(getgid()) != 0) { | ||
393 | sway_log(L_ERROR, "Unable to drop root"); | ||
394 | exit(EXIT_FAILURE); | ||
395 | } | ||
396 | if (setuid(getuid()) != 0) { | ||
397 | sway_log(L_ERROR, "Unable to drop root"); | ||
398 | exit(EXIT_FAILURE); | ||
399 | } | ||
400 | } | ||
401 | if (setuid(0) != -1) { | ||
402 | sway_log(L_ERROR, "Root privileges can be restored."); | ||
403 | exit(EXIT_FAILURE); | 347 | exit(EXIT_FAILURE); |
404 | } | 348 | } |
349 | drop_permissions(false); | ||
405 | char *socket_path = getenv("SWAYSOCK"); | 350 | char *socket_path = getenv("SWAYSOCK"); |
406 | if (!socket_path) { | 351 | if (!socket_path) { |
407 | sway_log(L_ERROR, "Unable to retrieve socket path"); | 352 | wlr_log(L_ERROR, "Unable to retrieve socket path"); |
408 | exit(EXIT_FAILURE); | 353 | exit(EXIT_FAILURE); |
409 | } | 354 | } |
410 | char *command = join_args(argv + optind, argc - optind); | 355 | char *command = join_args(argv + optind, argc - optind); |
@@ -413,49 +358,25 @@ int main(int argc, char **argv) { | |||
413 | } | 358 | } |
414 | 359 | ||
415 | executable_sanity_check(); | 360 | executable_sanity_check(); |
416 | #ifdef __linux__ | ||
417 | bool suid = false; | 361 | bool suid = false; |
362 | #ifdef __linux__ | ||
418 | if (getuid() != geteuid() || getgid() != getegid()) { | 363 | if (getuid() != geteuid() || getgid() != getegid()) { |
419 | // Retain capabilities after setuid() | 364 | // Retain capabilities after setuid() |
420 | if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { | 365 | if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { |
421 | sway_log(L_ERROR, "Cannot keep caps after setuid()"); | 366 | wlr_log(L_ERROR, "Cannot keep caps after setuid()"); |
422 | exit(EXIT_FAILURE); | 367 | exit(EXIT_FAILURE); |
423 | } | 368 | } |
424 | suid = true; | 369 | suid = true; |
425 | } | 370 | } |
426 | #endif | 371 | #endif |
427 | 372 | ||
428 | wlc_log_set_handler(wlc_log_handler); | ||
429 | log_kernel(); | 373 | log_kernel(); |
430 | log_distro(); | 374 | log_distro(); |
431 | log_env(); | ||
432 | detect_proprietary(); | 375 | detect_proprietary(); |
433 | detect_raspi(); | 376 | detect_raspi(); |
434 | 377 | ||
435 | input_devices = create_list(); | ||
436 | |||
437 | /* Changing code earlier than this point requires detailed review */ | ||
438 | /* (That code runs as root on systems without logind, and wlc_init drops to | ||
439 | * another user.) */ | ||
440 | register_wlc_handlers(); | ||
441 | if (!wlc_init()) { | ||
442 | return 1; | ||
443 | } | ||
444 | register_extensions(); | ||
445 | |||
446 | #ifdef __linux__ | 378 | #ifdef __linux__ |
447 | if (suid) { | 379 | drop_permissions(suid); |
448 | // Drop every cap except CAP_SYS_PTRACE | ||
449 | cap_t caps = cap_init(); | ||
450 | cap_value_t keep = CAP_SYS_PTRACE; | ||
451 | sway_log(L_INFO, "Dropping extra capabilities"); | ||
452 | if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) || | ||
453 | cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) || | ||
454 | cap_set_proc(caps)) { | ||
455 | sway_log(L_ERROR, "Failed to drop extra capabilities"); | ||
456 | exit(EXIT_FAILURE); | ||
457 | } | ||
458 | } | ||
459 | #endif | 380 | #endif |
460 | // handle SIGTERM signals | 381 | // handle SIGTERM signals |
461 | signal(SIGTERM, sig_handler); | 382 | signal(SIGTERM, sig_handler); |
@@ -463,11 +384,16 @@ int main(int argc, char **argv) { | |||
463 | // prevent ipc from crashing sway | 384 | // prevent ipc from crashing sway |
464 | signal(SIGPIPE, SIG_IGN); | 385 | signal(SIGPIPE, SIG_IGN); |
465 | 386 | ||
466 | sway_log(L_INFO, "Starting sway version " SWAY_VERSION "\n"); | 387 | wlr_log(L_INFO, "Starting sway version " SWAY_VERSION); |
388 | |||
389 | layout_init(); | ||
467 | 390 | ||
468 | init_layout(); | 391 | if (!server_init(&server)) { |
392 | return 1; | ||
393 | } | ||
469 | 394 | ||
470 | ipc_init(); | 395 | ipc_init(&server); |
396 | log_env(); | ||
471 | 397 | ||
472 | if (validate) { | 398 | if (validate) { |
473 | bool valid = load_main_config(config_path, false); | 399 | bool valid = load_main_config(config_path, false); |
@@ -484,11 +410,17 @@ int main(int argc, char **argv) { | |||
484 | 410 | ||
485 | security_sanity_check(); | 411 | security_sanity_check(); |
486 | 412 | ||
413 | // TODO: wait for server to be ready | ||
414 | // TODO: consume config->cmd_queue | ||
415 | config->active = true; | ||
416 | |||
487 | if (!terminate_request) { | 417 | if (!terminate_request) { |
488 | wlc_run(); | 418 | server_run(&server); |
489 | } | 419 | } |
490 | 420 | ||
491 | list_free(input_devices); | 421 | wlr_log(L_INFO, "Shutting down sway"); |
422 | |||
423 | server_fini(&server); | ||
492 | 424 | ||
493 | ipc_terminate(); | 425 | ipc_terminate(); |
494 | 426 | ||
diff --git a/sway/meson.build b/sway/meson.build new file mode 100644 index 00000000..9e55e335 --- /dev/null +++ b/sway/meson.build | |||
@@ -0,0 +1,131 @@ | |||
1 | sway_sources = files( | ||
2 | 'main.c', | ||
3 | 'server.c', | ||
4 | 'commands.c', | ||
5 | 'config.c', | ||
6 | 'criteria.c', | ||
7 | 'debug-tree.c', | ||
8 | 'ipc-json.c', | ||
9 | 'ipc-server.c', | ||
10 | 'security.c', | ||
11 | |||
12 | 'desktop/desktop.c', | ||
13 | 'desktop/output.c', | ||
14 | 'desktop/layer_shell.c', | ||
15 | 'desktop/wl_shell.c', | ||
16 | 'desktop/xdg_shell_v6.c', | ||
17 | 'desktop/xwayland.c', | ||
18 | |||
19 | 'input/input-manager.c', | ||
20 | 'input/seat.c', | ||
21 | 'input/cursor.c', | ||
22 | 'input/keyboard.c', | ||
23 | |||
24 | 'config/bar.c', | ||
25 | 'config/output.c', | ||
26 | 'config/seat.c', | ||
27 | 'config/input.c', | ||
28 | |||
29 | 'commands/bar.c', | ||
30 | 'commands/bind.c', | ||
31 | 'commands/default_orientation.c', | ||
32 | 'commands/exit.c', | ||
33 | 'commands/exec.c', | ||
34 | 'commands/exec_always.c', | ||
35 | 'commands/focus.c', | ||
36 | 'commands/focus_follows_mouse.c', | ||
37 | 'commands/kill.c', | ||
38 | 'commands/opacity.c', | ||
39 | 'commands/include.c', | ||
40 | 'commands/input.c', | ||
41 | 'commands/layout.c', | ||
42 | 'commands/mode.c', | ||
43 | 'commands/mouse_warping.c', | ||
44 | 'commands/move.c', | ||
45 | 'commands/output.c', | ||
46 | 'commands/reload.c', | ||
47 | 'commands/resize.c', | ||
48 | 'commands/seat.c', | ||
49 | 'commands/seat/attach.c', | ||
50 | 'commands/seat/cursor.c', | ||
51 | 'commands/seat/fallback.c', | ||
52 | 'commands/set.c', | ||
53 | 'commands/split.c', | ||
54 | 'commands/swaybg_command.c', | ||
55 | 'commands/workspace.c', | ||
56 | 'commands/ws_auto_back_and_forth.c', | ||
57 | |||
58 | 'commands/bar/activate_button.c', | ||
59 | 'commands/bar/binding_mode_indicator.c', | ||
60 | 'commands/bar/bindsym.c', | ||
61 | 'commands/bar/colors.c', | ||
62 | 'commands/bar/context_button.c', | ||
63 | 'commands/bar/font.c', | ||
64 | 'commands/bar/height.c', | ||
65 | 'commands/bar/hidden_state.c', | ||
66 | 'commands/bar/icon_theme.c', | ||
67 | 'commands/bar/id.c', | ||
68 | 'commands/bar/mode.c', | ||
69 | 'commands/bar/modifier.c', | ||
70 | 'commands/bar/output.c', | ||
71 | 'commands/bar/pango_markup.c', | ||
72 | 'commands/bar/position.c', | ||
73 | 'commands/bar/secondary_button.c', | ||
74 | 'commands/bar/separator_symbol.c', | ||
75 | 'commands/bar/status_command.c', | ||
76 | 'commands/bar/strip_workspace_numbers.c', | ||
77 | 'commands/bar/swaybar_command.c', | ||
78 | 'commands/bar/tray_output.c', | ||
79 | 'commands/bar/tray_padding.c', | ||
80 | 'commands/bar/workspace_buttons.c', | ||
81 | 'commands/bar/wrap_scroll.c', | ||
82 | |||
83 | 'commands/input/accel_profile.c', | ||
84 | 'commands/input/click_method.c', | ||
85 | 'commands/input/drag_lock.c', | ||
86 | 'commands/input/dwt.c', | ||
87 | 'commands/input/events.c', | ||
88 | 'commands/input/left_handed.c', | ||
89 | 'commands/input/map_to_output.c', | ||
90 | 'commands/input/middle_emulation.c', | ||
91 | 'commands/input/natural_scroll.c', | ||
92 | 'commands/input/pointer_accel.c', | ||
93 | 'commands/input/scroll_method.c', | ||
94 | 'commands/input/tap.c', | ||
95 | 'commands/input/xkb_layout.c', | ||
96 | 'commands/input/xkb_model.c', | ||
97 | 'commands/input/xkb_options.c', | ||
98 | 'commands/input/xkb_rules.c', | ||
99 | 'commands/input/xkb_variant.c', | ||
100 | |||
101 | 'tree/container.c', | ||
102 | 'tree/layout.c', | ||
103 | 'tree/view.c', | ||
104 | 'tree/workspace.c', | ||
105 | 'tree/output.c', | ||
106 | ) | ||
107 | |||
108 | sway_deps = [ | ||
109 | cairo, | ||
110 | gdk_pixbuf, | ||
111 | jsonc, | ||
112 | libcap, | ||
113 | libinput, | ||
114 | math, | ||
115 | pango, | ||
116 | pcre, | ||
117 | pixman, | ||
118 | server_protos, | ||
119 | wayland_server, | ||
120 | wlroots, | ||
121 | xkbcommon, | ||
122 | ] | ||
123 | |||
124 | executable( | ||
125 | 'sway', | ||
126 | sway_sources, | ||
127 | include_directories: [sway_inc], | ||
128 | dependencies: sway_deps, | ||
129 | link_with: [lib_sway_common], | ||
130 | install: true | ||
131 | ) | ||
diff --git a/sway/output.c b/sway/output.c deleted file mode 100644 index c0f29c5a..00000000 --- a/sway/output.c +++ /dev/null | |||
@@ -1,277 +0,0 @@ | |||
1 | #include <strings.h> | ||
2 | #include <ctype.h> | ||
3 | #include <stdlib.h> | ||
4 | #include "sway/output.h" | ||
5 | #include "log.h" | ||
6 | #include "list.h" | ||
7 | |||
8 | void output_get_scaled_size(wlc_handle handle, struct wlc_size *size) { | ||
9 | *size = *wlc_output_get_resolution(handle); | ||
10 | uint32_t scale = wlc_output_get_scale(handle); | ||
11 | size->w /= scale; | ||
12 | size->h /= scale; | ||
13 | } | ||
14 | |||
15 | swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos) { | ||
16 | swayc_t *output = NULL; | ||
17 | // If there is no output directly next to the current one, use | ||
18 | // swayc_opposite_output to wrap. | ||
19 | if (strcasecmp(name, "left") == 0) { | ||
20 | output = swayc_adjacent_output(NULL, MOVE_LEFT, abs_pos, true); | ||
21 | if (!output) { | ||
22 | output = swayc_opposite_output(MOVE_RIGHT, abs_pos); | ||
23 | } | ||
24 | } else if (strcasecmp(name, "right") == 0) { | ||
25 | output = swayc_adjacent_output(NULL, MOVE_RIGHT, abs_pos, true); | ||
26 | if (!output) { | ||
27 | output = swayc_opposite_output(MOVE_LEFT, abs_pos); | ||
28 | } | ||
29 | } else if (strcasecmp(name, "up") == 0) { | ||
30 | output = swayc_adjacent_output(NULL, MOVE_UP, abs_pos, true); | ||
31 | if (!output) { | ||
32 | output = swayc_opposite_output(MOVE_DOWN, abs_pos); | ||
33 | } | ||
34 | } else if (strcasecmp(name, "down") == 0) { | ||
35 | output = swayc_adjacent_output(NULL, MOVE_DOWN, abs_pos, true); | ||
36 | if (!output) { | ||
37 | output = swayc_opposite_output(MOVE_UP, abs_pos); | ||
38 | } | ||
39 | } else { | ||
40 | for(int i = 0; i < root_container.children->length; ++i) { | ||
41 | swayc_t *c = root_container.children->items[i]; | ||
42 | if (c->type == C_OUTPUT && strcasecmp(c->name, name) == 0) { | ||
43 | return c; | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | return output; | ||
48 | } | ||
49 | |||
50 | swayc_t *swayc_opposite_output(enum movement_direction dir, | ||
51 | const struct wlc_point *abs_pos) { | ||
52 | |||
53 | // Search through all the outputs and pick the output whose edge covers the | ||
54 | // given position, and is at leftmost/rightmost/upmost/downmost side of the | ||
55 | // screen (decided by the direction given). | ||
56 | swayc_t *opposite = NULL; | ||
57 | char *dir_text = NULL; | ||
58 | switch(dir) { | ||
59 | case MOVE_LEFT: | ||
60 | case MOVE_RIGHT: ; | ||
61 | for (int i = 0; i < root_container.children->length; ++i) { | ||
62 | swayc_t *c = root_container.children->items[i]; | ||
63 | if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) { | ||
64 | if (!opposite) { | ||
65 | opposite = c; | ||
66 | } else if ((dir == MOVE_LEFT && c->x < opposite->x) | ||
67 | || (dir == MOVE_RIGHT && c->x > opposite->x)) { | ||
68 | opposite = c; | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | dir_text = dir == MOVE_LEFT ? "leftmost" : "rightmost"; | ||
73 | break; | ||
74 | case MOVE_UP: | ||
75 | case MOVE_DOWN: ; | ||
76 | for (int i = 0; i < root_container.children->length; ++i) { | ||
77 | swayc_t *c = root_container.children->items[i]; | ||
78 | if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) { | ||
79 | if (!opposite) { | ||
80 | opposite = c; | ||
81 | } else if ((dir == MOVE_UP && c->y < opposite->y) | ||
82 | || (dir == MOVE_DOWN && c->y > opposite->y)) { | ||
83 | opposite = c; | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | dir_text = dir == MOVE_UP ? "upmost" : "downmost"; | ||
88 | break; | ||
89 | default: | ||
90 | sway_abort("Function called with invalid argument."); | ||
91 | break; | ||
92 | } | ||
93 | if (opposite) { | ||
94 | sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s from y-position %i", | ||
95 | opposite->name, opposite->width, opposite->height, opposite->x, opposite->y, | ||
96 | dir_text, abs_pos->y); | ||
97 | } | ||
98 | return opposite; | ||
99 | } | ||
100 | |||
101 | // Position is where on the edge (as absolute position) the adjacent output should be searched for. | ||
102 | swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir, | ||
103 | const struct wlc_point *abs_pos, bool pick_closest) { | ||
104 | |||
105 | if (!output) { | ||
106 | output = swayc_active_output(); | ||
107 | } | ||
108 | // In order to find adjacent outputs we need to test that the outputs are | ||
109 | // aligned on one axis (decided by the direction given) and that the given | ||
110 | // position is within the edge of the adjacent output. If no such output | ||
111 | // exists we pick the adjacent output within the edge that is closest to | ||
112 | // the given position, if any. | ||
113 | swayc_t *adjacent = NULL; | ||
114 | char *dir_text = NULL; | ||
115 | switch(dir) { | ||
116 | case MOVE_LEFT: | ||
117 | case MOVE_RIGHT: ; | ||
118 | double delta_y = 0; | ||
119 | for(int i = 0; i < root_container.children->length; ++i) { | ||
120 | swayc_t *c = root_container.children->items[i]; | ||
121 | if (c == output || c->type != C_OUTPUT) { | ||
122 | continue; | ||
123 | } | ||
124 | bool x_aligned = dir == MOVE_LEFT ? | ||
125 | c->x + c->width == output->x : | ||
126 | c->x == output->x + output->width; | ||
127 | if (!x_aligned) { | ||
128 | continue; | ||
129 | } | ||
130 | if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) { | ||
131 | delta_y = 0; | ||
132 | adjacent = c; | ||
133 | break; | ||
134 | } else if (pick_closest) { | ||
135 | // track closest adjacent output | ||
136 | double top_y = c->y, bottom_y = c->y + c->height; | ||
137 | if (top_y >= output->y && top_y <= output->y + output->height) { | ||
138 | double delta = top_y - abs_pos->y; | ||
139 | if (delta < 0) delta = -delta; | ||
140 | if (delta < delta_y || !adjacent) { | ||
141 | delta_y = delta; | ||
142 | adjacent = c; | ||
143 | } | ||
144 | } | ||
145 | // we check both points and pick the closest | ||
146 | if (bottom_y >= output->y && bottom_y <= output->y + output->height) { | ||
147 | double delta = bottom_y - abs_pos->y; | ||
148 | if (delta < 0) delta = -delta; | ||
149 | if (delta < delta_y || !adjacent) { | ||
150 | delta_y = delta; | ||
151 | adjacent = c; | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | dir_text = dir == MOVE_LEFT ? "left of" : "right of"; | ||
157 | if (adjacent && delta_y == 0) { | ||
158 | sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i)", | ||
159 | adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y, | ||
160 | dir_text, output->name, abs_pos->y); | ||
161 | } else if (adjacent) { | ||
162 | // so we end up picking the closest adjacent output because | ||
163 | // there is no directly adjacent to the given position | ||
164 | sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i, delta: %.0f)", | ||
165 | adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y, | ||
166 | dir_text, output->name, abs_pos->y, delta_y); | ||
167 | } | ||
168 | break; | ||
169 | case MOVE_UP: | ||
170 | case MOVE_DOWN: ; | ||
171 | double delta_x = 0; | ||
172 | for(int i = 0; i < root_container.children->length; ++i) { | ||
173 | swayc_t *c = root_container.children->items[i]; | ||
174 | if (c == output || c->type != C_OUTPUT) { | ||
175 | continue; | ||
176 | } | ||
177 | bool y_aligned = dir == MOVE_UP ? | ||
178 | c->y + c->height == output->y : | ||
179 | c->y == output->y + output->height; | ||
180 | if (!y_aligned) { | ||
181 | continue; | ||
182 | } | ||
183 | if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) { | ||
184 | delta_x = 0; | ||
185 | adjacent = c; | ||
186 | break; | ||
187 | } else if (pick_closest) { | ||
188 | // track closest adjacent output | ||
189 | double left_x = c->x, right_x = c->x + c->width; | ||
190 | if (left_x >= output->x && left_x <= output->x + output->width) { | ||
191 | double delta = left_x - abs_pos->x; | ||
192 | if (delta < 0) delta = -delta; | ||
193 | if (delta < delta_x || !adjacent) { | ||
194 | delta_x = delta; | ||
195 | adjacent = c; | ||
196 | } | ||
197 | } | ||
198 | // we check both points and pick the closest | ||
199 | if (right_x >= output->x && right_x <= output->x + output->width) { | ||
200 | double delta = right_x - abs_pos->x; | ||
201 | if (delta < 0) delta = -delta; | ||
202 | if (delta < delta_x || !adjacent) { | ||
203 | delta_x = delta; | ||
204 | adjacent = c; | ||
205 | } | ||
206 | } | ||
207 | } | ||
208 | } | ||
209 | dir_text = dir == MOVE_UP ? "above" : "below"; | ||
210 | if (adjacent && delta_x == 0) { | ||
211 | sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i)", | ||
212 | adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y, | ||
213 | dir_text, output->name, abs_pos->x); | ||
214 | } else if (adjacent) { | ||
215 | // so we end up picking the closest adjacent output because | ||
216 | // there is no directly adjacent to the given position | ||
217 | sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i, delta: %.0f)", | ||
218 | adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y, | ||
219 | dir_text, output->name, abs_pos->x, delta_x); | ||
220 | } | ||
221 | break; | ||
222 | default: | ||
223 | sway_abort("Function called with invalid argument."); | ||
224 | break; | ||
225 | } | ||
226 | return adjacent; | ||
227 | } | ||
228 | |||
229 | void get_absolute_position(swayc_t *container, struct wlc_point *point) { | ||
230 | if (!container || !point) | ||
231 | sway_abort("Need container and wlc_point (was %p, %p).", container, point); | ||
232 | |||
233 | if (container->type == C_OUTPUT) { | ||
234 | // Coordinates are already absolute. | ||
235 | point->x = container->x; | ||
236 | point->y = container->y; | ||
237 | } else { | ||
238 | swayc_t *output = swayc_parent_by_type(container, C_OUTPUT); | ||
239 | if (container->type == C_WORKSPACE) { | ||
240 | // Workspace coordinates are actually wrong/arbitrary, but should | ||
241 | // be same as output. | ||
242 | point->x = output->x; | ||
243 | point->y = output->y; | ||
244 | } else { | ||
245 | point->x = output->x + container->x; | ||
246 | point->y = output->y + container->y; | ||
247 | } | ||
248 | } | ||
249 | } | ||
250 | |||
251 | void get_absolute_center_position(swayc_t *container, struct wlc_point *point) { | ||
252 | get_absolute_position(container, point); | ||
253 | point->x += container->width/2; | ||
254 | point->y += container->height/2; | ||
255 | } | ||
256 | |||
257 | static int sort_workspace_cmp_qsort(const void *_a, const void *_b) { | ||
258 | swayc_t *a = *(void **)_a; | ||
259 | swayc_t *b = *(void **)_b; | ||
260 | int retval = 0; | ||
261 | |||
262 | if (isdigit(a->name[0]) && isdigit(b->name[0])) { | ||
263 | int a_num = strtol(a->name, NULL, 10); | ||
264 | int b_num = strtol(b->name, NULL, 10); | ||
265 | retval = (a_num < b_num) ? -1 : (a_num > b_num); | ||
266 | } else if (isdigit(a->name[0])) { | ||
267 | retval = -1; | ||
268 | } else if (isdigit(b->name[0])) { | ||
269 | retval = 1; | ||
270 | } | ||
271 | |||
272 | return retval; | ||
273 | } | ||
274 | |||
275 | void sort_workspaces(swayc_t *output) { | ||
276 | list_stable_sort(output->children, sort_workspace_cmp_qsort); | ||
277 | } | ||
diff --git a/sway/security.c b/sway/security.c index fcd70f9d..cc0d3f66 100644 --- a/sway/security.c +++ b/sway/security.c | |||
@@ -1,102 +1,7 @@ | |||
1 | #define _XOPEN_SOURCE 700 | 1 | #define _XOPEN_SOURCE 700 |
2 | #include <sys/types.h> | 2 | #include <stdlib.h> |
3 | #include <sys/stat.h> | ||
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <unistd.h> | ||
6 | #include <stdio.h> | ||
7 | #include "sway/config.h" | ||
8 | #include "sway/security.h" | 4 | #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 | 5 | ||
101 | struct command_policy *alloc_command_policy(const char *command) { | 6 | struct command_policy *alloc_command_policy(const char *command) { |
102 | struct command_policy *policy = malloc(sizeof(struct command_policy)); | 7 | struct command_policy *policy = malloc(sizeof(struct command_policy)); |
@@ -111,118 +16,3 @@ struct command_policy *alloc_command_policy(const char *command) { | |||
111 | policy->context = 0; | 16 | policy->context = 0; |
112 | return policy; | 17 | return policy; |
113 | } | 18 | } |
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/server.c b/sway/server.c new file mode 100644 index 00000000..c1125f14 --- /dev/null +++ b/sway/server.c | |||
@@ -0,0 +1,141 @@ | |||
1 | #define _POSIX_C_SOURCE 200112L | ||
2 | #include <assert.h> | ||
3 | #include <stdbool.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <wayland-server.h> | ||
6 | #include <wlr/backend.h> | ||
7 | #include <wlr/backend/session.h> | ||
8 | #include <wlr/render/wlr_renderer.h> | ||
9 | #include <wlr/types/wlr_compositor.h> | ||
10 | #include <wlr/types/wlr_gamma_control.h> | ||
11 | #include <wlr/types/wlr_linux_dmabuf.h> | ||
12 | #include <wlr/types/wlr_layer_shell.h> | ||
13 | #include <wlr/types/wlr_primary_selection.h> | ||
14 | #include <wlr/types/wlr_screenshooter.h> | ||
15 | #include <wlr/types/wlr_server_decoration.h> | ||
16 | #include <wlr/types/wlr_xcursor_manager.h> | ||
17 | #include <wlr/types/wlr_xdg_output.h> | ||
18 | #include <wlr/types/wlr_wl_shell.h> | ||
19 | #include <wlr/util/log.h> | ||
20 | // TODO WLR: make Xwayland optional | ||
21 | #include <wlr/xwayland.h> | ||
22 | #include "sway/commands.h" | ||
23 | #include "sway/config.h" | ||
24 | #include "sway/input/input-manager.h" | ||
25 | #include "sway/server.h" | ||
26 | #include "sway/tree/layout.h" | ||
27 | |||
28 | static void server_ready(struct wl_listener *listener, void *data) { | ||
29 | wlr_log(L_DEBUG, "Compositor is ready, executing cmds in queue"); | ||
30 | // Execute commands until there are none left | ||
31 | config->active = true; | ||
32 | while (config->cmd_queue->length) { | ||
33 | char *line = config->cmd_queue->items[0]; | ||
34 | struct cmd_results *res = execute_command(line, NULL); | ||
35 | if (res->status != CMD_SUCCESS) { | ||
36 | wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error); | ||
37 | } | ||
38 | free_cmd_results(res); | ||
39 | free(line); | ||
40 | list_del(config->cmd_queue, 0); | ||
41 | } | ||
42 | } | ||
43 | |||
44 | bool server_init(struct sway_server *server) { | ||
45 | wlr_log(L_DEBUG, "Initializing Wayland server"); | ||
46 | |||
47 | server->wl_display = wl_display_create(); | ||
48 | server->wl_event_loop = wl_display_get_event_loop(server->wl_display); | ||
49 | server->backend = wlr_backend_autocreate(server->wl_display); | ||
50 | |||
51 | struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); | ||
52 | assert(renderer); | ||
53 | |||
54 | wl_display_init_shm(server->wl_display); | ||
55 | |||
56 | server->compositor = wlr_compositor_create(server->wl_display, renderer); | ||
57 | server->data_device_manager = | ||
58 | wlr_data_device_manager_create(server->wl_display); | ||
59 | |||
60 | wlr_screenshooter_create(server->wl_display); | ||
61 | wlr_gamma_control_manager_create(server->wl_display); | ||
62 | wlr_primary_selection_device_manager_create(server->wl_display); | ||
63 | |||
64 | server->new_output.notify = handle_new_output; | ||
65 | wl_signal_add(&server->backend->events.new_output, &server->new_output); | ||
66 | |||
67 | wlr_xdg_output_manager_create(server->wl_display, | ||
68 | root_container.sway_root->output_layout); | ||
69 | |||
70 | server->layer_shell = wlr_layer_shell_create(server->wl_display); | ||
71 | wl_signal_add(&server->layer_shell->events.new_surface, | ||
72 | &server->layer_shell_surface); | ||
73 | server->layer_shell_surface.notify = handle_layer_shell_surface; | ||
74 | |||
75 | server->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display); | ||
76 | wl_signal_add(&server->xdg_shell_v6->events.new_surface, | ||
77 | &server->xdg_shell_v6_surface); | ||
78 | server->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface; | ||
79 | |||
80 | server->wl_shell = wlr_wl_shell_create(server->wl_display); | ||
81 | wl_signal_add(&server->wl_shell->events.new_surface, | ||
82 | &server->wl_shell_surface); | ||
83 | server->wl_shell_surface.notify = handle_wl_shell_surface; | ||
84 | |||
85 | // TODO make xwayland optional | ||
86 | server->xwayland = | ||
87 | wlr_xwayland_create(server->wl_display, server->compositor); | ||
88 | wl_signal_add(&server->xwayland->events.new_surface, | ||
89 | &server->xwayland_surface); | ||
90 | server->xwayland_surface.notify = handle_xwayland_surface; | ||
91 | wl_signal_add(&server->xwayland->events.ready, | ||
92 | &server->xwayland_ready); | ||
93 | // TODO: call server_ready now if xwayland is not enabled | ||
94 | server->xwayland_ready.notify = server_ready; | ||
95 | |||
96 | // TODO: configurable cursor theme and size | ||
97 | server->xcursor_manager = wlr_xcursor_manager_create(NULL, 24); | ||
98 | wlr_xcursor_manager_load(server->xcursor_manager, 1); | ||
99 | struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( | ||
100 | server->xcursor_manager, "left_ptr", 1); | ||
101 | if (xcursor != NULL) { | ||
102 | struct wlr_xcursor_image *image = xcursor->images[0]; | ||
103 | wlr_xwayland_set_cursor(server->xwayland, image->buffer, | ||
104 | image->width * 4, image->width, image->height, image->hotspot_x, | ||
105 | image->hotspot_y); | ||
106 | } | ||
107 | |||
108 | // TODO: Integration with sway borders | ||
109 | struct wlr_server_decoration_manager *deco_manager = | ||
110 | wlr_server_decoration_manager_create(server->wl_display); | ||
111 | wlr_server_decoration_manager_set_default_mode( | ||
112 | deco_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); | ||
113 | |||
114 | wlr_linux_dmabuf_create(server->wl_display, renderer); | ||
115 | |||
116 | server->socket = wl_display_add_socket_auto(server->wl_display); | ||
117 | if (!server->socket) { | ||
118 | wlr_log(L_ERROR, "Unable to open wayland socket"); | ||
119 | wlr_backend_destroy(server->backend); | ||
120 | return false; | ||
121 | } | ||
122 | |||
123 | input_manager = input_manager_create(server); | ||
124 | return true; | ||
125 | } | ||
126 | |||
127 | void server_fini(struct sway_server *server) { | ||
128 | // TODO | ||
129 | } | ||
130 | |||
131 | void server_run(struct sway_server *server) { | ||
132 | wlr_log(L_INFO, "Running compositor on wayland display '%s'", | ||
133 | server->socket); | ||
134 | setenv("WAYLAND_DISPLAY", server->socket, true); | ||
135 | if (!wlr_backend_start(server->backend)) { | ||
136 | wlr_log(L_ERROR, "Failed to start backend"); | ||
137 | wlr_backend_destroy(server->backend); | ||
138 | return; | ||
139 | } | ||
140 | wl_display_run(server->wl_display); | ||
141 | } | ||
diff --git a/sway/sway-input.5.txt b/sway/sway-input.5.txt index f0c8f87c..05725360 100644 --- a/sway/sway-input.5.txt +++ b/sway/sway-input.5.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | ///// | 1 | ///// |
2 | vim:set ts=4 sw=4 tw=82 noet: | 2 | vim:set ft=asciidoc ts=4 sw=4 tw=82 noet: |
3 | ///// | 3 | ///// |
4 | sway-input (5) | 4 | sway-input (5) |
5 | ============== | 5 | ============== |
@@ -11,12 +11,57 @@ sway-input - input configuration file and commands | |||
11 | Description | 11 | Description |
12 | ----------- | 12 | ----------- |
13 | 13 | ||
14 | Sway allows for configuration of libinput devices within the sway configuration file. | 14 | Sway allows for configuration of devices within the sway configuration file. |
15 | sway-input commands must be used inside an _input { }_ block in the config. | 15 | sway-input commands must be used inside an _input { }_ block in the config. |
16 | To obtain a list of available device identifiers, run **swaymsg -t get_inputs**. | 16 | To obtain a list of available device identifiers, run **swaymsg -t get_inputs**. |
17 | 17 | ||
18 | Commands | 18 | Input Commands |
19 | -------- | 19 | -------------- |
20 | |||
21 | Keyboard Configuration | ||
22 | ~~~~~~~~~~~~~~~~~~~~~~ | ||
23 | |||
24 | For more information on these xkb configuration options, see | ||
25 | **xkeyboard-config**(7). | ||
26 | |||
27 | **input** <identifier> xkb_layout <layout_name>:: | ||
28 | Sets the layout of the keyboard like _us_ or _de_. | ||
29 | |||
30 | **input** <identifier> xkb_model <model_name>:: | ||
31 | Sets the model of the keyboard. This has an influence for some extra keys your | ||
32 | keyboard might have. | ||
33 | |||
34 | **input** <identifier> xkb_options <options>:: | ||
35 | Sets extra xkb configuration options for the keyboard. | ||
36 | |||
37 | **input** <identifier> xkb_rules <rules>:: | ||
38 | Sets files of rules to be used for keyboard mapping composition. | ||
39 | |||
40 | **input** <identifier> xkb_variant <variant>:: | ||
41 | Sets the variant of the keyboard like _dvorak_ or _colemak_. | ||
42 | |||
43 | Mapping Configuration | ||
44 | --------------------- | ||
45 | |||
46 | **input** <identifier> map_to_output <identifier>:: | ||
47 | Maps inputs from this device to the specified output. Only meaningful if the | ||
48 | device is a pointer, touch, or drawing tablet device. | ||
49 | |||
50 | **input** <identifier> map_to_region <WxH\@X,Y>:: | ||
51 | Maps inputs from this device to the specified region of the global output | ||
52 | layout. Only meaningful if the device is a pointer, touch, or drawing tablet | ||
53 | device. | ||
54 | |||
55 | **input** <identifier> map_region <WxH\@X,Y>:: | ||
56 | Ignores inputs from this device that do not occur within the specified region. | ||
57 | Can be in millimeters (e.g. 10mmx20mm\@10mm,20mm) or in terms of 0..1 (e.g. | ||
58 | 0.5x0.5\@0,0). Not all devices support millimeters. Only meaningful if the | ||
59 | device is not a keyboard an provides events in absolute terms (such as a | ||
60 | drawing tablet or touch screen - most pointers provide events relative to the | ||
61 | previous frame). | ||
62 | |||
63 | Libinput Configuration | ||
64 | ~~~~~~~~~~~~~~~~~~~~~~ | ||
20 | 65 | ||
21 | **input** <identifier> accel_profile <adaptive|flat>:: | 66 | **input** <identifier> accel_profile <adaptive|flat>:: |
22 | Sets the pointer acceleration profile for the specified input device. | 67 | Sets the pointer acceleration profile for the specified input device. |
@@ -53,6 +98,27 @@ Commands | |||
53 | **input** <identifier> tap <enabled|disabled>:: | 98 | **input** <identifier> tap <enabled|disabled>:: |
54 | Enables or disables tap for specified input device. | 99 | Enables or disables tap for specified input device. |
55 | 100 | ||
101 | Seat Configuration | ||
102 | ------------------ | ||
103 | |||
104 | Configure options for multiseat mode. sway-seat commands must be used inside a | ||
105 | _seat { }_ block in the config. | ||
106 | |||
107 | A _seat_ is a collection of input devices that act independently of each other. | ||
108 | Seats are identified by name and the default seat is _seat0_ if no seats are | ||
109 | configured. Each seat has an independent keyboard focus and a separate cursor that | ||
110 | is controlled by the pointer devices of the seat. This is useful for multiple | ||
111 | people using the desktop at the same time with their own devices (each sitting in | ||
112 | their own "seat"). | ||
113 | |||
114 | **seat** <name> attach <input_identifier>:: | ||
115 | Attach an input device to this seat by its input identifier. A special value | ||
116 | of _*_ will attach all devices to the seat. | ||
117 | |||
118 | **seat** <name> fallback <true|false>:: | ||
119 | Set this seat as the fallback seat. A fallback seat will attach any device not | ||
120 | explicitly attached to another seat (similar to a "default" seat). | ||
121 | |||
56 | See Also | 122 | See Also |
57 | -------- | 123 | -------- |
58 | 124 | ||
diff --git a/sway/sway.1.txt b/sway/sway.1.txt index 14ab9f49..17fc13da 100644 --- a/sway/sway.1.txt +++ b/sway/sway.1.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | ///// | 1 | ///// |
2 | vim:set ts=4 sw=4 tw=82 noet: | 2 | vim:set ft=asciidoc ts=4 sw=4 tw=82 noet: |
3 | ///// | 3 | ///// |
4 | :quotes.~: | 4 | :quotes.~: |
5 | 5 | ||
@@ -93,27 +93,6 @@ The following environment variables have an effect on sway: | |||
93 | *SWAYSOCK*:: | 93 | *SWAYSOCK*:: |
94 | Specifies the path to the sway IPC socket. | 94 | Specifies the path to the sway IPC socket. |
95 | 95 | ||
96 | *WLC_DRM_DEVICE*:: | ||
97 | Specifies the device to use in DRM mode. | ||
98 | |||
99 | *WLC_SHM*:: | ||
100 | Set 1 to force EGL clients to use shared memory. | ||
101 | |||
102 | *WLC_OUTPUTS*:: | ||
103 | Number of fake outputs to use when running in X11 mode. | ||
104 | |||
105 | *WLC_XWAYLAND*:: | ||
106 | Set to 0 to disable Xwayland support. | ||
107 | |||
108 | *WLC_LIBINPUT*:: | ||
109 | Set to 1 to force libinput (even in X11 mode). | ||
110 | |||
111 | *WLC_REPEAT_DELAY*:: | ||
112 | Configures the keyboard repeat delay. | ||
113 | |||
114 | *WLC_REPEAT_RATE*:: | ||
115 | Configures the keyboard repeat rate. | ||
116 | |||
117 | *XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*, *XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*:: | 96 | *XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*, *XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*:: |
118 | Configures the xkb keyboard settings. See xkeyboard-config(7). | 97 | Configures the xkb keyboard settings. See xkeyboard-config(7). |
119 | 98 | ||
diff --git a/sway/sway.5.txt b/sway/sway.5.txt index 750254c8..03975349 100644 --- a/sway/sway.5.txt +++ b/sway/sway.5.txt | |||
@@ -43,6 +43,9 @@ The following commands may only be used in the configuration file. | |||
43 | Sets variable $name to _value_. You can use the new variable in the arguments | 43 | Sets variable $name to _value_. You can use the new variable in the arguments |
44 | of future commands. | 44 | of future commands. |
45 | 45 | ||
46 | **swaybg_command** <command>:: | ||
47 | Executes custom bg command, default is _swaybg_. | ||
48 | |||
46 | The following commands cannot be used directly in the configuration file. | 49 | The following commands cannot be used directly in the configuration file. |
47 | They are expected to be used with **bindsym** or at runtime through **swaymsg**(1). | 50 | They are expected to be used with **bindsym** or at runtime through **swaymsg**(1). |
48 | 51 | ||
@@ -126,22 +129,21 @@ They are expected to be used with **bindsym** or at runtime through **swaymsg**( | |||
126 | 129 | ||
127 | **resize** <shrink|grow> <width|height> [<amount>] [px|ppt]:: | 130 | **resize** <shrink|grow> <width|height> [<amount>] [px|ppt]:: |
128 | Resizes the currently focused container or view by _amount_. _amount_ is | 131 | Resizes the currently focused container or view by _amount_. _amount_ is |
129 | optional: the default value is 10 (either px or ppt depending on the view | 132 | optional: the default value is 10 (either px or ppt depending on the view type). |
130 | type). The [px|ppt] parameter is optional. _px_ specifies that _amount_ refers | 133 | The [px|ppt] parameter is optional. _px_ specifies that _amount_ refers to pixels; |
131 | to pixels; _ppt_ specifies that _amount_ refers to percentage points of the | 134 | _ppt_ specifies that _amount_ refers to percentage points of the current |
132 | current dimension. Floating views use px dimensions by default (but can use | 135 | size. Floating views use px by default (but can use ppt if specified); tiled |
133 | ppt if specified); tiled views use ppt dimensions by default (but can use px | 136 | views use ppt by default (but can use px if specified). |
134 | if specified). | ||
135 | 137 | ||
136 | **resize set** <width> [px] <height> [px]:: | 138 | **resize set** <width> [px] <height> [px]:: |
137 | Sets the width and height of the currently focused container to _width_ pixels | 139 | Sets the width and height of the currently focused container to _width_ pixels |
138 | and _height_ pixels. The [px] parameters are optional and have no effect. This | 140 | and _height_ pixels. The [px] parameters are optional and have no effect. This |
139 | command only accepts pixel dimensions. | 141 | command only accepts a size in pixels. |
140 | 142 | ||
141 | **resize set** <width|height> <amount> [px] [<width|height> <amount> [px]]:: | 143 | **resize set** <width|height> <amount> [px] [<width|height> <amount> [px]]:: |
142 | Sets the _width_ and/or _height_ of the currently focused container to | 144 | Sets the _width_ and/or _height_ of the currently focused container to |
143 | _amount_. The [px] parameters are optional and have no effect. This command | 145 | _amount_. The [px] parameters are optional and have no effect. This command |
144 | only accepts pixel dimensions. | 146 | only accepts a size in pixels. |
145 | 147 | ||
146 | **scratchpad show**:: | 148 | **scratchpad show**:: |
147 | Shows a window from the scratchpad. Repeatedly using this command will cycle | 149 | Shows a window from the scratchpad. Repeatedly using this command will cycle |
@@ -254,14 +256,14 @@ The default colors are: | |||
254 | *restart* is executed. | 256 | *restart* is executed. |
255 | 257 | ||
256 | **floating_maximum_size** <width> x <height>:: | 258 | **floating_maximum_size** <width> x <height>:: |
257 | Specifies the maximum dimensions of floating windows. | 259 | Specifies the maximum size of floating windows. |
258 | Uses the container dimensions as default. | 260 | Uses the container size as default. |
259 | -1 x -1 will remove any restriction on dimensions. | 261 | -1 x -1 will remove any restriction on size. |
260 | 0 x 0 has the same behavior as not setting any value. | 262 | 0 x 0 has the same behavior as not setting any value. |
261 | If in conflict, this option has precedence over floating_minimum_size. | 263 | If in conflict, this option has precedence over floating_minimum_size. |
262 | 264 | ||
263 | **floating_minimum_size** <width> x <height>:: | 265 | **floating_minimum_size** <width> x <height>:: |
264 | Specifies the minimum dimensions of floating windows. | 266 | Specifies the minimum size of floating windows. |
265 | Default parameters are 75 x 50. | 267 | Default parameters are 75 x 50. |
266 | -1 and 0 are invalid parameters, default will be used instead. | 268 | -1 and 0 are invalid parameters, default will be used instead. |
267 | 269 | ||
@@ -313,7 +315,7 @@ The default colors are: | |||
313 | **hide_edge_borders** <none|vertical|horizontal|both|smart>:: | 315 | **hide_edge_borders** <none|vertical|horizontal|both|smart>:: |
314 | Hide window borders adjacent to the screen edges. Default is _none_. | 316 | Hide window borders adjacent to the screen edges. Default is _none_. |
315 | 317 | ||
316 | **input** <input device> <block of commands>:: | 318 | **input** <input_device> <block of commands>:: |
317 | Append _{_ to this command, the following lines will be commands to configure | 319 | Append _{_ to this command, the following lines will be commands to configure |
318 | the named input device, and _}_ on its own line will close the block. | 320 | the named input device, and _}_ on its own line will close the block. |
319 | + | 321 | + |
@@ -321,6 +323,18 @@ The default colors are: | |||
321 | + | 323 | + |
322 | See **sway-input**(5) for details. | 324 | See **sway-input**(5) for details. |
323 | 325 | ||
326 | **seat** <seat_name> <block of commands>:: | ||
327 | Append _{_ to this command, the following lines will be commands to configure | ||
328 | the named seat, and _}_ on its own line will close the block. | ||
329 | See **sway-input**(5) for details. | ||
330 | |||
331 | **seat** <seat_name> cursor <move|set> <x> <y>:: | ||
332 | Move cursor relatively to current position or set to absolute screen position. | ||
333 | A value of 0 causes the axis to be ignored in both commands. | ||
334 | |||
335 | **seat** <seat_name> cursor <press|release> <left|right|1|2|3...>:: | ||
336 | Simulate press of mouse button specified by left, right, or numerical code. | ||
337 | |||
324 | **kill**:: | 338 | **kill**:: |
325 | Kills (force-closes) the currently-focused container and all of its children. | 339 | Kills (force-closes) the currently-focused container and all of its children. |
326 | 340 | ||
@@ -349,35 +363,44 @@ The default colors are: | |||
349 | Prevents windows matching <criteria> from being focused automatically when | 363 | Prevents windows matching <criteria> from being focused automatically when |
350 | they're created. This does not apply to the first window in a workspace. | 364 | they're created. This does not apply to the first window in a workspace. |
351 | 365 | ||
352 | **output** <name> <resolution|res> <WIDTHxHEIGHT>:: | 366 | **output** <name> mode|resolution|res <WIDTHxHEIGHT>[@<RATE>[Hz]]:: |
353 | Configures the specified output to use the given resolution. | 367 | Configures the specified output to use the given mode. Modes are a combination |
368 | of width and height (in pixels) and a refresh rate that your display can be | ||
369 | configured to use. For a list of available modes, use swaymsg -t get_outputs. | ||
370 | + | ||
371 | Examples: | ||
372 | + | ||
373 | output HDMI-A-1 mode 1920x1080 | ||
354 | + | 374 | + |
355 | _Note_: sway does not currently support setting the output mode. Your output's native | 375 | output HDMI-A-1 mode 1920x1080@60Hz |
356 | resolution will be used and the screen will be scaled from the resolution | ||
357 | specified to your native resolution. | ||
358 | 376 | ||
359 | **output** <name> <position|pos> <X,Y>:: | 377 | **output** <name> position|pos <X,Y>:: |
360 | Configures the specified output to be arranged at the given position. | 378 | Configures the specified output to be arranged at the given position. |
361 | 379 | ||
362 | **output** <name> <scale> <I>:: | 380 | **output** <name> scale <I>:: |
363 | Configures the specified output to be scaled up by the specified integer | 381 | Configures the specified output to be scaled up by the specified integer |
364 | scaling factor (usually 2 for HiDPI screens). | 382 | scaling factor (usually 2 for HiDPI screens). Fractional scaling is supported. |
365 | 383 | ||
366 | **output** <name> <background|bg> <file> <mode>:: | 384 | **output** <name> background|bg <file> <mode>:: |
367 | Sets the wallpaper for the given output to the specified file, using the given | 385 | Sets the wallpaper for the given output to the specified file, using the given |
368 | scaling mode (one of "stretch", "fill", "fit", "center", "tile"). | 386 | scaling mode (one of "stretch", "fill", "fit", "center", "tile"). |
369 | 387 | ||
370 | **output** <name> <background|bg> <color> solid_color:: | 388 | **output** <name> background|bg <color> solid_color:: |
371 | Sets the background of the given output to the specified color. _color_ should | 389 | Sets the background of the given output to the specified color. _color_ should |
372 | be specified as an _#rrggbb_ (no alpha) color. | 390 | be specified as an _#rrggbb_ (no alpha) color. |
373 | 391 | ||
392 | **output** <name> transform <transform>:: | ||
393 | Sets the background transform to the given value. Can be one of "90", "180", | ||
394 | "270" for rotation; or "flipped", "flipped-90", "flipped-180", "flipped-270" | ||
395 | to apply a rotation and flip, or "normal" to apply no transform. | ||
396 | |||
374 | **output** <name> disable:: | 397 | **output** <name> disable:: |
375 | Disables the specified output. | 398 | Disables the specified output. |
376 | 399 | ||
377 | **NOTES FOR THE OUTPUT COMMAND**:: | 400 | **NOTES FOR THE OUTPUT COMMAND**:: |
378 | You may combine output commands into one, like so: | 401 | You may combine output commands into one, like so: |
379 | + | 402 | + |
380 | output HDMI-A-1 res 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch | 403 | output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch |
381 | + | 404 | + |
382 | You can get a list of output names like so: | 405 | You can get a list of output names like so: |
383 | + | 406 | + |
@@ -397,6 +420,10 @@ The default colors are: | |||
397 | However, any mark that starts with an underscore will not be drawn even if the | 420 | However, any mark that starts with an underscore will not be drawn even if the |
398 | option is on. The default option is _on_. | 421 | option is on. The default option is _on_. |
399 | 422 | ||
423 | **opacity** <value>:: | ||
424 | Set the opacity of the window between 0 (completely transparent) and 1 | ||
425 | (completely opaque). | ||
426 | |||
400 | **unmark** <identifier>:: | 427 | **unmark** <identifier>:: |
401 | **Unmark** will remove _identifier_ from the list of current marks on a window. If | 428 | **Unmark** will remove _identifier_ from the list of current marks on a window. If |
402 | no _identifier_ is specified, then **unmark** will remove all marks. | 429 | no _identifier_ is specified, then **unmark** will remove all marks. |
diff --git a/sway/tree/container.c b/sway/tree/container.c new file mode 100644 index 00000000..ea1c93bb --- /dev/null +++ b/sway/tree/container.c | |||
@@ -0,0 +1,534 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | ||
3 | #include <stdint.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | #include <strings.h> | ||
7 | #include <wayland-server.h> | ||
8 | #include <wlr/types/wlr_output_layout.h> | ||
9 | #include <wlr/types/wlr_wl_shell.h> | ||
10 | #include "sway/config.h" | ||
11 | #include "sway/input/input-manager.h" | ||
12 | #include "sway/input/seat.h" | ||
13 | #include "sway/ipc-server.h" | ||
14 | #include "sway/output.h" | ||
15 | #include "sway/server.h" | ||
16 | #include "sway/tree/layout.h" | ||
17 | #include "sway/tree/view.h" | ||
18 | #include "sway/tree/workspace.h" | ||
19 | #include "log.h" | ||
20 | |||
21 | static list_t *bfs_queue; | ||
22 | |||
23 | static list_t *get_bfs_queue() { | ||
24 | if (!bfs_queue) { | ||
25 | bfs_queue = create_list(); | ||
26 | if (!bfs_queue) { | ||
27 | wlr_log(L_ERROR, "could not allocate list for bfs queue"); | ||
28 | return NULL; | ||
29 | } | ||
30 | } | ||
31 | bfs_queue->length = 0; | ||
32 | |||
33 | return bfs_queue; | ||
34 | } | ||
35 | |||
36 | const char *container_type_to_str(enum sway_container_type type) { | ||
37 | switch (type) { | ||
38 | case C_ROOT: | ||
39 | return "C_ROOT"; | ||
40 | case C_OUTPUT: | ||
41 | return "C_OUTPUT"; | ||
42 | case C_WORKSPACE: | ||
43 | return "C_WORKSPACE"; | ||
44 | case C_CONTAINER: | ||
45 | return "C_CONTAINER"; | ||
46 | case C_VIEW: | ||
47 | return "C_VIEW"; | ||
48 | default: | ||
49 | return "C_UNKNOWN"; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | void container_create_notify(struct sway_container *container) { | ||
54 | // TODO send ipc event type based on the container type | ||
55 | wl_signal_emit(&root_container.sway_root->events.new_container, container); | ||
56 | |||
57 | if (container->type == C_VIEW || container->type == C_CONTAINER) { | ||
58 | ipc_event_window(container, "new"); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | static void container_close_notify(struct sway_container *container) { | ||
63 | if (container == NULL) { | ||
64 | return; | ||
65 | } | ||
66 | // TODO send ipc event type based on the container type | ||
67 | if (container->type == C_VIEW || container->type == C_WORKSPACE) { | ||
68 | ipc_event_window(container, "close"); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | struct sway_container *container_create(enum sway_container_type type) { | ||
73 | // next id starts at 1 because 0 is assigned to root_container in layout.c | ||
74 | static size_t next_id = 1; | ||
75 | struct sway_container *c = calloc(1, sizeof(struct sway_container)); | ||
76 | if (!c) { | ||
77 | return NULL; | ||
78 | } | ||
79 | c->id = next_id++; | ||
80 | c->layout = L_NONE; | ||
81 | c->workspace_layout = L_NONE; | ||
82 | c->type = type; | ||
83 | c->alpha = 1.0f; | ||
84 | |||
85 | if (type != C_VIEW) { | ||
86 | c->children = create_list(); | ||
87 | } | ||
88 | |||
89 | wl_signal_init(&c->events.destroy); | ||
90 | wl_signal_init(&c->events.reparent); | ||
91 | |||
92 | return c; | ||
93 | } | ||
94 | |||
95 | static void _container_destroy(struct sway_container *cont) { | ||
96 | if (cont == NULL) { | ||
97 | return; | ||
98 | } | ||
99 | |||
100 | wl_signal_emit(&cont->events.destroy, cont); | ||
101 | container_close_notify(cont); | ||
102 | |||
103 | struct sway_container *parent = cont->parent; | ||
104 | if (cont->children != NULL && cont->children->length) { | ||
105 | // remove children until there are no more, container_destroy calls | ||
106 | // container_remove_child, which removes child from this container | ||
107 | while (cont->children != NULL) { | ||
108 | struct sway_container *child = cont->children->items[0]; | ||
109 | container_remove_child(child); | ||
110 | _container_destroy(child); | ||
111 | } | ||
112 | } | ||
113 | if (cont->marks) { | ||
114 | list_foreach(cont->marks, free); | ||
115 | list_free(cont->marks); | ||
116 | } | ||
117 | if (parent) { | ||
118 | parent = container_remove_child(cont); | ||
119 | } | ||
120 | if (cont->name) { | ||
121 | free(cont->name); | ||
122 | } | ||
123 | list_free(cont->children); | ||
124 | cont->children = NULL; | ||
125 | free(cont); | ||
126 | } | ||
127 | |||
128 | static struct sway_container *container_output_destroy( | ||
129 | struct sway_container *output) { | ||
130 | if (!sway_assert(output, "cannot destroy null output")) { | ||
131 | return NULL; | ||
132 | } | ||
133 | |||
134 | if (output->children->length > 0) { | ||
135 | // TODO save workspaces when there are no outputs. | ||
136 | // TODO also check if there will ever be no outputs except for exiting | ||
137 | // program | ||
138 | if (root_container.children->length > 1) { | ||
139 | int p = root_container.children->items[0] == output; | ||
140 | // Move workspace from this output to another output | ||
141 | while (output->children->length) { | ||
142 | struct sway_container *child = output->children->items[0]; | ||
143 | container_remove_child(child); | ||
144 | container_add_child(root_container.children->items[p], child); | ||
145 | } | ||
146 | container_sort_workspaces(root_container.children->items[p]); | ||
147 | arrange_windows(root_container.children->items[p], | ||
148 | -1, -1); | ||
149 | } | ||
150 | } | ||
151 | |||
152 | wl_list_remove(&output->sway_output->destroy.link); | ||
153 | wl_list_remove(&output->sway_output->mode.link); | ||
154 | wl_list_remove(&output->sway_output->transform.link); | ||
155 | wl_list_remove(&output->sway_output->scale.link); | ||
156 | |||
157 | wl_list_remove(&output->sway_output->damage_destroy.link); | ||
158 | wl_list_remove(&output->sway_output->damage_frame.link); | ||
159 | |||
160 | wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); | ||
161 | _container_destroy(output); | ||
162 | return &root_container; | ||
163 | } | ||
164 | |||
165 | static struct sway_container *container_workspace_destroy( | ||
166 | struct sway_container *workspace) { | ||
167 | if (!sway_assert(workspace, "cannot destroy null workspace")) { | ||
168 | return NULL; | ||
169 | } | ||
170 | |||
171 | // Do not destroy this if it's the last workspace on this output | ||
172 | struct sway_container *output = container_parent(workspace, C_OUTPUT); | ||
173 | if (output && output->children->length == 1) { | ||
174 | return NULL; | ||
175 | } | ||
176 | |||
177 | struct sway_container *parent = workspace->parent; | ||
178 | if (workspace->children->length == 0) { | ||
179 | // destroy the WS if there are no children (TODO check for floating) | ||
180 | wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); | ||
181 | ipc_event_workspace(workspace, NULL, "empty"); | ||
182 | } else { | ||
183 | // Move children to a different workspace on this output | ||
184 | struct sway_container *new_workspace = NULL; | ||
185 | // TODO move floating | ||
186 | for (int i = 0; i < output->children->length; i++) { | ||
187 | if (output->children->items[i] != workspace) { | ||
188 | new_workspace = output->children->items[i]; | ||
189 | break; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | wlr_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'", | ||
194 | workspace->name, new_workspace->name); | ||
195 | for (int i = 0; i < workspace->children->length; i++) { | ||
196 | container_move_to(workspace->children->items[i], new_workspace); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | _container_destroy(workspace); | ||
201 | |||
202 | output_damage_whole(output->sway_output); | ||
203 | |||
204 | return parent; | ||
205 | } | ||
206 | |||
207 | static void container_root_finish(struct sway_container *con) { | ||
208 | wlr_log(L_ERROR, "TODO: destroy the root container"); | ||
209 | } | ||
210 | |||
211 | bool container_reap_empty(struct sway_container *con) { | ||
212 | switch (con->type) { | ||
213 | case C_ROOT: | ||
214 | case C_OUTPUT: | ||
215 | // dont reap these | ||
216 | break; | ||
217 | case C_WORKSPACE: | ||
218 | if (!workspace_is_visible(con) && con->children->length == 0) { | ||
219 | wlr_log(L_DEBUG, "Destroying workspace via reaper"); | ||
220 | container_workspace_destroy(con); | ||
221 | return true; | ||
222 | } | ||
223 | break; | ||
224 | case C_CONTAINER: | ||
225 | if (con->children->length == 0) { | ||
226 | _container_destroy(con); | ||
227 | return true; | ||
228 | } | ||
229 | case C_VIEW: | ||
230 | break; | ||
231 | case C_TYPES: | ||
232 | sway_assert(false, "container_reap_empty called on an invalid " | ||
233 | "container"); | ||
234 | break; | ||
235 | } | ||
236 | |||
237 | return false; | ||
238 | } | ||
239 | |||
240 | struct sway_container *container_reap_empty_recursive( | ||
241 | struct sway_container *con) { | ||
242 | while (con) { | ||
243 | struct sway_container *next = con->parent; | ||
244 | if (!container_reap_empty(con)) { | ||
245 | break; | ||
246 | } | ||
247 | con = next; | ||
248 | } | ||
249 | return con; | ||
250 | } | ||
251 | |||
252 | struct sway_container *container_flatten(struct sway_container *container) { | ||
253 | while (container->type == C_CONTAINER && container->children->length == 1) { | ||
254 | struct sway_container *child = container->children->items[0]; | ||
255 | struct sway_container *parent = container->parent; | ||
256 | container_replace_child(container, child); | ||
257 | container_destroy(container); | ||
258 | container = parent; | ||
259 | } | ||
260 | return container; | ||
261 | } | ||
262 | |||
263 | struct sway_container *container_destroy(struct sway_container *con) { | ||
264 | if (con == NULL) { | ||
265 | return NULL; | ||
266 | } | ||
267 | |||
268 | struct sway_container *parent = con->parent; | ||
269 | |||
270 | switch (con->type) { | ||
271 | case C_ROOT: | ||
272 | container_root_finish(con); | ||
273 | break; | ||
274 | case C_OUTPUT: | ||
275 | // dont try to reap the root after this | ||
276 | container_output_destroy(con); | ||
277 | break; | ||
278 | case C_WORKSPACE: | ||
279 | // dont try to reap the output after this | ||
280 | container_workspace_destroy(con); | ||
281 | break; | ||
282 | case C_CONTAINER: | ||
283 | if (con->children->length) { | ||
284 | for (int i = 0; i < con->children->length; ++i) { | ||
285 | struct sway_container *child = con->children->items[0]; | ||
286 | container_remove_child(child); | ||
287 | container_add_child(parent, child); | ||
288 | } | ||
289 | } | ||
290 | _container_destroy(con); | ||
291 | break; | ||
292 | case C_VIEW: | ||
293 | _container_destroy(con); | ||
294 | break; | ||
295 | case C_TYPES: | ||
296 | wlr_log(L_ERROR, "container_destroy called on an invalid " | ||
297 | "container"); | ||
298 | break; | ||
299 | } | ||
300 | |||
301 | return container_reap_empty_recursive(parent); | ||
302 | } | ||
303 | |||
304 | static void container_close_func(struct sway_container *container, void *data) { | ||
305 | if (container->type == C_VIEW) { | ||
306 | view_close(container->sway_view); | ||
307 | } | ||
308 | } | ||
309 | |||
310 | struct sway_container *container_close(struct sway_container *con) { | ||
311 | if (!sway_assert(con != NULL, | ||
312 | "container_close called with a NULL container")) { | ||
313 | return NULL; | ||
314 | } | ||
315 | |||
316 | struct sway_container *parent = con->parent; | ||
317 | |||
318 | if (con->type == C_VIEW) { | ||
319 | view_close(con->sway_view); | ||
320 | } else { | ||
321 | container_for_each_descendant_dfs(con, container_close_func, NULL); | ||
322 | } | ||
323 | |||
324 | return parent; | ||
325 | } | ||
326 | |||
327 | struct sway_container *container_view_create(struct sway_container *sibling, | ||
328 | struct sway_view *sway_view) { | ||
329 | if (!sway_assert(sibling, | ||
330 | "container_view_create called with NULL sibling/parent")) { | ||
331 | return NULL; | ||
332 | } | ||
333 | const char *title = view_get_title(sway_view); | ||
334 | struct sway_container *swayc = container_create(C_VIEW); | ||
335 | wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s", | ||
336 | swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); | ||
337 | // Setup values | ||
338 | swayc->sway_view = sway_view; | ||
339 | swayc->name = title ? strdup(title) : NULL; | ||
340 | swayc->width = 0; | ||
341 | swayc->height = 0; | ||
342 | |||
343 | if (sibling->type == C_WORKSPACE) { | ||
344 | // Case of focused workspace, just create as child of it | ||
345 | container_add_child(sibling, swayc); | ||
346 | } else { | ||
347 | // Regular case, create as sibling of current container | ||
348 | container_add_sibling(sibling, swayc); | ||
349 | } | ||
350 | container_create_notify(swayc); | ||
351 | return swayc; | ||
352 | } | ||
353 | |||
354 | void container_descendants(struct sway_container *root, | ||
355 | enum sway_container_type type, | ||
356 | void (*func)(struct sway_container *item, void *data), void *data) { | ||
357 | for (int i = 0; i < root->children->length; ++i) { | ||
358 | struct sway_container *item = root->children->items[i]; | ||
359 | if (item->type == type) { | ||
360 | func(item, data); | ||
361 | } | ||
362 | if (item->children && item->children->length) { | ||
363 | container_descendants(item, type, func, data); | ||
364 | } | ||
365 | } | ||
366 | } | ||
367 | |||
368 | struct sway_container *container_find(struct sway_container *container, | ||
369 | bool (*test)(struct sway_container *view, void *data), void *data) { | ||
370 | if (!container->children) { | ||
371 | return NULL; | ||
372 | } | ||
373 | // TODO: floating windows | ||
374 | for (int i = 0; i < container->children->length; ++i) { | ||
375 | struct sway_container *child = container->children->items[i]; | ||
376 | if (test(child, data)) { | ||
377 | return child; | ||
378 | } else { | ||
379 | struct sway_container *res = container_find(child, test, data); | ||
380 | if (res) { | ||
381 | return res; | ||
382 | } | ||
383 | } | ||
384 | } | ||
385 | return NULL; | ||
386 | } | ||
387 | |||
388 | struct sway_container *container_parent(struct sway_container *container, | ||
389 | enum sway_container_type type) { | ||
390 | if (!sway_assert(container, "container is NULL")) { | ||
391 | return NULL; | ||
392 | } | ||
393 | if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) { | ||
394 | return NULL; | ||
395 | } | ||
396 | do { | ||
397 | container = container->parent; | ||
398 | } while (container && container->type != type); | ||
399 | return container; | ||
400 | } | ||
401 | |||
402 | struct sway_container *container_at(struct sway_container *parent, | ||
403 | double lx, double ly, | ||
404 | struct wlr_surface **surface, double *sx, double *sy) { | ||
405 | list_t *queue = get_bfs_queue(); | ||
406 | if (!queue) { | ||
407 | return NULL; | ||
408 | } | ||
409 | |||
410 | list_add(queue, parent); | ||
411 | |||
412 | struct sway_container *swayc = NULL; | ||
413 | while (queue->length) { | ||
414 | swayc = queue->items[0]; | ||
415 | list_del(queue, 0); | ||
416 | if (swayc->type == C_VIEW) { | ||
417 | struct sway_view *sview = swayc->sway_view; | ||
418 | struct sway_container *soutput = container_parent(swayc, C_OUTPUT); | ||
419 | struct wlr_box *output_box = | ||
420 | wlr_output_layout_get_box( | ||
421 | root_container.sway_root->output_layout, | ||
422 | soutput->sway_output->wlr_output); | ||
423 | double ox = lx - output_box->x; | ||
424 | double oy = ly - output_box->y; | ||
425 | double view_sx = ox - swayc->x; | ||
426 | double view_sy = oy - swayc->y; | ||
427 | |||
428 | double _sx, _sy; | ||
429 | struct wlr_surface *_surface; | ||
430 | switch (sview->type) { | ||
431 | case SWAY_VIEW_XWAYLAND: | ||
432 | _surface = wlr_surface_surface_at(sview->surface, | ||
433 | view_sx, view_sy, &_sx, &_sy); | ||
434 | break; | ||
435 | case SWAY_VIEW_WL_SHELL: | ||
436 | _surface = wlr_wl_shell_surface_surface_at( | ||
437 | sview->wlr_wl_shell_surface, | ||
438 | view_sx, view_sy, &_sx, &_sy); | ||
439 | break; | ||
440 | case SWAY_VIEW_XDG_SHELL_V6: | ||
441 | // the top left corner of the sway container is the | ||
442 | // coordinate of the top left corner of the window geometry | ||
443 | view_sx += sview->wlr_xdg_surface_v6->geometry.x; | ||
444 | view_sy += sview->wlr_xdg_surface_v6->geometry.y; | ||
445 | |||
446 | _surface = wlr_xdg_surface_v6_surface_at( | ||
447 | sview->wlr_xdg_surface_v6, | ||
448 | view_sx, view_sy, &_sx, &_sy); | ||
449 | break; | ||
450 | } | ||
451 | if (_surface) { | ||
452 | *sx = _sx; | ||
453 | *sy = _sy; | ||
454 | *surface = _surface; | ||
455 | return swayc; | ||
456 | } | ||
457 | } else { | ||
458 | list_cat(queue, swayc->children); | ||
459 | } | ||
460 | } | ||
461 | |||
462 | return NULL; | ||
463 | } | ||
464 | |||
465 | void container_for_each_descendant_dfs(struct sway_container *container, | ||
466 | void (*f)(struct sway_container *container, void *data), | ||
467 | void *data) { | ||
468 | if (container) { | ||
469 | if (container->children) { | ||
470 | for (int i = 0; i < container->children->length; ++i) { | ||
471 | struct sway_container *child = | ||
472 | container->children->items[i]; | ||
473 | container_for_each_descendant_dfs(child, f, data); | ||
474 | } | ||
475 | } | ||
476 | f(container, data); | ||
477 | } | ||
478 | } | ||
479 | |||
480 | void container_for_each_descendant_bfs(struct sway_container *con, | ||
481 | void (*f)(struct sway_container *con, void *data), void *data) { | ||
482 | list_t *queue = get_bfs_queue(); | ||
483 | if (!queue) { | ||
484 | return; | ||
485 | } | ||
486 | |||
487 | if (queue == NULL) { | ||
488 | wlr_log(L_ERROR, "could not allocate list"); | ||
489 | return; | ||
490 | } | ||
491 | |||
492 | list_add(queue, con); | ||
493 | |||
494 | struct sway_container *current = NULL; | ||
495 | while (queue->length) { | ||
496 | current = queue->items[0]; | ||
497 | list_del(queue, 0); | ||
498 | f(current, data); | ||
499 | // TODO floating containers | ||
500 | list_cat(queue, current->children); | ||
501 | } | ||
502 | } | ||
503 | |||
504 | bool container_has_anscestor(struct sway_container *descendant, | ||
505 | struct sway_container *anscestor) { | ||
506 | while (descendant->type != C_ROOT) { | ||
507 | descendant = descendant->parent; | ||
508 | if (descendant == anscestor) { | ||
509 | return true; | ||
510 | } | ||
511 | } | ||
512 | return false; | ||
513 | } | ||
514 | |||
515 | static bool find_child_func(struct sway_container *con, void *data) { | ||
516 | struct sway_container *child = data; | ||
517 | return con == child; | ||
518 | } | ||
519 | |||
520 | bool container_has_child(struct sway_container *con, | ||
521 | struct sway_container *child) { | ||
522 | if (con == NULL || con->type == C_VIEW || con->children->length == 0) { | ||
523 | return false; | ||
524 | } | ||
525 | return container_find(con, find_child_func, child); | ||
526 | } | ||
527 | |||
528 | void container_damage_whole(struct sway_container *con) { | ||
529 | struct sway_container *output = con; | ||
530 | if (output->type != C_OUTPUT) { | ||
531 | output = container_parent(output, C_OUTPUT); | ||
532 | } | ||
533 | output_damage_whole_container(output->sway_output, con); | ||
534 | } | ||
diff --git a/sway/tree/layout.c b/sway/tree/layout.c new file mode 100644 index 00000000..ae76ca26 --- /dev/null +++ b/sway/tree/layout.c | |||
@@ -0,0 +1,1030 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | ||
3 | #include <math.h> | ||
4 | #include <stdbool.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <string.h> | ||
7 | #include <wlr/types/wlr_output.h> | ||
8 | #include <wlr/types/wlr_output_layout.h> | ||
9 | #include "sway/debug.h" | ||
10 | #include "sway/tree/container.h" | ||
11 | #include "sway/tree/layout.h" | ||
12 | #include "sway/output.h" | ||
13 | #include "sway/tree/workspace.h" | ||
14 | #include "sway/tree/view.h" | ||
15 | #include "sway/input/seat.h" | ||
16 | #include "sway/ipc-server.h" | ||
17 | #include "list.h" | ||
18 | #include "log.h" | ||
19 | |||
20 | struct sway_container root_container; | ||
21 | |||
22 | static void output_layout_handle_change(struct wl_listener *listener, | ||
23 | void *data) { | ||
24 | struct wlr_output_layout *output_layout = | ||
25 | root_container.sway_root->output_layout; | ||
26 | const struct wlr_box *layout_box = | ||
27 | wlr_output_layout_get_box(output_layout, NULL); | ||
28 | root_container.x = layout_box->x; | ||
29 | root_container.y = layout_box->y; | ||
30 | root_container.width = layout_box->width; | ||
31 | root_container.height = layout_box->height; | ||
32 | |||
33 | arrange_windows(&root_container, layout_box->width, layout_box->height); | ||
34 | } | ||
35 | |||
36 | struct sway_container *container_set_layout(struct sway_container *container, | ||
37 | enum sway_container_layout layout) { | ||
38 | if (container->type == C_WORKSPACE) { | ||
39 | container->workspace_layout = layout; | ||
40 | if (layout == L_HORIZ || layout == L_VERT) { | ||
41 | container->layout = layout; | ||
42 | } | ||
43 | } else { | ||
44 | container->layout = layout; | ||
45 | } | ||
46 | return container; | ||
47 | } | ||
48 | |||
49 | void layout_init(void) { | ||
50 | root_container.id = 0; // normally assigned in new_swayc() | ||
51 | root_container.type = C_ROOT; | ||
52 | root_container.layout = L_NONE; | ||
53 | root_container.name = strdup("root"); | ||
54 | root_container.children = create_list(); | ||
55 | wl_signal_init(&root_container.events.destroy); | ||
56 | |||
57 | root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); | ||
58 | root_container.sway_root->output_layout = wlr_output_layout_create(); | ||
59 | wl_list_init(&root_container.sway_root->xwayland_unmanaged); | ||
60 | wl_signal_init(&root_container.sway_root->events.new_container); | ||
61 | |||
62 | root_container.sway_root->output_layout_change.notify = | ||
63 | output_layout_handle_change; | ||
64 | wl_signal_add(&root_container.sway_root->output_layout->events.change, | ||
65 | &root_container.sway_root->output_layout_change); | ||
66 | } | ||
67 | |||
68 | static int index_child(const struct sway_container *child) { | ||
69 | // TODO handle floating | ||
70 | struct sway_container *parent = child->parent; | ||
71 | int i, len; | ||
72 | len = parent->children->length; | ||
73 | for (i = 0; i < len; ++i) { | ||
74 | if (parent->children->items[i] == child) { | ||
75 | break; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | if (!sway_assert(i < len, "Stray container")) { | ||
80 | return -1; | ||
81 | } | ||
82 | return i; | ||
83 | } | ||
84 | |||
85 | void container_insert_child(struct sway_container *parent, | ||
86 | struct sway_container *child, int i) { | ||
87 | struct sway_container *old_parent = child->parent; | ||
88 | if (old_parent) { | ||
89 | container_remove_child(child); | ||
90 | } | ||
91 | wlr_log(L_DEBUG, "Inserting id:%zd at index %d", child->id, i); | ||
92 | list_insert(parent->children, i, child); | ||
93 | child->parent = parent; | ||
94 | wl_signal_emit(&child->events.reparent, old_parent); | ||
95 | } | ||
96 | |||
97 | struct sway_container *container_add_sibling(struct sway_container *fixed, | ||
98 | struct sway_container *active) { | ||
99 | // TODO handle floating | ||
100 | struct sway_container *old_parent = NULL; | ||
101 | if (active->parent) { | ||
102 | old_parent = active->parent; | ||
103 | container_remove_child(active); | ||
104 | } | ||
105 | struct sway_container *parent = fixed->parent; | ||
106 | int i = index_child(fixed); | ||
107 | list_insert(parent->children, i + 1, active); | ||
108 | active->parent = parent; | ||
109 | wl_signal_emit(&active->events.reparent, old_parent); | ||
110 | return active->parent; | ||
111 | } | ||
112 | |||
113 | void container_add_child(struct sway_container *parent, | ||
114 | struct sway_container *child) { | ||
115 | wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", | ||
116 | child, child->type, child->width, child->height, | ||
117 | parent, parent->type, parent->width, parent->height); | ||
118 | list_add(parent->children, child); | ||
119 | child->parent = parent; | ||
120 | } | ||
121 | |||
122 | struct sway_container *container_remove_child(struct sway_container *child) { | ||
123 | struct sway_container *parent = child->parent; | ||
124 | for (int i = 0; i < parent->children->length; ++i) { | ||
125 | if (parent->children->items[i] == child) { | ||
126 | list_del(parent->children, i); | ||
127 | break; | ||
128 | } | ||
129 | } | ||
130 | child->parent = NULL; | ||
131 | return parent; | ||
132 | } | ||
133 | |||
134 | void container_move_to(struct sway_container *container, | ||
135 | struct sway_container *destination) { | ||
136 | if (container == destination | ||
137 | || container_has_anscestor(container, destination)) { | ||
138 | return; | ||
139 | } | ||
140 | struct sway_container *old_parent = container_remove_child(container); | ||
141 | container->width = container->height = 0; | ||
142 | struct sway_container *new_parent; | ||
143 | if (destination->type == C_VIEW) { | ||
144 | new_parent = container_add_sibling(destination, container); | ||
145 | } else { | ||
146 | new_parent = destination; | ||
147 | container_add_child(destination, container); | ||
148 | } | ||
149 | wl_signal_emit(&container->events.reparent, old_parent); | ||
150 | if (container->type == C_WORKSPACE) { | ||
151 | struct sway_seat *seat = input_manager_get_default_seat( | ||
152 | input_manager); | ||
153 | if (old_parent->children->length == 0) { | ||
154 | char *ws_name = workspace_next_name(old_parent->name); | ||
155 | struct sway_container *ws = | ||
156 | workspace_create(old_parent, ws_name); | ||
157 | free(ws_name); | ||
158 | seat_set_focus(seat, ws); | ||
159 | } | ||
160 | container_sort_workspaces(new_parent); | ||
161 | seat_set_focus(seat, new_parent); | ||
162 | } | ||
163 | if (old_parent) { | ||
164 | arrange_windows(old_parent, -1, -1); | ||
165 | } | ||
166 | arrange_windows(new_parent, -1, -1); | ||
167 | } | ||
168 | |||
169 | static bool sway_dir_to_wlr(enum movement_direction dir, | ||
170 | enum wlr_direction *out) { | ||
171 | switch (dir) { | ||
172 | case MOVE_UP: | ||
173 | *out = WLR_DIRECTION_UP; | ||
174 | break; | ||
175 | case MOVE_DOWN: | ||
176 | *out = WLR_DIRECTION_DOWN; | ||
177 | break; | ||
178 | case MOVE_LEFT: | ||
179 | *out = WLR_DIRECTION_LEFT; | ||
180 | break; | ||
181 | case MOVE_RIGHT: | ||
182 | *out = WLR_DIRECTION_RIGHT; | ||
183 | break; | ||
184 | default: | ||
185 | return false; | ||
186 | } | ||
187 | |||
188 | return true; | ||
189 | } | ||
190 | |||
191 | static bool is_parallel(enum sway_container_layout layout, | ||
192 | enum movement_direction dir) { | ||
193 | switch (layout) { | ||
194 | case L_TABBED: | ||
195 | case L_STACKED: | ||
196 | case L_HORIZ: | ||
197 | return dir == MOVE_LEFT || dir == MOVE_RIGHT; | ||
198 | case L_VERT: | ||
199 | return dir == MOVE_UP || dir == MOVE_DOWN; | ||
200 | default: | ||
201 | return false; | ||
202 | } | ||
203 | } | ||
204 | |||
205 | static enum movement_direction invert_movement(enum movement_direction dir) { | ||
206 | switch (dir) { | ||
207 | case MOVE_LEFT: | ||
208 | return MOVE_RIGHT; | ||
209 | case MOVE_RIGHT: | ||
210 | return MOVE_LEFT; | ||
211 | case MOVE_UP: | ||
212 | return MOVE_DOWN; | ||
213 | case MOVE_DOWN: | ||
214 | return MOVE_UP; | ||
215 | default: | ||
216 | sway_assert(0, "This function expects left|right|up|down"); | ||
217 | return MOVE_LEFT; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | static int move_offs(enum movement_direction move_dir) { | ||
222 | return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1; | ||
223 | } | ||
224 | |||
225 | /* Gets the index of the most extreme member based on the movement offset */ | ||
226 | static int container_limit(struct sway_container *container, | ||
227 | enum movement_direction move_dir) { | ||
228 | return move_offs(move_dir) < 0 ? 0 : container->children->length; | ||
229 | } | ||
230 | |||
231 | /* Takes one child, sets it aside, wraps the rest of the children in a new | ||
232 | * container, switches the layout of the workspace, and drops the child back in. | ||
233 | * In other words, rejigger it. */ | ||
234 | static void workspace_rejigger(struct sway_container *ws, | ||
235 | struct sway_container *child, enum movement_direction move_dir) { | ||
236 | struct sway_container *original_parent = child->parent; | ||
237 | struct sway_container *new_parent = | ||
238 | container_split(ws, ws->layout); | ||
239 | |||
240 | container_remove_child(child); | ||
241 | for (int i = 0; i < ws->children->length; ++i) { | ||
242 | struct sway_container *_child = ws->children->items[i]; | ||
243 | container_move_to(new_parent, _child); | ||
244 | } | ||
245 | |||
246 | int index = move_offs(move_dir); | ||
247 | container_insert_child(ws, child, index < 0 ? 0 : 1); | ||
248 | container_set_layout(ws, | ||
249 | move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); | ||
250 | |||
251 | container_flatten(ws); | ||
252 | container_reap_empty_recursive(original_parent); | ||
253 | wl_signal_emit(&child->events.reparent, original_parent); | ||
254 | container_create_notify(new_parent); | ||
255 | arrange_windows(ws, -1, -1); | ||
256 | } | ||
257 | |||
258 | void container_move(struct sway_container *container, | ||
259 | enum movement_direction move_dir, int move_amt) { | ||
260 | if (!sway_assert( | ||
261 | container->type != C_CONTAINER || container->type != C_VIEW, | ||
262 | "Can only move containers and views")) { | ||
263 | return; | ||
264 | } | ||
265 | int offs = move_offs(move_dir); | ||
266 | |||
267 | struct sway_container *sibling = NULL; | ||
268 | struct sway_container *current = container; | ||
269 | struct sway_container *parent = current->parent; | ||
270 | |||
271 | if (parent != container_flatten(parent)) { | ||
272 | // Special case: we were the last one in this container, so flatten it | ||
273 | // and leave | ||
274 | update_debug_tree(); | ||
275 | return; | ||
276 | } | ||
277 | |||
278 | while (!sibling) { | ||
279 | if (current->type == C_ROOT) { | ||
280 | return; | ||
281 | } | ||
282 | |||
283 | parent = current->parent; | ||
284 | wlr_log(L_DEBUG, "Visiting %p %s '%s'", current, | ||
285 | container_type_to_str(current->type), current->name); | ||
286 | |||
287 | int index = index_child(current); | ||
288 | |||
289 | switch (current->type) { | ||
290 | case C_OUTPUT: { | ||
291 | enum wlr_direction wlr_dir; | ||
292 | sway_dir_to_wlr(move_dir, &wlr_dir); | ||
293 | double ref_lx = current->x + current->width / 2; | ||
294 | double ref_ly = current->y + current->height / 2; | ||
295 | struct wlr_output *next = wlr_output_layout_adjacent_output( | ||
296 | root_container.sway_root->output_layout, wlr_dir, | ||
297 | current->sway_output->wlr_output, ref_lx, ref_ly); | ||
298 | if (!next) { | ||
299 | wlr_log(L_DEBUG, "Hit edge of output, nowhere else to go"); | ||
300 | return; | ||
301 | } | ||
302 | struct sway_output *next_output = next->data; | ||
303 | current = next_output->swayc; | ||
304 | wlr_log(L_DEBUG, "Selected next output (%s)", current->name); | ||
305 | // Select workspace and get outta here | ||
306 | current = seat_get_focus_inactive( | ||
307 | config->handler_context.seat, current); | ||
308 | if (current->type != C_WORKSPACE) { | ||
309 | current = container_parent(current, C_WORKSPACE); | ||
310 | } | ||
311 | sibling = current; | ||
312 | break; | ||
313 | } | ||
314 | case C_WORKSPACE: | ||
315 | if (!is_parallel(current->layout, move_dir)) { | ||
316 | if (current->children->length > 2) { | ||
317 | wlr_log(L_DEBUG, "Rejiggering the workspace (%d kiddos)", | ||
318 | current->children->length); | ||
319 | workspace_rejigger(current, container, move_dir); | ||
320 | } else if (current->children->length == 2) { | ||
321 | wlr_log(L_DEBUG, "Changing workspace layout"); | ||
322 | container_set_layout(current, | ||
323 | move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? | ||
324 | L_HORIZ : L_VERT); | ||
325 | container_insert_child(current, container, offs < 0 ? 0 : 1); | ||
326 | arrange_windows(current, -1, -1); | ||
327 | } | ||
328 | return; | ||
329 | } else { | ||
330 | wlr_log(L_DEBUG, "Selecting output"); | ||
331 | current = current->parent; | ||
332 | } | ||
333 | break; | ||
334 | case C_CONTAINER: | ||
335 | case C_VIEW: | ||
336 | if (is_parallel(parent->layout, move_dir)) { | ||
337 | if ((index == parent->children->length - 1 && offs > 0) | ||
338 | || (index == 0 && offs < 0)) { | ||
339 | if (current->parent == container->parent) { | ||
340 | wlr_log(L_DEBUG, "Hit limit, selecting parent"); | ||
341 | current = current->parent; | ||
342 | } else { | ||
343 | wlr_log(L_DEBUG, "Hit limit, " | ||
344 | "promoting descendant to sibling"); | ||
345 | // Special case | ||
346 | struct sway_container *old_parent = container->parent; | ||
347 | container_insert_child(current->parent, container, | ||
348 | index + (offs < 0 ? 0 : 1)); | ||
349 | container->width = container->height = 0; | ||
350 | arrange_windows(current->parent, -1, -1); | ||
351 | arrange_windows(old_parent, -1, -1); | ||
352 | return; | ||
353 | } | ||
354 | } else { | ||
355 | sibling = parent->children->items[index + offs]; | ||
356 | wlr_log(L_DEBUG, "Selecting sibling id:%zd", sibling->id); | ||
357 | } | ||
358 | } else { | ||
359 | wlr_log(L_DEBUG, "Moving up to find a parallel container"); | ||
360 | current = current->parent; | ||
361 | } | ||
362 | break; | ||
363 | default: | ||
364 | sway_assert(0, "Not expecting to see container of type %s here", | ||
365 | container_type_to_str(current->type)); | ||
366 | return; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | // Part two: move stuff around | ||
371 | int index = index_child(container); | ||
372 | struct sway_container *old_parent = container->parent; | ||
373 | |||
374 | while (sibling) { | ||
375 | switch (sibling->type) { | ||
376 | case C_VIEW: | ||
377 | if (sibling->parent == container->parent) { | ||
378 | wlr_log(L_DEBUG, "Swapping siblings"); | ||
379 | sibling->parent->children->items[index + offs] = container; | ||
380 | sibling->parent->children->items[index] = sibling; | ||
381 | arrange_windows(sibling->parent, -1, -1); | ||
382 | } else { | ||
383 | wlr_log(L_DEBUG, "Promoting to sibling of cousin"); | ||
384 | container_insert_child(sibling->parent, container, | ||
385 | index_child(sibling) + (offs > 0 ? 0 : 1)); | ||
386 | container->width = container->height = 0; | ||
387 | arrange_windows(sibling->parent, -1, -1); | ||
388 | arrange_windows(old_parent, -1, -1); | ||
389 | } | ||
390 | sibling = NULL; | ||
391 | break; | ||
392 | case C_WORKSPACE: // Note: only in the case of moving between outputs | ||
393 | case C_CONTAINER: | ||
394 | if (is_parallel(sibling->layout, move_dir)) { | ||
395 | int limit = container_limit(sibling, invert_movement(move_dir)); | ||
396 | wlr_log(L_DEBUG, "limit: %d", limit); | ||
397 | wlr_log(L_DEBUG, | ||
398 | "Reparenting container (parallel) to index %d " | ||
399 | "(move dir: %d)", limit, move_dir); | ||
400 | container_insert_child(sibling, container, limit); | ||
401 | container->width = container->height = 0; | ||
402 | arrange_windows(sibling, -1, -1); | ||
403 | arrange_windows(old_parent, -1, -1); | ||
404 | sibling = NULL; | ||
405 | } else { | ||
406 | wlr_log(L_DEBUG, "Reparenting container (perpendicular)"); | ||
407 | container_remove_child(container); | ||
408 | struct sway_container *focus_inactive = seat_get_focus_inactive( | ||
409 | config->handler_context.seat, sibling); | ||
410 | if (focus_inactive) { | ||
411 | while (focus_inactive->parent != sibling) { | ||
412 | focus_inactive = focus_inactive->parent; | ||
413 | } | ||
414 | wlr_log(L_DEBUG, "Focus inactive: id:%zd", | ||
415 | focus_inactive->id); | ||
416 | sibling = focus_inactive; | ||
417 | continue; | ||
418 | } else if (sibling->children->length) { | ||
419 | wlr_log(L_DEBUG, "No focus-inactive, adding arbitrarily"); | ||
420 | container_add_sibling(sibling->children->items[0], container); | ||
421 | } else { | ||
422 | wlr_log(L_DEBUG, "No kiddos, adding container alone"); | ||
423 | container_add_child(sibling, container); | ||
424 | } | ||
425 | container->width = container->height = 0; | ||
426 | arrange_windows(sibling, -1, -1); | ||
427 | arrange_windows(old_parent, -1, -1); | ||
428 | sibling = NULL; | ||
429 | } | ||
430 | break; | ||
431 | default: | ||
432 | sway_assert(0, "Not expecting to see container of type %s here", | ||
433 | container_type_to_str(sibling->type)); | ||
434 | return; | ||
435 | } | ||
436 | } | ||
437 | |||
438 | if (old_parent) { | ||
439 | seat_set_focus(config->handler_context.seat, old_parent); | ||
440 | seat_set_focus(config->handler_context.seat, container); | ||
441 | } | ||
442 | |||
443 | struct sway_container *last_ws = old_parent; | ||
444 | struct sway_container *next_ws = container->parent; | ||
445 | if (last_ws && last_ws->type != C_WORKSPACE) { | ||
446 | last_ws = container_parent(last_ws, C_WORKSPACE); | ||
447 | } | ||
448 | if (next_ws && next_ws->type != C_WORKSPACE) { | ||
449 | next_ws = container_parent(next_ws, C_WORKSPACE); | ||
450 | } | ||
451 | if (last_ws && next_ws && last_ws != next_ws) { | ||
452 | ipc_event_workspace(last_ws, container, "focus"); | ||
453 | } | ||
454 | } | ||
455 | |||
456 | enum sway_container_layout container_get_default_layout( | ||
457 | struct sway_container *con) { | ||
458 | if (con->type != C_OUTPUT) { | ||
459 | con = container_parent(con, C_OUTPUT); | ||
460 | } | ||
461 | |||
462 | if (!sway_assert(con != NULL, | ||
463 | "container_get_default_layout must be called on an attached" | ||
464 | " container below the root container")) { | ||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | if (config->default_layout != L_NONE) { | ||
469 | return config->default_layout; | ||
470 | } else if (config->default_orientation != L_NONE) { | ||
471 | return config->default_orientation; | ||
472 | } else if (con->width >= con->height) { | ||
473 | return L_HORIZ; | ||
474 | } else { | ||
475 | return L_VERT; | ||
476 | } | ||
477 | } | ||
478 | |||
479 | static int sort_workspace_cmp_qsort(const void *_a, const void *_b) { | ||
480 | struct sway_container *a = *(void **)_a; | ||
481 | struct sway_container *b = *(void **)_b; | ||
482 | int retval = 0; | ||
483 | |||
484 | if (isdigit(a->name[0]) && isdigit(b->name[0])) { | ||
485 | int a_num = strtol(a->name, NULL, 10); | ||
486 | int b_num = strtol(b->name, NULL, 10); | ||
487 | retval = (a_num < b_num) ? -1 : (a_num > b_num); | ||
488 | } else if (isdigit(a->name[0])) { | ||
489 | retval = -1; | ||
490 | } else if (isdigit(b->name[0])) { | ||
491 | retval = 1; | ||
492 | } | ||
493 | |||
494 | return retval; | ||
495 | } | ||
496 | |||
497 | void container_sort_workspaces(struct sway_container *output) { | ||
498 | list_stable_sort(output->children, sort_workspace_cmp_qsort); | ||
499 | } | ||
500 | |||
501 | static void apply_horiz_layout(struct sway_container *container, const double x, | ||
502 | const double y, const double width, | ||
503 | const double height, const int start, | ||
504 | const int end); | ||
505 | |||
506 | static void apply_vert_layout(struct sway_container *container, const double x, | ||
507 | const double y, const double width, | ||
508 | const double height, const int start, | ||
509 | const int end); | ||
510 | |||
511 | void arrange_windows(struct sway_container *container, | ||
512 | double width, double height) { | ||
513 | if (config->reloading) { | ||
514 | return; | ||
515 | } | ||
516 | int i; | ||
517 | if (width == -1 || height == -1) { | ||
518 | width = container->width; | ||
519 | height = container->height; | ||
520 | } | ||
521 | // pixels are indivisible. if we don't round the pixels, then the view | ||
522 | // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's | ||
523 | // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y. | ||
524 | width = floor(width); | ||
525 | height = floor(height); | ||
526 | |||
527 | wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container, | ||
528 | container->name, container->width, container->height, container->x, | ||
529 | container->y); | ||
530 | |||
531 | double x = 0, y = 0; | ||
532 | switch (container->type) { | ||
533 | case C_ROOT: | ||
534 | for (i = 0; i < container->children->length; ++i) { | ||
535 | struct sway_container *output = container->children->items[i]; | ||
536 | const struct wlr_box *output_box = wlr_output_layout_get_box( | ||
537 | container->sway_root->output_layout, | ||
538 | output->sway_output->wlr_output); | ||
539 | output->x = output_box->x; | ||
540 | output->y = output_box->y; | ||
541 | output->width = output_box->width; | ||
542 | output->height = output_box->height; | ||
543 | wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f", | ||
544 | output->name, output->x, output->y); | ||
545 | arrange_windows(output, output_box->width, output_box->height); | ||
546 | } | ||
547 | return; | ||
548 | case C_OUTPUT: | ||
549 | // arrange all workspaces: | ||
550 | for (i = 0; i < container->children->length; ++i) { | ||
551 | struct sway_container *child = container->children->items[i]; | ||
552 | arrange_windows(child, -1, -1); | ||
553 | } | ||
554 | return; | ||
555 | case C_WORKSPACE: | ||
556 | { | ||
557 | struct sway_container *output = | ||
558 | container_parent(container, C_OUTPUT); | ||
559 | struct wlr_box *area = &output->sway_output->usable_area; | ||
560 | wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", | ||
561 | area->width, area->height, area->x, area->y); | ||
562 | container->width = width = area->width; | ||
563 | container->height = height = area->height; | ||
564 | container->x = x = area->x; | ||
565 | container->y = y = area->y; | ||
566 | wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", | ||
567 | container->name, container->x, container->y); | ||
568 | } | ||
569 | // children are properly handled below | ||
570 | break; | ||
571 | case C_VIEW: | ||
572 | { | ||
573 | container->width = width; | ||
574 | container->height = height; | ||
575 | view_configure(container->sway_view, container->x, container->y, | ||
576 | container->width, container->height); | ||
577 | wlr_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f", | ||
578 | container->width, container->height, | ||
579 | container->x, container->y); | ||
580 | } | ||
581 | return; | ||
582 | default: | ||
583 | container->width = width; | ||
584 | container->height = height; | ||
585 | x = container->x; | ||
586 | y = container->y; | ||
587 | break; | ||
588 | } | ||
589 | |||
590 | switch (container->layout) { | ||
591 | case L_HORIZ: | ||
592 | apply_horiz_layout(container, x, y, width, height, 0, | ||
593 | container->children->length); | ||
594 | break; | ||
595 | case L_VERT: | ||
596 | apply_vert_layout(container, x, y, width, height, 0, | ||
597 | container->children->length); | ||
598 | break; | ||
599 | default: | ||
600 | wlr_log(L_DEBUG, "TODO: arrange layout type %d", container->layout); | ||
601 | apply_horiz_layout(container, x, y, width, height, 0, | ||
602 | container->children->length); | ||
603 | break; | ||
604 | } | ||
605 | container_damage_whole(container); | ||
606 | // TODO: Make this less shitty | ||
607 | update_debug_tree(); | ||
608 | } | ||
609 | |||
610 | static void apply_horiz_layout(struct sway_container *container, | ||
611 | const double x, const double y, | ||
612 | const double width, const double height, | ||
613 | const int start, const int end) { | ||
614 | double scale = 0; | ||
615 | // Calculate total width | ||
616 | for (int i = start; i < end; ++i) { | ||
617 | double *old_width = | ||
618 | &((struct sway_container *)container->children->items[i])->width; | ||
619 | if (*old_width <= 0) { | ||
620 | if (end - start > 1) { | ||
621 | *old_width = width / (end - start - 1); | ||
622 | } else { | ||
623 | *old_width = width; | ||
624 | } | ||
625 | } | ||
626 | scale += *old_width; | ||
627 | } | ||
628 | scale = width / scale; | ||
629 | |||
630 | // Resize windows | ||
631 | double child_x = x; | ||
632 | if (scale > 0.1) { | ||
633 | wlr_log(L_DEBUG, "Arranging %p horizontally", container); | ||
634 | for (int i = start; i < end; ++i) { | ||
635 | struct sway_container *child = container->children->items[i]; | ||
636 | wlr_log(L_DEBUG, | ||
637 | "Calculating arrangement for %p:%d (will scale %f by %f)", | ||
638 | child, child->type, width, scale); | ||
639 | |||
640 | if (child->type == C_VIEW) { | ||
641 | view_configure(child->sway_view, child_x, y, child->width, | ||
642 | child->height); | ||
643 | } else { | ||
644 | child->x = child_x; | ||
645 | child->y = y; | ||
646 | } | ||
647 | |||
648 | if (i == end - 1) { | ||
649 | double remaining_width = x + width - child_x; | ||
650 | arrange_windows(child, remaining_width, height); | ||
651 | } else { | ||
652 | arrange_windows(child, child->width * scale, height); | ||
653 | } | ||
654 | child_x += child->width; | ||
655 | } | ||
656 | |||
657 | // update focused view border last because it may | ||
658 | // depend on the title bar geometry of its siblings. | ||
659 | /* TODO WLR | ||
660 | if (focused && container->children->length > 1) { | ||
661 | update_container_border(focused); | ||
662 | } | ||
663 | */ | ||
664 | } | ||
665 | } | ||
666 | |||
667 | void apply_vert_layout(struct sway_container *container, | ||
668 | const double x, const double y, | ||
669 | const double width, const double height, const int start, | ||
670 | const int end) { | ||
671 | int i; | ||
672 | double scale = 0; | ||
673 | // Calculate total height | ||
674 | for (i = start; i < end; ++i) { | ||
675 | double *old_height = | ||
676 | &((struct sway_container *)container->children->items[i])->height; | ||
677 | if (*old_height <= 0) { | ||
678 | if (end - start > 1) { | ||
679 | *old_height = height / (end - start - 1); | ||
680 | } else { | ||
681 | *old_height = height; | ||
682 | } | ||
683 | } | ||
684 | scale += *old_height; | ||
685 | } | ||
686 | scale = height / scale; | ||
687 | |||
688 | // Resize | ||
689 | double child_y = y; | ||
690 | if (scale > 0.1) { | ||
691 | wlr_log(L_DEBUG, "Arranging %p vertically", container); | ||
692 | for (i = start; i < end; ++i) { | ||
693 | struct sway_container *child = container->children->items[i]; | ||
694 | wlr_log(L_DEBUG, | ||
695 | "Calculating arrangement for %p:%d (will scale %f by %f)", | ||
696 | child, child->type, height, scale); | ||
697 | if (child->type == C_VIEW) { | ||
698 | view_configure(child->sway_view, x, child_y, child->width, | ||
699 | child->height); | ||
700 | } else { | ||
701 | child->x = x; | ||
702 | child->y = child_y; | ||
703 | } | ||
704 | |||
705 | if (i == end - 1) { | ||
706 | double remaining_height = y + height - child_y; | ||
707 | arrange_windows(child, width, remaining_height); | ||
708 | } else { | ||
709 | arrange_windows(child, width, child->height * scale); | ||
710 | } | ||
711 | child_y += child->height; | ||
712 | } | ||
713 | |||
714 | // update focused view border last because it may | ||
715 | // depend on the title bar geometry of its siblings. | ||
716 | /* TODO WLR | ||
717 | if (focused && container->children->length > 1) { | ||
718 | update_container_border(focused); | ||
719 | } | ||
720 | */ | ||
721 | } | ||
722 | } | ||
723 | |||
724 | /** | ||
725 | * Get swayc in the direction of newly entered output. | ||
726 | */ | ||
727 | static struct sway_container *get_swayc_in_output_direction( | ||
728 | struct sway_container *output, enum movement_direction dir, | ||
729 | struct sway_seat *seat) { | ||
730 | if (!output) { | ||
731 | return NULL; | ||
732 | } | ||
733 | |||
734 | struct sway_container *ws = seat_get_focus_inactive(seat, output); | ||
735 | if (ws->type != C_WORKSPACE) { | ||
736 | ws = container_parent(ws, C_WORKSPACE); | ||
737 | } | ||
738 | |||
739 | if (ws == NULL) { | ||
740 | wlr_log(L_ERROR, "got an output without a workspace"); | ||
741 | return NULL; | ||
742 | } | ||
743 | |||
744 | if (ws->children->length > 0) { | ||
745 | switch (dir) { | ||
746 | case MOVE_LEFT: | ||
747 | // get most right child of new output | ||
748 | return ws->children->items[ws->children->length-1]; | ||
749 | case MOVE_RIGHT: | ||
750 | // get most left child of new output | ||
751 | return ws->children->items[0]; | ||
752 | case MOVE_UP: | ||
753 | case MOVE_DOWN: { | ||
754 | struct sway_container *focused = | ||
755 | seat_get_focus_inactive(seat, ws); | ||
756 | if (focused && focused->parent) { | ||
757 | struct sway_container *parent = focused->parent; | ||
758 | if (parent->layout == L_VERT) { | ||
759 | if (dir == MOVE_UP) { | ||
760 | // get child furthest down on new output | ||
761 | int idx = parent->children->length - 1; | ||
762 | return parent->children->items[idx]; | ||
763 | } else if (dir == MOVE_DOWN) { | ||
764 | // get child furthest up on new output | ||
765 | return parent->children->items[0]; | ||
766 | } | ||
767 | } | ||
768 | return focused; | ||
769 | } | ||
770 | break; | ||
771 | } | ||
772 | default: | ||
773 | break; | ||
774 | } | ||
775 | } | ||
776 | |||
777 | return ws; | ||
778 | } | ||
779 | |||
780 | static void get_layout_center_position(struct sway_container *container, | ||
781 | int *x, int *y) { | ||
782 | // FIXME view coords are inconsistently referred to in layout/output systems | ||
783 | if (container->type == C_OUTPUT) { | ||
784 | *x = container->x + container->width/2; | ||
785 | *y = container->y + container->height/2; | ||
786 | } else { | ||
787 | struct sway_container *output = container_parent(container, C_OUTPUT); | ||
788 | if (container->type == C_WORKSPACE) { | ||
789 | // Workspace coordinates are actually wrong/arbitrary, but should | ||
790 | // be same as output. | ||
791 | *x = output->x; | ||
792 | *y = output->y; | ||
793 | } else { | ||
794 | *x = output->x + container->x; | ||
795 | *y = output->y + container->y; | ||
796 | } | ||
797 | } | ||
798 | } | ||
799 | |||
800 | static struct sway_container *sway_output_from_wlr(struct wlr_output *output) { | ||
801 | if (output == NULL) { | ||
802 | return NULL; | ||
803 | } | ||
804 | for (int i = 0; i < root_container.children->length; ++i) { | ||
805 | struct sway_container *o = root_container.children->items[i]; | ||
806 | if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) { | ||
807 | return o; | ||
808 | } | ||
809 | } | ||
810 | return NULL; | ||
811 | } | ||
812 | |||
813 | struct sway_container *container_get_in_direction( | ||
814 | struct sway_container *container, struct sway_seat *seat, | ||
815 | enum movement_direction dir) { | ||
816 | if (dir == MOVE_CHILD) { | ||
817 | return seat_get_focus_inactive(seat, container); | ||
818 | } | ||
819 | |||
820 | struct sway_container *parent = container->parent; | ||
821 | if (dir == MOVE_PARENT) { | ||
822 | if (parent->type == C_OUTPUT) { | ||
823 | return NULL; | ||
824 | } else { | ||
825 | return parent; | ||
826 | } | ||
827 | } | ||
828 | |||
829 | // TODO WLR fullscreen | ||
830 | /* | ||
831 | if (container->type == C_VIEW && swayc_is_fullscreen(container)) { | ||
832 | wlr_log(L_DEBUG, "Moving from fullscreen view, skipping to output"); | ||
833 | container = container_parent(container, C_OUTPUT); | ||
834 | get_layout_center_position(container, &abs_pos); | ||
835 | struct sway_container *output = | ||
836 | swayc_adjacent_output(container, dir, &abs_pos, true); | ||
837 | return get_swayc_in_output_direction(output, dir); | ||
838 | } | ||
839 | if (container->type == C_WORKSPACE && container->fullscreen) { | ||
840 | sway_log(L_DEBUG, "Moving to fullscreen view"); | ||
841 | return container->fullscreen; | ||
842 | } | ||
843 | */ | ||
844 | |||
845 | struct sway_container *wrap_candidate = NULL; | ||
846 | while (true) { | ||
847 | bool can_move = false; | ||
848 | int desired; | ||
849 | int idx = index_child(container); | ||
850 | if (parent->type == C_ROOT) { | ||
851 | enum wlr_direction wlr_dir = 0; | ||
852 | if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir), | ||
853 | "got invalid direction: %d", dir)) { | ||
854 | return NULL; | ||
855 | } | ||
856 | int lx, ly; | ||
857 | get_layout_center_position(container, &lx, &ly); | ||
858 | struct wlr_output_layout *layout = | ||
859 | root_container.sway_root->output_layout; | ||
860 | struct wlr_output *wlr_adjacent = | ||
861 | wlr_output_layout_adjacent_output(layout, wlr_dir, | ||
862 | container->sway_output->wlr_output, lx, ly); | ||
863 | struct sway_container *adjacent = | ||
864 | sway_output_from_wlr(wlr_adjacent); | ||
865 | |||
866 | if (!adjacent || adjacent == container) { | ||
867 | return wrap_candidate; | ||
868 | } | ||
869 | struct sway_container *next = | ||
870 | get_swayc_in_output_direction(adjacent, dir, seat); | ||
871 | if (next == NULL) { | ||
872 | return NULL; | ||
873 | } | ||
874 | if (next->children && next->children->length) { | ||
875 | // TODO consider floating children as well | ||
876 | return seat_get_focus_inactive_view(seat, next); | ||
877 | } else { | ||
878 | return next; | ||
879 | } | ||
880 | } else { | ||
881 | if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { | ||
882 | if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { | ||
883 | can_move = true; | ||
884 | desired = idx + (dir == MOVE_LEFT ? -1 : 1); | ||
885 | } | ||
886 | } else { | ||
887 | if (parent->layout == L_VERT || parent->layout == L_STACKED) { | ||
888 | can_move = true; | ||
889 | desired = idx + (dir == MOVE_UP ? -1 : 1); | ||
890 | } | ||
891 | } | ||
892 | } | ||
893 | |||
894 | if (can_move) { | ||
895 | // TODO handle floating | ||
896 | if (desired < 0 || desired >= parent->children->length) { | ||
897 | can_move = false; | ||
898 | int len = parent->children->length; | ||
899 | if (!wrap_candidate && len > 1) { | ||
900 | if (desired < 0) { | ||
901 | wrap_candidate = parent->children->items[len-1]; | ||
902 | } else { | ||
903 | wrap_candidate = parent->children->items[0]; | ||
904 | } | ||
905 | if (config->force_focus_wrapping) { | ||
906 | return wrap_candidate; | ||
907 | } | ||
908 | } | ||
909 | } else { | ||
910 | struct sway_container *desired_con = parent->children->items[desired]; | ||
911 | wlr_log(L_DEBUG, | ||
912 | "cont %d-%p dir %i sibling %d: %p", idx, | ||
913 | container, dir, desired, desired_con); | ||
914 | struct sway_container *next = seat_get_focus_inactive_view(seat, desired_con); | ||
915 | return next; | ||
916 | } | ||
917 | } | ||
918 | |||
919 | if (!can_move) { | ||
920 | container = parent; | ||
921 | parent = parent->parent; | ||
922 | if (!parent) { | ||
923 | // wrapping is the last chance | ||
924 | return wrap_candidate; | ||
925 | } | ||
926 | } | ||
927 | } | ||
928 | } | ||
929 | |||
930 | struct sway_container *container_replace_child(struct sway_container *child, | ||
931 | struct sway_container *new_child) { | ||
932 | struct sway_container *parent = child->parent; | ||
933 | if (parent == NULL) { | ||
934 | return NULL; | ||
935 | } | ||
936 | int i = index_child(child); | ||
937 | |||
938 | // TODO floating | ||
939 | if (new_child->parent) { | ||
940 | container_remove_child(new_child); | ||
941 | } | ||
942 | parent->children->items[i] = new_child; | ||
943 | new_child->parent = parent; | ||
944 | child->parent = NULL; | ||
945 | |||
946 | // Set geometry for new child | ||
947 | new_child->x = child->x; | ||
948 | new_child->y = child->y; | ||
949 | new_child->width = child->width; | ||
950 | new_child->height = child->height; | ||
951 | |||
952 | // reset geometry for child | ||
953 | child->width = 0; | ||
954 | child->height = 0; | ||
955 | |||
956 | return parent; | ||
957 | } | ||
958 | |||
959 | struct sway_container *container_split(struct sway_container *child, | ||
960 | enum sway_container_layout layout) { | ||
961 | // TODO floating: cannot split a floating container | ||
962 | if (!sway_assert(child, "child cannot be null")) { | ||
963 | return NULL; | ||
964 | } | ||
965 | if (child->type == C_WORKSPACE && child->children->length == 0) { | ||
966 | // Special case: this just behaves like splitt | ||
967 | child->prev_layout = child->layout; | ||
968 | child->layout = layout; | ||
969 | arrange_windows(child, -1, -1); | ||
970 | return child; | ||
971 | } | ||
972 | |||
973 | struct sway_container *cont = container_create(C_CONTAINER); | ||
974 | |||
975 | wlr_log(L_DEBUG, "creating container %p around %p", cont, child); | ||
976 | |||
977 | cont->prev_layout = L_NONE; | ||
978 | cont->width = child->width; | ||
979 | cont->height = child->height; | ||
980 | cont->x = child->x; | ||
981 | cont->y = child->y; | ||
982 | |||
983 | if (child->type == C_WORKSPACE) { | ||
984 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | ||
985 | struct sway_container *workspace = child; | ||
986 | bool set_focus = (seat_get_focus(seat) == workspace); | ||
987 | |||
988 | while (workspace->children->length) { | ||
989 | struct sway_container *ws_child = workspace->children->items[0]; | ||
990 | container_remove_child(ws_child); | ||
991 | container_add_child(cont, ws_child); | ||
992 | } | ||
993 | |||
994 | container_add_child(workspace, cont); | ||
995 | enum sway_container_layout old_layout = workspace->layout; | ||
996 | container_set_layout(workspace, layout); | ||
997 | cont->layout = old_layout; | ||
998 | |||
999 | if (set_focus) { | ||
1000 | seat_set_focus(seat, cont); | ||
1001 | } | ||
1002 | } else { | ||
1003 | cont->layout = layout; | ||
1004 | container_replace_child(child, cont); | ||
1005 | container_add_child(cont, child); | ||
1006 | } | ||
1007 | |||
1008 | return cont; | ||
1009 | } | ||
1010 | |||
1011 | void container_recursive_resize(struct sway_container *container, | ||
1012 | double amount, enum resize_edge edge) { | ||
1013 | bool layout_match = true; | ||
1014 | wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount); | ||
1015 | if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { | ||
1016 | container->width += amount; | ||
1017 | layout_match = container->layout == L_HORIZ; | ||
1018 | } else if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) { | ||
1019 | container->height += amount; | ||
1020 | layout_match = container->layout == L_VERT; | ||
1021 | } | ||
1022 | if (container->children) { | ||
1023 | for (int i = 0; i < container->children->length; i++) { | ||
1024 | struct sway_container *child = container->children->items[i]; | ||
1025 | double amt = layout_match ? | ||
1026 | amount / container->children->length : amount; | ||
1027 | container_recursive_resize(child, amt, edge); | ||
1028 | } | ||
1029 | } | ||
1030 | } | ||
diff --git a/sway/tree/output.c b/sway/tree/output.c new file mode 100644 index 00000000..6c7044a2 --- /dev/null +++ b/sway/tree/output.c | |||
@@ -0,0 +1,73 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | ||
3 | #include <strings.h> | ||
4 | #include "sway/output.h" | ||
5 | #include "sway/tree/output.h" | ||
6 | #include "sway/tree/workspace.h" | ||
7 | #include "log.h" | ||
8 | |||
9 | struct sway_container *output_create( | ||
10 | struct sway_output *sway_output) { | ||
11 | struct wlr_box size; | ||
12 | wlr_output_effective_resolution(sway_output->wlr_output, &size.width, | ||
13 | &size.height); | ||
14 | |||
15 | const char *name = sway_output->wlr_output->name; | ||
16 | char identifier[128]; | ||
17 | output_get_identifier(identifier, sizeof(identifier), sway_output); | ||
18 | |||
19 | struct output_config *oc = NULL, *all = NULL; | ||
20 | for (int i = 0; i < config->output_configs->length; ++i) { | ||
21 | struct output_config *cur = config->output_configs->items[i]; | ||
22 | |||
23 | if (strcasecmp(name, cur->name) == 0 || | ||
24 | strcasecmp(identifier, cur->name) == 0) { | ||
25 | wlr_log(L_DEBUG, "Matched output config for %s", name); | ||
26 | oc = cur; | ||
27 | } | ||
28 | if (strcasecmp("*", cur->name) == 0) { | ||
29 | wlr_log(L_DEBUG, "Matched wildcard output config for %s", name); | ||
30 | all = cur; | ||
31 | } | ||
32 | |||
33 | if (oc && all) { | ||
34 | break; | ||
35 | } | ||
36 | } | ||
37 | if (!oc) { | ||
38 | oc = all; | ||
39 | } | ||
40 | |||
41 | if (oc && !oc->enabled) { | ||
42 | return NULL; | ||
43 | } | ||
44 | |||
45 | struct sway_container *output = container_create(C_OUTPUT); | ||
46 | output->sway_output = sway_output; | ||
47 | output->name = strdup(name); | ||
48 | if (output->name == NULL) { | ||
49 | container_destroy(output); | ||
50 | return NULL; | ||
51 | } | ||
52 | |||
53 | apply_output_config(oc, output); | ||
54 | container_add_child(&root_container, output); | ||
55 | load_swaybars(); | ||
56 | |||
57 | // Create workspace | ||
58 | char *ws_name = workspace_next_name(output->name); | ||
59 | wlr_log(L_DEBUG, "Creating default workspace %s", ws_name); | ||
60 | struct sway_container *ws = workspace_create(output, ws_name); | ||
61 | // Set each seat's focus if not already set | ||
62 | struct sway_seat *seat = NULL; | ||
63 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
64 | if (!seat->has_focus) { | ||
65 | seat_set_focus(seat, ws); | ||
66 | } | ||
67 | } | ||
68 | |||
69 | free(ws_name); | ||
70 | container_create_notify(output); | ||
71 | return output; | ||
72 | } | ||
73 | |||
diff --git a/sway/tree/view.c b/sway/tree/view.c new file mode 100644 index 00000000..99b44720 --- /dev/null +++ b/sway/tree/view.c | |||
@@ -0,0 +1,329 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <wayland-server.h> | ||
3 | #include <wlr/types/wlr_output_layout.h> | ||
4 | #include "log.h" | ||
5 | #include "sway/output.h" | ||
6 | #include "sway/tree/container.h" | ||
7 | #include "sway/tree/layout.h" | ||
8 | #include "sway/tree/view.h" | ||
9 | |||
10 | void view_init(struct sway_view *view, enum sway_view_type type, | ||
11 | const struct sway_view_impl *impl) { | ||
12 | view->type = type; | ||
13 | view->impl = impl; | ||
14 | wl_signal_init(&view->events.unmap); | ||
15 | } | ||
16 | |||
17 | void view_destroy(struct sway_view *view) { | ||
18 | if (view == NULL) { | ||
19 | return; | ||
20 | } | ||
21 | |||
22 | if (view->surface != NULL) { | ||
23 | view_unmap(view); | ||
24 | } | ||
25 | |||
26 | container_destroy(view->swayc); | ||
27 | |||
28 | if (view->impl->destroy) { | ||
29 | view->impl->destroy(view); | ||
30 | } else { | ||
31 | free(view); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | const char *view_get_title(struct sway_view *view) { | ||
36 | if (view->impl->get_prop) { | ||
37 | return view->impl->get_prop(view, VIEW_PROP_TITLE); | ||
38 | } | ||
39 | return NULL; | ||
40 | } | ||
41 | |||
42 | const char *view_get_app_id(struct sway_view *view) { | ||
43 | if (view->impl->get_prop) { | ||
44 | return view->impl->get_prop(view, VIEW_PROP_APP_ID); | ||
45 | } | ||
46 | return NULL; | ||
47 | } | ||
48 | |||
49 | const char *view_get_class(struct sway_view *view) { | ||
50 | if (view->impl->get_prop) { | ||
51 | return view->impl->get_prop(view, VIEW_PROP_CLASS); | ||
52 | } | ||
53 | return NULL; | ||
54 | } | ||
55 | |||
56 | const char *view_get_instance(struct sway_view *view) { | ||
57 | if (view->impl->get_prop) { | ||
58 | return view->impl->get_prop(view, VIEW_PROP_INSTANCE); | ||
59 | } | ||
60 | return NULL; | ||
61 | } | ||
62 | |||
63 | void view_configure(struct sway_view *view, double ox, double oy, int width, | ||
64 | int height) { | ||
65 | if (view->impl->configure) { | ||
66 | view->impl->configure(view, ox, oy, width, height); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | void view_set_activated(struct sway_view *view, bool activated) { | ||
71 | if (view->impl->set_activated) { | ||
72 | view->impl->set_activated(view, activated); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | void view_close(struct sway_view *view) { | ||
77 | if (view->impl->close) { | ||
78 | view->impl->close(view); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | void view_damage(struct sway_view *view, bool whole) { | ||
83 | for (int i = 0; i < root_container.children->length; ++i) { | ||
84 | struct sway_container *cont = root_container.children->items[i]; | ||
85 | if (cont->type == C_OUTPUT) { | ||
86 | output_damage_view(cont->sway_output, view, whole); | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) { | ||
92 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | ||
93 | |||
94 | box->x = output->x + view->swayc->x; | ||
95 | box->y = output->y + view->swayc->y; | ||
96 | box->width = view->width; | ||
97 | box->height = view->height; | ||
98 | } | ||
99 | |||
100 | void view_for_each_surface(struct sway_view *view, | ||
101 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
102 | if (view->impl->for_each_surface) { | ||
103 | view->impl->for_each_surface(view, iterator, user_data); | ||
104 | } else { | ||
105 | wlr_surface_for_each_surface(view->surface, iterator, user_data); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | static void view_subsurface_create(struct sway_view *view, | ||
110 | struct wlr_subsurface *subsurface); | ||
111 | |||
112 | static void view_init_subsurfaces(struct sway_view *view, | ||
113 | struct wlr_surface *surface); | ||
114 | |||
115 | static void view_handle_surface_new_subsurface(struct wl_listener *listener, | ||
116 | void *data) { | ||
117 | struct sway_view *view = | ||
118 | wl_container_of(listener, view, surface_new_subsurface); | ||
119 | struct wlr_subsurface *subsurface = data; | ||
120 | view_subsurface_create(view, subsurface); | ||
121 | } | ||
122 | |||
123 | static void surface_send_enter_iterator(struct wlr_surface *surface, | ||
124 | int x, int y, void *data) { | ||
125 | struct wlr_output *wlr_output = data; | ||
126 | wlr_surface_send_enter(surface, wlr_output); | ||
127 | } | ||
128 | |||
129 | static void surface_send_leave_iterator(struct wlr_surface *surface, | ||
130 | int x, int y, void *data) { | ||
131 | struct wlr_output *wlr_output = data; | ||
132 | wlr_surface_send_leave(surface, wlr_output); | ||
133 | } | ||
134 | |||
135 | static void view_handle_container_reparent(struct wl_listener *listener, | ||
136 | void *data) { | ||
137 | struct sway_view *view = | ||
138 | wl_container_of(listener, view, container_reparent); | ||
139 | struct sway_container *old_parent = data; | ||
140 | |||
141 | struct sway_container *old_output = old_parent; | ||
142 | if (old_output != NULL && old_output->type != C_OUTPUT) { | ||
143 | old_output = container_parent(old_output, C_OUTPUT); | ||
144 | } | ||
145 | |||
146 | struct sway_container *new_output = view->swayc->parent; | ||
147 | if (new_output != NULL && new_output->type != C_OUTPUT) { | ||
148 | new_output = container_parent(new_output, C_OUTPUT); | ||
149 | } | ||
150 | |||
151 | if (old_output == new_output) { | ||
152 | return; | ||
153 | } | ||
154 | |||
155 | if (old_output != NULL) { | ||
156 | view_for_each_surface(view, surface_send_leave_iterator, | ||
157 | old_output->sway_output->wlr_output); | ||
158 | } | ||
159 | if (new_output != NULL) { | ||
160 | view_for_each_surface(view, surface_send_enter_iterator, | ||
161 | new_output->sway_output->wlr_output); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | ||
166 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { | ||
167 | return; | ||
168 | } | ||
169 | |||
170 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
171 | struct sway_container *focus = seat_get_focus_inactive(seat, | ||
172 | &root_container); | ||
173 | struct sway_container *cont = container_view_create(focus, view); | ||
174 | |||
175 | view->surface = wlr_surface; | ||
176 | view->swayc = cont; | ||
177 | |||
178 | view_init_subsurfaces(view, wlr_surface); | ||
179 | wl_signal_add(&wlr_surface->events.new_subsurface, | ||
180 | &view->surface_new_subsurface); | ||
181 | view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; | ||
182 | |||
183 | wl_signal_add(&view->swayc->events.reparent, &view->container_reparent); | ||
184 | view->container_reparent.notify = view_handle_container_reparent; | ||
185 | |||
186 | arrange_windows(cont->parent, -1, -1); | ||
187 | input_manager_set_focus(input_manager, cont); | ||
188 | |||
189 | view_damage(view, true); | ||
190 | view_handle_container_reparent(&view->container_reparent, NULL); | ||
191 | } | ||
192 | |||
193 | void view_unmap(struct sway_view *view) { | ||
194 | if (!sway_assert(view->surface != NULL, "cannot unmap unmapped view")) { | ||
195 | return; | ||
196 | } | ||
197 | |||
198 | wl_signal_emit(&view->events.unmap, view); | ||
199 | |||
200 | view_damage(view, true); | ||
201 | |||
202 | wl_list_remove(&view->surface_new_subsurface.link); | ||
203 | wl_list_remove(&view->container_reparent.link); | ||
204 | |||
205 | struct sway_container *parent = container_destroy(view->swayc); | ||
206 | |||
207 | view->swayc = NULL; | ||
208 | view->surface = NULL; | ||
209 | |||
210 | arrange_windows(parent, -1, -1); | ||
211 | } | ||
212 | |||
213 | void view_update_position(struct sway_view *view, double ox, double oy) { | ||
214 | if (view->swayc->x == ox && view->swayc->y == oy) { | ||
215 | return; | ||
216 | } | ||
217 | |||
218 | view_damage(view, true); | ||
219 | view->swayc->x = ox; | ||
220 | view->swayc->y = oy; | ||
221 | view_damage(view, true); | ||
222 | } | ||
223 | |||
224 | void view_update_size(struct sway_view *view, int width, int height) { | ||
225 | if (view->width == width && view->height == height) { | ||
226 | return; | ||
227 | } | ||
228 | |||
229 | view_damage(view, true); | ||
230 | view->width = width; | ||
231 | view->height = height; | ||
232 | view_damage(view, true); | ||
233 | } | ||
234 | |||
235 | |||
236 | static void view_subsurface_create(struct sway_view *view, | ||
237 | struct wlr_subsurface *subsurface) { | ||
238 | struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); | ||
239 | if (child == NULL) { | ||
240 | wlr_log(L_ERROR, "Allocation failed"); | ||
241 | return; | ||
242 | } | ||
243 | view_child_init(child, NULL, view, subsurface->surface); | ||
244 | } | ||
245 | |||
246 | static void view_child_handle_surface_commit(struct wl_listener *listener, | ||
247 | void *data) { | ||
248 | struct sway_view_child *child = | ||
249 | wl_container_of(listener, child, surface_commit); | ||
250 | // TODO: only accumulate damage from the child | ||
251 | view_damage(child->view, false); | ||
252 | } | ||
253 | |||
254 | static void view_child_handle_surface_new_subsurface( | ||
255 | struct wl_listener *listener, void *data) { | ||
256 | struct sway_view_child *child = | ||
257 | wl_container_of(listener, child, surface_new_subsurface); | ||
258 | struct wlr_subsurface *subsurface = data; | ||
259 | view_subsurface_create(child->view, subsurface); | ||
260 | } | ||
261 | |||
262 | static void view_child_handle_surface_destroy(struct wl_listener *listener, | ||
263 | void *data) { | ||
264 | struct sway_view_child *child = | ||
265 | wl_container_of(listener, child, surface_destroy); | ||
266 | view_child_destroy(child); | ||
267 | } | ||
268 | |||
269 | static void view_child_handle_view_unmap(struct wl_listener *listener, | ||
270 | void *data) { | ||
271 | struct sway_view_child *child = | ||
272 | wl_container_of(listener, child, view_unmap); | ||
273 | view_child_destroy(child); | ||
274 | } | ||
275 | |||
276 | static void view_init_subsurfaces(struct sway_view *view, | ||
277 | struct wlr_surface *surface) { | ||
278 | struct wlr_subsurface *subsurface; | ||
279 | wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) { | ||
280 | view_subsurface_create(view, subsurface); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | void view_child_init(struct sway_view_child *child, | ||
285 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
286 | struct wlr_surface *surface) { | ||
287 | child->impl = impl; | ||
288 | child->view = view; | ||
289 | child->surface = surface; | ||
290 | |||
291 | wl_signal_add(&surface->events.commit, &child->surface_commit); | ||
292 | child->surface_commit.notify = view_child_handle_surface_commit; | ||
293 | wl_signal_add(&surface->events.new_subsurface, | ||
294 | &child->surface_new_subsurface); | ||
295 | child->surface_new_subsurface.notify = | ||
296 | view_child_handle_surface_new_subsurface; | ||
297 | wl_signal_add(&surface->events.destroy, &child->surface_destroy); | ||
298 | child->surface_destroy.notify = view_child_handle_surface_destroy; | ||
299 | wl_signal_add(&view->events.unmap, &child->view_unmap); | ||
300 | child->view_unmap.notify = view_child_handle_view_unmap; | ||
301 | |||
302 | struct sway_container *output = child->view->swayc->parent; | ||
303 | if (output != NULL) { | ||
304 | if (output->type != C_OUTPUT) { | ||
305 | output = container_parent(output, C_OUTPUT); | ||
306 | } | ||
307 | wlr_surface_send_enter(child->surface, output->sway_output->wlr_output); | ||
308 | } | ||
309 | |||
310 | view_init_subsurfaces(child->view, surface); | ||
311 | |||
312 | // TODO: only damage the whole child | ||
313 | view_damage(child->view, true); | ||
314 | } | ||
315 | |||
316 | void view_child_destroy(struct sway_view_child *child) { | ||
317 | // TODO: only damage the whole child | ||
318 | view_damage(child->view, true); | ||
319 | |||
320 | wl_list_remove(&child->surface_commit.link); | ||
321 | wl_list_remove(&child->surface_destroy.link); | ||
322 | wl_list_remove(&child->view_unmap.link); | ||
323 | |||
324 | if (child->impl && child->impl->destroy) { | ||
325 | child->impl->destroy(child); | ||
326 | } else { | ||
327 | free(child); | ||
328 | } | ||
329 | } | ||
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c new file mode 100644 index 00000000..316f01e4 --- /dev/null +++ b/sway/tree/workspace.c | |||
@@ -0,0 +1,401 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
2 | #include <ctype.h> | ||
3 | #include <limits.h> | ||
4 | #include <stdbool.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <stdio.h> | ||
7 | #include <strings.h> | ||
8 | #include "stringop.h" | ||
9 | #include "sway/input/input-manager.h" | ||
10 | #include "sway/input/seat.h" | ||
11 | #include "sway/ipc-server.h" | ||
12 | #include "sway/tree/container.h" | ||
13 | #include "sway/tree/workspace.h" | ||
14 | #include "log.h" | ||
15 | #include "util.h" | ||
16 | |||
17 | static struct sway_container *get_workspace_initial_output(const char *name) { | ||
18 | struct sway_container *parent; | ||
19 | // Search for workspace<->output pair | ||
20 | int e = config->workspace_outputs->length; | ||
21 | for (int i = 0; i < config->workspace_outputs->length; ++i) { | ||
22 | struct workspace_output *wso = config->workspace_outputs->items[i]; | ||
23 | if (strcasecmp(wso->workspace, name) == 0) { | ||
24 | // Find output to use if it exists | ||
25 | e = root_container.children->length; | ||
26 | for (i = 0; i < e; ++i) { | ||
27 | parent = root_container.children->items[i]; | ||
28 | if (strcmp(parent->name, wso->output) == 0) { | ||
29 | return parent; | ||
30 | } | ||
31 | } | ||
32 | break; | ||
33 | } | ||
34 | } | ||
35 | // Otherwise put it on the focused output | ||
36 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
37 | struct sway_container *focus = | ||
38 | seat_get_focus_inactive(seat, &root_container); | ||
39 | parent = focus; | ||
40 | parent = container_parent(parent, C_OUTPUT); | ||
41 | return parent; | ||
42 | } | ||
43 | |||
44 | struct sway_container *workspace_create(struct sway_container *output, | ||
45 | const char *name) { | ||
46 | if (output == NULL) { | ||
47 | output = get_workspace_initial_output(name); | ||
48 | } | ||
49 | |||
50 | wlr_log(L_DEBUG, "Added workspace %s for output %s", name, output->name); | ||
51 | struct sway_container *workspace = container_create(C_WORKSPACE); | ||
52 | |||
53 | workspace->x = output->x; | ||
54 | workspace->y = output->y; | ||
55 | workspace->width = output->width; | ||
56 | workspace->height = output->height; | ||
57 | workspace->name = !name ? NULL : strdup(name); | ||
58 | workspace->prev_layout = L_NONE; | ||
59 | workspace->layout = container_get_default_layout(output); | ||
60 | workspace->workspace_layout = workspace->layout; | ||
61 | |||
62 | container_add_child(output, workspace); | ||
63 | container_sort_workspaces(output); | ||
64 | container_create_notify(workspace); | ||
65 | |||
66 | return workspace; | ||
67 | } | ||
68 | |||
69 | char *prev_workspace_name = NULL; | ||
70 | struct workspace_by_number_data { | ||
71 | int len; | ||
72 | const char *cset; | ||
73 | const char *name; | ||
74 | }; | ||
75 | |||
76 | void next_name_map(struct sway_container *ws, void *data) { | ||
77 | int *count = data; | ||
78 | ++count; | ||
79 | } | ||
80 | |||
81 | static bool workspace_valid_on_output(const char *output_name, | ||
82 | const char *ws_name) { | ||
83 | int i; | ||
84 | for (i = 0; i < config->workspace_outputs->length; ++i) { | ||
85 | struct workspace_output *wso = config->workspace_outputs->items[i]; | ||
86 | if (strcasecmp(wso->workspace, ws_name) == 0) { | ||
87 | if (strcasecmp(wso->output, output_name) != 0) { | ||
88 | return false; | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | return true; | ||
94 | } | ||
95 | |||
96 | char *workspace_next_name(const char *output_name) { | ||
97 | wlr_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", | ||
98 | output_name); | ||
99 | int l = 1; | ||
100 | // Scan all workspace bindings to find the next available workspace name, | ||
101 | // if none are found/available then default to a number | ||
102 | struct sway_mode *mode = config->current_mode; | ||
103 | |||
104 | // TODO: iterate over keycode bindings too | ||
105 | int order = INT_MAX; | ||
106 | char *target = NULL; | ||
107 | for (int i = 0; i < mode->keysym_bindings->length; ++i) { | ||
108 | struct sway_binding *binding = mode->keysym_bindings->items[i]; | ||
109 | char *cmdlist = strdup(binding->command); | ||
110 | char *dup = cmdlist; | ||
111 | char *name = NULL; | ||
112 | |||
113 | // workspace n | ||
114 | char *cmd = argsep(&cmdlist, " "); | ||
115 | if (cmdlist) { | ||
116 | name = argsep(&cmdlist, ",;"); | ||
117 | } | ||
118 | |||
119 | if (strcmp("workspace", cmd) == 0 && name) { | ||
120 | wlr_log(L_DEBUG, "Got valid workspace command for target: '%s'", name); | ||
121 | char *_target = strdup(name); | ||
122 | strip_quotes(_target); | ||
123 | while (isspace(*_target)) { | ||
124 | memmove(_target, _target+1, strlen(_target+1)); | ||
125 | } | ||
126 | |||
127 | // Make sure that the command references an actual workspace | ||
128 | // not a command about workspaces | ||
129 | if (strcmp(_target, "next") == 0 || | ||
130 | strcmp(_target, "prev") == 0 || | ||
131 | strcmp(_target, "next_on_output") == 0 || | ||
132 | strcmp(_target, "prev_on_output") == 0 || | ||
133 | strcmp(_target, "number") == 0 || | ||
134 | strcmp(_target, "back_and_forth") == 0 || | ||
135 | strcmp(_target, "current") == 0) | ||
136 | { | ||
137 | free(_target); | ||
138 | free(dup); | ||
139 | continue; | ||
140 | } | ||
141 | |||
142 | // If the command is workspace number <name>, isolate the name | ||
143 | if (strncmp(_target, "number ", strlen("number ")) == 0) { | ||
144 | size_t length = strlen(_target) - strlen("number ") + 1; | ||
145 | char *temp = malloc(length); | ||
146 | strncpy(temp, _target + strlen("number "), length - 1); | ||
147 | temp[length - 1] = '\0'; | ||
148 | free(_target); | ||
149 | _target = temp; | ||
150 | wlr_log(L_DEBUG, "Isolated name from workspace number: '%s'", _target); | ||
151 | |||
152 | // Make sure the workspace number doesn't already exist | ||
153 | if (workspace_by_number(_target)) { | ||
154 | free(_target); | ||
155 | free(dup); | ||
156 | continue; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | // Make sure that the workspace doesn't already exist | ||
161 | if (workspace_by_name(_target)) { | ||
162 | free(_target); | ||
163 | free(dup); | ||
164 | continue; | ||
165 | } | ||
166 | |||
167 | // make sure that the workspace can appear on the given | ||
168 | // output | ||
169 | if (!workspace_valid_on_output(output_name, _target)) { | ||
170 | free(_target); | ||
171 | free(dup); | ||
172 | continue; | ||
173 | } | ||
174 | |||
175 | if (binding->order < order) { | ||
176 | order = binding->order; | ||
177 | free(target); | ||
178 | target = _target; | ||
179 | wlr_log(L_DEBUG, "Workspace: Found free name %s", _target); | ||
180 | } | ||
181 | } | ||
182 | free(dup); | ||
183 | } | ||
184 | if (target != NULL) { | ||
185 | return target; | ||
186 | } | ||
187 | // As a fall back, get the current number of active workspaces | ||
188 | // and return that + 1 for the next workspace's name | ||
189 | int ws_num = root_container.children->length; | ||
190 | if (ws_num >= 10) { | ||
191 | l = 2; | ||
192 | } else if (ws_num >= 100) { | ||
193 | l = 3; | ||
194 | } | ||
195 | char *name = malloc(l + 1); | ||
196 | if (!name) { | ||
197 | wlr_log(L_ERROR, "Could not allocate workspace name"); | ||
198 | return NULL; | ||
199 | } | ||
200 | sprintf(name, "%d", ws_num++); | ||
201 | return name; | ||
202 | } | ||
203 | |||
204 | static bool _workspace_by_number(struct sway_container *view, void *data) { | ||
205 | if (view->type != C_WORKSPACE) { | ||
206 | return false; | ||
207 | } | ||
208 | struct workspace_by_number_data *wbnd = data; | ||
209 | int a = strspn(view->name, wbnd->cset); | ||
210 | return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0; | ||
211 | } | ||
212 | |||
213 | struct sway_container *workspace_by_number(const char* name) { | ||
214 | struct workspace_by_number_data wbnd = {0, "1234567890", name}; | ||
215 | wbnd.len = strspn(name, wbnd.cset); | ||
216 | if (wbnd.len <= 0) { | ||
217 | return NULL; | ||
218 | } | ||
219 | return container_find(&root_container, | ||
220 | _workspace_by_number, (void *) &wbnd); | ||
221 | } | ||
222 | |||
223 | static bool _workspace_by_name(struct sway_container *view, void *data) { | ||
224 | return (view->type == C_WORKSPACE) && | ||
225 | (strcasecmp(view->name, (char *) data) == 0); | ||
226 | } | ||
227 | |||
228 | struct sway_container *workspace_by_name(const char *name) { | ||
229 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
230 | struct sway_container *current_workspace = NULL, *current_output = NULL; | ||
231 | struct sway_container *focus = seat_get_focus(seat); | ||
232 | if (focus) { | ||
233 | current_workspace = container_parent(focus, C_WORKSPACE); | ||
234 | current_output = container_parent(focus, C_OUTPUT); | ||
235 | } | ||
236 | if (strcmp(name, "prev") == 0) { | ||
237 | return workspace_prev(current_workspace); | ||
238 | } else if (strcmp(name, "prev_on_output") == 0) { | ||
239 | return workspace_output_prev(current_output); | ||
240 | } else if (strcmp(name, "next") == 0) { | ||
241 | return workspace_next(current_workspace); | ||
242 | } else if (strcmp(name, "next_on_output") == 0) { | ||
243 | return workspace_output_next(current_output); | ||
244 | } else if (strcmp(name, "current") == 0) { | ||
245 | return current_workspace; | ||
246 | } else { | ||
247 | return container_find(&root_container, _workspace_by_name, | ||
248 | (void *)name); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | /** | ||
253 | * Get the previous or next workspace on the specified output. Wraps around at | ||
254 | * the end and beginning. If next is false, the previous workspace is returned, | ||
255 | * otherwise the next one is returned. | ||
256 | */ | ||
257 | struct sway_container *workspace_output_prev_next_impl( | ||
258 | struct sway_container *output, bool next) { | ||
259 | if (!sway_assert(output->type == C_OUTPUT, | ||
260 | "Argument must be an output, is %d", output->type)) { | ||
261 | return NULL; | ||
262 | } | ||
263 | |||
264 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
265 | struct sway_container *focus = seat_get_focus_inactive(seat, output); | ||
266 | struct sway_container *workspace = (focus->type == C_WORKSPACE ? | ||
267 | focus : | ||
268 | container_parent(focus, C_WORKSPACE)); | ||
269 | |||
270 | int i; | ||
271 | for (i = 0; i < output->children->length; i++) { | ||
272 | if (output->children->items[i] == workspace) { | ||
273 | return output->children->items[ | ||
274 | wrap(i + (next ? 1 : -1), output->children->length)]; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | // Doesn't happen, at worst the for loop returns the previously active | ||
279 | // workspace | ||
280 | return NULL; | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * Get the previous or next workspace. If the first/last workspace on an output | ||
285 | * is active, proceed to the previous/next output's previous/next workspace. If | ||
286 | * next is false, the previous workspace is returned, otherwise the next one is | ||
287 | * returned. | ||
288 | */ | ||
289 | struct sway_container *workspace_prev_next_impl( | ||
290 | struct sway_container *workspace, bool next) { | ||
291 | if (!sway_assert(workspace->type == C_WORKSPACE, | ||
292 | "Argument must be a workspace, is %d", workspace->type)) { | ||
293 | return NULL; | ||
294 | } | ||
295 | |||
296 | struct sway_container *current_output = workspace->parent; | ||
297 | int offset = next ? 1 : -1; | ||
298 | int start = next ? 0 : 1; | ||
299 | int end; | ||
300 | if (next) { | ||
301 | end = current_output->children->length - 1; | ||
302 | } else { | ||
303 | end = current_output->children->length; | ||
304 | } | ||
305 | int i; | ||
306 | for (i = start; i < end; i++) { | ||
307 | if (current_output->children->items[i] == workspace) { | ||
308 | return current_output->children->items[i + offset]; | ||
309 | } | ||
310 | } | ||
311 | |||
312 | // Given workspace is the first/last on the output, jump to the | ||
313 | // previous/next output | ||
314 | int num_outputs = root_container.children->length; | ||
315 | for (i = 0; i < num_outputs; i++) { | ||
316 | if (root_container.children->items[i] == current_output) { | ||
317 | struct sway_container *next_output = root_container.children->items[ | ||
318 | wrap(i + offset, num_outputs)]; | ||
319 | return workspace_output_prev_next_impl(next_output, next); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | // Doesn't happen, at worst the for loop returns the previously active | ||
324 | // workspace on the active output | ||
325 | return NULL; | ||
326 | } | ||
327 | |||
328 | struct sway_container *workspace_output_next(struct sway_container *current) { | ||
329 | return workspace_output_prev_next_impl(current, true); | ||
330 | } | ||
331 | |||
332 | struct sway_container *workspace_next(struct sway_container *current) { | ||
333 | return workspace_prev_next_impl(current, true); | ||
334 | } | ||
335 | |||
336 | struct sway_container *workspace_output_prev(struct sway_container *current) { | ||
337 | return workspace_output_prev_next_impl(current, false); | ||
338 | } | ||
339 | |||
340 | struct sway_container *workspace_prev(struct sway_container *current) { | ||
341 | return workspace_prev_next_impl(current, false); | ||
342 | } | ||
343 | |||
344 | bool workspace_switch(struct sway_container *workspace) { | ||
345 | if (!workspace) { | ||
346 | return false; | ||
347 | } | ||
348 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
349 | struct sway_container *focus = | ||
350 | seat_get_focus_inactive(seat, &root_container); | ||
351 | if (!seat || !focus) { | ||
352 | return false; | ||
353 | } | ||
354 | struct sway_container *active_ws = focus; | ||
355 | if (active_ws->type != C_WORKSPACE) { | ||
356 | active_ws = container_parent(focus, C_WORKSPACE); | ||
357 | } | ||
358 | |||
359 | if (config->auto_back_and_forth | ||
360 | && active_ws == workspace | ||
361 | && prev_workspace_name) { | ||
362 | struct sway_container *new_ws = workspace_by_name(prev_workspace_name); | ||
363 | workspace = new_ws ? | ||
364 | new_ws : | ||
365 | workspace_create(NULL, prev_workspace_name); | ||
366 | } | ||
367 | |||
368 | if (!prev_workspace_name || (strcmp(prev_workspace_name, active_ws->name) | ||
369 | && active_ws != workspace)) { | ||
370 | free(prev_workspace_name); | ||
371 | prev_workspace_name = malloc(strlen(active_ws->name) + 1); | ||
372 | if (!prev_workspace_name) { | ||
373 | wlr_log(L_ERROR, "Unable to allocate previous workspace name"); | ||
374 | return false; | ||
375 | } | ||
376 | strcpy(prev_workspace_name, active_ws->name); | ||
377 | } | ||
378 | |||
379 | // TODO: Deal with sticky containers | ||
380 | |||
381 | wlr_log(L_DEBUG, "Switching to workspace %p:%s", | ||
382 | workspace, workspace->name); | ||
383 | struct sway_container *next = seat_get_focus_inactive(seat, workspace); | ||
384 | if (next == NULL) { | ||
385 | next = workspace; | ||
386 | } | ||
387 | seat_set_focus(seat, next); | ||
388 | struct sway_container *output = container_parent(workspace, C_OUTPUT); | ||
389 | arrange_windows(output, -1, -1); | ||
390 | return true; | ||
391 | } | ||
392 | |||
393 | bool workspace_is_visible(struct sway_container *ws) { | ||
394 | struct sway_container *output = container_parent(ws, C_OUTPUT); | ||
395 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
396 | struct sway_container *focus = seat_get_focus_inactive(seat, output); | ||
397 | if (focus->type != C_WORKSPACE) { | ||
398 | focus = container_parent(focus, C_WORKSPACE); | ||
399 | } | ||
400 | return focus == ws; | ||
401 | } | ||
diff --git a/sway/workspace.c b/sway/workspace.c deleted file mode 100644 index e0367190..00000000 --- a/sway/workspace.c +++ /dev/null | |||
@@ -1,374 +0,0 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
2 | #include <stdlib.h> | ||
3 | #include <stdbool.h> | ||
4 | #include <limits.h> | ||
5 | #include <ctype.h> | ||
6 | #include <wlc/wlc.h> | ||
7 | #include <string.h> | ||
8 | #include <strings.h> | ||
9 | #include <sys/types.h> | ||
10 | #include "sway/ipc-server.h" | ||
11 | #include "sway/extensions.h" | ||
12 | #include "sway/workspace.h" | ||
13 | #include "sway/layout.h" | ||
14 | #include "sway/container.h" | ||
15 | #include "sway/handlers.h" | ||
16 | #include "sway/config.h" | ||
17 | #include "sway/focus.h" | ||
18 | #include "stringop.h" | ||
19 | #include "util.h" | ||
20 | #include "list.h" | ||
21 | #include "log.h" | ||
22 | #include "ipc.h" | ||
23 | |||
24 | char *prev_workspace_name = NULL; | ||
25 | struct workspace_by_number_data { | ||
26 | int len; | ||
27 | const char *cset; | ||
28 | const char *name; | ||
29 | }; | ||
30 | |||
31 | static bool workspace_valid_on_output(const char *output_name, const char *ws_name) { | ||
32 | int i; | ||
33 | for (i = 0; i < config->workspace_outputs->length; ++i) { | ||
34 | struct workspace_output *wso = config->workspace_outputs->items[i]; | ||
35 | if (strcasecmp(wso->workspace, ws_name) == 0) { | ||
36 | if (strcasecmp(wso->output, output_name) != 0) { | ||
37 | return false; | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | |||
42 | return true; | ||
43 | } | ||
44 | |||
45 | char *workspace_next_name(const char *output_name) { | ||
46 | sway_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", output_name); | ||
47 | int i; | ||
48 | int l = 1; | ||
49 | // Scan all workspace bindings to find the next available workspace name, | ||
50 | // if none are found/available then default to a number | ||
51 | struct sway_mode *mode = config->current_mode; | ||
52 | |||
53 | int order = INT_MAX; | ||
54 | char *target = NULL; | ||
55 | for (i = 0; i < mode->bindings->length; ++i) { | ||
56 | struct sway_binding *binding = mode->bindings->items[i]; | ||
57 | char *cmdlist = strdup(binding->command); | ||
58 | char *dup = cmdlist; | ||
59 | char *name = NULL; | ||
60 | |||
61 | // workspace n | ||
62 | char *cmd = argsep(&cmdlist, " "); | ||
63 | if (cmdlist) { | ||
64 | name = argsep(&cmdlist, ",;"); | ||
65 | } | ||
66 | |||
67 | if (strcmp("workspace", cmd) == 0 && name) { | ||
68 | sway_log(L_DEBUG, "Got valid workspace command for target: '%s'", name); | ||
69 | char *_target = strdup(name); | ||
70 | strip_quotes(_target); | ||
71 | while (isspace(*_target)) | ||
72 | _target++; | ||
73 | |||
74 | // Make sure that the command references an actual workspace | ||
75 | // not a command about workspaces | ||
76 | if (strcmp(_target, "next") == 0 || | ||
77 | strcmp(_target, "prev") == 0 || | ||
78 | strcmp(_target, "next_on_output") == 0 || | ||
79 | strcmp(_target, "prev_on_output") == 0 || | ||
80 | strcmp(_target, "number") == 0 || | ||
81 | strcmp(_target, "back_and_forth") == 0 || | ||
82 | strcmp(_target, "current") == 0) | ||
83 | { | ||
84 | free(_target); | ||
85 | free(dup); | ||
86 | continue; | ||
87 | } | ||
88 | |||
89 | // Make sure that the workspace doesn't already exist | ||
90 | if (workspace_by_name(_target)) { | ||
91 | free(_target); | ||
92 | free(dup); | ||
93 | continue; | ||
94 | } | ||
95 | |||
96 | // make sure that the workspace can appear on the given | ||
97 | // output | ||
98 | if (!workspace_valid_on_output(output_name, _target)) { | ||
99 | free(_target); | ||
100 | free(dup); | ||
101 | continue; | ||
102 | } | ||
103 | |||
104 | if (binding->order < order) { | ||
105 | order = binding->order; | ||
106 | free(target); | ||
107 | target = _target; | ||
108 | sway_log(L_DEBUG, "Workspace: Found free name %s", _target); | ||
109 | } | ||
110 | } | ||
111 | free(dup); | ||
112 | } | ||
113 | if (target != NULL) { | ||
114 | return target; | ||
115 | } | ||
116 | // As a fall back, get the current number of active workspaces | ||
117 | // and return that + 1 for the next workspace's name | ||
118 | int ws_num = root_container.children->length; | ||
119 | if (ws_num >= 10) { | ||
120 | l = 2; | ||
121 | } else if (ws_num >= 100) { | ||
122 | l = 3; | ||
123 | } | ||
124 | char *name = malloc(l + 1); | ||
125 | if (!name) { | ||
126 | sway_log(L_ERROR, "Could not allocate workspace name"); | ||
127 | return NULL; | ||
128 | } | ||
129 | sprintf(name, "%d", ws_num++); | ||
130 | return name; | ||
131 | } | ||
132 | |||
133 | swayc_t *workspace_create(const char* name) { | ||
134 | swayc_t *parent; | ||
135 | // Search for workspace<->output pair | ||
136 | int i, e = config->workspace_outputs->length; | ||
137 | for (i = 0; i < e; ++i) { | ||
138 | struct workspace_output *wso = config->workspace_outputs->items[i]; | ||
139 | if (strcasecmp(wso->workspace, name) == 0) | ||
140 | { | ||
141 | // Find output to use if it exists | ||
142 | e = root_container.children->length; | ||
143 | for (i = 0; i < e; ++i) { | ||
144 | parent = root_container.children->items[i]; | ||
145 | if (strcmp(parent->name, wso->output) == 0) { | ||
146 | return new_workspace(parent, name); | ||
147 | } | ||
148 | } | ||
149 | break; | ||
150 | } | ||
151 | } | ||
152 | // Otherwise create a new one | ||
153 | parent = get_focused_container(&root_container); | ||
154 | parent = swayc_parent_by_type(parent, C_OUTPUT); | ||
155 | return new_workspace(parent, name); | ||
156 | } | ||
157 | |||
158 | static bool _workspace_by_name(swayc_t *view, void *data) { | ||
159 | return (view->type == C_WORKSPACE) && | ||
160 | (strcasecmp(view->name, (char *) data) == 0); | ||
161 | } | ||
162 | |||
163 | swayc_t *workspace_by_name(const char* name) { | ||
164 | if (strcmp(name, "prev") == 0) { | ||
165 | return workspace_prev(); | ||
166 | } | ||
167 | else if (strcmp(name, "prev_on_output") == 0) { | ||
168 | return workspace_output_prev(); | ||
169 | } | ||
170 | else if (strcmp(name, "next") == 0) { | ||
171 | return workspace_next(); | ||
172 | } | ||
173 | else if (strcmp(name, "next_on_output") == 0) { | ||
174 | return workspace_output_next(); | ||
175 | } | ||
176 | else if (strcmp(name, "current") == 0) { | ||
177 | return swayc_active_workspace(); | ||
178 | } | ||
179 | else { | ||
180 | return swayc_by_test(&root_container, _workspace_by_name, (void *) name); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | static bool _workspace_by_number(swayc_t *view, void *data) { | ||
185 | if (view->type != C_WORKSPACE) { | ||
186 | return false; | ||
187 | } | ||
188 | struct workspace_by_number_data *wbnd = data; | ||
189 | int a = strspn(view->name, wbnd->cset); | ||
190 | return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0; | ||
191 | } | ||
192 | swayc_t *workspace_by_number(const char* name) { | ||
193 | struct workspace_by_number_data wbnd = {0, "1234567890", name}; | ||
194 | wbnd.len = strspn(name, wbnd.cset); | ||
195 | if (wbnd.len <= 0) { | ||
196 | return NULL; | ||
197 | } | ||
198 | return swayc_by_test(&root_container, _workspace_by_number, (void *) &wbnd); | ||
199 | } | ||
200 | |||
201 | /** | ||
202 | * Get the previous or next workspace on the specified output. | ||
203 | * Wraps around at the end and beginning. | ||
204 | * If next is false, the previous workspace is returned, otherwise the next one is returned. | ||
205 | */ | ||
206 | swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) { | ||
207 | if (!sway_assert(output->type == C_OUTPUT, "Argument must be an output, is %d", output->type)) { | ||
208 | return NULL; | ||
209 | } | ||
210 | |||
211 | int i; | ||
212 | for (i = 0; i < output->children->length; i++) { | ||
213 | if (output->children->items[i] == output->focused) { | ||
214 | return output->children->items[wrap(i + (next ? 1 : -1), output->children->length)]; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | // Doesn't happen, at worst the for loop returns the previously active workspace | ||
219 | return NULL; | ||
220 | } | ||
221 | |||
222 | /** | ||
223 | * Get the previous or next workspace. If the first/last workspace on an output is active, | ||
224 | * proceed to the previous/next output's previous/next workspace. | ||
225 | * If next is false, the previous workspace is returned, otherwise the next one is returned. | ||
226 | */ | ||
227 | swayc_t *workspace_prev_next_impl(swayc_t *workspace, bool next) { | ||
228 | if (!sway_assert(workspace->type == C_WORKSPACE, "Argument must be a workspace, is %d", workspace->type)) { | ||
229 | return NULL; | ||
230 | } | ||
231 | |||
232 | swayc_t *current_output = workspace->parent; | ||
233 | int offset = next ? 1 : -1; | ||
234 | int start = next ? 0 : 1; | ||
235 | int end = next ? (current_output->children->length) - 1 : current_output->children->length; | ||
236 | int i; | ||
237 | for (i = start; i < end; i++) { | ||
238 | if (current_output->children->items[i] == workspace) { | ||
239 | return current_output->children->items[i + offset]; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | // Given workspace is the first/last on the output, jump to the previous/next output | ||
244 | int num_outputs = root_container.children->length; | ||
245 | for (i = 0; i < num_outputs; i++) { | ||
246 | if (root_container.children->items[i] == current_output) { | ||
247 | swayc_t *next_output = root_container.children->items[wrap(i + offset, num_outputs)]; | ||
248 | return workspace_output_prev_next_impl(next_output, next); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | // Doesn't happen, at worst the for loop returns the previously active workspace on the active output | ||
253 | return NULL; | ||
254 | } | ||
255 | |||
256 | swayc_t *workspace_output_next() { | ||
257 | return workspace_output_prev_next_impl(swayc_active_output(), true); | ||
258 | } | ||
259 | |||
260 | swayc_t *workspace_next() { | ||
261 | return workspace_prev_next_impl(swayc_active_workspace(), true); | ||
262 | } | ||
263 | |||
264 | swayc_t *workspace_output_prev() { | ||
265 | return workspace_output_prev_next_impl(swayc_active_output(), false); | ||
266 | } | ||
267 | |||
268 | swayc_t *workspace_prev() { | ||
269 | return workspace_prev_next_impl(swayc_active_workspace(), false); | ||
270 | } | ||
271 | |||
272 | bool workspace_switch(swayc_t *workspace) { | ||
273 | if (!workspace) { | ||
274 | return false; | ||
275 | } | ||
276 | swayc_t *active_ws = swayc_active_workspace(); | ||
277 | if (config->auto_back_and_forth && active_ws == workspace && prev_workspace_name) { | ||
278 | swayc_t *new_ws = workspace_by_name(prev_workspace_name); | ||
279 | workspace = new_ws ? new_ws : workspace_create(prev_workspace_name); | ||
280 | } | ||
281 | |||
282 | if (!prev_workspace_name | ||
283 | || (strcmp(prev_workspace_name, active_ws->name) | ||
284 | && active_ws != workspace)) { | ||
285 | free(prev_workspace_name); | ||
286 | prev_workspace_name = malloc(strlen(active_ws->name) + 1); | ||
287 | if (!prev_workspace_name) { | ||
288 | sway_log(L_ERROR, "Unable to allocate previous workspace name"); | ||
289 | return false; | ||
290 | } | ||
291 | strcpy(prev_workspace_name, active_ws->name); | ||
292 | } | ||
293 | |||
294 | // move sticky containers | ||
295 | if (swayc_parent_by_type(active_ws, C_OUTPUT) == swayc_parent_by_type(workspace, C_OUTPUT)) { | ||
296 | // don't change list while traversing it, use intermediate list instead | ||
297 | list_t *stickies = create_list(); | ||
298 | for (int i = 0; i < active_ws->floating->length; i++) { | ||
299 | swayc_t *cont = active_ws->floating->items[i]; | ||
300 | if (cont->sticky) { | ||
301 | list_add(stickies, cont); | ||
302 | } | ||
303 | } | ||
304 | for (int i = 0; i < stickies->length; i++) { | ||
305 | swayc_t *cont = stickies->items[i]; | ||
306 | sway_log(L_DEBUG, "Moving sticky container %p to %p:%s", | ||
307 | cont, workspace, workspace->name); | ||
308 | swayc_t *parent = remove_child(cont); | ||
309 | add_floating(workspace, cont); | ||
310 | // Destroy old container if we need to | ||
311 | destroy_container(parent); | ||
312 | } | ||
313 | list_free(stickies); | ||
314 | } | ||
315 | sway_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name); | ||
316 | if (!set_focused_container(get_focused_view(workspace))) { | ||
317 | return false; | ||
318 | } | ||
319 | swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT); | ||
320 | arrange_backgrounds(); | ||
321 | arrange_windows(output, -1, -1); | ||
322 | return true; | ||
323 | } | ||
324 | |||
325 | swayc_t *workspace_for_pid(pid_t pid) { | ||
326 | int i; | ||
327 | swayc_t *ws = NULL; | ||
328 | struct pid_workspace *pw = NULL; | ||
329 | |||
330 | sway_log(L_DEBUG, "looking for workspace for pid %d", pid); | ||
331 | |||
332 | // leaving this here as it's useful for debugging | ||
333 | // sway_log(L_DEBUG, "all pid_workspaces"); | ||
334 | // for (int k = 0; k < config->pid_workspaces->length; k++) { | ||
335 | // pw = config->pid_workspaces->items[k]; | ||
336 | // sway_log(L_DEBUG, "pid %d workspace %s time_added %li", *pw->pid, pw->workspace, *pw->time_added); | ||
337 | // } | ||
338 | |||
339 | do { | ||
340 | for (i = 0; i < config->pid_workspaces->length; i++) { | ||
341 | pw = config->pid_workspaces->items[i]; | ||
342 | pid_t *pw_pid = pw->pid; | ||
343 | |||
344 | if (pid == *pw_pid) { | ||
345 | sway_log(L_DEBUG, "found pid_workspace for pid %d, workspace %s", pid, pw->workspace); | ||
346 | break; // out of for loop | ||
347 | } | ||
348 | |||
349 | pw = NULL; | ||
350 | } | ||
351 | |||
352 | if (pw) { | ||
353 | break; // out of do-while loop | ||
354 | } | ||
355 | |||
356 | pid = get_parent_pid(pid); | ||
357 | // no sense in looking for matches for pid 0. | ||
358 | // also, if pid == getpid(), that is the compositor's | ||
359 | // pid, which definitely isn't helpful | ||
360 | } while (pid > 0 && pid != getpid()); | ||
361 | |||
362 | if (pw) { | ||
363 | ws = workspace_by_name(pw->workspace); | ||
364 | |||
365 | if (!ws) { | ||
366 | sway_log(L_DEBUG, "Creating workspace %s for pid %d because it disappeared", pw->workspace, pid); | ||
367 | ws = workspace_create(pw->workspace); | ||
368 | } | ||
369 | |||
370 | list_del(config->pid_workspaces, i); | ||
371 | } | ||
372 | |||
373 | return ws; | ||
374 | } | ||