aboutsummaryrefslogtreecommitdiff
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/CMakeLists.txt107
-rw-r--r--sway/border.c510
-rw-r--r--sway/commands.c480
-rw-r--r--sway/commands/assign.c57
-rw-r--r--sway/commands/bar.c12
-rw-r--r--sway/commands/bar/activate_button.c22
-rw-r--r--sway/commands/bar/binding_mode_indicator.c20
-rw-r--r--sway/commands/bar/bindsym.c39
-rw-r--r--sway/commands/bar/colors.c51
-rw-r--r--sway/commands/bar/context_button.c22
-rw-r--r--sway/commands/bar/font.c13
-rw-r--r--sway/commands/bar/height.c5
-rw-r--r--sway/commands/bar/hidden_state.c24
-rw-r--r--sway/commands/bar/icon_theme.c21
-rw-r--r--sway/commands/bar/id.c7
-rw-r--r--sway/commands/bar/mode.c15
-rw-r--r--sway/commands/bar/modifier.c8
-rw-r--r--sway/commands/bar/output.c15
-rw-r--r--sway/commands/bar/pango_markup.c11
-rw-r--r--sway/commands/bar/position.c29
-rw-r--r--sway/commands/bar/secondary_button.c22
-rw-r--r--sway/commands/bar/separator_symbol.c9
-rw-r--r--sway/commands/bar/status_command.c9
-rw-r--r--sway/commands/bar/strip_workspace_numbers.c18
-rw-r--r--sway/commands/bar/swaybar_command.c9
-rw-r--r--sway/commands/bar/tray_output.c25
-rw-r--r--sway/commands/bar/tray_padding.c29
-rw-r--r--sway/commands/bar/workspace_buttons.c15
-rw-r--r--sway/commands/bar/wrap_scroll.c12
-rw-r--r--sway/commands/bind.c139
-rw-r--r--sway/commands/border.c65
-rw-r--r--sway/commands/client.c72
-rw-r--r--sway/commands/clipboard.c38
-rw-r--r--sway/commands/commands.c26
-rw-r--r--sway/commands/debuglog.c27
-rw-r--r--sway/commands/default_border.c44
-rw-r--r--sway/commands/default_floating_border.c45
-rw-r--r--sway/commands/default_orientation.c (renamed from sway/commands/orientation.c)8
-rw-r--r--sway/commands/exec.c4
-rw-r--r--sway/commands/exec_always.c22
-rw-r--r--sway/commands/exit.c9
-rw-r--r--sway/commands/floating.c81
-rw-r--r--sway/commands/floating_maximum_size.c38
-rw-r--r--sway/commands/floating_minimum_size.c38
-rw-r--r--sway/commands/floating_mod.c42
-rw-r--r--sway/commands/floating_scroll.c46
-rw-r--r--sway/commands/focus.c134
-rw-r--r--sway/commands/focus_follows_mouse.c1
-rw-r--r--sway/commands/font.c27
-rw-r--r--sway/commands/for_window.c41
-rw-r--r--sway/commands/force_focus_wrapping.c13
-rw-r--r--sway/commands/fullscreen.c60
-rw-r--r--sway/commands/gaps.c166
-rw-r--r--sway/commands/hide_edge_borders.c27
-rw-r--r--sway/commands/input.c100
-rw-r--r--sway/commands/input/accel_profile.c13
-rw-r--r--sway/commands/input/click_method.c17
-rw-r--r--sway/commands/input/drag_lock.c16
-rw-r--r--sway/commands/input/dwt.c13
-rw-r--r--sway/commands/input/events.c22
-rw-r--r--sway/commands/input/left_handed.c16
-rw-r--r--sway/commands/input/map_to_output.c27
-rw-r--r--sway/commands/input/middle_emulation.c19
-rw-r--r--sway/commands/input/natural_scroll.c16
-rw-r--r--sway/commands/input/pointer_accel.c16
-rw-r--r--sway/commands/input/scroll_method.c16
-rw-r--r--sway/commands/input/tap.c17
-rw-r--r--sway/commands/input/xkb_layout.c26
-rw-r--r--sway/commands/input/xkb_model.c26
-rw-r--r--sway/commands/input/xkb_options.c26
-rw-r--r--sway/commands/input/xkb_rules.c26
-rw-r--r--sway/commands/input/xkb_variant.c26
-rw-r--r--sway/commands/ipc.c162
-rw-r--r--sway/commands/kill.c16
-rw-r--r--sway/commands/layout.c173
-rw-r--r--sway/commands/log_colors.c22
-rw-r--r--sway/commands/mark.c87
-rw-r--r--sway/commands/mode.c38
-rw-r--r--sway/commands/mouse_warping.c4
-rw-r--r--sway/commands/move.c328
-rw-r--r--sway/commands/new_float.c8
-rw-r--r--sway/commands/new_window.c8
-rw-r--r--sway/commands/no_focus.c41
-rw-r--r--sway/commands/opacity.c36
-rw-r--r--sway/commands/output.c429
-rw-r--r--sway/commands/permit.c108
-rw-r--r--sway/commands/reload.c4
-rw-r--r--sway/commands/resize.c515
-rw-r--r--sway/commands/scratchpad.c72
-rw-r--r--sway/commands/seamless_mouse.c13
-rw-r--r--sway/commands/seat.c61
-rw-r--r--sway/commands/seat/attach.c28
-rw-r--r--sway/commands/seat/cursor.c89
-rw-r--r--sway/commands/seat/fallback.c32
-rw-r--r--sway/commands/set.c13
-rw-r--r--sway/commands/show_marks.c13
-rw-r--r--sway/commands/smart_gaps.c20
-rw-r--r--sway/commands/split.c103
-rw-r--r--sway/commands/sticky.c25
-rw-r--r--sway/commands/swaybg_command.c20
-rw-r--r--sway/commands/unmark.c31
-rw-r--r--sway/commands/workspace.c48
-rw-r--r--sway/commands/workspace_layout.c40
-rw-r--r--sway/commands/ws_auto_back_and_forth.c (renamed from sway/commands/workspace_auto_back_and_forth.c)8
-rw-r--r--sway/config.c1027
-rw-r--r--sway/config/bar.c240
-rw-r--r--sway/config/input.c119
-rw-r--r--sway/config/output.c213
-rw-r--r--sway/config/seat.c146
-rw-r--r--sway/container.c1016
-rw-r--r--sway/criteria.c156
-rw-r--r--sway/debug-tree.c119
-rw-r--r--sway/debug_log.c103
-rw-r--r--sway/desktop/desktop.c14
-rw-r--r--sway/desktop/layer_shell.c347
-rw-r--r--sway/desktop/output.c579
-rw-r--r--sway/desktop/wl_shell.c131
-rw-r--r--sway/desktop/xdg_shell_v6.c249
-rw-r--r--sway/desktop/xwayland.c310
-rw-r--r--sway/extensions.c407
-rw-r--r--sway/focus.c277
-rw-r--r--sway/handlers.c1143
-rw-r--r--sway/input.c69
-rw-r--r--sway/input/cursor.c384
-rw-r--r--sway/input/input-manager.c445
-rw-r--r--sway/input/keyboard.c504
-rw-r--r--sway/input/seat.c675
-rw-r--r--sway/input_state.c490
-rw-r--r--sway/ipc-json.c655
-rw-r--r--sway/ipc-server.c983
-rw-r--r--sway/layout.c1770
-rw-r--r--sway/main.c254
-rw-r--r--sway/meson.build131
-rw-r--r--sway/output.c277
-rw-r--r--sway/security.c212
-rw-r--r--sway/server.c141
-rw-r--r--sway/sway-input.5.txt74
-rw-r--r--sway/sway.1.txt23
-rw-r--r--sway/sway.5.txt75
-rw-r--r--sway/tree/container.c534
-rw-r--r--sway/tree/layout.c1030
-rw-r--r--sway/tree/output.c73
-rw-r--r--sway/tree/view.c329
-rw-r--r--sway/tree/workspace.c401
-rw-r--r--sway/workspace.c374
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 @@
-include_directories(
- ${PROTOCOLS_INCLUDE_DIRS}
- ${WLC_INCLUDE_DIRS}
- ${PCRE_INCLUDE_DIRS}
- ${JSONC_INCLUDE_DIRS}
- ${XKBCOMMON_INCLUDE_DIRS}
- ${LIBINPUT_INCLUDE_DIRS}
- ${CAIRO_INCLUDE_DIRS}
- ${PANGO_INCLUDE_DIRS}
- ${WAYLAND_INCLUDE_DIR}
-)
-
-file(GLOB cmds
- "commands/*.c"
- "commands/bar/*.c"
- "commands/input/*.c"
-)
-
-add_executable(sway
- commands.c
- ${cmds}
- base64.c
- config.c
- container.c
- criteria.c
- debug_log.c
- extensions.c
- focus.c
- handlers.c
- input.c
- input_state.c
- ipc-json.c
- ipc-server.c
- layout.c
- main.c
- output.c
- workspace.c
- border.c
- security.c
-)
-
-add_definitions(
- -DSYSCONFDIR="${CMAKE_INSTALL_FULL_SYSCONFDIR}"
-)
-
-target_link_libraries(sway
- sway-common
- sway-protocols
- sway-wayland
- ${WLC_LIBRARIES}
- ${XKBCOMMON_LIBRARIES}
- ${PCRE_LIBRARIES}
- ${JSONC_LIBRARIES}
- ${WAYLAND_SERVER_LIBRARIES}
- ${LIBINPUT_LIBRARIES}
- ${PANGO_LIBRARIES}
- ${JSONC_LIBRARIES}
- m
-)
-
-if (CMAKE_SYSTEM_NAME STREQUAL Linux)
- target_link_libraries(sway cap)
-endif (CMAKE_SYSTEM_NAME STREQUAL Linux)
-
-install(
- TARGETS sway
- RUNTIME
- DESTINATION bin
- COMPONENT runtime
-)
-
-add_custom_target(configs ALL)
-
-function(add_config name source destination)
- add_custom_command(
- OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
- COMMAND sed -r
- 's?__PREFIX__?${CMAKE_INSTALL_PREFIX}?g\; s?__SYSCONFDIR__?${CMAKE_INSTALL_FULL_SYSCONFDIR}?g\; s?__DATADIR__?${CMAKE_INSTALL_FULL_DATADIR}?g'
- ${PROJECT_SOURCE_DIR}/${source}.in > ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
- DEPENDS ${PROJECT_SOURCE_DIR}/${source}.in
- COMMENT "Generating config file ${source}"
- )
-
- install(
- FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
- DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/${destination}
- COMPONENT configuration
- )
-
- add_custom_target(config-${name} DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name})
- add_dependencies(configs config-${name})
-endfunction()
-
-add_config(config config sway)
-add_config(00-defaults security.d/00-defaults sway/security.d)
-
-if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
- add_config(10-freebsd security.d/10-freebsd sway/security.d)
-endif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
-
-if (A2X_FOUND)
- add_manpage(sway 1)
- add_manpage(sway 5)
- add_manpage(sway-input 5)
- add_manpage(sway-bar 5)
- add_manpage(sway-security 7)
-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 @@
-#define _XOPEN_SOURCE 700
-#include <wlc/wlc-render.h>
-#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <strings.h>
-#include <arpa/inet.h>
-#include "sway/border.h"
-#include "sway/container.h"
-#include "sway/config.h"
-#include "client/pango.h"
-
-void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
- color = htonl(color);
-
- cairo_set_source_rgba(cairo,
- (color >> (2*8) & 0xFF) / 255.0,
- (color >> (1*8) & 0xFF) / 255.0,
- (color >> (0*8) & 0xFF) / 255.0,
- (color >> (3*8) & 0xFF) / 255.0);
-}
-
-void border_clear(struct border *border) {
- if (border && border->buffer) {
- free(border->buffer);
- border->buffer = NULL;
- }
-}
-
-static cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry g, cairo_surface_t **surface) {
- if (view->border == NULL) {
- view->border = malloc(sizeof(struct border));
- if (!view->border) {
- sway_log(L_ERROR, "Unable to allocate window border information");
- return NULL;
- }
- }
- cairo_t *cr;
- int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, g.size.w);
- view->border->buffer = calloc(stride * g.size.h, sizeof(unsigned char));
- view->border->geometry = g;
- if (!view->border->buffer) {
- sway_log(L_ERROR, "Unable to allocate window border buffer");
- return NULL;
- }
- *surface = cairo_image_surface_create_for_data(view->border->buffer,
- CAIRO_FORMAT_ARGB32, g.size.w, g.size.h, stride);
- if (cairo_surface_status(*surface) != CAIRO_STATUS_SUCCESS) {
- border_clear(view->border);
- sway_log(L_ERROR, "Unable to allocate window border surface");
- return NULL;
- }
- cr = cairo_create(*surface);
- cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
- if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
- cairo_surface_destroy(*surface);
- border_clear(view->border);
- sway_log(L_ERROR, "Unable to create cairo context");
- return NULL;
- }
- return cr;
-}
-
-// TODO: move to client/cairo.h when local set_source_u32 is fixed.
-/**
- * Renders a sharp line of any width and height.
- *
- * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0
- * if the line has a width/height of one pixel, respectively.
- */
-static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) {
- cairo_set_source_u32(cairo, color);
-
- if (width > 1 && height > 1) {
- cairo_rectangle(cairo, x, y, width, height);
- cairo_fill(cairo);
- } else {
- if (width == 1) {
- x += 0.5;
- height += y;
- width = x;
- }
-
- if (height == 1) {
- y += 0.5;
- width += x;
- height = y;
- }
-
- cairo_move_to(cairo, x, y);
- cairo_set_line_width(cairo, 1.0);
- cairo_line_to(cairo, width, height);
- cairo_stroke(cairo);
- }
-}
-
-int get_font_text_height(const char *font) {
- cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
- cairo_t *cr = cairo_create(surface);
- int width, height;
- get_text_size(cr, font, &width, &height, 1, false, "Gg");
- cairo_surface_destroy(surface);
- cairo_destroy(cr);
- return height;
-}
-
-static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *colors, bool top) {
- struct wlc_geometry *g = &view->border->geometry;
- struct wlc_geometry *b = &view->border_geometry;
- struct wlc_geometry *v = &view->actual_geometry;
- enum swayc_layouts layout = view->parent->layout;
- uint32_t color;
-
- int x = b->origin.x - g->origin.x;
- int y = b->origin.y - g->origin.y;
-
- // draw vertical/horizontal indicator if container is the only child of its parent container
- bool is_only_child = view->parent && view->parent->children && view->parent->children->length == 1;
-
- // left border
- int left_border = v->origin.x - b->origin.x;
- if (left_border > 0) {
- render_sharp_line(cr,
- colors->child_border,
- x, y,
- left_border,
- b->size.h);
- }
-
- // right border
- int right_border = b->size.w - v->size.w - left_border;
- if (right_border > 0) {
- if (is_only_child && layout == L_HORIZ && !view->is_floating) {
- color = colors->indicator;
- } else {
- color = colors->child_border;
- }
- render_sharp_line(cr,
- color,
- x + b->size.w - right_border,
- y,
- right_border,
- b->size.h);
- }
-
- // top border
- int top_border = v->origin.y - b->origin.y;
- if (top && top_border > 0) {
- render_sharp_line(cr,
- colors->child_border,
- x, y,
- b->size.w,
- top_border);
- }
-
- // bottom border
- int bottom_border = b->size.h - (top_border + v->size.h);
- if (bottom_border > 0) {
- if (is_only_child && layout == L_VERT && !view->is_floating) {
- color = colors->indicator;
- } else {
- color = colors->child_border;
- }
- render_sharp_line(cr,
- color,
- x,
- y + b->size.h - bottom_border,
- b->size.w,
- bottom_border);
- }
-}
-
-static void render_title_bar(swayc_t *view, cairo_t *cr, struct wlc_geometry *b, struct border_colors *colors) {
- struct wlc_geometry *tb = &view->title_bar_geometry;
- int x = MIN(tb->origin.x, tb->origin.x - b->origin.x);
- int y = MIN(tb->origin.y, tb->origin.y - b->origin.y);
-
- // title bar background
- cairo_set_source_u32(cr, colors->background);
- cairo_rectangle(cr, x, y, tb->size.w, tb->size.h);
- cairo_fill(cr);
-
- // header top line
- render_sharp_line(cr, colors->border, x, y, tb->size.w, 1);
-
- // text
- if (view->name) {
- int width, height;
- get_text_size(cr, config->font, &width, &height, 1, false, "%s", view->name);
- cairo_move_to(cr, x + 2, y + 2);
- cairo_set_source_u32(cr, colors->text);
- pango_printf(cr, config->font, 1, false, "%s", view->name);
- }
- // Marks
- if (config->show_marks && view->marks) {
- int total_len = 0;
-
- for(int i = view->marks->length - 1; i >= 0; --i) {
- char *mark = (char *)view->marks->items[i];
- if (*mark != '_') {
- int width, height;
- get_text_size(cr, config->font, &width, &height, 1, false, "[%s]", mark);
- total_len += width;
- if ((int)tb->size.w + x - (total_len + 2) < x + 2) {
- break;
- } else {
- cairo_move_to(cr, (int)tb->size.w + x - (total_len + 2), y + 2);
- cairo_set_source_u32(cr, colors->text);
- pango_printf(cr, config->font, 1, false, "[%s]", mark);
- }
- }
- }
- }
-
- // titlebars has a border all around for tabbed layouts
- if (view->parent->layout == L_TABBED) {
- // header bottom line
- render_sharp_line(cr, colors->border, x, y + tb->size.h - 1,
- tb->size.w, 1);
-
- // left border
- render_sharp_line(cr, colors->border, x, y, 1, tb->size.h);
-
- // right border
- render_sharp_line(cr, colors->border, x + tb->size.w - 1, y,
- 1, tb->size.h);
-
- return;
- }
-
- if ((uint32_t)(view->actual_geometry.origin.y - tb->origin.y) == tb->size.h) {
- // header bottom line
- render_sharp_line(cr, colors->border,
- x + view->actual_geometry.origin.x - tb->origin.x,
- y + tb->size.h - 1,
- view->actual_geometry.size.w, 1);
- } else {
- // header bottom line
- render_sharp_line(cr, colors->border, x,
- y + tb->size.h - 1,
- tb->size.w, 1);
- }
-}
-
-/**
- * Generate nested container title for tabbed/stacked layouts
- */
-static char *generate_container_title(swayc_t *container) {
- char layout = 'H';
- char *name, *prev_name = NULL;
- switch (container->layout) {
- case L_TABBED:
- layout = 'T';
- break;
- case L_STACKED:
- layout = 'S';
- break;
- case L_VERT:
- layout = 'V';
- break;
- default:
- layout = 'H';
- }
- int len = 9;
- name = malloc(len * sizeof(char));
- if (!name) {
- sway_log(L_ERROR, "Unable to allocate container title");
- return NULL;
- }
- snprintf(name, len, "sway: %c[", layout);
-
- int i;
- for (i = 0; i < container->children->length; ++i) {
- prev_name = name;
- swayc_t* child = container->children->items[i];
- const char *title = NULL;
- if (child->type == C_VIEW) {
- title = child->app_id ? child->app_id :
- (child->instance ? child->instance :
- (child->class ? child->class :"(null)"));
- } else { //child->type == C_CONTAINER
- title = generate_container_title(child);
- }
-
- len = strlen(name) + strlen(title) + 1;
- if (i < container->children->length-1) {
- len++;
- }
-
- name = malloc(len * sizeof(char));
- if (!name) {
- free(prev_name);
- sway_log(L_ERROR, "Unable to allocate container title");
- return NULL;
- }
- if (i < container->children->length-1) {
- snprintf(name, len, "%s%s ", prev_name, title);
- } else {
- snprintf(name, len, "%s%s", prev_name, title);
- }
- free(prev_name);
- }
-
- prev_name = name;
- len = strlen(name) + 2;
- name = malloc(len * sizeof(char));
- if (!name) {
- free(prev_name);
- sway_log(L_ERROR, "Unable to allocate container title");
- return NULL;
- }
- snprintf(name, len, "%s]", prev_name);
- free(prev_name);
- free(container->name);
- container->name = name;
- return container->name + 6; // don't include "sway: "
-}
-
-void update_tabbed_stacked_titlebars(swayc_t *c, cairo_t *cr, struct wlc_geometry *g, swayc_t *focused, swayc_t *focused_inactive) {
- if (c->type == C_CONTAINER) {
- if (c->parent->focused == c) {
- render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
- } else {
- render_title_bar(c, cr, g, &config->border_colors.unfocused);
- }
-
- if (!c->visible) {
- return;
- }
-
- int i;
- for (i = 0; i < c->children->length; ++i) {
- swayc_t *child = c->children->items[i];
- update_tabbed_stacked_titlebars(child, cr, g, focused, focused_inactive);
- }
- } else {
- bool is_child_of_focused = swayc_is_child_of(c, get_focused_container(&root_container));
-
- if (focused == c || is_child_of_focused) {
- render_title_bar(c, cr, g, &config->border_colors.focused);
- } else if (focused_inactive == c) {
- render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
- } else {
- render_title_bar(c, cr, g, &config->border_colors.unfocused);
- }
- }
-}
-
-static void update_view_border(swayc_t *view) {
- if (!view->visible) {
- return;
- }
-
- cairo_t *cr = NULL;
- cairo_surface_t *surface = NULL;
-
- // clear previous border buffer.
- border_clear(view->border);
-
- // get focused and focused_inactive views
- swayc_t *focused = get_focused_view(&root_container);
- swayc_t *container = swayc_parent_by_type(view, C_CONTAINER);
- swayc_t *focused_inactive = NULL;
-
- bool is_child_of_focused = swayc_is_parent_of(get_focused_container(&root_container), view);
-
- if (container) {
- focused_inactive = swayc_focus_by_type(container, C_VIEW);
- } else {
- container = swayc_parent_by_type(view, C_WORKSPACE);
- if (container) {
- focused_inactive = swayc_focus_by_type(container, C_VIEW);
- }
- }
-
- // for tabbed/stacked layouts the focused view has to draw all the
- // titlebars of the hidden views.
- swayc_t *p = NULL;
- if (view->parent->focused == view && (p = swayc_tabbed_stacked_ancestor(view))) {
- struct wlc_geometry g = {
- .origin = {
- .x = p->x,
- .y = p->y
- },
- .size = {
- .w = p->width,
- .h = p->height
- }
- };
- cr = create_border_buffer(view, g, &surface);
- if (!cr) {
- goto cleanup;
- }
-
- bool render_top = !should_hide_top_border(view, view->y);
- if (view == focused || is_child_of_focused) {
- render_borders(view, cr, &config->border_colors.focused, render_top);
- } else {
- render_borders(view, cr, &config->border_colors.focused_inactive, render_top);
- }
-
- // generate container titles
- int i;
- for (i = 0; i < p->children->length; ++i) {
- swayc_t *child = p->children->items[i];
- if (child->type == C_CONTAINER) {
- generate_container_title(child);
- }
- }
-
- update_tabbed_stacked_titlebars(p, cr, &g, focused, focused_inactive);
- } else {
- switch (view->border_type) {
- case B_NONE:
- break;
- case B_PIXEL:
- cr = create_border_buffer(view, view->border_geometry, &surface);
- if (!cr) {
- break;
- }
-
- if (focused == view || is_child_of_focused) {
- render_borders(view, cr, &config->border_colors.focused, true);
- } else if (focused_inactive == view) {
- render_borders(view, cr, &config->border_colors.focused_inactive, true);
- } else {
- render_borders(view, cr, &config->border_colors.unfocused, true);
- }
-
- break;
- case B_NORMAL:
- cr = create_border_buffer(view, view->border_geometry, &surface);
- if (!cr) {
- break;
- }
-
- if (focused == view || is_child_of_focused) {
- render_borders(view, cr, &config->border_colors.focused, false);
- render_title_bar(view, cr, &view->border_geometry,
- &config->border_colors.focused);
- } else if (focused_inactive == view) {
- render_borders(view, cr, &config->border_colors.focused_inactive, false);
- render_title_bar(view, cr, &view->border_geometry,
- &config->border_colors.focused_inactive);
- } else {
- render_borders(view, cr, &config->border_colors.unfocused, false);
- render_title_bar(view, cr, &view->border_geometry,
- &config->border_colors.unfocused);
- }
-
- break;
- }
- }
-
-cleanup:
-
- if (surface) {
- cairo_surface_flush(surface);
- cairo_surface_destroy(surface);
- }
-
- if (cr) {
- cairo_destroy(cr);
- }
-}
-
-void update_container_border(swayc_t *container) {
- if (container->type == C_VIEW) {
- update_view_border(container);
- return;
- } else {
- for (int i = 0; i < container->children->length; ++i) {
- update_container_border(container->children->items[i]);
- }
- }
-}
-
-void render_view_borders(wlc_handle view) {
- swayc_t *c = swayc_by_handle(view);
-
-
- // emulate i3 behavior for drawing borders for tabbed and stacked layouts:
- // if we are not the only child in the container, always draw borders,
- // regardless of the border setting on the individual view
- if (!c || (c->border_type == B_NONE
- && !((c->parent->layout == L_TABBED || c->parent->layout == L_STACKED)
- && c->parent->children->length > 1))) {
- return;
- }
-
- if (c->border && c->border->buffer) {
- wlc_pixels_write(WLC_RGBA8888, &c->border->geometry, c->border->buffer);
- }
-}
-
-bool should_hide_top_border(swayc_t *con, double y) {
- // returns true if container is child of tabbed/stacked layout and is
- // sharing top border with tabbed titlebar
- swayc_t *par = con->parent;
- while (par->type != C_WORKSPACE) {
- if (par->layout == L_TABBED || par->layout == L_STACKED) {
- return con->y == y;
- }
- con = par;
- par = par->parent;
- }
- return false;
-}
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 @@
-#define _XOPEN_SOURCE 700
-#include <xkbcommon/xkbcommon.h>
-#include <xkbcommon/xkbcommon-names.h>
-#include <wlc/wlc.h>
-#include <wlc/wlc-render.h>
-#include <stdio.h>
+#define _XOPEN_SOURCE 500
+#include <ctype.h>
+#include <stdarg.h>
#include <stdlib.h>
-#include <errno.h>
#include <string.h>
#include <strings.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <wordexp.h>
-#include <libgen.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <limits.h>
-#include <float.h>
-#include <libinput.h>
-#include "sway/layout.h"
-#include "sway/focus.h"
-#include "sway/workspace.h"
+#include <stdio.h>
+#include <json-c/json.h>
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/output.h"
-#include "sway/handlers.h"
-#include "sway/input_state.h"
+#include "sway/config.h"
#include "sway/criteria.h"
-#include "sway/ipc-server.h"
#include "sway/security.h"
-#include "sway/input.h"
-#include "sway/border.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
#include "stringop.h"
-#include "sway.h"
-#include "util.h"
-#include "list.h"
#include "log.h"
struct cmd_handler {
@@ -41,10 +20,6 @@ struct cmd_handler {
sway_cmd *handle;
};
-int sp_index = 0;
-
-swayc_t *current_container = NULL;
-
// Returns error object, or NULL if check succeeds.
struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) {
struct cmd_results *error = NULL;
@@ -84,24 +59,7 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type
return error;
}
-void hide_view_in_scratchpad(swayc_t *sp_view) {
- if (sp_view == NULL) {
- return;
- }
-
- wlc_view_set_mask(sp_view->handle, 0);
- sp_view->visible = false;
- swayc_t *ws = sp_view->parent;
- remove_child(sp_view);
- if (swayc_active_workspace() != ws && ws->floating->length == 0 && ws->children->length == 0) {
- destroy_workspace(ws);
- } else {
- arrange_windows(ws, -1, -1);
- }
- set_focused_container(container_under_pointer());
-}
-
-void input_cmd_apply(struct input_config *input) {
+void apply_input_config(struct input_config *input) {
int i;
i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier);
if (i >= 0) {
@@ -114,111 +72,41 @@ void input_cmd_apply(struct input_config *input) {
list_add(config->input_configs, input);
}
- current_input_config = input;
-
- if (input->identifier) {
- // Try to find the input device and apply configuration now. If
- // this is during startup then there will be no container and config
- // will be applied during normal "new input" event from wlc.
- struct libinput_device *device = NULL;
- for (int i = 0; i < input_devices->length; ++i) {
- device = input_devices->items[i];
- char* dev_identifier = libinput_dev_unique_id(device);
- if (!dev_identifier) {
- break;
- }
- int match = dev_identifier && strcmp(dev_identifier, input->identifier) == 0;
- free(dev_identifier);
- if (match) {
- apply_input_config(input, device);
- break;
- }
- }
- }
+ input_manager_apply_input_config(input_manager, input);
}
-void remove_view_from_scratchpad(swayc_t *view) {
+void apply_seat_config(struct seat_config *seat_config) {
int i;
- for (i = 0; i < scratchpad->length; i++) {
- if (scratchpad->items[i] == view) {
- if (sp_index == 0) {
- sp_index = scratchpad->length - 1;
- } else {
- sp_index--;
- }
- list_del(scratchpad, sp_index);
- sp_view = NULL;
- }
+ i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name);
+ if (i >= 0) {
+ // merge existing config
+ struct seat_config *sc = config->seat_configs->items[i];
+ merge_seat_config(sc, seat_config);
+ free_seat_config(seat_config);
+ seat_config = sc;
+ } else {
+ list_add(config->seat_configs, seat_config);
}
+
+ input_manager_apply_seat_config(input_manager, seat_config);
}
/* Keep alphabetized */
static struct cmd_handler handlers[] = {
- { "assign", cmd_assign },
{ "bar", cmd_bar },
{ "bindcode", cmd_bindcode },
{ "bindsym", cmd_bindsym },
- { "border", cmd_border },
- { "client.background", cmd_client_background },
- { "client.focused", cmd_client_focused },
- { "client.focused_inactive", cmd_client_focused_inactive },
- { "client.placeholder", cmd_client_placeholder },
- { "client.unfocused", cmd_client_unfocused },
- { "client.urgent", cmd_client_urgent },
- { "clipboard", cmd_clipboard },
- { "commands", cmd_commands },
- { "debuglog", cmd_debuglog },
- { "default_border", cmd_default_border },
- { "default_floating_border", cmd_default_floating_border },
- { "default_orientation", cmd_orientation },
{ "exec", cmd_exec },
{ "exec_always", cmd_exec_always },
- { "exit", cmd_exit },
- { "floating", cmd_floating },
- { "floating_maximum_size", cmd_floating_maximum_size },
- { "floating_minimum_size", cmd_floating_minimum_size },
- { "floating_modifier", cmd_floating_mod },
- { "floating_scroll", cmd_floating_scroll },
- { "focus", cmd_focus },
{ "focus_follows_mouse", cmd_focus_follows_mouse },
- { "font", cmd_font },
- { "for_window", cmd_for_window },
- { "force_focus_wrapping", cmd_force_focus_wrapping },
- { "fullscreen", cmd_fullscreen },
- { "gaps", cmd_gaps },
- { "hide_edge_borders", cmd_hide_edge_borders },
{ "include", cmd_include },
{ "input", cmd_input },
- { "ipc", cmd_ipc },
- { "kill", cmd_kill },
- { "layout", cmd_layout },
- { "log_colors", cmd_log_colors },
- { "mark", cmd_mark },
{ "mode", cmd_mode },
{ "mouse_warping", cmd_mouse_warping },
- { "move", cmd_move },
- { "new_float", cmd_new_float },
- { "new_window", cmd_new_window },
- { "no_focus", cmd_no_focus },
{ "output", cmd_output },
- { "permit", cmd_permit },
- { "reject", cmd_reject },
- { "reload", cmd_reload },
- { "resize", cmd_resize },
- { "scratchpad", cmd_scratchpad },
- { "seamless_mouse", cmd_seamless_mouse },
- { "set", cmd_set },
- { "show_marks", cmd_show_marks },
- { "smart_gaps", cmd_smart_gaps },
- { "split", cmd_split },
- { "splith", cmd_splith },
- { "splitt", cmd_splitt },
- { "splitv", cmd_splitv },
- { "sticky", cmd_sticky },
- { "unmark", cmd_unmark },
+ { "seat", cmd_seat },
{ "workspace", cmd_workspace },
{ "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
- { "workspace_layout", cmd_workspace_layout },
};
static struct cmd_handler bar_handlers[] = {
@@ -248,54 +136,6 @@ static struct cmd_handler bar_handlers[] = {
{ "wrap_scroll", bar_cmd_wrap_scroll },
};
-/**
- * Check and add color to buffer.
- *
- * return error object, or NULL if color is valid.
- */
-struct cmd_results *add_color(const char *name, char *buffer, const char *color) {
- int len = strlen(color);
- if (len != 7 && len != 9) {
- return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
- }
-
- if (color[0] != '#') {
- return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
- }
-
- int i;
- for (i = 1; i < len; ++i) {
- if (!isxdigit(color[i])) {
- return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
- }
- }
-
- // copy color to buffer
- strncpy(buffer, color, len);
- // add default alpha channel if color was defined without it
- if (len == 7) {
- buffer[7] = 'f';
- buffer[8] = 'f';
- }
- buffer[9] = '\0';
-
- return NULL;
-}
-
-static struct cmd_handler input_handlers[] = {
- { "accel_profile", input_cmd_accel_profile },
- { "click_method", input_cmd_click_method },
- { "drag_lock", input_cmd_drag_lock },
- { "dwt", input_cmd_dwt },
- { "events", input_cmd_events },
- { "left_handed", input_cmd_left_handed },
- { "middle_emulation", input_cmd_middle_emulation },
- { "natural_scroll", input_cmd_natural_scroll },
- { "pointer_accel", input_cmd_pointer_accel },
- { "scroll_method", input_cmd_scroll_method },
- { "tap", input_cmd_tap },
-};
-
static struct cmd_handler bar_colors_handlers[] = {
{ "active_workspace", bar_colors_cmd_active_workspace },
{ "background", bar_colors_cmd_background },
@@ -310,26 +150,27 @@ static struct cmd_handler bar_colors_handlers[] = {
{ "urgent_workspace", bar_colors_cmd_urgent_workspace },
};
-static struct cmd_handler ipc_handlers[] = {
- { "*", cmd_ipc_cmd },
- { "bar-config", cmd_ipc_cmd },
- { "command", cmd_ipc_cmd },
- { "events", cmd_ipc_events },
- { "inputs", cmd_ipc_cmd },
- { "marks", cmd_ipc_cmd },
- { "outputs", cmd_ipc_cmd },
- { "tree", cmd_ipc_cmd },
- { "workspaces", cmd_ipc_cmd },
+/* Config-time only commands. Keep alphabetized */
+static struct cmd_handler config_handlers[] = {
+ { "default_orientation", cmd_default_orientation },
+ { "set", cmd_set },
+ { "swaybg_command", cmd_swaybg_command },
};
-static struct cmd_handler ipc_event_handlers[] = {
- { "*", cmd_ipc_event_cmd },
- { "binding", cmd_ipc_event_cmd },
- { "input", cmd_ipc_event_cmd },
- { "mode", cmd_ipc_event_cmd },
- { "output", cmd_ipc_event_cmd },
- { "window", cmd_ipc_event_cmd },
- { "workspace", cmd_ipc_event_cmd },
+/* Runtime-only commands. Keep alphabetized */
+static struct cmd_handler command_handlers[] = {
+ { "exit", cmd_exit },
+ { "focus", cmd_focus },
+ { "kill", cmd_kill },
+ { "layout", cmd_layout },
+ { "move", cmd_move },
+ { "opacity", cmd_opacity },
+ { "reload", cmd_reload },
+ { "resize", cmd_resize },
+ { "split", cmd_split },
+ { "splith", cmd_splith },
+ { "splitt", cmd_splitt },
+ { "splitv", cmd_splitv },
};
static int handler_compare(const void *_a, const void *_b) {
@@ -338,42 +179,90 @@ static int handler_compare(const void *_a, const void *_b) {
return strcasecmp(a->command, b->command);
}
+// must be in order for the bsearch
+static struct cmd_handler input_handlers[] = {
+ { "accel_profile", input_cmd_accel_profile },
+ { "click_method", input_cmd_click_method },
+ { "drag_lock", input_cmd_drag_lock },
+ { "dwt", input_cmd_dwt },
+ { "events", input_cmd_events },
+ { "left_handed", input_cmd_left_handed },
+ { "map_to_output", input_cmd_map_to_output },
+ { "middle_emulation", input_cmd_middle_emulation },
+ { "natural_scroll", input_cmd_natural_scroll },
+ { "pointer_accel", input_cmd_pointer_accel },
+ { "scroll_method", input_cmd_scroll_method },
+ { "tap", input_cmd_tap },
+ { "xkb_layout", input_cmd_xkb_layout },
+ { "xkb_model", input_cmd_xkb_model },
+ { "xkb_options", input_cmd_xkb_options },
+ { "xkb_rules", input_cmd_xkb_rules },
+ { "xkb_variant", input_cmd_xkb_variant },
+};
+
+// must be in order for the bsearch
+static struct cmd_handler seat_handlers[] = {
+ { "attach", seat_cmd_attach },
+ { "cursor", seat_cmd_cursor },
+ { "fallback", seat_cmd_fallback },
+};
+
static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
struct cmd_handler d = { .command=line };
struct cmd_handler *res = NULL;
- sway_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_INPUT);
+ wlr_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_SEAT);
+
+ bool config_loading = config->reading || !config->active;
+
if (block == CMD_BLOCK_BAR) {
- res = bsearch(&d, bar_handlers,
- sizeof(bar_handlers) / sizeof(struct cmd_handler),
- sizeof(struct cmd_handler), handler_compare);
- } else if (block == CMD_BLOCK_BAR_COLORS){
- res = bsearch(&d, bar_colors_handlers,
- sizeof(bar_colors_handlers) / sizeof(struct cmd_handler),
- sizeof(struct cmd_handler), handler_compare);
+ return bsearch(&d, bar_handlers,
+ sizeof(bar_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+ } else if (block == CMD_BLOCK_BAR_COLORS) {
+ return bsearch(&d, bar_colors_handlers,
+ sizeof(bar_colors_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
} else if (block == CMD_BLOCK_INPUT) {
- res = bsearch(&d, input_handlers,
- sizeof(input_handlers) / sizeof(struct cmd_handler),
- sizeof(struct cmd_handler), handler_compare);
- } else if (block == CMD_BLOCK_IPC) {
- res = bsearch(&d, ipc_handlers,
- sizeof(ipc_handlers) / sizeof(struct cmd_handler),
- sizeof(struct cmd_handler), handler_compare);
- } else if (block == CMD_BLOCK_IPC_EVENTS) {
- res = bsearch(&d, ipc_event_handlers,
- sizeof(ipc_event_handlers) / sizeof(struct cmd_handler),
- sizeof(struct cmd_handler), handler_compare);
- } else {
- res = bsearch(&d, handlers,
+ return bsearch(&d, input_handlers,
+ sizeof(input_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+ } else if (block == CMD_BLOCK_SEAT) {
+ return bsearch(&d, seat_handlers,
+ sizeof(seat_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+ }
+
+ if (!config_loading) {
+ res = bsearch(&d, command_handlers,
+ sizeof(command_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+
+ if (res) {
+ return res;
+ }
+ }
+
+ if (config->reading) {
+ res = bsearch(&d, config_handlers,
+ sizeof(config_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+
+ if (res) {
+ return res;
+ }
+ }
+
+ res = bsearch(&d, handlers,
sizeof(handlers) / sizeof(struct cmd_handler),
sizeof(struct cmd_handler), handler_compare);
- }
+
return res;
}
-struct cmd_results *handle_command(char *_exec, enum command_context context) {
+struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
// Even though this function will process multiple commands we will only
// return the last error, if any (for now). (Since we have access to an
- // error string we could e.g. concatonate all errors there.)
+ // error string we could e.g. concatenate all errors there.)
struct cmd_results *results = NULL;
char *exec = strdup(_exec);
char *head = exec;
@@ -381,10 +270,22 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
char *cmd;
list_t *containers = NULL;
+ if (seat == NULL) {
+ // passing a NULL seat means we just pick the default seat
+ seat = input_manager_get_default_seat(input_manager);
+ if (!sway_assert(seat, "could not find a seat to run the command on")) {
+ return NULL;
+ }
+ }
+
+ config->handler_context.seat = seat;
+
head = exec;
do {
// Extract criteria (valid for this command list only).
+ bool has_criteria = false;
if (*head == '[') {
+ has_criteria = true;
++head;
char *criteria_string = argsep(&head, "]");
if (head) {
@@ -393,13 +294,14 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
char *error;
if ((error = extract_crit_tokens(tokens, criteria_string))) {
+ wlr_log(L_DEBUG, "criteria string parse error: %s", error);
results = cmd_results_new(CMD_INVALID, criteria_string,
"Can't parse criteria string: %s", error);
free(error);
free(tokens);
goto cleanup;
}
- containers = container_for(tokens);
+ containers = container_for_crit_tokens(tokens);
free(tokens);
} else {
@@ -419,10 +321,10 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
cmd = argsep(&cmdlist, ",");
cmd += strspn(cmd, whitespace);
if (strcmp(cmd, "") == 0) {
- sway_log(L_INFO, "Ignoring empty command.");
+ wlr_log(L_INFO, "Ignoring empty command.");
continue;
}
- sway_log(L_INFO, "Handling command '%s'", cmd);
+ wlr_log(L_INFO, "Handling command '%s'", cmd);
//TODO better handling of argv
int argc;
char **argv = split_args(cmd, &argc);
@@ -443,31 +345,16 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
free_argv(argc, argv);
goto cleanup;
}
- if (!(get_command_policy_mask(argv[0]) & context)) {
- if (results) {
- free_cmd_results(results);
- }
- results = cmd_results_new(CMD_INVALID, cmd,
- "Permission denied for %s via %s", cmd,
- command_policy_str(context));
- free_argv(argc, argv);
- goto cleanup;
- }
- int i = 0;
- do {
- if (!containers) {
- current_container = get_focused_container(&root_container);
- } else if (containers->length == 0) {
- if (results) {
- free_cmd_results(results);
- }
- results = cmd_results_new(CMD_FAILURE, argv[0], "No matching container");
- goto cleanup;
- } else {
- current_container = (swayc_t *)containers->items[i];
- }
- sway_log(L_INFO, "Running on container '%s'", current_container->name);
+ if (!has_criteria) {
+ // without criteria, the command acts upon the focused
+ // container
+ config->handler_context.current_container =
+ seat_get_focus_inactive(seat, &root_container);
+ if (!sway_assert(config->handler_context.current_container,
+ "could not get focus-inactive for root container")) {
+ return NULL;
+ }
struct cmd_results *res = handler->handle(argc-1, argv+1);
if (res->status != CMD_SUCCESS) {
free_argv(argc, argv);
@@ -478,35 +365,39 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
goto cleanup;
}
free_cmd_results(res);
- ++i;
- } while(containers && i < containers->length);
-
+ } else {
+ for (int i = 0; i < containers->length; ++i) {
+ config->handler_context.current_container = containers->items[i];
+ struct cmd_results *res = handler->handle(argc-1, argv+1);
+ if (res->status != CMD_SUCCESS) {
+ free_argv(argc, argv);
+ if (results) {
+ free_cmd_results(results);
+ }
+ results = res;
+ goto cleanup;
+ }
+ free_cmd_results(res);
+ }
+ }
free_argv(argc, argv);
} while(cmdlist);
-
- if (containers) {
- list_free(containers);
- containers = NULL;
- }
} while(head);
- cleanup:
+cleanup:
free(exec);
- if (containers) {
- free(containers);
- }
if (!results) {
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
return results;
}
-// this is like handle_command above, except:
+// this is like execute_command above, except:
// 1) it ignores empty commands (empty lines)
// 2) it does variable substitution
// 3) it doesn't split commands (because the multiple commands are supposed to
// be chained together)
-// 4) handle_command handles all state internally while config_command has some
-// state handled outside (notably the block mode, in read_config)
+// 4) execute_command handles all state internally while config_command has
+// some state handled outside (notably the block mode, in read_config)
struct cmd_results *config_command(char *exec, enum cmd_status block) {
struct cmd_results *results = NULL;
int argc;
@@ -516,7 +407,7 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) {
goto cleanup;
}
- sway_log(L_INFO, "handling config command '%s'", exec);
+ wlr_log(L_INFO, "handling config command '%s'", exec);
// Endblock
if (**argv == '}') {
results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
@@ -530,12 +421,13 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) {
}
int i;
// Var replacement, for all but first argument of set
+ // TODO commands
for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
argv[i] = do_var_replacement(argv[i]);
unescape_string(argv[i]);
}
- /* Strip quotes for first argument.
- * TODO This part needs to be handled much better */
+ // Strip quotes for first argument.
+ // TODO This part needs to be handled much better
if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
strip_quotes(argv[1]);
}
@@ -619,7 +511,7 @@ struct cmd_results *config_commands_command(char *exec) {
}
policy->context = context;
- sway_log(L_INFO, "Set command policy for %s to %d",
+ wlr_log(L_INFO, "Set command policy for %s to %d",
policy->command, policy->context);
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -629,10 +521,11 @@ cleanup:
return results;
}
-struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *format, ...) {
+struct cmd_results *cmd_results_new(enum cmd_status status,
+ const char *input, const char *format, ...) {
struct cmd_results *results = malloc(sizeof(struct cmd_results));
if (!results) {
- sway_log(L_ERROR, "Unable to allocate command results");
+ wlr_log(L_ERROR, "Unable to allocate command results");
return NULL;
}
results->status = status;
@@ -669,12 +562,15 @@ void free_cmd_results(struct cmd_results *results) {
const char *cmd_results_to_json(struct cmd_results *results) {
json_object *result_array = json_object_new_array();
json_object *root = json_object_new_object();
- json_object_object_add(root, "success", json_object_new_boolean(results->status == CMD_SUCCESS));
+ json_object_object_add(root, "success",
+ json_object_new_boolean(results->status == CMD_SUCCESS));
if (results->input) {
- json_object_object_add(root, "input", json_object_new_string(results->input));
+ json_object_object_add(
+ root, "input", json_object_new_string(results->input));
}
if (results->error) {
- json_object_object_add(root, "error", json_object_new_string(results->error));
+ json_object_object_add(
+ root, "error", json_object_new_string(results->error));
}
json_object_array_add(result_array, root);
const char *json = json_object_to_json_string(result_array);
@@ -682,3 +578,35 @@ const char *cmd_results_to_json(struct cmd_results *results) {
free(root);
return json;
}
+
+/**
+ * Check and add color to buffer.
+ *
+ * return error object, or NULL if color is valid.
+ */
+struct cmd_results *add_color(const char *name,
+ char *buffer, const char *color) {
+ int len = strlen(color);
+ if (len != 7 && len != 9) {
+ return cmd_results_new(CMD_INVALID, name,
+ "Invalid color definition %s", color);
+ }
+ if (color[0] != '#') {
+ return cmd_results_new(CMD_INVALID, name,
+ "Invalid color definition %s", color);
+ }
+ for (int i = 1; i < len; ++i) {
+ if (!isxdigit(color[i])) {
+ return cmd_results_new(CMD_INVALID, name,
+ "Invalid color definition %s", color);
+ }
+ }
+ strncpy(buffer, color, len);
+ // add default alpha channel if color was defined without it
+ if (len == 7) {
+ buffer[7] = 'f';
+ buffer[8] = 'f';
+ }
+ buffer[9] = '\0';
+ return NULL;
+}
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 @@
-#define _XOPEN_SOURCE 700
-#include <stdio.h>
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/criteria.h"
-#include "list.h"
-#include "log.h"
-
-struct cmd_results *cmd_assign(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "assign", EXPECTED_AT_LEAST, 2))) {
- return error;
- }
-
- char *criteria = *argv++;
-
- if (strncmp(*argv, "→", strlen("→")) == 0) {
- if (argc < 3) {
- return cmd_results_new(CMD_INVALID, "assign", "Missing workspace");
- }
- argv++;
- }
-
- char *movecmd = "move container to workspace ";
- int arglen = strlen(movecmd) + strlen(*argv) + 1;
- char *cmdlist = calloc(1, arglen);
- if (!cmdlist) {
- return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate command list");
- }
- snprintf(cmdlist, arglen, "%s%s", movecmd, *argv);
-
- struct criteria *crit = malloc(sizeof(struct criteria));
- if (!crit) {
- free(cmdlist);
- return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate criteria");
- }
- crit->crit_raw = strdup(criteria);
- crit->cmdlist = cmdlist;
- crit->tokens = create_list();
- char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
-
- if (err_str) {
- error = cmd_results_new(CMD_INVALID, "assign", err_str);
- free(err_str);
- free_criteria(crit);
- } else if (crit->tokens->length == 0) {
- error = cmd_results_new(CMD_INVALID, "assign", "Found no name/value pairs in criteria");
- free_criteria(crit);
- } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) {
- sway_log(L_DEBUG, "assign: Duplicate, skipping.");
- free_criteria(crit);
- } else {
- sway_log(L_DEBUG, "assign: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist);
- list_add(config->criteria, crit);
- }
- return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdio.h>
#include <string.h>
#include <strings.h>
+#include <wlr/util/log.h>
#include "sway/commands.h"
#include "sway/config.h"
-#include "log.h"
#include "util.h"
struct cmd_results *cmd_bar(int argc, char **argv) {
@@ -27,7 +26,6 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
return bar_cmd_hidden_state(argc-1, argv + 1);
}
}
-
return cmd_results_new(CMD_FAILURE, "bar", "Can only be used in config file.");
}
@@ -38,15 +36,15 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
}
// set bar id
- int i;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
if (bar == config->bars->items[i]) {
const int len = 5 + numlen(i); // "bar-" + i + \0
bar->id = malloc(len * sizeof(char));
if (bar->id) {
snprintf(bar->id, len, "bar-%d", i);
} else {
- return cmd_results_new(CMD_FAILURE, "bar", "Unable to allocate bar ID");
+ return cmd_results_new(CMD_FAILURE,
+ "bar", "Unable to allocate bar ID");
}
break;
}
@@ -54,6 +52,6 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
// Set current bar
config->current_bar = bar;
- sway_log(L_DEBUG, "Configuring bar %s", bar->id);
+ wlr_log(L_DEBUG, "Configuring bar %s", bar->id);
return cmd_results_new(CMD_BLOCK_BAR, NULL, NULL);
}
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 @@
#include "log.h"
struct cmd_results *bar_cmd_activate_button(int argc, char **argv) {
- const char *cmd_name = "activate_button";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- // User should be able to prefix with 0x or whatever they want
- config->current_bar->secondary_button = strtoul(argv[0], NULL, 0);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "activate_button", "TODO TRAY");
}
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 @@
struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) {
+ if ((error = checkarg(argc,
+ "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "binding_mode_indicator", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "binding_mode_indicator", "No bar defined.");
}
-
if (strcasecmp("yes", argv[0]) == 0) {
config->current_bar->binding_mode_indicator = true;
- sway_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s",
+ config->current_bar->id);
} else if (strcasecmp("no", argv[0]) == 0) {
config->current_bar->binding_mode_indicator = false;
- sway_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", config->current_bar->id);
- } else {
- error = cmd_results_new(CMD_INVALID, "binding_mode_indicator", "Invalid value %s", argv[0]);
- return error;
+ wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s",
+ config->current_bar->id);
}
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ return cmd_results_new(CMD_INVALID, "binding_mode_indicator",
+ "Invalid value %s", argv[0]);
}
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 @@
#include "stringop.h"
struct cmd_results *bar_cmd_bindsym(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
- return error;
- } else if (!config->reading) {
- return cmd_results_new(CMD_FAILURE, "bindsym", "Can only be used in config file.");
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "bindsym", "No bar defined.");
- }
-
- if (strlen(argv[1]) != 7) {
- return cmd_results_new(CMD_INVALID, "bindsym", "Invalid mouse binding %s", argv[1]);
- }
- uint32_t numbutton = (uint32_t)atoi(argv[1] + 6);
- if (numbutton < 1 || numbutton > 5 || strncmp(argv[1], "button", 6) != 0) {
- return cmd_results_new(CMD_INVALID, "bindsym", "Invalid mouse binding %s", argv[1]);
- }
- struct sway_mouse_binding *binding = malloc(sizeof(struct sway_mouse_binding));
- if (!binding) {
- return cmd_results_new(CMD_FAILURE, "bindsym", "Unable to allocate binding");
- }
- binding->button = numbutton;
- binding->command = join_args(argv + 1, argc - 1);
-
- struct bar_config *bar = config->current_bar;
- int i = list_seq_find(bar->bindings, sway_mouse_binding_cmp_buttons, binding);
- if (i > -1) {
- sway_log(L_DEBUG, "bindsym - '%s' for swaybar already exists, overwriting", argv[0]);
- struct sway_mouse_binding *dup = bar->bindings->items[i];
- free_sway_mouse_binding(dup);
- list_del(bar->bindings, i);
- }
- list_add(bar->bindings, binding);
- list_qsort(bar->bindings, sway_mouse_binding_cmp_qsort);
-
- sway_log(L_DEBUG, "bindsym - Bound %s to command %s when clicking swaybar", argv[0], binding->command);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ return cmd_results_new(CMD_FAILURE, "bindsym", "TODO"); // TODO
}
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 @@
#include <string.h>
#include "sway/commands.h"
-static struct cmd_results *parse_single_color(char **color, const char *cmd_name, int argc, char **argv) {
+static struct cmd_results *parse_single_color(char **color,
+ const char *cmd_name, int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
- if (!*color) {
- *color = malloc(10);
- if (!*color) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color");
- }
+ if (!*color && !(*color = malloc(10))) {
+ return NULL;
}
-
error = add_color(cmd_name, *color, argv[0]);
if (error) {
return error;
}
-
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
-static struct cmd_results *parse_three_colors(char ***colors, const char *cmd_name, int argc, char **argv) {
+static struct cmd_results *parse_three_colors(char ***colors,
+ const char *cmd_name, int argc, char **argv) {
struct cmd_results *error = NULL;
if (argc != 3) {
- return cmd_results_new(CMD_INVALID, cmd_name, "Requires exactly three color values");
+ return cmd_results_new(CMD_INVALID,
+ cmd_name, "Requires exactly three color values");
}
-
- int i;
- for (i = 0; i < 3; i++) {
- if (!*colors[i]) {
- *(colors[i]) = malloc(10);
- if (!*(colors[i])) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color");
- }
+ for (size_t i = 0; i < 3; i++) {
+ if (!*colors[i] && !(*(colors[i]) = malloc(10))) {
+ return NULL;
}
error = add_color(cmd_name, *(colors[i]), argv[i]);
if (error) {
return error;
}
}
-
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
@@ -50,12 +41,10 @@ struct cmd_results *bar_cmd_colors(int argc, char **argv) {
if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (strcmp("{", argv[0]) != 0) {
return cmd_results_new(CMD_INVALID, "colors",
"Expected '{' at the start of colors config definition.");
}
-
return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL);
}
@@ -69,11 +58,13 @@ struct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) {
}
struct cmd_results *bar_colors_cmd_background(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.background), "background", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.background),
+ "background", argc, argv);
}
struct cmd_results *bar_colors_cmd_focused_background(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.focused_background), "focused_background", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.focused_background),
+ "focused_background", argc, argv);
}
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) {
}
struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.separator), "separator", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.separator),
+ "separator", argc, argv);
}
struct cmd_results *bar_colors_cmd_focused_separator(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.focused_separator),
+ "focused_separator", argc, argv);
}
struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.statusline), "statusline", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.statusline),
+ "statusline", argc, argv);
}
struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.focused_separator),
+ "focused_separator", argc, argv);
}
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 @@
#include "log.h"
struct cmd_results *bar_cmd_context_button(int argc, char **argv) {
- const char *cmd_name = "context_button";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- // User should be able to prefix with 0x or whatever they want
- config->current_bar->context_button = strtoul(argv[0], NULL, 0);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "context_button", "TODO TRAY");
}
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 @@
+#define _POSIX_C_SOURCE 200809L
#include <string.h>
#include "sway/commands.h"
#include "log.h"
@@ -8,19 +9,13 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) {
if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) {
return error;
}
-
if (!config->current_bar) {
return cmd_results_new(CMD_FAILURE, "font", "No bar defined.");
}
-
char *font = join_args(argv, argc);
free(config->current_bar->font);
- if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) {
- config->current_bar->font = font;
- } else {
- config->current_bar->font = font;
- }
-
- sway_log(L_DEBUG, "Settings font '%s' for bar: %s", config->current_bar->font, config->current_bar->id);
+ config->current_bar->font = strdup(font);
+ wlr_log(L_DEBUG, "Settings font '%s' for bar: %s",
+ config->current_bar->font, config->current_bar->id);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "height", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
int height = atoi(argv[0]);
if (height < 0) {
return cmd_results_new(CMD_INVALID, "height",
"Invalid height value: %s", argv[0]);
}
-
config->current_bar->height = height;
- sway_log(L_DEBUG, "Setting bar height to %d on bar: %s", height, config->current_bar->id);
+ wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s",
+ height, config->current_bar->id);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include "sway/ipc-server.h"
#include "log.h"
-static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, const char *hidden_state) {
+static struct cmd_results *bar_set_hidden_state(struct bar_config *bar,
+ const char *hidden_state) {
char *old_state = bar->hidden_state;
if (strcasecmp("toggle", hidden_state) == 0 && !config->reading) {
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
} else if (strcasecmp("show", hidden_state) == 0) {
bar->hidden_state = strdup("show");
} else {
- return cmd_results_new(CMD_INVALID, "hidden_state", "Invalid value %s", hidden_state);
+ return cmd_results_new(CMD_INVALID, "hidden_state",
+ "Invalid value %s", hidden_state);
}
-
if (strcmp(old_state, bar->hidden_state) != 0) {
if (!config->reading) {
ipc_event_barconfig_update(bar);
}
- sway_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", bar->hidden_state, bar->id);
+ wlr_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s",
+ bar->hidden_state, bar->id);
}
-
// free old mode
free(old_state);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -42,13 +43,12 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
if ((error = checkarg(argc, "hidden_state", EXPECTED_LESS_THAN, 3))) {
return error;
}
-
if (config->reading && argc > 1) {
- return cmd_results_new(CMD_INVALID, "hidden_state", "Unexpected value %s in config mode", argv[1]);
+ return cmd_results_new(CMD_INVALID, "hidden_state",
+ "Unexpected value %s in config mode", argv[1]);
}
const char *state = argv[0];
-
if (config->reading) {
return bar_set_hidden_state(config->current_bar, state);
}
@@ -57,10 +57,8 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
if (argc == 2) {
id = argv[1];
}
-
- int i;
struct bar_config *bar;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
bar = config->bars->items[i];
if (id && strcmp(id, bar->id) == 0) {
return bar_set_hidden_state(bar, state);
@@ -71,9 +69,5 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
return error;
}
}
-
- // active bar modifiers might have changed.
- update_active_bar_modifiers();
-
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include "sway/commands.h"
struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) {
- const char *cmd_name = "tray_output";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- config->current_bar->icon_theme = strdup(argv[0]);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "icon_theme", "TODO TRAY");
}
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) {
const char *name = argv[0];
const char *oldname = config->current_bar->id;
-
// check if id is used by a previously defined bar
- int i;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
struct bar_config *find = config->bars->items[i];
if (strcmp(name, find->id) == 0 && config->current_bar != find) {
return cmd_results_new(CMD_FAILURE, "id",
@@ -23,11 +21,10 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) {
}
}
- sway_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name);
+ wlr_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name);
// free old bar id
free(config->current_bar->id);
-
config->current_bar->id = strdup(name);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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
if (strcmp(old_mode, bar->mode) != 0) {
if (!config->reading) {
ipc_event_barconfig_update(bar);
-
- // active bar modifiers might have changed.
- update_active_bar_modifiers();
}
- sway_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id);
+ wlr_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id);
}
// free old mode
@@ -47,13 +44,12 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
if ((error = checkarg(argc, "mode", EXPECTED_LESS_THAN, 3))) {
return error;
}
-
if (config->reading && argc > 1) {
- return cmd_results_new(CMD_INVALID, "mode", "Unexpected value %s in config mode", argv[1]);
+ return cmd_results_new(CMD_INVALID,
+ "mode", "Unexpected value %s in config mode", argv[1]);
}
const char *mode = argv[0];
-
if (config->reading) {
return bar_set_mode(config->current_bar, mode);
}
@@ -63,19 +59,16 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
id = argv[1];
}
- int i;
struct bar_config *bar;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
bar = config->bars->items[i];
if (id && strcmp(id, bar->id) == 0) {
return bar_set_mode(bar, mode);
}
-
error = bar_set_mode(bar, mode);
if (error) {
return error;
}
}
-
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
}
uint32_t mod = 0;
-
list_t *split = split_string(argv[0], "+");
for (int i = 0; i < split->length; ++i) {
uint32_t tmp_mod;
@@ -24,12 +23,13 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) {
continue;
} else {
free_flat_list(split);
- return cmd_results_new(CMD_INVALID, "modifier", "Unknown modifier '%s'", split->items[i]);
+ return cmd_results_new(CMD_INVALID, "modifier",
+ "Unknown modifier '%s'", split->items[i]);
}
}
free_flat_list(split);
-
config->current_bar->modifier = mod;
- sway_log(L_DEBUG, "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);
+ wlr_log(L_DEBUG,
+ "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#define _XOPEN_SOURCE 500
+#include <stdbool.h>
#include <string.h>
#include "sway/commands.h"
#include "list.h"
@@ -9,7 +10,6 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {
if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
return cmd_results_new(CMD_FAILURE, "output", "No bar defined.");
}
@@ -21,21 +21,20 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {
config->current_bar->outputs = outputs;
}
- int i;
- int add_output = 1;
+ bool add_output = true;
if (strcmp("*", output) == 0) {
// remove all previous defined outputs and replace with '*'
- for (i = 0; i < outputs->length; ++i) {
+ for (int i = 0; i < outputs->length; ++i) {
free(outputs->items[i]);
list_del(outputs, i);
}
} else {
// only add output if not already defined with either the same
// name or as '*'
- for (i = 0; i < outputs->length; ++i) {
+ for (int i = 0; i < outputs->length; ++i) {
const char *find = outputs->items[i];
if (strcmp("*", find) == 0 || strcmp(output, find) == 0) {
- add_output = 0;
+ add_output = false;
break;
}
}
@@ -43,8 +42,8 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {
if (add_output) {
list_add(outputs, strdup(output));
- sway_log(L_DEBUG, "Adding bar: '%s' to output '%s'", config->current_bar->id, output);
+ wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'",
+ config->current_bar->id, output);
}
-
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined.");
}
-
if (strcasecmp("enabled", argv[0]) == 0) {
config->current_bar->pango_markup = true;
- sway_log(L_DEBUG, "Enabling pango markup for bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Enabling pango markup for bar: %s",
+ config->current_bar->id);
} else if (strcasecmp("disabled", argv[0]) == 0) {
config->current_bar->pango_markup = false;
- sway_log(L_DEBUG, "Disabling pango markup for bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Disabling pango markup for bar: %s",
+ config->current_bar->id);
} else {
- error = cmd_results_new(CMD_INVALID, "pango_markup", "Invalid value %s", argv[0]);
+ error = cmd_results_new(CMD_INVALID, "pango_markup",
+ "Invalid value %s", argv[0]);
return error;
}
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 @@
+#define _POSIX_C_SOURCE 200809L
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
@@ -8,26 +9,18 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) {
if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
return cmd_results_new(CMD_FAILURE, "position", "No bar defined.");
}
-
- if (strcasecmp("top", argv[0]) == 0) {
- config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_TOP;
- } else if (strcasecmp("bottom", argv[0]) == 0) {
- config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
- } else if (strcasecmp("left", argv[0]) == 0) {
- sway_log(L_INFO, "Warning: swaybar currently only supports top and bottom positioning. YMMV");
- config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_LEFT;
- } else if (strcasecmp("right", argv[0]) == 0) {
- sway_log(L_INFO, "Warning: swaybar currently only supports top and bottom positioning. YMMV");
- config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_RIGHT;
- } else {
- error = cmd_results_new(CMD_INVALID, "position", "Invalid value %s", argv[0]);
- return error;
+ char *valid[] = { "top", "bottom", "left", "right" };
+ for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) {
+ if (strcasecmp(valid[i], argv[0]) == 0) {
+ wlr_log(L_DEBUG, "Setting bar position '%s' for bar: %s",
+ argv[0], config->current_bar->id);
+ config->current_bar->position = strdup(argv[0]);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ }
}
-
- sway_log(L_DEBUG, "Setting bar position '%s' for bar: %s", argv[0], config->current_bar->id);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ return cmd_results_new(CMD_INVALID,
+ "position", "Invalid value %s", argv[0]);
}
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 @@
#include "log.h"
struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) {
- const char *cmd_name = "secondary_button";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- // User should be able to prefix with 0x or whatever they want
- config->current_bar->secondary_button = strtoul(argv[0], NULL, 0);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "secondary_button", "TODO TRAY");
}
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) {
if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "separator_symbol", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "separator_symbol", "No bar defined.");
}
-
free(config->current_bar->separator_symbol);
config->current_bar->separator_symbol = strdup(argv[0]);
- sway_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", config->current_bar->separator_symbol, config->current_bar->id);
-
+ wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s",
+ config->current_bar->separator_symbol, config->current_bar->id);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "status_command", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "status_command", "No bar defined.");
}
-
free(config->current_bar->status_command);
config->current_bar->status_command = join_args(argv, argc);
- sway_log(L_DEBUG, "Feeding bar with status command: %s", config->current_bar->status_command);
-
+ wlr_log(L_DEBUG, "Feeding bar with status command: %s",
+ config->current_bar->status_command);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {
struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) {
+ if ((error = checkarg(argc,
+ "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "strip_workspace_numbers", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "strip_workspace_numbers", "No bar defined.");
}
-
if (strcasecmp("yes", argv[0]) == 0) {
config->current_bar->strip_workspace_numbers = true;
- sway_log(L_DEBUG, "Stripping workspace numbers on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s",
+ config->current_bar->id);
} else if (strcasecmp("no", argv[0]) == 0) {
config->current_bar->strip_workspace_numbers = false;
- sway_log(L_DEBUG, "Enabling workspace numbers on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s",
+ config->current_bar->id);
} else {
- error = cmd_results_new(CMD_INVALID, "strip_workspace_numbers", "Invalid value %s", argv[0]);
- return error;
+ return cmd_results_new(CMD_INVALID,
+ "strip_workspace_numbers", "Invalid value %s", argv[0]);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "swaybar_command", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "swaybar_command", "No bar defined.");
}
-
free(config->current_bar->swaybar_command);
config->current_bar->swaybar_command = join_args(argv, argc);
- sway_log(L_DEBUG, "Using custom swaybar command: %s", config->current_bar->swaybar_command);
-
+ wlr_log(L_DEBUG, "Using custom swaybar command: %s",
+ config->current_bar->swaybar_command);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include "sway/commands.h"
struct cmd_results *bar_cmd_tray_output(int argc, char **argv) {
- const char *cmd_name = "tray_output";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- if (strcmp(argv[0], "all") == 0) {
- // Default behaviour
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- config->current_bar->tray_output = strdup(argv[0]);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "tray_output", "TODO TRAY");
}
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 @@
#include "log.h"
struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) {
- const char *cmd_name = "tray_padding";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command"
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- if (argc == 1 || (argc == 2 && strcasecmp("px", argv[1]) == 0)) {
- char *inv;
- uint32_t padding = strtoul(argv[0], &inv, 10);
- if (*inv == '\0' || strcasecmp(inv, "px") == 0) {
- config->current_bar->tray_padding = padding;
- sway_log(L_DEBUG, "Enabling tray padding of %d px on bar: %s", padding, config->current_bar->id);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- }
- return cmd_results_new(CMD_FAILURE, cmd_name,
- "Expected 'tray_padding <padding>[px]'");
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "tray_padding", "TODO TRAY");
}
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) {
if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "workspace_buttons", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "workspace_buttons", "No bar defined.");
}
-
if (strcasecmp("yes", argv[0]) == 0) {
config->current_bar->workspace_buttons = true;
- sway_log(L_DEBUG, "Enabling workspace buttons on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s",
+ config->current_bar->id);
} else if (strcasecmp("no", argv[0]) == 0) {
config->current_bar->workspace_buttons = false;
- sway_log(L_DEBUG, "Disabling workspace buttons on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s",
+ config->current_bar->id);
} else {
- error = cmd_results_new(CMD_INVALID, "workspace_buttons", "Invalid value %s", argv[0]);
- return error;
+ return cmd_results_new(CMD_INVALID, "workspace_buttons",
+ "Invalid value %s", argv[0]);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined.");
}
-
if (strcasecmp("yes", argv[0]) == 0) {
config->current_bar->wrap_scroll = true;
- sway_log(L_DEBUG, "Enabling wrap scroll on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s",
+ config->current_bar->id);
} else if (strcasecmp("no", argv[0]) == 0) {
config->current_bar->wrap_scroll = false;
- sway_log(L_DEBUG, "Disabling wrap scroll on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s",
+ config->current_bar->id);
} else {
- error = cmd_results_new(CMD_INVALID, "wrap_scroll", "Invalid value %s", argv[0]);
- return error;
+ return cmd_results_new(CMD_INVALID,
+ "wrap_scroll", "Invalid value %s", argv[0]);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-names.h>
#include <strings.h>
#include "sway/commands.h"
#include "sway/config.h"
-#include "sway/input_state.h"
#include "list.h"
#include "log.h"
#include "stringop.h"
@@ -11,13 +15,67 @@
int binding_order = 0;
+void free_sway_binding(struct sway_binding *binding) {
+ if (!binding) {
+ return;
+ }
+
+ if (binding->keys) {
+ free_flat_list(binding->keys);
+ }
+ free(binding->command);
+ free(binding);
+}
+
+/**
+ * Returns true if the bindings have the same key and modifier combinations.
+ * Note that keyboard layout is not considered, so the bindings might actually
+ * not be equivalent on some layouts.
+ */
+bool binding_key_compare(struct sway_binding *binding_a,
+ struct sway_binding *binding_b) {
+ if (binding_a->release != binding_b->release) {
+ return false;
+ }
+
+ if (binding_a->bindcode != binding_b->bindcode) {
+ return false;
+ }
+
+ if (binding_a->modifiers ^ binding_b->modifiers) {
+ return false;
+ }
+
+ if (binding_a->keys->length != binding_b->keys->length) {
+ return false;
+ }
+
+ int keys_len = binding_a->keys->length;
+ for (int i = 0; i < keys_len; ++i) {
+ uint32_t key_a = *(uint32_t*)binding_a->keys->items[i];
+ bool found = false;
+ for (int j = 0; j < keys_len; ++j) {
+ uint32_t key_b = *(uint32_t*)binding_b->keys->items[j];
+ if (key_b == key_a) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
struct cmd_results *cmd_bindsym(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
return error;
}
- struct sway_binding *binding = malloc(sizeof(struct sway_binding));
+ struct sway_binding *binding = calloc(1, sizeof(struct sway_binding));
if (!binding) {
return cmd_results_new(CMD_FAILURE, "bindsym",
"Unable to allocate binding");
@@ -58,7 +116,7 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
// Check for mouse binding
if (strncasecmp(split->items[i], "button", strlen("button")) == 0 &&
strlen(split->items[i]) == strlen("button0")) {
- sym = ((char *)split->items[i])[strlen("button")] - '1' + M_LEFT_CLICK;
+ sym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT;
}
if (!sym) {
struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym",
@@ -67,7 +125,7 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
free_flat_list(split);
return ret;
}
- xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
+ xkb_keysym_t *key = calloc(1, sizeof(xkb_keysym_t));
if (!key) {
free_sway_binding(binding);
free_flat_list(split);
@@ -78,20 +136,29 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
list_add(binding->keys, key);
}
free_flat_list(split);
+ binding->order = binding_order++;
- struct sway_mode *mode = config->current_mode;
- int i = list_seq_find(mode->bindings, sway_binding_cmp_keys, binding);
- if (i > -1) {
- sway_log(L_DEBUG, "bindsym - '%s' already exists, overwriting", argv[0]);
- struct sway_binding *dup = mode->bindings->items[i];
- free_sway_binding(dup);
- list_del(mode->bindings, i);
+ list_t *mode_bindings = config->current_mode->keysym_bindings;
+
+ // overwrite the binding if it already exists
+ bool overwritten = false;
+ for (int i = 0; i < mode_bindings->length; ++i) {
+ struct sway_binding *config_binding = mode_bindings->items[i];
+ if (binding_key_compare(binding, config_binding)) {
+ wlr_log(L_DEBUG, "overwriting old binding with command '%s'",
+ config_binding->command);
+ free_sway_binding(config_binding);
+ mode_bindings->items[i] = binding;
+ overwritten = true;
+ }
}
- binding->order = binding_order++;
- list_add(mode->bindings, binding);
- list_qsort(mode->bindings, sway_binding_cmp_qsort);
- sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command);
+ if (!overwritten) {
+ list_add(mode_bindings, binding);
+ }
+
+ wlr_log(L_DEBUG, "bindsym - Bound %s to command %s",
+ argv[0], binding->command);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
@@ -101,7 +168,7 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
return error;
}
- struct sway_binding *binding = malloc(sizeof(struct sway_binding));
+ struct sway_binding *binding = calloc(1, sizeof(struct sway_binding));
if (!binding) {
return cmd_results_new(CMD_FAILURE, "bindsym",
"Unable to allocate binding");
@@ -138,33 +205,41 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
// parse keycode
xkb_keycode_t keycode = (int)strtol(split->items[i], NULL, 10);
if (!xkb_keycode_is_legal_ext(keycode)) {
- error = cmd_results_new(CMD_INVALID, "bindcode", "Invalid keycode '%s'", (char *)split->items[i]);
+ error =
+ cmd_results_new(CMD_INVALID, "bindcode",
+ "Invalid keycode '%s'", (char *)split->items[i]);
free_sway_binding(binding);
list_free(split);
return error;
}
- xkb_keycode_t *key = malloc(sizeof(xkb_keycode_t));
+ xkb_keycode_t *key = calloc(1, sizeof(xkb_keycode_t));
*key = keycode - 8;
list_add(binding->keys, key);
}
free_flat_list(split);
- struct sway_mode *mode = config->current_mode;
- int i = list_seq_find(mode->bindings, sway_binding_cmp_keys, binding);
- if (i > -1) {
- struct sway_binding *dup = mode->bindings->items[i];
- if (dup->bindcode) {
- sway_log(L_DEBUG, "bindcode - '%s' already exists, overwriting", argv[0]);
- } else {
- sway_log(L_DEBUG, "bindcode - '%s' already exists as bindsym, overwriting", argv[0]);
+ binding->order = binding_order++;
+
+ list_t *mode_bindings = config->current_mode->keycode_bindings;
+
+ // overwrite the binding if it already exists
+ bool overwritten = false;
+ for (int i = 0; i < mode_bindings->length; ++i) {
+ struct sway_binding *config_binding = mode_bindings->items[i];
+ if (binding_key_compare(binding, config_binding)) {
+ wlr_log(L_DEBUG, "overwriting old binding with command '%s'",
+ config_binding->command);
+ free_sway_binding(config_binding);
+ mode_bindings->items[i] = binding;
+ overwritten = true;
}
- free_sway_binding(dup);
- list_del(mode->bindings, i);
}
- binding->order = binding_order++;
- list_add(mode->bindings, binding);
- list_qsort(mode->bindings, sway_binding_cmp_qsort);
- sway_log(L_DEBUG, "bindcode - Bound %s to command %s", argv[0], binding->command);
+ if (!overwritten) {
+ list_add(mode_bindings, binding);
+ }
+
+ wlr_log(L_DEBUG, "bindcode - Bound %s to command %s",
+ argv[0], binding->command);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-
-struct cmd_results *cmd_border(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (!config->active) {
- return cmd_results_new(CMD_FAILURE, "border", "Can only be used when sway is running.");
- }
- if ((error = checkarg(argc, "border", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- if (argc > 2) {
- return cmd_results_new(CMD_INVALID, "border",
- "Expected 'border <normal|pixel|none|toggle> [<n>]");
- }
-
- swayc_t *view = current_container;
- enum swayc_border_types border = view->border_type;
- int thickness = view->border_thickness;
-
- if (strcasecmp(argv[0], "none") == 0) {
- border = B_NONE;
- } else if (strcasecmp(argv[0], "normal") == 0) {
- border = B_NORMAL;
- } else if (strcasecmp(argv[0], "pixel") == 0) {
- border = B_PIXEL;
- } else if (strcasecmp(argv[0], "toggle") == 0) {
- switch (border) {
- case B_NONE:
- border = B_PIXEL;
- break;
- case B_NORMAL:
- border = B_NONE;
- break;
- case B_PIXEL:
- border = B_NORMAL;
- break;
- }
- } else {
- return cmd_results_new(CMD_INVALID, "border",
- "Expected 'border <normal|pixel|none|toggle>");
- }
-
- if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
- thickness = (int)strtol(argv[1], NULL, 10);
- if (errno == ERANGE || thickness < 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "border", "Number is out of range.");
- }
- }
-
- if (view) {
- view->border_type = border;
- view->border_thickness = thickness;
- update_geometry(view);
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdlib.h>
-#include <string.h>
-#include "sway/commands.h"
-
-static struct cmd_results *parse_border_color(struct border_colors *border_colors, const char *cmd_name, int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (argc < 3 || argc > 5) {
- return cmd_results_new(CMD_INVALID, cmd_name, "Requires between three and five color values");
- }
-
- uint32_t *colors[5] = {
- &border_colors->border,
- &border_colors->background,
- &border_colors->text,
- &border_colors->indicator,
- &border_colors->child_border
- };
- int i;
- for (i = 0; i < argc; i++) {
- char buffer[10];
- error = add_color(cmd_name, buffer, argv[i]);
- if (error) {
- return error;
- }
- *colors[i] = strtoul(buffer + 1, NULL, 16);
- }
-
- if (argc < 5) {
- border_colors->child_border = border_colors->background;
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
-
-struct cmd_results *cmd_client_focused(int argc, char **argv) {
- return parse_border_color(&config->border_colors.focused, "client.focused", argc, argv);
-}
-
-struct cmd_results *cmd_client_focused_inactive(int argc, char **argv) {
- return parse_border_color(&config->border_colors.focused_inactive, "client.focused_inactive", argc, argv);
-}
-
-struct cmd_results *cmd_client_unfocused(int argc, char **argv) {
- return parse_border_color(&config->border_colors.unfocused, "client.unfocused", argc, argv);
-}
-
-struct cmd_results *cmd_client_urgent(int argc, char **argv) {
- return parse_border_color(&config->border_colors.urgent, "client.urgent", argc, argv);
-}
-
-struct cmd_results *cmd_client_placeholder(int argc, char **argv) {
- return parse_border_color(&config->border_colors.placeholder, "client.placeholder", argc, argv);
-}
-
-struct cmd_results *cmd_client_background(int argc, char **argv) {
- char buffer[10];
- struct cmd_results *error = NULL;
- uint32_t background;
-
- if (argc != 1) {
- return cmd_results_new(CMD_INVALID, "client.background", "Requires exactly one color value");
- }
-
- error = add_color("client.background", buffer, argv[0]);
- if (error) {
- return error;
- }
-
- background = strtoul(buffer+1, NULL, 16);
- config->border_colors.background = background;
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <wlc/wlc.h>
-#include <unistd.h>
-#include <string.h>
-#include "sway/commands.h"
-#include "stringop.h"
-
-static void send_clipboard(void *data, const char *type, int fd) {
- if (strcmp(type, "text/plain") != 0
- && strcmp(type, "text/plain;charset=utf-8") != 0) {
- close(fd);
- return;
- }
-
- const char *str = data;
- write(fd, str, strlen(str));
- close(fd);
-}
-
-struct cmd_results *cmd_clipboard(int argc, char **argv) {
- static char *current_data = NULL;
-
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "clipboard", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- static const char *types[2] = {
- "text/plain",
- "text/plain;charset=utf-8"
- };
-
- char *str = join_args(argv, argc);
- wlc_set_selection(str, types, 2, &send_clipboard);
-
- free(current_data);
- current_data = str;
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdbool.h>
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/config.h"
-#include "list.h"
-#include "log.h"
-
-struct cmd_results *cmd_commands(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "commands", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
- if ((error = check_security_config())) {
- return error;
- }
-
- if (strcmp(argv[0], "{") != 0) {
- return cmd_results_new(CMD_FAILURE, "commands", "Expected block declaration");
- }
-
- if (!config->reading) {
- return cmd_results_new(CMD_FAILURE, "commands", "Can only be used in config file.");
- }
-
- return cmd_results_new(CMD_BLOCK_COMMANDS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "log.h"
-
-struct cmd_results *cmd_debuglog(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "debuglog", EXPECTED_EQUAL_TO, 1))) {
- return error;
- } else if (strcasecmp(argv[0], "toggle") == 0) {
- if (config->reading) {
- return cmd_results_new(CMD_FAILURE, "debuglog toggle", "Can't be used in config file.");
- }
- if (toggle_debug_logging()) {
- sway_log(L_DEBUG, "Debuglog turned on.");
- }
- } else if (strcasecmp(argv[0], "on") == 0) {
- set_log_level(L_DEBUG);
- sway_log(L_DEBUG, "Debuglog turned on.");
- } else if (strcasecmp(argv[0], "off") == 0) {
- reset_log_level();
- } else {
- return cmd_results_new(CMD_FAILURE, "debuglog", "Expected 'debuglog on|off|toggle'");
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
-
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 @@
-#include <errno.h>
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-
-struct cmd_results *cmd_default_border(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "default_border", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- if (argc > 2) {
- return cmd_results_new(CMD_INVALID, "default_border",
- "Expected 'default_border <normal|none|pixel> [<n>]");
- }
-
- enum swayc_border_types border = config->border;
- int thickness = config->border_thickness;
-
- if (strcasecmp(argv[0], "none") == 0) {
- border = B_NONE;
- } else if (strcasecmp(argv[0], "normal") == 0) {
- border = B_NORMAL;
- } else if (strcasecmp(argv[0], "pixel") == 0) {
- border = B_PIXEL;
- } else {
- return cmd_results_new(CMD_INVALID, "default_border",
- "Expected 'default_border <normal|none|pixel> [<n>]");
- }
-
- if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
- thickness = (int)strtol(argv[1], NULL, 10);
- if (errno == ERANGE || thickness < 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "default_border", "Number is out out of range.");
- }
- }
-
- config->border = border;
- config->border_thickness = thickness;
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <errno.h>
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-
-struct cmd_results *cmd_default_floating_border(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "default_floating_border", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- if (argc > 2) {
- return cmd_results_new(CMD_INVALID, "default_floating_border",
- "Expected 'default_floating_border <normal|none|pixel> [<n>]");
- }
-
- enum swayc_border_types border = config->floating_border;
- int thickness = config->floating_border_thickness;
-
- if (strcasecmp(argv[0], "none") == 0) {
- border = B_NONE;
- } else if (strcasecmp(argv[0], "normal") == 0) {
- border = B_NORMAL;
- } else if (strcasecmp(argv[0], "pixel") == 0) {
- border = B_PIXEL;
- } else {
- return cmd_results_new(CMD_INVALID, "default_floating_border",
- "Expected 'default_floating_border <normal|none|pixel> [<n>]");
- }
-
- if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
- thickness = (int)strtol(argv[1], NULL, 10);
- if (errno == ERANGE || thickness < 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "default_floating_border",
- "Number is out out of range.");
- }
- }
-
- config->floating_border = border;
- config->floating_border_thickness = thickness;
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
#include <strings.h>
#include "sway/commands.h"
-struct cmd_results *cmd_orientation(int argc, char **argv) {
+struct cmd_results *cmd_default_orientation(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (!config->reading) return cmd_results_new(CMD_FAILURE, "orientation", "Can only be used in config file.");
- if ((error = checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1))) {
+ if ((error = checkarg(argc, "default_orientation", EXPECTED_EQUAL_TO, 1))) {
return error;
}
if (strcasecmp(argv[0], "horizontal") == 0) {
@@ -15,7 +14,8 @@ struct cmd_results *cmd_orientation(int argc, char **argv) {
} else if (strcasecmp(argv[0], "auto") == 0) {
// Do nothing
} else {
- return cmd_results_new(CMD_INVALID, "orientation", "Expected 'orientation <horizontal|vertical|auto>'");
+ return cmd_results_new(CMD_INVALID, "default_orientation",
+ "Expected 'orientation <horizontal|vertical|auto>'");
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include "sway/commands.h"
+#include "sway/config.h"
#include "log.h"
#include "stringop.h"
@@ -7,10 +8,9 @@ struct cmd_results *cmd_exec(int argc, char **argv) {
if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL);
if (config->reloading) {
char *args = join_args(argv, argc);
- sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args);
+ wlr_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args);
free(args);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
return cmd_exec_always(argc, argv);
}
-
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 @@
#define _XOPEN_SOURCE 500
+#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "sway/commands.h"
#include "sway/config.h"
+#include "sway/tree/container.h"
+#include "sway/tree/workspace.h"
#include "log.h"
#include "stringop.h"
@@ -16,7 +20,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
char *tmp = NULL;
if (strcmp((char*)*argv, "--no-startup-id") == 0) {
- sway_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored.");
+ wlr_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored.");
if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) {
return error;
}
@@ -31,11 +35,11 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
strncpy(cmd, tmp, sizeof(cmd));
cmd[sizeof(cmd) - 1] = 0;
free(tmp);
- sway_log(L_DEBUG, "Executing %s", cmd);
+ wlr_log(L_DEBUG, "Executing %s", cmd);
int fd[2];
if (pipe(fd) != 0) {
- sway_log(L_ERROR, "Unable to create pipe for fork");
+ wlr_log(L_ERROR, "Unable to create pipe for fork");
}
pid_t pid;
@@ -49,7 +53,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
setsid();
if ((*child = fork()) == 0) {
execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL);
- /* Not reached */
+ // Not reached
}
close(fd[0]);
ssize_t s = 0;
@@ -70,13 +74,9 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
close(fd[0]);
// cleanup child process
wait(0);
- swayc_t *ws = swayc_active_workspace();
- if (*child > 0 && ws) {
- sway_log(L_DEBUG, "Child process created with pid %d for workspace %s", *child, ws->name);
- struct pid_workspace *pw = malloc(sizeof(struct pid_workspace));
- pw->pid = child;
- pw->workspace = strdup(ws->name);
- pid_workspace_add(pw);
+ if (*child > 0) {
+ wlr_log(L_DEBUG, "Child process created with pid %d", *child);
+ // TODO: add PID to active workspace
} else {
free(child);
}
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 @@
+#include <stddef.h>
#include "sway/commands.h"
-#include "sway/container.h"
+#include "sway/config.h"
void sway_terminate(int exit_code);
struct cmd_results *cmd_exit(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "exit", "Can't be used in config file.");
if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) {
return error;
}
- // Close all views
- close_views(&root_container);
- sway_terminate(EXIT_SUCCESS);
+ sway_terminate(0);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
-
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/ipc-server.h"
-#include "sway/layout.h"
-#include "list.h"
-#include "log.h"
-
-struct cmd_results *cmd_floating(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "floating", "Can't be used in config file.");
- if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
- swayc_t *view = current_container;
- bool wants_floating;
- if (strcasecmp(argv[0], "enable") == 0) {
- wants_floating = true;
- } else if (strcasecmp(argv[0], "disable") == 0) {
- wants_floating = false;
- } else if (strcasecmp(argv[0], "toggle") == 0) {
- wants_floating = !view->is_floating;
- } else {
- return cmd_results_new(CMD_FAILURE, "floating",
- "Expected 'floating <enable|disable|toggle>");
- }
-
- // Prevent running floating commands on things like workspaces
- if (view->type != C_VIEW) {
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
-
- // Change from nonfloating to floating
- if (!view->is_floating && wants_floating) {
- // Remove view from its current location
- destroy_container(remove_child(view));
-
- // and move it into workspace floating
- add_floating(swayc_active_workspace(), view);
- view->x = (swayc_active_workspace()->width - view->width)/2;
- view->y = (swayc_active_workspace()->height - view->height)/2;
- if (view->desired_width != -1) {
- view->width = view->desired_width;
- }
- if (view->desired_height != -1) {
- view->height = view->desired_height;
- }
- arrange_windows(swayc_active_workspace(), -1, -1);
-
- } else if (view->is_floating && !wants_floating) {
- // Delete the view from the floating list and unset its is_floating flag
- remove_child(view);
- view->is_floating = false;
- // Get the properly focused container, and add in the view there
- swayc_t *focused = container_under_pointer();
- // If focused is null, it's because the currently focused container is a workspace
- if (focused == NULL || focused->is_floating) {
- focused = swayc_active_workspace();
- }
- set_focused_container(focused);
-
- sway_log(L_DEBUG, "Non-floating focused container is %p", focused);
-
- // Case of focused workspace, just create as child of it
- if (focused->type == C_WORKSPACE) {
- add_child(focused, view);
- }
- // Regular case, create as sibling of current container
- else {
- add_sibling(focused, view);
- }
- // Refocus on the view once its been put back into the layout
- view->width = view->height = 0;
- arrange_windows(swayc_active_workspace(), -1, -1);
- remove_view_from_scratchpad(view);
- ipc_event_window(view, "floating");
- }
- set_focused_container(view);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdlib.h>
-#include <string.h>
-#include "sway/commands.h"
-#include "log.h"
-
-struct cmd_results *cmd_floating_maximum_size(int argc, char **argv) {
- struct cmd_results *error = NULL;
- int32_t width;
- int32_t height;
- char *ptr;
-
- if ((error = checkarg(argc, "floating_maximum_size", EXPECTED_EQUAL_TO, 3))) {
- return error;
- }
- width = strtol(argv[0], &ptr, 10);
- height = strtol(argv[2], &ptr, 10);
-
- if (width < -1) {
- sway_log(L_DEBUG, "floating_maximum_size invalid width value: '%s'", argv[0]);
-
- } else {
- config->floating_maximum_width = width;
-
- }
-
- if (height < -1) {
- sway_log(L_DEBUG, "floating_maximum_size invalid height value: '%s'", argv[2]);
- }
- else {
- config->floating_maximum_height = height;
-
- }
-
- sway_log(L_DEBUG, "New floating_maximum_size: '%d' x '%d'", config->floating_maximum_width,
- config->floating_maximum_height);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdlib.h>
-#include <string.h>
-#include "sway/commands.h"
-#include "log.h"
-
-struct cmd_results *cmd_floating_minimum_size(int argc, char **argv) {
- struct cmd_results *error = NULL;
- int32_t width;
- int32_t height;
- char *ptr;
-
- if ((error = checkarg(argc, "floating_minimum_size", EXPECTED_EQUAL_TO, 3))) {
- return error;
- }
- width = strtol(argv[0], &ptr, 10);
- height = strtol(argv[2], &ptr, 10);
-
- if (width <= 0) {
- sway_log(L_DEBUG, "floating_minimum_size invalid width value: '%s'", argv[0]);
-
- } else {
- config->floating_minimum_width = width;
-
- }
-
- if (height <= 0) {
- sway_log(L_DEBUG, "floating_minimum_size invalid height value: '%s'", argv[2]);
- }
- else {
- config->floating_minimum_height = height;
-
- }
-
- sway_log(L_DEBUG, "New floating_minimum_size: '%d' x '%d'", config->floating_minimum_width,
- config->floating_minimum_height);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/input_state.h"
-#include "list.h"
-#include "log.h"
-#include "stringop.h"
-#include "util.h"
-
-struct cmd_results *cmd_floating_mod(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "floating_modifier", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
- int i;
- list_t *split = split_string(argv[0], "+");
- config->floating_mod = 0;
-
- // set modifier keys
- for (i = 0; i < split->length; ++i) {
- config->floating_mod |= get_modifier_mask_by_name(split->items[i]);
- }
- free_flat_list(split);
- if (!config->floating_mod) {
- error = cmd_results_new(CMD_INVALID, "floating_modifier", "Unknown keys %s", argv[0]);
- return error;
- }
-
- if (argc >= 2) {
- if (strcasecmp("inverse", argv[1]) == 0) {
- config->dragging_key = M_RIGHT_CLICK;
- config->resizing_key = M_LEFT_CLICK;
- } else if (strcasecmp("normal", argv[1]) == 0) {
- config->dragging_key = M_LEFT_CLICK;
- config->resizing_key = M_RIGHT_CLICK;
- } else {
- error = cmd_results_new(CMD_INVALID, "floating_modifier", "Invalid definition %s", argv[1]);
- return error;
- }
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "log.h"
-#include "stringop.h"
-
-struct cmd_results *cmd_floating_scroll(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "floating_scroll", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
- if (!strcasecmp("up", argv[0])) {
- free(config->floating_scroll_up_cmd);
- if (argc < 2) {
- config->floating_scroll_up_cmd = strdup("");
- } else {
- config->floating_scroll_up_cmd = join_args(argv + 1, argc - 1);
- }
- } else if (!strcasecmp("down", argv[0])) {
- free(config->floating_scroll_down_cmd);
- if (argc < 2) {
- config->floating_scroll_down_cmd = strdup("");
- } else {
- config->floating_scroll_down_cmd = join_args(argv + 1, argc - 1);
- }
- } else if (!strcasecmp("left", argv[0])) {
- free(config->floating_scroll_left_cmd);
- if (argc < 2) {
- config->floating_scroll_left_cmd = strdup("");
- } else {
- config->floating_scroll_left_cmd = join_args(argv + 1, argc - 1);
- }
- } else if (!strcasecmp("right", argv[0])) {
- free(config->floating_scroll_right_cmd);
- if (argc < 2) {
- config->floating_scroll_right_cmd = strdup("");
- } else {
- config->floating_scroll_right_cmd = join_args(argv + 1, argc - 1);
- }
- } else {
- error = cmd_results_new(CMD_INVALID, "floating_scroll", "Unknown command: '%s'", argv[0]);
- return error;
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
#include <strings.h>
-#include <wlc/wlc.h>
+#include <wlr/util/log.h>
+#include "log.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/tree/view.h"
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-#include "sway/input_state.h"
-#include "sway/output.h"
-#include "sway/workspace.h"
+
+static bool parse_movement_direction(const char *name,
+ enum movement_direction *out) {
+ if (strcasecmp(name, "left") == 0) {
+ *out = MOVE_LEFT;
+ } else if (strcasecmp(name, "right") == 0) {
+ *out = MOVE_RIGHT;
+ } else if (strcasecmp(name, "up") == 0) {
+ *out = MOVE_UP;
+ } else if (strcasecmp(name, "down") == 0) {
+ *out = MOVE_DOWN;
+ } else if (strcasecmp(name, "parent") == 0) {
+ *out = MOVE_PARENT;
+ } else if (strcasecmp(name, "child") == 0) {
+ *out = MOVE_CHILD;
+ } else {
+ return false;
+ }
+
+ return true;
+}
struct cmd_results *cmd_focus(int argc, char **argv) {
- if (config->reading) return cmd_results_new(CMD_FAILURE, "focus", "Can't be used in config file.");
- struct cmd_results *error = NULL;
- if (argc > 0 && strcasecmp(argv[0], "output") == 0) {
- swayc_t *output = NULL;
- struct wlc_point abs_pos;
- get_absolute_center_position(get_focused_container(&root_container), &abs_pos);
- if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 2))) {
- return error;
- } else if (!(output = output_by_name(argv[1], &abs_pos))) {
- return cmd_results_new(CMD_FAILURE, "focus output",
- "Can't find output with name/at direction '%s' @ (%i,%i)", argv[1], abs_pos.x, abs_pos.y);
- } else if (!workspace_switch(swayc_active_workspace_for(output))) {
- return cmd_results_new(CMD_FAILURE, "focus output",
- "Switching to workspace on output '%s' was blocked", argv[1]);
- } else if (config->mouse_warping) {
- swayc_t *focused = get_focused_view(output);
- if (focused && focused->type == C_VIEW) {
- center_pointer_on(focused);
- }
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- } else if (argc == 0) {
- set_focused_container(current_container);
+ struct sway_container *con = config->handler_context.current_container;
+ struct sway_seat *seat = config->handler_context.seat;
+ if (con->type < C_WORKSPACE) {
+ return cmd_results_new(CMD_FAILURE, "focus",
+ "Command 'focus' cannot be used above the workspace level");
+ }
+
+ if (argc == 0) {
+ seat_set_focus(seat, con);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- } else if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) {
- return error;
}
- static int floating_toggled_index = 0;
- static int tiled_toggled_index = 0;
- if (strcasecmp(argv[0], "left") == 0) {
- move_focus(MOVE_LEFT);
- } else if (strcasecmp(argv[0], "right") == 0) {
- move_focus(MOVE_RIGHT);
- } else if (strcasecmp(argv[0], "up") == 0) {
- move_focus(MOVE_UP);
- } else if (strcasecmp(argv[0], "down") == 0) {
- move_focus(MOVE_DOWN);
- } else if (strcasecmp(argv[0], "parent") == 0) {
- move_focus(MOVE_PARENT);
- } else if (strcasecmp(argv[0], "child") == 0) {
- move_focus(MOVE_CHILD);
- } else if (strcasecmp(argv[0], "next") == 0) {
- move_focus(MOVE_NEXT);
- } else if (strcasecmp(argv[0], "prev") == 0) {
- move_focus(MOVE_PREV);
- } else if (strcasecmp(argv[0], "mode_toggle") == 0) {
- int i;
- swayc_t *workspace = swayc_active_workspace();
- swayc_t *focused = get_focused_view(workspace);
- if (focused->is_floating) {
- if (workspace->children->length > 0) {
- for (i = 0;i < workspace->floating->length; i++) {
- if (workspace->floating->items[i] == focused) {
- floating_toggled_index = i;
- break;
- }
- }
- if (workspace->children->length > tiled_toggled_index) {
- set_focused_container(get_focused_view(workspace->children->items[tiled_toggled_index]));
- } else {
- set_focused_container(get_focused_view(workspace->children->items[0]));
- tiled_toggled_index = 0;
- }
- }
- } else {
- if (workspace->floating->length > 0) {
- for (i = 0;i < workspace->children->length; i++) {
- if (workspace->children->items[i] == focused) {
- tiled_toggled_index = i;
- break;
- }
- }
- if (workspace->floating->length > floating_toggled_index) {
- swayc_t *floating = workspace->floating->items[floating_toggled_index];
- set_focused_container(get_focused_view(floating));
- } else {
- swayc_t *floating = workspace->floating->items[workspace->floating->length - 1];
- set_focused_container(get_focused_view(floating));
- tiled_toggled_index = workspace->floating->length - 1;
- }
- }
- }
- } else {
+
+ // TODO mode_toggle
+ enum movement_direction direction = 0;
+ if (!parse_movement_direction(argv[0], &direction)) {
return cmd_results_new(CMD_INVALID, "focus",
"Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'");
}
+
+ struct sway_container *next_focus = container_get_in_direction(
+ con, seat, direction);
+ if (next_focus) {
+ seat_set_focus(seat, next_focus);
+ }
+
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
config->focus_follows_mouse = !strcasecmp(argv[0], "yes");
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
-#define _XOPEN_SOURCE 500
-#include <string.h>
-#include "sway/border.h"
-#include "sway/commands.h"
-#include "log.h"
-#include "stringop.h"
-
-struct cmd_results *cmd_font(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- char *font = join_args(argv, argc);
- free(config->font);
- if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) {
- config->font = strdup(font + 6);
- free(font);
- } else {
- config->font = font;
- }
-
- config->font_height = get_font_text_height(config->font);
-
- sway_log(L_DEBUG, "Settings font %s", config->font);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/criteria.h"
-#include "list.h"
-#include "log.h"
-#include "stringop.h"
-
-struct cmd_results *cmd_for_window(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "for_window", EXPECTED_AT_LEAST, 2))) {
- return error;
- }
- // add command to a criteria/command pair that is run against views when they appear.
- char *criteria = argv[0], *cmdlist = join_args(argv + 1, argc - 1);
-
- struct criteria *crit = malloc(sizeof(struct criteria));
- if (!crit) {
- return cmd_results_new(CMD_FAILURE, "for_window", "Unable to allocate criteria");
- }
- crit->crit_raw = strdup(criteria);
- crit->cmdlist = cmdlist;
- crit->tokens = create_list();
- char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
-
- if (err_str) {
- error = cmd_results_new(CMD_INVALID, "for_window", err_str);
- free(err_str);
- free_criteria(crit);
- } else if (crit->tokens->length == 0) {
- error = cmd_results_new(CMD_INVALID, "for_window", "Found no name/value pairs in criteria");
- free_criteria(crit);
- } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) {
- sway_log(L_DEBUG, "for_window: Duplicate, skipping.");
- free_criteria(crit);
- } else {
- sway_log(L_DEBUG, "for_window: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist);
- list_add(config->criteria, crit);
- }
- return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "force_focus_wrapping", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- config->force_focus_wrapping = !strcasecmp(argv[0], "yes");
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdbool.h>
-#include <string.h>
-#include <wlc/wlc.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-#include "sway/ipc-server.h"
-#include "sway/layout.h"
-
-struct cmd_results *cmd_fullscreen(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can only be used when sway is running.");
- if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) {
- return error;
- }
- swayc_t *container = current_container;
- if(container->type != C_VIEW){
- return cmd_results_new(CMD_INVALID, "fullscreen", "Only views can fullscreen");
- }
- swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE);
- bool current = swayc_is_fullscreen(container);
- wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current);
-
- if (container->is_floating) {
- if (current) {
- // set dimensions back to what they were before we fullscreened this
- container->x = container->cached_geometry.origin.x;
- container->y = container->cached_geometry.origin.y;
- container->width = container->cached_geometry.size.w;
- container->height = container->cached_geometry.size.h;
- } else {
- // cache dimensions so we can reset them after we "unfullscreen" this
- struct wlc_geometry geo = {
- .origin = {
- .x = container->x,
- .y = container->y
- },
- .size = {
- .w = container->width,
- .h = container->height
- }
- };
- container->cached_geometry = geo;
- }
- }
-
- // Resize workspace if going from fullscreen -> notfullscreen
- // otherwise just resize container
- if (!current) {
- arrange_windows(workspace, -1, -1);
- workspace->fullscreen = container;
- } else {
- arrange_windows(container, -1, -1);
- workspace->fullscreen = NULL;
- }
- ipc_event_window(container, "fullscreen_mode");
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <ctype.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-#include "sway/layout.h"
-
-struct cmd_results *cmd_gaps(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
- const char *expected_syntax =
- "Expected 'gaps edge_gaps <on|off|toggle>' or "
- "'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'";
- const char *amount_str = argv[0];
- // gaps amount
- if (argc >= 1 && isdigit(*amount_str)) {
- int amount = (int)strtol(amount_str, NULL, 10);
- if (errno == ERANGE) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
- }
- config->gaps_inner = config->gaps_outer = amount;
- arrange_windows(&root_container, -1, -1);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- // gaps inner|outer n
- else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) {
- int amount = (int)strtol(amount_str, NULL, 10);
- if (errno == ERANGE) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
- }
- const char *target_str = argv[0];
- if (strcasecmp(target_str, "inner") == 0) {
- config->gaps_inner = amount;
- } else if (strcasecmp(target_str, "outer") == 0) {
- config->gaps_outer = amount;
- }
- arrange_windows(&root_container, -1, -1);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- } else if (argc == 2 && strcasecmp(argv[0], "edge_gaps") == 0) {
- // gaps edge_gaps <on|off|toggle>
- if (strcasecmp(argv[1], "toggle") == 0) {
- if (config->reading) {
- return cmd_results_new(CMD_FAILURE, "gaps edge_gaps toggle",
- "Can't be used in config file.");
- }
- config->edge_gaps = !config->edge_gaps;
- } else {
- config->edge_gaps =
- (strcasecmp(argv[1], "yes") == 0 || strcasecmp(argv[1], "on") == 0);
- }
- arrange_windows(&root_container, -1, -1);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- // gaps inner|outer current|all set|plus|minus n
- if (argc < 4 || config->reading) {
- return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
- }
- // gaps inner|outer ...
- const char *inout_str = argv[0];
- enum {INNER, OUTER} inout;
- if (strcasecmp(inout_str, "inner") == 0) {
- inout = INNER;
- } else if (strcasecmp(inout_str, "outer") == 0) {
- inout = OUTER;
- } else {
- return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
- }
-
- // gaps ... current|all ...
- const char *target_str = argv[1];
- enum {CURRENT, WORKSPACE, ALL} target;
- if (strcasecmp(target_str, "current") == 0) {
- target = CURRENT;
- } else if (strcasecmp(target_str, "all") == 0) {
- target = ALL;
- } else if (strcasecmp(target_str, "workspace") == 0) {
- if (inout == OUTER) {
- target = CURRENT;
- } else {
- // Set gap for views in workspace
- target = WORKSPACE;
- }
- } else {
- return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
- }
-
- // gaps ... n
- amount_str = argv[3];
- int amount = (int)strtol(amount_str, NULL, 10);
- if (errno == ERANGE) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
- }
-
- // gaps ... set|plus|minus ...
- const char *method_str = argv[2];
- enum {SET, ADD} method;
- if (strcasecmp(method_str, "set") == 0) {
- method = SET;
- } else if (strcasecmp(method_str, "plus") == 0) {
- method = ADD;
- } else if (strcasecmp(method_str, "minus") == 0) {
- method = ADD;
- amount *= -1;
- } else {
- return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
- }
-
- if (target == CURRENT) {
- swayc_t *cont;
- if (inout == OUTER) {
- if ((cont = swayc_active_workspace()) == NULL) {
- return cmd_results_new(CMD_FAILURE, "gaps", "There's no active workspace.");
- }
- } else {
- if ((cont = get_focused_view(&root_container))->type != C_VIEW) {
- return cmd_results_new(CMD_FAILURE, "gaps", "Currently focused item is not a view.");
- }
- }
- cont->gaps = swayc_gap(cont);
- if (method == SET) {
- cont->gaps = amount;
- } else if ((cont->gaps += amount) < 0) {
- cont->gaps = 0;
- }
- arrange_windows(cont->parent, -1, -1);
- } else if (inout == OUTER) {
- //resize all workspace.
- int i,j;
- for (i = 0; i < root_container.children->length; ++i) {
- swayc_t *op = root_container.children->items[i];
- for (j = 0; j < op->children->length; ++j) {
- swayc_t *ws = op->children->items[j];
- if (method == SET) {
- ws->gaps = amount;
- } else if ((ws->gaps += amount) < 0) {
- ws->gaps = 0;
- }
- }
- }
- arrange_windows(&root_container, -1, -1);
- } else {
- // Resize gaps for all views in workspace
- swayc_t *top;
- if (target == WORKSPACE) {
- if ((top = swayc_active_workspace()) == NULL) {
- return cmd_results_new(CMD_FAILURE, "gaps", "There's currently no active workspace.");
- }
- } else {
- top = &root_container;
- }
- int top_gap = top->gaps;
- container_map(top, method == SET ? set_gaps : add_gaps, &amount);
- top->gaps = top_gap;
- arrange_windows(top, -1, -1);
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (strcasecmp(argv[0], "none") == 0) {
- config->hide_edge_borders = E_NONE;
- } else if (strcasecmp(argv[0], "vertical") == 0) {
- config->hide_edge_borders = E_VERTICAL;
- } else if (strcasecmp(argv[0], "horizontal") == 0) {
- config->hide_edge_borders = E_HORIZONTAL;
- } else if (strcasecmp(argv[0], "both") == 0) {
- config->hide_edge_borders = E_BOTH;
- } else if (strcasecmp(argv[0], "smart") == 0) {
- config->hide_edge_borders = E_SMART;
- } else {
- return cmd_results_new(CMD_INVALID, "hide_edge_borders",
- "Expected 'hide_edge_borders <none|vertical|horizontal|both>'");
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
#include "log.h"
struct cmd_results *cmd_input(int argc, char **argv) {
@@ -11,45 +11,73 @@ struct cmd_results *cmd_input(int argc, char **argv) {
}
if (config->reading && strcmp("{", argv[1]) == 0) {
- current_input_config = new_input_config(argv[0]);
- sway_log(L_DEBUG, "entering input block: %s", current_input_config->identifier);
+ free_input_config(config->handler_context.input_config);
+ config->handler_context.input_config = new_input_config(argv[0]);
+ if (!config->handler_context.input_config) {
+ return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config");
+ }
+ wlr_log(L_DEBUG, "entering input block: %s", argv[0]);
return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
}
- if (argc > 2) {
- int argc_new = argc-2;
- char **argv_new = argv+2;
-
- struct cmd_results *res;
- current_input_config = new_input_config(argv[0]);
- if (strcasecmp("accel_profile", argv[1]) == 0) {
- res = input_cmd_accel_profile(argc_new, argv_new);
- } else if (strcasecmp("click_method", argv[1]) == 0) {
- res = input_cmd_click_method(argc_new, argv_new);
- } else if (strcasecmp("drag_lock", argv[1]) == 0) {
- res = input_cmd_drag_lock(argc_new, argv_new);
- } else if (strcasecmp("dwt", argv[1]) == 0) {
- res = input_cmd_dwt(argc_new, argv_new);
- } else if (strcasecmp("events", argv[1]) == 0) {
- res = input_cmd_events(argc_new, argv_new);
- } else if (strcasecmp("left_handed", argv[1]) == 0) {
- res = input_cmd_left_handed(argc_new, argv_new);
- } else if (strcasecmp("middle_emulation", argv[1]) == 0) {
- res = input_cmd_middle_emulation(argc_new, argv_new);
- } else if (strcasecmp("natural_scroll", argv[1]) == 0) {
- res = input_cmd_natural_scroll(argc_new, argv_new);
- } else if (strcasecmp("pointer_accel", argv[1]) == 0) {
- res = input_cmd_pointer_accel(argc_new, argv_new);
- } else if (strcasecmp("scroll_method", argv[1]) == 0) {
- res = input_cmd_scroll_method(argc_new, argv_new);
- } else if (strcasecmp("tap", argv[1]) == 0) {
- res = input_cmd_tap(argc_new, argv_new);
- } else {
- res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
+ if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 3))) {
+ return error;
+ }
+
+ bool has_context = (config->handler_context.input_config != NULL);
+ if (!has_context) {
+ // caller did not give a context so create one just for this command
+ config->handler_context.input_config = new_input_config(argv[0]);
+ if (!config->handler_context.input_config) {
+ return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config");
}
- current_input_config = NULL;
- return res;
}
- return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
+ int argc_new = argc-2;
+ char **argv_new = argv+2;
+
+ struct cmd_results *res;
+ if (strcasecmp("accel_profile", argv[1]) == 0) {
+ res = input_cmd_accel_profile(argc_new, argv_new);
+ } else if (strcasecmp("click_method", argv[1]) == 0) {
+ res = input_cmd_click_method(argc_new, argv_new);
+ } else if (strcasecmp("drag_lock", argv[1]) == 0) {
+ res = input_cmd_drag_lock(argc_new, argv_new);
+ } else if (strcasecmp("dwt", argv[1]) == 0) {
+ res = input_cmd_dwt(argc_new, argv_new);
+ } else if (strcasecmp("events", argv[1]) == 0) {
+ res = input_cmd_events(argc_new, argv_new);
+ } else if (strcasecmp("left_handed", argv[1]) == 0) {
+ res = input_cmd_left_handed(argc_new, argv_new);
+ } else if (strcasecmp("middle_emulation", argv[1]) == 0) {
+ res = input_cmd_middle_emulation(argc_new, argv_new);
+ } else if (strcasecmp("natural_scroll", argv[1]) == 0) {
+ res = input_cmd_natural_scroll(argc_new, argv_new);
+ } else if (strcasecmp("pointer_accel", argv[1]) == 0) {
+ res = input_cmd_pointer_accel(argc_new, argv_new);
+ } else if (strcasecmp("scroll_method", argv[1]) == 0) {
+ res = input_cmd_scroll_method(argc_new, argv_new);
+ } else if (strcasecmp("tap", argv[1]) == 0) {
+ res = input_cmd_tap(argc_new, argv_new);
+ } else if (strcasecmp("xkb_layout", argv[1]) == 0) {
+ res = input_cmd_xkb_layout(argc_new, argv_new);
+ } else if (strcasecmp("xkb_model", argv[1]) == 0) {
+ res = input_cmd_xkb_model(argc_new, argv_new);
+ } else if (strcasecmp("xkb_options", argv[1]) == 0) {
+ res = input_cmd_xkb_options(argc_new, argv_new);
+ } else if (strcasecmp("xkb_rules", argv[1]) == 0) {
+ res = input_cmd_xkb_rules(argc_new, argv_new);
+ } else if (strcasecmp("xkb_variant", argv[1]) == 0) {
+ res = input_cmd_xkb_variant(argc_new, argv_new);
+ } else {
+ res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
+ }
+
+ if (!has_context) {
+ // clean up the context we created earlier
+ free_input_config(config->handler_context.input_config);
+ config->handler_context.input_config = NULL;
+ }
+
+ return res;
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "accel_profile", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "accel_profile",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "adaptive") == 0) {
new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
@@ -22,6 +27,6 @@ struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
"Expected 'accel_profile <adaptive|flat>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/config.h"
+#include "sway/input/input-manager.h"
#include "log.h"
struct cmd_results *input_cmd_click_method(int argc, char **argv) {
- sway_log(L_DEBUG, "click_method for device: %d %s", current_input_config==NULL, current_input_config->identifier);
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "click_method", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "click_method",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "none") == 0) {
new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
@@ -22,9 +26,10 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) {
} else if (strcasecmp(argv[0], "clickfinger") == 0) {
new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
} else {
- return cmd_results_new(CMD_INVALID, "click_method", "Expected 'click_method <none|button_areas|clickfinger'");
+ return cmd_results_new(CMD_INVALID, "click_method",
+ "Expected 'click_method <none|button_areas|clickfinger'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "drag_lock", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "drag_lock", "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
} else {
- return cmd_results_new(CMD_INVALID, "drag_lock", "Expected 'drag_lock <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "drag_lock",
+ "Expected 'drag_lock <enabled|disabled>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_dwt(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
} else {
- return cmd_results_new(CMD_INVALID, "dwt", "Expected 'dwt <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "dwt",
+ "Expected 'dwt <enabled|disabled>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
#include "log.h"
struct cmd_results *input_cmd_events(int argc, char **argv) {
- sway_log(L_DEBUG, "events for device: %s", current_input_config->identifier);
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "events", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "events",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ wlr_log(L_DEBUG, "events for device: %s",
+ current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
} else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) {
- new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
+ new_config->send_events =
+ LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
} else {
- return cmd_results_new(CMD_INVALID, "events", "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
+ return cmd_results_new(CMD_INVALID, "events",
+ "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "left_handed", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "left_handed",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->left_handed = 1;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->left_handed = 0;
} else {
- return cmd_results_new(CMD_INVALID, "left_handed", "Expected 'left_handed <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "left_handed",
+ "Expected 'left_handed <enabled|disabled>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <string.h>
+#include <strings.h>
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_map_to_output(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "map_to_output", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "map_to_output",
+ "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->mapped_output = strdup(argv[0]);
+ apply_input_config(new_config);
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "middle_emulation", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "middle_emulation",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
} else if (strcasecmp(argv[0], "disabled") == 0) {
- new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+ new_config->middle_emulation =
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
} else {
- return cmd_results_new(CMD_INVALID, "middle_emulation", "Expected 'middle_emulation <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "middle_emulation",
+ "Expected 'middle_emulation <enabled|disabled>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "natural_scoll", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "natural_scoll",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->natural_scroll = 1;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->natural_scroll = 0;
} else {
- return cmd_results_new(CMD_INVALID, "natural_scroll", "Expected 'natural_scroll <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "natural_scroll",
+ "Expected 'natural_scroll <enabled|disabled>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <stdlib.h>
#include <string.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "pointer_accel", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "pointer_accel", "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
float pointer_accel = atof(argv[0]);
if (pointer_accel < -1 || pointer_accel > 1) {
- return cmd_results_new(CMD_INVALID, "pointer_accel", "Input out of range [-1, 1]");
+ return cmd_results_new(CMD_INVALID, "pointer_accel",
+ "Input out of range [-1, 1]");
}
new_config->pointer_accel = pointer_accel;
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "scroll_method", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "scroll_method",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "none") == 0) {
new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
@@ -22,9 +27,10 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
} else if (strcasecmp(argv[0], "on_button_down") == 0) {
new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
} else {
- return cmd_results_new(CMD_INVALID, "scroll_method", "Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
+ return cmd_results_new(CMD_INVALID, "scroll_method",
+ "Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
#include "log.h"
struct cmd_results *input_cmd_tap(int argc, char **argv) {
- sway_log(L_DEBUG, "tap for device: %s", current_input_config->identifier);
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
return cmd_results_new(CMD_FAILURE, "tap", "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED;
} else {
- return cmd_results_new(CMD_INVALID, "tap", "Expected 'tap <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "tap",
+ "Expected 'tap <enabled|disabled>'");
}
- sway_log(L_DEBUG, "apply-tap for device: %s", current_input_config->identifier);
- input_cmd_apply(new_config);
+ wlr_log(L_DEBUG, "apply-tap for device: %s",
+ current_input_config->identifier);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
+#define _XOPEN_SOURCE 700
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_layout", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_layout", "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->xkb_layout = strdup(argv[0]);
+
+ wlr_log(L_DEBUG, "apply-xkb_layout for device: %s layout: %s",
+ current_input_config->identifier, new_config->xkb_layout);
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_model(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_model", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_model", "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->xkb_model = strdup(argv[0]);
+
+ wlr_log(L_DEBUG, "apply-xkb_model for device: %s model: %s",
+ current_input_config->identifier, new_config->xkb_model);
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_options(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_options", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_options", "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->xkb_options = strdup(argv[0]);
+
+ wlr_log(L_DEBUG, "apply-xkb_options for device: %s options: %s",
+ current_input_config->identifier, new_config->xkb_options);
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_rules", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_rules", "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->xkb_rules = strdup(argv[0]);
+
+ wlr_log(L_DEBUG, "apply-xkb_rules for device: %s rules: %s",
+ current_input_config->identifier, new_config->xkb_rules);
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_variant", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_variant", "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->xkb_variant = strdup(argv[0]);
+
+ wlr_log(L_DEBUG, "apply-xkb_variant for device: %s variant: %s",
+ current_input_config->identifier, new_config->xkb_variant);
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
-#define _XOPEN_SOURCE 500
-#include <stdio.h>
-#include <string.h>
-#include "sway/security.h"
-#include "sway/commands.h"
-#include "sway/config.h"
-#include "ipc.h"
-#include "log.h"
-#include "util.h"
-
-static struct ipc_policy *current_policy = NULL;
-
-struct cmd_results *cmd_ipc(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 2))) {
- return error;
- }
- if ((error = check_security_config())) {
- return error;
- }
-
- char *program = NULL;
-
- if (!strcmp(argv[0], "*")) {
- program = strdup(argv[0]);
- } else if (!(program = resolve_path(argv[0]))) {
- return cmd_results_new(
- CMD_INVALID, "ipc", "Unable to resolve IPC Policy target.");
- }
- if (config->reading && strcmp("{", argv[1]) != 0) {
- return cmd_results_new(CMD_INVALID, "ipc",
- "Expected '{' at start of IPC config definition.");
- }
-
- if (!config->reading) {
- return cmd_results_new(CMD_FAILURE, "ipc", "Can only be used in config file.");
- }
-
- current_policy = alloc_ipc_policy(program);
- list_add(config->ipc_policies, current_policy);
-
- free(program);
- return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL);
-}
-
-struct cmd_results *cmd_ipc_events(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "events", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (config->reading && strcmp("{", argv[0]) != 0) {
- return cmd_results_new(CMD_INVALID, "events",
- "Expected '{' at start of IPC event config definition.");
- }
-
- if (!config->reading) {
- return cmd_results_new(CMD_FAILURE, "events", "Can only be used in config file.");
- }
-
- return cmd_results_new(CMD_BLOCK_IPC_EVENTS, NULL, NULL);
-}
-
-struct cmd_results *cmd_ipc_cmd(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- bool enabled;
- if (strcmp(argv[0], "enabled") == 0) {
- enabled = true;
- } else if (strcmp(argv[0], "disabled") == 0) {
- enabled = false;
- } else {
- return cmd_results_new(CMD_INVALID, argv[-1],
- "Argument must be one of 'enabled' or 'disabled'");
- }
-
- struct {
- char *name;
- enum ipc_feature type;
- } types[] = {
- { "*", IPC_FEATURE_ALL_COMMANDS },
- { "command", IPC_FEATURE_COMMAND },
- { "workspaces", IPC_FEATURE_GET_WORKSPACES },
- { "outputs", IPC_FEATURE_GET_OUTPUTS },
- { "tree", IPC_FEATURE_GET_TREE },
- { "marks", IPC_FEATURE_GET_MARKS },
- { "bar-config", IPC_FEATURE_GET_BAR_CONFIG },
- { "inputs", IPC_FEATURE_GET_INPUTS },
- { "clipboard", IPC_FEATURE_GET_CLIPBOARD },
- };
-
- uint32_t type = 0;
-
- for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
- if (strcmp(types[i].name, argv[-1]) == 0) {
- type = types[i].type;
- break;
- }
- }
-
- if (enabled) {
- current_policy->features |= type;
- sway_log(L_DEBUG, "Enabled IPC %s feature", argv[-1]);
- } else {
- current_policy->features &= ~type;
- sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]);
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
-
-struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- bool enabled;
- if (strcmp(argv[0], "enabled") == 0) {
- enabled = true;
- } else if (strcmp(argv[0], "disabled") == 0) {
- enabled = false;
- } else {
- return cmd_results_new(CMD_INVALID, argv[-1],
- "Argument must be one of 'enabled' or 'disabled'");
- }
-
- struct {
- char *name;
- enum ipc_feature type;
- } types[] = {
- { "*", IPC_FEATURE_ALL_EVENTS },
- { "workspace", IPC_FEATURE_EVENT_WORKSPACE },
- { "output", IPC_FEATURE_EVENT_OUTPUT },
- { "mode", IPC_FEATURE_EVENT_MODE },
- { "window", IPC_FEATURE_EVENT_WINDOW },
- { "binding", IPC_FEATURE_EVENT_BINDING },
- { "input", IPC_FEATURE_EVENT_INPUT },
- };
-
- uint32_t type = 0;
-
- for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
- if (strcmp(types[i].name, argv[-1]) == 0) {
- type = types[i].type;
- break;
- }
- }
-
- if (enabled) {
- current_policy->features |= type;
- sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]);
- } else {
- current_policy->features &= ~type;
- sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]);
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
+#include <wlr/util/log.h>
+#include "log.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/tree/view.h"
+#include "sway/tree/container.h"
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
struct cmd_results *cmd_kill(int argc, char **argv) {
- if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running.");
+ struct sway_container *con =
+ config->handler_context.current_container;
+
+ container_close(con);
- swayc_t *container = current_container;
- close_views(container);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/layout.h"
-
-/**
- * handle "layout auto" command group
- */
-static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv);
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "log.h"
struct cmd_results *cmd_layout(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "layout", "Can only be used when sway is running.");
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
return error;
}
- swayc_t *parent = current_container;
+ struct sway_container *parent = config->handler_context.current_container;
+
+ // TODO: floating
+ /*
if (parent->is_floating) {
return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows");
}
+ */
while (parent->type == C_VIEW) {
parent = parent->parent;
}
- enum swayc_layouts old_layout = parent->layout;
+ // TODO: stacks and tabs
if (strcasecmp(argv[0], "default") == 0) {
- swayc_change_layout(parent, parent->prev_layout);
+ container_set_layout(parent, parent->prev_layout);
if (parent->layout == L_NONE) {
- swayc_t *output = swayc_parent_by_type(parent, C_OUTPUT);
- swayc_change_layout(parent, default_layout(output));
+ container_set_layout(parent, container_get_default_layout(parent));
}
} else {
if (parent->layout != L_TABBED && parent->layout != L_STACKED) {
parent->prev_layout = parent->layout;
}
- if (strcasecmp(argv[0], "tabbed") == 0) {
- if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)){
- parent = new_container(parent, L_TABBED);
- }
-
- swayc_change_layout(parent, L_TABBED);
- } else if (strcasecmp(argv[0], "stacking") == 0) {
- if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)) {
- parent = new_container(parent, L_STACKED);
- }
-
- swayc_change_layout(parent, L_STACKED);
- } else if (strcasecmp(argv[0], "splith") == 0) {
- swayc_change_layout(parent, L_HORIZ);
+ if (strcasecmp(argv[0], "splith") == 0) {
+ container_set_layout(parent, L_HORIZ);
} else if (strcasecmp(argv[0], "splitv") == 0) {
- swayc_change_layout(parent, L_VERT);
+ container_set_layout(parent, L_VERT);
} else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE
|| parent->workspace_layout == L_HORIZ)) {
- swayc_change_layout(parent, L_VERT);
+ container_set_layout(parent, L_VERT);
} else {
- swayc_change_layout(parent, L_HORIZ);
+ container_set_layout(parent, L_HORIZ);
}
- } else if (strcasecmp(argv[0], "auto") == 0) {
- return cmd_layout_auto(parent, argc, argv);
}
}
- update_layout_geometry(parent, old_layout);
- update_geometry(parent);
-
arrange_windows(parent, parent->width, parent->height);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
-
-static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv) {
- // called after checking that argv[0] is auto, so just continue parsing from there
- struct cmd_results *error = NULL;
- const char *cmd_name = "layout auto";
- const char *set_inc_cmd_name = "layout auto [master|ncol] [set|inc]";
- const char *err_msg = "Allowed arguments are <right|left|top|bottom|next|prev|master|ncol>";
-
- bool need_layout_update = false;
- enum swayc_layouts old_layout = container->layout;
- enum swayc_layouts layout = old_layout;
-
- if ((error = checkarg(argc, "layout auto", EXPECTED_MORE_THAN, 1))) {
- return error;
- }
-
- if (strcasecmp(argv[1], "left") == 0) {
- layout = L_AUTO_LEFT;
- } else if (strcasecmp(argv[1], "right") == 0) {
- layout = L_AUTO_RIGHT;
- } else if (strcasecmp(argv[1], "top") == 0) {
- layout = L_AUTO_TOP;
- } else if (strcasecmp(argv[1], "bottom") == 0) {
- layout = L_AUTO_BOTTOM;
- } else if (strcasecmp(argv[1], "next") == 0) {
- if (is_auto_layout(container->layout) && container->layout < L_AUTO_LAST) {
- layout = container->layout + 1;
- } else {
- layout = L_AUTO_FIRST;
- }
- } else if (strcasecmp(argv[1], "prev") == 0) {
- if (is_auto_layout(container->layout) && container->layout > L_AUTO_FIRST) {
- layout = container->layout - 1;
- } else {
- layout = L_AUTO_LAST;
- }
- } else {
- bool is_nmaster;
- bool is_set;
- if (strcasecmp(argv[1], "master") == 0) {
- is_nmaster = true;
- } else if (strcasecmp(argv[1], "ncol") == 0) {
- is_nmaster = false;
- } else {
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command. %s",
- cmd_name, err_msg);
- }
- if ((error = checkarg(argc, "auto <master|ncol>", EXPECTED_EQUAL_TO, 4))) {
- return error;
- }
- if (strcasecmp(argv[2], "set") == 0) {
- is_set = true;
- } else if (strcasecmp(argv[2], "inc") == 0) {
- is_set = false;
- } else {
- return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command. %s, "
- "Argument must be on of <set|inc>",
- set_inc_cmd_name);
- }
- char *end;
- int n = (int)strtol(argv[3], &end, 10);
- if (*end) {
- return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
- "(argument must be an integer)", set_inc_cmd_name);
- }
- if (is_auto_layout(container->layout)) {
- int inc = 0; /* difference between current master/ncol and requested value */
- if (is_nmaster) {
- if (is_set) {
- if (n < 0) {
- return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
- "(master must be >= 0)", set_inc_cmd_name);
- }
- inc = n - (int)container->nb_master;
- } else { /* inc command */
- if ((int)container->nb_master + n >= 0) {
- inc = n;
- }
- }
- if (inc) {
- for (int i = container->nb_master;
- i >= 0 && i < container->children->length
- && i != (int)container->nb_master + inc;) {
- ((swayc_t *)container->children->items[i])->height = -1;
- ((swayc_t *)container->children->items[i])->width = -1;
- i += inc > 0 ? 1 : -1;
- }
- container->nb_master += inc;
- need_layout_update = true;
- }
- } else { /* ncol modification */
- if (is_set) {
- if (n <= 0) {
- return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
- "(ncol must be > 0)", set_inc_cmd_name);
- }
- inc = n - (int)container->nb_slave_groups;
- } else { /* inc command */
- if ((int)container->nb_slave_groups + n > 0) {
- inc = n;
- }
- }
- if (inc) {
- container->nb_slave_groups += inc;
- need_layout_update = true;
- }
- }
- }
- }
-
- if (layout != old_layout) {
- swayc_change_layout(container, layout);
- update_layout_geometry(container, old_layout);
- need_layout_update = true;
- }
- if (need_layout_update) {
- update_geometry(container);
- arrange_windows(container, container->width, container->height);
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "log.h"
-
-struct cmd_results *cmd_log_colors(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (!config->reading) return cmd_results_new(CMD_FAILURE, "log_colors", "Can only be used in config file.");
- if ((error = checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
- if (strcasecmp(argv[0], "no") == 0) {
- sway_log_colors(0);
- } else if (strcasecmp(argv[0], "yes") == 0) {
- sway_log_colors(1);
- } else {
- error = cmd_results_new(CMD_FAILURE, "log_colors",
- "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]);
- return error;
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include <stdbool.h>
-#include "sway/commands.h"
-#include "list.h"
-#include "stringop.h"
-
-static void find_marks_callback(swayc_t *container, void *_mark) {
- char *mark = (char *)_mark;
-
- int index;
- if (container->marks && ((index = list_seq_find(container->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1)) {
- list_del(container->marks, index);
- }
-}
-
-struct cmd_results *cmd_mark(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "mark", "Can't be used in config file.");
- if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- swayc_t *view = current_container;
- bool add = false;
- bool toggle = false;
-
- if (strcmp(argv[0], "--add") == 0) {
- --argc; ++argv;
- add = true;
- } else if (strcmp(argv[0], "--replace") == 0) {
- --argc; ++argv;
- }
-
- if (argc && strcmp(argv[0], "--toggle") == 0) {
- --argc; ++argv;
- toggle = true;
- }
-
- if (argc) {
- char *mark = join_args(argv, argc);
-
- // Remove all existing marks of this type
- container_map(&root_container, find_marks_callback, mark);
-
- if (view->marks) {
- if (add) {
- int index;
- if ((index = list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1) {
- if (toggle) {
- free(view->marks->items[index]);
- list_del(view->marks, index);
-
- if (0 == view->marks->length) {
- list_free(view->marks);
- view->marks = NULL;
- }
- }
- free(mark);
- } else {
- list_add(view->marks, mark);
- }
- } else {
- if (toggle && list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark) != -1) {
- // Delete the list
- list_foreach(view->marks, free);
- list_free(view->marks);
- view->marks = NULL;
- } else {
- // Delete and replace with a new list
- list_foreach(view->marks, free);
- list_free(view->marks);
-
- view->marks = create_list();
- list_add(view->marks, mark);
- }
- }
- } else {
- view->marks = create_list();
- list_add(view->marks, mark);
- }
- } else {
- return cmd_results_new(CMD_FAILURE, "mark",
- "Expected 'mark [--add|--replace] [--toggle] <mark>'");
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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) {
}
const char *mode_name = argv[0];
- bool mode_make = (argc == 2 && strcmp(argv[1], "{") == 0);
- if (mode_make) {
- if (!config->reading)
- return cmd_results_new(CMD_FAILURE, "mode", "Can only be used in config file.");
+ bool new_mode = (argc == 2 && strcmp(argv[1], "{") == 0);
+ if (new_mode && !config->reading) {
+ return cmd_results_new(CMD_FAILURE,
+ "mode", "Can only be used in config file.");
}
struct sway_mode *mode = NULL;
// Find mode
- int i, len = config->modes->length;
- for (i = 0; i < len; ++i) {
- struct sway_mode *find = config->modes->items[i];
- if (strcasecmp(find->name, mode_name) == 0) {
- mode = find;
+ for (int i = 0; i < config->modes->length; ++i) {
+ struct sway_mode *test = config->modes->items[i];
+ if (strcasecmp(test->name, mode_name) == 0) {
+ mode = test;
break;
}
}
// Create mode if it doesn't exist
- if (!mode && mode_make) {
- mode = malloc(sizeof(struct sway_mode));
+ if (!mode && new_mode) {
+ mode = calloc(1, sizeof(struct sway_mode));
if (!mode) {
- return cmd_results_new(CMD_FAILURE, "mode", "Unable to allocate mode");
+ return cmd_results_new(CMD_FAILURE,
+ "mode", "Unable to allocate mode");
}
mode->name = strdup(mode_name);
- mode->bindings = create_list();
+ mode->keysym_bindings = create_list();
+ mode->keycode_bindings = create_list();
list_add(config->modes, mode);
}
if (!mode) {
- error = cmd_results_new(CMD_INVALID, "mode", "Unknown mode `%s'", mode_name);
+ error = cmd_results_new(CMD_INVALID,
+ "mode", "Unknown mode `%s'", mode_name);
return error;
}
- if ((config->reading && mode_make) || (!config->reading && !mode_make)) {
- sway_log(L_DEBUG, "Switching to mode `%s'",mode->name);
+ if ((config->reading && new_mode) || (!config->reading && !new_mode)) {
+ wlr_log(L_DEBUG, "Switching to mode `%s'",mode->name);
}
// Set current mode
config->current_mode = mode;
- if (!mode_make) {
+ if (!new_mode) {
// trigger IPC mode event
ipc_event_mode(config->current_mode->name);
}
- return cmd_results_new(mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL);
+ return cmd_results_new(new_mode ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL);
}
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) {
} else if (strcasecmp(argv[0], "none") == 0) {
config->mouse_warping = false;
} else {
- return cmd_results_new(CMD_FAILURE, "mouse_warping", "Expected 'mouse_warping output|none'");
+ return cmd_results_new(CMD_FAILURE, "mouse_warping",
+ "Expected 'mouse_warping output|none'");
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
+
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 @@
+#define _XOPEN_SOURCE 500
#include <string.h>
#include <strings.h>
-#include <wlc/wlc.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/util/log.h>
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/layout.h"
+#include "sway/input/seat.h"
#include "sway/output.h"
-#include "sway/workspace.h"
-#include "list.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/workspace.h"
#include "stringop.h"
+#include "list.h"
+
+static const char* expected_syntax =
+ "Expected 'move <left|right|up|down> <[px] px>' or "
+ "'move <container|window> to workspace <name>' or "
+ "'move <container|window|workspace> to output <name|direction>' or "
+ "'move position mouse'";
+
+static struct sway_container *output_in_direction(const char *direction,
+ struct wlr_output *reference, int ref_ox, int ref_oy) {
+ int ref_lx = ref_ox + reference->lx,
+ ref_ly = ref_oy + reference->ly;
+ struct {
+ char *name;
+ enum wlr_direction direction;
+ } names[] = {
+ { "up", WLR_DIRECTION_UP },
+ { "down", WLR_DIRECTION_DOWN },
+ { "left", WLR_DIRECTION_LEFT },
+ { "right", WLR_DIRECTION_RIGHT },
+ };
+ for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
+ if (strcasecmp(names[i].name, direction) == 0) {
+ struct wlr_output *adjacent = wlr_output_layout_adjacent_output(
+ root_container.sway_root->output_layout,
+ names[i].direction, reference, ref_lx, ref_ly);
+ if (adjacent) {
+ struct sway_output *sway_output = adjacent->data;
+ return sway_output->swayc;
+ }
+ break;
+ }
+ }
+ return output_by_name(direction);
+}
+
+static struct cmd_results *cmd_move_container(struct sway_container *current,
+ int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "move container/window",
+ EXPECTED_AT_LEAST, 4))) {
+ return error;
+ } else if (strcasecmp(argv[1], "to") == 0
+ && strcasecmp(argv[2], "workspace") == 0) {
+ // move container to workspace x
+ if (current->type == C_WORKSPACE) {
+ // TODO: Wrap children in a container and move that
+ return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
+ } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
+ return cmd_results_new(CMD_FAILURE, "move",
+ "Can only move containers and views.");
+ }
+ struct sway_container *ws;
+ char *ws_name = NULL;
+ if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
+ // move "container to workspace number x"
+ ws_name = strdup(argv[4]);
+ ws = workspace_by_number(ws_name);
+ } else {
+ ws_name = join_args(argv + 3, argc - 3);
+ ws = workspace_by_name(ws_name);
+ }
+
+ if (config->auto_back_and_forth && prev_workspace_name) {
+ // auto back and forth move
+ struct sway_container *curr_ws = container_parent(current, C_WORKSPACE);
+ if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) {
+ // if target workspace is the current one
+ free(ws_name);
+ ws_name = strdup(prev_workspace_name);
+ ws = workspace_by_name(ws_name);
+ }
+ }
+
+ if (!ws) {
+ ws = workspace_create(NULL, ws_name);
+ }
+ free(ws_name);
+ struct sway_container *old_parent = current->parent;
+ struct sway_container *focus = seat_get_focus_inactive(
+ config->handler_context.seat, ws);
+ container_move_to(current, focus);
+ seat_set_focus(config->handler_context.seat, old_parent);
+ container_reap_empty(old_parent);
+ container_reap_empty(focus->parent);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ } else if (strcasecmp(argv[1], "to") == 0
+ && strcasecmp(argv[2], "output") == 0) {
+ if (current->type == C_WORKSPACE) {
+ // TODO: Wrap children in a container and move that
+ return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
+ } else if (current->type != C_CONTAINER
+ && current->type != C_VIEW) {
+ return cmd_results_new(CMD_FAILURE, "move",
+ "Can only move containers and views.");
+ }
+ struct sway_container *source = container_parent(current, C_OUTPUT);
+ struct sway_container *destination = output_in_direction(argv[3],
+ source->sway_output->wlr_output, current->x, current->y);
+ if (!destination) {
+ return cmd_results_new(CMD_FAILURE, "move workspace",
+ "Can't find output with name/direction '%s'", argv[3]);
+ }
+ struct sway_container *focus = seat_get_focus_inactive(
+ config->handler_context.seat, destination);
+ if (!focus) {
+ // We've never been to this output before
+ focus = destination->children->items[0];
+ }
+ struct sway_container *old_parent = current->parent;
+ container_move_to(current, focus);
+ seat_set_focus(config->handler_context.seat, old_parent);
+ container_reap_empty(old_parent);
+ container_reap_empty(focus->parent);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ }
+ return cmd_results_new(CMD_INVALID, "move", expected_syntax);
+}
+
+static struct cmd_results *cmd_move_workspace(struct sway_container *current,
+ int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
+ return error;
+ } else if (strcasecmp(argv[1], "to") != 0
+ || strcasecmp(argv[2], "output") != 0) {
+ return cmd_results_new(CMD_INVALID, "move", expected_syntax);
+ }
+ struct sway_container *source = container_parent(current, C_OUTPUT);
+ int center_x = current->width / 2 + current->x,
+ center_y = current->height / 2 + current->y;
+ struct sway_container *destination = output_in_direction(argv[3],
+ source->sway_output->wlr_output, center_x, center_y);
+ if (!destination) {
+ return cmd_results_new(CMD_FAILURE, "move workspace",
+ "Can't find output with name/direction '%s'", argv[3]);
+ }
+ if (current->type != C_WORKSPACE) {
+ current = container_parent(current, C_WORKSPACE);
+ }
+ container_move_to(current, destination);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
struct cmd_results *cmd_move(int argc, char **argv) {
struct cmd_results *error = NULL;
int move_amt = 10;
-
- if (config->reading) return cmd_results_new(CMD_FAILURE, "move", "Can't be used in config file.");
if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
return error;
}
- const char* expected_syntax = "Expected 'move <left|right|up|down|next|prev|first> <[px] px>' or "
- "'move <container|window> to workspace <name>' or "
- "'move <container|window|workspace> to output <name|direction>' or "
- "'move position mouse'";
- swayc_t *view = current_container;
+ struct sway_container *current = config->handler_context.current_container;
- if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0 )) {
+ if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) {
char *inv;
move_amt = (int)strtol(argv[1], &inv, 10);
if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
- move_amt = 10;
+ return cmd_results_new(CMD_FAILURE, "move",
+ "Invalid distance specified");
}
}
if (strcasecmp(argv[0], "left") == 0) {
- move_container(view, MOVE_LEFT, move_amt);
+ container_move(current, MOVE_LEFT, move_amt);
} else if (strcasecmp(argv[0], "right") == 0) {
- move_container(view, MOVE_RIGHT, move_amt);
+ container_move(current, MOVE_RIGHT, move_amt);
} else if (strcasecmp(argv[0], "up") == 0) {
- move_container(view, MOVE_UP, move_amt);
+ container_move(current, MOVE_UP, move_amt);
} else if (strcasecmp(argv[0], "down") == 0) {
- move_container(view, MOVE_DOWN, move_amt);
- } else if (strcasecmp(argv[0], "next") == 0) {
- move_container(view, MOVE_NEXT, move_amt);
- } else if (strcasecmp(argv[0], "prev") == 0) {
- move_container(view, MOVE_PREV, move_amt);
- } else if (strcasecmp(argv[0], "first") == 0) {
- move_container(view, MOVE_FIRST, move_amt);
- } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) {
- // "move container ...
- if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 4))) {
- return error;
- } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "workspace") == 0) {
- // move container to workspace x
- if (view->type == C_WORKSPACE) {
- if (!view->children || view->children->length == 0) {
- return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace");
- }
- view = new_container(view, view->workspace_layout);
- } if (view->type != C_CONTAINER && view->type != C_VIEW) {
- return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
- }
-
- swayc_t *ws;
- const char *num_name = NULL;
- char *ws_name = NULL;
- if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
- // move "container to workspace number x"
- num_name = argv[4];
- ws = workspace_by_number(num_name);
- } else {
- ws_name = join_args(argv + 3, argc - 3);
- ws = workspace_by_name(ws_name);
- }
-
- if (ws == NULL) {
- ws = workspace_create(ws_name ? ws_name : num_name);
- }
- if (ws_name) {
- free(ws_name);
- }
- move_container_to(view, get_focused_container(ws));
- } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) {
- // move container to output x
- swayc_t *output = NULL;
- struct wlc_point abs_pos;
- get_absolute_center_position(view, &abs_pos);
- if (view->type == C_WORKSPACE) {
- if (!view->children || view->children->length == 0) {
- return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace");
- }
- view = new_container(view, view->workspace_layout);
- } else if (view->type != C_CONTAINER && view->type != C_VIEW) {
- return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
- } else if (!(output = output_by_name(argv[3], &abs_pos))) {
- return cmd_results_new(CMD_FAILURE, "move",
- "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
- }
-
- swayc_t *container = get_focused_container(output);
- if (container->is_floating) {
- move_container_to(view, container->parent);
- } else {
- move_container_to(view, container);
- }
- } else {
- return cmd_results_new(CMD_INVALID, "move", expected_syntax);
- }
+ container_move(current, MOVE_DOWN, move_amt);
+ } else if (strcasecmp(argv[0], "container") == 0
+ || strcasecmp(argv[0], "window") == 0) {
+ return cmd_move_container(current, argc, argv);
} else if (strcasecmp(argv[0], "workspace") == 0) {
- // move workspace (to output x)
- swayc_t *output = NULL;
- struct wlc_point abs_pos;
- get_absolute_center_position(view, &abs_pos);
- if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
- return error;
- } else if (strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "output") != 0) {
- return cmd_results_new(CMD_INVALID, "move", expected_syntax);
- } else if (!(output = output_by_name(argv[3], &abs_pos))) {
- return cmd_results_new(CMD_FAILURE, "move workspace",
- "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
- }
- if (view->type == C_WORKSPACE) {
- // This probably means we're moving an empty workspace, but
- // that's fine.
- move_workspace_to(view, output);
- } else {
- swayc_t *workspace = swayc_parent_by_type(view, C_WORKSPACE);
- move_workspace_to(workspace, output);
- }
- } else if (strcasecmp(argv[0], "scratchpad") == 0 || (strcasecmp(argv[0], "to") == 0 && strcasecmp(argv[1], "scratchpad") == 0)) {
- // move scratchpad ...
- if (view->type != C_CONTAINER && view->type != C_VIEW) {
- return cmd_results_new(CMD_FAILURE, "move scratchpad", "Can only move containers and views.");
- }
- swayc_t *view = current_container;
- int i;
- for (i = 0; i < scratchpad->length; i++) {
- if (scratchpad->items[i] == view) {
- hide_view_in_scratchpad(view);
- sp_view = NULL;
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- }
- list_add(scratchpad, view);
- if (!view->is_floating) {
- destroy_container(remove_child(view));
- } else {
- remove_child(view);
- }
- wlc_view_set_mask(view->handle, 0);
- arrange_windows(swayc_active_workspace(), -1, -1);
- swayc_t *focused = container_under_pointer();
- if (focused == NULL) {
- focused = swayc_active_workspace();
- }
- set_focused_container(focused);
+ return cmd_move_workspace(current, argc, argv);
+ } else if (strcasecmp(argv[0], "scratchpad") == 0
+ || (strcasecmp(argv[0], "to") == 0
+ && strcasecmp(argv[1], "scratchpad") == 0)) {
+ // TODO: scratchpad
+ return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
} else if (strcasecmp(argv[0], "position") == 0) {
- if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 2))) {
- return error;
- }
- if (strcasecmp(argv[1], "mouse")) {
- return cmd_results_new(CMD_INVALID, "move", expected_syntax);
- }
-
- if (view->is_floating) {
- swayc_t *output = swayc_parent_by_type(view, C_OUTPUT);
- struct wlc_geometry g;
- wlc_view_get_visible_geometry(view->handle, &g);
- const struct wlc_size *size = wlc_output_get_resolution(output->handle);
-
- double x_pos, y_pos;
- wlc_pointer_get_position_v2(&x_pos, &y_pos);
-
- int32_t x = x_pos - g.size.w / 2;
- int32_t y = y_pos - g.size.h / 2;
-
- uint32_t w = size->w - g.size.w;
- uint32_t h = size->h - g.size.h;
-
- view->x = g.origin.x = MIN((int32_t)w, MAX(x, 0));
- view->y = g.origin.y = MIN((int32_t)h, MAX(y, 0));
-
- wlc_view_set_geometry(view->handle, 0, &g);
- }
+ // TODO: floating
+ return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
} else {
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
}
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 @@
-#include "log.h"
-#include "sway/commands.h"
-
-struct cmd_results *cmd_new_float(int argc, char **argv) {
- sway_log(L_INFO, "`new_float` is deprecated and will be removed in the future. "
- "Please use `default_floating_border` instead.");
- return cmd_default_floating_border(argc, argv);
-}
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 @@
-#include "log.h"
-#include "sway/commands.h"
-
-struct cmd_results *cmd_new_window(int argc, char **argv) {
- sway_log(L_INFO, "`new_window` is deprecated and will be removed in the future. "
- "Please use `default_border` instead.");
- return cmd_default_border(argc, argv);
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/criteria.h"
-#include "list.h"
-#include "log.h"
-#include "stringop.h"
-
-struct cmd_results *cmd_no_focus(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "no_focus", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
- // add command to a criteria/command pair that is run against views when they appear.
- char *criteria = argv[0];
-
- struct criteria *crit = malloc(sizeof(struct criteria));
- if (!crit) {
- return cmd_results_new(CMD_FAILURE, "no_focus", "Unable to allocate criteria");
- }
- crit->crit_raw = strdup(criteria);
- crit->tokens = create_list();
- crit->cmdlist = NULL;
- char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
-
- if (err_str) {
- error = cmd_results_new(CMD_INVALID, "no_focus", err_str);
- free(err_str);
- free_criteria(crit);
- } else if (crit->tokens->length == 0) {
- error = cmd_results_new(CMD_INVALID, "no_focus", "Found no name/value pairs in criteria");
- free_criteria(crit);
- } else if (list_seq_find(config->no_focus, criteria_cmp, crit) != -1) {
- sway_log(L_DEBUG, "no_focus: Duplicate, skipping.");
- free_criteria(crit);
- } else {
- sway_log(L_DEBUG, "no_focus: '%s' added", crit->crit_raw);
- list_add(config->no_focus, crit);
- }
- return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
+#include <assert.h>
+#include <stdlib.h>
+#include "sway/commands.h"
+#include "sway/tree/view.h"
+#include "log.h"
+
+static bool parse_opacity(const char *opacity, float *val) {
+ char *err;
+ *val = strtof(opacity, &err);
+ if (*val < 0 || *val > 1 || *err) {
+ return false;
+ }
+ return true;
+}
+
+struct cmd_results *cmd_opacity(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "layout", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+
+ struct sway_container *con =
+ config->handler_context.current_container;
+
+ float opacity = 0.0f;
+
+ if (!parse_opacity(argv[0], &opacity)) {
+ return cmd_results_new(CMD_INVALID, "opacity <value>",
+ "Invalid value (expected 0..1): %s", argv[0]);
+ }
+
+ con->alpha = opacity;
+ container_damage_whole(con);
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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[] = {
"center",
"fill",
"fit",
- "tile"
+ "tile",
};
-struct cmd_results *cmd_output(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) {
- return error;
+static struct cmd_results *cmd_output_mode(struct output_config *output,
+ int *i, int argc, char **argv) {
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output", "Missing mode argument.");
}
- const char *name = argv[0];
- struct output_config *output = calloc(1, sizeof(struct output_config));
- if (!output) {
- return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate output config");
+ char *end;
+ output->width = strtol(argv[*i], &end, 10);
+ if (*end) {
+ // Format is 1234x4321
+ if (*end != 'x') {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid mode width.");
+ }
+ ++end;
+ output->height = strtol(end, &end, 10);
+ if (*end) {
+ if (*end != '@') {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid mode height.");
+ }
+ ++end;
+ output->refresh_rate = strtof(end, &end);
+ if (strcasecmp("Hz", end) != 0) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid mode refresh rate.");
+ }
+ }
+ } else {
+ // Format is 1234 4321
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing mode argument (height).");
+ }
+ output->height = strtol(argv[*i], &end, 10);
+ if (*end) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid mode height.");
+ }
}
- output->x = output->y = output->width = output->height = -1;
- output->name = strdup(name);
- output->enabled = -1;
- output->scale = 1;
- // TODO: atoi doesn't handle invalid numbers
+ return NULL;
+}
- int i;
- for (i = 1; i < argc; ++i) {
- const char *command = argv[i];
+static struct cmd_results *cmd_output_position(struct output_config *output,
+ int *i, int argc, char **argv) {
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing position argument.");
+ }
- if (strcasecmp(command, "disable") == 0) {
- output->enabled = 0;
- } else if (strcasecmp(command, "resolution") == 0 || strcasecmp(command, "res") == 0) {
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument.");
- goto fail;
- }
- char *res = argv[i];
- char *x = strchr(res, 'x');
- int width = -1, height = -1;
- if (x != NULL) {
- // Format is 1234x4321
- *x = '\0';
- width = atoi(res);
- height = atoi(x + 1);
- *x = 'x';
- } else {
- // Format is 1234 4321
- width = atoi(res);
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height).");
- goto fail;
+ char *end;
+ output->x = strtol(argv[*i], &end, 10);
+ if (*end) {
+ // Format is 1234,4321
+ if (*end != ',') {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid position x.");
+ }
+ ++end;
+ output->y = strtol(end, &end, 10);
+ if (*end) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid position y.");
+ }
+ } else {
+ // Format is 1234 4321 (legacy)
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing position argument (y).");
+ }
+ output->y = strtol(argv[*i], &end, 10);
+ if (*end) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid position y.");
+ }
+ }
+
+ return NULL;
+}
+
+static struct cmd_results *cmd_output_scale(struct output_config *output,
+ int *i, int argc, char **argv) {
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing scale argument.");
+ }
+
+ char *end;
+ output->scale = strtof(argv[*i], &end);
+ if (*end) {
+ return cmd_results_new(CMD_INVALID, "output", "Invalid scale.");
+ }
+
+ return NULL;
+}
+
+static struct cmd_results *cmd_output_transform(struct output_config *output,
+ int *i, int argc, char **argv) {
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing transform argument.");
+ }
+
+ char *value = argv[*i];
+ if (strcmp(value, "normal") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ } else if (strcmp(value, "90") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_90;
+ } else if (strcmp(value, "180") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_180;
+ } else if (strcmp(value, "270") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_270;
+ } else if (strcmp(value, "flipped") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
+ } else if (strcmp(value, "flipped-90") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
+ } else if (strcmp(value, "flipped-180") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
+ } else if (strcmp(value, "flipped-270") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
+ } else {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid output transform.");
+ }
+
+ return NULL;
+}
+
+static struct cmd_results *cmd_output_background(struct output_config *output,
+ int *i, int argc, char **argv) {
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing background file or color specification.");
+ }
+ const char *background = argv[*i];
+ if (*i + 1 >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing background scaling mode or `solid_color`.");
+ }
+ const char *background_option = argv[*i];
+
+ if (strcasecmp(background_option, "solid_color") == 0) {
+ output->background = strdup(background);
+ output->background_option = strdup("solid_color");
+ } else {
+ bool valid = false;
+ char *mode;
+ size_t j;
+ for (j = 0; j < (size_t)(argc - *i); ++j) {
+ mode = argv[*i + j];
+ size_t n = sizeof(bg_options) / sizeof(char *);
+ for (size_t k = 0; k < n; ++k) {
+ if (strcasecmp(mode, bg_options[k]) == 0) {
+ valid = true;
+ break;
}
- res = argv[i];
- height = atoi(res);
}
- output->width = width;
- output->height = height;
- } else if (strcasecmp(command, "position") == 0 || strcasecmp(command, "pos") == 0) {
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing position argument.");
- goto fail;
+ if (valid) {
+ break;
}
- char *res = argv[i];
- char *c = strchr(res, ',');
- int x = -1, y = -1;
- if (c != NULL) {
- // Format is 1234,4321
- *c = '\0';
- x = atoi(res);
- y = atoi(c + 1);
- *c = ',';
- } else {
- // Format is 1234 4321
- x = atoi(res);
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing position argument (y).");
- goto fail;
+ }
+ if (!valid) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing background scaling mode.");
+ }
+
+ wordexp_t p;
+ char *src = join_args(argv + *i, j);
+ if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid syntax (%s).", src);
+ }
+ free(src);
+ src = p.we_wordv[0];
+ if (config->reading && *src != '/') {
+ char *conf = strdup(config->current_config);
+ if (conf) {
+ char *conf_path = dirname(conf);
+ src = malloc(strlen(conf_path) + strlen(src) + 2);
+ if (src) {
+ sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
+ } else {
+ wlr_log(L_ERROR,
+ "Unable to allocate background source");
}
- res = argv[i];
- y = atoi(res);
- }
- output->x = x;
- output->y = y;
- } else if (strcasecmp(command, "scale") == 0) {
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing scale parameter.");
- goto fail;
- }
- output->scale = atoi(argv[i]);
- } else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) {
- wordexp_t p;
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing background file or color specification.");
- goto fail;
- }
- if (i + 1 >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode or `solid_color`.");
- goto fail;
- }
- if (strcasecmp(argv[i + 1], "solid_color") == 0) {
- output->background = strdup(argv[argc - 2]);
- output->background_option = strdup("solid_color");
+ free(conf);
} else {
- // argv[i+j]=bg_option
- bool valid = false;
- char *mode;
- size_t j;
- for (j = 0; j < (size_t) (argc - i); ++j) {
- mode = argv[i + j];
- for (size_t k = 0; k < sizeof(bg_options) / sizeof(char *); ++k) {
- if (strcasecmp(mode, bg_options[k]) == 0) {
- valid = true;
- break;
- }
- }
- if (valid) {
- break;
- }
- }
- if (!valid) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode.");
- goto fail;
- }
+ wlr_log(L_ERROR, "Unable to allocate background source");
+ }
+ }
+ if (!src || access(src, F_OK) == -1) {
+ wordfree(&p);
+ return cmd_results_new(CMD_INVALID, "output",
+ "Background file unreadable (%s).", src);
+ }
- char *src = join_args(argv + i, j);
- if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
- error = cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src);
- goto fail;
- }
- free(src);
- src = p.we_wordv[0];
- if (config->reading && *src != '/') {
- char *conf = strdup(config->current_config);
- if (conf) {
- char *conf_path = dirname(conf);
- src = malloc(strlen(conf_path) + strlen(src) + 2);
- if (src) {
- sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
- } else {
- sway_log(L_ERROR, "Unable to allocate background source");
- }
- free(conf);
- } else {
- sway_log(L_ERROR, "Unable to allocate background source");
- }
- }
- if (!src || access(src, F_OK) == -1) {
- error = cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src);
- wordfree(&p);
- goto fail;
- }
+ output->background = strdup(src);
+ output->background_option = strdup(mode);
+ if (src != p.we_wordv[0]) {
+ free(src);
+ }
+ wordfree(&p);
- output->background = strdup(src);
- output->background_option = strdup(mode);
- if (src != p.we_wordv[0]) {
- free(src);
- }
- wordfree(&p);
+ *i += j;
+ }
- i += j;
- }
+ return NULL;
+}
+
+struct cmd_results *cmd_output(int argc, char **argv) {
+ struct cmd_results *error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1);
+ if (error != NULL) {
+ return error;
+ }
+
+ struct output_config *output = new_output_config(argv[0]);
+ if (!output) {
+ wlr_log(L_ERROR, "Failed to allocate output config");
+ return NULL;
+ }
+
+ for (int i = 1; i < argc; ++i) {
+ const char *command = argv[i];
+
+ if (strcasecmp(command, "enable") == 0) {
+ output->enabled = 1;
+ } else if (strcasecmp(command, "disable") == 0) {
+ output->enabled = 0;
+ } else if (strcasecmp(command, "mode") == 0 ||
+ strcasecmp(command, "resolution") == 0 ||
+ strcasecmp(command, "res") == 0) {
+ error = cmd_output_mode(output, &i, argc, argv);
+ } else if (strcasecmp(command, "position") == 0 ||
+ strcasecmp(command, "pos") == 0) {
+ error = cmd_output_position(output, &i, argc, argv);
+ } else if (strcasecmp(command, "scale") == 0) {
+ error = cmd_output_scale(output, &i, argc, argv);
+ } else if (strcasecmp(command, "transform") == 0) {
+ error = cmd_output_transform(output, &i, argc, argv);
+ } else if (strcasecmp(command, "background") == 0 ||
+ strcasecmp(command, "bg") == 0) {
+ error = cmd_output_background(output, &i, argc, argv);
+ } else {
+ error = cmd_results_new(CMD_INVALID, "output",
+ "Invalid output subcommand: %s.", command);
+ }
+
+ if (error != NULL) {
+ goto fail;
}
}
- i = list_seq_find(config->output_configs, output_name_cmp, name);
+ int i = list_seq_find(config->output_configs, output_name_cmp, output->name);
if (i >= 0) {
- // merge existing config
- struct output_config *oc = config->output_configs->items[i];
- merge_output_config(oc, output);
+ // Merge existing config
+ struct output_config *current = config->output_configs->items[i];
+ merge_output_config(current, output);
free_output_config(output);
- output = oc;
+ output = current;
} else {
list_add(config->output_configs, output);
}
- sway_log(L_DEBUG, "Config stored for output %s (enabled:%d) (%d x %d @ %d, %d scale %d) (bg %s %s)",
- output->name, output->enabled, output->width,
- output->height, output->x, output->y, output->scale,
- output->background, output->background_option);
+ wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
+ "position %d,%d scale %f transform %d) (bg %s %s)",
+ output->name, output->enabled, output->width, output->height,
+ output->refresh_rate, output->x, output->y, output->scale,
+ output->transform, output->background, output->background_option);
- if (output->name) {
- // Try to find the output container and apply configuration now. If
- // this is during startup then there will be no container and config
- // will be applied during normal "new output" event from wlc.
- swayc_t *cont = NULL;
- for (int i = 0; i < root_container.children->length; ++i) {
- cont = root_container.children->items[i];
- if (cont->name && ((strcmp(cont->name, output->name) == 0) || (strcmp(output->name, "*") == 0))) {
- apply_output_config(output, cont);
+ // Try to find the output container and apply configuration now. If
+ // this is during startup then there will be no container and config
+ // will be applied during normal "new output" event from wlroots.
+ char identifier[128];
+ bool all = strcmp(output->name, "*") == 0;
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *cont = root_container.children->items[i];
+ if (cont->type != C_OUTPUT) {
+ continue;
+ }
- if (strcmp(output->name, "*") != 0) {
- // stop looking if the output config isn't applicable to all outputs
- break;
- }
+ output_get_identifier(identifier, sizeof(identifier), cont->sway_output);
+ if (all || strcmp(cont->name, output->name) == 0 ||
+ strcmp(identifier, output->name) == 0) {
+ apply_output_config(output, cont);
+
+ if (!all) {
+ // Stop looking if the output config isn't applicable to all
+ // outputs
+ break;
}
}
}
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 @@
-#define _XOPEN_SOURCE 500
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/config.h"
-#include "sway/security.h"
-#include "util.h"
-#include "log.h"
-
-static enum secure_feature get_features(int argc, char **argv,
- struct cmd_results **error) {
- enum secure_feature features = 0;
-
- struct {
- char *name;
- enum secure_feature feature;
- } feature_names[] = {
- { "lock", FEATURE_LOCK },
- { "panel", FEATURE_PANEL },
- { "background", FEATURE_BACKGROUND },
- { "screenshot", FEATURE_SCREENSHOT },
- { "fullscreen", FEATURE_FULLSCREEN },
- { "keyboard", FEATURE_KEYBOARD },
- { "mouse", FEATURE_MOUSE },
- };
-
- for (int i = 1; i < argc; ++i) {
- size_t j;
- for (j = 0; j < sizeof(feature_names) / sizeof(feature_names[0]); ++j) {
- if (strcmp(feature_names[j].name, argv[i]) == 0) {
- break;
- }
- }
- if (j == sizeof(feature_names) / sizeof(feature_names[0])) {
- *error = cmd_results_new(CMD_INVALID,
- "permit", "Invalid feature grant %s", argv[i]);
- return 0;
- }
- features |= feature_names[j].feature;
- }
- return features;
-}
-
-struct cmd_results *cmd_permit(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) {
- return error;
- }
- if ((error = check_security_config())) {
- return error;
- }
-
- bool assign_perms = true;
- char *program = NULL;
-
- if (!strcmp(argv[0], "*")) {
- program = strdup(argv[0]);
- } else {
- program = resolve_path(argv[0]);
- }
- if (!program) {
- sway_assert(program, "Unable to resolve IPC permit target '%s'."
- " will issue empty policy", argv[0]);
- assign_perms = false;
- program = strdup(argv[0]);
- }
-
- struct feature_policy *policy = get_feature_policy(program);
- if (policy && assign_perms) {
- policy->features |= get_features(argc, argv, &error);
- sway_log(L_DEBUG, "Permissions granted to %s for features %d",
- policy->program, policy->features);
- }
-
- free(program);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
-
-struct cmd_results *cmd_reject(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) {
- return error;
- }
- if ((error = check_security_config())) {
- return error;
- }
-
- char *program = NULL;
- if (!strcmp(argv[0], "*")) {
- program = strdup(argv[0]);
- } else {
- program = resolve_path(argv[0]);
- }
- if (!program) {
- // Punt
- sway_log(L_INFO, "Unable to resolve IPC reject target '%s'."
- " Will use provided path", argv[0]);
- program = strdup(argv[0]);
- }
-
- struct feature_policy *policy = get_feature_policy(program);
- policy->features &= ~get_features(argc, argv, &error);
-
- sway_log(L_DEBUG, "Permissions granted to %s for features %d",
- policy->program, policy->features);
-
- free(program);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
#include "sway/commands.h"
#include "sway/config.h"
-#include "sway/layout.h"
+#include "sway/tree/layout.h"
struct cmd_results *cmd_reload(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "reload", "Can't be used in config file.");
if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
return error;
}
@@ -13,7 +12,6 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
}
load_swaybars();
-
arrange_windows(&root_container, -1, -1);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <stdlib.h>
#include <string.h>
#include <strings.h>
-#include <wlc/wlc.h>
+#include <wlr/util/log.h>
#include "sway/commands.h"
-#include "sway/layout.h"
-#include "sway/focus.h"
-#include "sway/input_state.h"
-#include "sway/handlers.h"
+#include "sway/tree/layout.h"
#include "log.h"
-enum resize_dim_types {
- RESIZE_DIM_PX,
- RESIZE_DIM_PPT,
- RESIZE_DIM_DEFAULT,
-};
-
-static bool set_size_floating(int new_dimension, bool use_width) {
- swayc_t *view = current_container;
- if (view) {
- if (use_width) {
- int current_width = view->width;
- view->desired_width = new_dimension;
- floating_view_sane_size(view);
-
- int new_x = view->x + (int)(((view->desired_width - current_width) / 2) * -1);
- view->width = view->desired_width;
- view->x = new_x;
+static const int MIN_SANE_W = 100, MIN_SANE_H = 60;
- update_geometry(view);
- } else {
- int current_height = view->height;
- view->desired_height = new_dimension;
- floating_view_sane_size(view);
-
- int new_y = view->y + (int)(((view->desired_height - current_height) / 2) * -1);
- view->height = view->desired_height;
- view->y = new_y;
+enum resize_unit {
+ RESIZE_UNIT_PX,
+ RESIZE_UNIT_PPT,
+ RESIZE_UNIT_DEFAULT,
+ RESIZE_UNIT_INVALID,
+};
- update_geometry(view);
- }
+enum resize_axis {
+ RESIZE_AXIS_HORIZONTAL,
+ RESIZE_AXIS_VERTICAL,
+ RESIZE_AXIS_INVALID,
+};
- return true;
+static enum resize_unit parse_resize_unit(const char *unit) {
+ if (strcasecmp(unit, "px") == 0) {
+ return RESIZE_UNIT_PX;
}
-
- return false;
+ if (strcasecmp(unit, "ppt") == 0) {
+ return RESIZE_UNIT_PPT;
+ }
+ if (strcasecmp(unit, "default") == 0) {
+ return RESIZE_UNIT_DEFAULT;
+ }
+ return RESIZE_UNIT_INVALID;
}
-static bool resize_floating(int amount, bool use_width) {
- swayc_t *view = current_container;
-
- if (view) {
- if (use_width) {
- return set_size_floating(view->width + amount, true);
- } else {
- return set_size_floating(view->height + amount, false);
- }
+static enum resize_axis parse_resize_axis(const char *axis) {
+ if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) {
+ return RESIZE_AXIS_HORIZONTAL;
+ }
+ if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) {
+ return RESIZE_AXIS_VERTICAL;
}
+ return RESIZE_AXIS_INVALID;
+}
- return false;
+static int parallel_coord(struct sway_container *c, enum resize_axis a) {
+ return a == RESIZE_AXIS_HORIZONTAL ? c->x : c->y;
}
-static bool resize_tiled(int amount, bool use_width) {
- swayc_t *container = current_container;
- swayc_t *parent = container->parent;
- int idx_focused = 0;
- bool use_major = false;
- size_t nb_before = 0;
- size_t nb_after = 0;
-
- // 1. Identify a container ancestor that will allow the focused child to grow in the requested
- // direction.
- while (container->parent) {
- parent = container->parent;
- if ((parent->children && parent->children->length > 1)
- && (is_auto_layout(parent->layout)
- || (use_width ? parent->layout == L_HORIZ : parent->layout == L_VERT))) {
- // check if container has siblings that can provide/absorb the space needed for
- // the resize operation.
- use_major = use_width
- ? parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
- : parent->layout == L_AUTO_TOP || parent->layout == L_AUTO_BOTTOM;
- // Note: use_major will be false for L_HORIZ and L_VERT
-
- idx_focused = index_child(container);
- if (idx_focused < 0) {
- sway_log(L_ERROR, "Something weird is happening, child container not "
- "present in its parent's children list.");
- continue;
- }
- if (use_major) {
- nb_before = auto_group_index(parent, idx_focused);
- nb_after = auto_group_count(parent) - nb_before - 1;
- } else {
- nb_before = idx_focused - auto_group_start_index(parent, idx_focused);
- nb_after = auto_group_end_index(parent, idx_focused) - idx_focused - 1;
- sway_log(L_DEBUG, "+++ focused: %d, start: %d, end: %d, before: %d, after: %d",
- idx_focused,
- (int)auto_group_start_index(parent, idx_focused),
- (int)auto_group_end_index(parent, idx_focused),
- (int)nb_before, (int)nb_after);
+static int parallel_size(struct sway_container *c, enum resize_axis a) {
+ return a == RESIZE_AXIS_HORIZONTAL ? c->width : c->height;
+}
- }
- if (nb_before || nb_after) {
- break;
- }
- }
- container = parent; /* continue up the tree to the next ancestor */
- }
- if (parent == &root_container) {
- return true;
+static void resize_tiled(int amount, enum resize_axis axis) {
+ struct sway_container *parent = config->handler_context.current_container;
+ struct sway_container *focused = parent;
+ if (!parent) {
+ return;
}
- sway_log(L_DEBUG, "Found the proper parent: %p. It has %zu before conts, "
- "and %zu after conts", parent, nb_before, nb_after);
- // 2. Ensure that the resize operation will not make one of the resized containers drop
- // below the "sane" size threshold.
- bool valid = true;
- swayc_t *focused = parent->children->items[idx_focused];
- int start = use_major ? 0 : auto_group_start_index(parent, idx_focused);
- int end = use_major ? parent->children->length : auto_group_end_index(parent, idx_focused);
- sway_log(L_DEBUG, "Check children of container %p [%d,%d[", container, start, end);
- for (int i = start; i < end; ) {
- swayc_t *sibling = parent->children->items[i];
- double pixels = amount;
- bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y;
- bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y;
- if (is_before || is_after) {
- pixels = -pixels;
- pixels /= is_before ? nb_before : nb_after;
- if (nb_after != 0 && nb_before != 0) {
- pixels /= 2;
- }
- }
- 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);
- if (use_width ?
- sibling->width + pixels < min_sane_w :
- sibling->height + pixels < min_sane_h) {
- valid = false;
- sway_log(L_DEBUG, "Container size no longer sane");
- break;
- }
- i = use_major ? auto_group_end_index(parent, i) : (i + 1);
- sway_log(L_DEBUG, "+++++ check %i", i);
- }
- // 3. Apply the size change
- if (valid) {
- for (int i = start; i < end; ) {
- int next_i = use_major ? auto_group_end_index(parent, i) : (i + 1);
- swayc_t *sibling = parent->children->items[i];
- double pixels = amount;
- bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y;
- bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y;
- if (is_before || is_after) {
- pixels = -pixels;
- pixels /= is_before ? nb_before : nb_after;
- if (nb_after != 0 && nb_before != 0) {
- pixels /= 2;
- }
- sway_log(L_DEBUG, "%p: %s", sibling, is_before ? "before" : "after");
- if (use_major) {
- for (int j = i; j < next_i; ++j) {
- recursive_resize(parent->children->items[j], pixels,
- use_width ?
- (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
- (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
- }
- } else {
- recursive_resize(sibling, pixels,
- use_width ?
- (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
- (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
- }
- } else {
- if (use_major) {
- for (int j = i; j < next_i; ++j) {
- recursive_resize(parent->children->items[j], pixels / 2,
- use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
- recursive_resize(parent->children->items[j], pixels / 2,
- use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
+
+ enum sway_container_layout parallel_layout =
+ axis == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT;
+ int minor_weight = 0;
+ int major_weight = 0;
+ while (parent->parent) {
+ struct sway_container *next = parent->parent;
+ if (next->layout == parallel_layout) {
+ for (int i = 0; i < next->children->length; i++) {
+ struct sway_container *sibling = next->children->items[i];
+
+ int sibling_pos = parallel_coord(sibling, axis);
+ int focused_pos = parallel_coord(focused, axis);
+ int parent_pos = parallel_coord(parent, axis);
+
+ if (sibling_pos != focused_pos) {
+ if (sibling_pos < parent_pos) {
+ minor_weight++;
+ } else if (sibling_pos > parent_pos) {
+ major_weight++;
}
- } else {
- recursive_resize(sibling, pixels / 2,
- use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
- recursive_resize(sibling, pixels / 2,
- use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
}
}
- i = next_i;
+ if (major_weight || minor_weight) {
+ break;
+ }
}
- // Recursive resize does not handle positions, let arrange_windows
- // take care of that.
- arrange_windows(swayc_active_workspace(), -1, -1);
+ parent = next;
}
- return true;
-}
-
-static bool set_size_tiled(int amount, bool use_width) {
- int desired;
- swayc_t *focused = current_container;
- if (use_width) {
- desired = amount - focused->width;
- } else {
- desired = amount - focused->height;
+ if (parent->type == C_ROOT) {
+ return;
}
- return resize_tiled(desired, use_width);
-}
-
-static bool set_size(int dimension, bool use_width) {
- swayc_t *focused = current_container;
+ wlr_log(L_DEBUG,
+ "Found the proper parent: %p. It has %d l conts, and %d r conts",
+ parent->parent, minor_weight, major_weight);
- if (focused) {
- if (focused->is_floating) {
- return set_size_floating(dimension, use_width);
- } else {
- return set_size_tiled(dimension, use_width);
- }
- }
+ int min_sane = axis == RESIZE_AXIS_HORIZONTAL ? MIN_SANE_W : MIN_SANE_H;
- return false;
-}
+ //TODO: Ensure rounding is done in such a way that there are NO pixel leaks
+ // ^ ?????
-static bool resize(int dimension, bool use_width, enum resize_dim_types dim_type) {
- swayc_t *focused = current_container;
+ for (int i = 0; i < parent->parent->children->length; i++) {
+ struct sway_container *sibling = parent->parent->children->items[i];
- // translate "10 ppt" (10%) to appropriate # of pixels in case we need it
- float ppt_dim = (float)dimension / 100;
+ int sibling_pos = parallel_coord(sibling, axis);
+ int focused_pos = parallel_coord(focused, axis);
+ int parent_pos = parallel_coord(parent, axis);
- if (use_width) {
- ppt_dim = focused->width * ppt_dim;
- } else {
- ppt_dim = focused->height * ppt_dim;
- }
+ int sibling_size = parallel_size(sibling, axis);
+ int parent_size = parallel_size(parent, axis);
- if (focused) {
- if (focused->is_floating) {
- // floating view resize dimensions should default to px, so only
- // use ppt if specified
- if (dim_type == RESIZE_DIM_PPT) {
- dimension = (int)ppt_dim;
+ if (sibling_pos != focused_pos) {
+ if (sibling_pos < parent_pos) {
+ double pixels = -amount / minor_weight;
+ if (major_weight && (sibling_size + pixels / 2) < min_sane) {
+ return; // Too small
+ } else if ((sibling_size + pixels) < min_sane) {
+ return; // Too small
+ }
+ } else if (sibling_pos > parent_pos) {
+ double pixels = -amount / major_weight;
+ if (minor_weight && (sibling_size + pixels / 2) < min_sane) {
+ return; // Too small
+ } else if ((sibling_size + pixels) < min_sane) {
+ return; // Too small
+ }
}
-
- return resize_floating(dimension, use_width);
} else {
- // tiled view resize dimensions should default to ppt, so only use
- // px if specified
- if (dim_type != RESIZE_DIM_PX) {
- dimension = (int)ppt_dim;
+ double pixels = amount;
+ if (parent_size + pixels < min_sane) {
+ return; // Too small
}
-
- return resize_tiled(dimension, use_width);
}
}
- return false;
-}
+ enum resize_edge minor_edge = axis == RESIZE_AXIS_HORIZONTAL ?
+ RESIZE_EDGE_LEFT : RESIZE_EDGE_TOP;
+ enum resize_edge major_edge = axis == RESIZE_AXIS_HORIZONTAL ?
+ RESIZE_EDGE_RIGHT : RESIZE_EDGE_BOTTOM;
-static struct cmd_results *cmd_resize_set(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "resize set", EXPECTED_AT_LEAST, 2))) {
- return error;
- }
+ for (int i = 0; i < parent->parent->children->length; i++) {
+ struct sway_container *sibling = parent->parent->children->items[i];
- if (strcasecmp(argv[0], "width") == 0 || strcasecmp(argv[0], "height") == 0) {
- // handle `reset set width 100 px height 100 px` syntax, also allows
- // specifying only one dimension for a `resize set`
- int cmd_num = 0;
- int dim;
-
- while ((cmd_num + 1) < argc) {
- dim = (int)strtol(argv[cmd_num + 1], NULL, 10);
- if (errno == ERANGE || dim == 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "resize set",
- "Expected 'resize set <width|height> <amount> [px] [<width|height> <amount> [px]]'");
- }
+ int sibling_pos = parallel_coord(sibling, axis);
+ int focused_pos = parallel_coord(focused, axis);
+ int parent_pos = parallel_coord(parent, axis);
- if (strcasecmp(argv[cmd_num], "width") == 0) {
- set_size(dim, true);
- } else if (strcasecmp(argv[cmd_num], "height") == 0) {
- set_size(dim, false);
- } else {
- return cmd_results_new(CMD_INVALID, "resize set",
- "Expected 'resize set <width|height> <amount> [px] [<width|height> <amount> [px]]'");
+ if (sibling_pos != focused_pos) {
+ if (sibling_pos < parent_pos) {
+ double pixels = -1 * amount;
+ pixels /= minor_weight;
+ if (major_weight) {
+ container_recursive_resize(sibling, pixels / 2, major_edge);
+ } else {
+ container_recursive_resize(sibling, pixels, major_edge);
+ }
+ } else if (sibling_pos > parent_pos) {
+ double pixels = -1 * amount;
+ pixels /= major_weight;
+ if (minor_weight) {
+ container_recursive_resize(sibling, pixels / 2, minor_edge);
+ } else {
+ container_recursive_resize(sibling, pixels, minor_edge);
+ }
}
-
- cmd_num += 2;
-
- if (cmd_num < argc && strcasecmp(argv[cmd_num], "px") == 0) {
- // if this was `resize set width 400 px height 300 px`, disregard the `px` arg
- cmd_num++;
+ } else {
+ if (major_weight != 0 && minor_weight != 0) {
+ double pixels = amount;
+ pixels /= 2;
+ container_recursive_resize(parent, pixels, minor_edge);
+ container_recursive_resize(parent, pixels, major_edge);
+ } else if (major_weight) {
+ container_recursive_resize(parent, amount, major_edge);
+ } else if (minor_weight) {
+ container_recursive_resize(parent, amount, minor_edge);
}
}
- } else {
- // handle `reset set 100 px 100 px` syntax
- int width = (int)strtol(argv[0], NULL, 10);
- if (errno == ERANGE || width == 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "resize set",
- "Expected 'resize set <width> [px] <height> [px]'");
- }
+ }
- int height_arg = 1;
- if (strcasecmp(argv[1], "px") == 0) {
- height_arg = 2;
- }
+ arrange_windows(parent->parent, -1, -1);
+}
- int height = (int)strtol(argv[height_arg], NULL, 10);
- if (errno == ERANGE || height == 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "resize set",
- "Expected 'resize set <width> [px] <height> [px]'");
- }
+static void resize(int amount, enum resize_axis axis, enum resize_unit unit) {
+ struct sway_container *current = config->handler_context.current_container;
+ if (unit == RESIZE_UNIT_DEFAULT) {
+ // Default for tiling; TODO floating should be px
+ unit = RESIZE_UNIT_PPT;
+ }
- set_size(width, true);
- set_size(height, false);
+ if (unit == RESIZE_UNIT_PPT) {
+ float pct = amount / 100.0f;
+ switch (axis) {
+ case RESIZE_AXIS_HORIZONTAL:
+ amount = (float)current->width * pct;
+ break;
+ case RESIZE_AXIS_VERTICAL:
+ amount = (float)current->height * pct;
+ break;
+ default:
+ sway_assert(0, "invalid resize axis");
+ return;
+ }
}
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ return resize_tiled(amount, axis);
}
struct cmd_results *cmd_resize(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "resize", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "resize", "Can only be used when sway is running.");
-
+ struct sway_container *current = config->handler_context.current_container;
+ if (!current) {
+ return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing");
+ }
+ if (current->type != C_VIEW && current->type != C_CONTAINER) {
+ return cmd_results_new(CMD_INVALID, "resize",
+ "Can only resize views/containers");
+ }
if (strcasecmp(argv[0], "set") == 0) {
- return cmd_resize_set(argc - 1, &argv[1]);
+ // TODO
+ //return cmd_resize_set(argc - 1, &argv[1]);
+ return cmd_results_new(CMD_INVALID, "resize", "resize set unimplemented");
}
-
+ struct cmd_results *error;
if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
return error;
}
- int dim_arg = argc - 1;
+ // TODO: resize grow|shrink left|right|up|down
- enum resize_dim_types dim_type = RESIZE_DIM_DEFAULT;
- if (strcasecmp(argv[dim_arg], "ppt") == 0) {
- dim_type = RESIZE_DIM_PPT;
- dim_arg--;
- } else if (strcasecmp(argv[dim_arg], "px") == 0) {
- dim_type = RESIZE_DIM_PX;
- dim_arg--;
+ const char *usage = "Expected 'resize <shrink|grow> "
+ "<width|height> [<amount>] [px|ppt]'";
+
+ int multiplier = 0;
+ if (strcasecmp(*argv, "grow") == 0) {
+ multiplier = 1;
+ } else if (strcasecmp(*argv, "shrink") == 0) {
+ multiplier = -1;
+ } else {
+ return cmd_results_new(CMD_INVALID, "resize", usage);
}
+ --argc; ++argv;
- int amount = (int)strtol(argv[dim_arg], NULL, 10);
- if (errno == ERANGE || amount == 0) {
- errno = 0;
- amount = 10; // this is the default resize dimension used by i3 for both px and ppt
- sway_log(L_DEBUG, "Tried to get resize dimension out of '%s' but failed; setting dimension to default %d",
- argv[dim_arg], amount);
+ enum resize_axis axis = parse_resize_axis(*argv);
+ if (axis == RESIZE_AXIS_INVALID) {
+ return cmd_results_new(CMD_INVALID, "resize", usage);
+ }
+ --argc; ++argv;
+
+ int amount = 10; // Default amount
+ enum resize_unit unit = RESIZE_UNIT_DEFAULT;
+
+ if (argc) {
+ char *err;
+ amount = (int)strtol(*argv, &err, 10);
+ if (*err) {
+ // e.g. `resize grow width 10px`
+ unit = parse_resize_unit(err);
+ if (unit == RESIZE_UNIT_INVALID) {
+ return cmd_results_new(CMD_INVALID, "resize", usage);
+ }
+ }
+ --argc; ++argv;
}
- bool use_width = false;
- if (strcasecmp(argv[1], "width") == 0) {
- use_width = true;
- } else if (strcasecmp(argv[1], "height") != 0) {
- return cmd_results_new(CMD_INVALID, "resize",
- "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'");
+ if (argc) {
+ unit = parse_resize_unit(*argv);
+ if (unit == RESIZE_UNIT_INVALID) {
+ return cmd_results_new(CMD_INVALID, "resize", usage);
+ }
+ --argc; ++argv;
}
- if (strcasecmp(argv[0], "shrink") == 0) {
- amount *= -1;
- } else if (strcasecmp(argv[0], "grow") != 0) {
- return cmd_results_new(CMD_INVALID, "resize",
- "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'");
+ if (argc) {
+ // Provied too many args, the bastard
+ return cmd_results_new(CMD_INVALID, "resize", usage);
}
- resize(amount, use_width, dim_type);
+ resize(amount * multiplier, axis, unit);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
-#include <string.h>
-#include <strings.h>
-#include <wlc/wlc.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-#include "sway/layout.h"
-
-static swayc_t *fetch_view_from_scratchpad() {
- sp_index = (sp_index + 1) % scratchpad->length;
- swayc_t *view = scratchpad->items[sp_index];
-
- if (wlc_view_get_output(view->handle) != swayc_active_output()->handle) {
- wlc_view_set_output(view->handle, swayc_active_output()->handle);
- }
- if (!view->is_floating) {
- view->width = swayc_active_workspace()->width * 0.5;
- view->height = swayc_active_workspace()->height * 0.75;
- view->x = (swayc_active_workspace()->width - view->width)/2;
- view->y = (swayc_active_workspace()->height - view->height)/2;
- }
- if (swayc_active_workspace()->width < view->x + 20 || view->x + view->width < 20) {
- view->x = (swayc_active_workspace()->width - view->width)/2;
- }
- if (swayc_active_workspace()->height < view->y + 20 || view->y + view->height < 20) {
- view->y = (swayc_active_workspace()->height - view->height)/2;
- }
-
- add_floating(swayc_active_workspace(), view);
- wlc_view_set_mask(view->handle, VISIBLE);
- view->visible = true;
- arrange_windows(swayc_active_workspace(), -1, -1);
- set_focused_container(view);
- return view;
-}
-
-struct cmd_results *cmd_scratchpad(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can only be used when sway is running.");
- if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (strcasecmp(argv[0], "show") == 0 && scratchpad->length > 0) {
- if (!sp_view) {
- if (current_container) {
- // Haxor the scratchpad index if criteria'd
- for (int i = 0; i < scratchpad->length; ++i) {
- if (scratchpad->items[i] == current_container) {
- sp_index = (i - 1) % scratchpad->length;
- }
- }
- }
- sp_view = fetch_view_from_scratchpad();
- } else {
- if (swayc_active_workspace() != sp_view->parent) {
- hide_view_in_scratchpad(sp_view);
- if (sp_index == 0) {
- sp_index = scratchpad->length;
- }
- sp_index--;
- sp_view = fetch_view_from_scratchpad();
- } else {
- hide_view_in_scratchpad(sp_view);
- sp_view = NULL;
- }
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- return cmd_results_new(CMD_FAILURE, "scratchpad", "Expected 'scratchpad show' when scratchpad is not empty.");
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_seamless_mouse(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "seamless_mouse", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- config->seamless_mouse = (strcasecmp(argv[0], "on") == 0 || strcasecmp(argv[0], "yes") == 0);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *cmd_seat(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 2))) {
+ return error;
+ }
+
+ if (config->reading && strcmp("{", argv[1]) == 0) {
+ free_seat_config(config->handler_context.seat_config);
+ config->handler_context.seat_config = new_seat_config(argv[0]);
+ if (!config->handler_context.seat_config) {
+ return cmd_results_new(CMD_FAILURE, NULL,
+ "Couldn't allocate config");
+ }
+ wlr_log(L_DEBUG, "entering seat block: %s", argv[0]);
+ return cmd_results_new(CMD_BLOCK_SEAT, NULL, NULL);
+ }
+
+ if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 3))) {
+ return error;
+ }
+
+ bool has_context = (config->handler_context.seat_config != NULL);
+ if (!has_context) {
+ config->handler_context.seat_config = new_seat_config(argv[0]);
+ if (!config->handler_context.seat_config) {
+ return cmd_results_new(CMD_FAILURE, NULL,
+ "Couldn't allocate config");
+ }
+ }
+
+ int argc_new = argc-2;
+ char **argv_new = argv+2;
+
+ struct cmd_results *res;
+ if (strcasecmp("attach", argv[1]) == 0) {
+ res = seat_cmd_attach(argc_new, argv_new);
+ } else if (strcasecmp("cursor", argv[1]) == 0) {
+ res = seat_cmd_cursor(argc_new, argv_new);
+ } else if (strcasecmp("fallback", argv[1]) == 0) {
+ res = seat_cmd_fallback(argc_new, argv_new);
+ } else {
+ res =
+ cmd_results_new(CMD_INVALID,
+ "seat <name>", "Unknown command %s",
+ argv[1]);
+ }
+
+ if (!has_context) {
+ // clean up the context we created earlier
+ free_seat_config(config->handler_context.seat_config);
+ config->handler_context.seat_config = NULL;
+ }
+
+ return res;
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include <string.h>
+#include <strings.h>
+#include "sway/input/input-manager.h"
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "log.h"
+#include "stringop.h"
+
+struct cmd_results *seat_cmd_attach(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "attach", EXPECTED_AT_LEAST, 1))) {
+ return error;
+ }
+ struct seat_config *current_seat_config =
+ config->handler_context.seat_config;
+ if (!current_seat_config) {
+ return cmd_results_new(CMD_FAILURE, "attach", "No seat defined");
+ }
+
+ struct seat_config *new_config = new_seat_config(current_seat_config->name);
+ struct seat_attachment_config *new_attachment = seat_attachment_config_new();
+ new_attachment->identifier = strdup(argv[0]);
+ list_add(new_config->attachments, new_attachment);
+
+ apply_seat_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
+
+#include <strings.h>
+#include <wlr/types/wlr_cursor.h>
+#include "sway/commands.h"
+#include "sway/input/cursor.h"
+
+static struct cmd_results *press_or_release(struct sway_cursor *cursor,
+ char *action, char *button_str, uint32_t time);
+
+static const char *expected_syntax = "Expected 'cursor <move> <x> <y>' or "
+ "'cursor <set> <x> <y>' or "
+ "'curor <press|release> <left|right|1|2|3...>'";
+
+struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "cursor", EXPECTED_AT_LEAST, 2))) {
+ return error;
+ }
+ struct sway_seat *seat = config->handler_context.seat;
+ if (!seat) {
+ return cmd_results_new(CMD_FAILURE, "cursor", "No seat defined");
+ }
+
+ struct sway_cursor *cursor = seat->cursor;
+
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ uint32_t time = now.tv_nsec / 1000;
+
+ if (strcasecmp(argv[0], "move") == 0) {
+ if (argc < 3) {
+ return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
+ }
+ int delta_x = strtol(argv[1], NULL, 10);
+ int delta_y = strtol(argv[2], NULL, 10);
+ wlr_cursor_move(cursor->cursor, NULL, delta_x, delta_y);
+ cursor_send_pointer_motion(cursor, time);
+ } else if (strcasecmp(argv[0], "set") == 0) {
+ if (argc < 3) {
+ return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
+ }
+ // map absolute coords (0..1,0..1) to root container coords
+ float x = strtof(argv[1], NULL) / root_container.width;
+ float y = strtof(argv[2], NULL) / root_container.height;
+ wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y);
+ cursor_send_pointer_motion(cursor, time);
+ } else {
+ if (argc < 2) {
+ return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
+ }
+ if ((error = press_or_release(cursor, argv[0], argv[1], time))) {
+ return error;
+ }
+ }
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
+
+static struct cmd_results *press_or_release(struct sway_cursor *cursor,
+ char *action, char *button_str, uint32_t time) {
+ enum wlr_button_state state;
+ uint32_t button;
+ if (strcasecmp(action, "press") == 0) {
+ state = WLR_BUTTON_PRESSED;
+ } else if (strcasecmp(action, "release") == 0) {
+ state = WLR_BUTTON_RELEASED;
+ } else {
+ return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
+ }
+
+ if (strcasecmp(button_str, "left") == 0) {
+ button = BTN_LEFT;
+ } else if (strcasecmp(button_str, "right") == 0) {
+ button = BTN_RIGHT;
+ } else {
+ button = strtol(button_str, NULL, 10);
+ if (button == 0) {
+ return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
+ }
+ }
+ dispatch_cursor_button(cursor, time, button, state);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *seat_cmd_fallback(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "fallback", EXPECTED_AT_LEAST, 1))) {
+ return error;
+ }
+ struct seat_config *current_seat_config =
+ config->handler_context.seat_config;
+ if (!current_seat_config) {
+ return cmd_results_new(CMD_FAILURE, "fallback", "No seat defined");
+ }
+ struct seat_config *new_config =
+ new_seat_config(current_seat_config->name);
+
+ if (strcasecmp(argv[0], "true") == 0) {
+ new_config->fallback = 1;
+ } else if (strcasecmp(argv[0], "false") == 0) {
+ new_config->fallback = 0;
+ } else {
+ free_seat_config(new_config);
+ return cmd_results_new(CMD_INVALID, "fallback",
+ "Expected 'fallback <true|false>'");
+ }
+
+ apply_seat_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
#include "sway/commands.h"
#include "sway/config.h"
#include "list.h"
+#include "log.h"
#include "stringop.h"
// sort in order of longest->shortest
@@ -14,16 +15,24 @@ static int compare_set_qsort(const void *_l, const void *_r) {
return strlen(r->name) - strlen(l->name);
}
+void free_sway_variable(struct sway_variable *var) {
+ if (!var) {
+ return;
+ }
+ free(var->name);
+ free(var->value);
+ free(var);
+}
+
struct cmd_results *cmd_set(int argc, char **argv) {
char *tmp;
struct cmd_results *error = NULL;
- if (!config->reading) return cmd_results_new(CMD_FAILURE, "set", "Can only be used in config file.");
if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) {
return error;
}
if (argv[0][0] != '$') {
- sway_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]);
+ wlr_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]);
size_t size = snprintf(NULL, 0, "$%s", argv[0]);
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_show_marks(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "show_marks", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- config->show_marks = !strcasecmp(argv[0], "on");
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "smart_gaps", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (strcasecmp(argv[0], "on") == 0) {
- config->smart_gaps = true;
- } else if (strcasecmp(argv[0], "off") == 0) {
- config->smart_gaps = false;
- } else {
- return cmd_results_new(CMD_INVALID, "smart_gaps", "Expected 'smart_gaps <on|off>'");
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
#include <string.h>
#include <strings.h>
-#include <wlc/wlc-render.h>
-#include "sway/border.h"
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-#include "sway/layout.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/view.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
#include "log.h"
-static struct cmd_results *_do_split(int argc, char **argv, int layout) {
- char *name = layout == L_VERT ? "splitv" :
- layout == L_HORIZ ? "splith" : "split";
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, name, "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, name, "Can only be used when sway is running.");
- if ((error = checkarg(argc, name, EXPECTED_EQUAL_TO, 0))) {
- return error;
- }
- swayc_t *focused = current_container;
-
- // Case of floating window, don't split
- if (focused->is_floating) {
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- /* Case that focus is on an workspace with 0/1 children.change its layout */
- if (focused->type == C_WORKSPACE && focused->children->length <= 1) {
- sway_log(L_DEBUG, "changing workspace layout");
- swayc_change_layout(focused, layout);
- } else if (focused->type != C_WORKSPACE && focused->parent->children->length == 1) {
- /* Case of no siblings. change parent layout */
- sway_log(L_DEBUG, "changing container layout");
- swayc_change_layout(focused->parent, layout);
- } else {
- /* regular case where new split container is build around focused container
- * or in case of workspace, container inherits its children */
- sway_log(L_DEBUG, "Adding new container around current focused container");
- sway_log(L_INFO, "FOCUSED SIZE: %.f %.f", focused->width, focused->height);
- swayc_t *parent = new_container(focused, layout);
- set_focused_container(focused);
- arrange_windows(parent, -1, -1);
- }
-
- // update container every time
- // if it is tabbed/stacked then the title must change
- // if the indicator color is different then the border must change
- update_container_border(focused);
- swayc_t *output = swayc_parent_by_type(focused, C_OUTPUT);
- // schedule render to make changes take effect right away,
- // otherwise we would have to wait for the view to render,
- // which is unpredictable.
- wlc_output_schedule_render(output->handle);
+static struct cmd_results *do_split(int layout) {
+ struct sway_container *con = config->handler_context.current_container;
+ struct sway_container *parent = container_split(con, layout);
+ container_create_notify(parent);
+ arrange_windows(parent, -1, -1);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
struct cmd_results *cmd_split(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "split", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "split", "Can only be used when sway is running.");
if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
return error;
}
if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) {
- _do_split(argc - 1, argv + 1, L_VERT);
- } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) {
- _do_split(argc - 1, argv + 1, L_HORIZ);
- } else if (strcasecmp(argv[0], "t") == 0 || strcasecmp(argv[0], "toggle") == 0) {
- swayc_t *focused = current_container;
+ do_split(L_VERT);
+ } else if (strcasecmp(argv[0], "h") == 0 ||
+ strcasecmp(argv[0], "horizontal") == 0) {
+ do_split(L_HORIZ);
+ } else if (strcasecmp(argv[0], "t") == 0 ||
+ strcasecmp(argv[0], "toggle") == 0) {
+ struct sway_container *focused =
+ config->handler_context.current_container;
+
if (focused->parent->layout == L_VERT) {
- _do_split(argc - 1, argv + 1, L_HORIZ);
+ do_split(L_HORIZ);
} else {
- _do_split(argc - 1, argv + 1, L_VERT);
+ do_split(L_VERT);
}
} else {
error = cmd_results_new(CMD_FAILURE, "split",
@@ -81,18 +46,32 @@ struct cmd_results *cmd_split(int argc, char **argv) {
}
struct cmd_results *cmd_splitv(int argc, char **argv) {
- return _do_split(argc, argv, L_VERT);
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
+ return error;
+ }
+ return do_split(L_VERT);
}
struct cmd_results *cmd_splith(int argc, char **argv) {
- return _do_split(argc, argv, L_HORIZ);
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
+ return error;
+ }
+ return do_split(L_HORIZ);
}
struct cmd_results *cmd_splitt(int argc, char **argv) {
- swayc_t *focused = current_container;
- if (focused->parent->layout == L_VERT) {
- return _do_split(argc, argv, L_HORIZ);
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
+ return error;
+ }
+
+ struct sway_container *con = config->handler_context.current_container;
+
+ if (con->parent->layout == L_VERT) {
+ return do_split(L_HORIZ);
} else {
- return _do_split(argc, argv, L_VERT);
+ return do_split(L_VERT);
}
}
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 @@
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/focus.h"
-
-struct cmd_results *cmd_sticky(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "sticky", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "sticky", "Can only be used when sway is running.");
- if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
- char *action = argv[0];
- swayc_t *cont = get_focused_view(&root_container);
- if (strcmp(action, "toggle") == 0) {
- cont->sticky = !cont->sticky;
- } else if (strcmp(action, "enable") == 0) {
- cont->sticky = true;
- } else if (strcmp(action, "disable") == 0) {
- cont->sticky = false;
- } else {
- return cmd_results_new(CMD_FAILURE, "sticky",
- "Expected 'sticky enable|disable|toggle'");
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
+#include <string.h>
+#include "sway/commands.h"
+#include "log.h"
+#include "stringop.h"
+
+struct cmd_results *cmd_swaybg_command(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "swaybg_command", EXPECTED_AT_LEAST, 1))) {
+ return error;
+ }
+
+ if (config->swaybg_command) {
+ free(config->swaybg_command);
+ }
+ config->swaybg_command = join_args(argv, argc);
+ wlr_log(L_DEBUG, "Using custom swaybg command: %s",
+ config->swaybg_command);
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "list.h"
-#include "stringop.h"
-
-struct cmd_results *cmd_unmark(int argc, char **argv) {
- swayc_t *view = current_container;
-
- if (view->marks) {
- if (argc) {
- char *mark = join_args(argv, argc);
- int index;
- if ((index = list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1) {
- free(view->marks->items[index]);
- list_del(view->marks, index);
-
- if (view->marks->length == 0) {
- list_free(view->marks);
- view->marks = NULL;
- }
- }
- free(mark);
- } else {
- list_foreach(view->marks, free);
- list_free(view->marks);
- view->marks = NULL;
- }
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
#include <strings.h>
#include "sway/commands.h"
#include "sway/config.h"
-#include "sway/input_state.h"
-#include "sway/workspace.h"
+#include "sway/input/seat.h"
+#include "sway/tree/workspace.h"
#include "list.h"
#include "log.h"
#include "stringop.h"
@@ -17,6 +17,17 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
int output_location = -1;
+ struct sway_container *current_container = config->handler_context.current_container;
+ struct sway_container *old_workspace = NULL, *old_output = NULL;
+ if (current_container) {
+ if (current_container->type == C_WORKSPACE) {
+ old_workspace = current_container;
+ } else {
+ old_workspace = container_parent(current_container, C_WORKSPACE);
+ }
+ old_output = container_parent(current_container, C_OUTPUT);
+ }
+
for (int i = 0; i < argc; ++i) {
if (strcasecmp(argv[i], "output") == 0) {
output_location = i;
@@ -40,52 +51,51 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
free(old); // workspaces can only be assigned to a single output
list_del(config->workspace_outputs, i);
}
- sway_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output);
+ wlr_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output);
list_add(config->workspace_outputs, wso);
} else {
if (config->reading || !config->active) {
return cmd_results_new(CMD_DEFER, "workspace", NULL);
}
- swayc_t *ws = NULL;
+ struct sway_container *ws = NULL;
if (strcasecmp(argv[0], "number") == 0) {
if (!(ws = workspace_by_number(argv[1]))) {
char *name = join_args(argv + 1, argc - 1);
- ws = workspace_create(name);
+ ws = workspace_create(NULL, name);
free(name);
}
} else if (strcasecmp(argv[0], "next") == 0) {
- ws = workspace_next();
+ ws = workspace_next(old_workspace);
} else if (strcasecmp(argv[0], "prev") == 0) {
- ws = workspace_prev();
+ ws = workspace_prev(old_workspace);
} else if (strcasecmp(argv[0], "next_on_output") == 0) {
- ws = workspace_output_next();
+ ws = workspace_output_next(old_output);
} else if (strcasecmp(argv[0], "prev_on_output") == 0) {
- ws = workspace_output_prev();
+ ws = workspace_output_prev(old_output);
} else if (strcasecmp(argv[0], "back_and_forth") == 0) {
// if auto_back_and_forth is enabled, workspace_switch will swap
// the workspaces. If we created prev_workspace here, workspace_switch
// would put us back on original workspace.
if (config->auto_back_and_forth) {
- ws = swayc_active_workspace();
- } else if (prev_workspace_name && !(ws = workspace_by_name(prev_workspace_name))) {
- ws = workspace_create(prev_workspace_name);
+ ws = old_workspace;
+ } else if (prev_workspace_name
+ && !(ws = workspace_by_name(prev_workspace_name))) {
+ ws = workspace_create(NULL, prev_workspace_name);
}
} else {
char *name = join_args(argv, argc);
if (!(ws = workspace_by_name(name))) {
- ws = workspace_create(name);
+ ws = workspace_create(NULL, name);
}
free(name);
}
- swayc_t *old_output = swayc_active_output();
workspace_switch(ws);
- swayc_t *new_output = swayc_active_output();
+ current_container =
+ seat_get_focus(config->handler_context.seat);
+ struct sway_container *new_output = container_parent(current_container, C_OUTPUT);
if (config->mouse_warping && old_output != new_output) {
- swayc_t *focused = get_focused_view(ws);
- if (focused && focused->type == C_VIEW) {
- center_pointer_on(focused);
- }
+ // TODO: Warp mouse
}
}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_workspace_layout(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "workspace_layout", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- if (strcasecmp(argv[0], "default") == 0) {
- config->default_layout = L_NONE;
- } else if (strcasecmp(argv[0], "stacking") == 0) {
- config->default_layout = L_STACKED;
- } else if (strcasecmp(argv[0], "tabbed") == 0) {
- config->default_layout = L_TABBED;
- } else if (strcasecmp(argv[0], "auto") == 0) {
- if (argc == 1) {
- config->default_layout = L_AUTO_FIRST;
- } else {
- if ((error = checkarg(argc, "workspace_layout auto", EXPECTED_EQUAL_TO, 2))) {
- return error;
- }
- if (strcasecmp(argv[1], "left") == 0) {
- config->default_layout = L_AUTO_LEFT;
- } else if (strcasecmp(argv[1], "right") == 0) {
- config->default_layout = L_AUTO_RIGHT;
- } else if (strcasecmp(argv[1], "top") == 0) {
- config->default_layout = L_AUTO_TOP;
- } else if (strcasecmp(argv[1], "bottom") == 0) {
- config->default_layout = L_AUTO_BOTTOM;
- } else {
- return cmd_results_new(CMD_INVALID, "workspace_layout auto", "Expected 'workspace_layout auto <left|right|top|bottom>'");
- }
- }
- } else {
- return cmd_results_new(CMD_INVALID, "workspace_layout", "Expected 'workspace_layout <default|stacking|tabbed|auto|auto left|auto right|auto top|auto bottom>'");
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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) {
if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) {
return error;
}
- if (strcasecmp(argv[0], "yes") == 0) {
- config->auto_back_and_forth = true;
- } else if (strcasecmp(argv[0], "no") == 0) {
- config->auto_back_and_forth = false;
- } else {
- return cmd_results_new(CMD_INVALID, "workspace_auto_back_and_forth", "Expected 'workspace_auto_back_and_forth <yes|no>'");
- }
+ config->auto_back_and_forth = !strcasecmp(argv[0], "yes");
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <signal.h>
#include <libinput.h>
#include <limits.h>
-#include <float.h>
#include <dirent.h>
#include <strings.h>
-#include "wayland-desktop-shell-server-protocol.h"
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
+#include <wlr/types/wlr_output.h>
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
#include "sway/commands.h"
#include "sway/config.h"
-#include "sway/layout.h"
-#include "sway/input_state.h"
-#include "sway/criteria.h"
-#include "sway/input.h"
-#include "sway/border.h"
+#include "sway/tree/layout.h"
#include "readline.h"
#include "stringop.h"
#include "list.h"
@@ -30,269 +32,102 @@
struct sway_config *config = NULL;
-static void terminate_swaybar(pid_t pid);
-
-static void free_variable(struct sway_variable *var) {
- if (!var) {
- return;
- }
- free(var->name);
- free(var->value);
- free(var);
-}
-
-static void free_binding(struct sway_binding *bind) {
- if (!bind) {
- return;
- }
- free_flat_list(bind->keys);
- free(bind->command);
- free(bind);
-}
-
static void free_mode(struct sway_mode *mode) {
- if (!mode) {
- return;
- }
- free(mode->name);
- int i;
- for (i = 0; mode->bindings && i < mode->bindings->length; ++i) {
- free_binding(mode->bindings->items[i]);
- }
- list_free(mode->bindings);
- free(mode);
-}
-
-static void free_bar(struct bar_config *bar) {
- if (!bar) {
- return;
- }
- free(bar->mode);
- free(bar->hidden_state);
-#ifdef ENABLE_TRAY
- free(bar->tray_output);
- free(bar->icon_theme);
-#endif
- free(bar->status_command);
- free(bar->font);
- free(bar->separator_symbol);
int i;
- for (i = 0; bar->bindings && i < bar->bindings->length; ++i) {
- free_sway_mouse_binding(bar->bindings->items[i]);
- }
- list_free(bar->bindings);
-
- if (bar->outputs) {
- free_flat_list(bar->outputs);
- }
-
- if (bar->pid != 0) {
- terminate_swaybar(bar->pid);
- }
-
- free(bar->colors.background);
- free(bar->colors.statusline);
- free(bar->colors.separator);
- free(bar->colors.focused_background);
- free(bar->colors.focused_statusline);
- free(bar->colors.focused_separator);
- free(bar->colors.focused_workspace_border);
- free(bar->colors.focused_workspace_bg);
- free(bar->colors.focused_workspace_text);
- free(bar->colors.active_workspace_border);
- free(bar->colors.active_workspace_bg);
- free(bar->colors.active_workspace_text);
- free(bar->colors.inactive_workspace_border);
- free(bar->colors.inactive_workspace_bg);
- free(bar->colors.inactive_workspace_text);
- free(bar->colors.urgent_workspace_border);
- free(bar->colors.urgent_workspace_bg);
- free(bar->colors.urgent_workspace_text);
- free(bar->colors.binding_mode_border);
- free(bar->colors.binding_mode_bg);
- free(bar->colors.binding_mode_text);
-
- free(bar);
-}
-void free_input_config(struct input_config *ic) {
- if (!ic) {
- return;
- }
- free(ic->identifier);
- free(ic);
-}
-
-void free_output_config(struct output_config *oc) {
- if (!oc) {
- return;
- }
- free(oc->name);
- free(oc->background);
- free(oc->background_option);
- free(oc);
-}
-
-static void free_workspace_output(struct workspace_output *wo) {
- if (!wo) {
+ if (!mode) {
return;
}
- free(wo->output);
- free(wo->workspace);
- free(wo);
-}
-
-static void pid_workspace_cleanup() {
- struct timespec ts;
- struct pid_workspace *pw = NULL;
-
- clock_gettime(CLOCK_MONOTONIC, &ts);
-
- // work backwards through list and remove any entries
- // older than PID_WORKSPACE_TIMEOUT
- for (int i = config->pid_workspaces->length - 1; i > -1; i--) {
- pw = config->pid_workspaces->items[i];
-
- if (difftime(ts.tv_sec, *pw->time_added) >= PID_WORKSPACE_TIMEOUT) {
- free_pid_workspace(config->pid_workspaces->items[i]);
- list_del(config->pid_workspaces, i);
+ free(mode->name);
+ if (mode->keysym_bindings) {
+ for (i = 0; i < mode->keysym_bindings->length; i++) {
+ free_sway_binding(mode->keysym_bindings->items[i]);
}
+ list_free(mode->keysym_bindings);
}
-}
-
-// de-dupe pid_workspaces to ensure pid uniqueness
-void pid_workspace_add(struct pid_workspace *pw) {
- struct pid_workspace *list_pw = NULL;
- struct timespec ts;
- time_t *now = malloc(sizeof(time_t));
- if (!now) {
- sway_log(L_ERROR, "Allocating time for pid_workspace failed");
- return;
- }
-
- pid_workspace_cleanup();
-
- // add current time to pw
- clock_gettime(CLOCK_MONOTONIC, &ts);
- *now = ts.tv_sec;
-
- pw->time_added = now;
-
- // work backwards through list and delete any entries that
- // have the same pid as that in our new pid_workspace
- for (int i = config->pid_workspaces->length - 1; i > -1; i--) {
- list_pw = config->pid_workspaces->items[i];
-
- if (pw->pid == list_pw->pid) {
- free_pid_workspace(config->pid_workspaces->items[i]);
- list_del(config->pid_workspaces, i);
+ if (mode->keycode_bindings) {
+ for (i = 0; i < mode->keycode_bindings->length; i++) {
+ free_sway_binding(mode->keycode_bindings->items[i]);
}
+ list_free(mode->keycode_bindings);
}
-
- list_add(config->pid_workspaces, pw);
-}
-
-void free_pid_workspace(struct pid_workspace *pw) {
- if (!pw) {
- return;
- }
- free(pw->pid);
- free(pw->workspace);
- free(pw->time_added);
- free(pw);
-}
-
-void free_command_policy(struct command_policy *policy) {
- if (!policy) {
- return;
- }
- free(policy->command);
- free(policy);
-}
-
-void free_feature_policy(struct feature_policy *policy) {
- if (!policy) {
- return;
- }
- free(policy->program);
- free(policy);
+ free(mode);
}
void free_config(struct sway_config *config) {
+ config_clear_handler_context(config);
+
if (!config) {
return;
}
- int i;
- for (i = 0; config->symbols && i < config->symbols->length; ++i) {
- free_variable(config->symbols->items[i]);
- }
- list_free(config->symbols);
- for (i = 0; config->modes && i < config->modes->length; ++i) {
- free_mode(config->modes->items[i]);
+ // TODO: handle all currently unhandled lists as we add implementations
+ if (config->symbols) {
+ for (int i = 0; i < config->symbols->length; ++i) {
+ free_sway_variable(config->symbols->items[i]);
+ }
+ list_free(config->symbols);
}
- list_free(config->modes);
-
- for (i = 0; config->bars && i < config->bars->length; ++i) {
- free_bar(config->bars->items[i]);
+ if (config->modes) {
+ for (int i = 0; i < config->modes->length; ++i) {
+ free_mode(config->modes->items[i]);
+ }
+ list_free(config->modes);
}
- list_free(config->bars);
-
- free_flat_list(config->cmd_queue);
-
- for (i = 0; config->workspace_outputs && i < config->workspace_outputs->length; ++i) {
- free_workspace_output(config->workspace_outputs->items[i]);
+ if (config->bars) {
+ for (int i = 0; i < config->bars->length; ++i) {
+ free_bar_config(config->bars->items[i]);
+ }
+ list_free(config->bars);
}
+ list_free(config->cmd_queue);
list_free(config->workspace_outputs);
-
- for (i = 0; config->pid_workspaces && i < config->pid_workspaces->length; ++i) {
- free_pid_workspace(config->pid_workspaces->items[i]);
- }
list_free(config->pid_workspaces);
-
- for (i = 0; config->criteria && i < config->criteria->length; ++i) {
- free_criteria(config->criteria->items[i]);
+ list_free(config->output_configs);
+ if (config->input_configs) {
+ for (int i = 0; i < config->input_configs->length; i++) {
+ free_input_config(config->input_configs->items[i]);
+ }
+ list_free(config->input_configs);
}
- list_free(config->criteria);
-
- for (i = 0; config->no_focus && i < config->no_focus->length; ++i) {
- free_criteria(config->no_focus->items[i]);
+ if (config->seat_configs) {
+ for (int i = 0; i < config->seat_configs->length; i++) {
+ free_seat_config(config->seat_configs->items[i]);
+ }
+ list_free(config->seat_configs);
}
+ list_free(config->criteria);
list_free(config->no_focus);
-
- for (i = 0; config->input_configs && i < config->input_configs->length; ++i) {
- free_input_config(config->input_configs->items[i]);
- }
- list_free(config->input_configs);
-
- for (i = 0; config->output_configs && i < config->output_configs->length; ++i) {
- free_output_config(config->output_configs->items[i]);
- }
- list_free(config->output_configs);
-
- for (i = 0; config->command_policies && i < config->command_policies->length; ++i) {
- free_command_policy(config->command_policies->items[i]);
- }
+ list_free(config->active_bar_modifiers);
+ list_free(config->config_chain);
list_free(config->command_policies);
-
- for (i = 0; config->feature_policies && i < config->feature_policies->length; ++i) {
- free_feature_policy(config->feature_policies->items[i]);
- }
list_free(config->feature_policies);
-
- list_free(config->active_bar_modifiers);
- free_flat_list(config->config_chain);
- free(config->font);
+ list_free(config->ipc_policies);
+ free(config->current_bar);
free(config->floating_scroll_up_cmd);
free(config->floating_scroll_down_cmd);
free(config->floating_scroll_left_cmd);
free(config->floating_scroll_right_cmd);
+ free(config->font);
+ free((char *)config->current_config);
free(config);
}
-
-static bool file_exists(const char *path) {
- return path && access(path, R_OK) != -1;
+static void destroy_removed_seats(struct sway_config *old_config,
+ struct sway_config *new_config) {
+ struct seat_config *seat_config;
+ struct sway_seat *seat;
+ int i;
+ for (i = 0; i < old_config->seat_configs->length; i++) {
+ seat_config = old_config->seat_configs->items[i];
+ /* Also destroy seats that aren't present in new config */
+ if (new_config && list_seq_find(new_config->seat_configs,
+ seat_name_cmp, seat_config->name) < 0) {
+ seat = input_manager_get_seat(input_manager,
+ seat_config->name);
+ seat_destroy(seat);
+ }
+ }
}
static void config_defaults(struct sway_config *config) {
@@ -304,19 +139,22 @@ static void config_defaults(struct sway_config *config) {
if (!(config->criteria = create_list())) goto cleanup;
if (!(config->no_focus = create_list())) goto cleanup;
if (!(config->input_configs = create_list())) goto cleanup;
+ if (!(config->seat_configs = create_list())) goto cleanup;
if (!(config->output_configs = create_list())) goto cleanup;
if (!(config->cmd_queue = create_list())) goto cleanup;
- if (!(config->current_mode = malloc(sizeof(struct sway_mode)))) goto cleanup;
+ if (!(config->current_mode = malloc(sizeof(struct sway_mode))))
+ goto cleanup;
if (!(config->current_mode->name = malloc(sizeof("default")))) goto cleanup;
strcpy(config->current_mode->name, "default");
- if (!(config->current_mode->bindings = create_list())) goto cleanup;
+ if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup;
+ if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
list_add(config->modes, config->current_mode);
config->floating_mod = 0;
- config->dragging_key = M_LEFT_CLICK;
- config->resizing_key = M_RIGHT_CLICK;
+ config->dragging_key = BTN_LEFT;
+ config->resizing_key = BTN_RIGHT;
if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup;
if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup;
if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup;
@@ -324,7 +162,8 @@ static void config_defaults(struct sway_config *config) {
config->default_layout = L_NONE;
config->default_orientation = L_NONE;
if (!(config->font = strdup("monospace 10"))) goto cleanup;
- config->font_height = get_font_text_height(config->font);
+ // TODO: border
+ //config->font_height = get_font_text_height(config->font);
// floating view
config->floating_maximum_width = 0;
@@ -339,7 +178,6 @@ static void config_defaults(struct sway_config *config) {
config->active = false;
config->failed = false;
config->auto_back_and_forth = false;
- config->seamless_mouse = true;
config->reading = false;
config->show_marks = true;
@@ -403,29 +241,8 @@ cleanup:
sway_abort("Unable to allocate config structures");
}
-static int compare_modifiers(const void *left, const void *right) {
- uint32_t a = *(uint32_t *)left;
- uint32_t b = *(uint32_t *)right;
-
- return a - b;
-}
-
-void update_active_bar_modifiers() {
- if (config->active_bar_modifiers->length > 0) {
- list_free(config->active_bar_modifiers);
- config->active_bar_modifiers = create_list();
- }
-
- struct bar_config *bar;
- int i;
- for (i = 0; i < config->bars->length; ++i) {
- bar = config->bars->items[i];
- if (strcmp(bar->mode, "hide") == 0 && strcmp(bar->hidden_state, "hide") == 0) {
- if (list_seq_find(config->active_bar_modifiers, compare_modifiers, &bar->modifier) < 0) {
- list_add(config->active_bar_modifiers, &bar->modifier);
- }
- }
- }
+static bool file_exists(const char *path) {
+ return path && access(path, R_OK) != -1;
}
static char *get_config_path(void) {
@@ -442,12 +259,12 @@ static char *get_config_path(void) {
char *home = getenv("HOME");
char *config_home = malloc(strlen(home) + strlen("/.config") + 1);
if (!config_home) {
- sway_log(L_ERROR, "Unable to allocate $HOME/.config");
+ wlr_log(L_ERROR, "Unable to allocate $HOME/.config");
} else {
strcpy(config_home, home);
strcat(config_home, "/.config");
setenv("XDG_CONFIG_HOME", config_home, 1);
- sway_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);
+ wlr_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);
free(config_home);
}
}
@@ -463,6 +280,7 @@ static char *get_config_path(void) {
if (file_exists(path)) {
return path;
}
+ free(path);
}
}
@@ -472,7 +290,7 @@ static char *get_config_path(void) {
const char *current_config_path;
static bool load_config(const char *path, struct sway_config *config) {
- sway_log(L_INFO, "Loading config from %s", path);
+ wlr_log(L_INFO, "Loading config from %s", path);
current_config_path = path;
struct stat sb;
@@ -481,13 +299,13 @@ static bool load_config(const char *path, struct sway_config *config) {
}
if (path == NULL) {
- sway_log(L_ERROR, "Unable to find a config file!");
+ wlr_log(L_ERROR, "Unable to find a config file!");
return false;
}
FILE *f = fopen(path, "r");
if (!f) {
- sway_log(L_ERROR, "Unable to open %s for reading", path);
+ wlr_log(L_ERROR, "Unable to open %s for reading", path);
return false;
}
@@ -495,20 +313,14 @@ static bool load_config(const char *path, struct sway_config *config) {
fclose(f);
if (!config_load_success) {
- sway_log(L_ERROR, "Error(s) loading config!");
+ wlr_log(L_ERROR, "Error(s) loading config!");
}
current_config_path = NULL;
return true;
}
-static int qstrcmp(const void* a, const void* b) {
- return strcmp(*((char**) a), *((char**) b));
-}
-
bool load_main_config(const char *file, bool is_active) {
- input_init();
-
char *path;
if (file != NULL) {
path = strdup(file);
@@ -524,7 +336,7 @@ bool load_main_config(const char *file, bool is_active) {
config_defaults(config);
if (is_active) {
- sway_log(L_DEBUG, "Performing configuration file reload");
+ wlr_log(L_DEBUG, "Performing configuration file reload");
config->reloading = true;
config->active = true;
}
@@ -535,11 +347,14 @@ bool load_main_config(const char *file, bool is_active) {
config->reading = true;
// Read security configs
+ // TODO: Security
bool success = true;
+ /*
DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
if (!dir) {
- sway_log(L_ERROR, "%s does not exist, sway will have no security configuration"
- " and will probably be broken", SYSCONFDIR "/sway/security.d");
+ wlr_log(L_ERROR,
+ "%s does not exist, sway will have no security configuration"
+ " and will probably be broken", SYSCONFDIR "/sway/security.d");
} else {
list_t *secconfigs = create_list();
char *base = SYSCONFDIR "/sway/security.d/";
@@ -563,8 +378,12 @@ bool load_main_config(const char *file, bool is_active) {
list_qsort(secconfigs, qstrcmp);
for (int i = 0; i < secconfigs->length; ++i) {
char *_path = secconfigs->items[i];
- if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || (((s.st_mode & 0777) != 0644) && (s.st_mode & 0777) != 0444)) {
- sway_log(L_ERROR, "Refusing to load %s - it must be owned by root and mode 644 or 444", _path);
+ if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||
+ (((s.st_mode & 0777) != 0644) &&
+ (s.st_mode & 0777) != 0444)) {
+ wlr_log(L_ERROR,
+ "Refusing to load %s - it must be owned by root "
+ "and mode 644 or 444", _path);
success = false;
} else {
success = success && load_config(_path, config);
@@ -573,6 +392,7 @@ bool load_main_config(const char *file, bool is_active) {
free_flat_list(secconfigs);
}
+ */
success = success && load_config(path, config);
@@ -581,18 +401,15 @@ bool load_main_config(const char *file, bool is_active) {
}
if (old_config) {
+ destroy_removed_seats(old_config, config);
free_config(old_config);
}
config->reading = false;
-
- if (success) {
- update_active_bar_modifiers();
- }
-
return success;
}
-static bool load_include_config(const char *path, const char *parent_dir, struct sway_config *config) {
+static bool load_include_config(const char *path, const char *parent_dir,
+ struct sway_config *config) {
// save parent config
const char *parent_config = config->current_config;
@@ -602,7 +419,8 @@ static bool load_include_config(const char *path, const char *parent_dir, struct
len = len + strlen(parent_dir) + 2;
full_path = malloc(len * sizeof(char));
if (!full_path) {
- sway_log(L_ERROR, "Unable to allocate full path to included config");
+ wlr_log(L_ERROR,
+ "Unable to allocate full path to included config");
return false;
}
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
free(full_path);
if (real_path == NULL) {
- sway_log(L_DEBUG, "%s not found.", path);
+ wlr_log(L_DEBUG, "%s not found.", path);
return false;
}
@@ -621,7 +439,9 @@ static bool load_include_config(const char *path, const char *parent_dir, struct
for (j = 0; j < config->config_chain->length; ++j) {
char *old_path = config->config_chain->items[j];
if (strcmp(real_path, old_path) == 0) {
- sway_log(L_DEBUG, "%s already included once, won't be included again.", real_path);
+ wlr_log(L_DEBUG,
+ "%s already included once, won't be included again.",
+ real_path);
free(real_path);
return false;
}
@@ -673,7 +493,7 @@ bool load_include_configs(const char *path, struct sway_config *config) {
// restore wd
if (chdir(wd) < 0) {
free(wd);
- sway_log(L_ERROR, "failed to restore working directory");
+ wlr_log(L_ERROR, "failed to restore working directory");
return false;
}
@@ -681,13 +501,11 @@ bool load_include_configs(const char *path, struct sway_config *config) {
return true;
}
-struct cmd_results *check_security_config() {
- if (!current_config_path || strncmp(SYSCONFDIR "/sway/security.d/", current_config_path,
- strlen(SYSCONFDIR "/sway/security.d/")) != 0) {
- return cmd_results_new(CMD_INVALID, "permit",
- "This command is only permitted to run from " SYSCONFDIR "/sway/security.d/*");
- }
- return NULL;
+void config_clear_handler_context(struct sway_config *config) {
+ free_input_config(config->handler_context.input_config);
+ free_seat_config(config->handler_context.seat_config);
+
+ memset(&config->handler_context, 0, sizeof(config->handler_context));
}
bool read_config(FILE *file, struct sway_config *config) {
@@ -717,13 +535,13 @@ bool read_config(FILE *file, struct sway_config *config) {
switch(res->status) {
case CMD_FAILURE:
case CMD_INVALID:
- sway_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, line,
- res->error, config->current_config);
+ wlr_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number,
+ line, res->error, config->current_config);
success = false;
break;
case CMD_DEFER:
- sway_log(L_DEBUG, "Defferring command `%s'", line);
+ wlr_log(L_DEBUG, "Deferring command `%s'", line);
list_add(config->cmd_queue, strdup(line));
break;
@@ -731,7 +549,7 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_END) {
block = CMD_BLOCK_MODE;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -739,7 +557,15 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_END) {
block = CMD_BLOCK_INPUT;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
+ }
+ break;
+
+ case CMD_BLOCK_SEAT:
+ if (block == CMD_BLOCK_END) {
+ block = CMD_BLOCK_SEAT;
+ } else {
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -747,7 +573,7 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_END) {
block = CMD_BLOCK_BAR;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -755,7 +581,7 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_BAR) {
block = CMD_BLOCK_BAR_COLORS;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -763,7 +589,7 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_END) {
block = CMD_BLOCK_COMMANDS;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -771,7 +597,7 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_END) {
block = CMD_BLOCK_IPC;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -779,56 +605,61 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_IPC) {
block = CMD_BLOCK_IPC_EVENTS;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
case CMD_BLOCK_END:
switch(block) {
case CMD_BLOCK_MODE:
- sway_log(L_DEBUG, "End of mode block");
+ wlr_log(L_DEBUG, "End of mode block");
config->current_mode = config->modes->items[0];
block = CMD_BLOCK_END;
break;
case CMD_BLOCK_INPUT:
- sway_log(L_DEBUG, "End of input block");
- current_input_config = NULL;
+ wlr_log(L_DEBUG, "End of input block");
+ block = CMD_BLOCK_END;
+ break;
+
+ case CMD_BLOCK_SEAT:
+ wlr_log(L_DEBUG, "End of seat block");
block = CMD_BLOCK_END;
break;
case CMD_BLOCK_BAR:
- sway_log(L_DEBUG, "End of bar block");
+ wlr_log(L_DEBUG, "End of bar block");
config->current_bar = NULL;
block = CMD_BLOCK_END;
break;
case CMD_BLOCK_BAR_COLORS:
- sway_log(L_DEBUG, "End of bar colors block");
+ wlr_log(L_DEBUG, "End of bar colors block");
block = CMD_BLOCK_BAR;
break;
case CMD_BLOCK_COMMANDS:
- sway_log(L_DEBUG, "End of commands block");
+ wlr_log(L_DEBUG, "End of commands block");
block = CMD_BLOCK_END;
break;
case CMD_BLOCK_IPC:
- sway_log(L_DEBUG, "End of IPC block");
+ wlr_log(L_DEBUG, "End of IPC block");
block = CMD_BLOCK_END;
break;
case CMD_BLOCK_IPC_EVENTS:
- sway_log(L_DEBUG, "End of IPC events block");
+ wlr_log(L_DEBUG, "End of IPC events block");
block = CMD_BLOCK_IPC;
break;
case CMD_BLOCK_END:
- sway_log(L_ERROR, "Unmatched }");
+ wlr_log(L_ERROR, "Unmatched }");
break;
default:;
}
+ config_clear_handler_context(config);
default:;
}
free(line);
@@ -838,365 +669,6 @@ bool read_config(FILE *file, struct sway_config *config) {
return success;
}
-int input_identifier_cmp(const void *item, const void *data) {
- const struct input_config *ic = item;
- const char *identifier = data;
- return strcmp(ic->identifier, identifier);
-}
-
-int output_name_cmp(const void *item, const void *data) {
- const struct output_config *output = item;
- const char *name = data;
-
- return strcmp(output->name, name);
-}
-
-void merge_input_config(struct input_config *dst, struct input_config *src) {
- if (src->identifier) {
- if (dst->identifier) {
- free(dst->identifier);
- }
- dst->identifier = strdup(src->identifier);
- }
- if (src->accel_profile != INT_MIN) {
- dst->accel_profile = src->accel_profile;
- }
- if (src->click_method != INT_MIN) {
- dst->click_method = src->click_method;
- }
- if (src->drag_lock != INT_MIN) {
- dst->drag_lock = src->drag_lock;
- }
- if (src->dwt != INT_MIN) {
- dst->dwt = src->dwt;
- }
- if (src->middle_emulation != INT_MIN) {
- dst->middle_emulation = src->middle_emulation;
- }
- if (src->natural_scroll != INT_MIN) {
- dst->natural_scroll = src->natural_scroll;
- }
- if (src->pointer_accel != FLT_MIN) {
- dst->pointer_accel = src->pointer_accel;
- }
- if (src->scroll_method != INT_MIN) {
- dst->scroll_method = src->scroll_method;
- }
- if (src->send_events != INT_MIN) {
- dst->send_events = src->send_events;
- }
- if (src->tap != INT_MIN) {
- dst->tap = src->tap;
- }
-}
-
-void merge_output_config(struct output_config *dst, struct output_config *src) {
- if (src->name) {
- if (dst->name) {
- free(dst->name);
- }
- dst->name = strdup(src->name);
- }
- if (src->enabled != -1) {
- dst->enabled = src->enabled;
- }
- if (src->width != -1) {
- dst->width = src->width;
- }
- if (src->height != -1) {
- dst->height = src->height;
- }
- if (src->x != -1) {
- dst->x = src->x;
- }
- if (src->y != -1) {
- dst->y = src->y;
- }
- if (src->scale != -1) {
- dst->scale = src->scale;
- }
- if (src->background) {
- if (dst->background) {
- free(dst->background);
- }
- dst->background = strdup(src->background);
- }
- if (src->background_option) {
- if (dst->background_option) {
- free(dst->background_option);
- }
- dst->background_option = strdup(src->background_option);
- }
-}
-
-static void invoke_swaybar(struct bar_config *bar) {
- // Pipe to communicate errors
- int filedes[2];
- if (pipe(filedes) == -1) {
- sway_log(L_ERROR, "Pipe setup failed! Cannot fork into bar");
- return;
- }
-
- bar->pid = fork();
- if (bar->pid == 0) {
- close(filedes[0]);
- if (!bar->swaybar_command) {
- char *const cmd[] = {
- "swaybar",
- "-b",
- bar->id,
- NULL,
- };
-
- close(filedes[1]);
- execvp(cmd[0], cmd);
- _exit(EXIT_SUCCESS);
- } else {
- // run custom swaybar
- int len = strlen(bar->swaybar_command) + strlen(bar->id) + 5;
- char *command = malloc(len * sizeof(char));
- if (!command) {
- const char msg[] = "Unable to allocate swaybar command string";
- int len = sizeof(msg);
- if (write(filedes[1], &len, sizeof(int))) {};
- if (write(filedes[1], msg, len)) {};
- close(filedes[1]);
- _exit(EXIT_FAILURE);
- }
- snprintf(command, len, "%s -b %s", bar->swaybar_command, bar->id);
-
- char *const cmd[] = {
- "sh",
- "-c",
- command,
- NULL,
- };
-
- close(filedes[1]);
- execvp(cmd[0], cmd);
- free(command);
- _exit(EXIT_SUCCESS);
- }
- }
- close(filedes[0]);
- int len;
- if(read(filedes[1], &len, sizeof(int)) == sizeof(int)) {
- char *buf = malloc(len);
- if(!buf) {
- sway_log(L_ERROR, "Cannot allocate error string");
- return;
- }
- if(read(filedes[1], buf, len)) {
- sway_log(L_ERROR, "%s", buf);
- }
- free(buf);
- }
- close(filedes[1]);
-}
-
-static void terminate_swaybar(pid_t pid) {
- int ret = kill(pid, SIGTERM);
- if (ret != 0) {
- sway_log(L_ERROR, "Unable to terminate swaybar [pid: %d]", pid);
- } else {
- int status;
- waitpid(pid, &status, 0);
- }
-}
-
-void terminate_swaybg(pid_t pid) {
- int ret = kill(pid, SIGTERM);
- if (ret != 0) {
- sway_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid);
- } else {
- int status;
- waitpid(pid, &status, 0);
- }
-}
-
-static bool active_output(const char *name) {
- int i;
- swayc_t *cont = NULL;
- for (i = 0; i < root_container.children->length; ++i) {
- cont = root_container.children->items[i];
- if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) {
- return true;
- }
- }
-
- return false;
-}
-
-void load_swaybars() {
- // Check for bars
- list_t *bars = create_list();
- struct bar_config *bar = NULL;
- int i;
- for (i = 0; i < config->bars->length; ++i) {
- bar = config->bars->items[i];
- bool apply = false;
- if (bar->outputs) {
- int j;
- for (j = 0; j < bar->outputs->length; ++j) {
- char *o = bar->outputs->items[j];
- if (!strcmp(o, "*") || active_output(o)) {
- apply = true;
- break;
- }
- }
- } else {
- apply = true;
- }
- if (apply) {
- list_add(bars, bar);
- }
- }
-
- for (i = 0; i < bars->length; ++i) {
- bar = bars->items[i];
- if (bar->pid != 0) {
- terminate_swaybar(bar->pid);
- }
- sway_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
- invoke_swaybar(bar);
- }
-
- list_free(bars);
-}
-
-void apply_input_config(struct input_config *ic, struct libinput_device *dev) {
- if (!ic) {
- return;
- }
-
- sway_log(L_DEBUG, "apply_input_config(%s)", ic->identifier);
-
- if (ic->accel_profile != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) accel_set_profile(%d)", ic->identifier, ic->accel_profile);
- libinput_device_config_accel_set_profile(dev, ic->accel_profile);
- }
- if (ic->click_method != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) click_set_method(%d)", ic->identifier, ic->click_method);
- libinput_device_config_click_set_method(dev, ic->click_method);
- }
- if (ic->drag_lock != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) tap_set_drag_lock_enabled(%d)", ic->identifier, ic->click_method);
- libinput_device_config_tap_set_drag_lock_enabled(dev, ic->drag_lock);
- }
- if (ic->dwt != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) dwt_set_enabled(%d)", ic->identifier, ic->dwt);
- libinput_device_config_dwt_set_enabled(dev, ic->dwt);
- }
- if (ic->left_handed != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) left_handed_set_enabled(%d)", ic->identifier, ic->left_handed);
- libinput_device_config_left_handed_set(dev, ic->left_handed);
- }
- if (ic->middle_emulation != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) middle_emulation_set_enabled(%d)", ic->identifier, ic->middle_emulation);
- libinput_device_config_middle_emulation_set_enabled(dev, ic->middle_emulation);
- }
- if (ic->natural_scroll != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) natural_scroll_set_enabled(%d)", ic->identifier, ic->natural_scroll);
- libinput_device_config_scroll_set_natural_scroll_enabled(dev, ic->natural_scroll);
- }
- if (ic->pointer_accel != FLT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) accel_set_speed(%f)", ic->identifier, ic->pointer_accel);
- libinput_device_config_accel_set_speed(dev, ic->pointer_accel);
- }
- if (ic->scroll_method != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) scroll_set_method(%d)", ic->identifier, ic->scroll_method);
- libinput_device_config_scroll_set_method(dev, ic->scroll_method);
- }
- if (ic->send_events != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) send_events_set_mode(%d)", ic->identifier, ic->send_events);
- libinput_device_config_send_events_set_mode(dev, ic->send_events);
- }
- if (ic->tap != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) tap_set_enabled(%d)", ic->identifier, ic->tap);
- libinput_device_config_tap_set_enabled(dev, ic->tap);
- }
-}
-
-void apply_output_config(struct output_config *oc, swayc_t *output) {
- if (oc && oc->enabled == 0) {
- destroy_output(output);
- return;
- }
-
- if (oc && oc->width > 0 && oc->height > 0) {
- output->width = oc->width;
- output->height = oc->height;
-
- sway_log(L_DEBUG, "Set %s size to %ix%i (%d)", oc->name, oc->width, oc->height, oc->scale);
- struct wlc_size new_size = { .w = oc->width, .h = oc->height };
- wlc_output_set_resolution(output->handle, &new_size, (uint32_t)oc->scale);
- } else if (oc) {
- const struct wlc_size *new_size = wlc_output_get_resolution(output->handle);
- wlc_output_set_resolution(output->handle, new_size, (uint32_t)oc->scale);
- }
-
- // Find position for it
- if (oc && oc->x != -1 && oc->y != -1) {
- sway_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);
- output->x = oc->x;
- output->y = oc->y;
- } else {
- int x = 0;
- for (int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (c->type == C_OUTPUT) {
- if (c->width + c->x > x) {
- x = c->width + c->x;
- }
- }
- }
- output->x = x;
- }
-
- if (!oc || !oc->background) {
- // Look for a * config for background
- int i = list_seq_find(config->output_configs, output_name_cmp, "*");
- if (i >= 0) {
- oc = config->output_configs->items[i];
- } else {
- oc = NULL;
- }
- }
-
- int output_i;
- for (output_i = 0; output_i < root_container.children->length; ++output_i) {
- if (root_container.children->items[output_i] == output) {
- break;
- }
- }
-
- if (oc && oc->background) {
- if (output->bg_pid != 0) {
- terminate_swaybg(output->bg_pid);
- }
-
- sway_log(L_DEBUG, "Setting background for output %d to %s", output_i, oc->background);
-
- size_t bufsize = 12;
- char output_id[bufsize];
- snprintf(output_id, bufsize, "%d", output_i);
- output_id[bufsize-1] = 0;
-
- char *const cmd[] = {
- "swaybg",
- output_id,
- oc->background,
- oc->background_option,
- NULL,
- };
-
- output->bg_pid = fork();
- if (output->bg_pid == 0) {
- execvp(cmd[0], cmd);
- }
- }
-}
-
char *do_var_replacement(char *str) {
int i;
char *find = str;
@@ -1216,8 +688,9 @@ char *do_var_replacement(char *str) {
int vvlen = strlen(var->value);
char *newstr = malloc(strlen(str) - vnlen + vvlen + 1);
if (!newstr) {
- sway_log(L_ERROR,
- "Unable to allocate replacement during variable expansion");
+ wlr_log(L_ERROR,
+ "Unable to allocate replacement "
+ "during variable expansion");
break;
}
char *newptr = newstr;
@@ -1247,197 +720,3 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
const struct workspace_output *wsa = a, *wsb = b;
return lenient_strcmp(wsa->workspace, wsb->workspace);
}
-
-int sway_binding_cmp_keys(const void *a, const void *b) {
- const struct sway_binding *binda = a, *bindb = b;
-
- // Count keys pressed for this binding. important so we check long before
- // short ones. for example mod+a+b before mod+a
- unsigned int moda = 0, modb = 0, i;
-
- // Count how any modifiers are pressed
- for (i = 0; i < 8 * sizeof(binda->modifiers); ++i) {
- moda += (binda->modifiers & 1 << i) != 0;
- modb += (bindb->modifiers & 1 << i) != 0;
- }
- if (bindb->keys->length + modb != binda->keys->length + moda) {
- return (bindb->keys->length + modb) - (binda->keys->length + moda);
- }
-
- // Otherwise compare keys
- if (binda->modifiers > bindb->modifiers) {
- return 1;
- } else if (binda->modifiers < bindb->modifiers) {
- return -1;
- }
- struct wlc_modifiers no_mods = { 0, 0 };
- for (int i = 0; i < binda->keys->length; i++) {
- xkb_keysym_t ka = *(xkb_keysym_t *)binda->keys->items[i],
- kb = *(xkb_keysym_t *)bindb->keys->items[i];
- if (binda->bindcode) {
- uint32_t *keycode = binda->keys->items[i];
- ka = wlc_keyboard_get_keysym_for_key(*keycode, &no_mods);
- }
-
- if (bindb->bindcode) {
- uint32_t *keycode = bindb->keys->items[i];
- kb = wlc_keyboard_get_keysym_for_key(*keycode, &no_mods);
- }
-
- if (ka > kb) {
- return 1;
- } else if (ka < kb) {
- return -1;
- }
- }
-
- return 0;
-}
-
-int sway_binding_cmp(const void *a, const void *b) {
- int cmp = 0;
- if ((cmp = sway_binding_cmp_keys(a, b)) != 0) {
- return cmp;
- }
- const struct sway_binding *binda = a, *bindb = b;
- return lenient_strcmp(binda->command, bindb->command);
-}
-
-int sway_binding_cmp_qsort(const void *a, const void *b) {
- return sway_binding_cmp(*(void **)a, *(void **)b);
-}
-
-void free_sway_binding(struct sway_binding *binding) {
- if (binding->keys) {
- for (int i = 0; i < binding->keys->length; i++) {
- free(binding->keys->items[i]);
- }
- list_free(binding->keys);
- }
- if (binding->command) {
- free(binding->command);
- }
- free(binding);
-}
-
-int sway_mouse_binding_cmp_buttons(const void *a, const void *b) {
- const struct sway_mouse_binding *binda = a, *bindb = b;
- if (binda->button > bindb->button) {
- return 1;
- }
- if (binda->button < bindb->button) {
- return -1;
- }
- return 0;
-}
-
-int sway_mouse_binding_cmp(const void *a, const void *b) {
- int cmp = 0;
- if ((cmp = sway_binding_cmp_keys(a, b)) != 0) {
- return cmp;
- }
- const struct sway_mouse_binding *binda = a, *bindb = b;
- return lenient_strcmp(binda->command, bindb->command);
-}
-
-int sway_mouse_binding_cmp_qsort(const void *a, const void *b) {
- return sway_mouse_binding_cmp(*(void **)a, *(void **)b);
-}
-
-void free_sway_mouse_binding(struct sway_mouse_binding *binding) {
- if (binding->command) {
- free(binding->command);
- }
- free(binding);
-}
-
-struct sway_binding *sway_binding_dup(struct sway_binding *sb) {
- struct sway_binding *new_sb = malloc(sizeof(struct sway_binding));
- if (!new_sb) {
- return NULL;
- }
-
- new_sb->order = sb->order;
- new_sb->modifiers = sb->modifiers;
- new_sb->command = strdup(sb->command);
-
- new_sb->keys = create_list();
- int i;
- for (i = 0; i < sb->keys->length; ++i) {
- xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
- if (!key) {
- free_sway_binding(new_sb);
- return NULL;
- }
- *key = *(xkb_keysym_t *)sb->keys->items[i];
- list_add(new_sb->keys, key);
- }
-
- return new_sb;
-}
-
-struct bar_config *default_bar_config(void) {
- struct bar_config *bar = NULL;
- bar = malloc(sizeof(struct bar_config));
- if (!bar) {
- return NULL;
- }
- if (!(bar->mode = strdup("dock"))) goto cleanup;
- if (!(bar->hidden_state = strdup("hide"))) goto cleanup;
- bar->modifier = WLC_BIT_MOD_LOGO;
- bar->outputs = NULL;
- bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
- if (!(bar->bindings = create_list())) goto cleanup;
- if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup;
- bar->pango_markup = false;
- bar->swaybar_command = NULL;
- bar->font = NULL;
- bar->height = -1;
- bar->workspace_buttons = true;
- bar->wrap_scroll = false;
- bar->separator_symbol = NULL;
- bar->strip_workspace_numbers = false;
- bar->binding_mode_indicator = true;
-#ifdef ENABLE_TRAY
- bar->tray_output = NULL;
- bar->icon_theme = NULL;
- bar->tray_padding = 2;
- bar->activate_button = 0x110; /* BTN_LEFT */
- bar->context_button = 0x111; /* BTN_RIGHT */
- bar->secondary_button = 0x112; /* BTN_MIDDLE */
-#endif
- bar->verbose = false;
- bar->pid = 0;
- // set default colors
- if (!(bar->colors.background = strndup("#000000ff", 9))) goto cleanup;
- if (!(bar->colors.statusline = strndup("#ffffffff", 9))) goto cleanup;
- if (!(bar->colors.separator = strndup("#666666ff", 9))) goto cleanup;
- if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) goto cleanup;
- if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) goto cleanup;
- if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
- if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) goto cleanup;
- if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) goto cleanup;
- if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
- if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) goto cleanup;
- if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) goto cleanup;
- if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) goto cleanup;
- if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) goto cleanup;
- if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) goto cleanup;
- if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
- // if the following colors stay undefined, they fall back to background,
- // statusline, separator and urgent_workspace_*.
- bar->colors.focused_background = NULL;
- bar->colors.focused_statusline = NULL;
- bar->colors.focused_separator = NULL;
- bar->colors.binding_mode_border = NULL;
- bar->colors.binding_mode_bg = NULL;
- bar->colors.binding_mode_text = NULL;
-
- list_add(config->bars, bar);
-
- return bar;
-
-cleanup:
- free_bar(bar);
- return NULL;
-}
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 @@
+#define _POSIX_C_SOURCE 200809L
+#define _XOPEN_SOURCE 700
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <wordexp.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <strings.h>
+#include "sway/config.h"
+#include "stringop.h"
+#include "list.h"
+#include "log.h"
+
+static void terminate_swaybar(pid_t pid) {
+ wlr_log(L_DEBUG, "Terminating swaybar %d", pid);
+ int ret = kill(pid, SIGTERM);
+ if (ret != 0) {
+ wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid);
+ } else {
+ int status;
+ waitpid(pid, &status, 0);
+ }
+}
+
+void free_bar_config(struct bar_config *bar) {
+ if (!bar) {
+ return;
+ }
+ free(bar->mode);
+ free(bar->position);
+ free(bar->hidden_state);
+ free(bar->status_command);
+ free(bar->font);
+ free(bar->separator_symbol);
+ // TODO: Free mouse bindings
+ list_free(bar->bindings);
+ if (bar->outputs) {
+ free_flat_list(bar->outputs);
+ }
+ if (bar->pid != 0) {
+ terminate_swaybar(bar->pid);
+ }
+ free(bar->colors.background);
+ free(bar->colors.statusline);
+ free(bar->colors.separator);
+ free(bar->colors.focused_background);
+ free(bar->colors.focused_statusline);
+ free(bar->colors.focused_separator);
+ free(bar->colors.focused_workspace_border);
+ free(bar->colors.focused_workspace_bg);
+ free(bar->colors.focused_workspace_text);
+ free(bar->colors.active_workspace_border);
+ free(bar->colors.active_workspace_bg);
+ free(bar->colors.active_workspace_text);
+ free(bar->colors.inactive_workspace_border);
+ free(bar->colors.inactive_workspace_bg);
+ free(bar->colors.inactive_workspace_text);
+ free(bar->colors.urgent_workspace_border);
+ free(bar->colors.urgent_workspace_bg);
+ free(bar->colors.urgent_workspace_text);
+ free(bar->colors.binding_mode_border);
+ free(bar->colors.binding_mode_bg);
+ free(bar->colors.binding_mode_text);
+ free(bar);
+}
+
+struct bar_config *default_bar_config(void) {
+ struct bar_config *bar = NULL;
+ bar = malloc(sizeof(struct bar_config));
+ if (!bar) {
+ return NULL;
+ }
+ if (!(bar->mode = strdup("dock"))) goto cleanup;
+ if (!(bar->hidden_state = strdup("hide"))) goto cleanup;
+ bar->outputs = NULL;
+ bar->position = strdup("bottom");
+ if (!(bar->bindings = create_list())) goto cleanup;
+ if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup;
+ bar->pango_markup = false;
+ bar->swaybar_command = NULL;
+ bar->font = NULL;
+ bar->height = -1;
+ bar->workspace_buttons = true;
+ bar->wrap_scroll = false;
+ bar->separator_symbol = NULL;
+ bar->strip_workspace_numbers = false;
+ bar->binding_mode_indicator = true;
+ bar->verbose = false;
+ bar->pid = 0;
+ // set default colors
+ if (!(bar->colors.background = strndup("#000000ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.statusline = strndup("#ffffffff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.separator = strndup("#666666ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) {
+ goto cleanup;
+ }
+ // if the following colors stay undefined, they fall back to background,
+ // statusline, separator and urgent_workspace_*.
+ bar->colors.focused_background = NULL;
+ bar->colors.focused_statusline = NULL;
+ bar->colors.focused_separator = NULL;
+ bar->colors.binding_mode_border = NULL;
+ bar->colors.binding_mode_bg = NULL;
+ bar->colors.binding_mode_text = NULL;
+
+ list_add(config->bars, bar);
+ return bar;
+cleanup:
+ free_bar_config(bar);
+ return NULL;
+}
+
+void invoke_swaybar(struct bar_config *bar) {
+ // Pipe to communicate errors
+ int filedes[2];
+ if (pipe(filedes) == -1) {
+ wlr_log(L_ERROR, "Pipe setup failed! Cannot fork into bar");
+ return;
+ }
+
+ bar->pid = fork();
+ if (bar->pid == 0) {
+ close(filedes[0]);
+
+ // run custom swaybar
+ size_t len = snprintf(NULL, 0, "%s -b %s",
+ bar->swaybar_command ? bar->swaybar_command : "swaybar",
+ bar->id);
+ char *command = malloc(len + 1);
+ if (!command) {
+ const char msg[] = "Unable to allocate swaybar command string";
+ size_t len = sizeof(msg);
+ if (write(filedes[1], &len, sizeof(int))) {};
+ if (write(filedes[1], msg, len)) {};
+ close(filedes[1]);
+ exit(1);
+ }
+ snprintf(command, len + 1, "%s -b %s",
+ bar->swaybar_command ? bar->swaybar_command : "swaybar",
+ bar->id);
+ char *const cmd[] = { "sh", "-c", command, NULL, };
+ close(filedes[1]);
+ execvp(cmd[0], cmd);
+ exit(1);
+ }
+ wlr_log(L_DEBUG, "Spawned swaybar %d", bar->pid);
+ close(filedes[0]);
+ ssize_t len;
+ if (read(filedes[1], &len, sizeof(int)) == sizeof(int)) {
+ char *buf = malloc(len);
+ if(!buf) {
+ wlr_log(L_ERROR, "Cannot allocate error string");
+ return;
+ }
+ if (read(filedes[1], buf, len)) {
+ wlr_log(L_ERROR, "%s", buf);
+ }
+ free(buf);
+ }
+ close(filedes[1]);
+}
+
+static bool active_output(const char *name) {
+ struct sway_container *cont = NULL;
+ for (int i = 0; i < root_container.children->length; ++i) {
+ cont = root_container.children->items[i];
+ if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void load_swaybars() {
+ for (int i = 0; i < config->bars->length; ++i) {
+ struct bar_config *bar = config->bars->items[i];
+ bool apply = false;
+ if (bar->outputs) {
+ for (int j = 0; j < bar->outputs->length; ++j) {
+ char *o = bar->outputs->items[j];
+ if (!strcmp(o, "*") || active_output(o)) {
+ apply = true;
+ break;
+ }
+ }
+ } else {
+ apply = true;
+ }
+ if (apply) {
+ if (bar->pid != 0) {
+ terminate_swaybar(bar->pid);
+ }
+ wlr_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
+ invoke_swaybar(bar);
+ }
+ }
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include <stdlib.h>
+#include <limits.h>
+#include <float.h>
+#include "sway/config.h"
+#include "log.h"
+
+struct input_config *new_input_config(const char* identifier) {
+ struct input_config *input = calloc(1, sizeof(struct input_config));
+ if (!input) {
+ wlr_log(L_DEBUG, "Unable to allocate input config");
+ return NULL;
+ }
+ wlr_log(L_DEBUG, "new_input_config(%s)", identifier);
+ if (!(input->identifier = strdup(identifier))) {
+ free(input);
+ wlr_log(L_DEBUG, "Unable to allocate input config");
+ return NULL;
+ }
+
+ input->tap = INT_MIN;
+ input->drag_lock = INT_MIN;
+ input->dwt = INT_MIN;
+ input->send_events = INT_MIN;
+ input->click_method = INT_MIN;
+ input->middle_emulation = INT_MIN;
+ input->natural_scroll = INT_MIN;
+ input->accel_profile = INT_MIN;
+ input->pointer_accel = FLT_MIN;
+ input->scroll_method = INT_MIN;
+ input->left_handed = INT_MIN;
+
+ return input;
+}
+
+void merge_input_config(struct input_config *dst, struct input_config *src) {
+ if (src->identifier) {
+ free(dst->identifier);
+ dst->identifier = strdup(src->identifier);
+ }
+ if (src->accel_profile != INT_MIN) {
+ dst->accel_profile = src->accel_profile;
+ }
+ if (src->click_method != INT_MIN) {
+ dst->click_method = src->click_method;
+ }
+ if (src->drag_lock != INT_MIN) {
+ dst->drag_lock = src->drag_lock;
+ }
+ if (src->dwt != INT_MIN) {
+ dst->dwt = src->dwt;
+ }
+ if (src->middle_emulation != INT_MIN) {
+ dst->middle_emulation = src->middle_emulation;
+ }
+ if (src->natural_scroll != INT_MIN) {
+ dst->natural_scroll = src->natural_scroll;
+ }
+ if (src->pointer_accel != FLT_MIN) {
+ dst->pointer_accel = src->pointer_accel;
+ }
+ if (src->scroll_method != INT_MIN) {
+ dst->scroll_method = src->scroll_method;
+ }
+ if (src->send_events != INT_MIN) {
+ dst->send_events = src->send_events;
+ }
+ if (src->tap != INT_MIN) {
+ dst->tap = src->tap;
+ }
+ if (src->xkb_layout) {
+ free(dst->xkb_layout);
+ dst->xkb_layout = strdup(src->xkb_layout);
+ }
+ if (src->xkb_model) {
+ free(dst->xkb_model);
+ dst->xkb_model = strdup(src->xkb_model);
+ }
+ if (src->xkb_options) {
+ free(dst->xkb_options);
+ dst->xkb_options = strdup(src->xkb_options);
+ }
+ if (src->xkb_rules) {
+ free(dst->xkb_rules);
+ dst->xkb_rules = strdup(src->xkb_rules);
+ }
+ if (src->xkb_variant) {
+ free(dst->xkb_variant);
+ dst->xkb_variant = strdup(src->xkb_variant);
+ }
+ if (src->mapped_output) {
+ free(dst->mapped_output);
+ dst->mapped_output = strdup(src->mapped_output);
+ }
+}
+
+struct input_config *copy_input_config(struct input_config *ic) {
+ struct input_config *copy = calloc(1, sizeof(struct input_config));
+ if (copy == NULL) {
+ wlr_log(L_ERROR, "could not allocate input config");
+ return NULL;
+ }
+ merge_input_config(copy, ic);
+ return copy;
+}
+
+void free_input_config(struct input_config *ic) {
+ if (!ic) {
+ return;
+ }
+ free(ic->identifier);
+ free(ic);
+}
+
+int input_identifier_cmp(const void *item, const void *data) {
+ const struct input_config *ic = item;
+ const char *identifier = data;
+ return strcmp(ic->identifier, identifier);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include "sway/config.h"
+#include "sway/output.h"
+#include "log.h"
+
+int output_name_cmp(const void *item, const void *data) {
+ const struct output_config *output = item;
+ const char *name = data;
+
+ return strcmp(output->name, name);
+}
+
+void output_get_identifier(char *identifier, size_t len,
+ struct sway_output *output) {
+ struct wlr_output *wlr_output = output->wlr_output;
+ snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model,
+ wlr_output->serial);
+}
+
+struct output_config *new_output_config(const char *name) {
+ struct output_config *oc = calloc(1, sizeof(struct output_config));
+ if (oc == NULL) {
+ return NULL;
+ }
+ oc->name = strdup(name);
+ if (oc->name == NULL) {
+ free(oc);
+ return NULL;
+ }
+ oc->enabled = -1;
+ oc->width = oc->height = -1;
+ oc->refresh_rate = -1;
+ oc->x = oc->y = -1;
+ oc->scale = -1;
+ oc->transform = -1;
+ return oc;
+}
+
+void merge_output_config(struct output_config *dst, struct output_config *src) {
+ if (src->name) {
+ free(dst->name);
+ dst->name = strdup(src->name);
+ }
+ if (src->enabled != -1) {
+ dst->enabled = src->enabled;
+ }
+ if (src->width != -1) {
+ dst->width = src->width;
+ }
+ if (src->height != -1) {
+ dst->height = src->height;
+ }
+ if (src->x != -1) {
+ dst->x = src->x;
+ }
+ if (src->y != -1) {
+ dst->y = src->y;
+ }
+ if (src->scale != -1) {
+ dst->scale = src->scale;
+ }
+ if (src->refresh_rate != -1) {
+ dst->refresh_rate = src->refresh_rate;
+ }
+ if (src->transform != -1) {
+ dst->transform = src->transform;
+ }
+ if (src->background) {
+ free(dst->background);
+ dst->background = strdup(src->background);
+ }
+ if (src->background_option) {
+ free(dst->background_option);
+ dst->background_option = strdup(src->background_option);
+ }
+}
+
+static void set_mode(struct wlr_output *output, int width, int height,
+ float refresh_rate) {
+ int mhz = (int)(refresh_rate * 1000);
+ if (wl_list_empty(&output->modes)) {
+ wlr_log(L_DEBUG, "Assigning custom mode to %s", output->name);
+ wlr_output_set_custom_mode(output, width, height, mhz);
+ return;
+ }
+
+ struct wlr_output_mode *mode, *best = NULL;
+ wl_list_for_each(mode, &output->modes, link) {
+ if (mode->width == width && mode->height == height) {
+ if (mode->refresh == mhz) {
+ best = mode;
+ break;
+ }
+ best = mode;
+ }
+ }
+ if (!best) {
+ wlr_log(L_ERROR, "Configured mode for %s not available", output->name);
+ } else {
+ wlr_log(L_DEBUG, "Assigning configured mode to %s", output->name);
+ wlr_output_set_mode(output, best);
+ }
+}
+
+void terminate_swaybg(pid_t pid) {
+ int ret = kill(pid, SIGTERM);
+ if (ret != 0) {
+ wlr_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid);
+ } else {
+ int status;
+ waitpid(pid, &status, 0);
+ }
+}
+
+void apply_output_config(struct output_config *oc, struct sway_container *output) {
+ assert(output->type == C_OUTPUT);
+
+ struct wlr_output_layout *output_layout =
+ root_container.sway_root->output_layout;
+ struct wlr_output *wlr_output = output->sway_output->wlr_output;
+
+ if (oc && oc->enabled == 0) {
+ container_destroy(output);
+ wlr_output_layout_remove(root_container.sway_root->output_layout,
+ wlr_output);
+ return;
+ }
+
+ if (oc && oc->width > 0 && oc->height > 0) {
+ wlr_log(L_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width,
+ oc->height, oc->refresh_rate);
+ set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate);
+ }
+ if (oc && oc->scale > 0) {
+ wlr_log(L_DEBUG, "Set %s scale to %f", oc->name, oc->scale);
+ wlr_output_set_scale(wlr_output, oc->scale);
+ }
+ if (oc && oc->transform >= 0) {
+ wlr_log(L_DEBUG, "Set %s transform to %d", oc->name, oc->transform);
+ wlr_output_set_transform(wlr_output, oc->transform);
+ }
+
+ // Find position for it
+ if (oc && (oc->x != -1 || oc->y != -1)) {
+ wlr_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);
+ wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y);
+ } else {
+ wlr_output_layout_add_auto(output_layout, wlr_output);
+ }
+
+ if (!oc || !oc->background) {
+ // Look for a * config for background
+ int i = list_seq_find(config->output_configs, output_name_cmp, "*");
+ if (i >= 0) {
+ oc = config->output_configs->items[i];
+ } else {
+ oc = NULL;
+ }
+ }
+
+ int output_i;
+ for (output_i = 0; output_i < root_container.children->length; ++output_i) {
+ if (root_container.children->items[output_i] == output) {
+ break;
+ }
+ }
+
+ if (oc && oc->background) {
+ if (output->sway_output->bg_pid != 0) {
+ terminate_swaybg(output->sway_output->bg_pid);
+ }
+
+ wlr_log(L_DEBUG, "Setting background for output %d to %s",
+ output_i, oc->background);
+
+ size_t len = snprintf(NULL, 0, "%s %d %s %s",
+ config->swaybg_command ? config->swaybg_command : "swaybg",
+ output_i, oc->background, oc->background_option);
+ char *command = malloc(len + 1);
+ if (!command) {
+ wlr_log(L_DEBUG, "Unable to allocate swaybg command");
+ return;
+ }
+ snprintf(command, len + 1, "%s %d %s %s",
+ config->swaybg_command ? config->swaybg_command : "swaybg",
+ output_i, oc->background, oc->background_option);
+ wlr_log(L_DEBUG, "-> %s", command);
+
+ char *const cmd[] = { "sh", "-c", command, NULL };
+ output->sway_output->bg_pid = fork();
+ if (output->sway_output->bg_pid == 0) {
+ execvp(cmd[0], cmd);
+ }
+ }
+}
+
+void free_output_config(struct output_config *oc) {
+ if (!oc) {
+ return;
+ }
+ free(oc->name);
+ free(oc->background);
+ free(oc->background_option);
+ free(oc);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include <stdlib.h>
+#include <string.h>
+#include "sway/config.h"
+#include "log.h"
+
+struct seat_config *new_seat_config(const char* name) {
+ struct seat_config *seat = calloc(1, sizeof(struct seat_config));
+ if (!seat) {
+ wlr_log(L_DEBUG, "Unable to allocate seat config");
+ return NULL;
+ }
+
+ wlr_log(L_DEBUG, "new_seat_config(%s)", name);
+ seat->name = strdup(name);
+ if (!sway_assert(seat->name, "could not allocate name for seat")) {
+ free(seat);
+ return NULL;
+ }
+
+ seat->fallback = -1;
+ seat->attachments = create_list();
+ if (!sway_assert(seat->attachments,
+ "could not allocate seat attachments list")) {
+ free(seat->name);
+ free(seat);
+ return NULL;
+ }
+
+ return seat;
+}
+
+struct seat_attachment_config *seat_attachment_config_new() {
+ struct seat_attachment_config *attachment =
+ calloc(1, sizeof(struct seat_attachment_config));
+ if (!attachment) {
+ wlr_log(L_DEBUG, "cannot allocate attachment config");
+ return NULL;
+ }
+ return attachment;
+}
+
+static void seat_attachment_config_free(
+ struct seat_attachment_config *attachment) {
+ free(attachment->identifier);
+ free(attachment);
+ return;
+}
+
+static struct seat_attachment_config *seat_attachment_config_copy(
+ struct seat_attachment_config *attachment) {
+ struct seat_attachment_config *copy = seat_attachment_config_new();
+ if (!copy) {
+ return NULL;
+ }
+
+ copy->identifier = strdup(attachment->identifier);
+
+ return copy;
+}
+
+static void merge_seat_attachment_config(struct seat_attachment_config *dest,
+ struct seat_attachment_config *source) {
+ // nothing to merge yet, but there will be some day
+}
+
+void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
+ if (source->name) {
+ free(dest->name);
+ dest->name = strdup(source->name);
+ }
+
+ if (source->fallback != -1) {
+ dest->fallback = source->fallback;
+ }
+
+ for (int i = 0; i < source->attachments->length; ++i) {
+ struct seat_attachment_config *source_attachment =
+ source->attachments->items[i];
+ bool found = false;
+ for (int j = 0; j < dest->attachments->length; ++j) {
+ struct seat_attachment_config *dest_attachment =
+ dest->attachments->items[j];
+ if (strcmp(source_attachment->identifier,
+ dest_attachment->identifier) == 0) {
+ merge_seat_attachment_config(dest_attachment,
+ source_attachment);
+ found = true;
+ }
+ }
+
+ if (!found) {
+ struct seat_attachment_config *copy =
+ seat_attachment_config_copy(source_attachment);
+ if (copy) {
+ list_add(dest->attachments, copy);
+ }
+ }
+ }
+}
+
+struct seat_config *copy_seat_config(struct seat_config *seat) {
+ struct seat_config *copy = new_seat_config(seat->name);
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ merge_seat_config(copy, seat);
+
+ return copy;
+}
+
+void free_seat_config(struct seat_config *seat) {
+ if (!seat) {
+ return;
+ }
+
+ free(seat->name);
+ for (int i = 0; i < seat->attachments->length; ++i) {
+ struct seat_attachment_config *attachment =
+ seat->attachments->items[i];
+ seat_attachment_config_free(attachment);
+ }
+
+ list_free(seat->attachments);
+ free(seat);
+}
+
+int seat_name_cmp(const void *item, const void *data) {
+ const struct seat_config *sc = item;
+ const char *name = data;
+ return strcmp(sc->name, name);
+}
+
+struct seat_attachment_config *seat_config_get_attachment(
+ struct seat_config *seat_config, char *identifier) {
+ for (int i = 0; i < seat_config->attachments->length; ++i) {
+ struct seat_attachment_config *attachment =
+ seat_config->attachments->items[i];
+ if (strcmp(attachment->identifier, identifier) == 0) {
+ return attachment;
+ }
+ }
+
+ return NULL;
+}
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 @@
-#define _XOPEN_SOURCE 500
-#include <ctype.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <strings.h>
-#include <string.h>
-#include "sway/config.h"
-#include "sway/container.h"
-#include "sway/workspace.h"
-#include "sway/focus.h"
-#include "sway/border.h"
-#include "sway/layout.h"
-#include "sway/input_state.h"
-#include "sway/ipc-server.h"
-#include "sway/output.h"
-#include "log.h"
-#include "stringop.h"
-
-#define ASSERT_NONNULL(PTR) \
- sway_assert (PTR, #PTR "must be non-null")
-
-static swayc_t *new_swayc(enum swayc_types type) {
- // next id starts at 1 because 0 is assigned to root_container in layout.c
- static size_t next_id = 1;
- swayc_t *c = calloc(1, sizeof(swayc_t));
- if (!c) {
- return NULL;
- }
- c->id = next_id++;
- c->handle = -1;
- c->gaps = -1;
- c->layout = L_NONE;
- c->workspace_layout = L_NONE;
- c->type = type;
- c->nb_master = 1;
- c->nb_slave_groups = 1;
- if (type != C_VIEW) {
- c->children = create_list();
- }
- return c;
-}
-
-static void free_swayc(swayc_t *cont) {
- if (!ASSERT_NONNULL(cont)) {
- return;
- }
- if (cont->children) {
- // remove children until there are no more, free_swayc calls
- // remove_child, which removes child from this container
- while (cont->children->length) {
- free_swayc(cont->children->items[0]);
- }
- list_free(cont->children);
- }
- if (cont->unmanaged) {
- list_free(cont->unmanaged);
- }
- if (cont->floating) {
- while (cont->floating->length) {
- free_swayc(cont->floating->items[0]);
- }
- list_free(cont->floating);
- }
- if (cont->marks) {
- list_foreach(cont->marks, free);
- list_free(cont->marks);
- }
- if (cont->parent) {
- remove_child(cont);
- }
- if (cont->name) {
- free(cont->name);
- }
- if (cont->class) {
- free(cont->class);
- }
- if (cont->instance) {
- free(cont->instance);
- }
- if (cont->app_id) {
- free(cont->app_id);
- }
- if (cont->bg_pid != 0) {
- terminate_swaybg(cont->bg_pid);
- }
- if (cont->border) {
- if (cont->border->buffer) {
- free(cont->border->buffer);
- }
- free(cont->border);
- }
- free(cont);
-}
-
-static void update_root_geometry() {
- int width = 0;
- int height = 0;
- swayc_t *child;
- int child_width;
- int child_height;
-
- for (int i = 0; i < root_container.children->length; ++i) {
- child = root_container.children->items[i];
- child_width = child->width + child->x;
- child_height = child->height + child->y;
- if (child_width > width) {
- width = child_width;
- }
-
- if (child_height > height) {
- height = child_height;
- }
- }
-
- root_container.width = width;
- root_container.height = height;
-}
-
-// New containers
-
-swayc_t *new_output(wlc_handle handle) {
- struct wlc_size size;
- output_get_scaled_size(handle, &size);
- const char *name = wlc_output_get_name(handle);
- // Find current outputs to see if this already exists
- {
- int i, len = root_container.children->length;
- for (i = 0; i < len; ++i) {
- swayc_t *op = root_container.children->items[i];
- const char *op_name = op->name;
- if (op_name && name && strcmp(op_name, name) == 0) {
- sway_log(L_DEBUG, "restoring output %" PRIuPTR ":%s", handle, op_name);
- return op;
- }
- }
- }
-
- sway_log(L_DEBUG, "New output %" PRIuPTR ":%s", handle, name);
-
- struct output_config *oc = NULL, *all = NULL;
- int i;
- for (i = 0; i < config->output_configs->length; ++i) {
- struct output_config *cur = config->output_configs->items[i];
- if (strcasecmp(name, cur->name) == 0) {
- sway_log(L_DEBUG, "Matched output config for %s", name);
- oc = cur;
- }
- if (strcasecmp("*", cur->name) == 0) {
- sway_log(L_DEBUG, "Matched wildcard output config for %s", name);
- all = cur;
- }
-
- if (oc && all) {
- break;
- }
- }
-
- if (!oc) {
- oc = all;
- }
-
- if (oc && !oc->enabled) {
- return NULL;
- }
-
- swayc_t *output = new_swayc(C_OUTPUT);
- output->handle = handle;
- output->name = name ? strdup(name) : NULL;
- output->width = size.w;
- output->height = size.h;
- output->unmanaged = create_list();
- output->bg_pid = 0;
-
- apply_output_config(oc, output);
- add_child(&root_container, output);
- load_swaybars();
-
- // Create workspace
- char *ws_name = NULL;
- swayc_t *ws = NULL;
-
- if (name) {
- for (i = 0; i < config->workspace_outputs->length; ++i) {
- struct workspace_output *wso = config->workspace_outputs->items[i];
- if (strcasecmp(wso->output, name) == 0) {
- sway_log(L_DEBUG, "Matched workspace to output: %s for %s", wso->workspace, wso->output);
- // Check if any other workspaces are using this name
- if ((ws = workspace_by_name(wso->workspace))) {
- // if yes, move those to this output, because they should be here
- move_workspace_to(ws, output);
- } else if (!ws_name) {
- // set a workspace name in case we need to create a default one
- ws_name = strdup(wso->workspace);
- }
- }
- }
- }
-
- if (output->children->length == 0) {
- if (!ws_name) {
- ws_name = workspace_next_name(output->name);
- }
- // create and initialize default workspace
- sway_log(L_DEBUG, "Creating default workspace %s", ws_name);
- ws = new_workspace(output, ws_name);
- ws->is_focused = true;
- } else {
- sort_workspaces(output);
- set_focused_container(output->children->items[0]);
- }
-
- free(ws_name);
- update_root_geometry();
- return output;
-}
-
-swayc_t *new_workspace(swayc_t *output, const char *name) {
- if (!ASSERT_NONNULL(output)) {
- return NULL;
- }
- sway_log(L_DEBUG, "Added workspace %s for output %u", name, (unsigned int)output->handle);
- swayc_t *workspace = new_swayc(C_WORKSPACE);
-
- workspace->prev_layout = L_NONE;
- workspace->layout = default_layout(output);
- workspace->workspace_layout = default_layout(output);
-
- workspace->x = output->x;
- workspace->y = output->y;
- workspace->width = output->width;
- workspace->height = output->height;
- workspace->name = !name ? NULL : strdup(name);
- workspace->visible = false;
- workspace->floating = create_list();
-
- add_child(output, workspace);
- sort_workspaces(output);
-
- return workspace;
-}
-
-swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) {
- if (!ASSERT_NONNULL(child)
- && !sway_assert(!child->is_floating, "cannot create container around floating window")) {
- return NULL;
- }
- swayc_t *cont = new_swayc(C_CONTAINER);
-
- sway_log(L_DEBUG, "creating container %p around %p", cont, child);
-
- cont->prev_layout = L_NONE;
- cont->layout = layout;
- cont->width = child->width;
- cont->height = child->height;
- cont->x = child->x;
- cont->y = child->y;
- cont->visible = child->visible;
- cont->cached_geometry = child->cached_geometry;
- cont->gaps = child->gaps;
-
- /* Container inherits all of workspaces children, layout and whatnot */
- if (child->type == C_WORKSPACE) {
- swayc_t *workspace = child;
- // reorder focus
- cont->focused = workspace->focused;
- workspace->focused = cont;
- // set all children focu to container
- int i;
- for (i = 0; i < workspace->children->length; ++i) {
- ((swayc_t *)workspace->children->items[i])->parent = cont;
- }
- // Swap children
- list_t *tmp_list = workspace->children;
- workspace->children = cont->children;
- cont->children = tmp_list;
- // add container to workspace chidren
- add_child(workspace, cont);
- // give them proper layouts
- cont->layout = workspace->workspace_layout;
- cont->prev_layout = workspace->prev_layout;
- /* TODO: might break shit in move_container!!! workspace->layout = layout; */
- set_focused_container_for(workspace, get_focused_view(workspace));
- } else { // Or is built around container
- swayc_t *parent = replace_child(child, cont);
- if (parent) {
- add_child(cont, child);
- }
- }
- return cont;
-}
-
-swayc_t *new_view(swayc_t *sibling, wlc_handle handle) {
- if (!ASSERT_NONNULL(sibling)) {
- return NULL;
- }
- const char *title = wlc_view_get_title(handle);
- swayc_t *view = new_swayc(C_VIEW);
- sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%s to container %p %d",
- handle, title, sibling, sibling ? sibling->type : 0);
- // Setup values
- view->handle = handle;
- view->name = title ? strdup(title) : NULL;
- const char *class = wlc_view_get_class(handle);
- view->class = class ? strdup(class) : NULL;
- const char *instance = wlc_view_get_instance(handle);
- view->instance = instance ? strdup(instance) : NULL;
- const char *app_id = wlc_view_get_app_id(handle);
- view->app_id = app_id ? strdup(app_id) : NULL;
- view->visible = true;
- view->is_focused = true;
- view->sticky = false;
- view->width = 0;
- view->height = 0;
- view->desired_width = -1;
- view->desired_height = -1;
- // setup border
- view->border_type = config->border;
- view->border_thickness = config->border_thickness;
-
- view->is_floating = false;
-
- if (sibling->type == C_WORKSPACE) {
- // Case of focused workspace, just create as child of it
- add_child(sibling, view);
- } else {
- // Regular case, create as sibling of current container
- add_sibling(sibling, view);
- }
- return view;
-}
-
-swayc_t *new_floating_view(wlc_handle handle) {
- if (swayc_active_workspace() == NULL) {
- return NULL;
- }
- const char *title = wlc_view_get_title(handle);
- swayc_t *view = new_swayc(C_VIEW);
- sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%x:%s as a floating view",
- handle, wlc_view_get_type(handle), title);
- // Setup values
- view->handle = handle;
- view->name = title ? strdup(title) : NULL;
- const char *class = wlc_view_get_class(handle);
- view->class = class ? strdup(class) : NULL;
- const char *instance = wlc_view_get_instance(handle);
- view->instance = instance ? strdup(instance) : NULL;
- const char *app_id = wlc_view_get_app_id(handle);
- view->app_id = app_id ? strdup(app_id) : NULL;
- view->visible = true;
- view->sticky = false;
-
- // Set the geometry of the floating view
- const struct wlc_geometry *geometry = wlc_view_get_geometry(handle);
-
- // give it requested geometry, but place in center if possible
- // in top left otherwise
- if (geometry->size.w != 0) {
- view->x = (swayc_active_workspace()->width - geometry->size.w) / 2;
- } else {
- view->x = 0;
- }
- if (geometry->size.h != 0) {
- view->y = (swayc_active_workspace()->height - geometry->size.h) / 2;
- } else {
- view->y = 0;
- }
-
- view->width = geometry->size.w;
- view->height = geometry->size.h;
-
- view->desired_width = view->width;
- view->desired_height = view->height;
-
- // setup border
- view->border_type = config->floating_border;
- view->border_thickness = config->floating_border_thickness;
-
- view->is_floating = true;
-
- // Case of focused workspace, just create as child of it
- list_add(swayc_active_workspace()->floating, view);
- view->parent = swayc_active_workspace();
- if (swayc_active_workspace()->focused == NULL) {
- set_focused_container_for(swayc_active_workspace(), view);
- }
- return view;
-}
-
-void floating_view_sane_size(swayc_t *view) {
- // floating_minimum is used as sane value.
- // floating_maximum has priority in case of conflict
- // TODO: implement total_outputs_dimensions()
- if (config->floating_minimum_height != -1 &&
- view->desired_height < config->floating_minimum_height) {
- view->desired_height = config->floating_minimum_height;
- }
- if (config->floating_minimum_width != -1 &&
- view->desired_width < config->floating_minimum_width) {
- view->desired_width = config->floating_minimum_width;
- }
-
- // if 0 do not resize, only enforce max value
- if (config->floating_maximum_height == 0) {
- // Missing total_outputs_dimensions() using swayc_active_workspace()
- config->floating_maximum_height = swayc_active_workspace()->height;
-
- } else if (config->floating_maximum_height != -1 &&
- view->desired_height > config->floating_maximum_height) {
- view->desired_height = config->floating_maximum_height;
- }
-
- // if 0 do not resize, only enforce max value
- if (config->floating_maximum_width == 0) {
- // Missing total_outputs_dimensions() using swayc_active_workspace()
- config->floating_maximum_width = swayc_active_workspace()->width;
-
- } else if (config->floating_maximum_width != -1 &&
- view->desired_width > config->floating_maximum_width) {
- view->desired_width = config->floating_maximum_width;
- }
-
- sway_log(L_DEBUG, "Sane values for view to %d x %d @ %.f, %.f",
- view->desired_width, view->desired_height, view->x, view->y);
-
- return;
-}
-
-
-// Destroy container
-
-swayc_t *destroy_output(swayc_t *output) {
- if (!ASSERT_NONNULL(output)) {
- return NULL;
- }
- if (output->children->length > 0) {
- // TODO save workspaces when there are no outputs.
- // TODO also check if there will ever be no outputs except for exiting
- // program
- if (root_container.children->length > 1) {
- int p = root_container.children->items[0] == output;
- // Move workspace from this output to another output
- while (output->children->length) {
- swayc_t *child = output->children->items[0];
- remove_child(child);
- add_child(root_container.children->items[p], child);
- }
- sort_workspaces(root_container.children->items[p]);
- update_visibility(root_container.children->items[p]);
- arrange_windows(root_container.children->items[p], -1, -1);
- }
- }
- sway_log(L_DEBUG, "OUTPUT: Destroying output '%" PRIuPTR "'", output->handle);
- free_swayc(output);
- update_root_geometry();
- return &root_container;
-}
-
-swayc_t *destroy_workspace(swayc_t *workspace) {
- if (!ASSERT_NONNULL(workspace)) {
- return NULL;
- }
-
- // Do not destroy this if it's the last workspace on this output
- swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
- if (output && output->children->length == 1) {
- return NULL;
- }
-
- swayc_t *parent = workspace->parent;
- // destroy the WS if there are no children
- if (workspace->children->length == 0 && workspace->floating->length == 0) {
- sway_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
- ipc_event_workspace(workspace, NULL, "empty");
- } else {
- // Move children to a different workspace on this output
- swayc_t *new_workspace = NULL;
- int i;
- for(i = 0; i < output->children->length; i++) {
- if(output->children->items[i] != workspace) {
- break;
- }
- }
- new_workspace = output->children->items[i];
-
- sway_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'",
- workspace->name, new_workspace->name);
-
- for(i = 0; i < workspace->children->length; i++) {
- move_container_to(workspace->children->items[i], new_workspace);
- }
-
- for(i = 0; i < workspace->floating->length; i++) {
- move_container_to(workspace->floating->items[i], new_workspace);
- }
- }
-
- free_swayc(workspace);
- return parent;
-}
-
-swayc_t *destroy_container(swayc_t *container) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- while (container->children->length == 0 && container->type == C_CONTAINER) {
- sway_log(L_DEBUG, "Container: Destroying container '%p'", container);
- swayc_t *parent = container->parent;
- free_swayc(container);
- container = parent;
- }
- return container;
-}
-
-swayc_t *destroy_view(swayc_t *view) {
- if (!ASSERT_NONNULL(view)) {
- return NULL;
- }
- sway_log(L_DEBUG, "Destroying view '%p'", view);
- swayc_t *parent = view->parent;
- free_swayc(view);
-
- // Destroy empty containers
- if (parent && parent->type == C_CONTAINER) {
- return destroy_container(parent);
- }
- return parent;
-}
-
-// Container lookup
-
-
-swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data) {
- if (!container->children) {
- return NULL;
- }
- // Special case for checking floating stuff
- int i;
- if (container->type == C_WORKSPACE) {
- for (i = 0; i < container->floating->length; ++i) {
- swayc_t *child = container->floating->items[i];
- if (test(child, data)) {
- return child;
- }
- }
- }
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- if (test(child, data)) {
- return child;
- } else {
- swayc_t *res = swayc_by_test(child, test, data);
- if (res) {
- return res;
- }
- }
- }
- return NULL;
-}
-
-static bool test_name(swayc_t *view, void *data) {
- if (!view || !view->name) {
- return false;
- }
- return strcmp(view->name, data) == 0;
-}
-
-swayc_t *swayc_by_name(const char *name) {
- return swayc_by_test(&root_container, test_name, (void *)name);
-}
-
-swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
- return NULL;
- }
- do {
- container = container->parent;
- } while (container && container->type != type);
- return container;
-}
-
-swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts layout) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
- return NULL;
- }
- do {
- container = container->parent;
- } while (container && container->layout != layout);
- return container;
-}
-
-swayc_t *swayc_focus_by_type(swayc_t *container, enum swayc_types type) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
- return NULL;
- }
- do {
- container = container->focused;
- } while (container && container->type != type);
- return container;
-}
-
-swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts layout) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
- return NULL;
- }
- do {
- container = container->focused;
- } while (container && container->layout != layout);
- return container;
-}
-
-
-static swayc_t *_swayc_by_handle_helper(wlc_handle handle, swayc_t *parent) {
- if (!parent || !parent->children) {
- return NULL;
- }
- int i, len;
- swayc_t **child;
- if (parent->type == C_WORKSPACE) {
- len = parent->floating->length;
- child = (swayc_t **)parent->floating->items;
- for (i = 0; i < len; ++i, ++child) {
- if ((*child)->handle == handle) {
- return *child;
- }
- }
- }
-
- len = parent->children->length;
- child = (swayc_t**)parent->children->items;
- for (i = 0; i < len; ++i, ++child) {
- if ((*child)->handle == handle) {
- return *child;
- } else {
- swayc_t *res;
- if ((res = _swayc_by_handle_helper(handle, *child))) {
- return res;
- }
- }
- }
- return NULL;
-}
-
-swayc_t *swayc_by_handle(wlc_handle handle) {
- return _swayc_by_handle_helper(handle, &root_container);
-}
-
-swayc_t *swayc_active_output(void) {
- return root_container.focused;
-}
-
-swayc_t *swayc_active_workspace(void) {
- return root_container.focused ? root_container.focused->focused : NULL;
-}
-
-swayc_t *swayc_active_workspace_for(swayc_t *cont) {
- if (!cont) {
- return NULL;
- }
- switch (cont->type) {
- case C_ROOT:
- cont = cont->focused;
- /* Fallthrough */
-
- case C_OUTPUT:
- cont = cont ? cont->focused : NULL;
- /* Fallthrough */
-
- case C_WORKSPACE:
- return cont;
-
- default:
- return swayc_parent_by_type(cont, C_WORKSPACE);
- }
-}
-
-static bool pointer_test(swayc_t *view, void *_origin) {
- const struct wlc_point *origin = _origin;
- // Determine the output that the view is under
- swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT);
- if (origin->x >= view->x && origin->y >= view->y
- && origin->x < view->x + view->width && origin->y < view->y + view->height
- && view->visible && parent == root_container.focused) {
- return true;
- }
- return false;
-}
-
-swayc_t *container_under_pointer(void) {
- // root.output->workspace
- if (!root_container.focused) {
- return NULL;
- }
- swayc_t *lookup = root_container.focused;
- // Case of empty workspace
- if (lookup->children && !lookup->unmanaged) {
- return NULL;
- }
- double x, y;
- wlc_pointer_get_position_v2(&x, &y);
- struct wlc_point origin = { .x = x, .y = y };
-
- while (lookup && lookup->type != C_VIEW) {
- int i;
- int len;
- for (int _i = 0; lookup->unmanaged && _i < lookup->unmanaged->length; ++_i) {
- wlc_handle *handle = lookup->unmanaged->items[_i];
- const struct wlc_geometry *geo = wlc_view_get_geometry(*handle);
- if (origin.x >= geo->origin.x && origin.y >= geo->origin.y
- && origin.x < geo->origin.x + (int)geo->size.w
- && origin.y < geo->origin.y + (int)geo->size.h) {
- // Hack: we force focus upon unmanaged views here
- wlc_view_focus(*handle);
- return NULL;
- }
- }
- // if tabbed/stacked go directly to focused container, otherwise search
- // children
- if (lookup->layout == L_TABBED || lookup->layout == L_STACKED) {
- lookup = lookup->focused;
- continue;
- }
- // if workspace, search floating
- if (lookup->type == C_WORKSPACE) {
- i = len = lookup->floating->length;
- bool got_floating = false;
- while (--i > -1) {
- if (pointer_test(lookup->floating->items[i], &origin)) {
- lookup = lookup->floating->items[i];
- got_floating = true;
- break;
- }
- }
- if (got_floating) {
- continue;
- }
- }
- // search children
- len = lookup->children->length;
- for (i = 0; i < len; ++i) {
- if (pointer_test(lookup->children->items[i], &origin)) {
- lookup = lookup->children->items[i];
- break;
- }
- }
- // when border and titles are done, this could happen
- if (i == len) {
- break;
- }
- }
- return lookup;
-}
-
-swayc_t *container_find(swayc_t *container, bool (*f)(swayc_t *, const void *), const void *data) {
- if (container->children == NULL || container->children->length == 0) {
- return NULL;
- }
-
- swayc_t *con;
- if (container->type == C_WORKSPACE) {
- for (int i = 0; i < container->floating->length; ++i) {
- con = container->floating->items[i];
- if (f(con, data)) {
- return con;
- }
- con = container_find(con, f, data);
- if (con != NULL) {
- return con;
- }
- }
- }
-
- for (int i = 0; i < container->children->length; ++i) {
- con = container->children->items[i];
- if (f(con, data)) {
- return con;
- }
-
- con = container_find(con, f, data);
- if (con != NULL) {
- return con;
- }
- }
-
- return NULL;
-}
-
-// Container information
-
-bool swayc_is_fullscreen(swayc_t *view) {
- return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN);
-}
-
-bool swayc_is_active(swayc_t *view) {
- return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_ACTIVATED);
-}
-
-bool swayc_is_parent_of(swayc_t *parent, swayc_t *child) {
- while (child != &root_container) {
- child = child->parent;
- if (child == parent) {
- return true;
- }
- }
- return false;
-}
-
-bool swayc_is_child_of(swayc_t *child, swayc_t *parent) {
- return swayc_is_parent_of(parent, child);
-}
-
-bool swayc_is_empty_workspace(swayc_t *container) {
- return container->type == C_WORKSPACE && container->children->length == 0;
-}
-
-int swayc_gap(swayc_t *container) {
- if (container->type == C_VIEW || container->type == C_CONTAINER) {
- return container->gaps >= 0 ? container->gaps : config->gaps_inner;
- } else if (container->type == C_WORKSPACE) {
- int base = container->gaps >= 0 ? container->gaps : config->gaps_outer;
- if (config->edge_gaps && !(config->smart_gaps && container->children->length == 1)) {
- // the inner gap is created via a margin around each window which
- // is half the gap size, so the workspace also needs half a gap
- // size to make the outermost gap the same size (excluding the
- // actual "outer gap" size which is handled independently)
- return base + config->gaps_inner / 2;
- } else if (config->smart_gaps && container->children->length == 1) {
- return 0;
- } else {
- return base;
- }
- } else {
- return 0;
- }
-}
-
-// Mapping
-
-void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) {
- if (container) {
- int i;
- if (container->children) {
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- container_map(child, f, data);
- }
- }
- if (container->floating) {
- for (i = 0; i < container->floating->length; ++i) {
- swayc_t *child = container->floating->items[i];
- container_map(child, f, data);
- }
- }
- f(container, data);
- }
-}
-
-void update_visibility_output(swayc_t *container, wlc_handle output) {
- // Inherit visibility
- swayc_t *parent = container->parent;
- container->visible = parent->visible;
- // special cases where visibility depends on focus
- if (parent->type == C_OUTPUT || parent->layout == L_TABBED ||
- parent->layout == L_STACKED) {
- container->visible = parent->focused == container && parent->visible;
- }
- // Set visibility and output for view
- if (container->type == C_VIEW) {
- wlc_view_set_output(container->handle, output);
- wlc_view_set_mask(container->handle, container->visible ? VISIBLE : 0);
- }
- // Update visibility for children
- else {
- if (container->children) {
- int i, len = container->children->length;
- for (i = 0; i < len; ++i) {
- update_visibility_output(container->children->items[i], output);
- }
- }
- if (container->floating) {
- int i, len = container->floating->length;
- for (i = 0; i < len; ++i) {
- update_visibility_output(container->floating->items[i], output);
- }
- }
- }
-}
-
-void update_visibility(swayc_t *container) {
- if (!container) return;
- switch (container->type) {
- case C_ROOT:
- container->visible = true;
- if (container->children) {
- int i, len = container->children->length;
- for (i = 0; i < len; ++i) {
- update_visibility(container->children->items[i]);
- }
- }
- return;
-
- case C_OUTPUT:
- container->visible = true;
- if (container->children) {
- int i, len = container->children->length;
- for (i = 0; i < len; ++i) {
- update_visibility_output(container->children->items[i], container->handle);
- }
- }
- return;
-
- default:
- {
- swayc_t *op = swayc_parent_by_type(container, C_OUTPUT);
- update_visibility_output(container, op->handle);
- }
- }
-}
-
-void set_gaps(swayc_t *view, void *_data) {
- int *data = _data;
- if (!ASSERT_NONNULL(view)) {
- return;
- }
- if (view->type == C_WORKSPACE || view->type == C_VIEW) {
- view->gaps = *data;
- }
-}
-
-void add_gaps(swayc_t *view, void *_data) {
- int *data = _data;
- if (!ASSERT_NONNULL(view)) {
- return;
- }
- if (view->type == C_WORKSPACE || view->type == C_VIEW) {
- if ((view->gaps += *data) < 0) {
- view->gaps = 0;
- }
- }
-}
-
-static void close_view(swayc_t *container, void *data) {
- if (container->type == C_VIEW) {
- wlc_view_close(container->handle);
- }
-}
-
-void close_views(swayc_t *container) {
- container_map(container, close_view, NULL);
-}
-
-swayc_t *swayc_tabbed_stacked_ancestor(swayc_t *view) {
- swayc_t *parent = NULL;
- if (!ASSERT_NONNULL(view)) {
- return NULL;
- }
- while (view->type != C_WORKSPACE && view->parent && view->parent->type != C_WORKSPACE) {
- view = view->parent;
- if (view->layout == L_TABBED || view->layout == L_STACKED) {
- parent = view;
- }
- }
-
- return parent;
-}
-
-swayc_t *swayc_tabbed_stacked_parent(swayc_t *con) {
- if (!ASSERT_NONNULL(con)) {
- return NULL;
- }
- if (con->parent && (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED)) {
- return con->parent;
- }
- return NULL;
-}
-
-swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) {
- // if layout change modifies the auto layout's major axis, swap width and height
- // to preserve current ratios.
- if (is_auto_layout(layout) && is_auto_layout(container->layout)) {
- enum swayc_layouts prev_major =
- container->layout == L_AUTO_LEFT || container->layout == L_AUTO_RIGHT
- ? L_HORIZ : L_VERT;
- enum swayc_layouts new_major =
- layout == L_AUTO_LEFT || layout == L_AUTO_RIGHT
- ? L_HORIZ : L_VERT;
- if (new_major != prev_major) {
- for (int i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- double h = child->height;
- child->height = child->width;
- child->width = h;
- }
- }
- }
- if (container->type == C_WORKSPACE) {
- container->workspace_layout = layout;
- if (layout == L_HORIZ || layout == L_VERT || is_auto_layout(layout)) {
- container->layout = layout;
- }
- } else {
- container->layout = layout;
- }
- return container;
-}
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 @@
#include <stdbool.h>
#include <pcre.h>
#include "sway/criteria.h"
-#include "sway/container.h"
+#include "sway/tree/container.h"
#include "sway/config.h"
+#include "sway/tree/view.h"
#include "stringop.h"
#include "list.h"
#include "log.h"
enum criteria_type { // *must* keep in sync with criteria_strings[]
+ CRIT_APP_ID,
CRIT_CLASS,
CRIT_CON_ID,
CRIT_CON_MARK,
@@ -27,6 +29,7 @@ enum criteria_type { // *must* keep in sync with criteria_strings[]
};
static const char * const criteria_strings[CRIT_LAST] = {
+ [CRIT_APP_ID] = "app_id",
[CRIT_CLASS] = "class",
[CRIT_CON_ID] = "con_id",
[CRIT_CON_MARK] = "con_mark",
@@ -100,8 +103,9 @@ static int countchr(char *str, char c) {
// of buf.
//
// Returns error string or NULL if successful.
-static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str) {
- sway_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str);
+static char *crit_tokens(int *argc, char ***buf,
+ const char * const criteria_str) {
+ wlr_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str);
char *base = criteria_from(criteria_str);
char *head = base;
char *namep = head; // start of criteria name
@@ -247,13 +251,13 @@ char *extract_crit_tokens(list_t *tokens, const char * const criteria) {
free_crit_token(token);
goto ect_cleanup;
} else if (token->type == CRIT_URGENT || crit_is_focused(value)) {
- sway_log(L_DEBUG, "%s -> \"%s\"", name, value);
+ wlr_log(L_DEBUG, "%s -> \"%s\"", name, value);
list_add(tokens, token);
} else if((error = generate_regex(&token->regex, value))) {
free_crit_token(token);
goto ect_cleanup;
} else {
- sway_log(L_DEBUG, "%s -> /%s/", name, value);
+ wlr_log(L_DEBUG, "%s -> /%s/", name, value);
list_add(tokens, token);
}
}
@@ -268,8 +272,8 @@ static int regex_cmp(const char *item, const pcre *regex) {
}
// test a single view if it matches list of criteria tokens (all of them).
-static bool criteria_test(swayc_t *cont, list_t *tokens) {
- if (cont->type != C_VIEW) {
+static bool criteria_test(struct sway_container *cont, list_t *tokens) {
+ if (cont->type != C_CONTAINER && cont->type != C_VIEW) {
return false;
}
int matches = 0;
@@ -277,94 +281,85 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) {
struct crit_token *crit = tokens->items[i];
switch (crit->type) {
case CRIT_CLASS:
- if (!cont->class) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused = get_focused_view(&root_container);
- if (focused->class && strcmp(cont->class, focused->class) == 0) {
+ {
+ const char *class = view_get_class(cont->sway_view);
+ if (!class) {
+ break;
+ }
+ if (crit->regex && regex_cmp(class, crit->regex) == 0) {
matches++;
}
- } else if (crit->regex && regex_cmp(cont->class, crit->regex) == 0) {
- matches++;
+ break;
}
- break;
- case CRIT_CON_ID: {
- char *endptr;
- size_t crit_id = strtoul(crit->raw, &endptr, 10);
+ case CRIT_CON_ID:
+ {
+ char *endptr;
+ size_t crit_id = strtoul(crit->raw, &endptr, 10);
- if (*endptr == 0 && cont->id == crit_id) {
- ++matches;
- }
- break;
- }
- case CRIT_CON_MARK:
- if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) {
- // Make sure it isn't matching the NUL string
- if ((strcmp(crit->raw, "") == 0) == (list_seq_find(cont->marks, (int (*)(const void *, const void *))strcmp, "") != -1)) {
+ if (*endptr == 0 && cont->id == crit_id) {
++matches;
}
+ break;
}
+ case CRIT_CON_MARK:
+ // TODO
break;
case CRIT_FLOATING:
- if (cont->is_floating) {
- matches++;
- }
+ // TODO
break;
case CRIT_ID:
- if (!cont->app_id) {
- // ignore
- } else if (crit->regex && regex_cmp(cont->app_id, crit->regex) == 0) {
- matches++;
- }
+ // TODO
break;
+ case CRIT_APP_ID:
+ {
+ const char *app_id = view_get_app_id(cont->sway_view);
+ if (!app_id) {
+ break;
+ }
+
+ if (crit->regex && regex_cmp(app_id, crit->regex) == 0) {
+ matches++;
+ }
+ break;
+ }
case CRIT_INSTANCE:
- if (!cont->instance) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused = get_focused_view(&root_container);
- if (focused->instance && strcmp(cont->instance, focused->instance) == 0) {
+ {
+ const char *instance = view_get_instance(cont->sway_view);
+ if (!instance) {
+ break;
+ }
+
+ if (crit->regex && regex_cmp(instance, crit->regex) == 0) {
matches++;
}
- } else if (crit->regex && regex_cmp(cont->instance, crit->regex) == 0) {
- matches++;
+ break;
}
- break;
case CRIT_TILING:
- if (!cont->is_floating) {
- matches++;
- }
+ // TODO
break;
case CRIT_TITLE:
- if (!cont->name) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused = get_focused_view(&root_container);
- if (focused->name && strcmp(cont->name, focused->name) == 0) {
+ {
+ const char *title = view_get_title(cont->sway_view);
+ if (!title) {
+ break;
+ }
+
+ if (crit->regex && regex_cmp(title, crit->regex) == 0) {
matches++;
}
- } else if (crit->regex && regex_cmp(cont->name, crit->regex) == 0) {
- matches++;
+ break;
}
- break;
- case CRIT_URGENT: // "latest" or "oldest"
+ case CRIT_URGENT:
+ // TODO "latest" or "oldest"
break;
case CRIT_WINDOW_ROLE:
+ // TODO
break;
case CRIT_WINDOW_TYPE:
- // TODO wlc indeed exposes this information
+ // TODO
break;
- case CRIT_WORKSPACE: ;
- swayc_t *cont_ws = swayc_parent_by_type(cont, C_WORKSPACE);
- if (!cont_ws || !cont_ws->name) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused_ws = swayc_active_workspace();
- if (focused_ws->name && strcmp(cont_ws->name, focused_ws->name) == 0) {
- matches++;
- }
- } else if (crit->regex && regex_cmp(cont_ws->name, crit->regex) == 0) {
- matches++;
- }
+ case CRIT_WORKSPACE:
+ // TODO
break;
default:
sway_abort("Invalid criteria type (%i)", crit->type);
@@ -403,7 +398,7 @@ void free_criteria(struct criteria *crit) {
free(crit);
}
-bool criteria_any(swayc_t *cont, list_t *criteria) {
+bool criteria_any(struct sway_container *cont, list_t *criteria) {
for (int i = 0; i < criteria->length; i++) {
struct criteria *bc = criteria->items[i];
if (criteria_test(cont, bc->tokens)) {
@@ -413,7 +408,7 @@ bool criteria_any(swayc_t *cont, list_t *criteria) {
return false;
}
-list_t *criteria_for(swayc_t *cont) {
+list_t *criteria_for(struct sway_container *cont) {
list_t *criteria = config->criteria, *matches = create_list();
for (int i = 0; i < criteria->length; i++) {
struct criteria *bc = criteria->items[i];
@@ -429,23 +424,22 @@ struct list_tokens {
list_t *tokens;
};
-static void container_match_add(swayc_t *container, struct list_tokens *list_tokens) {
+static void container_match_add(struct sway_container *container,
+ struct list_tokens *list_tokens) {
if (criteria_test(container, list_tokens->tokens)) {
list_add(list_tokens->list, container);
}
}
-list_t *container_for(list_t *tokens) {
- struct list_tokens list_tokens = (struct list_tokens){create_list(), tokens};
+list_t *container_for_crit_tokens(list_t *tokens) {
+ struct list_tokens list_tokens =
+ (struct list_tokens){create_list(), tokens};
- container_map(&root_container, (void (*)(swayc_t *, void *))container_match_add, &list_tokens);
-
- for (int i = 0; i < scratchpad->length; ++i) {
- swayc_t *c = scratchpad->items[i];
- if (criteria_test(c, tokens)) {
- list_add(list_tokens.list, c);
- }
- }
+ container_for_each_descendant_dfs(&root_container,
+ (void (*)(struct sway_container *, void *))container_match_add,
+ &list_tokens);
+ // TODO look in the scratchpad
+
return list_tokens.list;
}
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 @@
+#include <pango/pangocairo.h>
+#include <wlr/backend.h>
+#include <wlr/render/wlr_texture.h>
+#include <wlr/util/log.h>
+#include "config.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/server.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "cairo.h"
+#include "config.h"
+#include "pango.h"
+
+static const char *layout_to_str(enum sway_container_layout layout) {
+ switch (layout) {
+ case L_HORIZ:
+ return "L_HORIZ";
+ case L_VERT:
+ return "L_VERT";
+ case L_STACKED:
+ return "L_STACKED";
+ case L_TABBED:
+ return "L_TABBED";
+ case L_FLOATING:
+ return "L_FLOATING";
+ case L_NONE:
+ default:
+ return "L_NONE";
+ }
+}
+
+static int draw_container(cairo_t *cairo, struct sway_container *container,
+ struct sway_container *focus, int x, int y) {
+ int text_width, text_height;
+ get_text_size(cairo, "monospace", &text_width, &text_height,
+ 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f",
+ container_type_to_str(container->type), container->id, container->name,
+ layout_to_str(container->layout),
+ container->width, container->height, container->x, container->y);
+ cairo_save(cairo);
+ cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height);
+ cairo_set_source_u32(cairo, 0xFFFFFFE0);
+ cairo_fill(cairo);
+ int height = text_height;
+ if (container->children) {
+ for (int i = 0; i < container->children->length; ++i) {
+ struct sway_container *child = container->children->items[i];
+ if (child->parent == container) {
+ cairo_set_source_u32(cairo, 0x000000FF);
+ } else {
+ cairo_set_source_u32(cairo, 0xFF0000FF);
+ }
+ height += draw_container(cairo, child, focus, x + 10, y + height);
+ }
+ }
+ cairo_set_source_u32(cairo, 0xFFFFFFE0);
+ cairo_rectangle(cairo, x, y, 2, height);
+ cairo_fill(cairo);
+ cairo_restore(cairo);
+ cairo_move_to(cairo, x, y);
+ if (focus == container) {
+ cairo_set_source_u32(cairo, 0x0000FFFF);
+ }
+ pango_printf(cairo, "monospace", 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f",
+ container_type_to_str(container->type), container->id, container->name,
+ layout_to_str(container->layout),
+ container->width, container->height, container->x, container->y);
+ return height;
+}
+
+bool enable_debug_tree = false;
+
+void update_debug_tree() {
+ if (!enable_debug_tree) {
+ return;
+ }
+
+ int width = 640, height = 480;
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *container = root_container.children->items[i];
+ if (container->width > width) {
+ width = container->width;
+ }
+ if (container->height > height) {
+ height = container->height;
+ }
+ }
+ cairo_surface_t *surface =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ cairo_t *cairo = cairo_create(surface);
+ PangoContext *pango = pango_cairo_create_context(cairo);
+
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ break;
+ }
+
+ struct sway_container *focus = NULL;
+ if (seat != NULL) {
+ focus = seat_get_focus(seat);
+ }
+ cairo_set_source_u32(cairo, 0x000000FF);
+ draw_container(cairo, &root_container, focus, 0, 0);
+
+ cairo_surface_flush(surface);
+ struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend);
+ if (root_container.sway_root->debug_tree) {
+ wlr_texture_destroy(root_container.sway_root->debug_tree);
+ }
+ unsigned char *data = cairo_image_surface_get_data(surface);
+ int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
+ struct wlr_texture *texture = wlr_texture_from_pixels(renderer,
+ WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
+ root_container.sway_root->debug_tree = texture;
+ cairo_surface_destroy(surface);
+ g_object_unref(pango);
+ cairo_destroy(cairo);
+}
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 @@
-#include "log.h"
-#include "sway.h"
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <libgen.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <string.h>
-#include <stringop.h>
-#include "sway/workspace.h"
-
-/* XXX:DEBUG:XXX */
-static void container_log(const swayc_t *c, int depth) {
- fprintf(stderr, "focus:%c",
- c == get_focused_view(&root_container) ? 'K':
- c == get_focused_container(&root_container) ? 'F' : // Focused
- c == swayc_active_workspace() ? 'W' : // active workspace
- c == &root_container ? 'R' : // root
- 'X');// not any others
- for (int i = 6; i > depth; i--) { fprintf(stderr, " "); }
- fprintf(stderr,"|(%p)",c);
- fprintf(stderr,"(p:%-8p)",c->parent);
- fprintf(stderr,"(f:%-8p)",c->focused);
- fprintf(stderr,"(h:%2" PRIuPTR ")",c->handle);
- fprintf(stderr,"Type:%-4s|",
- c->type == C_ROOT ? "root" :
- c->type == C_OUTPUT ? "op" :
- c->type == C_WORKSPACE ? "ws" :
- c->type == C_CONTAINER ? "cont" :
- c->type == C_VIEW ? "view" : "?");
- fprintf(stderr,"layout:%-5s|",
- c->layout == L_NONE ? "-" :
- c->layout == L_HORIZ ? "Horiz":
- c->layout == L_VERT ? "Vert":
- c->layout == L_STACKED ? "Stack":
- c->layout == L_TABBED ? "Tab":
- c->layout == L_FLOATING ? "Float":
- c->layout == L_AUTO_LEFT ? "A_lft":
- c->layout == L_AUTO_RIGHT ? "A_rgt":
- c->layout == L_AUTO_TOP ? "A_top":
- c->layout == L_AUTO_BOTTOM ? "A_bot":
- "Unknown");
- fprintf(stderr, "w:%4.f|h:%4.f|", c->width, c->height);
- fprintf(stderr, "x:%4.f|y:%4.f|", c->x, c->y);
- fprintf(stderr, "g:%3d|",c->gaps);
- fprintf(stderr, "vis:%c|", c->visible?'t':'f');
- fprintf(stderr, "children:%2d|",c->children?c->children->length:0);
- fprintf(stderr, "name:%.16s\n", c->name);
-}
-void layout_log(const swayc_t *c, int depth) {
- if (L_DEBUG > get_log_level()) return;
- int i, d;
- int e = c->children ? c->children->length : 0;
- container_log(c, depth);
- if (e) {
- for (i = 0; i < e; ++i) {
- fputc('|',stderr);
- for (d = 0; d < depth; ++d) fputc('-', stderr);
- layout_log(c->children->items[i], depth + 1);
- }
- }
- if (c->type == C_WORKSPACE) {
- e = c->floating?c->floating->length:0;
- if (e) {
- for (i = 0; i < e; ++i) {
- fputc('|',stderr);
- for (d = 0; d < depth; ++d) fputc('=', stderr);
- layout_log(c->floating->items[i], depth + 1);
- }
- }
- }
-}
-
-const char *swayc_type_string(enum swayc_types type) {
- return type == C_ROOT ? "ROOT" :
- type == C_OUTPUT ? "OUTPUT" :
- type == C_WORKSPACE ? "WORKSPACE" :
- type == C_CONTAINER ? "CONTAINER" :
- type == C_VIEW ? "VIEW" :
- "UNKNOWN";
-}
-
-// Like sway_log, but also appends some info about given container to log output.
-void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) {
- sway_assert(cont, "swayc_log: no container ...");
- va_list args;
- va_start(args, format);
- char *txt = malloc(128);
- vsprintf(txt, format, args);
- va_end(args);
-
- char *debug_txt = malloc(32);
- snprintf(debug_txt, 32, "%s '%s'", swayc_type_string(cont->type), cont->name);
-
- sway_log(verbosity, "%s (%s)", txt, debug_txt);
- free(txt);
- free(debug_txt);
-}
-
-/* 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 @@
+#include "sway/tree/container.h"
+#include "sway/desktop.h"
+#include "sway/output.h"
+
+void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
+ bool whole) {
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *cont = root_container.children->items[i];
+ if (cont->type == C_OUTPUT) {
+ output_damage_surface(cont->sway_output, lx - cont->x, ly - cont->y,
+ surface, whole);
+ }
+ }
+}
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 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_layer_shell.h>
+#include <wlr/types/wlr_output_damage.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/util/log.h>
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/layers.h"
+#include "sway/output.h"
+#include "sway/server.h"
+#include "sway/tree/layout.h"
+
+static void apply_exclusive(struct wlr_box *usable_area,
+ uint32_t anchor, int32_t exclusive,
+ int32_t margin_top, int32_t margin_right,
+ int32_t margin_bottom, int32_t margin_left) {
+ if (exclusive <= 0) {
+ return;
+ }
+ struct {
+ uint32_t anchors;
+ int *positive_axis;
+ int *negative_axis;
+ int margin;
+ } edges[] = {
+ {
+ .anchors =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP,
+ .positive_axis = &usable_area->y,
+ .negative_axis = &usable_area->height,
+ .margin = margin_top,
+ },
+ {
+ .anchors =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
+ .positive_axis = NULL,
+ .negative_axis = &usable_area->height,
+ .margin = margin_bottom,
+ },
+ {
+ .anchors =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
+ .positive_axis = &usable_area->x,
+ .negative_axis = &usable_area->width,
+ .margin = margin_left,
+ },
+ {
+ .anchors =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
+ .positive_axis = NULL,
+ .negative_axis = &usable_area->width,
+ .margin = margin_right,
+ },
+ };
+ for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
+ if ((anchor & edges[i].anchors) == edges[i].anchors) {
+ if (edges[i].positive_axis) {
+ *edges[i].positive_axis += exclusive + edges[i].margin;
+ }
+ if (edges[i].negative_axis) {
+ *edges[i].negative_axis -= exclusive + edges[i].margin;
+ }
+ }
+ }
+}
+
+static void arrange_layer(struct sway_output *output, struct wl_list *list,
+ struct wlr_box *usable_area, bool exclusive) {
+ struct sway_layer_surface *sway_layer;
+ struct wlr_box full_area = { 0 };
+ wlr_output_effective_resolution(output->wlr_output,
+ &full_area.width, &full_area.height);
+ wl_list_for_each(sway_layer, list, link) {
+ struct wlr_layer_surface *layer = sway_layer->layer_surface;
+ struct wlr_layer_surface_state *state = &layer->current;
+ if (exclusive != (state->exclusive_zone > 0)) {
+ continue;
+ }
+ struct wlr_box bounds;
+ if (state->exclusive_zone == -1) {
+ bounds = full_area;
+ } else {
+ bounds = *usable_area;
+ }
+ struct wlr_box box = {
+ .width = state->desired_width,
+ .height = state->desired_height
+ };
+ // Horizontal axis
+ const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
+ if ((state->anchor & both_horiz) && box.width == 0) {
+ box.x = bounds.x;
+ box.width = bounds.width;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
+ box.x = bounds.x;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
+ box.x = bounds.x + (bounds.width - box.width);
+ } else {
+ box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
+ }
+ // Vertical axis
+ const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
+ if ((state->anchor & both_vert) && box.height == 0) {
+ box.y = bounds.y;
+ box.height = bounds.height;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
+ box.y = bounds.y;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
+ box.y = bounds.y + (bounds.height - box.height);
+ } else {
+ box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
+ }
+ // Margin
+ if ((state->anchor & both_horiz) == both_horiz) {
+ box.x += state->margin.left;
+ box.width -= state->margin.left + state->margin.right;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
+ box.x += state->margin.left;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
+ box.x -= state->margin.right;
+ }
+ if ((state->anchor & both_vert) == both_vert) {
+ box.y += state->margin.top;
+ box.height -= state->margin.top + state->margin.bottom;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
+ box.y += state->margin.top;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
+ box.y -= state->margin.bottom;
+ }
+ if (box.width < 0 || box.height < 0) {
+ // TODO: Bubble up a protocol error?
+ wlr_layer_surface_close(layer);
+ continue;
+ }
+ // Apply
+ sway_layer->geo = box;
+ apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
+ state->margin.top, state->margin.right,
+ state->margin.bottom, state->margin.left);
+ wlr_layer_surface_configure(layer, box.width, box.height);
+ }
+}
+
+void arrange_layers(struct sway_output *output) {
+ struct wlr_box usable_area = { 0 };
+ wlr_output_effective_resolution(output->wlr_output,
+ &usable_area.width, &usable_area.height);
+
+ // Arrange exclusive surfaces from top->bottom
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
+ &usable_area, true);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
+ &usable_area, true);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
+ &usable_area, true);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
+ &usable_area, true);
+
+ if (memcmp(&usable_area, &output->usable_area,
+ sizeof(struct wlr_box)) != 0) {
+ wlr_log(L_DEBUG, "Usable area changed, rearranging output");
+ memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
+ arrange_windows(output->swayc, -1, -1);
+ }
+
+ // Arrange non-exlusive surfaces from top->bottom
+ usable_area.x = usable_area.y = 0;
+ wlr_output_effective_resolution(output->wlr_output,
+ &usable_area.width, &usable_area.height);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
+ &usable_area, false);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
+ &usable_area, false);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
+ &usable_area, false);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
+ &usable_area, false);
+
+ // Find topmost keyboard interactive layer, if such a layer exists
+ uint32_t layers_above_shell[] = {
+ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
+ ZWLR_LAYER_SHELL_V1_LAYER_TOP,
+ };
+ size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
+ struct sway_layer_surface *layer, *topmost = NULL;
+ for (size_t i = 0; i < nlayers; ++i) {
+ wl_list_for_each_reverse(layer,
+ &output->layers[layers_above_shell[i]], link) {
+ if (layer->layer_surface->current.keyboard_interactive) {
+ topmost = layer;
+ break;
+ }
+ }
+ if (topmost != NULL) {
+ break;
+ }
+ }
+
+ struct sway_seat *seat;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ seat_set_focus_layer(seat, topmost ? topmost->layer_surface : NULL);
+ }
+}
+
+static void handle_output_destroy(struct wl_listener *listener, void *data) {
+ struct sway_layer_surface *sway_layer =
+ wl_container_of(listener, sway_layer, output_destroy);
+ wl_list_remove(&sway_layer->output_destroy.link);
+ sway_layer->layer_surface->output = NULL;
+ wlr_layer_surface_close(sway_layer->layer_surface);
+}
+
+static void handle_surface_commit(struct wl_listener *listener, void *data) {
+ struct sway_layer_surface *layer =
+ wl_container_of(listener, layer, surface_commit);
+ struct wlr_layer_surface *layer_surface = layer->layer_surface;
+ struct wlr_output *wlr_output = layer_surface->output;
+ if (wlr_output == NULL) {
+ return;
+ }
+
+ struct sway_output *output = wlr_output->data;
+ struct wlr_box old_geo = layer->geo;
+ arrange_layers(output);
+ if (memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0) {
+ output_damage_surface(output, old_geo.x, old_geo.y,
+ layer_surface->surface, true);
+ output_damage_surface(output, layer->geo.x, layer->geo.y,
+ layer_surface->surface, true);
+ } else {
+ output_damage_surface(output, layer->geo.x, layer->geo.y,
+ layer_surface->surface, false);
+ }
+}
+
+static void unmap(struct sway_layer_surface *sway_layer) {
+ struct wlr_output *wlr_output = sway_layer->layer_surface->output;
+ if (wlr_output == NULL) {
+ return;
+ }
+ struct sway_output *output = wlr_output->data;
+ output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
+ sway_layer->layer_surface->surface, true);
+}
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_layer_surface *sway_layer =
+ wl_container_of(listener, sway_layer, destroy);
+ wlr_log(L_DEBUG, "Layer surface destroyed (%s)",
+ sway_layer->layer_surface->namespace);
+ if (sway_layer->layer_surface->mapped) {
+ unmap(sway_layer);
+ }
+ wl_list_remove(&sway_layer->link);
+ wl_list_remove(&sway_layer->destroy.link);
+ wl_list_remove(&sway_layer->map.link);
+ wl_list_remove(&sway_layer->unmap.link);
+ wl_list_remove(&sway_layer->surface_commit.link);
+ if (sway_layer->layer_surface->output != NULL) {
+ struct sway_output *output = sway_layer->layer_surface->output->data;
+ arrange_layers(output);
+
+ wl_list_remove(&sway_layer->output_destroy.link);
+ }
+ free(sway_layer);
+}
+
+static void handle_map(struct wl_listener *listener, void *data) {
+ struct sway_layer_surface *sway_layer = wl_container_of(listener,
+ sway_layer, map);
+ struct sway_output *output = sway_layer->layer_surface->output->data;
+ output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
+ sway_layer->layer_surface->surface, true);
+ // TODO: send enter to subsurfaces and popups
+ wlr_surface_send_enter(sway_layer->layer_surface->surface,
+ sway_layer->layer_surface->output);
+}
+
+static void handle_unmap(struct wl_listener *listener, void *data) {
+ struct sway_layer_surface *sway_layer = wl_container_of(
+ listener, sway_layer, unmap);
+ unmap(sway_layer);
+}
+
+void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
+ struct wlr_layer_surface *layer_surface = data;
+ struct sway_server *server =
+ wl_container_of(listener, server, layer_shell_surface);
+ wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d "
+ "size %dx%d margin %d,%d,%d,%d",
+ layer_surface->namespace, layer_surface->layer, layer_surface->layer,
+ layer_surface->client_pending.desired_width,
+ layer_surface->client_pending.desired_height,
+ layer_surface->client_pending.margin.top,
+ layer_surface->client_pending.margin.right,
+ layer_surface->client_pending.margin.bottom,
+ layer_surface->client_pending.margin.left);
+
+ struct sway_layer_surface *sway_layer =
+ calloc(1, sizeof(struct sway_layer_surface));
+ if (!sway_layer) {
+ return;
+ }
+
+ sway_layer->surface_commit.notify = handle_surface_commit;
+ wl_signal_add(&layer_surface->surface->events.commit,
+ &sway_layer->surface_commit);
+
+ sway_layer->output_destroy.notify = handle_output_destroy;
+ wl_signal_add(&layer_surface->output->events.destroy,
+ &sway_layer->output_destroy);
+
+ sway_layer->destroy.notify = handle_destroy;
+ wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy);
+ sway_layer->map.notify = handle_map;
+ wl_signal_add(&layer_surface->events.map, &sway_layer->map);
+ sway_layer->unmap.notify = handle_unmap;
+ wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap);
+ // TODO: Listen for subsurfaces
+
+ sway_layer->layer_surface = layer_surface;
+ layer_surface->data = sway_layer;
+
+ struct sway_output *output = layer_surface->output->data;
+ wl_list_insert(&output->layers[layer_surface->layer], &sway_layer->link);
+
+ // Temporarily set the layer's current state to client_pending
+ // So that we can easily arrange it
+ struct wlr_layer_surface_state old_state = layer_surface->current;
+ layer_surface->current = layer_surface->client_pending;
+ arrange_layers(output);
+ layer_surface->current = old_state;
+}
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <time.h>
+#include <wayland-server.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_matrix.h>
+#include <wlr/types/wlr_output_damage.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_surface.h>
+#include <wlr/types/wlr_wl_shell.h>
+#include <wlr/util/region.h>
+#include "log.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/layers.h"
+#include "sway/output.h"
+#include "sway/server.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/view.h"
+
+struct sway_container *output_by_name(const char *name) {
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *output = root_container.children->items[i];
+ if (strcasecmp(output->name, name) == 0) {
+ return output;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Rotate a child's position relative to a parent. The parent size is (pw, ph),
+ * the child position is (*sx, *sy) and its size is (sw, sh).
+ */
+static void rotate_child_position(double *sx, double *sy, double sw, double sh,
+ double pw, double ph, float rotation) {
+ if (rotation == 0.0f) {
+ return;
+ }
+
+ // Coordinates relative to the center of the subsurface
+ double ox = *sx - pw/2 + sw/2,
+ oy = *sy - ph/2 + sh/2;
+ // Rotated coordinates
+ double rx = cos(-rotation)*ox - sin(-rotation)*oy,
+ ry = cos(-rotation)*oy + sin(-rotation)*ox;
+ *sx = rx + pw/2 - sw/2;
+ *sy = ry + ph/2 - sh/2;
+}
+
+/**
+ * Contains a surface's root geometry information. For instance, when rendering
+ * a popup, this will contain the parent view's position and size.
+ */
+struct root_geometry {
+ double x, y;
+ int width, height;
+ float rotation;
+};
+
+static bool get_surface_box(struct root_geometry *geo,
+ struct sway_output *output, struct wlr_surface *surface, int sx, int sy,
+ struct wlr_box *surface_box) {
+ if (!wlr_surface_has_buffer(surface)) {
+ return false;
+ }
+
+ int sw = surface->current->width;
+ int sh = surface->current->height;
+
+ double _sx = sx, _sy = sy;
+ rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height,
+ geo->rotation);
+
+ struct wlr_box box = {
+ .x = geo->x + _sx,
+ .y = geo->y + _sy,
+ .width = sw,
+ .height = sh,
+ };
+ if (surface_box != NULL) {
+ memcpy(surface_box, &box, sizeof(struct wlr_box));
+ }
+
+ struct wlr_box rotated_box;
+ wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box);
+
+ struct wlr_box output_box = {
+ .width = output->swayc->width,
+ .height = output->swayc->height,
+ };
+
+ struct wlr_box intersection;
+ return wlr_box_intersection(&output_box, &rotated_box, &intersection);
+}
+
+static void surface_for_each_surface(struct wlr_surface *surface,
+ double ox, double oy, struct root_geometry *geo,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ geo->x = ox;
+ geo->y = oy;
+ geo->width = surface->current->width;
+ geo->height = surface->current->height;
+ geo->rotation = 0;
+
+ wlr_surface_for_each_surface(surface, iterator, user_data);
+}
+
+static void output_view_for_each_surface(struct sway_view *view,
+ struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
+ void *user_data) {
+ geo->x = view->swayc->x;
+ geo->y = view->swayc->y;
+ geo->width = view->surface->current->width;
+ geo->height = view->surface->current->height;
+ geo->rotation = 0; // TODO
+
+ view_for_each_surface(view, iterator, user_data);
+}
+
+static void layer_for_each_surface(struct wl_list *layer_surfaces,
+ struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
+ void *user_data) {
+ struct sway_layer_surface *layer_surface;
+ wl_list_for_each(layer_surface, layer_surfaces, link) {
+ struct wlr_layer_surface *wlr_layer_surface =
+ layer_surface->layer_surface;
+ surface_for_each_surface(wlr_layer_surface->surface,
+ layer_surface->geo.x, layer_surface->geo.y, geo, iterator,
+ user_data);
+ }
+}
+
+static void unmanaged_for_each_surface(struct wl_list *unmanaged,
+ struct sway_output *output, struct root_geometry *geo,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ struct sway_xwayland_unmanaged *unmanaged_surface;
+ wl_list_for_each(unmanaged_surface, unmanaged, link) {
+ struct wlr_xwayland_surface *xsurface =
+ unmanaged_surface->wlr_xwayland_surface;
+ double ox = unmanaged_surface->lx - output->swayc->x;
+ double oy = unmanaged_surface->ly - output->swayc->y;
+
+ surface_for_each_surface(xsurface->surface, ox, oy, geo,
+ iterator, user_data);
+ }
+}
+
+static void scale_box(struct wlr_box *box, float scale) {
+ box->x *= scale;
+ box->y *= scale;
+ box->width *= scale;
+ box->height *= scale;
+}
+
+struct render_data {
+ struct root_geometry root_geo;
+ struct sway_output *output;
+ float alpha;
+};
+
+static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
+ void *_data) {
+ struct render_data *data = _data;
+ struct wlr_output *wlr_output = data->output->wlr_output;
+ float rotation = data->root_geo.rotation;
+ float alpha = data->alpha;
+
+ if (!wlr_surface_has_buffer(surface)) {
+ return;
+ }
+
+ struct wlr_box box;
+ bool intersects = get_surface_box(&data->root_geo, data->output, surface,
+ sx, sy, &box);
+ if (!intersects) {
+ return;
+ }
+
+ struct wlr_renderer *renderer =
+ wlr_backend_get_renderer(wlr_output->backend);
+ if (!sway_assert(renderer != NULL,
+ "expected the output backend to have a renderer")) {
+ return;
+ }
+
+ scale_box(&box, wlr_output->scale);
+
+ float matrix[9];
+ enum wl_output_transform transform =
+ wlr_output_transform_invert(surface->current->transform);
+ wlr_matrix_project_box(matrix, &box, transform, rotation,
+ wlr_output->transform_matrix);
+
+ wlr_render_texture_with_matrix(renderer, surface->texture,
+ matrix, alpha);
+}
+
+static void render_layer(struct sway_output *output,
+ struct wl_list *layer_surfaces) {
+ struct render_data data = { .output = output, .alpha = 1.0f };
+ layer_for_each_surface(layer_surfaces, &data.root_geo,
+ render_surface_iterator, &data);
+}
+
+static void render_unmanaged(struct sway_output *output,
+ struct wl_list *unmanaged) {
+ struct render_data data = { .output = output, .alpha = 1.0f };
+ unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
+ render_surface_iterator, &data);
+}
+
+static void render_container_iterator(struct sway_container *con,
+ void *_data) {
+ struct sway_output *output = _data;
+ if (!sway_assert(con->type == C_VIEW, "expected a view")) {
+ return;
+ }
+ struct render_data data = { .output = output, .alpha = con->alpha };
+ output_view_for_each_surface(con->sway_view, &data.root_geo,
+ render_surface_iterator, &data);
+}
+
+static void render_container(struct sway_output *output,
+ struct sway_container *con) {
+ container_descendants(con, C_VIEW, render_container_iterator, output);
+}
+
+static struct sway_container *output_get_active_workspace(
+ struct sway_output *output) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus =
+ seat_get_focus_inactive(seat, output->swayc);
+ if (!focus) {
+ // We've never been to this output before
+ focus = output->swayc->children->items[0];
+ }
+ struct sway_container *workspace = focus;
+ if (workspace->type != C_WORKSPACE) {
+ workspace = container_parent(workspace, C_WORKSPACE);
+ }
+ return workspace;
+}
+
+static void render_output(struct sway_output *output, struct timespec *when,
+ pixman_region32_t *damage) {
+ struct wlr_output *wlr_output = output->wlr_output;
+
+ struct wlr_renderer *renderer =
+ wlr_backend_get_renderer(wlr_output->backend);
+ if (!sway_assert(renderer != NULL,
+ "expected the output backend to have a renderer")) {
+ return;
+ }
+
+ wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
+
+ if (!pixman_region32_not_empty(damage)) {
+ // Output isn't damaged but needs buffer swap
+ goto renderer_end;
+ }
+
+ // TODO: don't damage the whole output
+ int width, height;
+ wlr_output_transformed_resolution(wlr_output, &width, &height);
+ pixman_region32_union_rect(damage, damage, 0, 0, width, height);
+
+ float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
+ wlr_renderer_clear(renderer, clear_color);
+
+ render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
+ render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
+
+ struct sway_container *workspace = output_get_active_workspace(output);
+ render_container(output, workspace);
+
+ render_unmanaged(output, &root_container.sway_root->xwayland_unmanaged);
+
+ // TODO: consider revising this when fullscreen windows are supported
+ render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
+ render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
+
+renderer_end:
+ if (root_container.sway_root->debug_tree) {
+ wlr_render_texture(renderer, root_container.sway_root->debug_tree,
+ wlr_output->transform_matrix, 0, 0, 1);
+ }
+
+ wlr_renderer_end(renderer);
+ if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
+ return;
+ }
+ output->last_frame = *when;
+}
+
+struct send_frame_done_data {
+ struct root_geometry root_geo;
+ struct sway_output *output;
+ struct timespec *when;
+};
+
+static void send_frame_done_iterator(struct wlr_surface *surface,
+ int sx, int sy, void *_data) {
+ struct send_frame_done_data *data = _data;
+
+ bool intersects = get_surface_box(&data->root_geo, data->output, surface,
+ sx, sy, NULL);
+ if (intersects) {
+ wlr_surface_send_frame_done(surface, data->when);
+ }
+}
+
+static void send_frame_done_layer(struct send_frame_done_data *data,
+ struct wl_list *layer_surfaces) {
+ layer_for_each_surface(layer_surfaces, &data->root_geo,
+ send_frame_done_iterator, data);
+}
+
+static void send_frame_done_unmanaged(struct send_frame_done_data *data,
+ struct wl_list *unmanaged) {
+ unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo,
+ send_frame_done_iterator, data);
+}
+
+static void send_frame_done_container_iterator(struct sway_container *con,
+ void *_data) {
+ struct send_frame_done_data *data = _data;
+ if (!sway_assert(con->type == C_VIEW, "expected a view")) {
+ return;
+ }
+ output_view_for_each_surface(con->sway_view, &data->root_geo,
+ send_frame_done_iterator, data);
+}
+
+static void send_frame_done_container(struct send_frame_done_data *data,
+ struct sway_container *con) {
+ container_descendants(con, C_VIEW,
+ send_frame_done_container_iterator, data);
+}
+
+static void send_frame_done(struct sway_output *output, struct timespec *when) {
+ struct send_frame_done_data data = {
+ .output = output,
+ .when = when,
+ };
+
+ send_frame_done_layer(&data,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
+ send_frame_done_layer(&data,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
+
+ struct sway_container *workspace = output_get_active_workspace(output);
+ send_frame_done_container(&data, workspace);
+
+ send_frame_done_unmanaged(&data,
+ &root_container.sway_root->xwayland_unmanaged);
+
+ send_frame_done_layer(&data,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
+ send_frame_done_layer(&data,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
+}
+
+static void damage_handle_frame(struct wl_listener *listener, void *data) {
+ struct sway_output *output =
+ wl_container_of(listener, output, damage_frame);
+
+ if (!output->wlr_output->enabled) {
+ return;
+ }
+
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ bool needs_swap;
+ pixman_region32_t damage;
+ pixman_region32_init(&damage);
+ if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) {
+ return;
+ }
+
+ if (needs_swap) {
+ render_output(output, &now, &damage);
+ }
+
+ pixman_region32_fini(&damage);
+
+ // Send frame done to all visible surfaces
+ send_frame_done(output, &now);
+}
+
+void output_damage_whole(struct sway_output *output) {
+ wlr_output_damage_add_whole(output->damage);
+}
+
+struct damage_data {
+ struct root_geometry root_geo;
+ struct sway_output *output;
+ bool whole;
+};
+
+static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
+ void *_data) {
+ struct damage_data *data = _data;
+ struct sway_output *output = data->output;
+ float rotation = data->root_geo.rotation;
+ bool whole = data->whole;
+
+ struct wlr_box box;
+ bool intersects = get_surface_box(&data->root_geo, data->output, surface,
+ sx, sy, &box);
+ if (!intersects) {
+ return;
+ }
+
+ scale_box(&box, output->wlr_output->scale);
+
+ if (whole) {
+ wlr_box_rotated_bounds(&box, rotation, &box);
+ wlr_output_damage_add_box(output->damage, &box);
+ } else {
+ int center_x = box.x + box.width/2;
+ int center_y = box.y + box.height/2;
+
+ pixman_region32_t damage;
+ pixman_region32_init(&damage);
+ pixman_region32_copy(&damage, &surface->current->surface_damage);
+ wlr_region_scale(&damage, &damage, output->wlr_output->scale);
+ if (ceil(output->wlr_output->scale) > surface->current->scale) {
+ // When scaling up a surface, it'll become blurry so we need to
+ // expand the damage region
+ wlr_region_expand(&damage, &damage,
+ ceil(output->wlr_output->scale) - surface->current->scale);
+ }
+ pixman_region32_translate(&damage, box.x, box.y);
+ wlr_region_rotated_bounds(&damage, &damage, rotation,
+ center_x, center_y);
+ wlr_output_damage_add(output->damage, &damage);
+ pixman_region32_fini(&damage);
+ }
+}
+
+void output_damage_surface(struct sway_output *output, double ox, double oy,
+ struct wlr_surface *surface, bool whole) {
+ struct damage_data data = {
+ .output = output,
+ .whole = whole,
+ };
+
+ surface_for_each_surface(surface, ox, oy, &data.root_geo,
+ damage_surface_iterator, &data);
+}
+
+void output_damage_view(struct sway_output *output, struct sway_view *view,
+ bool whole) {
+ if (!sway_assert(view->swayc != NULL, "expected a view in the tree")) {
+ return;
+ }
+
+ struct damage_data data = {
+ .output = output,
+ .whole = whole,
+ };
+
+ output_view_for_each_surface(view, &data.root_geo,
+ damage_surface_iterator, &data);
+}
+
+static void output_damage_whole_container_iterator(struct sway_container *con,
+ void *data) {
+ struct sway_output *output = data;
+
+ if (!sway_assert(con->type == C_VIEW, "expected a view")) {
+ return;
+ }
+
+ output_damage_view(output, con->sway_view, true);
+}
+
+void output_damage_whole_container(struct sway_output *output,
+ struct sway_container *con) {
+ float scale = output->wlr_output->scale;
+ struct wlr_box box = {
+ .x = con->x * scale,
+ .y = con->y * scale,
+ .width = con->width * scale,
+ .height = con->height * scale,
+ };
+ wlr_output_damage_add_box(output->damage, &box);
+
+ container_descendants(con, C_VIEW, output_damage_whole_container_iterator,
+ output);
+}
+
+static void damage_handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_output *output =
+ wl_container_of(listener, output, damage_destroy);
+ container_destroy(output->swayc);
+}
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_output *output = wl_container_of(listener, output, destroy);
+ container_destroy(output->swayc);
+}
+
+static void handle_mode(struct wl_listener *listener, void *data) {
+ struct sway_output *output = wl_container_of(listener, output, mode);
+ arrange_layers(output);
+ arrange_windows(output->swayc, -1, -1);
+}
+
+static void handle_transform(struct wl_listener *listener, void *data) {
+ struct sway_output *output = wl_container_of(listener, output, transform);
+ arrange_layers(output);
+ arrange_windows(output->swayc, -1, -1);
+}
+
+static void handle_scale(struct wl_listener *listener, void *data) {
+ struct sway_output *output = wl_container_of(listener, output, scale);
+ arrange_layers(output);
+ arrange_windows(output->swayc, -1, -1);
+}
+
+void handle_new_output(struct wl_listener *listener, void *data) {
+ struct sway_server *server = wl_container_of(listener, server, new_output);
+ struct wlr_output *wlr_output = data;
+ wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
+
+ struct sway_output *output = calloc(1, sizeof(struct sway_output));
+ if (!output) {
+ return;
+ }
+ output->wlr_output = wlr_output;
+ wlr_output->data = output;
+ output->server = server;
+
+ if (!wl_list_empty(&wlr_output->modes)) {
+ struct wlr_output_mode *mode =
+ wl_container_of(wlr_output->modes.prev, mode, link);
+ wlr_output_set_mode(wlr_output, mode);
+ }
+
+ output->damage = wlr_output_damage_create(wlr_output);
+
+ output->swayc = output_create(output);
+ if (!output->swayc) {
+ free(output);
+ return;
+ }
+
+ size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
+ for (size_t i = 0; i < len; ++i) {
+ wl_list_init(&output->layers[i]);
+ }
+
+ input_manager_configure_xcursor(input_manager);
+
+ wl_signal_add(&wlr_output->events.destroy, &output->destroy);
+ output->destroy.notify = handle_destroy;
+ wl_signal_add(&wlr_output->events.mode, &output->mode);
+ output->mode.notify = handle_mode;
+ wl_signal_add(&wlr_output->events.transform, &output->transform);
+ output->transform.notify = handle_transform;
+ wl_signal_add(&wlr_output->events.scale, &output->scale);
+ output->scale.notify = handle_scale;
+
+ wl_signal_add(&output->damage->events.frame, &output->damage_frame);
+ output->damage_frame.notify = damage_handle_frame;
+ wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
+ output->damage_destroy.notify = damage_handle_destroy;
+
+ arrange_layers(output);
+ arrange_windows(&root_container, -1, -1);
+}
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 @@
+#define _POSIX_C_SOURCE 199309L
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_wl_shell.h>
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/server.h"
+#include "sway/tree/view.h"
+#include "sway/input/seat.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+static struct sway_wl_shell_view *wl_shell_view_from_view(
+ struct sway_view *view) {
+ if (!sway_assert(view->type == SWAY_VIEW_WL_SHELL,
+ "Expected wl_shell view")) {
+ return NULL;
+ }
+ return (struct sway_wl_shell_view *)view;
+}
+
+static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
+ if (wl_shell_view_from_view(view) == NULL) {
+ return NULL;
+ }
+ switch (prop) {
+ case VIEW_PROP_TITLE:
+ return view->wlr_wl_shell_surface->title;
+ case VIEW_PROP_CLASS:
+ return view->wlr_wl_shell_surface->class;
+ default:
+ return NULL;
+ }
+}
+
+static void configure(struct sway_view *view, double ox, double oy, int width,
+ int height) {
+ struct sway_wl_shell_view *wl_shell_view = wl_shell_view_from_view(view);
+ if (wl_shell_view == NULL) {
+ return;
+ }
+ view_update_position(view, ox, oy);
+ wl_shell_view->pending_width = width;
+ wl_shell_view->pending_height = height;
+ wlr_wl_shell_surface_configure(view->wlr_wl_shell_surface, 0, width, height);
+}
+
+static void _close(struct sway_view *view) {
+ if (wl_shell_view_from_view(view) == NULL) {
+ return;
+ }
+
+ wl_client_destroy(view->wlr_wl_shell_surface->client);
+}
+
+static void destroy(struct sway_view *view) {
+ struct sway_wl_shell_view *wl_shell_view = wl_shell_view_from_view(view);
+ if (wl_shell_view == NULL) {
+ return;
+ }
+ wl_list_remove(&wl_shell_view->commit.link);
+ wl_list_remove(&wl_shell_view->destroy.link);
+ free(wl_shell_view);
+}
+
+static const struct sway_view_impl view_impl = {
+ .get_prop = get_prop,
+ .configure = configure,
+ .close = _close,
+ .destroy = destroy,
+};
+
+static void handle_commit(struct wl_listener *listener, void *data) {
+ struct sway_wl_shell_view *wl_shell_view =
+ wl_container_of(listener, wl_shell_view, commit);
+ struct sway_view *view = &wl_shell_view->view;
+ // NOTE: We intentionally discard the view's desired width here
+ // TODO: Let floating views do whatever
+ view_update_size(view, wl_shell_view->pending_width,
+ wl_shell_view->pending_height);
+ view_damage(view, false);
+}
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_wl_shell_view *wl_shell_view =
+ wl_container_of(listener, wl_shell_view, destroy);
+ view_destroy(&wl_shell_view->view);
+}
+
+void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
+ struct sway_server *server = wl_container_of(listener, server,
+ wl_shell_surface);
+ struct wlr_wl_shell_surface *shell_surface = data;
+
+ if (shell_surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) {
+ // popups don't get views
+ wlr_log(L_DEBUG, "New wl_shell popup");
+ return;
+ }
+
+ // TODO: make transient windows floating
+
+ wlr_log(L_DEBUG, "New wl_shell toplevel title='%s' app_id='%s'",
+ shell_surface->title, shell_surface->class);
+ wlr_wl_shell_surface_ping(shell_surface);
+
+ struct sway_wl_shell_view *wl_shell_view =
+ calloc(1, sizeof(struct sway_wl_shell_view));
+ if (!sway_assert(wl_shell_view, "Failed to allocate view")) {
+ return;
+ }
+
+ view_init(&wl_shell_view->view, SWAY_VIEW_WL_SHELL, &view_impl);
+ wl_shell_view->view.wlr_wl_shell_surface = shell_surface;
+
+ // TODO:
+ // - Wire up listeners
+ // - Look up pid and open on appropriate workspace
+ // - Set new view to maximized so it behaves nicely
+ // - Criteria
+
+ wl_shell_view->commit.notify = handle_commit;
+ wl_signal_add(&shell_surface->surface->events.commit,
+ &wl_shell_view->commit);
+
+ wl_shell_view->destroy.notify = handle_destroy;
+ wl_signal_add(&shell_surface->events.destroy, &wl_shell_view->destroy);
+
+ view_map(&wl_shell_view->view, shell_surface->surface);
+}
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 @@
+#define _POSIX_C_SOURCE 199309L
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_xdg_shell_v6.h>
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/server.h"
+#include "sway/tree/view.h"
+#include "sway/input/seat.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+static const struct sway_view_child_impl popup_impl;
+
+static void popup_destroy(struct sway_view_child *child) {
+ if (!sway_assert(child->impl == &popup_impl,
+ "Expected an xdg_shell_v6 popup")) {
+ return;
+ }
+ struct sway_xdg_popup_v6 *popup = (struct sway_xdg_popup_v6 *)child;
+ wl_list_remove(&popup->new_popup.link);
+ wl_list_remove(&popup->unmap.link);
+ wl_list_remove(&popup->destroy.link);
+ free(popup);
+}
+
+static const struct sway_view_child_impl popup_impl = {
+ .destroy = popup_destroy,
+};
+
+static struct sway_xdg_popup_v6 *popup_create(
+ struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view);
+
+static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
+ struct sway_xdg_popup_v6 *popup =
+ wl_container_of(listener, popup, new_popup);
+ struct wlr_xdg_popup_v6 *wlr_popup = data;
+ popup_create(wlr_popup, popup->child.view);
+}
+
+static void popup_handle_unmap(struct wl_listener *listener, void *data) {
+ struct sway_xdg_popup_v6 *popup = wl_container_of(listener, popup, unmap);
+ view_child_destroy(&popup->child);
+}
+
+static void popup_handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_xdg_popup_v6 *popup = wl_container_of(listener, popup, destroy);
+ view_child_destroy(&popup->child);
+}
+
+static struct sway_xdg_popup_v6 *popup_create(
+ struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) {
+ struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base;
+
+ struct sway_xdg_popup_v6 *popup =
+ calloc(1, sizeof(struct sway_xdg_popup_v6));
+ if (popup == NULL) {
+ return NULL;
+ }
+ view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
+
+ wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
+ popup->new_popup.notify = popup_handle_new_popup;
+ wl_signal_add(&xdg_surface->events.unmap, &popup->unmap);
+ popup->unmap.notify = popup_handle_unmap;
+ wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);
+ popup->destroy.notify = popup_handle_destroy;
+
+ return popup;
+}
+
+
+static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view(
+ struct sway_view *view) {
+ if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL_V6,
+ "Expected xdg_shell_v6 view")) {
+ return NULL;
+ }
+ return (struct sway_xdg_shell_v6_view *)view;
+}
+
+static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
+ if (xdg_shell_v6_view_from_view(view) == NULL) {
+ return NULL;
+ }
+ switch (prop) {
+ case VIEW_PROP_TITLE:
+ return view->wlr_xdg_surface_v6->toplevel->title;
+ case VIEW_PROP_APP_ID:
+ return view->wlr_xdg_surface_v6->toplevel->app_id;
+ default:
+ return NULL;
+ }
+}
+
+static void configure(struct sway_view *view, double ox, double oy, int width,
+ int height) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ xdg_shell_v6_view_from_view(view);
+ if (xdg_shell_v6_view == NULL) {
+ return;
+ }
+
+ view_update_position(view, ox, oy);
+ xdg_shell_v6_view->pending_width = width;
+ xdg_shell_v6_view->pending_height = height;
+ wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height);
+}
+
+static void set_activated(struct sway_view *view, bool activated) {
+ if (xdg_shell_v6_view_from_view(view) == NULL) {
+ return;
+ }
+ struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6;
+ if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
+ wlr_xdg_toplevel_v6_set_activated(surface, activated);
+ }
+}
+
+static void for_each_surface(struct sway_view *view,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ if (xdg_shell_v6_view_from_view(view) == NULL) {
+ return;
+ }
+ wlr_xdg_surface_v6_for_each_surface(view->wlr_xdg_surface_v6, iterator,
+ user_data);
+}
+
+static void _close(struct sway_view *view) {
+ if (xdg_shell_v6_view_from_view(view) == NULL) {
+ return;
+ }
+ struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6;
+ if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
+ wlr_xdg_surface_v6_send_close(surface);
+ }
+}
+
+static void destroy(struct sway_view *view) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ xdg_shell_v6_view_from_view(view);
+ if (xdg_shell_v6_view == NULL) {
+ return;
+ }
+ wl_list_remove(&xdg_shell_v6_view->destroy.link);
+ wl_list_remove(&xdg_shell_v6_view->map.link);
+ wl_list_remove(&xdg_shell_v6_view->unmap.link);
+ free(xdg_shell_v6_view);
+}
+
+static const struct sway_view_impl view_impl = {
+ .get_prop = get_prop,
+ .configure = configure,
+ .set_activated = set_activated,
+ .for_each_surface = for_each_surface,
+ .close = _close,
+ .destroy = destroy,
+};
+
+static void handle_commit(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, commit);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ // NOTE: We intentionally discard the view's desired width here
+ // TODO: Store this for restoration when moving to floating plane
+ // TODO: Let floating views do whatever
+ view_update_size(view, xdg_shell_v6_view->pending_width,
+ xdg_shell_v6_view->pending_height);
+ view_damage(view, false);
+}
+
+static void handle_new_popup(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, new_popup);
+ struct wlr_xdg_popup_v6 *wlr_popup = data;
+ popup_create(wlr_popup, &xdg_shell_v6_view->view);
+}
+
+static void handle_unmap(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, unmap);
+
+ view_unmap(&xdg_shell_v6_view->view);
+
+ wl_list_remove(&xdg_shell_v6_view->commit.link);
+ wl_list_remove(&xdg_shell_v6_view->new_popup.link);
+}
+
+static void handle_map(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, map);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ struct wlr_xdg_surface_v6 *xdg_surface = view->wlr_xdg_surface_v6;
+
+ view_map(view, view->wlr_xdg_surface_v6->surface);
+
+ xdg_shell_v6_view->commit.notify = handle_commit;
+ wl_signal_add(&xdg_surface->surface->events.commit,
+ &xdg_shell_v6_view->commit);
+
+ xdg_shell_v6_view->new_popup.notify = handle_new_popup;
+ wl_signal_add(&xdg_surface->events.new_popup,
+ &xdg_shell_v6_view->new_popup);
+}
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, destroy);
+ view_destroy(&xdg_shell_v6_view->view);
+}
+
+void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
+ struct sway_server *server = wl_container_of(listener, server,
+ xdg_shell_v6_surface);
+ struct wlr_xdg_surface_v6 *xdg_surface = data;
+
+ if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
+ wlr_log(L_DEBUG, "New xdg_shell_v6 popup");
+ return;
+ }
+
+ wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'",
+ xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
+ wlr_xdg_surface_v6_ping(xdg_surface);
+ wlr_xdg_toplevel_v6_set_maximized(xdg_surface, true);
+
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ calloc(1, sizeof(struct sway_xdg_shell_v6_view));
+ if (!sway_assert(xdg_shell_v6_view, "Failed to allocate view")) {
+ return;
+ }
+
+ view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl);
+ xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface;
+
+ // TODO:
+ // - Look up pid and open on appropriate workspace
+ // - Criteria
+
+ xdg_shell_v6_view->map.notify = handle_map;
+ wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map);
+
+ xdg_shell_v6_view->unmap.notify = handle_unmap;
+ wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_v6_view->unmap);
+
+ xdg_shell_v6_view->destroy.notify = handle_destroy;
+ wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_v6_view->destroy);
+}
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 @@
+#define _POSIX_C_SOURCE 199309L
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/xwayland.h>
+#include "log.h"
+#include "sway/desktop.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/output.h"
+#include "sway/server.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/view.h"
+
+static void unmanaged_handle_request_configure(struct wl_listener *listener,
+ void *data) {
+ struct sway_xwayland_unmanaged *surface =
+ wl_container_of(listener, surface, request_configure);
+ struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
+ struct wlr_xwayland_surface_configure_event *ev = data;
+ wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
+ ev->width, ev->height);
+}
+
+static void unmanaged_handle_commit(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_unmanaged *surface =
+ wl_container_of(listener, surface, commit);
+ struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
+
+ if (xsurface->x != surface->lx || xsurface->y != surface->ly) {
+ // Surface has moved
+ desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
+ true);
+ surface->lx = xsurface->x;
+ surface->ly = xsurface->y;
+ desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
+ true);
+ } else {
+ desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y,
+ false);
+ }
+}
+
+static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_unmanaged *surface =
+ wl_container_of(listener, surface, map);
+ struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
+
+ wl_list_insert(&root_container.sway_root->xwayland_unmanaged,
+ &surface->link);
+
+ wl_signal_add(&xsurface->surface->events.commit, &surface->commit);
+ surface->commit.notify = unmanaged_handle_commit;
+
+ surface->lx = xsurface->x;
+ surface->ly = xsurface->y;
+ desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true);
+
+ if (!wlr_xwayland_surface_is_unmanaged(xsurface)) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct wlr_xwayland *xwayland = seat->input->server->xwayland;
+ wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
+ seat_set_focus_surface(seat, xsurface->surface);
+ }
+
+ // TODO: we don't send surface enter/leave events to xwayland unmanaged
+ // surfaces, but xwayland doesn't support HiDPI anyway
+}
+
+static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_unmanaged *surface =
+ wl_container_of(listener, surface, unmap);
+ struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
+ desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true);
+ wl_list_remove(&surface->link);
+ wl_list_remove(&surface->commit.link);
+}
+
+static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_unmanaged *surface =
+ wl_container_of(listener, surface, destroy);
+ struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
+ if (xsurface->mapped) {
+ unmanaged_handle_unmap(&surface->unmap, xsurface);
+ }
+ wl_list_remove(&surface->map.link);
+ wl_list_remove(&surface->unmap.link);
+ wl_list_remove(&surface->destroy.link);
+ free(surface);
+}
+
+static struct sway_xwayland_unmanaged *create_unmanaged(
+ struct wlr_xwayland_surface *xsurface) {
+ struct sway_xwayland_unmanaged *surface =
+ calloc(1, sizeof(struct sway_xwayland_unmanaged));
+ if (surface == NULL) {
+ wlr_log(L_ERROR, "Allocation failed");
+ return NULL;
+ }
+
+ surface->wlr_xwayland_surface = xsurface;
+
+ wl_signal_add(&xsurface->events.request_configure,
+ &surface->request_configure);
+ surface->request_configure.notify = unmanaged_handle_request_configure;
+ wl_signal_add(&xsurface->events.map, &surface->map);
+ surface->map.notify = unmanaged_handle_map;
+ wl_signal_add(&xsurface->events.unmap, &surface->unmap);
+ surface->unmap.notify = unmanaged_handle_unmap;
+ wl_signal_add(&xsurface->events.destroy, &surface->destroy);
+ surface->destroy.notify = unmanaged_handle_destroy;
+
+ unmanaged_handle_map(&surface->map, xsurface);
+
+ return surface;
+}
+
+
+static struct sway_xwayland_view *xwayland_view_from_view(
+ struct sway_view *view) {
+ if (!sway_assert(view->type == SWAY_VIEW_XWAYLAND,
+ "Expected xwayland view")) {
+ return NULL;
+ }
+ return (struct sway_xwayland_view *)view;
+}
+
+static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
+ if (xwayland_view_from_view(view) == NULL) {
+ return NULL;
+ }
+ switch (prop) {
+ case VIEW_PROP_TITLE:
+ return view->wlr_xwayland_surface->title;
+ case VIEW_PROP_CLASS:
+ return view->wlr_xwayland_surface->class;
+ default:
+ return NULL;
+ }
+}
+
+static void configure(struct sway_view *view, double ox, double oy, int width,
+ int height) {
+ struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
+ if (xwayland_view == NULL) {
+ return;
+ }
+ struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
+
+ struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
+ if (!sway_assert(output, "view must be within tree to set position")) {
+ return;
+ }
+ struct sway_container *root = container_parent(output, C_ROOT);
+ if (!sway_assert(root, "output must be within tree to set position")) {
+ return;
+ }
+ struct wlr_output_layout *layout = root->sway_root->output_layout;
+ struct wlr_output_layout_output *loutput =
+ wlr_output_layout_get(layout, output->sway_output->wlr_output);
+ if (!sway_assert(loutput, "output must be within layout to set position")) {
+ return;
+ }
+
+ view_update_position(view, ox, oy);
+
+ xwayland_view->pending_width = width;
+ xwayland_view->pending_height = height;
+ wlr_xwayland_surface_configure(xsurface, ox + loutput->x, oy + loutput->y,
+ width, height);
+}
+
+static void set_activated(struct sway_view *view, bool activated) {
+ if (xwayland_view_from_view(view) == NULL) {
+ return;
+ }
+ struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
+ wlr_xwayland_surface_activate(surface, activated);
+}
+
+static void _close(struct sway_view *view) {
+ if (xwayland_view_from_view(view) == NULL) {
+ return;
+ }
+ wlr_xwayland_surface_close(view->wlr_xwayland_surface);
+}
+
+static void destroy(struct sway_view *view) {
+ struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
+ if (xwayland_view == NULL) {
+ return;
+ }
+ wl_list_remove(&xwayland_view->destroy.link);
+ wl_list_remove(&xwayland_view->request_configure.link);
+ wl_list_remove(&xwayland_view->map.link);
+ wl_list_remove(&xwayland_view->unmap.link);
+ free(xwayland_view);
+}
+
+static const struct sway_view_impl view_impl = {
+ .get_prop = get_prop,
+ .configure = configure,
+ .set_activated = set_activated,
+ .close = _close,
+ .destroy = destroy,
+};
+
+static void handle_commit(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, commit);
+ struct sway_view *view = &xwayland_view->view;
+ // NOTE: We intentionally discard the view's desired width here
+ // TODO: Let floating views do whatever
+ view_update_size(view, xwayland_view->pending_width,
+ xwayland_view->pending_height);
+ view_damage(view, false);
+}
+
+static void handle_unmap(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, unmap);
+ wl_list_remove(&xwayland_view->commit.link);
+ view_unmap(&xwayland_view->view);
+}
+
+static void handle_map(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, map);
+ struct wlr_xwayland_surface *xsurface = data;
+ struct sway_view *view = &xwayland_view->view;
+
+ // Wire up the commit listener here, because xwayland map/unmap can change
+ // the underlying wlr_surface
+ wl_signal_add(&xsurface->surface->events.commit, &xwayland_view->commit);
+ xwayland_view->commit.notify = handle_commit;
+
+ // Put it back into the tree
+ wlr_xwayland_surface_set_maximized(xsurface, true);
+ view_map(view, xsurface->surface);
+}
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, destroy);
+ struct sway_view *view = &xwayland_view->view;
+ struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
+ if (xsurface->mapped) {
+ handle_unmap(&xwayland_view->unmap, xsurface);
+ }
+ view_destroy(&xwayland_view->view);
+}
+
+static void handle_request_configure(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, request_configure);
+ struct wlr_xwayland_surface_configure_event *ev = data;
+ struct sway_view *view = &xwayland_view->view;
+ struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
+ // TODO: floating windows are allowed to move around like this, but make
+ // sure tiling windows always stay in place.
+ wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
+ ev->width, ev->height);
+}
+
+void handle_xwayland_surface(struct wl_listener *listener, void *data) {
+ struct sway_server *server = wl_container_of(listener, server,
+ xwayland_surface);
+ struct wlr_xwayland_surface *xsurface = data;
+
+ if (wlr_xwayland_surface_is_unmanaged(xsurface) ||
+ xsurface->override_redirect) {
+ wlr_log(L_DEBUG, "New xwayland unmanaged surface");
+ create_unmanaged(xsurface);
+ return;
+ }
+
+ wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'",
+ xsurface->title, xsurface->class);
+
+ struct sway_xwayland_view *xwayland_view =
+ calloc(1, sizeof(struct sway_xwayland_view));
+ if (!sway_assert(xwayland_view, "Failed to allocate view")) {
+ return;
+ }
+
+ view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl);
+ xwayland_view->view.wlr_xwayland_surface = xsurface;
+
+ // TODO:
+ // - Look up pid and open on appropriate workspace
+ // - Criteria
+
+ wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
+ xwayland_view->destroy.notify = handle_destroy;
+
+ wl_signal_add(&xsurface->events.request_configure,
+ &xwayland_view->request_configure);
+ xwayland_view->request_configure.notify = handle_request_configure;
+
+ wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap);
+ xwayland_view->unmap.notify = handle_unmap;
+
+ wl_signal_add(&xsurface->events.map, &xwayland_view->map);
+ xwayland_view->map.notify = handle_map;
+
+ handle_map(&xwayland_view->map, xsurface);
+}
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 @@
-#include <stdlib.h>
-#include <wlc/wlc.h>
-#include <wlc/wlc-wayland.h>
-#include <wlc/wlc-render.h>
-#include "wayland-desktop-shell-server-protocol.h"
-#include "wayland-swaylock-server-protocol.h"
-#include "wayland-gamma-control-server-protocol.h"
-#include "wayland-server-decoration-server-protocol.h"
-#include "sway/layout.h"
-#include "sway/input_state.h"
-#include "sway/extensions.h"
-#include "sway/security.h"
-#include "sway/ipc-server.h"
-#include "log.h"
-
-struct desktop_shell_state desktop_shell;
-struct decoration_state decoration_state;
-
-static struct panel_config *find_or_create_panel_config(struct wl_resource *resource) {
- for (int i = 0; i < desktop_shell.panels->length; i++) {
- struct panel_config *conf = desktop_shell.panels->items[i];
- if (conf->wl_resource == resource) {
- sway_log(L_DEBUG, "Found existing panel config for resource %p", resource);
- return conf;
- }
- }
- sway_log(L_DEBUG, "Creating panel config for resource %p", resource);
- struct panel_config *config = calloc(1, sizeof(struct panel_config));
- if (!config) {
- sway_log(L_ERROR, "Unable to create panel config");
- return NULL;
- }
- list_add(desktop_shell.panels, config);
- config->wl_resource = resource;
- return config;
-}
-
-void background_surface_destructor(struct wl_resource *resource) {
- sway_log(L_DEBUG, "Background surface killed");
- int i;
- for (i = 0; i < desktop_shell.backgrounds->length; ++i) {
- struct background_config *config = desktop_shell.backgrounds->items[i];
- if (config->wl_surface_res == resource) {
- list_del(desktop_shell.backgrounds, i);
- break;
- }
- }
-}
-
-void panel_surface_destructor(struct wl_resource *resource) {
- sway_log(L_DEBUG, "Panel surface killed");
- int i;
- for (i = 0; i < desktop_shell.panels->length; ++i) {
- struct panel_config *config = desktop_shell.panels->items[i];
- if (config->wl_surface_res == resource) {
- list_del(desktop_shell.panels, i);
- arrange_windows(&root_container, -1, -1);
- break;
- }
- }
-}
-
-void lock_surface_destructor(struct wl_resource *resource) {
- sway_log(L_DEBUG, "Lock surface killed");
- int i;
- for (i = 0; i < desktop_shell.lock_surfaces->length; ++i) {
- struct wl_resource *surface = desktop_shell.lock_surfaces->items[i];
- if (surface == resource) {
- list_del(desktop_shell.lock_surfaces, i);
- arrange_windows(&root_container, -1, -1);
- break;
- }
- }
- if (desktop_shell.lock_surfaces->length == 0) {
- sway_log(L_DEBUG, "Desktop shell unlocked");
- desktop_shell.is_locked = false;
-
- // We need to now give focus back to the focus which we internally
- // track, since when we lock sway we don't actually change our internal
- // focus tracking.
- swayc_t *focus = get_focused_container(swayc_active_workspace());
- set_focused_container(focus);
- wlc_view_focus(focus->handle);
- }
-}
-
-static void set_background(struct wl_client *client, struct wl_resource *resource,
- struct wl_resource *_output, struct wl_resource *surface) {
- pid_t pid;
- wl_client_get_credentials(client, &pid, NULL, NULL);
- if (!(get_feature_policy_mask(pid) & FEATURE_BACKGROUND)) {
- sway_log(L_INFO, "Denying background feature to %d", pid);
- return;
- }
- wlc_handle output = wlc_handle_from_wl_output_resource(_output);
- if (!output) {
- return;
- }
- sway_log(L_DEBUG, "Setting surface %p as background for output %d", surface, (int)output);
- struct background_config *config = malloc(sizeof(struct background_config));
- if (!config) {
- sway_log(L_ERROR, "Unable to allocate background config");
- return;
- }
- config->client = client;
- config->output = output;
- config->surface = wlc_resource_from_wl_surface_resource(surface);
- config->wl_surface_res = surface;
- list_add(desktop_shell.backgrounds, config);
- wl_resource_set_destructor(surface, background_surface_destructor);
- arrange_windows(swayc_by_handle(output), -1, -1);
- wlc_output_schedule_render(config->output);
-}
-
-static void set_panel(struct wl_client *client, struct wl_resource *resource,
- struct wl_resource *_output, struct wl_resource *surface) {
- pid_t pid;
- wl_client_get_credentials(client, &pid, NULL, NULL);
- if (!(get_feature_policy_mask(pid) & FEATURE_PANEL)) {
- sway_log(L_INFO, "Denying panel feature to %d", pid);
- return;
- }
- wlc_handle output = wlc_handle_from_wl_output_resource(_output);
- if (!output) {
- return;
- }
- sway_log(L_DEBUG, "Setting surface %p as panel for output %d (wl_resource: %p)", surface, (int)output, resource);
- struct panel_config *config = find_or_create_panel_config(resource);
- config->output = output;
- config->client = client;
- config->surface = wlc_resource_from_wl_surface_resource(surface);
- config->wl_surface_res = surface;
- wl_resource_set_destructor(surface, panel_surface_destructor);
- arrange_windows(&root_container, -1, -1);
- wlc_output_schedule_render(config->output);
-}
-
-static void desktop_set_lock_surface(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface) {
- sway_log(L_ERROR, "desktop_set_lock_surface is not currently supported");
-}
-
-static void desktop_unlock(struct wl_client *client, struct wl_resource *resource) {
- sway_log(L_ERROR, "desktop_unlock is not currently supported");
-}
-
-static void set_grab_surface(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface) {
- sway_log(L_ERROR, "desktop_set_grab_surface is not currently supported");
-}
-
-static void desktop_ready(struct wl_client *client, struct wl_resource *resource) {
- // nop
-}
-
-static void set_panel_position(struct wl_client *client, struct wl_resource *resource, uint32_t position) {
- pid_t pid;
- wl_client_get_credentials(client, &pid, NULL, NULL);
- if (!(get_feature_policy_mask(pid) & FEATURE_PANEL)) {
- sway_log(L_INFO, "Denying panel feature to %d", pid);
- return;
- }
- struct panel_config *config = find_or_create_panel_config(resource);
- sway_log(L_DEBUG, "Panel position for wl_resource %p changed %d => %d", resource, config->panel_position, position);
- config->panel_position = position;
- arrange_windows(&root_container, -1, -1);
-}
-
-static struct desktop_shell_interface desktop_shell_implementation = {
- .set_background = set_background,
- .set_panel = set_panel,
- .set_lock_surface = desktop_set_lock_surface,
- .unlock = desktop_unlock,
- .set_grab_surface = set_grab_surface,
- .desktop_ready = desktop_ready,
- .set_panel_position = set_panel_position
-};
-
-static void desktop_shell_bind(struct wl_client *client, void *data,
- uint32_t version, uint32_t id) {
- if (version > 3) {
- // Unsupported version
- return;
- }
-
- struct wl_resource *resource = wl_resource_create(client, &desktop_shell_interface, version, id);
- if (!resource) {
- wl_client_post_no_memory(client);
- }
-
- wl_resource_set_implementation(resource, &desktop_shell_implementation, NULL, NULL);
-}
-
-static void set_lock_surface(struct wl_client *client, struct wl_resource *resource,
- struct wl_resource *_output, struct wl_resource *surface) {
- pid_t pid;
- wl_client_get_credentials(client, &pid, NULL, NULL);
- if (!(get_feature_policy_mask(pid) & FEATURE_LOCK)) {
- sway_log(L_INFO, "Denying lock feature to %d", pid);
- return;
- }
- swayc_t *output = swayc_by_handle(wlc_handle_from_wl_output_resource(_output));
- swayc_t *view = swayc_by_handle(wlc_handle_from_wl_surface_resource(surface));
- sway_log(L_DEBUG, "Setting lock surface to %p", view);
- if (view && output) {
- swayc_t *workspace = output->focused;
- if (!swayc_is_child_of(view, workspace)) {
- move_container_to(view, workspace);
- }
- // make the view floating so it doesn't rearrange other siblings.
- if (!view->is_floating) {
- destroy_container(remove_child(view));
- add_floating(workspace, view);
- }
- wlc_view_set_state(view->handle, WLC_BIT_FULLSCREEN, true);
- wlc_view_bring_to_front(view->handle);
- wlc_view_focus(view->handle);
- desktop_shell.is_locked = true;
- input_init();
- arrange_windows(workspace, -1, -1);
- list_add(desktop_shell.lock_surfaces, surface);
- wl_resource_set_destructor(surface, lock_surface_destructor);
- } else {
- sway_log(L_ERROR, "Attempted to set lock surface to non-view");
- }
-}
-
-static void unlock(struct wl_client *client, struct wl_resource *resource) {
- sway_log(L_ERROR, "unlock is not currently supported");
- // This isn't really necessary, we just unlock when the client exits.
-}
-
-static struct lock_interface swaylock_implementation = {
- .set_lock_surface = set_lock_surface,
- .unlock = unlock
-};
-
-static void swaylock_bind(struct wl_client *client, void *data,
- uint32_t version, uint32_t id) {
- if (version > 1) {
- // Unsupported version
- return;
- }
-
- struct wl_resource *resource = wl_resource_create(client, &lock_interface, version, id);
- if (!resource) {
- wl_client_post_no_memory(client);
- }
-
- wl_resource_set_implementation(resource, &swaylock_implementation, NULL, NULL);
-}
-
-static void gamma_control_destroy(struct wl_client *client, struct wl_resource *res) {
- wl_resource_destroy(res);
-}
-
-static void gamma_control_set_gamma(struct wl_client *client,
- struct wl_resource *res, struct wl_array *red,
- struct wl_array *green, struct wl_array *blue) {
- if (red->size != green->size || red->size != blue->size) {
- wl_resource_post_error(res, GAMMA_CONTROL_ERROR_INVALID_GAMMA,
- "The gamma ramps don't have the same size");
- return;
- }
- uint16_t *r = (uint16_t *)red->data;
- uint16_t *g = (uint16_t *)green->data;
- uint16_t *b = (uint16_t *)blue->data;
- wlc_handle output = wlc_handle_from_wl_output_resource(
- wl_resource_get_user_data(res));
- if (!output) {
- return;
- }
- sway_log(L_DEBUG, "Setting gamma for output");
- wlc_output_set_gamma(output, red->size / sizeof(uint16_t), r, g, b);
-}
-
-static void gamma_control_reset_gamma(struct wl_client *client,
- struct wl_resource *resource) {
- // This space intentionally left blank
-}
-
-static struct gamma_control_interface gamma_control_implementation = {
- .destroy = gamma_control_destroy,
- .set_gamma = gamma_control_set_gamma,
- .reset_gamma = gamma_control_reset_gamma
-};
-
-static void gamma_control_manager_destroy(struct wl_client *client,
- struct wl_resource *res) {
- wl_resource_destroy(res);
-}
-
-static void gamma_control_manager_get(struct wl_client *client,
- struct wl_resource *res, uint32_t id, struct wl_resource *_output) {
- struct wl_resource *manager_res = wl_resource_create(client,
- &gamma_control_interface, wl_resource_get_version(res), id);
- wlc_handle output = wlc_handle_from_wl_output_resource(_output);
- if (!output) {
- return;
- }
- wl_resource_set_implementation(manager_res, &gamma_control_implementation,
- _output, NULL);
- gamma_control_send_gamma_size(manager_res, wlc_output_get_gamma_size(output));
-}
-
-static struct gamma_control_manager_interface gamma_manager_implementation = {
- .destroy = gamma_control_manager_destroy,
- .get_gamma_control = gamma_control_manager_get
-};
-
-static void gamma_control_manager_bind(struct wl_client *client, void *data,
- uint32_t version, uint32_t id) {
- if (version > 1) {
- // Unsupported version
- return;
- }
- struct wl_resource *resource = wl_resource_create(client,
- &gamma_control_manager_interface, version, id);
- if (!resource) {
- wl_client_post_no_memory(client);
- }
- wl_resource_set_implementation(resource, &gamma_manager_implementation, NULL, NULL);
-}
-
-static void server_decoration_release(struct wl_client *client,
- struct wl_resource *resource) {
- wl_resource_destroy(resource);
-}
-
-void server_decoration_enable_csd(wlc_handle handle) {
- swayc_t *view = swayc_by_handle(handle);
- if (!view) {
- sway_log(L_DEBUG, "view invalid");
- return;
- }
- sway_log(L_DEBUG, "%s requested client side decorations", view->name);
- view->border_type = B_NONE;
- update_geometry(view);
-}
-
-static void server_decoration_request_mode(struct wl_client *client,
- struct wl_resource *resource, uint32_t mode) {
- sway_log(L_DEBUG, "Client requested server decoration mode %d", mode);
- if (mode == ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER) {
- return;
- }
- struct wl_resource *surface = wl_resource_get_user_data(resource);
- if (!surface) {
- sway_log(L_DEBUG, "surface invalid");
- return;
- }
- wlc_handle handle = wlc_handle_from_wl_surface_resource(surface);
- if (!handle) {
- list_add(decoration_state.csd_resources, surface);
- return;
- }
- server_decoration_enable_csd(handle);
-}
-
-static struct org_kde_kwin_server_decoration_interface server_decoration_implementation = {
- .release = server_decoration_release,
- .request_mode = server_decoration_request_mode,
-};
-
-static void server_decoration_manager_create(struct wl_client *client,
- struct wl_resource *resource, uint32_t id, struct wl_resource *surface) {
- sway_log(L_DEBUG, "Client requested server decoration manager");
- struct wl_resource *manager = wl_resource_create(client,
- &org_kde_kwin_server_decoration_interface, 1, id);
- if (!manager) {
- wl_client_post_no_memory(client);
- }
- wl_resource_set_implementation(manager, &server_decoration_implementation, surface, NULL);
-}
-
-// Jesus christ KDE, these names are whack as hell
-static struct org_kde_kwin_server_decoration_manager_interface server_decoration_manager_implementation = {
- .create = server_decoration_manager_create,
-};
-
-static void server_decoration_manager_bind(struct wl_client *client, void *data,
- uint32_t version, uint32_t id) {
- if (version > 1) {
- // Unsupported version
- return;
- }
- struct wl_resource *resource = wl_resource_create(client,
- &org_kde_kwin_server_decoration_manager_interface, version, id);
- if (!resource) {
- wl_client_post_no_memory(client);
- }
- wl_resource_set_implementation(resource, &server_decoration_manager_implementation, NULL, NULL);
- org_kde_kwin_server_decoration_manager_send_default_mode(resource,
- ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER);
-}
-
-void register_extensions(void) {
- wl_global_create(wlc_get_wl_display(), &desktop_shell_interface, 3, NULL, desktop_shell_bind);
- desktop_shell.backgrounds = create_list();
- desktop_shell.panels = create_list();
- desktop_shell.lock_surfaces = create_list();
- desktop_shell.is_locked = false;
- decoration_state.csd_resources = create_list();
- wl_global_create(wlc_get_wl_display(), &lock_interface, 1, NULL, swaylock_bind);
- wl_global_create(wlc_get_wl_display(), &gamma_control_manager_interface, 1,
- NULL, gamma_control_manager_bind);
- wl_global_create(wlc_get_wl_display(), &org_kde_kwin_server_decoration_manager_interface ,
- 1, NULL, server_decoration_manager_bind);
-}
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 @@
-#include "stdbool.h"
-#include <wlc/wlc.h>
-#include "sway/focus.h"
-#include "sway/workspace.h"
-#include "sway/layout.h"
-#include "sway/config.h"
-#include "sway/extensions.h"
-#include "sway/input_state.h"
-#include "sway/ipc-server.h"
-#include "sway/border.h"
-#include "log.h"
-
-bool locked_container_focus = false;
-bool suspend_workspace_cleanup = false;
-
-// switches parent focus to c. will switch it accordingly
-static void update_focus(swayc_t *c) {
- // Handle if focus switches
- swayc_t *parent = c->parent;
- if (!parent) return;
- if (parent->focused != c) {
- // Get previous focus
- swayc_t *prev = parent->focused;
- // Set new focus
- parent->focused = c;
-
- switch (c->type) {
- // Shouldn't happen
- case C_ROOT: return;
-
- // Case where output changes
- case C_OUTPUT:
- wlc_output_focus(c->handle);
- break;
-
- // Case where workspace changes
- case C_WORKSPACE:
- if (prev) {
- ipc_event_workspace(prev, c, "focus");
-
- // if the old workspace has no children, destroy it
- if(prev->children->length == 0 && prev->floating->length == 0 && !suspend_workspace_cleanup) {
- destroy_workspace(prev);
- } else {
- // update visibility of old workspace
- update_visibility(prev);
- }
- }
- // Update visibility of newly focused workspace
- update_visibility(c);
- break;
-
- default:
- case C_VIEW:
- case C_CONTAINER:
- break;
- }
- }
-}
-
-bool move_focus(enum movement_direction direction) {
- swayc_t *old_view = get_focused_container(&root_container);
- swayc_t *new_view = get_swayc_in_direction(old_view, direction);
- if (!new_view) {
- return false;
- } else if (new_view->type == C_ROOT) {
- sway_log(L_DEBUG, "Not setting focus above the workspace level");
- return false;
- } else if (new_view->type == C_OUTPUT) {
- return set_focused_container(swayc_active_workspace_for(new_view));
- } else if (direction == MOVE_PARENT || direction == MOVE_CHILD) {
- return set_focused_container(new_view);
- } else if (config->mouse_warping) {
- swayc_t *old_op = old_view->type == C_OUTPUT ?
- old_view : swayc_parent_by_type(old_view, C_OUTPUT);
- swayc_t *focused = get_focused_view(new_view);
- if (set_focused_container(focused)) {
- if (old_op != swayc_active_output() && focused && focused->type == C_VIEW) {
- center_pointer_on(focused);
- }
- return true;
- }
- } else {
- return set_focused_container(get_focused_view(new_view));
- }
- return false;
-}
-
-swayc_t *get_focused_container(swayc_t *parent) {
- if (!parent) {
- return swayc_active_workspace();
- }
- while (!parent->is_focused && parent->focused) {
- parent = parent->focused;
- }
- return parent;
-}
-
-bool set_focused_container(swayc_t *c) {
- if (locked_container_focus || !c || !c->parent) {
- return false;
- }
-
- // current ("old") workspace for sending workspace change event later
- swayc_t *old_ws = swayc_active_workspace();
- // keep track of child count so we can determine if it gets destroyed
- int old_ws_child_count = 0;
- if (old_ws) {
- old_ws_child_count = old_ws->children->length + old_ws->floating->length;
- }
-
- // current ("old") focused container
- swayc_t *old_focus = get_focused_container(&root_container);
- // if old_focus is a workspace, then it's the same workspace as
- // old_ws, and we'll need to null its pointer too, since it will
- // be destroyed in the update_focus() call
- bool old_focus_was_ws = (old_focus->type == C_WORKSPACE);
-
- // workspace of new focused container
- swayc_t *workspace = swayc_active_workspace_for(c);
-
- if (swayc_is_fullscreen(get_focused_container(workspace))) {
- // if switching to a workspace with a fullscreen view,
- // focus on the fullscreen view
- c = get_focused_container(workspace);
- }
-
- swayc_log(L_DEBUG, c, "Setting focus to %p:%" PRIuPTR, c, c->handle);
-
- if (c->type == C_VIEW) {
- // dispatch a window event
- ipc_event_window(c, "focus");
- }
-
- // update the global pointer
- current_focus = c;
-
- // update container focus from here to root, making necessary changes along
- // the way
- swayc_t *p = c;
- if (p->type != C_OUTPUT && p->type != C_ROOT) {
- p->is_focused = true;
- }
- while (p != &root_container) {
- update_focus(p);
- p = p->parent;
- p->is_focused = false;
- }
-
- if (old_focus_was_ws && old_ws_child_count == 0) {
- // this workspace was destroyed in update_focus(), so null the pointers
- old_focus = NULL;
- old_ws = NULL;
- }
-
- if (!(wlc_view_get_type(p->handle) & WLC_BIT_POPUP)) {
- if (old_focus) {
- if (old_focus->type == C_VIEW) {
- wlc_view_set_state(old_focus->handle, WLC_BIT_ACTIVATED, false);
- }
- update_container_border(old_focus);
- }
- if (c->type == C_VIEW) {
- wlc_view_set_state(c->handle, WLC_BIT_ACTIVATED, true);
- }
- if (!desktop_shell.is_locked) {
- // If the system is locked, we do everything _but_ actually setting
- // focus. This includes making our internals think that this view is
- // focused.
- wlc_view_focus(c->handle);
- }
- if (c->parent->layout != L_TABBED && c->parent->layout != L_STACKED) {
- update_container_border(c);
- }
-
- swayc_t *parent = swayc_tabbed_stacked_ancestor(c);
- if (parent != NULL) {
- arrange_backgrounds();
- arrange_windows(parent, -1, -1);
- }
- }
-
- if (old_ws != workspace) {
- // old_ws might be NULL here but that's ok
- ipc_event_workspace(old_ws, workspace, "focus");
- }
-
- return true;
-}
-
-bool set_focused_container_for(swayc_t *a, swayc_t *c) {
- if (locked_container_focus || !c) {
- return false;
- }
- swayc_t *find = c;
- while (find != a && (find = find->parent)) {
- if (find == &root_container) {
- return false;
- }
- }
-
- // Get workspace for c, get that workspaces current focused container.
- swayc_t *workspace = swayc_active_workspace_for(c);
- swayc_t *focused = get_focused_view(workspace);
- // if the workspace we are changing focus to has a fullscreen view return
- if (swayc_is_fullscreen(focused) && c != focused) {
- return false;
- }
-
- // Check if we are changing a parent container that will see change
- bool effective = true;
- while (find != &root_container) {
- if (find->parent->focused != find) {
- effective = false;
- }
- find = find->parent;
- }
- if (effective) {
- // Go to set_focused_container
- return set_focused_container(c);
- }
-
- sway_log(L_DEBUG, "Setting focus for %p:%" PRIuPTR " to %p:%" PRIuPTR,
- a, a->handle, c, c->handle);
-
- c->is_focused = true;
- swayc_t *p = c;
- while (p != a) {
- update_focus(p);
- p = p->parent;
- p->is_focused = false;
- }
- return true;
-}
-
-swayc_t *get_focused_view(swayc_t *parent) {
- swayc_t *c = parent;
- while (c && c->type != C_VIEW) {
- if (c->type == C_WORKSPACE && c->focused == NULL) {
- return c;
- }
- c = c->focused;
- }
- if (c == NULL) {
- c = swayc_active_workspace_for(parent);
- }
- return c;
-}
-
-swayc_t *get_focused_float(swayc_t *ws) {
- if(!sway_assert(ws->type == C_WORKSPACE, "must be of workspace type")) {
- ws = swayc_active_workspace();
- }
- if (ws->floating->length) {
- return ws->floating->items[ws->floating->length - 1];
- }
- return NULL;
-}
-
-swayc_t *get_focused_view_include_floating(swayc_t *parent) {
- swayc_t *c = parent;
- swayc_t *f = NULL;
-
- while (c && c->type != C_VIEW) {
- if (c->type == C_WORKSPACE && c->focused == NULL) {
- return ((f = get_focused_float(c))) ? f : c;
- }
-
- c = c->focused;
- }
-
- if (c == NULL) {
- c = swayc_active_workspace_for(parent);
- }
-
- return c;
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <xkbcommon/xkbcommon.h>
-#include <strings.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <libinput.h>
-#include <math.h>
-#include <wlc/wlc.h>
-#include <wlc/wlc-render.h>
-#include <wlc/wlc-wayland.h>
-#include <ctype.h>
-#include "sway/handlers.h"
-#include "sway/border.h"
-#include "sway/layout.h"
-#include "sway/config.h"
-#include "sway/commands.h"
-#include "sway/workspace.h"
-#include "sway/container.h"
-#include "sway/output.h"
-#include "sway/focus.h"
-#include "sway/input_state.h"
-#include "sway/extensions.h"
-#include "sway/criteria.h"
-#include "sway/ipc-server.h"
-#include "sway/input.h"
-#include "sway/security.h"
-#include "list.h"
-#include "stringop.h"
-#include "log.h"
-
-// Event should be sent to client
-#define EVENT_PASSTHROUGH false
-
-// Event handled by sway and should not be sent to client
-#define EVENT_HANDLED true
-
-static struct panel_config *if_panel_find_config(struct wl_client *client) {
- int i;
- for (i = 0; i < desktop_shell.panels->length; i++) {
- struct panel_config *config = desktop_shell.panels->items[i];
- if (config->client == client) {
- return config;
- }
- }
- return NULL;
-}
-
-static struct background_config *if_background_find_config(struct wl_client *client) {
- int i;
- for (i = 0; i < desktop_shell.backgrounds->length; i++) {
- struct background_config *config = desktop_shell.backgrounds->items[i];
- if (config->client == client) {
- return config;
- }
- }
- return NULL;
-}
-
-static struct wlc_geometry compute_panel_geometry(struct panel_config *config) {
- struct wlc_size resolution;
- output_get_scaled_size(config->output, &resolution);
- const struct wlc_geometry *old = wlc_view_get_geometry(config->handle);
- struct wlc_geometry new;
-
- switch (config->panel_position) {
- case DESKTOP_SHELL_PANEL_POSITION_TOP:
- new.origin.x = 0;
- new.origin.y = 0;
- new.size.w = resolution.w;
- new.size.h = old->size.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
- new.origin.x = 0;
- new.origin.y = resolution.h - old->size.h;
- new.size.w = resolution.w;
- new.size.h = old->size.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_LEFT:
- new.origin.x = 0;
- new.origin.y = 0;
- new.size.w = old->size.w;
- new.size.h = resolution.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
- new.origin.x = resolution.w - old->size.w;
- new.origin.y = 0;
- new.size.w = old->size.w;
- new.size.h = resolution.h;
- break;
- }
-
- return new;
-}
-
-static void update_panel_geometry(struct panel_config *config) {
- struct wlc_geometry geometry = compute_panel_geometry(config);
- wlc_view_set_geometry(config->handle, 0, &geometry);
-}
-
-static void update_panel_geometries(wlc_handle output) {
- for (int i = 0; i < desktop_shell.panels->length; i++) {
- struct panel_config *config = desktop_shell.panels->items[i];
- if (config->output == output) {
- update_panel_geometry(config);
- }
- }
-}
-
-static void update_background_geometry(struct background_config *config) {
- struct wlc_geometry geometry = wlc_geometry_zero;
- output_get_scaled_size(config->output, &geometry.size);
- wlc_view_set_geometry(config->handle, 0, &geometry);
-}
-
-static void update_background_geometries(wlc_handle output) {
- for (int i = 0; i < desktop_shell.backgrounds->length; i++) {
- struct background_config *config = desktop_shell.backgrounds->items[i];
- if (config->output == output) {
- update_background_geometry(config);
- }
- }
-}
-
-/* Handles */
-
-static bool handle_input_created(struct libinput_device *device) {
- const char *identifier = libinput_dev_unique_id(device);
- if (!identifier) {
- sway_log(L_ERROR, "Unable to allocate unique name for input device %p",
- device);
- return true;
- }
- sway_log(L_INFO, "Found input device (%s)", identifier);
-
- list_add(input_devices, device);
-
- struct input_config *ic = NULL;
- int i;
- for (i = 0; i < config->input_configs->length; ++i) {
- struct input_config *cur = config->input_configs->items[i];
- if (strcasecmp(identifier, cur->identifier) == 0) {
- sway_log(L_DEBUG, "Matched input config for %s",
- identifier);
- ic = cur;
- break;
- }
- if (strcasecmp("*", cur->identifier) == 0) {
- sway_log(L_DEBUG, "Matched wildcard input config for %s",
- identifier);
- ic = cur;
- break;
- }
- }
-
- apply_input_config(ic, device);
- return true;
-}
-
-static void handle_input_destroyed(struct libinput_device *device) {
- int i;
- list_t *list = input_devices;
- for (i = 0; i < list->length; ++i) {
- if(((struct libinput_device *)list->items[i]) == device) {
- list_del(list, i);
- break;
- }
- }
-}
-
-static bool handle_output_created(wlc_handle output) {
- swayc_t *op = new_output(output);
-
- // Visibility mask to be able to make view invisible
- wlc_output_set_mask(output, VISIBLE);
-
- if (!op) {
- return false;
- }
-
- // Switch to workspace if we need to
- if (swayc_active_workspace() == NULL) {
- swayc_t *ws = op->children->items[0];
- workspace_switch(ws);
- }
-
- // Fixes issues with backgrounds and wlc
- wlc_handle prev = wlc_get_focused_output();
- wlc_output_focus(output);
- wlc_output_focus(prev);
- return true;
-}
-
-static void handle_output_destroyed(wlc_handle output) {
- int i;
- list_t *list = root_container.children;
- for (i = 0; i < list->length; ++i) {
- if (((swayc_t *)list->items[i])->handle == output) {
- break;
- }
- }
- if (i < list->length) {
- destroy_output(list->items[i]);
- } else {
- return;
- }
- if (list->length > 0) {
- // switch to other outputs active workspace
- workspace_switch(((swayc_t *)root_container.children->items[0])->focused);
- }
-}
-
-static void handle_output_post_render(wlc_handle output) {
- ipc_get_pixels(output);
-}
-
-static void handle_view_pre_render(wlc_handle view) {
- render_view_borders(view);
-}
-
-static void handle_output_resolution_change(wlc_handle output, const struct wlc_size *from, const struct wlc_size *to) {
- sway_log(L_DEBUG, "Output %u resolution changed to %d x %d", (unsigned int)output, to->w, to->h);
-
- swayc_t *c = swayc_by_handle(output);
- if (!c) {
- return;
- }
- c->width = to->w;
- c->height = to->h;
-
- update_panel_geometries(output);
- update_background_geometries(output);
-
- arrange_windows(&root_container, -1, -1);
-}
-
-static void handle_output_focused(wlc_handle output, bool focus) {
- swayc_t *c = swayc_by_handle(output);
- // if for some reason this output doesn't exist, create it.
- if (!c) {
- handle_output_created(output);
- }
- if (focus) {
- set_focused_container(get_focused_container(c));
- }
-}
-
-static void ws_cleanup() {
- swayc_t *op, *ws;
- int i = 0, j;
- if (!root_container.children)
- return;
- while (i < root_container.children->length) {
- op = root_container.children->items[i++];
- if (!op->children)
- continue;
- j = 0;
- while (j < op->children->length) {
- ws = op->children->items[j++];
- if (ws->children->length == 0 && ws->floating->length == 0 && ws != op->focused) {
- if (destroy_workspace(ws)) {
- j--;
- }
- }
- }
- }
-}
-
-static void positioner_place_window(wlc_handle handle) {
- const struct wlc_geometry *anchor = wlc_view_positioner_get_anchor_rect(handle);
- const struct wlc_size *sr = wlc_view_positioner_get_size(handle);
- // a positioner is required to have a non-null anchor and a non-negative size
- if (!anchor || !sr ||
- sr->w <= 0 || sr->h <= 0 ||
- anchor->size.w <= 0 || anchor->size.h <= 0) {
- return;
- }
- const struct wlc_point offset = *wlc_view_positioner_get_offset(handle);
- enum wlc_positioner_anchor_bit anchors = wlc_view_positioner_get_anchor(handle);
- enum wlc_positioner_gravity_bit gravity = wlc_view_positioner_get_gravity(handle);
- struct wlc_geometry geo = {
- .origin = offset,
- .size = *sr
- };
-
- if (anchors & WLC_BIT_ANCHOR_TOP) {
- geo.origin.y += anchor->origin.y;
- } else if (anchors & WLC_BIT_ANCHOR_BOTTOM) {
- geo.origin.y += anchor->origin.y + anchor->size.h;
- } else {
- geo.origin.y += anchor->origin.y + anchor->size.h / 2;
- }
- if (anchors & WLC_BIT_ANCHOR_LEFT) {
- geo.origin.x += anchor->origin.x;
- } else if (anchors & WLC_BIT_ANCHOR_RIGHT) {
- geo.origin.x += anchor->origin.x + anchor->size.w;
- } else {
- geo.origin.x += anchor->origin.x + anchor->size.w / 2;
- }
-
- if (gravity & WLC_BIT_GRAVITY_TOP) {
- geo.origin.y -= geo.size.h;
- } else if (gravity & WLC_BIT_GRAVITY_BOTTOM) {
- /* default */
- } else {
- geo.origin.y -= geo.size.h / 2;
- }
- if (gravity & WLC_BIT_GRAVITY_LEFT) {
- geo.origin.x -= geo.size.w;
- } else if (gravity & WLC_BIT_GRAVITY_RIGHT) {
- /* default */
- } else {
- geo.origin.x -= geo.size.w / 2;
- }
-
- sway_log(L_DEBUG, "xdg-positioner: placing window %" PRIuPTR " "
- "sized (%u,%u) offset by (%d,%d), "
- "anchor rectangle sized (%u,%u) at (%d,%d), "
- "anchor edges: %s %s, gravity: %s %s",
- handle,
- sr->w, sr->h, offset.x, offset.y,
- anchor->size.w, anchor->size.h, anchor->origin.x, anchor->origin.y,
- anchors & WLC_BIT_ANCHOR_TOP ? "top" :
- (anchors & WLC_BIT_ANCHOR_BOTTOM ? "bottom" : "middle"),
- anchors & WLC_BIT_ANCHOR_LEFT ? "left" :
- (anchors & WLC_BIT_ANCHOR_RIGHT ? "right" : "center"),
- gravity & WLC_BIT_GRAVITY_TOP ? "top" :
- (gravity & WLC_BIT_GRAVITY_BOTTOM ? "bottom" : "middle"),
- gravity & WLC_BIT_GRAVITY_LEFT ? "left" :
- (gravity & WLC_BIT_GRAVITY_RIGHT ? "right" : "center"));
-
- wlc_handle parent = wlc_view_get_parent(handle);
- if (parent) {
- const struct wlc_geometry *pg = wlc_view_get_geometry(parent);
- geo.origin.x += pg->origin.x;
- geo.origin.y += pg->origin.y;
- }
- wlc_view_set_geometry(handle, 0, &geo);
-}
-
-static bool handle_view_created(wlc_handle handle) {
- // if view is child of another view, the use that as focused container
- wlc_handle parent = wlc_view_get_parent(handle);
- swayc_t *focused = NULL;
- swayc_t *newview = NULL;
- swayc_t *current_ws = swayc_active_workspace();
- bool return_to_workspace = false;
- struct wl_client *client = wlc_view_get_wl_client(handle);
- struct wl_resource *resource = wlc_surface_get_wl_resource(
- wlc_view_get_surface(handle));
- pid_t pid;
- struct panel_config *panel_config = NULL;
- struct background_config *background_config = NULL;
-
- panel_config = if_panel_find_config(client);
- if (panel_config) {
- panel_config->handle = handle;
- update_panel_geometry(panel_config);
- wlc_view_set_mask(handle, VISIBLE);
- wlc_view_set_output(handle, panel_config->output);
- wlc_view_bring_to_front(handle);
- arrange_windows(&root_container, -1, -1);
- return true;
- }
-
- background_config = if_background_find_config(client);
- if (background_config) {
- background_config->handle = handle;
- update_background_geometry(background_config);
- wlc_view_set_mask(handle, VISIBLE);
- wlc_view_set_output(handle, background_config->output);
- wlc_view_send_to_back(handle);
- return true;
- }
-
- // Get parent container, to add view in
- if (parent) {
- focused = swayc_by_handle(parent);
- }
-
- if (client) {
- pid = wlc_view_get_pid(handle);
-
- if (pid) {
- // using newview as a temp storage location here,
- // rather than adding yet another workspace var
- newview = workspace_for_pid(pid);
- if (newview) {
- focused = get_focused_container(newview);
- return_to_workspace = true;
- }
- newview = NULL;
- }
- }
-
- swayc_t *prev_focus = get_focused_container(&root_container);
-
- if (!focused || focused->type == C_OUTPUT) {
- focused = prev_focus;
- // Move focus from floating view
- if (focused->is_floating) {
- // To workspace if there are no children
- if (focused->parent->children->length == 0) {
- focused = focused->parent;
- }
- // TODO find a better way of doing this
- // Or to focused container
- else {
- focused = get_focused_container(focused->parent->children->items[0]);
- }
- }
- }
-
- positioner_place_window(handle);
-
- sway_log(L_DEBUG, "handle:%" PRIuPTR " type:%x state:%x parent:%" PRIuPTR " "
- "mask:%d (x:%d y:%d w:%d h:%d) title:%s "
- "class:%s appid:%s",
- handle, wlc_view_get_type(handle), wlc_view_get_state(handle), parent,
- wlc_view_get_mask(handle), wlc_view_get_geometry(handle)->origin.x,
- wlc_view_get_geometry(handle)->origin.y,wlc_view_get_geometry(handle)->size.w,
- wlc_view_get_geometry(handle)->size.h, wlc_view_get_title(handle),
- wlc_view_get_class(handle), wlc_view_get_app_id(handle));
-
- // TODO properly figure out how each window should be handled.
- switch (wlc_view_get_type(handle)) {
- // regular view created regularly
- case 0:
- if (parent) {
- newview = new_floating_view(handle);
- } else {
- newview = new_view(focused, handle);
- wlc_view_set_state(handle, WLC_BIT_MAXIMIZED, true);
- }
- break;
-
- // Dmenu keeps viewfocus, but others with this flag don't, for now simulate
- // dmenu
- case WLC_BIT_OVERRIDE_REDIRECT:
- wlc_view_focus(handle);
- wlc_view_set_state(handle, WLC_BIT_ACTIVATED, true);
- wlc_view_bring_to_front(handle);
- break;
-
- // Firefox popups have this flag set.
- case WLC_BIT_OVERRIDE_REDIRECT|WLC_BIT_UNMANAGED:
- wlc_view_bring_to_front(handle);
- locked_container_focus = true;
- break;
-
- // Modals, get focus, popups do not
- case WLC_BIT_MODAL:
- wlc_view_focus(handle);
- wlc_view_bring_to_front(handle);
- newview = new_floating_view(handle);
- /* fallthrough */
- case WLC_BIT_POPUP:
- wlc_view_bring_to_front(handle);
- break;
- }
-
- // Prevent current ws from being destroyed, if empty
- suspend_workspace_cleanup = true;
-
- if (newview) {
- ipc_event_window(newview, "new");
- set_focused_container(newview);
- wlc_view_set_mask(handle, VISIBLE);
- swayc_t *output = swayc_parent_by_type(newview, C_OUTPUT);
- arrange_windows(output, -1, -1);
- // check if it matches for_window in config and execute if so
- list_t *criteria = criteria_for(newview);
- for (int i = 0; i < criteria->length; i++) {
- struct criteria *crit = criteria->items[i];
- sway_log(L_DEBUG, "for_window '%s' matches new view %p, cmd: '%s'",
- crit->crit_raw, newview, crit->cmdlist);
- struct cmd_results *res = handle_command(crit->cmdlist, CONTEXT_CRITERIA);
- if (res->status != CMD_SUCCESS) {
- sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
- }
- free_cmd_results(res);
- // view must be focused for commands to affect it, so always
- // refocus in-between command lists
- set_focused_container(newview);
- }
- swayc_t *workspace = swayc_parent_by_type(focused, C_WORKSPACE);
- if (workspace && workspace->fullscreen) {
- set_focused_container(workspace->fullscreen);
- }
- for (int i = 0; i < decoration_state.csd_resources->length; ++i) {
- struct wl_resource *res = decoration_state.csd_resources->items[i];
- if (res == resource) {
- list_del(decoration_state.csd_resources, i);
- server_decoration_enable_csd(handle);
- break;
- }
- }
- } else {
- swayc_t *output = swayc_parent_by_type(focused, C_OUTPUT);
- wlc_handle *h = malloc(sizeof(wlc_handle));
- if (!h) {
- sway_log(L_ERROR, "Unable to allocate window handle, view handler bailing out");
- return true;
- }
- *h = handle;
- sway_log(L_DEBUG, "Adding unmanaged window %p to %p", h, output->unmanaged);
- list_add(output->unmanaged, h);
- wlc_view_set_mask(handle, VISIBLE);
- }
-
- if (return_to_workspace && current_ws != swayc_active_workspace()) {
- // we were on one workspace, switched to another to add this view,
- // now let's return to where we were
- workspace_switch(current_ws);
- set_focused_container(get_focused_container(current_ws));
- }
- if (prev_focus && prev_focus->type == C_VIEW
- && newview && criteria_any(newview, config->no_focus)) {
- // Restore focus
- swayc_t *ws = swayc_parent_by_type(newview, C_WORKSPACE);
- if (!ws || ws != newview->parent
- || ws->children->length + ws->floating->length != 1) {
- sway_log(L_DEBUG, "no_focus: restoring focus to %s", prev_focus->name);
- set_focused_container(prev_focus);
- }
- }
-
- suspend_workspace_cleanup = false;
- ws_cleanup();
- return true;
-}
-
-static void handle_view_destroyed(wlc_handle handle) {
- sway_log(L_DEBUG, "Destroying window %" PRIuPTR, handle);
- swayc_t *view = swayc_by_handle(handle);
-
- // destroy views by type
- switch (wlc_view_get_type(handle)) {
- // regular view created regularly
- case 0:
- case WLC_BIT_MODAL:
- case WLC_BIT_POPUP:
- break;
- // DMENU has this flag, and takes view_focus, but other things with this
- // flag don't
- case WLC_BIT_OVERRIDE_REDIRECT:
- break;
- case WLC_BIT_OVERRIDE_REDIRECT|WLC_BIT_UNMANAGED:
- locked_container_focus = false;
- break;
- }
-
- if (view) {
- bool fullscreen = swayc_is_fullscreen(view);
- remove_view_from_scratchpad(view);
- swayc_t *parent = destroy_view(view), *iter = NULL;
- if (parent) {
- ipc_event_window(parent, "close");
-
- // Destroy empty workspaces
- if (parent->type == C_WORKSPACE &&
- parent->children->length == 0 &&
- parent->floating->length == 0 &&
- swayc_active_workspace() != parent &&
- !parent->visible) {
- parent = destroy_workspace(parent);
- }
-
- if (fullscreen) {
- iter = parent;
- while (iter) {
- if (iter->fullscreen) {
- iter->fullscreen = NULL;
- break;
- }
- iter = iter->parent;
- }
- }
-
-
- arrange_windows(iter ? iter : parent, -1, -1);
- }
- } else {
- // Is it unmanaged?
- int i;
- for (i = 0; i < root_container.children->length; ++i) {
- swayc_t *output = root_container.children->items[i];
- int j;
- for (j = 0; j < output->unmanaged->length; ++j) {
- wlc_handle *_handle = output->unmanaged->items[j];
- if (*_handle == handle) {
- list_del(output->unmanaged, j);
- free(_handle);
- break;
- }
- }
- }
- // Is it in the scratchpad?
- for (i = 0; i < scratchpad->length; ++i) {
- swayc_t *item = scratchpad->items[i];
- if (item->handle == handle) {
- list_del(scratchpad, i);
- destroy_view(item);
- break;
- }
- }
- }
- set_focused_container(get_focused_view(&root_container));
-}
-
-static void handle_view_focus(wlc_handle view, bool focus) {
- return;
-}
-
-static void handle_view_geometry_request(wlc_handle handle, const struct wlc_geometry *geometry) {
- sway_log(L_DEBUG, "geometry request for %" PRIuPTR " %dx%d @ %d,%d", handle,
- geometry->size.w, geometry->size.h, geometry->origin.x, geometry->origin.y);
- // If the view is floating, then apply the geometry.
- // Otherwise save the desired width/height for the view.
- // This will not do anything for the time being as WLC improperly sends geometry requests
- swayc_t *view = swayc_by_handle(handle);
- if (view) {
- view->desired_width = geometry->size.w;
- view->desired_height = geometry->size.h;
-
- if (view->is_floating) {
- floating_view_sane_size(view);
- view->width = view->desired_width;
- view->height = view->desired_height;
- view->x = geometry->origin.x;
- view->y = geometry->origin.y;
- update_geometry(view);
- }
- } else {
- wlc_view_set_geometry(handle, 0, geometry);
- }
-}
-
-static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit state, bool toggle) {
- swayc_t *c = swayc_by_handle(view);
- pid_t pid = wlc_view_get_pid(view);
- switch (state) {
- case WLC_BIT_FULLSCREEN:
- if (!(get_feature_policy_mask(pid) & FEATURE_FULLSCREEN)) {
- sway_log(L_INFO, "Denying fullscreen to %d (%s)", pid, c->name);
- break;
- }
- // i3 just lets it become fullscreen
- wlc_view_set_state(view, state, toggle);
- if (c) {
- sway_log(L_DEBUG, "setting view %" PRIuPTR " %s, fullscreen %d", view, c->name, toggle);
- arrange_windows(c->parent, -1, -1);
- // Set it as focused window for that workspace if its going fullscreen
- swayc_t *ws = swayc_parent_by_type(c, C_WORKSPACE);
- if (toggle) {
- // Set ws focus to c
- set_focused_container_for(ws, c);
- ws->fullscreen = c;
- } else {
- ws->fullscreen = NULL;
- }
- }
- break;
- case WLC_BIT_MAXIMIZED:
- case WLC_BIT_RESIZING:
- case WLC_BIT_MOVING:
- break;
- case WLC_BIT_ACTIVATED:
- sway_log(L_DEBUG, "View %p requested to be activated", c);
- break;
- }
- return;
-}
-
-static void handle_view_properties_updated(wlc_handle view, uint32_t mask) {
- if (mask == WLC_BIT_PROPERTY_TITLE) {
- swayc_t *c = swayc_by_handle(view);
- if (!c) {
- return;
- }
-
- // update window title
- const char *new_name = wlc_view_get_title(view);
-
- if (new_name) {
- if (!c->name || strcmp(c->name, new_name) != 0) {
- free(c->name);
- c->name = strdup(new_name);
- swayc_t *p = swayc_tabbed_stacked_ancestor(c);
- if (p) {
- // TODO: we only got the topmost tabbed/stacked container, update borders of all containers on the path
- update_container_border(get_focused_view(p));
- } else if (c->border_type == B_NORMAL) {
- update_container_border(c);
- }
- ipc_event_window(c, "title");
- }
- }
- }
-}
-
-static void handle_binding_command(struct sway_binding *binding) {
- struct sway_binding *binding_copy = binding;
- bool reload = false;
- // if this is a reload command we need to make a duplicate of the
- // binding since it will be gone after the reload has completed.
- if (strcasecmp(binding->command, "reload") == 0) {
- binding_copy = sway_binding_dup(binding);
- if (!binding_copy) {
- sway_log(L_ERROR, "Unable to duplicate binding during reload");
- return;
- }
- reload = true;
- }
-
- struct cmd_results *res = handle_command(binding->command, CONTEXT_BINDING);
- if (res->status != CMD_SUCCESS) {
- sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
- }
- ipc_event_binding_keyboard(binding_copy);
-
- if (reload) { // free the binding if we made a copy
- free_sway_binding(binding_copy);
- }
-
- free_cmd_results(res);
-}
-
-static bool handle_bindsym(struct sway_binding *binding, uint32_t keysym, uint32_t keycode) {
- int i;
- for (i = 0; i < binding->keys->length; ++i) {
- if (binding->bindcode) {
- xkb_keycode_t *key = binding->keys->items[i];
- if (keycode == *key) {
- handle_binding_command(binding);
- return true;
- }
- } else {
- xkb_keysym_t *key = binding->keys->items[i];
- if (keysym == *key) {
- handle_binding_command(binding);
- return true;
- }
- }
- }
-
- return false;
-}
-
-static bool valid_bindsym(struct sway_binding *binding) {
- bool match = false;
- int i;
- for (i = 0; i < binding->keys->length; ++i) {
- if (binding->bindcode) {
- xkb_keycode_t *key = binding->keys->items[i];
- if ((match = check_key(0, *key)) == false) {
- break;
- }
- } else {
- xkb_keysym_t *key = binding->keys->items[i];
- if ((match = check_key(*key, 0)) == false) {
- break;
- }
- }
- }
-
- return match;
-}
-
-static bool handle_bindsym_release(struct sway_binding *binding) {
- if (binding->keys->length == 1) {
- xkb_keysym_t *key = binding->keys->items[0];
- if (check_released_key(*key)) {
- handle_binding_command(binding);
- return true;
- }
- }
-
- return false;
-}
-
-static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifiers *modifiers,
- uint32_t key, enum wlc_key_state state) {
-
- if (desktop_shell.is_locked) {
- return EVENT_PASSTHROUGH;
- }
-
- // reset pointer mode on keypress
- if (state == WLC_KEY_STATE_PRESSED && pointer_state.mode) {
- pointer_mode_reset();
- }
-
- struct sway_mode *mode = config->current_mode;
-
- struct wlc_modifiers no_mods = { 0, 0 };
- uint32_t sym = tolower(wlc_keyboard_get_keysym_for_key(key, &no_mods));
-
- int i;
-
- if (state == WLC_KEY_STATE_PRESSED) {
- press_key(sym, key);
- } else { // WLC_KEY_STATE_RELEASED
- release_key(sym, key);
- }
-
- // handle bar modifiers pressed/released
- uint32_t modifier;
- for (i = 0; i < config->active_bar_modifiers->length; ++i) {
- modifier = *(uint32_t *)config->active_bar_modifiers->items[i];
-
- switch (modifier_state_changed(modifiers->mods, modifier)) {
- case MOD_STATE_PRESSED:
- ipc_event_modifier(modifier, "pressed");
- break;
- case MOD_STATE_RELEASED:
- ipc_event_modifier(modifier, "released");
- break;
- }
- }
- // update modifiers state
- modifiers_state_update(modifiers->mods);
-
- // handle bindings
- list_t *candidates = create_list();
- for (i = 0; i < mode->bindings->length; ++i) {
- struct sway_binding *binding = mode->bindings->items[i];
- if ((modifiers->mods ^ binding->modifiers) == 0) {
- switch (state) {
- case WLC_KEY_STATE_PRESSED:
- if (!binding->release && valid_bindsym(binding)) {
- list_add(candidates, binding);
- }
- break;
- case WLC_KEY_STATE_RELEASED:
- if (binding->release && handle_bindsym_release(binding)) {
- list_free(candidates);
- return EVENT_HANDLED;
- }
- break;
- }
- }
- }
-
- for (i = 0; i < candidates->length; ++i) {
- struct sway_binding *binding = candidates->items[i];
- if (state == WLC_KEY_STATE_PRESSED) {
- if (!binding->release && handle_bindsym(binding, sym, key)) {
- list_free(candidates);
- return EVENT_HANDLED;
- }
- }
- }
-
- list_free(candidates);
-
- swayc_t *focused = get_focused_container(&root_container);
- if (focused->type == C_VIEW) {
- pid_t pid = wlc_view_get_pid(focused->handle);
- if (!(get_feature_policy_mask(pid) & FEATURE_KEYBOARD)) {
- return EVENT_HANDLED;
- }
- }
- return EVENT_PASSTHROUGH;
-}
-
-static bool handle_pointer_motion(wlc_handle handle, uint32_t time, double x, double y) {
- if (desktop_shell.is_locked) {
- return EVENT_PASSTHROUGH;
- }
-
- double new_x = x;
- double new_y = y;
- // Switch to adjacent output if touching output edge.
- //
- // Since this doesn't currently support moving windows between outputs we
- // don't do the switch if the pointer is in a mode.
- if (config->seamless_mouse && !pointer_state.mode &&
- !pointer_state.left.held && !pointer_state.right.held && !pointer_state.scroll.held) {
-
- swayc_t *output = swayc_active_output(), *adjacent = NULL;
- struct wlc_point abs_pos = { .x = x + output->x, .y = y + output->y };
- if (x <= 0) { // Left edge
- if ((adjacent = swayc_adjacent_output(output, MOVE_LEFT, &abs_pos, false))) {
- if (workspace_switch(swayc_active_workspace_for(adjacent))) {
- new_x = adjacent->width;
- // adjust for differently aligned outputs (well, this is
- // only correct when the two outputs have the same
- // resolution or the same dpi I guess, it should take
- // physical attributes into account)
- new_y += (output->y - adjacent->y);
- }
- }
- } else if (x >= output->width) { // Right edge
- if ((adjacent = swayc_adjacent_output(output, MOVE_RIGHT, &abs_pos, false))) {
- if (workspace_switch(swayc_active_workspace_for(adjacent))) {
- new_x = 0;
- new_y += (output->y - adjacent->y);
- }
- }
- } else if (y <= 0) { // Top edge
- if ((adjacent = swayc_adjacent_output(output, MOVE_UP, &abs_pos, false))) {
- if (workspace_switch(swayc_active_workspace_for(adjacent))) {
- new_y = adjacent->height;
- new_x += (output->x - adjacent->x);
- }
- }
- } else if (y >= output->height) { // Bottom edge
- if ((adjacent = swayc_adjacent_output(output, MOVE_DOWN, &abs_pos, false))) {
- if (workspace_switch(swayc_active_workspace_for(adjacent))) {
- new_y = 0;
- new_x += (output->x - adjacent->x);
- }
- }
- }
- }
-
- pointer_position_set(new_x, new_y, false);
-
- swayc_t *focused = get_focused_container(&root_container);
- if (focused->type == C_VIEW) {
- pid_t pid = wlc_view_get_pid(focused->handle);
- if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
- return EVENT_HANDLED;
- }
- }
-
- return EVENT_PASSTHROUGH;
-}
-
-static bool swayc_border_check(swayc_t *c, const void *_origin) {
- const struct wlc_point *origin = _origin;
- const struct wlc_geometry title_bar = c->title_bar_geometry;
-
- if (c->border_type != B_NORMAL) {
- return false;
- }
-
- if (origin->x >= title_bar.origin.x && origin->y >= title_bar.origin.y
- && origin->x < title_bar.origin.x + (int32_t)title_bar.size.w
- && origin->y < title_bar.origin.y + (int32_t)title_bar.size.h) {
- return true;
- }
- return false;
-}
-
-static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct wlc_modifiers *modifiers,
- uint32_t button, enum wlc_button_state state, const struct wlc_point *origin) {
-
- // Update view pointer is on
- pointer_state.view = container_under_pointer();
-
- struct sway_mode *mode = config->current_mode;
- // handle bindings
- for (int i = 0; i < mode->bindings->length; ++i) {
- struct sway_binding *binding = mode->bindings->items[i];
- if ((modifiers->mods ^ binding->modifiers) == 0) {
- switch (state) {
- case WLC_BUTTON_STATE_PRESSED:
- if (!binding->release && handle_bindsym(binding, button, 0)) {
- return EVENT_HANDLED;
- }
- break;
- case WLC_BUTTON_STATE_RELEASED:
- if (binding->release && handle_bindsym(binding, button, 0)) {
- return EVENT_HANDLED;
- }
- break;
- }
- }
- }
-
- // Update pointer_state
- switch (button) {
- case M_LEFT_CLICK:
- if (state == WLC_BUTTON_STATE_PRESSED) {
- pointer_state.left.held = true;
- pointer_state.left.x = origin->x;
- pointer_state.left.y = origin->y;
- pointer_state.left.view = pointer_state.view;
- } else {
- pointer_state.left.held = false;
- }
- break;
-
- case M_RIGHT_CLICK:
- if (state == WLC_BUTTON_STATE_PRESSED) {
- pointer_state.right.held = true;
- pointer_state.right.x = origin->x;
- pointer_state.right.y = origin->y;
- pointer_state.right.view = pointer_state.view;
- } else {
- pointer_state.right.held = false;
- }
- break;
-
- case M_SCROLL_CLICK:
- if (state == WLC_BUTTON_STATE_PRESSED) {
- pointer_state.scroll.held = true;
- pointer_state.scroll.x = origin->x;
- pointer_state.scroll.y = origin->y;
- pointer_state.scroll.view = pointer_state.view;
- } else {
- pointer_state.scroll.held = false;
- }
- break;
-
- //TODO scrolling behavior
- case M_SCROLL_UP:
- case M_SCROLL_DOWN:
- break;
- }
-
- // get focused window and check if to change focus on mouse click
- swayc_t *focused = get_focused_container(&root_container);
-
- // don't change focus or mode if fullscreen
- if (swayc_is_fullscreen(focused)) {
- if (focused->type == C_VIEW) {
- pid_t pid = wlc_view_get_pid(focused->handle);
- if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
- return EVENT_HANDLED;
- }
- }
- return EVENT_PASSTHROUGH;
- }
-
- // set pointer mode only if floating mod has been set
- if (config->floating_mod) {
- pointer_mode_set(button, !(modifiers->mods ^ config->floating_mod));
- }
-
- // Check whether to change focus
- swayc_t *pointer = pointer_state.view;
- if (pointer) {
- swayc_t *ws = swayc_parent_by_type(focused, C_WORKSPACE);
- if (ws != NULL) {
- swayc_t *find = container_find(ws, &swayc_border_check, origin);
- if (find != NULL) {
- set_focused_container(find);
- return EVENT_HANDLED;
- }
- }
-
- if (focused != pointer) {
- set_focused_container(pointer_state.view);
- }
- // Send to front if floating
- if (pointer->is_floating) {
- int i;
- for (i = 0; i < pointer->parent->floating->length; i++) {
- if (pointer->parent->floating->items[i] == pointer) {
- list_del(pointer->parent->floating, i);
- list_add(pointer->parent->floating, pointer);
- break;
- }
- }
- wlc_view_bring_to_front(pointer->handle);
- }
- }
-
- // Return if mode has been set
- if (pointer_state.mode) {
- return EVENT_HANDLED;
- }
-
- if (focused->type == C_VIEW) {
- pid_t pid = wlc_view_get_pid(focused->handle);
- if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
- return EVENT_HANDLED;
- }
- }
-
- // Always send mouse release
- if (state == WLC_BUTTON_STATE_RELEASED) {
- return EVENT_PASSTHROUGH;
- }
-
- // Finally send click
- return EVENT_PASSTHROUGH;
-}
-
-bool handle_pointer_scroll(wlc_handle view, uint32_t time, const struct wlc_modifiers* modifiers,
- uint8_t axis_bits, double _amount[2]) {
- if (!(modifiers->mods ^ config->floating_mod)) {
- int x_amount = (int)_amount[0];
- int y_amount = (int)_amount[1];
-
- if (x_amount > 0 && strcmp(config->floating_scroll_up_cmd, "")) {
- handle_command(config->floating_scroll_up_cmd, CONTEXT_BINDING);
- return EVENT_HANDLED;
- } else if (x_amount < 0 && strcmp(config->floating_scroll_down_cmd, "")) {
- handle_command(config->floating_scroll_down_cmd, CONTEXT_BINDING);
- return EVENT_HANDLED;
- }
-
- if (y_amount > 0 && strcmp(config->floating_scroll_right_cmd, "")) {
- handle_command(config->floating_scroll_right_cmd, CONTEXT_BINDING);
- return EVENT_HANDLED;
- } else if (y_amount < 0 && strcmp(config->floating_scroll_left_cmd, "")) {
- handle_command(config->floating_scroll_left_cmd, CONTEXT_BINDING);
- return EVENT_HANDLED;
- }
- }
- return EVENT_PASSTHROUGH;
-}
-
-static void handle_wlc_ready(void) {
- sway_log(L_DEBUG, "Compositor is ready, executing cmds in queue");
- // Execute commands until there are none left
- config->active = true;
- while (config->cmd_queue->length) {
- char *line = config->cmd_queue->items[0];
- struct cmd_results *res = handle_command(line, CONTEXT_CONFIG);
- if (res->status != CMD_SUCCESS) {
- sway_log(L_ERROR, "Error on line '%s': %s", line, res->error);
- }
- free_cmd_results(res);
- free(line);
- list_del(config->cmd_queue, 0);
- }
-}
-
-void register_wlc_handlers() {
- wlc_set_output_created_cb(handle_output_created);
- wlc_set_output_destroyed_cb(handle_output_destroyed);
- wlc_set_output_resolution_cb(handle_output_resolution_change);
- wlc_set_output_focus_cb(handle_output_focused);
- wlc_set_output_render_post_cb(handle_output_post_render);
- wlc_set_view_created_cb(handle_view_created);
- wlc_set_view_destroyed_cb(handle_view_destroyed);
- wlc_set_view_focus_cb(handle_view_focus);
- wlc_set_view_render_pre_cb(handle_view_pre_render);
- wlc_set_view_request_geometry_cb(handle_view_geometry_request);
- wlc_set_view_request_state_cb(handle_view_state_request);
- wlc_set_view_properties_updated_cb(handle_view_properties_updated);
- wlc_set_keyboard_key_cb(handle_key);
- wlc_set_pointer_motion_cb_v2(handle_pointer_motion);
- wlc_set_pointer_button_cb(handle_pointer_button);
- wlc_set_pointer_scroll_cb(handle_pointer_scroll);
- wlc_set_compositor_ready_cb(handle_wlc_ready);
- wlc_set_input_created_cb(handle_input_created);
- wlc_set_input_destroyed_cb(handle_input_destroyed);
-}
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 @@
-#define _XOPEN_SOURCE 700
-#include <ctype.h>
-#include <float.h>
-#include <limits.h>
-#include <stdio.h>
-#include <string.h>
-#include <libinput.h>
-#include "sway/config.h"
-#include "sway/input.h"
-#include "list.h"
-#include "log.h"
-
-struct input_config *new_input_config(const char* identifier) {
- struct input_config *input = calloc(1, sizeof(struct input_config));
- if (!input) {
- sway_log(L_DEBUG, "Unable to allocate input config");
- return NULL;
- }
- sway_log(L_DEBUG, "new_input_config(%s)", identifier);
- if (!(input->identifier = strdup(identifier))) {
- free(input);
- sway_log(L_DEBUG, "Unable to allocate input config");
- return NULL;
- }
-
- input->tap = INT_MIN;
- input->drag_lock = INT_MIN;
- input->dwt = INT_MIN;
- input->send_events = INT_MIN;
- input->click_method = INT_MIN;
- input->middle_emulation = INT_MIN;
- input->natural_scroll = INT_MIN;
- input->accel_profile = INT_MIN;
- input->pointer_accel = FLT_MIN;
- input->scroll_method = INT_MIN;
- input->left_handed = INT_MIN;
-
- return input;
-}
-
-char *libinput_dev_unique_id(struct libinput_device *device) {
- int vendor = libinput_device_get_id_vendor(device);
- int product = libinput_device_get_id_product(device);
- char *name = strdup(libinput_device_get_name(device));
-
- char *p = name;
- for (; *p; ++p) {
- if (*p == ' ') {
- *p = '_';
- }
- }
-
- sway_log(L_DEBUG, "rewritten name %s", name);
-
- int len = strlen(name) + sizeof(char) * 6;
- char *identifier = malloc(len);
- if (!identifier) {
- sway_log(L_ERROR, "Unable to allocate unique input device name");
- return NULL;
- }
-
- const char *fmt = "%d:%d:%s";
- snprintf(identifier, len, fmt, vendor, product, name);
- free(name);
- return identifier;
-}
-
-list_t *input_devices = NULL;
-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 @@
+#define _XOPEN_SOURCE 700
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
+#include <wlr/types/wlr_cursor.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+#include "list.h"
+#include "log.h"
+#include "sway/input/cursor.h"
+#include "sway/layers.h"
+#include "sway/output.h"
+#include "sway/tree/view.h"
+#include "wlr-layer-shell-unstable-v1-protocol.h"
+
+static struct wlr_surface *layer_surface_at(struct sway_output *output,
+ struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
+ struct sway_layer_surface *sway_layer;
+ wl_list_for_each_reverse(sway_layer, layer, link) {
+ struct wlr_surface *wlr_surface =
+ sway_layer->layer_surface->surface;
+ double _sx = ox - sway_layer->geo.x;
+ double _sy = oy - sway_layer->geo.y;
+ // TODO: Test popups/subsurfaces
+ if (wlr_surface_point_accepts_input(wlr_surface, _sx, _sy)) {
+ *sx = _sx;
+ *sy = _sy;
+ return wlr_surface;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Returns the container at the cursor's position. If there is a surface at that
+ * location, it is stored in **surface (it may not be a view).
+ */
+static struct sway_container *container_at_cursor(struct sway_cursor *cursor,
+ struct wlr_surface **surface, double *sx, double *sy) {
+ // check for unmanaged views first
+ struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged;
+ struct sway_xwayland_unmanaged *unmanaged_surface;
+ wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
+ struct wlr_xwayland_surface *xsurface =
+ unmanaged_surface->wlr_xwayland_surface;
+
+ double _sx = cursor->cursor->x - unmanaged_surface->lx;
+ double _sy = cursor->cursor->y - unmanaged_surface->ly;
+ if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) {
+ *surface = xsurface->surface;
+ *sx = _sx;
+ *sy = _sy;
+ return NULL;
+ }
+ }
+
+ // find the output the cursor is on
+ struct wlr_output_layout *output_layout =
+ root_container.sway_root->output_layout;
+ struct wlr_output *wlr_output =
+ wlr_output_layout_output_at(output_layout,
+ cursor->cursor->x, cursor->cursor->y);
+ if (wlr_output == NULL) {
+ return NULL;
+ }
+ struct sway_output *output = wlr_output->data;
+ double ox = cursor->cursor->x, oy = cursor->cursor->y;
+ wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy);
+
+ // find the focused workspace on the output for this seat
+ struct sway_container *ws =
+ seat_get_focus_inactive(cursor->seat, output->swayc);
+ if (ws && ws->type != C_WORKSPACE) {
+ ws = container_parent(ws, C_WORKSPACE);
+ }
+ if (!ws) {
+ return output->swayc;
+ }
+
+ if ((*surface = layer_surface_at(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
+ ox, oy, sx, sy))) {
+ return ws;
+ }
+ if ((*surface = layer_surface_at(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
+ ox, oy, sx, sy))) {
+ return ws;
+ }
+
+ struct sway_container *c;
+ if ((c = container_at(ws, cursor->cursor->x, cursor->cursor->y,
+ surface, sx, sy))) {
+ return c;
+ }
+
+ if ((*surface = layer_surface_at(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
+ ox, oy, sx, sy))) {
+ return ws;
+ }
+ if ((*surface = layer_surface_at(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
+ ox, oy, sx, sy))) {
+ return ws;
+ }
+
+ c = seat_get_focus_inactive(cursor->seat, output->swayc);
+ if (c) {
+ return c;
+ }
+ if (!c && output->swayc->children->length) {
+ c = output->swayc->children->items[0];
+ return c;
+ }
+
+ return output->swayc;
+}
+
+void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time) {
+ struct wlr_seat *seat = cursor->seat->wlr_seat;
+ struct wlr_surface *surface = NULL;
+ double sx, sy;
+ struct sway_container *c = container_at_cursor(cursor, &surface, &sx, &sy);
+ if (c && config->focus_follows_mouse) {
+ seat_set_focus_warp(cursor->seat, c, false);
+ }
+
+ // reset cursor if switching between clients
+ struct wl_client *client = NULL;
+ if (surface != NULL) {
+ client = wl_resource_get_client(surface->resource);
+ }
+ if (client != cursor->image_client) {
+ wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
+ "left_ptr", cursor->cursor);
+ cursor->image_client = client;
+ }
+
+ // send pointer enter/leave
+ if (surface != NULL) {
+ if (seat_is_input_allowed(cursor->seat, surface)) {
+ wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
+ wlr_seat_pointer_notify_motion(seat, time, sx, sy);
+ }
+ } else {
+ wlr_seat_pointer_clear_focus(seat);
+ }
+}
+
+static void handle_cursor_motion(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
+ struct wlr_event_pointer_motion *event = data;
+ wlr_cursor_move(cursor->cursor, event->device,
+ event->delta_x, event->delta_y);
+ cursor_send_pointer_motion(cursor, event->time_msec);
+}
+
+static void handle_cursor_motion_absolute(
+ struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor =
+ wl_container_of(listener, cursor, motion_absolute);
+ struct wlr_event_pointer_motion_absolute *event = data;
+ wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
+ cursor_send_pointer_motion(cursor, event->time_msec);
+}
+
+void dispatch_cursor_button(struct sway_cursor *cursor,
+ uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
+ struct wlr_surface *surface = NULL;
+ double sx, sy;
+ struct sway_container *cont =
+ container_at_cursor(cursor, &surface, &sx, &sy);
+ if (surface && wlr_surface_is_layer_surface(surface)) {
+ struct wlr_layer_surface *layer =
+ wlr_layer_surface_from_wlr_surface(surface);
+ if (layer->current.keyboard_interactive) {
+ seat_set_focus_layer(cursor->seat, layer);
+ return;
+ }
+ }
+ // Avoid moving keyboard focus from a surface that accepts it to one
+ // that does not unless the change would move us to a new workspace.
+ //
+ // This prevents, for example, losing focus when clicking on swaybar.
+ if (surface && cont && cont->type != C_VIEW) {
+ struct sway_container *new_ws = cont;
+ if (new_ws && new_ws->type != C_WORKSPACE) {
+ new_ws = container_parent(new_ws, C_WORKSPACE);
+ }
+ struct sway_container *old_ws = seat_get_focus(cursor->seat);
+ if (old_ws && old_ws->type != C_WORKSPACE) {
+ old_ws = container_parent(old_ws, C_WORKSPACE);
+ }
+ if (new_ws != old_ws) {
+ seat_set_focus(cursor->seat, cont);
+ }
+ } else {
+ seat_set_focus(cursor->seat, cont);
+ }
+
+ wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
+ time_msec, button, state);
+}
+
+static void handle_cursor_button(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
+ struct wlr_event_pointer_button *event = data;
+ dispatch_cursor_button(cursor,
+ event->time_msec, event->button, event->state);
+}
+
+static void handle_cursor_axis(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
+ struct wlr_event_pointer_axis *event = data;
+ wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
+ event->orientation, event->delta);
+}
+
+static void handle_touch_down(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);
+ struct wlr_event_touch_down *event = data;
+ wlr_log(L_DEBUG, "TODO: handle touch down event: %p", event);
+}
+
+static void handle_touch_up(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);
+ struct wlr_event_touch_up *event = data;
+ wlr_log(L_DEBUG, "TODO: handle touch up event: %p", event);
+}
+
+static void handle_touch_motion(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor =
+ wl_container_of(listener, cursor, touch_motion);
+ struct wlr_event_touch_motion *event = data;
+ wlr_log(L_DEBUG, "TODO: handle touch motion event: %p", event);
+}
+
+static void handle_tool_axis(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
+ struct wlr_event_tablet_tool_axis *event = data;
+
+ if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
+ (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
+ wlr_cursor_warp_absolute(cursor->cursor, event->device,
+ event->x, event->y);
+ cursor_send_pointer_motion(cursor, event->time_msec);
+ } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) {
+ wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, -1);
+ cursor_send_pointer_motion(cursor, event->time_msec);
+ } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
+ wlr_cursor_warp_absolute(cursor->cursor, event->device, -1, event->y);
+ cursor_send_pointer_motion(cursor, event->time_msec);
+ }
+}
+
+static void handle_tool_tip(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
+ struct wlr_event_tablet_tool_tip *event = data;
+ dispatch_cursor_button(cursor, event->time_msec,
+ BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ?
+ WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED);
+}
+
+static void handle_tool_button(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
+ struct wlr_event_tablet_tool_button *event = data;
+ // TODO: the user may want to configure which tool buttons are mapped to
+ // which simulated pointer buttons
+ switch (event->state) {
+ case WLR_BUTTON_PRESSED:
+ if (cursor->tool_buttons == 0) {
+ dispatch_cursor_button(cursor,
+ event->time_msec, BTN_RIGHT, event->state);
+ }
+ cursor->tool_buttons++;
+ break;
+ case WLR_BUTTON_RELEASED:
+ if (cursor->tool_buttons == 1) {
+ dispatch_cursor_button(cursor,
+ event->time_msec, BTN_RIGHT, event->state);
+ }
+ cursor->tool_buttons--;
+ break;
+ }
+}
+
+static void handle_request_set_cursor(struct wl_listener *listener,
+ void *data) {
+ struct sway_cursor *cursor =
+ wl_container_of(listener, cursor, request_set_cursor);
+ struct wlr_seat_pointer_request_set_cursor_event *event = data;
+
+ struct wl_client *focused_client = NULL;
+ struct wlr_surface *focused_surface =
+ cursor->seat->wlr_seat->pointer_state.focused_surface;
+ if (focused_surface != NULL) {
+ focused_client = wl_resource_get_client(focused_surface->resource);
+ }
+
+ // TODO: check cursor mode
+ if (focused_client == NULL ||
+ event->seat_client->client != focused_client) {
+ wlr_log(L_DEBUG, "denying request to set cursor from unfocused client");
+ return;
+ }
+
+ wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x,
+ event->hotspot_y);
+ cursor->image_client = focused_client;
+}
+
+void sway_cursor_destroy(struct sway_cursor *cursor) {
+ if (!cursor) {
+ return;
+ }
+
+ wlr_xcursor_manager_destroy(cursor->xcursor_manager);
+ wlr_cursor_destroy(cursor->cursor);
+ free(cursor);
+}
+
+struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
+ struct sway_cursor *cursor = calloc(1, sizeof(struct sway_cursor));
+ if (!sway_assert(cursor, "could not allocate sway cursor")) {
+ return NULL;
+ }
+
+ struct wlr_cursor *wlr_cursor = wlr_cursor_create();
+ if (!sway_assert(wlr_cursor, "could not allocate wlr cursor")) {
+ free(cursor);
+ return NULL;
+ }
+
+ cursor->seat = seat;
+ wlr_cursor_attach_output_layout(wlr_cursor,
+ root_container.sway_root->output_layout);
+
+ // input events
+ wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
+ cursor->motion.notify = handle_cursor_motion;
+
+ wl_signal_add(&wlr_cursor->events.motion_absolute,
+ &cursor->motion_absolute);
+ cursor->motion_absolute.notify = handle_cursor_motion_absolute;
+
+ wl_signal_add(&wlr_cursor->events.button, &cursor->button);
+ cursor->button.notify = handle_cursor_button;
+
+ wl_signal_add(&wlr_cursor->events.axis, &cursor->axis);
+ cursor->axis.notify = handle_cursor_axis;
+
+ wl_signal_add(&wlr_cursor->events.touch_down, &cursor->touch_down);
+ cursor->touch_down.notify = handle_touch_down;
+
+ wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up);
+ cursor->touch_up.notify = handle_touch_up;
+
+ wl_signal_add(&wlr_cursor->events.touch_motion,
+ &cursor->touch_motion);
+ cursor->touch_motion.notify = handle_touch_motion;
+
+ // TODO: tablet protocol support
+ // Note: We should emulate pointer events for clients that don't support the
+ // tablet protocol when the time comes
+ wl_signal_add(&wlr_cursor->events.tablet_tool_axis,
+ &cursor->tool_axis);
+ cursor->tool_axis.notify = handle_tool_axis;
+
+ wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip);
+ cursor->tool_tip.notify = handle_tool_tip;
+
+ wl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tool_button);
+ cursor->tool_button.notify = handle_tool_button;
+
+ wl_signal_add(&seat->wlr_seat->events.request_set_cursor,
+ &cursor->request_set_cursor);
+ cursor->request_set_cursor.notify = handle_request_set_cursor;
+
+ cursor->cursor = wlr_cursor;
+
+ return cursor;
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include <ctype.h>
+#include <float.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <libinput.h>
+#include <math.h>
+#include <wlr/backend/libinput.h>
+#include <wlr/types/wlr_input_inhibitor.h>
+#include "sway/config.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/server.h"
+#include "stringop.h"
+#include "list.h"
+#include "log.h"
+
+static const char *default_seat = "seat0";
+
+// TODO make me not global
+struct sway_input_manager *input_manager;
+
+struct input_config *current_input_config = NULL;
+struct seat_config *current_seat_config = NULL;
+
+struct sway_seat *input_manager_current_seat(struct sway_input_manager *input) {
+ struct sway_seat *seat = config->handler_context.seat;
+ if (!seat) {
+ seat = input_manager_get_default_seat(input_manager);
+ }
+ return seat;
+}
+
+struct sway_seat *input_manager_get_seat(
+ struct sway_input_manager *input, const char *seat_name) {
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ if (strcmp(seat->wlr_seat->name, seat_name) == 0) {
+ return seat;
+ }
+ }
+
+ return seat_create(input, seat_name);
+}
+
+static char *get_device_identifier(struct wlr_input_device *device) {
+ int vendor = device->vendor;
+ int product = device->product;
+ char *name = strdup(device->name);
+ name = strip_whitespace(name);
+
+ char *p = name;
+ for (; *p; ++p) {
+ if (*p == ' ') {
+ *p = '_';
+ }
+ }
+
+ const char *fmt = "%d:%d:%s";
+ int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1;
+ char *identifier = malloc(len);
+ if (!identifier) {
+ wlr_log(L_ERROR, "Unable to allocate unique input device name");
+ return NULL;
+ }
+
+ snprintf(identifier, len, fmt, vendor, product, name);
+ free(name);
+ return identifier;
+}
+
+static struct sway_input_device *input_sway_device_from_wlr(
+ struct sway_input_manager *input, struct wlr_input_device *device) {
+ struct sway_input_device *input_device = NULL;
+ wl_list_for_each(input_device, &input->devices, link) {
+ if (input_device->wlr_device == device) {
+ return input_device;
+ }
+ }
+ return NULL;
+}
+
+static bool input_has_seat_configuration(struct sway_input_manager *input) {
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ struct seat_config *seat_config = seat_get_config(seat);
+ if (seat_config) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void input_manager_libinput_config_pointer(
+ struct sway_input_device *input_device) {
+ struct wlr_input_device *wlr_device = input_device->wlr_device;
+ struct input_config *ic = input_device_get_config(input_device);
+ struct libinput_device *libinput_device;
+
+ if (!ic || !wlr_input_device_is_libinput(wlr_device)) {
+ return;
+ }
+
+ libinput_device = wlr_libinput_get_device_handle(wlr_device);
+ wlr_log(L_DEBUG, "input_manager_libinput_config_pointer(%s)",
+ ic->identifier);
+
+ if (ic->accel_profile != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)",
+ ic->identifier, ic->accel_profile);
+ libinput_device_config_accel_set_profile(libinput_device,
+ ic->accel_profile);
+ }
+ if (ic->click_method != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)",
+ ic->identifier, ic->click_method);
+ libinput_device_config_click_set_method(libinput_device,
+ ic->click_method);
+ }
+ if (ic->drag_lock != INT_MIN) {
+ wlr_log(L_DEBUG,
+ "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",
+ ic->identifier, ic->click_method);
+ libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
+ ic->drag_lock);
+ }
+ if (ic->dwt != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)",
+ ic->identifier, ic->dwt);
+ libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt);
+ }
+ if (ic->left_handed != INT_MIN) {
+ wlr_log(L_DEBUG,
+ "libinput_config_pointer(%s) left_handed_set_enabled(%d)",
+ ic->identifier, ic->left_handed);
+ libinput_device_config_left_handed_set(libinput_device,
+ ic->left_handed);
+ }
+ if (ic->middle_emulation != INT_MIN) {
+ wlr_log(L_DEBUG,
+ "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)",
+ ic->identifier, ic->middle_emulation);
+ libinput_device_config_middle_emulation_set_enabled(libinput_device,
+ ic->middle_emulation);
+ }
+ if (ic->natural_scroll != INT_MIN) {
+ wlr_log(L_DEBUG,
+ "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)",
+ ic->identifier, ic->natural_scroll);
+ libinput_device_config_scroll_set_natural_scroll_enabled(
+ libinput_device, ic->natural_scroll);
+ }
+ if (ic->pointer_accel != FLT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)",
+ ic->identifier, ic->pointer_accel);
+ libinput_device_config_accel_set_speed(libinput_device,
+ ic->pointer_accel);
+ }
+ if (ic->scroll_method != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)",
+ ic->identifier, ic->scroll_method);
+ libinput_device_config_scroll_set_method(libinput_device,
+ ic->scroll_method);
+ }
+ if (ic->send_events != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)",
+ ic->identifier, ic->send_events);
+ libinput_device_config_send_events_set_mode(libinput_device,
+ ic->send_events);
+ }
+ if (ic->tap != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)",
+ ic->identifier, ic->tap);
+ libinput_device_config_tap_set_enabled(libinput_device, ic->tap);
+ }
+}
+
+static void handle_device_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_input_device *device = data;
+
+ struct sway_input_device *input_device =
+ input_sway_device_from_wlr(input_manager, device);
+
+ if (!sway_assert(input_device, "could not find sway device")) {
+ return;
+ }
+
+ wlr_log(L_DEBUG, "removing device: '%s'",
+ input_device->identifier);
+
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ seat_remove_device(seat, input_device);
+ }
+
+ wl_list_remove(&input_device->link);
+ wl_list_remove(&input_device->device_destroy.link);
+ free(input_device->identifier);
+ free(input_device);
+}
+
+static void handle_new_input(struct wl_listener *listener, void *data) {
+ struct sway_input_manager *input =
+ wl_container_of(listener, input, new_input);
+ struct wlr_input_device *device = data;
+
+ struct sway_input_device *input_device =
+ calloc(1, sizeof(struct sway_input_device));
+ if (!sway_assert(input_device, "could not allocate input device")) {
+ return;
+ }
+
+ input_device->wlr_device = device;
+ input_device->identifier = get_device_identifier(device);
+ wl_list_insert(&input->devices, &input_device->link);
+
+ wlr_log(L_DEBUG, "adding device: '%s'",
+ input_device->identifier);
+
+ if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
+ input_manager_libinput_config_pointer(input_device);
+ }
+
+ struct sway_seat *seat = NULL;
+ if (!input_has_seat_configuration(input)) {
+ wlr_log(L_DEBUG, "no seat configuration, using default seat");
+ seat = input_manager_get_seat(input, default_seat);
+ seat_add_device(seat, input_device);
+ return;
+ }
+
+ bool added = false;
+ wl_list_for_each(seat, &input->seats, link) {
+ struct seat_config *seat_config = seat_get_config(seat);
+ bool has_attachment = seat_config &&
+ (seat_config_get_attachment(seat_config, input_device->identifier) ||
+ seat_config_get_attachment(seat_config, "*"));
+
+ if (has_attachment) {
+ seat_add_device(seat, input_device);
+ added = true;
+ }
+ }
+
+ if (!added) {
+ wl_list_for_each(seat, &input->seats, link) {
+ struct seat_config *seat_config = seat_get_config(seat);
+ if (seat_config && seat_config->fallback == 1) {
+ seat_add_device(seat, input_device);
+ added = true;
+ }
+ }
+ }
+
+ if (!added) {
+ wlr_log(L_DEBUG,
+ "device '%s' is not configured on any seats",
+ input_device->identifier);
+ }
+
+ wl_signal_add(&device->events.destroy, &input_device->device_destroy);
+ input_device->device_destroy.notify = handle_device_destroy;
+}
+
+static void handle_inhibit_activate(struct wl_listener *listener, void *data) {
+ struct sway_input_manager *input_manager = wl_container_of(
+ listener, input_manager, inhibit_activate);
+ struct sway_seat *seat;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ seat_set_exclusive_client(seat, input_manager->inhibit->active_client);
+ }
+}
+
+static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) {
+ struct sway_input_manager *input_manager = wl_container_of(
+ listener, input_manager, inhibit_deactivate);
+ struct sway_seat *seat;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ seat_set_exclusive_client(seat, NULL);
+ struct sway_container *previous = seat_get_focus(seat);
+ if (previous) {
+ wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous,
+ container_type_to_str(previous->type), previous->name);
+ // Hack to get seat to re-focus the return value of get_focus
+ seat_set_focus(seat, previous->parent);
+ seat_set_focus(seat, previous);
+ }
+ }
+}
+
+struct sway_input_manager *input_manager_create(
+ struct sway_server *server) {
+ struct sway_input_manager *input =
+ calloc(1, sizeof(struct sway_input_manager));
+ if (!input) {
+ return NULL;
+ }
+ input->server = server;
+
+ wl_list_init(&input->devices);
+ wl_list_init(&input->seats);
+
+ // create the default seat
+ input_manager_get_seat(input, default_seat);
+
+ input->new_input.notify = handle_new_input;
+ wl_signal_add(&server->backend->events.new_input, &input->new_input);
+
+ input->inhibit = wlr_input_inhibit_manager_create(server->wl_display);
+ input->inhibit_activate.notify = handle_inhibit_activate;
+ wl_signal_add(&input->inhibit->events.activate,
+ &input->inhibit_activate);
+ input->inhibit_deactivate.notify = handle_inhibit_deactivate;
+ wl_signal_add(&input->inhibit->events.deactivate,
+ &input->inhibit_deactivate);
+
+ return input;
+}
+
+bool input_manager_has_focus(struct sway_input_manager *input,
+ struct sway_container *container) {
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ if (seat_get_focus(seat) == container) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void input_manager_set_focus(struct sway_input_manager *input,
+ struct sway_container *container) {
+ struct sway_seat *seat;
+ wl_list_for_each(seat, &input->seats, link) {
+ seat_set_focus(seat, container);
+ }
+}
+
+void input_manager_apply_input_config(struct sway_input_manager *input,
+ struct input_config *input_config) {
+ struct sway_input_device *input_device = NULL;
+ wl_list_for_each(input_device, &input->devices, link) {
+ if (strcmp(input_device->identifier, input_config->identifier) == 0) {
+ if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
+ input_manager_libinput_config_pointer(input_device);
+ }
+
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ seat_configure_device(seat, input_device);
+ }
+ }
+ }
+}
+
+void input_manager_apply_seat_config(struct sway_input_manager *input,
+ struct seat_config *seat_config) {
+ wlr_log(L_DEBUG, "applying new seat config for seat %s",
+ seat_config->name);
+ struct sway_seat *seat = input_manager_get_seat(input, seat_config->name);
+ if (!seat) {
+ return;
+ }
+
+ seat_apply_config(seat, seat_config);
+
+ // for every device, try to add it to a seat and if no seat has it
+ // attached, add it to the fallback seats.
+ struct sway_input_device *input_device = NULL;
+ wl_list_for_each(input_device, &input->devices, link) {
+ list_t *seat_list = create_list();
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ struct seat_config *seat_config = seat_get_config(seat);
+ if (!seat_config) {
+ continue;
+ }
+ if (seat_config_get_attachment(seat_config, "*") ||
+ seat_config_get_attachment(seat_config,
+ input_device->identifier)) {
+ list_add(seat_list, seat);
+ }
+ }
+
+ if (seat_list->length) {
+ wl_list_for_each(seat, &input->seats, link) {
+ bool attached = false;
+ for (int i = 0; i < seat_list->length; ++i) {
+ if (seat == seat_list->items[i]) {
+ attached = true;
+ break;
+ }
+ }
+ if (attached) {
+ seat_add_device(seat, input_device);
+ } else {
+ seat_remove_device(seat, input_device);
+ }
+ }
+ } else {
+ wl_list_for_each(seat, &input->seats, link) {
+ struct seat_config *seat_config = seat_get_config(seat);
+ if (seat_config && seat_config->fallback == 1) {
+ seat_add_device(seat, input_device);
+ } else {
+ seat_remove_device(seat, input_device);
+ }
+ }
+ }
+ list_free(seat_list);
+ }
+}
+
+void input_manager_configure_xcursor(struct sway_input_manager *input) {
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ seat_configure_xcursor(seat);
+ }
+}
+
+struct sway_seat *input_manager_get_default_seat(
+ struct sway_input_manager *input) {
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ if (strcmp(seat->wlr_seat->name, "seat0") == 0) {
+ return seat;
+ }
+ }
+ return seat;
+}
+
+struct input_config *input_device_get_config(struct sway_input_device *device) {
+ struct input_config *input_config = NULL;
+ for (int i = 0; i < config->input_configs->length; ++i) {
+ input_config = config->input_configs->items[i];
+ if (strcmp(input_config->identifier, device->identifier) == 0) {
+ return input_config;
+ }
+ }
+
+ return NULL;
+}
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 @@
+#include <assert.h>
+#include <wlr/backend/multi.h>
+#include <wlr/backend/session.h>
+#include "sway/input/seat.h"
+#include "sway/input/keyboard.h"
+#include "sway/input/input-manager.h"
+#include "sway/commands.h"
+#include "log.h"
+
+static bool keysym_is_modifier(xkb_keysym_t keysym) {
+ switch (keysym) {
+ case XKB_KEY_Shift_L: case XKB_KEY_Shift_R:
+ case XKB_KEY_Control_L: case XKB_KEY_Control_R:
+ case XKB_KEY_Caps_Lock:
+ case XKB_KEY_Shift_Lock:
+ case XKB_KEY_Meta_L: case XKB_KEY_Meta_R:
+ case XKB_KEY_Alt_L: case XKB_KEY_Alt_R:
+ case XKB_KEY_Super_L: case XKB_KEY_Super_R:
+ case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static size_t pressed_keysyms_length(xkb_keysym_t *pressed_keysyms) {
+ size_t n = 0;
+ for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) {
+ if (pressed_keysyms[i] != XKB_KEY_NoSymbol) {
+ ++n;
+ }
+ }
+ return n;
+}
+
+static ssize_t pressed_keysyms_index(xkb_keysym_t *pressed_keysyms,
+ xkb_keysym_t keysym) {
+ for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) {
+ if (pressed_keysyms[i] == keysym) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void pressed_keysyms_add(xkb_keysym_t *pressed_keysyms,
+ xkb_keysym_t keysym) {
+ ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym);
+ if (i < 0) {
+ i = pressed_keysyms_index(pressed_keysyms, XKB_KEY_NoSymbol);
+ if (i >= 0) {
+ pressed_keysyms[i] = keysym;
+ }
+ }
+}
+
+static void pressed_keysyms_remove(xkb_keysym_t *pressed_keysyms,
+ xkb_keysym_t keysym) {
+ ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym);
+ if (i >= 0) {
+ pressed_keysyms[i] = XKB_KEY_NoSymbol;
+ }
+}
+
+static void pressed_keysyms_update(xkb_keysym_t *pressed_keysyms,
+ const xkb_keysym_t *keysyms, size_t keysyms_len,
+ enum wlr_key_state state) {
+ for (size_t i = 0; i < keysyms_len; ++i) {
+ if (keysym_is_modifier(keysyms[i])) {
+ continue;
+ }
+ if (state == WLR_KEY_PRESSED) {
+ pressed_keysyms_add(pressed_keysyms, keysyms[i]);
+ } else { // WLR_KEY_RELEASED
+ pressed_keysyms_remove(pressed_keysyms, keysyms[i]);
+ }
+ }
+}
+
+static bool binding_matches_key_state(struct sway_binding *binding,
+ enum wlr_key_state key_state) {
+ if (key_state == WLR_KEY_PRESSED && !binding->release) {
+ return true;
+ }
+ if (key_state == WLR_KEY_RELEASED && binding->release) {
+ return true;
+ }
+
+ return false;
+}
+
+static void keyboard_execute_command(struct sway_keyboard *keyboard,
+ struct sway_binding *binding) {
+ wlr_log(L_DEBUG, "running command for binding: %s",
+ binding->command);
+ config_clear_handler_context(config);
+ config->handler_context.seat = keyboard->seat_device->sway_seat;
+ struct cmd_results *results = execute_command(binding->command, NULL);
+ if (results->status != CMD_SUCCESS) {
+ wlr_log(L_DEBUG, "could not run command for binding: %s (%s)",
+ binding->command, results->error);
+ }
+ free_cmd_results(results);
+}
+
+/**
+ * Execute a built-in, hardcoded compositor binding. These are triggered from a
+ * single keysym.
+ *
+ * Returns true if the keysym was handled by a binding and false if the event
+ * should be propagated to clients.
+ */
+static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
+ xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) {
+ for (size_t i = 0; i < keysyms_len; ++i) {
+ xkb_keysym_t keysym = pressed_keysyms[i];
+ if (keysym >= XKB_KEY_XF86Switch_VT_1 &&
+ keysym <= XKB_KEY_XF86Switch_VT_12) {
+ if (wlr_backend_is_multi(server.backend)) {
+ struct wlr_session *session =
+ wlr_multi_get_session(server.backend);
+ if (session) {
+ unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
+ wlr_session_change_vt(session, vt);
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Execute keyboard bindings bound with `bindysm` for the given keyboard state.
+ *
+ * Returns true if the keysym was handled by a binding and false if the event
+ * should be propagated to clients.
+ */
+static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
+ xkb_keysym_t *pressed_keysyms, uint32_t modifiers,
+ enum wlr_key_state key_state) {
+ // configured bindings
+ int n = pressed_keysyms_length(pressed_keysyms);
+ list_t *keysym_bindings = config->current_mode->keysym_bindings;
+ for (int i = 0; i < keysym_bindings->length; ++i) {
+ struct sway_binding *binding = keysym_bindings->items[i];
+ if (!binding_matches_key_state(binding, key_state) ||
+ modifiers ^ binding->modifiers ||
+ n != binding->keys->length) {
+ continue;
+ }
+
+ bool match = true;
+ for (int j = 0; j < binding->keys->length; ++j) {
+ match =
+ pressed_keysyms_index(pressed_keysyms,
+ *(int*)binding->keys->items[j]) >= 0;
+
+ if (!match) {
+ break;
+ }
+ }
+
+ if (match) {
+ keyboard_execute_command(keyboard, binding);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
+ struct sway_binding *binding, struct wlr_event_keyboard_key *event) {
+ assert(binding->bindcode);
+
+ uint32_t keycode = event->keycode + 8;
+
+ if (!binding_matches_key_state(binding, event->state)) {
+ return false;
+ }
+
+ uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
+ if (modifiers ^ binding->modifiers) {
+ return false;
+ }
+
+ // on release, the released key must be in the binding
+ if (event->state == WLR_KEY_RELEASED) {
+ bool found = false;
+ for (int i = 0; i < binding->keys->length; ++i) {
+ uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8;
+ if (binding_keycode == keycode) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+
+ // every keycode in the binding must be present in the pressed keys on the
+ // keyboard
+ for (int i = 0; i < binding->keys->length; ++i) {
+ uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8;
+ if (event->state == WLR_KEY_RELEASED && keycode == binding_keycode) {
+ continue;
+ }
+
+ bool found = false;
+ for (size_t j = 0; j < keyboard->num_keycodes; ++j) {
+ xkb_keycode_t keycode = keyboard->keycodes[j] + 8;
+ if (keycode == binding_keycode) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ return false;
+ }
+ }
+
+ // every keycode pressed on the keyboard must be present within the binding
+ // keys (unless it is a modifier)
+ for (size_t i = 0; i < keyboard->num_keycodes; ++i) {
+ xkb_keycode_t keycode = keyboard->keycodes[i] + 8;
+ bool found = false;
+ for (int j = 0; j < binding->keys->length; ++j) {
+ uint32_t binding_keycode = *(uint32_t*)binding->keys->items[j] + 8;
+ if (binding_keycode == keycode) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (!binding->modifiers) {
+ return false;
+ }
+
+ // check if it is a modifier, which we know matched from the check
+ // above
+ const xkb_keysym_t *keysyms;
+ int num_keysyms =
+ xkb_state_key_get_syms(keyboard->xkb_state,
+ keycode, &keysyms);
+ if (num_keysyms != 1 || !keysym_is_modifier(keysyms[0])) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Execute keyboard bindings bound with `bindcode` for the given keyboard state.
+ *
+ * Returns true if the keysym was handled by a binding and false if the event
+ * should be propagated to clients.
+ */
+static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard,
+ struct wlr_event_keyboard_key *event) {
+ struct wlr_keyboard *wlr_keyboard =
+ keyboard->seat_device->input_device->wlr_device->keyboard;
+ list_t *keycode_bindings = config->current_mode->keycode_bindings;
+ for (int i = 0; i < keycode_bindings->length; ++i) {
+ struct sway_binding *binding = keycode_bindings->items[i];
+ if (binding_matches_keycodes(wlr_keyboard, binding, event)) {
+ keyboard_execute_command(keyboard, binding);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Get keysyms and modifiers from the keyboard as xkb sees them.
+ *
+ * This uses the xkb keysyms translation based on pressed modifiers and clears
+ * the consumed modifiers from the list of modifiers passed to keybind
+ * detection.
+ *
+ * On US layout, pressing Alt+Shift+2 will trigger Alt+@.
+ */
+static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
+ xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
+ uint32_t *modifiers) {
+ struct wlr_input_device *device =
+ keyboard->seat_device->input_device->wlr_device;
+ *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
+ xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
+ device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
+ *modifiers = *modifiers & ~consumed;
+
+ return xkb_state_key_get_syms(device->keyboard->xkb_state,
+ keycode, keysyms);
+}
+
+/**
+ * Get keysyms and modifiers from the keyboard as if modifiers didn't change
+ * keysyms.
+ *
+ * This avoids the xkb keysym translation based on modifiers considered pressed
+ * in the state.
+ *
+ * This will trigger keybinds such as Alt+Shift+2.
+ */
+static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
+ xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
+ uint32_t *modifiers) {
+ struct wlr_input_device *device =
+ keyboard->seat_device->input_device->wlr_device;
+ *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
+
+ xkb_layout_index_t layout_index = xkb_state_key_get_layout(
+ device->keyboard->xkb_state, keycode);
+ return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap,
+ keycode, layout_index, 0, keysyms);
+}
+
+static void handle_keyboard_key(struct wl_listener *listener, void *data) {
+ struct sway_keyboard *keyboard =
+ wl_container_of(listener, keyboard, keyboard_key);
+ struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
+ struct wlr_input_device *wlr_device =
+ keyboard->seat_device->input_device->wlr_device;
+ struct wlr_event_keyboard_key *event = data;
+
+ xkb_keycode_t keycode = event->keycode + 8;
+ bool handled = false;
+
+ // handle keycodes
+ handled = keyboard_execute_bindcode(keyboard, event);
+
+ // handle translated keysyms
+ if (!handled && event->state == WLR_KEY_RELEASED) {
+ handled = keyboard_execute_bindsym(keyboard,
+ keyboard->pressed_keysyms_translated,
+ keyboard->modifiers_translated,
+ event->state);
+ }
+ const xkb_keysym_t *translated_keysyms;
+ size_t translated_keysyms_len =
+ keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms,
+ &keyboard->modifiers_translated);
+ pressed_keysyms_update(keyboard->pressed_keysyms_translated,
+ translated_keysyms, translated_keysyms_len, event->state);
+ if (!handled && event->state == WLR_KEY_PRESSED) {
+ handled = keyboard_execute_bindsym(keyboard,
+ keyboard->pressed_keysyms_translated,
+ keyboard->modifiers_translated,
+ event->state);
+ }
+
+ // Handle raw keysyms
+ if (!handled && event->state == WLR_KEY_RELEASED) {
+ handled = keyboard_execute_bindsym(keyboard,
+ keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
+ event->state);
+ }
+ const xkb_keysym_t *raw_keysyms;
+ size_t raw_keysyms_len =
+ keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &keyboard->modifiers_raw);
+ pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms,
+ raw_keysyms_len, event->state);
+ if (!handled && event->state == WLR_KEY_PRESSED) {
+ handled = keyboard_execute_bindsym(keyboard,
+ keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
+ event->state);
+ }
+
+ // Compositor bindings
+ if (!handled && event->state == WLR_KEY_PRESSED) {
+ handled =
+ keyboard_execute_compositor_binding(keyboard,
+ keyboard->pressed_keysyms_translated,
+ keyboard->modifiers_translated,
+ translated_keysyms_len);
+ }
+ if (!handled && event->state == WLR_KEY_PRESSED) {
+ handled =
+ keyboard_execute_compositor_binding(keyboard,
+ keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
+ raw_keysyms_len);
+ }
+
+ if (!handled || event->state == WLR_KEY_RELEASED) {
+ wlr_seat_set_keyboard(wlr_seat, wlr_device);
+ wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
+ event->keycode, event->state);
+ }
+}
+
+static void handle_keyboard_modifiers(struct wl_listener *listener,
+ void *data) {
+ struct sway_keyboard *keyboard =
+ wl_container_of(listener, keyboard, keyboard_modifiers);
+ struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
+ struct wlr_input_device *wlr_device =
+ keyboard->seat_device->input_device->wlr_device;
+ wlr_seat_set_keyboard(wlr_seat, wlr_device);
+ wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers);
+}
+
+struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
+ struct sway_seat_device *device) {
+ struct sway_keyboard *keyboard =
+ calloc(1, sizeof(struct sway_keyboard));
+ if (!sway_assert(keyboard, "could not allocate sway keyboard")) {
+ return NULL;
+ }
+
+ keyboard->seat_device = device;
+ device->keyboard = keyboard;
+
+ wl_list_init(&keyboard->keyboard_key.link);
+ wl_list_init(&keyboard->keyboard_modifiers.link);
+
+ return keyboard;
+}
+
+void sway_keyboard_configure(struct sway_keyboard *keyboard) {
+ struct xkb_rule_names rules;
+ memset(&rules, 0, sizeof(rules));
+ struct input_config *input_config =
+ input_device_get_config(keyboard->seat_device->input_device);
+ struct wlr_input_device *wlr_device =
+ keyboard->seat_device->input_device->wlr_device;
+
+ if (input_config && input_config->xkb_layout) {
+ rules.layout = input_config->xkb_layout;
+ } else {
+ rules.layout = getenv("XKB_DEFAULT_LAYOUT");
+ }
+ if (input_config && input_config->xkb_model) {
+ rules.model = input_config->xkb_model;
+ } else {
+ rules.model = getenv("XKB_DEFAULT_MODEL");
+ }
+
+ if (input_config && input_config->xkb_options) {
+ rules.options = input_config->xkb_options;
+ } else {
+ rules.options = getenv("XKB_DEFAULT_OPTIONS");
+ }
+
+ if (input_config && input_config->xkb_rules) {
+ rules.rules = input_config->xkb_rules;
+ } else {
+ rules.rules = getenv("XKB_DEFAULT_RULES");
+ }
+
+ if (input_config && input_config->xkb_variant) {
+ rules.variant = input_config->xkb_variant;
+ } else {
+ rules.variant = getenv("XKB_DEFAULT_VARIANT");
+ }
+
+ struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ if (!sway_assert(context, "cannot create XKB context")) {
+ return;
+ }
+
+ struct xkb_keymap *keymap =
+ xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+ if (!keymap) {
+ wlr_log(L_DEBUG, "cannot configure keyboard: keymap does not exist");
+ xkb_context_unref(context);
+ return;
+ }
+
+ xkb_keymap_unref(keyboard->keymap);
+ keyboard->keymap = keymap;
+ wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
+
+ wlr_keyboard_set_repeat_info(wlr_device->keyboard, 25, 600);
+ xkb_context_unref(context);
+ struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
+ wlr_seat_set_keyboard(seat, wlr_device);
+
+ wl_list_remove(&keyboard->keyboard_key.link);
+ wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key);
+ keyboard->keyboard_key.notify = handle_keyboard_key;
+
+ wl_list_remove(&keyboard->keyboard_modifiers.link);
+ wl_signal_add( &wlr_device->keyboard->events.modifiers,
+ &keyboard->keyboard_modifiers);
+ keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
+}
+
+void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
+ if (!keyboard) {
+ return;
+ }
+ wl_list_remove(&keyboard->keyboard_key.link);
+ wl_list_remove(&keyboard->keyboard_modifiers.link);
+ free(keyboard);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#define _POSIX_C_SOURCE 199309L
+#include <assert.h>
+#include <strings.h>
+#include <time.h>
+#include <wlr/types/wlr_cursor.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+#include "sway/debug.h"
+#include "sway/tree/container.h"
+#include "sway/tree/workspace.h"
+#include "sway/input/seat.h"
+#include "sway/input/cursor.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/keyboard.h"
+#include "sway/ipc-server.h"
+#include "sway/layers.h"
+#include "sway/output.h"
+#include "sway/tree/container.h"
+#include "sway/tree/view.h"
+#include "log.h"
+
+static void seat_device_destroy(struct sway_seat_device *seat_device) {
+ if (!seat_device) {
+ return;
+ }
+
+ sway_keyboard_destroy(seat_device->keyboard);
+ wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,
+ seat_device->input_device->wlr_device);
+ wl_list_remove(&seat_device->link);
+ free(seat_device);
+}
+
+void seat_destroy(struct sway_seat *seat) {
+ struct sway_seat_device *seat_device, *next;
+ wl_list_for_each_safe(seat_device, next, &seat->devices, link) {
+ seat_device_destroy(seat_device);
+ }
+ sway_cursor_destroy(seat->cursor);
+ wl_list_remove(&seat->link);
+ wlr_seat_destroy(seat->wlr_seat);
+}
+
+static struct sway_seat_container *seat_container_from_container(
+ struct sway_seat *seat, struct sway_container *con);
+
+static void seat_container_destroy(struct sway_seat_container *seat_con) {
+ struct sway_container *con = seat_con->container;
+ struct sway_container *child = NULL;
+
+ if (con->children != NULL) {
+ for (int i = 0; i < con->children->length; ++i) {
+ child = con->children->items[i];
+ struct sway_seat_container *seat_child =
+ seat_container_from_container(seat_con->seat, child);
+ seat_container_destroy(seat_child);
+ }
+ }
+
+ wl_list_remove(&seat_con->destroy.link);
+ wl_list_remove(&seat_con->link);
+ free(seat_con);
+}
+
+static void seat_send_focus(struct sway_seat *seat,
+ struct sway_container *con) {
+ if (con->type != C_VIEW) {
+ return;
+ }
+ struct sway_view *view = con->sway_view;
+ if (view->type == SWAY_VIEW_XWAYLAND) {
+ struct wlr_xwayland *xwayland =
+ seat->input->server->xwayland;
+ wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
+ }
+ view_set_activated(view, true);
+ struct wlr_keyboard *keyboard =
+ wlr_seat_get_keyboard(seat->wlr_seat);
+ if (keyboard) {
+ wlr_seat_keyboard_notify_enter(seat->wlr_seat,
+ view->surface, keyboard->keycodes,
+ keyboard->num_keycodes, &keyboard->modifiers);
+ } else {
+ wlr_seat_keyboard_notify_enter(
+ seat->wlr_seat, view->surface, NULL, 0, NULL);
+ }
+}
+
+static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat,
+ struct sway_container *container, enum sway_container_type type) {
+ if (container->type == C_VIEW || container->children->length == 0) {
+ return container;
+ }
+
+ struct sway_seat_container *current = NULL;
+ wl_list_for_each(current, &seat->focus_stack, link) {
+ if (current->container->type != type && type != C_TYPES) {
+ continue;
+ }
+
+ if (container_has_child(container, current->container)) {
+ return current->container;
+ }
+ }
+
+ return NULL;
+}
+
+void seat_focus_inactive_children_for_each(struct sway_seat *seat,
+ struct sway_container *container,
+ void (*f)(struct sway_container *container, void *data), void *data) {
+ struct sway_seat_container *current = NULL;
+ wl_list_for_each(current, &seat->focus_stack, link) {
+ if (current->container->parent == NULL) {
+ continue;
+ }
+ if (current->container->parent == container) {
+ f(current->container, data);
+ }
+ }
+}
+
+struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
+ struct sway_container *container) {
+ return seat_get_focus_by_type(seat, container, C_VIEW);
+}
+
+static void handle_seat_container_destroy(struct wl_listener *listener,
+ void *data) {
+ struct sway_seat_container *seat_con =
+ wl_container_of(listener, seat_con, destroy);
+ struct sway_seat *seat = seat_con->seat;
+ struct sway_container *con = seat_con->container;
+ struct sway_container *parent = con->parent;
+ struct sway_container *focus = seat_get_focus(seat);
+
+ bool set_focus =
+ focus != NULL &&
+ (focus == con || container_has_child(con, focus)) &&
+ con->type != C_WORKSPACE;
+
+ seat_container_destroy(seat_con);
+
+ if (set_focus) {
+ struct sway_container *next_focus = NULL;
+ while (next_focus == NULL) {
+ next_focus = seat_get_focus_by_type(seat, parent, C_VIEW);
+
+ if (next_focus == NULL && parent->type == C_WORKSPACE) {
+ next_focus = parent;
+ break;
+ }
+
+ parent = parent->parent;
+ }
+
+ // the structure change might have caused it to move up to the top of
+ // the focus stack without sending focus notifications to the view
+ if (seat_get_focus(seat) == next_focus) {
+ seat_send_focus(seat, next_focus);
+ } else {
+ seat_set_focus(seat, next_focus);
+ }
+ }
+}
+
+static struct sway_seat_container *seat_container_from_container(
+ struct sway_seat *seat, struct sway_container *con) {
+ if (con->type == C_ROOT || con->type == C_OUTPUT) {
+ // these don't get seat containers ever
+ return NULL;
+ }
+
+ struct sway_seat_container *seat_con = NULL;
+ wl_list_for_each(seat_con, &seat->focus_stack, link) {
+ if (seat_con->container == con) {
+ return seat_con;
+ }
+ }
+
+ seat_con = calloc(1, sizeof(struct sway_seat_container));
+ if (seat_con == NULL) {
+ wlr_log(L_ERROR, "could not allocate seat container");
+ return NULL;
+ }
+
+ seat_con->container = con;
+ seat_con->seat = seat;
+ wl_list_insert(seat->focus_stack.prev, &seat_con->link);
+ wl_signal_add(&con->events.destroy, &seat_con->destroy);
+ seat_con->destroy.notify = handle_seat_container_destroy;
+
+ return seat_con;
+}
+
+static void handle_new_container(struct wl_listener *listener, void *data) {
+ struct sway_seat *seat = wl_container_of(listener, seat, new_container);
+ struct sway_container *con = data;
+ seat_container_from_container(seat, con);
+}
+
+static void collect_focus_iter(struct sway_container *con, void *data) {
+ struct sway_seat *seat = data;
+ if (con->type > C_WORKSPACE) {
+ return;
+ }
+ struct sway_seat_container *seat_con =
+ seat_container_from_container(seat, con);
+ if (!seat_con) {
+ return;
+ }
+ wl_list_remove(&seat_con->link);
+ wl_list_insert(&seat->focus_stack, &seat_con->link);
+}
+
+struct sway_seat *seat_create(struct sway_input_manager *input,
+ const char *seat_name) {
+ struct sway_seat *seat = calloc(1, sizeof(struct sway_seat));
+ if (!seat) {
+ return NULL;
+ }
+
+ seat->wlr_seat = wlr_seat_create(input->server->wl_display, seat_name);
+ if (!sway_assert(seat->wlr_seat, "could not allocate seat")) {
+ free(seat);
+ return NULL;
+ }
+
+ seat->cursor = sway_cursor_create(seat);
+ if (!seat->cursor) {
+ wlr_seat_destroy(seat->wlr_seat);
+ free(seat);
+ return NULL;
+ }
+
+ // init the focus stack
+ wl_list_init(&seat->focus_stack);
+
+ container_for_each_descendant_dfs(&root_container,
+ collect_focus_iter, seat);
+
+ wl_signal_add(&root_container.sway_root->events.new_container,
+ &seat->new_container);
+ seat->new_container.notify = handle_new_container;
+
+ seat->input = input;
+ wl_list_init(&seat->devices);
+
+ wlr_seat_set_capabilities(seat->wlr_seat,
+ WL_SEAT_CAPABILITY_KEYBOARD |
+ WL_SEAT_CAPABILITY_POINTER |
+ WL_SEAT_CAPABILITY_TOUCH);
+
+ seat_configure_xcursor(seat);
+
+ wl_list_insert(&input->seats, &seat->link);
+
+ return seat;
+}
+
+static void seat_apply_input_config(struct sway_seat *seat,
+ struct sway_seat_device *sway_device) {
+ struct input_config *ic = input_device_get_config(
+ sway_device->input_device);
+ if (!ic) {
+ return;
+ }
+ wlr_log(L_DEBUG, "Applying input config to %s",
+ sway_device->input_device->identifier);
+ if (ic->mapped_output) {
+ struct sway_container *output = NULL;
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *_output = root_container.children->items[i];
+ if (strcasecmp(_output->name, ic->mapped_output) == 0) {
+ output = _output;
+ break;
+ }
+ }
+ if (output) {
+ wlr_cursor_map_input_to_output(seat->cursor->cursor,
+ sway_device->input_device->wlr_device,
+ output->sway_output->wlr_output);
+ wlr_log(L_DEBUG, "Mapped to output %s", output->name);
+ }
+ }
+}
+
+static void seat_configure_pointer(struct sway_seat *seat,
+ struct sway_seat_device *sway_device) {
+ wlr_cursor_attach_input_device(seat->cursor->cursor,
+ sway_device->input_device->wlr_device);
+ seat_apply_input_config(seat, sway_device);
+}
+
+static void seat_configure_keyboard(struct sway_seat *seat,
+ struct sway_seat_device *seat_device) {
+ if (!seat_device->keyboard) {
+ sway_keyboard_create(seat, seat_device);
+ }
+ struct wlr_keyboard *wlr_keyboard =
+ seat_device->input_device->wlr_device->keyboard;
+ sway_keyboard_configure(seat_device->keyboard);
+ wlr_seat_set_keyboard(seat->wlr_seat,
+ seat_device->input_device->wlr_device);
+ struct sway_container *focus = seat_get_focus(seat);
+ if (focus && focus->type == C_VIEW) {
+ // force notify reenter to pick up the new configuration
+ wlr_seat_keyboard_clear_focus(seat->wlr_seat);
+ wlr_seat_keyboard_notify_enter(seat->wlr_seat,
+ focus->sway_view->surface, wlr_keyboard->keycodes,
+ wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers);
+ }
+}
+
+static void seat_configure_tablet_tool(struct sway_seat *seat,
+ struct sway_seat_device *sway_device) {
+ wlr_cursor_attach_input_device(seat->cursor->cursor,
+ sway_device->input_device->wlr_device);
+ seat_apply_input_config(seat, sway_device);
+}
+
+static struct sway_seat_device *seat_get_device(struct sway_seat *seat,
+ struct sway_input_device *input_device) {
+ struct sway_seat_device *seat_device = NULL;
+ wl_list_for_each(seat_device, &seat->devices, link) {
+ if (seat_device->input_device == input_device) {
+ return seat_device;
+ }
+ }
+
+ return NULL;
+}
+
+void seat_configure_device(struct sway_seat *seat,
+ struct sway_input_device *input_device) {
+ struct sway_seat_device *seat_device =
+ seat_get_device(seat, input_device);
+ if (!seat_device) {
+ return;
+ }
+
+ switch (input_device->wlr_device->type) {
+ case WLR_INPUT_DEVICE_POINTER:
+ seat_configure_pointer(seat, seat_device);
+ break;
+ case WLR_INPUT_DEVICE_KEYBOARD:
+ seat_configure_keyboard(seat, seat_device);
+ break;
+ case WLR_INPUT_DEVICE_TABLET_TOOL:
+ seat_configure_tablet_tool(seat, seat_device);
+ break;
+ case WLR_INPUT_DEVICE_TABLET_PAD:
+ case WLR_INPUT_DEVICE_TOUCH:
+ wlr_log(L_DEBUG, "TODO: configure other devices");
+ break;
+ }
+}
+
+void seat_add_device(struct sway_seat *seat,
+ struct sway_input_device *input_device) {
+ if (seat_get_device(seat, input_device)) {
+ seat_configure_device(seat, input_device);
+ return;
+ }
+
+ struct sway_seat_device *seat_device =
+ calloc(1, sizeof(struct sway_seat_device));
+ if (!seat_device) {
+ wlr_log(L_DEBUG, "could not allocate seat device");
+ return;
+ }
+
+ wlr_log(L_DEBUG, "adding device %s to seat %s",
+ input_device->identifier, seat->wlr_seat->name);
+
+ seat_device->sway_seat = seat;
+ seat_device->input_device = input_device;
+ wl_list_insert(&seat->devices, &seat_device->link);
+
+ seat_configure_device(seat, input_device);
+}
+
+void seat_remove_device(struct sway_seat *seat,
+ struct sway_input_device *input_device) {
+ struct sway_seat_device *seat_device =
+ seat_get_device(seat, input_device);
+
+ if (!seat_device) {
+ return;
+ }
+
+ wlr_log(L_DEBUG, "removing device %s from seat %s",
+ input_device->identifier, seat->wlr_seat->name);
+
+ seat_device_destroy(seat_device);
+}
+
+void seat_configure_xcursor(struct sway_seat *seat) {
+ // TODO configure theme and size
+ const char *cursor_theme = NULL;
+
+ if (!seat->cursor->xcursor_manager) {
+ seat->cursor->xcursor_manager =
+ wlr_xcursor_manager_create(cursor_theme, 24);
+ if (sway_assert(seat->cursor->xcursor_manager,
+ "Cannot create XCursor manager for theme %s",
+ cursor_theme)) {
+ return;
+ }
+ }
+
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *output_container =
+ root_container.children->items[i];
+ struct wlr_output *output =
+ output_container->sway_output->wlr_output;
+ bool result =
+ wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
+ output->scale);
+
+ sway_assert(!result,
+ "Cannot load xcursor theme for output '%s' with scale %f",
+ // TODO: Fractional scaling
+ output->name, (double)output->scale);
+ }
+
+ wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager,
+ "left_ptr", seat->cursor->cursor);
+ wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
+ seat->cursor->cursor->y);
+}
+
+bool seat_is_input_allowed(struct sway_seat *seat,
+ struct wlr_surface *surface) {
+ struct wl_client *client = wl_resource_get_client(surface->resource);
+ return !seat->exclusive_client || seat->exclusive_client == client;
+}
+
+void seat_set_focus_warp(struct sway_seat *seat,
+ struct sway_container *container, bool warp) {
+ if (seat->focused_layer) {
+ return;
+ }
+
+ struct sway_container *last_focus = seat_get_focus(seat);
+ if (container && last_focus == container) {
+ return;
+ }
+
+ if (container) {
+ struct sway_seat_container *seat_con =
+ seat_container_from_container(seat, container);
+ if (seat_con == NULL) {
+ return;
+ }
+
+ // put all the anscestors of this container on top of the focus stack
+ struct sway_seat_container *parent =
+ seat_container_from_container(seat,
+ seat_con->container->parent);
+ while (parent) {
+ wl_list_remove(&parent->link);
+ wl_list_insert(&seat->focus_stack, &parent->link);
+
+ parent =
+ seat_container_from_container(seat,
+ parent->container->parent);
+ }
+
+ wl_list_remove(&seat_con->link);
+ wl_list_insert(&seat->focus_stack, &seat_con->link);
+
+ if (container->type == C_VIEW && !seat_is_input_allowed(
+ seat, container->sway_view->surface)) {
+ wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited");
+ return;
+ }
+
+ if (container->type == C_VIEW) {
+ seat_send_focus(seat, container);
+ }
+ }
+
+ if (last_focus) {
+ struct sway_container *last_ws = last_focus;
+ if (last_ws && last_ws->type != C_WORKSPACE) {
+ last_ws = container_parent(last_ws, C_WORKSPACE);
+ }
+ if (last_ws) {
+ ipc_event_workspace(last_ws, container, "focus");
+ if (!workspace_is_visible(last_ws)
+ && last_ws->children->length == 0) {
+ container_destroy(last_ws);
+ }
+ }
+
+ if (config->mouse_warping && warp) {
+ struct sway_container *last_output = last_focus;
+ if (last_output && last_output->type != C_OUTPUT) {
+ last_output = container_parent(last_output, C_OUTPUT);
+ }
+ struct sway_container *new_output = container;
+ if (new_output && new_output->type != C_OUTPUT) {
+ new_output = container_parent(new_output, C_OUTPUT);
+ }
+ if (new_output && last_output && new_output != last_output) {
+ double x = new_output->x + container->x +
+ container->width / 2.0;
+ double y = new_output->y + container->y +
+ container->height / 2.0;
+ struct wlr_output *wlr_output =
+ new_output->sway_output->wlr_output;
+ if (!wlr_output_layout_contains_point(
+ root_container.sway_root->output_layout,
+ wlr_output, seat->cursor->cursor->x,
+ seat->cursor->cursor->y)) {
+ wlr_cursor_warp(seat->cursor->cursor, NULL, x, y);
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ cursor_send_pointer_motion(seat->cursor, now.tv_nsec / 1000);
+ }
+ }
+ }
+ }
+
+ if (last_focus && last_focus->type == C_VIEW &&
+ !input_manager_has_focus(seat->input, last_focus)) {
+ struct sway_view *view = last_focus->sway_view;
+ view_set_activated(view, false);
+ }
+
+ seat->has_focus = (container != NULL);
+
+ update_debug_tree();
+}
+
+void seat_set_focus(struct sway_seat *seat,
+ struct sway_container *container) {
+ seat_set_focus_warp(seat, container, true);
+}
+
+void seat_set_focus_surface(struct sway_seat *seat,
+ struct wlr_surface *surface) {
+ if (seat->focused_layer != NULL) {
+ return;
+ }
+ if (seat->has_focus) {
+ struct sway_container *focus = seat_get_focus(seat);
+ if (focus->type == C_VIEW) {
+ wlr_seat_keyboard_clear_focus(seat->wlr_seat);
+ view_set_activated(focus->sway_view, false);
+ }
+ seat->has_focus = false;
+ }
+ struct wlr_keyboard *keyboard =
+ wlr_seat_get_keyboard(seat->wlr_seat);
+ if (keyboard) {
+ wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface,
+ keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
+ } else {
+ wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, NULL, 0, NULL);
+ }
+}
+
+void seat_set_focus_layer(struct sway_seat *seat,
+ struct wlr_layer_surface *layer) {
+ if (!layer && seat->focused_layer) {
+ seat->focused_layer = NULL;
+ struct sway_container *previous = seat_get_focus(seat);
+ if (previous) {
+ wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous,
+ container_type_to_str(previous->type), previous->name);
+ // Hack to get seat to re-focus the return value of get_focus
+ seat_set_focus(seat, previous->parent);
+ seat_set_focus(seat, previous);
+ }
+ return;
+ } else if (!layer || seat->focused_layer == layer) {
+ return;
+ }
+ seat_set_focus_surface(seat, layer->surface);
+ if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
+ seat->focused_layer = layer;
+ }
+}
+
+void seat_set_exclusive_client(struct sway_seat *seat,
+ struct wl_client *client) {
+ if (!client) {
+ seat->exclusive_client = client;
+ // Triggers a refocus of the topmost surface layer if necessary
+ // TODO: Make layer surface focus per-output based on cursor position
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *output = root_container.children->items[i];
+ if (!sway_assert(output->type == C_OUTPUT,
+ "root container has non-output child")) {
+ continue;
+ }
+ arrange_layers(output->sway_output);
+ }
+ return;
+ }
+ if (seat->focused_layer) {
+ if (wl_resource_get_client(seat->focused_layer->resource) != client) {
+ seat_set_focus_layer(seat, NULL);
+ }
+ }
+ if (seat->has_focus) {
+ struct sway_container *focus = seat_get_focus(seat);
+ if (focus->type == C_VIEW && wl_resource_get_client(
+ focus->sway_view->surface->resource) != client) {
+ seat_set_focus(seat, NULL);
+ }
+ }
+ if (seat->wlr_seat->pointer_state.focused_client) {
+ if (seat->wlr_seat->pointer_state.focused_client->client != client) {
+ wlr_seat_pointer_clear_focus(seat->wlr_seat);
+ }
+ }
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ struct wlr_touch_point *point;
+ wl_list_for_each(point, &seat->wlr_seat->touch_state.touch_points, link) {
+ if (point->client->client != client) {
+ wlr_seat_touch_point_clear_focus(seat->wlr_seat,
+ now.tv_nsec / 1000, point->touch_id);
+ }
+ }
+ seat->exclusive_client = client;
+}
+
+struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
+ struct sway_container *container) {
+ return seat_get_focus_by_type(seat, container, C_TYPES);
+}
+
+struct sway_container *sway_seat_get_focus(struct sway_seat *seat) {
+ if (!seat->has_focus) {
+ return NULL;
+ }
+ return seat_get_focus_inactive(seat, &root_container);
+}
+
+struct sway_container *seat_get_focus(struct sway_seat *seat) {
+ if (!seat->has_focus) {
+ return NULL;
+ }
+ return seat_get_focus_inactive(seat, &root_container);
+}
+
+void seat_apply_config(struct sway_seat *seat,
+ struct seat_config *seat_config) {
+ struct sway_seat_device *seat_device = NULL;
+
+ if (!seat_config) {
+ return;
+ }
+
+ wl_list_for_each(seat_device, &seat->devices, link) {
+ seat_configure_device(seat, seat_device->input_device);
+ }
+}
+
+struct seat_config *seat_get_config(struct sway_seat *seat) {
+ struct seat_config *seat_config = NULL;
+ for (int i = 0; i < config->seat_configs->length; ++i ) {
+ seat_config = config->seat_configs->items[i];
+ if (strcmp(seat->wlr_seat->name, seat_config->name) == 0) {
+ return seat_config;
+ }
+ }
+
+ return NULL;
+}
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 @@
-#include <string.h>
-#include <stdbool.h>
-#include <ctype.h>
-#include "sway/input_state.h"
-#include "sway/config.h"
-#include "log.h"
-
-#define KEY_STATE_MAX_LENGTH 64
-
-struct key_state {
- /*
- * Aims to store state regardless of modifiers.
- * If you press a key, then hold shift, then release the key, we'll
- * get two different key syms, but the same key code. This handles
- * that scenario and makes sure we can use the right bindings.
- */
- uint32_t key_sym;
- uint32_t alt_sym;
- uint32_t key_code;
-};
-
-static struct key_state key_state_array[KEY_STATE_MAX_LENGTH];
-
-static struct key_state last_released;
-
-static uint32_t modifiers_state;
-
-void input_init(void) {
- int i;
- for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) {
- struct key_state none = { 0, 0, 0 };
- key_state_array[i] = none;
- }
-
- struct key_state none = { 0, 0, 0 };
- last_released = none;
-
- modifiers_state = 0;
-}
-
-uint32_t modifier_state_changed(uint32_t new_state, uint32_t mod) {
- if ((new_state & mod) != 0) { // pressed
- if ((modifiers_state & mod) != 0) { // already pressed
- return MOD_STATE_UNCHANGED;
- } else { // pressed
- return MOD_STATE_PRESSED;
- }
- } else { // not pressed
- if ((modifiers_state & mod) != 0) { // released
- return MOD_STATE_RELEASED;
- } else { // already released
- return MOD_STATE_UNCHANGED;
- }
- }
-}
-
-void modifiers_state_update(uint32_t new_state) {
- modifiers_state = new_state;
-}
-
-static uint8_t find_key(uint32_t key_sym, uint32_t key_code, bool update) {
- int i;
- for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) {
- if (0 == key_sym && 0 == key_code && key_state_array[i].key_sym == 0) {
- break;
- }
- if (key_sym != 0 && (key_state_array[i].key_sym == key_sym
- || key_state_array[i].alt_sym == key_sym)) {
- break;
- }
- if (update && key_state_array[i].key_code == key_code) {
- key_state_array[i].alt_sym = key_sym;
- break;
- }
- if (key_sym == 0 && key_code != 0 && key_state_array[i].key_code == key_code) {
- break;
- }
- }
- return i;
-}
-
-bool check_key(uint32_t key_sym, uint32_t key_code) {
- return find_key(key_sym, key_code, false) < KEY_STATE_MAX_LENGTH;
-}
-
-bool check_released_key(uint32_t key_sym) {
- return (key_sym != 0
- && (last_released.key_sym == key_sym
- || last_released.alt_sym == key_sym));
-}
-
-void press_key(uint32_t key_sym, uint32_t key_code) {
- if (key_code == 0) {
- return;
- }
- // Check if key exists
- if (!check_key(key_sym, key_code)) {
- // Check that we don't exceed buffer length
- int insert = find_key(0, 0, true);
- if (insert < KEY_STATE_MAX_LENGTH) {
- key_state_array[insert].key_sym = key_sym;
- key_state_array[insert].key_code = key_code;
- }
- }
-}
-
-void release_key(uint32_t key_sym, uint32_t key_code) {
- uint8_t index = find_key(key_sym, key_code, true);
- if (index < KEY_STATE_MAX_LENGTH) {
- last_released.key_sym = key_state_array[index].key_sym;
- last_released.alt_sym = key_state_array[index].alt_sym;
- last_released.key_code = key_state_array[index].key_code;
- struct key_state none = { 0, 0, 0 };
- key_state_array[index] = none;
- }
-}
-
-// Pointer state and mode
-
-struct pointer_state pointer_state;
-
-static struct mode_state {
- // initial view state
- double x, y, w, h;
- swayc_t *ptr;
- // Containers used for resizing horizontally
- struct {
- double w;
- swayc_t *ptr;
- struct {
- double w;
- swayc_t *ptr;
- } parent;
- } horiz;
- // Containers used for resizing vertically
- struct {
- double h;
- swayc_t *ptr;
- struct {
- double h;
- swayc_t *ptr;
- } parent;
- } vert;
-} initial;
-
-static struct {
- bool left;
- bool top;
-} lock;
-
-// initial set/unset
-
-static void set_initial_view(swayc_t *view) {
- initial.ptr = view;
- initial.x = view->x;
- initial.y = view->y;
- initial.w = view->width;
- initial.h = view->height;
-}
-
-static void set_initial_sibling(void) {
- bool reset = true;
- swayc_t *ws = swayc_active_workspace_for(initial.ptr);
- if ((initial.horiz.ptr = get_swayc_in_direction_under(initial.ptr,
- lock.left ? MOVE_RIGHT: MOVE_LEFT, ws))) {
- initial.horiz.w = initial.horiz.ptr->width;
- initial.horiz.parent.ptr = get_swayc_in_direction_under(initial.horiz.ptr,
- lock.left ? MOVE_LEFT : MOVE_RIGHT, ws);
- initial.horiz.parent.w = initial.horiz.parent.ptr->width;
- reset = false;
- }
- if ((initial.vert.ptr = get_swayc_in_direction_under(initial.ptr,
- lock.top ? MOVE_DOWN: MOVE_UP, ws))) {
- initial.vert.h = initial.vert.ptr->height;
- initial.vert.parent.ptr = get_swayc_in_direction_under(initial.vert.ptr,
- lock.top ? MOVE_UP : MOVE_DOWN, ws);
- initial.vert.parent.h = initial.vert.parent.ptr->height;
- reset = false;
- }
- // If nothing will change just undo the mode
- if (reset) {
- pointer_state.mode = 0;
- }
-}
-
-static void reset_initial_view(void) {
- initial.ptr->x = initial.x;
- initial.ptr->y = initial.y;
- initial.ptr->width = initial.w;
- initial.ptr->height = initial.h;
- arrange_windows(initial.ptr, -1, -1);
- pointer_state.mode = 0;
-}
-
-static void reset_initial_sibling(void) {
- initial.horiz.ptr->width = initial.horiz.w;
- initial.horiz.parent.ptr->width = initial.horiz.parent.w;
- initial.vert.ptr->height = initial.vert.h;
- initial.vert.parent.ptr->height = initial.vert.parent.h;
- arrange_windows(initial.horiz.ptr->parent, -1, -1);
- arrange_windows(initial.vert.ptr->parent, -1, -1);
- pointer_state.mode = 0;
-}
-
-void pointer_position_set(double new_x, double new_y, bool force_focus) {
- double x, y;
- wlc_pointer_get_position_v2(&x, &y);
- pointer_state.delta.x = new_x - x;
- pointer_state.delta.y = new_y - y;
-
- wlc_pointer_set_position_v2(new_x, new_y);
-
- // Update view under pointer
- swayc_t *prev_view = pointer_state.view;
- pointer_state.view = container_under_pointer();
-
- // If pointer is in a mode, update it
- if (pointer_state.mode) {
- pointer_mode_update();
- // Otherwise change focus if config is set
- } else if (force_focus || (prev_view != pointer_state.view && config->focus_follows_mouse)) {
- if (pointer_state.view && pointer_state.view->type == C_VIEW) {
- set_focused_container(pointer_state.view);
- }
- }
-}
-
-void center_pointer_on(swayc_t *view) {
- pointer_position_set(view->x + view->width/2, view->y + view->height/2, true);
-}
-
-// Mode set left/right click
-
-static void pointer_mode_set_dragging(void) {
- switch (config->dragging_key) {
- case M_LEFT_CLICK:
- set_initial_view(pointer_state.left.view);
- break;
- case M_RIGHT_CLICK:
- set_initial_view(pointer_state.right.view);
- break;
- }
-
- if (initial.ptr->is_floating) {
- pointer_state.mode = M_DRAGGING | M_FLOATING;
- } else {
- pointer_state.mode = M_DRAGGING | M_TILING;
- // unset mode if we can't drag tile
- if (initial.ptr->parent->type == C_WORKSPACE &&
- initial.ptr->parent->children->length == 1) {
- pointer_state.mode = 0;
- }
- }
-}
-
-static void pointer_mode_set_resizing(void) {
- switch (config->resizing_key) {
- case M_LEFT_CLICK:
- set_initial_view(pointer_state.left.view);
- break;
- case M_RIGHT_CLICK:
- set_initial_view(pointer_state.right.view);
- break;
- }
- // Setup locking information
- int midway_x = initial.ptr->x + initial.ptr->width/2;
- int midway_y = initial.ptr->y + initial.ptr->height/2;
-
- double x, y;
- wlc_pointer_get_position_v2(&x, &y);
- lock.left = x > midway_x;
- lock.top = y > midway_y;
-
- if (initial.ptr->is_floating) {
- pointer_state.mode = M_RESIZING | M_FLOATING;
- } else {
- pointer_state.mode = M_RESIZING | M_TILING;
- set_initial_sibling();
- }
-}
-
-// Mode set/update/reset
-
-void pointer_mode_set(uint32_t button, bool condition) {
- // switch on drag/resize mode
- switch (pointer_state.mode & (M_DRAGGING | M_RESIZING)) {
- case M_DRAGGING:
- // end drag mode when 'dragging' click is unpressed
- if (config->dragging_key == M_LEFT_CLICK && !pointer_state.left.held) {
- pointer_state.mode = 0;
- } else if (config->dragging_key == M_RIGHT_CLICK && !pointer_state.right.held) {
- pointer_state.mode = 0;
- }
- break;
-
- case M_RESIZING:
- // end resize mode when 'resizing' click is unpressed
- if (config->resizing_key == M_LEFT_CLICK && !pointer_state.left.held) {
- pointer_state.mode = 0;
- } else if (config->resizing_key == M_RIGHT_CLICK && !pointer_state.right.held) {
- pointer_state.mode = 0;
- }
- break;
-
- // No mode case
- default:
- // return if failed condition, or no view
- if (!condition || !pointer_state.view) {
- break;
- }
-
- // Set mode depending on current button press
- switch (button) {
- // Start left-click mode
- case M_LEFT_CLICK:
- // if button release don't do anything
- if (pointer_state.left.held) {
- if (config->dragging_key == M_LEFT_CLICK) {
- pointer_mode_set_dragging();
- } else if (config->resizing_key == M_LEFT_CLICK) {
- pointer_mode_set_resizing();
- }
- }
- break;
-
- // Start right-click mode
- case M_RIGHT_CLICK:
- // if button release don't do anyhting
- if (pointer_state.right.held) {
- if (config->dragging_key == M_RIGHT_CLICK) {
- pointer_mode_set_dragging();
- } else if (config->resizing_key == M_RIGHT_CLICK) {
- pointer_mode_set_resizing();
- }
- }
- break;
- }
- }
-}
-
-void pointer_mode_update(void) {
- if (initial.ptr->type != C_VIEW) {
- pointer_state.mode = 0;
- return;
- }
- double x, y;
- wlc_pointer_get_position_v2(&x, &y);
- int dx = x;
- int dy = y;
-
- switch (pointer_state.mode) {
- case M_FLOATING | M_DRAGGING:
- // Update position
- switch (config->dragging_key) {
- case M_LEFT_CLICK:
- dx -= pointer_state.left.x;
- dy -= pointer_state.left.y;
- break;
- case M_RIGHT_CLICK:
- dx -= pointer_state.right.x;
- dy -= pointer_state.right.y;
- break;
- }
-
- if (initial.x + dx != initial.ptr->x) {
- initial.ptr->x = initial.x + dx;
- }
- if (initial.y + dy != initial.ptr->y) {
- initial.ptr->y = initial.y + dy;
- }
- update_geometry(initial.ptr);
- break;
-
- case M_FLOATING | M_RESIZING:
- switch (config->resizing_key) {
- case M_LEFT_CLICK:
- dx -= pointer_state.left.x;
- dy -= pointer_state.left.y;
- initial.ptr = pointer_state.left.view;
- break;
- case M_RIGHT_CLICK:
- dx -= pointer_state.right.x;
- dy -= pointer_state.right.y;
- initial.ptr = pointer_state.right.view;
- break;
- }
-
- if (lock.left) {
- if (initial.w + dx > min_sane_w) {
- initial.ptr->width = initial.w + dx;
- }
- } else { // lock.right
- if (initial.w - dx > min_sane_w) {
- initial.ptr->width = initial.w - dx;
- initial.ptr->x = initial.x + dx;
- }
- }
- if (lock.top) {
- if (initial.h + dy > min_sane_h) {
- initial.ptr->height = initial.h + dy;
- }
- } else { // lock.bottom
- if (initial.h - dy > min_sane_h) {
- initial.ptr->height = initial.h - dy;
- initial.ptr->y = initial.y + dy;
- }
- }
- update_geometry(initial.ptr);
- break;
-
- case M_TILING | M_DRAGGING:
- // swap current view under pointer with dragged view
- if (pointer_state.view && pointer_state.view->type == C_VIEW
- && pointer_state.view != initial.ptr
- && !pointer_state.view->is_floating) {
- // Swap them around
- swap_container(pointer_state.view, initial.ptr);
- swap_geometry(pointer_state.view, initial.ptr);
- update_geometry(pointer_state.view);
- update_geometry(initial.ptr);
- // Set focus back to initial view
- set_focused_container(initial.ptr);
- // Arrange the windows
- arrange_windows(&root_container, -1, -1);
- }
- break;
-
- case M_TILING | M_RESIZING:
- switch (config->resizing_key) {
- case M_LEFT_CLICK:
- dx -= pointer_state.left.x;
- dy -= pointer_state.left.y;
- break;
- case M_RIGHT_CLICK:
- dx -= pointer_state.right.x;
- dy -= pointer_state.right.y;
- break;
- }
-
- // resize if we can
- if (initial.horiz.ptr) {
- if (lock.left) {
- // Check whether its fine to resize
- if (initial.w + dx > min_sane_w && initial.horiz.w - dx > min_sane_w) {
- initial.horiz.ptr->width = initial.horiz.w - dx;
- initial.horiz.parent.ptr->width = initial.horiz.parent.w + dx;
- }
- } else { // lock.right
- if (initial.w - dx > min_sane_w && initial.horiz.w + dx > min_sane_w) {
- initial.horiz.ptr->width = initial.horiz.w + dx;
- initial.horiz.parent.ptr->width = initial.horiz.parent.w - dx;
- }
- }
- arrange_windows(initial.horiz.ptr->parent, -1, -1);
- }
- if (initial.vert.ptr) {
- if (lock.top) {
- if (initial.h + dy > min_sane_h && initial.vert.h - dy > min_sane_h) {
- initial.vert.ptr->height = initial.vert.h - dy;
- initial.vert.parent.ptr->height = initial.vert.parent.h + dy;
- }
- } else { // lock.bottom
- if (initial.h - dy > min_sane_h && initial.vert.h + dy > min_sane_h) {
- initial.vert.ptr->height = initial.vert.h + dy;
- initial.vert.parent.ptr->height = initial.vert.parent.h - dy;
- }
- }
- arrange_windows(initial.vert.ptr->parent, -1, -1);
- }
- default:
- return;
- }
-}
-
-void pointer_mode_reset(void) {
- switch (pointer_state.mode) {
- case M_FLOATING | M_RESIZING:
- case M_FLOATING | M_DRAGGING:
- reset_initial_view();
- break;
-
- case M_TILING | M_RESIZING:
- (void) reset_initial_sibling;
- break;
-
- case M_TILING | M_DRAGGING:
- default:
- break;
- }
-}
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 @@
#include <json-c/json.h>
+#include <stdio.h>
#include <ctype.h>
-#include <string.h>
-#include <stdint.h>
-#include <libinput.h>
-#include "sway/container.h"
-#include "sway/input.h"
+#include "log.h"
#include "sway/ipc-json.h"
-#include "util.h"
-
-static json_object *ipc_json_create_rect(swayc_t *c) {
- json_object *rect = json_object_new_object();
+#include "sway/tree/container.h"
+#include "sway/output.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_output.h>
+#include "wlr-layer-shell-unstable-v1-protocol.h"
+
+static const char *ipc_json_layout_description(enum sway_container_layout l) {
+ switch (l) {
+ case L_VERT:
+ return "splitv";
+ case L_HORIZ:
+ return "splith";
+ case L_TABBED:
+ return "tabbed";
+ case L_STACKED:
+ return "stacked";
+ case L_FLOATING:
+ return "floating";
+ case L_NONE:
+ break;
+ }
+ return "none";
+}
- json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x));
- json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y));
+json_object *ipc_json_get_version() {
+ int major = 0, minor = 0, patch = 0;
+ json_object *version = json_object_new_object();
- struct wlc_size size;
- if (c->type == C_OUTPUT) {
- size = *wlc_output_get_resolution(c->handle);
- } else {
- size.w = c->width;
- size.h = c->height;
- }
+ sscanf(SWAY_VERSION, "%u.%u.%u", &major, &minor, &patch);
- json_object_object_add(rect, "width", json_object_new_int((int32_t)size.w));
- json_object_object_add(rect, "height", json_object_new_int((int32_t)size.h));
+ json_object_object_add(version, "human_readable", json_object_new_string(SWAY_VERSION));
+ json_object_object_add(version, "variant", json_object_new_string("sway"));
+ json_object_object_add(version, "major", json_object_new_int(major));
+ json_object_object_add(version, "minor", json_object_new_int(minor));
+ json_object_object_add(version, "patch", json_object_new_int(patch));
- return rect;
+ return version;
}
-static json_object *ipc_json_create_rect_from_geometry(struct wlc_geometry g) {
+static json_object *ipc_json_create_rect(struct sway_container *c) {
json_object *rect = json_object_new_object();
- json_object_object_add(rect, "x", json_object_new_int(g.origin.x));
- json_object_object_add(rect, "y", json_object_new_int(g.origin.y));
- json_object_object_add(rect, "width", json_object_new_int(g.size.w));
- json_object_object_add(rect, "height", json_object_new_int(g.size.h));
+ json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x));
+ json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y));
+ json_object_object_add(rect, "width", json_object_new_int((int32_t)c->width));
+ json_object_object_add(rect, "height", json_object_new_int((int32_t)c->height));
return rect;
}
-static const char *ipc_json_border_description(swayc_t *c) {
- const char *border;
-
- switch (c->border_type) {
- case B_PIXEL:
- border = "1pixel";
- break;
-
- case B_NORMAL:
- border = "normal";
- break;
-
- case B_NONE: // fallthrough
- default:
- border = "none";
- break;
- }
-
- return border;
+static void ipc_json_describe_root(struct sway_container *root, json_object *object) {
+ json_object_object_add(object, "type", json_object_new_string("root"));
+ json_object_object_add(object, "layout", json_object_new_string("splith"));
}
-static const char *ipc_json_layout_description(enum swayc_layouts l) {
- const char *layout;
-
- switch (l) {
- case L_VERT:
- layout = "splitv";
- break;
-
- case L_HORIZ:
- layout = "splith";
- break;
-
- case L_TABBED:
- layout = "tabbed";
- break;
-
- case L_STACKED:
- layout = "stacked";
- break;
-
- case L_FLOATING:
- layout = "floating";
- break;
-
- case L_NONE: // fallthrough
- case L_LAYOUTS: // fallthrough; this should never happen, I'm just trying to silence compiler warnings
- default:
- layout = "null";
- break;
+static const char *ipc_json_get_output_transform(enum wl_output_transform transform) {
+ switch (transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ return "normal";
+ case WL_OUTPUT_TRANSFORM_90:
+ return "90";
+ case WL_OUTPUT_TRANSFORM_180:
+ return "180";
+ case WL_OUTPUT_TRANSFORM_270:
+ return "270";
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ return "flipped";
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ return "flipped-90";
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ return "flipped-180";
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ return "flipped-270";
}
-
- return layout;
+ return NULL;
}
-static float ipc_json_child_percentage(swayc_t *c) {
- float percent = 0;
- swayc_t *parent = c->parent;
-
- if (parent) {
- switch (parent->layout) {
- case L_VERT:
- percent = c->height / parent->height;
- break;
-
- case L_HORIZ:
- percent = c->width / parent->width;
- break;
-
- case L_STACKED: // fallthrough
- case L_TABBED: // fallthrough
- percent = 1.0;
- break;
-
- default:
- break;
+static void ipc_json_describe_output(struct sway_container *container, json_object *object) {
+ struct wlr_output *wlr_output = container->sway_output->wlr_output;
+ json_object_object_add(object, "type",
+ json_object_new_string("output"));
+ json_object_object_add(object, "active",
+ json_object_new_boolean(true));
+ json_object_object_add(object, "primary",
+ json_object_new_boolean(false));
+ json_object_object_add(object, "layout",
+ json_object_new_string("output"));
+ json_object_object_add(object, "make",
+ json_object_new_string(wlr_output->make));
+ json_object_object_add(object, "model",
+ json_object_new_string(wlr_output->model));
+ json_object_object_add(object, "serial",
+ json_object_new_string(wlr_output->serial));
+ json_object_object_add(object, "scale",
+ json_object_new_double(wlr_output->scale));
+ json_object_object_add(object, "refresh",
+ json_object_new_int(wlr_output->refresh));
+ json_object_object_add(object, "transform",
+ json_object_new_string(
+ ipc_json_get_output_transform(wlr_output->transform)));
+
+ struct sway_seat *seat = input_manager_get_default_seat(input_manager);
+ const char *ws = NULL;
+ if (seat) {
+ struct sway_container *focus =
+ seat_get_focus_inactive(seat, container);
+ if (focus && focus->type != C_WORKSPACE) {
+ focus = container_parent(focus, C_WORKSPACE);
+ }
+ if (focus) {
+ ws = focus->name;
}
}
+ json_object_object_add(object, "current_workspace",
+ json_object_new_string(ws));
+
+ json_object *modes_array = json_object_new_array();
+ struct wlr_output_mode *mode;
+ wl_list_for_each(mode, &wlr_output->modes, link) {
+ json_object *mode_object = json_object_new_object();
+ json_object_object_add(mode_object, "width",
+ json_object_new_int(mode->width));
+ json_object_object_add(mode_object, "height",
+ json_object_new_int(mode->height));
+ json_object_object_add(mode_object, "refresh",
+ json_object_new_int(mode->refresh));
+ json_object_array_add(modes_array, mode_object);
+ }
- return percent;
-}
-
-static void ipc_json_describe_output(swayc_t *output, json_object *object) {
- uint32_t scale = wlc_output_get_scale(output->handle);
- json_object_object_add(object, "active", json_object_new_boolean(true));
- json_object_object_add(object, "primary", json_object_new_boolean(false));
+ json_object_object_add(object, "modes", modes_array);
json_object_object_add(object, "layout", json_object_new_string("output"));
- json_object_object_add(object, "type", json_object_new_string("output"));
- json_object_object_add(object, "current_workspace",
- (output->focused) ? json_object_new_string(output->focused->name) : NULL);
- json_object_object_add(object, "scale", json_object_new_int(scale));
}
-static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object) {
- int num = (isdigit(workspace->name[0])) ? atoi(workspace->name) : -1;
- const char *layout = ipc_json_layout_description(workspace->workspace_layout);
+static void ipc_json_describe_workspace(struct sway_container *workspace,
+ json_object *object) {
+ int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1;
json_object_object_add(object, "num", json_object_new_int(num));
- json_object_object_add(object, "output", (workspace->parent) ? json_object_new_string(workspace->parent->name) : NULL);
+ json_object_object_add(object, "output", workspace->parent ?
+ json_object_new_string(workspace->parent->name) : NULL);
json_object_object_add(object, "type", json_object_new_string("workspace"));
- json_object_object_add(object, "layout", (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout));
-}
+ json_object_object_add(object, "urgent", json_object_new_boolean(false));
-// window is in the scratchpad ? changed : none
-static const char *ipc_json_get_scratchpad_state(swayc_t *c) {
- int i;
- for (i = 0; i < scratchpad->length; i++) {
- if (scratchpad->items[i] == c) {
- return "changed";
- }
- }
- return "none"; // we ignore the fresh value
+ const char *layout = ipc_json_layout_description(workspace->workspace_layout);
+ json_object_object_add(object, "layout", json_object_new_string(layout));
}
-static void ipc_json_describe_view(swayc_t *c, json_object *object) {
- json_object *props = json_object_new_object();
- json_object_object_add(object, "type", json_object_new_string((c->is_floating) ? "floating_con" : "con"));
-
- wlc_handle parent = wlc_view_get_parent(c->handle);
- json_object_object_add(object, "scratchpad_state",
- json_object_new_string(ipc_json_get_scratchpad_state(c)));
-
- json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL);
-
- json_object_object_add(props, "class", c->class ? json_object_new_string(c->class) :
- c->app_id ? json_object_new_string(c->app_id) : NULL);
- json_object_object_add(props, "instance", c->instance ? json_object_new_string(c->instance) :
- c->app_id ? json_object_new_string(c->app_id) : NULL);
- json_object_object_add(props, "title", (c->name) ? json_object_new_string(c->name) : NULL);
- json_object_object_add(props, "transient_for", parent ? json_object_new_int(parent) : NULL);
- json_object_object_add(object, "window_properties", props);
-
- json_object_object_add(object, "fullscreen_mode",
- json_object_new_int(swayc_is_fullscreen(c) ? 1 : 0));
- json_object_object_add(object, "sticky", json_object_new_boolean(c->sticky));
- json_object_object_add(object, "floating", json_object_new_string(
- c->is_floating ? "auto_on" : "auto_off")); // we can't state the cause
-
- json_object_object_add(object, "app_id", c->app_id ? json_object_new_string(c->app_id) : NULL);
+static void ipc_json_describe_view(struct sway_container *c, json_object *object) {
+ json_object_object_add(object, "name",
+ c->name ? json_object_new_string(c->name) : NULL);
+ json_object_object_add(object, "type", json_object_new_string("con"));
if (c->parent) {
- const char *layout = (c->parent->type == C_CONTAINER) ?
- ipc_json_layout_description(c->parent->layout) : "none";
- const char *last_layout = (c->parent->type == C_CONTAINER) ?
- ipc_json_layout_description(c->parent->prev_layout) : "none";
+ enum sway_container_layout layout = (c->parent->type == C_CONTAINER) ?
+ c->parent->layout : c->layout;
+
json_object_object_add(object, "layout",
- (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout));
- json_object_object_add(object, "last_split_layout",
- (strcmp(last_layout, "null") == 0) ? NULL : json_object_new_string(last_layout));
- json_object_object_add(object, "workspace_layout",
- json_object_new_string(ipc_json_layout_description(swayc_parent_by_type(c, C_WORKSPACE)->workspace_layout)));
+ json_object_new_string(ipc_json_layout_description(layout)));
}
}
-static void ipc_json_describe_root(swayc_t *c, json_object *object) {
- json_object_object_add(object, "type", json_object_new_string("root"));
- json_object_object_add(object, "layout", json_object_new_string("splith"));
+static void focus_inactive_children_iterator(struct sway_container *c, void *data) {
+ json_object *focus = data;
+ json_object_array_add(focus, json_object_new_int(c->id));
}
-json_object *ipc_json_describe_container(swayc_t *c) {
- float percent = ipc_json_child_percentage(c);
-
+json_object *ipc_json_describe_container(struct sway_container *c) {
if (!(sway_assert(c, "Container must not be null."))) {
return NULL;
}
+ struct sway_seat *seat = input_manager_get_default_seat(input_manager);
+ bool focused = seat_get_focus(seat) == c;
+
json_object *object = json_object_new_object();
json_object_object_add(object, "id", json_object_new_int((int)c->id));
- json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL);
+ json_object_object_add(object, "name",
+ c->name ? json_object_new_string(c->name) : NULL);
json_object_object_add(object, "rect", ipc_json_create_rect(c));
- json_object_object_add(object, "visible", json_object_new_boolean(c->visible));
- json_object_object_add(object, "focused", json_object_new_boolean(c == current_focus));
-
- json_object_object_add(object, "border", json_object_new_string(ipc_json_border_description(c)));
- json_object_object_add(object, "window_rect", ipc_json_create_rect_from_geometry(c->actual_geometry));
- json_object_object_add(object, "deco_rect", ipc_json_create_rect_from_geometry(c->title_bar_geometry));
- json_object_object_add(object, "geometry", ipc_json_create_rect_from_geometry(c->cached_geometry));
- json_object_object_add(object, "percent", (percent > 0) ? json_object_new_double(percent) : NULL);
- json_object_object_add(object, "window", json_object_new_int(c->handle)); // for the sake of i3 compat
- // TODO: make urgency actually work once Sway supports it
- json_object_object_add(object, "urgent", json_object_new_boolean(false));
- json_object_object_add(object, "current_border_width", json_object_new_int(c->border_thickness));
+ json_object_object_add(object, "focused",
+ json_object_new_boolean(focused));
+
+ json_object *focus = json_object_new_array();
+ seat_focus_inactive_children_for_each(seat, c,
+ focus_inactive_children_iterator, focus);
+ json_object_object_add(object, "focus", focus);
switch (c->type) {
case C_ROOT:
ipc_json_describe_root(c, object);
break;
-
case C_OUTPUT:
ipc_json_describe_output(c, object);
break;
-
- case C_CONTAINER: // fallthrough
+ case C_CONTAINER:
case C_VIEW:
ipc_json_describe_view(c, object);
break;
-
case C_WORKSPACE:
ipc_json_describe_workspace(c, object);
break;
-
- case C_TYPES: // fallthrough; this should never happen, I'm just trying to silence compiler warnings
+ case C_TYPES:
default:
break;
}
@@ -249,80 +215,56 @@ json_object *ipc_json_describe_container(swayc_t *c) {
return object;
}
-json_object *ipc_json_describe_input(struct libinput_device *device) {
- char* identifier = libinput_dev_unique_id(device);
- int vendor = libinput_device_get_id_vendor(device);
- int product = libinput_device_get_id_product(device);
- const char *name = libinput_device_get_name(device);
- double width = -1, height = -1;
- int has_size = libinput_device_get_size(device, &width, &height);
-
- json_object *device_object = json_object_new_object();
- json_object_object_add(device_object,"identifier",
- identifier ? json_object_new_string(identifier) : NULL);
- json_object_object_add(device_object,
- "vendor", json_object_new_int(vendor));
- json_object_object_add(device_object,
- "product", json_object_new_int(product));
- json_object_object_add(device_object,
- "name", json_object_new_string(name));
- if (has_size == 0) {
- json_object *size_object = json_object_new_object();
- json_object_object_add(size_object,
- "width", json_object_new_double(width));
- json_object_object_add(size_object,
- "height", json_object_new_double(height));
- } else {
- json_object_object_add(device_object, "size", NULL);
- }
+json_object *ipc_json_describe_container_recursive(struct sway_container *c) {
+ json_object *object = ipc_json_describe_container(c);
+ int i;
- struct {
- enum libinput_device_capability cap;
- const char *name;
- // If anyone feels like implementing device-specific IPC output,
- // be my guest
- json_object *(*describe)(struct libinput_device *);
- } caps[] = {
- { LIBINPUT_DEVICE_CAP_KEYBOARD, "keyboard", NULL },
- { LIBINPUT_DEVICE_CAP_POINTER, "pointer", NULL },
- { LIBINPUT_DEVICE_CAP_TOUCH, "touch", NULL },
- { LIBINPUT_DEVICE_CAP_TABLET_TOOL, "tablet_tool", NULL },
- { LIBINPUT_DEVICE_CAP_TABLET_PAD, "tablet_pad", NULL },
- { LIBINPUT_DEVICE_CAP_GESTURE, "gesture", NULL },
-#ifdef LIBINPUT_DEVICE_CAP_SWITCH // libinput 1.7.0+
- { LIBINPUT_DEVICE_CAP_SWITCH, "switch", NULL },
-#endif
- };
-
- json_object *_caps = json_object_new_array();
- for (size_t i = 0; i < sizeof(caps) / sizeof(caps[0]); ++i) {
- if (libinput_device_has_capability(device, caps[i].cap)) {
- json_object_array_add(_caps, json_object_new_string(caps[i].name));
- if (caps[i].describe) {
- json_object *desc = caps[i].describe(device);
- json_object_object_add(device_object, caps[i].name, desc);
- }
+ json_object *children = json_object_new_array();
+ if (c->type != C_VIEW && c->children) {
+ for (i = 0; i < c->children->length; ++i) {
+ json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i]));
}
}
- json_object_object_add(device_object, "capabilities", _caps);
+ json_object_object_add(object, "nodes", children);
- free(identifier);
- return device_object;
+ return object;
}
-json_object *ipc_json_get_version() {
- int major = 0, minor = 0, patch = 0;
- json_object *version = json_object_new_object();
+static const char *describe_device_type(struct sway_input_device *device) {
+ switch (device->wlr_device->type) {
+ case WLR_INPUT_DEVICE_POINTER:
+ return "pointer";
+ case WLR_INPUT_DEVICE_KEYBOARD:
+ return "keyboard";
+ case WLR_INPUT_DEVICE_TOUCH:
+ return "touch";
+ case WLR_INPUT_DEVICE_TABLET_TOOL:
+ return "tablet_tool";
+ case WLR_INPUT_DEVICE_TABLET_PAD:
+ return "tablet_pad";
+ }
+ return "unknown";
+}
- sscanf(SWAY_VERSION, "%u.%u.%u", &major, &minor, &patch);
+json_object *ipc_json_describe_input(struct sway_input_device *device) {
+ if (!(sway_assert(device, "Device must not be null"))) {
+ return NULL;
+ }
- json_object_object_add(version, "human_readable", json_object_new_string(SWAY_VERSION));
- json_object_object_add(version, "variant", json_object_new_string("sway"));
- json_object_object_add(version, "major", json_object_new_int(major));
- json_object_object_add(version, "minor", json_object_new_int(minor));
- json_object_object_add(version, "patch", json_object_new_int(patch));
+ json_object *object = json_object_new_object();
- return version;
+ json_object_object_add(object, "identifier",
+ json_object_new_string(device->identifier));
+ json_object_object_add(object, "name",
+ json_object_new_string(device->wlr_device->name));
+ json_object_object_add(object, "vendor",
+ json_object_new_int(device->wlr_device->vendor));
+ json_object_object_add(object, "product",
+ json_object_new_int(device->wlr_device->product));
+ json_object_object_add(object, "type",
+ json_object_new_string(describe_device_type(device)));
+
+ return object;
}
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) {
json_object *json = json_object_new_object();
json_object_object_add(json, "id", json_object_new_string(bar->id));
-#ifdef ENABLE_TRAY
- if (bar->tray_output) {
- json_object_object_add(json, "tray_output", json_object_new_string(bar->tray_output));
- } else {
- json_object_object_add(json, "tray_output", NULL);
- }
- if (bar->icon_theme) {
- json_object_object_add(json, "icon_theme", json_object_new_string(bar->icon_theme));
- } else {
- json_object_object_add(json, "icon_theme", NULL);
- }
- json_object_object_add(json, "tray_padding", json_object_new_int(bar->tray_padding));
- json_object_object_add(json, "activate_button", json_object_new_int(bar->activate_button));
- json_object_object_add(json, "context_button", json_object_new_int(bar->context_button));
- json_object_object_add(json, "secondary_button", json_object_new_int(bar->secondary_button));
-#endif
json_object_object_add(json, "mode", json_object_new_string(bar->mode));
- json_object_object_add(json, "hidden_state", json_object_new_string(bar->hidden_state));
- json_object_object_add(json, "modifier", json_object_new_string(get_modifier_name_by_mask(bar->modifier)));
- switch (bar->position) {
- case DESKTOP_SHELL_PANEL_POSITION_TOP:
- json_object_object_add(json, "position", json_object_new_string("top"));
- break;
- case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
- json_object_object_add(json, "position", json_object_new_string("bottom"));
- break;
- case DESKTOP_SHELL_PANEL_POSITION_LEFT:
- json_object_object_add(json, "position", json_object_new_string("left"));
- break;
- case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
- json_object_object_add(json, "position", json_object_new_string("right"));
- break;
- }
- json_object_object_add(json, "status_command", json_object_new_string(bar->status_command));
- json_object_object_add(json, "font", json_object_new_string((bar->font) ? bar->font : config->font));
+ json_object_object_add(json, "hidden_state",
+ json_object_new_string(bar->hidden_state));
+ json_object_object_add(json, "position",
+ json_object_new_string(bar->position));
+ json_object_object_add(json, "status_command",
+ json_object_new_string(bar->status_command));
+ json_object_object_add(json, "font",
+ json_object_new_string((bar->font) ? bar->font : config->font));
if (bar->separator_symbol) {
- json_object_object_add(json, "separator_symbol", json_object_new_string(bar->separator_symbol));
+ json_object_object_add(json, "separator_symbol",
+ json_object_new_string(bar->separator_symbol));
}
- json_object_object_add(json, "bar_height", json_object_new_int(bar->height));
- json_object_object_add(json, "wrap_scroll", json_object_new_boolean(bar->wrap_scroll));
- json_object_object_add(json, "workspace_buttons", json_object_new_boolean(bar->workspace_buttons));
- json_object_object_add(json, "strip_workspace_numbers", json_object_new_boolean(bar->strip_workspace_numbers));
- json_object_object_add(json, "binding_mode_indicator", json_object_new_boolean(bar->binding_mode_indicator));
- json_object_object_add(json, "verbose", json_object_new_boolean(bar->verbose));
- json_object_object_add(json, "pango_markup", json_object_new_boolean(bar->pango_markup));
+ json_object_object_add(json, "bar_height",
+ json_object_new_int(bar->height));
+ json_object_object_add(json, "wrap_scroll",
+ json_object_new_boolean(bar->wrap_scroll));
+ json_object_object_add(json, "workspace_buttons",
+ json_object_new_boolean(bar->workspace_buttons));
+ json_object_object_add(json, "strip_workspace_numbers",
+ json_object_new_boolean(bar->strip_workspace_numbers));
+ json_object_object_add(json, "binding_mode_indicator",
+ json_object_new_boolean(bar->binding_mode_indicator));
+ json_object_object_add(json, "verbose",
+ json_object_new_boolean(bar->verbose));
+ json_object_object_add(json, "pango_markup",
+ json_object_new_boolean(bar->pango_markup));
json_object *colors = json_object_new_object();
- json_object_object_add(colors, "background", json_object_new_string(bar->colors.background));
- json_object_object_add(colors, "statusline", json_object_new_string(bar->colors.statusline));
- json_object_object_add(colors, "separator", json_object_new_string(bar->colors.separator));
+ json_object_object_add(colors, "background",
+ json_object_new_string(bar->colors.background));
+ json_object_object_add(colors, "statusline",
+ json_object_new_string(bar->colors.statusline));
+ json_object_object_add(colors, "separator",
+ json_object_new_string(bar->colors.separator));
if (bar->colors.focused_background) {
- json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.focused_background));
+ json_object_object_add(colors, "focused_background",
+ json_object_new_string(bar->colors.focused_background));
} else {
- json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.background));
+ json_object_object_add(colors, "focused_background",
+ json_object_new_string(bar->colors.background));
}
if (bar->colors.focused_statusline) {
- json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.focused_statusline));
+ json_object_object_add(colors, "focused_statusline",
+ json_object_new_string(bar->colors.focused_statusline));
} else {
- json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.statusline));
+ json_object_object_add(colors, "focused_statusline",
+ json_object_new_string(bar->colors.statusline));
}
if (bar->colors.focused_separator) {
- json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.focused_separator));
+ json_object_object_add(colors, "focused_separator",
+ json_object_new_string(bar->colors.focused_separator));
} else {
- json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.separator));
+ json_object_object_add(colors, "focused_separator",
+ json_object_new_string(bar->colors.separator));
}
- json_object_object_add(colors, "focused_workspace_border", json_object_new_string(bar->colors.focused_workspace_border));
- json_object_object_add(colors, "focused_workspace_bg", json_object_new_string(bar->colors.focused_workspace_bg));
- json_object_object_add(colors, "focused_workspace_text", json_object_new_string(bar->colors.focused_workspace_text));
-
- json_object_object_add(colors, "inactive_workspace_border", json_object_new_string(bar->colors.inactive_workspace_border));
- json_object_object_add(colors, "inactive_workspace_bg", json_object_new_string(bar->colors.inactive_workspace_bg));
- json_object_object_add(colors, "inactive_workspace_text", json_object_new_string(bar->colors.inactive_workspace_text));
-
- json_object_object_add(colors, "active_workspace_border", json_object_new_string(bar->colors.active_workspace_border));
- json_object_object_add(colors, "active_workspace_bg", json_object_new_string(bar->colors.active_workspace_bg));
- json_object_object_add(colors, "active_workspace_text", json_object_new_string(bar->colors.active_workspace_text));
-
- json_object_object_add(colors, "urgent_workspace_border", json_object_new_string(bar->colors.urgent_workspace_border));
- json_object_object_add(colors, "urgent_workspace_bg", json_object_new_string(bar->colors.urgent_workspace_bg));
- json_object_object_add(colors, "urgent_workspace_text", json_object_new_string(bar->colors.urgent_workspace_text));
+ json_object_object_add(colors, "focused_workspace_border",
+ json_object_new_string(bar->colors.focused_workspace_border));
+ json_object_object_add(colors, "focused_workspace_bg",
+ json_object_new_string(bar->colors.focused_workspace_bg));
+ json_object_object_add(colors, "focused_workspace_text",
+ json_object_new_string(bar->colors.focused_workspace_text));
+
+ json_object_object_add(colors, "inactive_workspace_border",
+ json_object_new_string(bar->colors.inactive_workspace_border));
+ json_object_object_add(colors, "inactive_workspace_bg",
+ json_object_new_string(bar->colors.inactive_workspace_bg));
+ json_object_object_add(colors, "inactive_workspace_text",
+ json_object_new_string(bar->colors.inactive_workspace_text));
+
+ json_object_object_add(colors, "active_workspace_border",
+ json_object_new_string(bar->colors.active_workspace_border));
+ json_object_object_add(colors, "active_workspace_bg",
+ json_object_new_string(bar->colors.active_workspace_bg));
+ json_object_object_add(colors, "active_workspace_text",
+ json_object_new_string(bar->colors.active_workspace_text));
+
+ json_object_object_add(colors, "urgent_workspace_border",
+ json_object_new_string(bar->colors.urgent_workspace_border));
+ json_object_object_add(colors, "urgent_workspace_bg",
+ json_object_new_string(bar->colors.urgent_workspace_bg));
+ json_object_object_add(colors, "urgent_workspace_text",
+ json_object_new_string(bar->colors.urgent_workspace_text));
if (bar->colors.binding_mode_border) {
- json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.binding_mode_border));
+ json_object_object_add(colors, "binding_mode_border",
+ json_object_new_string(bar->colors.binding_mode_border));
} else {
- json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.urgent_workspace_border));
+ json_object_object_add(colors, "binding_mode_border",
+ json_object_new_string(bar->colors.urgent_workspace_border));
}
if (bar->colors.binding_mode_bg) {
- json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.binding_mode_bg));
+ json_object_object_add(colors, "binding_mode_bg",
+ json_object_new_string(bar->colors.binding_mode_bg));
} else {
- json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.urgent_workspace_bg));
+ json_object_object_add(colors, "binding_mode_bg",
+ json_object_new_string(bar->colors.urgent_workspace_bg));
}
if (bar->colors.binding_mode_text) {
- json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.binding_mode_text));
+ json_object_object_add(colors, "binding_mode_text",
+ json_object_new_string(bar->colors.binding_mode_text));
} else {
- json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.urgent_workspace_text));
+ json_object_object_add(colors, "binding_mode_text",
+ json_object_new_string(bar->colors.urgent_workspace_text));
}
json_object_object_add(json, "colors", colors);
@@ -440,75 +391,11 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
// Add outputs if defined
if (bar->outputs && bar->outputs->length > 0) {
json_object *outputs = json_object_new_array();
- int i;
- for (i = 0; i < bar->outputs->length; ++i) {
+ for (int i = 0; i < bar->outputs->length; ++i) {
const char *name = bar->outputs->items[i];
json_object_array_add(outputs, json_object_new_string(name));
}
json_object_object_add(json, "outputs", outputs);
}
-
return json;
}
-
-json_object *ipc_json_describe_container_recursive(swayc_t *c) {
- json_object *object = ipc_json_describe_container(c);
- int i;
-
- json_object *floating = json_object_new_array();
- if (c->type != C_VIEW && c->floating) {
- for (i = 0; i < c->floating->length; ++i) {
- swayc_t *item = c->floating->items[i];
- json_object_array_add(floating, ipc_json_describe_container_recursive(item));
- }
- }
- json_object_object_add(object, "floating_nodes", floating);
-
- json_object *children = json_object_new_array();
- if (c->type != C_VIEW && c->children) {
- for (i = 0; i < c->children->length; ++i) {
- json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i]));
- }
- }
- json_object_object_add(object, "nodes", children);
-
- json_object *focus = json_object_new_array();
- if (c->type != C_VIEW) {
- if (c->focused) {
- json_object_array_add(focus, json_object_new_double(c->focused->id));
- }
- if (c->floating) {
- for (i = 0; i < c->floating->length; ++i) {
- swayc_t *item = c->floating->items[i];
- if (item == c->focused) {
- continue;
- }
-
- json_object_array_add(focus, json_object_new_double(item->id));
- }
- }
- if (c->children) {
- for (i = 0; i < c->children->length; ++i) {
- swayc_t *item = c->children->items[i];
- if (item == c->focused) {
- continue;
- }
-
- json_object_array_add(focus, json_object_new_double(item->id));
- }
- }
- }
- json_object_object_add(object, "focus", focus);
-
- if (c->type == C_ROOT) {
- json_object *scratchpad_json = json_object_new_array();
- if (scratchpad->length > 0) {
- for (i = 0; i < scratchpad->length; ++i) {
- json_object_array_add(scratchpad_json, ipc_json_describe_container_recursive(scratchpad->items[i]));
- }
- }
- json_object_object_add(object, "scratchpad", scratchpad_json);
- }
-
- return object;
-}
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 @@
// See https://i3wm.org/docs/ipc.html for protocol information
-
#ifndef __FreeBSD__
// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0)
#define _XOPEN_SOURCE 700
#endif
-
+#include <assert.h>
#include <errno.h>
+#include <fcntl.h>
+#include <json-c/json.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
+#include <sys/ioctl.h>
#include <sys/un.h>
-#include <stdbool.h>
-#include <wlc/wlc-render.h>
#include <unistd.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <json-c/json.h>
-#include <list.h>
-#include <libinput.h>
-#ifdef __linux__
-struct ucred {
- pid_t pid;
- uid_t uid;
- gid_t gid;
-};
-#endif
+#include <wayland-server.h>
+#include "sway/commands.h"
#include "sway/ipc-json.h"
#include "sway/ipc-server.h"
-#include "sway/security.h"
-#include "sway/config.h"
-#include "sway/commands.h"
-#include "sway/input.h"
-#include "stringop.h"
-#include "log.h"
+#include "sway/server.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
#include "list.h"
-#include "util.h"
+#include "log.h"
static int ipc_socket = -1;
-static struct wlc_event_source *ipc_event_source = NULL;
+static struct wl_event_source *ipc_event_source = NULL;
static struct sockaddr_un *ipc_sockaddr = NULL;
static list_t *ipc_client_list = NULL;
static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
struct ipc_client {
- struct wlc_event_source *event_source;
- struct wlc_event_source *writable_event_source;
+ struct wl_event_source *event_source;
+ struct wl_event_source *writable_event_source;
+ struct sway_server *server;
int fd;
uint32_t payload_length;
uint32_t security_policy;
@@ -56,27 +46,6 @@ struct ipc_client {
char *write_buffer;
};
-static list_t *ipc_get_pixel_requests = NULL;
-
-struct get_pixels_request {
- struct ipc_client *client;
- wlc_handle output;
- struct wlc_geometry geo;
-};
-
-struct get_clipboard_request {
- struct ipc_client *client;
- json_object *json;
- int fd;
- struct wlc_event_source *fd_event_source;
- struct wlc_event_source *timer_event_source;
- char *type;
- unsigned int *pending;
- char *buf;
- size_t buf_size;
- size_t buf_position;
-};
-
struct sockaddr_un *ipc_user_sockaddr(void);
int ipc_handle_connection(int fd, uint32_t mask, void *data);
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);
void ipc_client_disconnect(struct ipc_client *client);
void ipc_client_handle_command(struct ipc_client *client);
bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length);
-void ipc_get_workspaces_callback(swayc_t *workspace, void *data);
-void ipc_get_outputs_callback(swayc_t *container, void *data);
-static void ipc_get_marks_callback(swayc_t *container, void *data);
-void ipc_init(void) {
+void ipc_init(struct sway_server *server) {
ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (ipc_socket == -1) {
sway_abort("Unable to create IPC socket");
@@ -116,14 +82,14 @@ void ipc_init(void) {
setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1);
ipc_client_list = create_list();
- ipc_get_pixel_requests = create_list();
- ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL);
+ ipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket,
+ WL_EVENT_READABLE, ipc_handle_connection, server);
}
void ipc_terminate(void) {
if (ipc_event_source) {
- wlc_event_source_remove(ipc_event_source);
+ wl_event_source_remove(ipc_event_source);
}
close(ipc_socket);
unlink(ipc_sockaddr->sun_path);
@@ -157,100 +123,82 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
return ipc_sockaddr;
}
-static pid_t get_client_pid(int client_fd) {
-// FreeBSD supports getting uid/gid, but not pid
-#ifdef __linux__
- struct ucred ucred;
- socklen_t len = sizeof(struct ucred);
-
- if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
- return -1;
- }
-
- return ucred.pid;
-#else
- return -1;
-#endif
-}
-
int ipc_handle_connection(int fd, uint32_t mask, void *data) {
- (void) fd; (void) data;
- sway_log(L_DEBUG, "Event on IPC listening socket");
- assert(mask == WLC_EVENT_READABLE);
+ (void) fd;
+ struct sway_server *server = data;
+ wlr_log(L_DEBUG, "Event on IPC listening socket");
+ assert(mask == WL_EVENT_READABLE);
int client_fd = accept(ipc_socket, NULL, NULL);
if (client_fd == -1) {
- sway_log_errno(L_ERROR, "Unable to accept IPC client connection");
+ wlr_log_errno(L_ERROR, "Unable to accept IPC client connection");
return 0;
}
int flags;
if ((flags = fcntl(client_fd, F_GETFD)) == -1
|| fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) {
- sway_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket");
+ wlr_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket");
close(client_fd);
return 0;
}
if ((flags = fcntl(client_fd, F_GETFL)) == -1
|| fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) {
- sway_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket");
+ wlr_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket");
close(client_fd);
return 0;
}
- struct ipc_client* client = malloc(sizeof(struct ipc_client));
+ struct ipc_client *client = malloc(sizeof(struct ipc_client));
if (!client) {
- sway_log(L_ERROR, "Unable to allocate ipc client");
+ wlr_log(L_ERROR, "Unable to allocate ipc client");
close(client_fd);
return 0;
}
+ client->server = server;
client->payload_length = 0;
client->fd = client_fd;
client->subscribed_events = 0;
- client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client);
+ client->event_source = wl_event_loop_add_fd(server->wl_event_loop,
+ client_fd, WL_EVENT_READABLE, ipc_client_handle_readable, client);
client->writable_event_source = NULL;
client->write_buffer_size = 128;
client->write_buffer_len = 0;
client->write_buffer = malloc(client->write_buffer_size);
if (!client->write_buffer) {
- sway_log(L_ERROR, "Unable to allocate ipc client write buffer");
+ wlr_log(L_ERROR, "Unable to allocate ipc client write buffer");
close(client_fd);
return 0;
}
- pid_t pid = get_client_pid(client->fd);
- client->security_policy = get_ipc_policy_mask(pid);
-
- sway_log(L_DEBUG, "New client: fd %d, pid %d", client_fd, pid);
-
+ wlr_log(L_DEBUG, "New client: fd %d", client_fd);
list_add(ipc_client_list, client);
-
return 0;
}
-static const int ipc_header_size = sizeof(ipc_magic)+8;
+static const int ipc_header_size = sizeof(ipc_magic) + 8;
int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
struct ipc_client *client = data;
- if (mask & WLC_EVENT_ERROR) {
- sway_log(L_ERROR, "IPC Client socket error, removing client");
+ if (mask & WL_EVENT_ERROR) {
+ wlr_log(L_ERROR, "IPC Client socket error, removing client");
ipc_client_disconnect(client);
return 0;
}
- if (mask & WLC_EVENT_HANGUP) {
- sway_log(L_DEBUG, "Client %d hung up", client->fd);
+ if (mask & WL_EVENT_HANGUP) {
+ wlr_log(L_DEBUG, "Client %d hung up", client->fd);
ipc_client_disconnect(client);
return 0;
}
- sway_log(L_DEBUG, "Client %d readable", client->fd);
+ wlr_log(L_DEBUG, "Client %d readable", client->fd);
int read_available;
if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
- sway_log_errno(L_INFO, "Unable to read IPC socket buffer size");
+ wlr_log_errno(L_INFO, "Unable to read IPC socket buffer size");
ipc_client_disconnect(client);
return 0;
}
@@ -272,13 +220,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
// Should be fully available, because read_available >= ipc_header_size
ssize_t received = recv(client_fd, buf, ipc_header_size, 0);
if (received == -1) {
- sway_log_errno(L_INFO, "Unable to receive header from IPC client");
+ wlr_log_errno(L_INFO, "Unable to receive header from IPC client");
ipc_client_disconnect(client);
return 0;
}
if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) {
- sway_log(L_DEBUG, "IPC header check failed");
+ wlr_log(L_DEBUG, "IPC header check failed");
ipc_client_disconnect(client);
return 0;
}
@@ -293,17 +241,110 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
return 0;
}
+static bool ipc_has_event_listeners(enum ipc_command_type event) {
+ for (int i = 0; i < ipc_client_list->length; i++) {
+ struct ipc_client *client = ipc_client_list->items[i];
+ if ((client->subscribed_events & event_mask(event)) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void ipc_send_event(const char *json_string, enum ipc_command_type event) {
+ struct ipc_client *client;
+ for (int i = 0; i < ipc_client_list->length; i++) {
+ client = ipc_client_list->items[i];
+ if ((client->subscribed_events & event_mask(event)) == 0) {
+ continue;
+ }
+ client->current_command = event;
+ if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) {
+ wlr_log_errno(L_INFO, "Unable to send reply to IPC client");
+ ipc_client_disconnect(client);
+ }
+ }
+}
+
+void ipc_event_workspace(struct sway_container *old,
+ struct sway_container *new, const char *change) {
+ if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) {
+ return;
+ }
+ wlr_log(L_DEBUG, "Sending workspace::%s event", change);
+ json_object *obj = json_object_new_object();
+ json_object_object_add(obj, "change", json_object_new_string(change));
+ if (strcmp("focus", change) == 0) {
+ if (old) {
+ json_object_object_add(obj, "old",
+ ipc_json_describe_container_recursive(old));
+ } else {
+ json_object_object_add(obj, "old", NULL);
+ }
+ }
+
+ if (new) {
+ json_object_object_add(obj, "current",
+ ipc_json_describe_container_recursive(new));
+ } else {
+ json_object_object_add(obj, "current", NULL);
+ }
+
+ const char *json_string = json_object_to_json_string(obj);
+ ipc_send_event(json_string, IPC_EVENT_WORKSPACE);
+ json_object_put(obj);
+}
+
+void ipc_event_window(struct sway_container *window, const char *change) {
+ if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) {
+ return;
+ }
+ wlr_log(L_DEBUG, "Sending window::%s event", change);
+ json_object *obj = json_object_new_object();
+ json_object_object_add(obj, "change", json_object_new_string(change));
+ json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window));
+
+ const char *json_string = json_object_to_json_string(obj);
+ ipc_send_event(json_string, IPC_EVENT_WINDOW);
+ json_object_put(obj);
+}
+
+void ipc_event_barconfig_update(struct bar_config *bar) {
+ if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) {
+ return;
+ }
+ wlr_log(L_DEBUG, "Sending barconfig_update event");
+ json_object *json = ipc_json_describe_bar_config(bar);
+
+ const char *json_string = json_object_to_json_string(json);
+ ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE);
+ json_object_put(json);
+}
+
+void ipc_event_mode(const char *mode) {
+ if (!ipc_has_event_listeners(IPC_EVENT_MODE)) {
+ return;
+ }
+ wlr_log(L_DEBUG, "Sending mode::%s event", mode);
+ json_object *obj = json_object_new_object();
+ json_object_object_add(obj, "change", json_object_new_string(mode));
+
+ const char *json_string = json_object_to_json_string(obj);
+ ipc_send_event(json_string, IPC_EVENT_MODE);
+ json_object_put(obj);
+}
+
int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
struct ipc_client *client = data;
- if (mask & WLC_EVENT_ERROR) {
- sway_log(L_ERROR, "IPC Client socket error, removing client");
+ if (mask & WL_EVENT_ERROR) {
+ wlr_log(L_ERROR, "IPC Client socket error, removing client");
ipc_client_disconnect(client);
return 0;
}
- if (mask & WLC_EVENT_HANGUP) {
- sway_log(L_DEBUG, "Client %d hung up", client->fd);
+ if (mask & WL_EVENT_HANGUP) {
+ wlr_log(L_DEBUG, "Client %d hung up", client->fd);
ipc_client_disconnect(client);
return 0;
}
@@ -312,14 +353,14 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
return 0;
}
- sway_log(L_DEBUG, "Client %d writable", client->fd);
+ wlr_log(L_DEBUG, "Client %d writable", client->fd);
ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
if (written == -1 && errno == EAGAIN) {
return 0;
} else if (written == -1) {
- sway_log_errno(L_INFO, "Unable to send data from queue to IPC client");
+ wlr_log_errno(L_INFO, "Unable to send data from queue to IPC client");
ipc_client_disconnect(client);
return 0;
}
@@ -328,7 +369,7 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
client->write_buffer_len -= written;
if (client->write_buffer_len == 0 && client->writable_event_source) {
- wlc_event_source_remove(client->writable_event_source);
+ wl_event_source_remove(client->writable_event_source);
client->writable_event_source = NULL;
}
@@ -344,332 +385,48 @@ void ipc_client_disconnect(struct ipc_client *client) {
shutdown(client->fd, SHUT_RDWR);
}
- sway_log(L_INFO, "IPC Client %d disconnected", client->fd);
- wlc_event_source_remove(client->event_source);
+ wlr_log(L_INFO, "IPC Client %d disconnected", client->fd);
+ wl_event_source_remove(client->event_source);
if (client->writable_event_source) {
- wlc_event_source_remove(client->writable_event_source);
+ wl_event_source_remove(client->writable_event_source);
}
int i = 0;
- while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++;
+ while (i < ipc_client_list->length && ipc_client_list->items[i] != client) {
+ i++;
+ }
list_del(ipc_client_list, i);
free(client->write_buffer);
close(client->fd);
free(client);
}
-bool output_by_name_test(swayc_t *view, void *data) {
- char *name = (char *)data;
- if (view->type != C_OUTPUT) {
- return false;
- }
- return !strcmp(name, view->name);
-}
-
-void ipc_get_pixels(wlc_handle output) {
- if (ipc_get_pixel_requests->length == 0) {
+static void ipc_get_workspaces_callback(struct sway_container *workspace,
+ void *data) {
+ if (workspace->type != C_WORKSPACE) {
return;
}
-
- list_t *unhandled = create_list();
-
- struct get_pixels_request *req;
- int i;
- for (i = 0; i < ipc_get_pixel_requests->length; ++i) {
- req = ipc_get_pixel_requests->items[i];
- if (req->output != output) {
- list_add(unhandled, req);
- continue;
- }
-
- const struct wlc_size *size = &req->geo.size;
- struct wlc_geometry g_out;
- char response_header[9];
- memset(response_header, 0, sizeof(response_header));
- char *data = malloc(sizeof(response_header) + size->w * size->h * 4);
- if (!data) {
- sway_log(L_ERROR, "Unable to allocate pixels for get_pixels");
- ipc_client_disconnect(req->client);
- free(req);
- continue;
- }
- wlc_pixels_read(WLC_RGBA8888, &req->geo, &g_out, data + sizeof(response_header));
-
- response_header[0] = 1;
- uint32_t *_size = (uint32_t *)(response_header + 1);
- _size[0] = g_out.size.w;
- _size[1] = g_out.size.h;
- size_t len = sizeof(response_header) + (g_out.size.w * g_out.size.h * 4);
- memcpy(data, response_header, sizeof(response_header));
- ipc_send_reply(req->client, data, len);
- free(data);
- // free the request since it has been handled
- free(req);
- }
-
- // free old list of pixel requests and set new list to all unhandled
- // requests (request for another output).
- list_free(ipc_get_pixel_requests);
- ipc_get_pixel_requests = unhandled;
-}
-
-static bool is_text_target(const char *target) {
- return (strncmp(target, "text/", 5) == 0
- || strcmp(target, "UTF8_STRING") == 0
- || strcmp(target, "STRING") == 0
- || strcmp(target, "TEXT") == 0
- || strcmp(target, "COMPOUND_TEXT") == 0);
-}
-
-static void release_clipboard_request(struct get_clipboard_request *req) {
- if (--(*req->pending) == 0) {
- const char *str = json_object_to_json_string(req->json);
- ipc_send_reply(req->client, str, (uint32_t)strlen(str));
- json_object_put(req->json);
- }
-
- free(req->type);
- free(req->buf);
- wlc_event_source_remove(req->fd_event_source);
- wlc_event_source_remove(req->timer_event_source);
- close(req->fd);
- free(req);
-}
-
-static int ipc_selection_data_cb(int fd, uint32_t mask, void *data) {
- assert(data);
- struct get_clipboard_request *req = (struct get_clipboard_request *)data;
-
- if (mask & WLC_EVENT_ERROR) {
- sway_log(L_ERROR, "Selection data fd error");
- goto error;
- }
-
- if (mask & WLC_EVENT_READABLE) {
- static const unsigned int max_size = 8192 * 1024;
- int amt = 0;
-
- do {
- int size = req->buf_size - req->buf_position;
- int amt = read(fd, req->buf + req->buf_position, size - 1);
- if (amt < 0) {
- if (errno == EAGAIN) {
- return 0;
- }
-
- sway_log_errno(L_INFO, "Failed to read from clipboard data fd");
- goto release;
- }
-
- req->buf_position += amt;
- if (req->buf_position >= req->buf_size - 1) {
- if (req->buf_size >= max_size) {
- sway_log(L_ERROR, "get_clipbard: selection data too large");
- goto error;
- }
- char *next = realloc(req->buf, req->buf_size *= 2);
- if (!next) {
- sway_log_errno(L_ERROR, "get_clipboard: realloc data buffer failed");
- goto error;
- }
-
- req->buf = next;
- }
- } while(amt != 0);
-
- req->buf[req->buf_position] = '\0';
-
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "success", json_object_new_boolean(true));
- if (is_text_target(req->type)) {
- json_object_object_add(obj, "content", json_object_new_string(req->buf));
- json_object_object_add(req->json, req->type, obj);
- } else {
- size_t outlen;
- char *b64 = b64_encode(req->buf, req->buf_position, &outlen);
- json_object_object_add(obj, "content", json_object_new_string(b64));
- free(b64);
-
- char *type = malloc(strlen(req->type) + 8);
- strcat(type, ";base64");
- json_object_object_add(req->json, type, obj);
- free(type);
- }
- }
-
- goto release;
-
-error:;
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "success", json_object_new_boolean(false));
- json_object_object_add(obj, "error",
- json_object_new_string("Failed to retrieve data"));
- json_object_object_add(req->json, req->type, obj);
-
-release:
- release_clipboard_request(req);
- return 0;
-}
-
-static int ipc_selection_timer_cb(void *data) {
- assert(data);
- struct get_clipboard_request *req = (struct get_clipboard_request *)data;
-
- sway_log(L_INFO, "get_clipbard: timeout for type %s", req->type);
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "success", json_object_new_boolean(false));
- json_object_object_add(obj, "error", json_object_new_string("Timeout"));
- json_object_object_add(req->json, req->type, obj);
-
- release_clipboard_request(req);
- return 0;
-}
-
-// greedy wildcard (only "*") matching
-bool mime_type_matches(const char *mime_type, const char *pattern) {
- const char *wildcard = NULL;
- while (*mime_type && *pattern) {
- if (*pattern == '*' && !wildcard) {
- wildcard = pattern;
- ++pattern;
- }
-
- if (*mime_type != *pattern) {
- if (!wildcard)
- return false;
-
- pattern = wildcard;
- ++mime_type;
- continue;
- }
-
- ++mime_type;
- ++pattern;
- }
-
- while (*pattern == '*') {
- ++pattern;
- }
-
- return (*mime_type == *pattern);
-}
-
-void ipc_get_clipboard(struct ipc_client *client, char *buf) {
- size_t size;
- const char **types = wlc_get_selection_types(&size);
- if (client->payload_length == 0) {
- json_object *obj = json_object_new_array();
- for (size_t i = 0; i < size; ++i) {
- json_object_array_add(obj, json_object_new_string(types[i]));
- }
-
- const char *str = json_object_to_json_string(obj);
- ipc_send_reply(client, str, strlen(str));
- json_object_put(obj);
- return;
- }
-
- unescape_string(buf);
- strip_quotes(buf);
- list_t *requested = split_string(buf, " ");
- json_object *json = json_object_new_object();
- unsigned int *pending = malloc(sizeof(unsigned int));
- *pending = 0;
-
- for (size_t l = 0; l < (size_t) requested->length; ++l) {
- const char *pattern = requested->items[l];
- bool found = false;
- for (size_t i = 0; i < size; ++i) {
- if (!mime_type_matches(types[i], pattern)) {
- continue;
- }
-
- found = true;
-
- struct get_clipboard_request *req = malloc(sizeof(*req));
- if (!req) {
- sway_log(L_ERROR, "get_clipboard: request malloc failed");
- goto data_error;
- }
-
- int pipes[2];
- if (pipe(pipes) == -1) {
- sway_log_errno(L_ERROR, "get_clipboard: pipe call failed");
- free(req);
- goto data_error;
- }
-
- fcntl(pipes[0], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
- fcntl(pipes[1], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
-
- if (!wlc_get_selection_data(types[i], pipes[1])) {
- close(pipes[0]);
- close(pipes[1]);
- free(req);
- sway_log(L_ERROR, "get_clipboard: failed to retrieve "
- "selection data");
- goto data_error;
- }
-
- if (!(req->buf = malloc(512))) {
- close(pipes[0]);
- close(pipes[1]);
- free(req);
- sway_log_errno(L_ERROR, "get_clipboard: buf malloc failed");
- goto data_error;
- }
-
- (*pending)++;
-
- req->client = client;
- req->type = strdup(types[i]);
- req->json = json;
- req->pending = pending;
- req->buf_position = 0;
- req->buf_size = 512;
- req->fd = pipes[0];
- req->timer_event_source = wlc_event_loop_add_timer(ipc_selection_timer_cb, req);
- req->fd_event_source = wlc_event_loop_add_fd(pipes[0],
- WLC_EVENT_READABLE | WLC_EVENT_ERROR | WLC_EVENT_HANGUP,
- &ipc_selection_data_cb, req);
-
- wlc_event_source_timer_update(req->timer_event_source, 30000);
-
- // NOTE: remove this goto to enable retrieving multiple
- // targets at once. The whole implementation is already
- // made for it. The only reason it was disabled
- // at the time of writing is that neither wlc's xselection
- // implementation nor (apparently) gtk on wayland supports
- // multiple send requests at the same time which makes
- // every request except the last one fail (and therefore
- // return empty data)
- goto cleanup;
- }
-
- if (!found) {
- sway_log(L_INFO, "Invalid clipboard type %s requested", pattern);
- }
- }
-
- if (*pending == 0) {
- static const char *error_empty = "{ \"success\": false, \"error\": "
- "\"No matching types found\" }";
- ipc_send_reply(client, error_empty, (uint32_t)strlen(error_empty));
- free(json);
- free(pending);
- }
-
- goto cleanup;
-
-data_error:;
- static const char *error_json = "{ \"success\": false, \"error\": "
- "\"Failed to create clipboard data request\" }";
- ipc_send_reply(client, error_json, (uint32_t)strlen(error_json));
- free(json);
- free(pending);
-
-cleanup:
- list_free(requested);
- free(types);
+ json_object *workspace_json = ipc_json_describe_container(workspace);
+ // override the default focused indicator because
+ // it's set differently for the get_workspaces reply
+ struct sway_seat *seat =
+ input_manager_get_default_seat(input_manager);
+ struct sway_container *focused_ws = seat_get_focus(seat);
+ if (focused_ws != NULL && focused_ws->type != C_WORKSPACE) {
+ focused_ws = container_parent(focused_ws, C_WORKSPACE);
+ }
+ bool focused = workspace == focused_ws;
+ json_object_object_del(workspace_json, "focused");
+ json_object_object_add(workspace_json, "focused",
+ json_object_new_boolean(focused));
+ json_object_array_add((json_object *)data, workspace_json);
+
+ focused_ws = seat_get_focus_inactive(seat, workspace->parent);
+ if (focused_ws->type != C_WORKSPACE) {
+ focused_ws = container_parent(focused_ws, C_WORKSPACE);
+ }
+ bool visible = workspace == focused_ws;
+ json_object_object_add(workspace_json, "visible",
+ json_object_new_boolean(visible));
}
void ipc_client_handle_command(struct ipc_client *client) {
@@ -679,7 +436,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
char *buf = malloc(client->payload_length + 1);
if (!buf) {
- sway_log_errno(L_INFO, "Unable to allocate IPC payload");
+ wlr_log_errno(L_INFO, "Unable to allocate IPC payload");
ipc_client_disconnect(client);
return;
}
@@ -688,7 +445,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
ssize_t received = recv(client->fd, buf, client->payload_length, 0);
if (received == -1)
{
- sway_log_errno(L_INFO, "Unable to receive payload from IPC client");
+ wlr_log_errno(L_INFO, "Unable to receive payload from IPC client");
ipc_client_disconnect(client);
free(buf);
return;
@@ -701,10 +458,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
switch (client->current_command) {
case IPC_COMMAND:
{
- if (!(client->security_policy & IPC_FEATURE_COMMAND)) {
- goto exit_denied;
- }
- struct cmd_results *results = handle_command(buf, CONTEXT_IPC);
+ config_clear_handler_context(config);
+ struct cmd_results *results = execute_command(buf, NULL);
const char *json = cmd_results_to_json(results);
char reply[256];
int length = snprintf(reply, sizeof(reply), "%s", json);
@@ -713,18 +468,45 @@ void ipc_client_handle_command(struct ipc_client *client) {
goto exit_cleanup;
}
+ case IPC_GET_OUTPUTS:
+ {
+ json_object *outputs = json_object_new_array();
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *container = root_container.children->items[i];
+ if (container->type == C_OUTPUT) {
+ json_object_array_add(outputs,
+ ipc_json_describe_container(container));
+ }
+ }
+ const char *json_string = json_object_to_json_string(outputs);
+ ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
+ json_object_put(outputs); // free
+ goto exit_cleanup;
+ }
+
+ case IPC_GET_WORKSPACES:
+ {
+ json_object *workspaces = json_object_new_array();
+ container_for_each_descendant_dfs(&root_container,
+ ipc_get_workspaces_callback, workspaces);
+ const char *json_string = json_object_to_json_string(workspaces);
+ ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
+ json_object_put(workspaces); // free
+ goto exit_cleanup;
+ }
+
case IPC_SUBSCRIBE:
{
// TODO: Check if they're permitted to use these events
struct json_object *request = json_tokener_parse(buf);
if (request == NULL) {
ipc_send_reply(client, "{\"success\": false}", 18);
- sway_log_errno(L_INFO, "Failed to read request");
+ wlr_log_errno(L_INFO, "Failed to read request");
goto exit_cleanup;
}
// parse requested event types
- for (int i = 0; i < json_object_array_length(request); i++) {
+ for (size_t i = 0; i < json_object_array_length(request); i++) {
const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
if (strcmp(event_type, "workspace") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE);
@@ -741,86 +523,39 @@ void ipc_client_handle_command(struct ipc_client *client) {
} else {
ipc_send_reply(client, "{\"success\": false}", 18);
json_object_put(request);
- sway_log_errno(L_INFO, "Failed to parse request");
+ wlr_log_errno(L_INFO, "Failed to parse request");
goto exit_cleanup;
}
}
json_object_put(request);
-
ipc_send_reply(client, "{\"success\": true}", 17);
goto exit_cleanup;
}
- case IPC_GET_WORKSPACES:
- {
- if (!(client->security_policy & IPC_FEATURE_GET_WORKSPACES)) {
- goto exit_denied;
- }
- json_object *workspaces = json_object_new_array();
- container_map(&root_container, ipc_get_workspaces_callback, workspaces);
- const char *json_string = json_object_to_json_string(workspaces);
- ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
- json_object_put(workspaces); // free
- goto exit_cleanup;
- }
-
case IPC_GET_INPUTS:
{
- if (!(client->security_policy & IPC_FEATURE_GET_INPUTS)) {
- goto exit_denied;
- }
json_object *inputs = json_object_new_array();
- if (input_devices) {
- for(int i = 0; i<input_devices->length; i++) {
- struct libinput_device *device = input_devices->items[i];
- json_object_array_add(inputs, ipc_json_describe_input(device));
- }
+ struct sway_input_device *device = NULL;
+ wl_list_for_each(device, &input_manager->devices, link) {
+ json_object_array_add(inputs, ipc_json_describe_input(device));
}
const char *json_string = json_object_to_json_string(inputs);
- ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
- json_object_put(inputs);
- goto exit_cleanup;
- }
-
- case IPC_GET_OUTPUTS:
- {
- if (!(client->security_policy & IPC_FEATURE_GET_OUTPUTS)) {
- goto exit_denied;
- }
- json_object *outputs = json_object_new_array();
- container_map(&root_container, ipc_get_outputs_callback, outputs);
- const char *json_string = json_object_to_json_string(outputs);
- ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
- json_object_put(outputs); // free
+ ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
+ json_object_put(inputs); // free
goto exit_cleanup;
}
case IPC_GET_TREE:
{
- if (!(client->security_policy & IPC_FEATURE_GET_TREE)) {
- goto exit_denied;
- }
- json_object *tree = ipc_json_describe_container_recursive(&root_container);
+ json_object *tree =
+ ipc_json_describe_container_recursive(&root_container);
const char *json_string = json_object_to_json_string(tree);
ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
json_object_put(tree);
goto exit_cleanup;
}
- case IPC_GET_MARKS:
- {
- if (!(client->security_policy & IPC_FEATURE_GET_MARKS)) {
- goto exit_denied;
- }
- json_object *marks = json_object_new_array();
- container_map(&root_container, ipc_get_marks_callback, marks);
- const char *json_string = json_object_to_json_string(marks);
- ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
- json_object_put(marks);
- goto exit_cleanup;
- }
-
case IPC_GET_VERSION:
{
json_object *version = ipc_json_get_version();
@@ -830,62 +565,12 @@ void ipc_client_handle_command(struct ipc_client *client) {
goto exit_cleanup;
}
- case IPC_SWAY_GET_PIXELS:
- {
- char response_header[9];
- memset(response_header, 0, sizeof(response_header));
-
- json_object *obj = json_tokener_parse(buf);
- json_object *o, *x, *y, *w, *h;
-
- json_object_object_get_ex(obj, "output", &o);
- json_object_object_get_ex(obj, "x", &x);
- json_object_object_get_ex(obj, "y", &y);
- json_object_object_get_ex(obj, "w", &w);
- json_object_object_get_ex(obj, "h", &h);
-
- struct wlc_geometry g = {
- .origin = {
- .x = json_object_get_int(x),
- .y = json_object_get_int(y)
- },
- .size = {
- .w = json_object_get_int(w),
- .h = json_object_get_int(h)
- }
- };
-
- swayc_t *output = swayc_by_test(&root_container, output_by_name_test, (void *)json_object_get_string(o));
- json_object_put(obj);
-
- if (!output) {
- sway_log(L_ERROR, "IPC GET_PIXELS request with unknown output name");
- ipc_send_reply(client, response_header, sizeof(response_header));
- goto exit_cleanup;
- }
- struct get_pixels_request *req = malloc(sizeof(struct get_pixels_request));
- if (!req) {
- sway_log(L_ERROR, "Unable to allocate get_pixels request");
- goto exit_cleanup;
- }
- req->client = client;
- req->output = output->handle;
- req->geo = g;
- list_add(ipc_get_pixel_requests, req);
- wlc_output_schedule_render(output->handle);
- goto exit_cleanup;
- }
-
case IPC_GET_BAR_CONFIG:
{
- if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) {
- goto exit_denied;
- }
if (!buf[0]) {
// Send list of configured bar IDs
json_object *bars = json_object_new_array();
- int i;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
struct bar_config *bar = config->bars->items[i];
json_object_array_add(bars, json_object_new_string(bar->id));
}
@@ -895,8 +580,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
} else {
// Send particular bar's details
struct bar_config *bar = NULL;
- int i;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
bar = config->bars->items[i];
if (strcmp(buf, bar->id) == 0) {
break;
@@ -916,24 +600,13 @@ void ipc_client_handle_command(struct ipc_client *client) {
goto exit_cleanup;
}
- case IPC_GET_CLIPBOARD:
- {
- if (!(client->security_policy & IPC_FEATURE_GET_CLIPBOARD)) {
- goto exit_denied;
- }
-
- ipc_get_clipboard(client, buf);
- goto exit_cleanup;
- }
-
default:
- sway_log(L_INFO, "Unknown IPC command type %i", client->current_command);
+ wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command);
goto exit_cleanup;
}
-exit_denied:
ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied));
- sway_log(L_DEBUG, "Denied IPC client access to %i", client->current_command);
+ wlr_log(L_DEBUG, "Denied IPC client access to %i", client->current_command);
exit_cleanup:
client->payload_length = 0;
@@ -956,16 +629,15 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
client->write_buffer_size *= 2;
}
- // TODO: reduce the limit back to 4 MB when screenshooter is implemented
- if (client->write_buffer_size > (1 << 28)) { // 256 MB
- sway_log(L_ERROR, "Client write buffer too big, disconnecting client");
+ if (client->write_buffer_size > 4e6) { // 4 MB
+ wlr_log(L_ERROR, "Client write buffer too big, disconnecting client");
ipc_client_disconnect(client);
return false;
}
char *new_buffer = realloc(client->write_buffer, client->write_buffer_size);
if (!new_buffer) {
- sway_log(L_ERROR, "Unable to reallocate ipc client write buffer");
+ wlr_log(L_ERROR, "Unable to reallocate ipc client write buffer");
ipc_client_disconnect(client);
return false;
}
@@ -977,212 +649,11 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
client->write_buffer_len += payload_length;
if (!client->writable_event_source) {
- client->writable_event_source = wlc_event_loop_add_fd(client->fd, WLC_EVENT_WRITABLE, ipc_client_handle_writable, client);
+ client->writable_event_source = wl_event_loop_add_fd(
+ server.wl_event_loop, client->fd, WL_EVENT_WRITABLE,
+ ipc_client_handle_writable, client);
}
- sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload);
-
+ wlr_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload);
return true;
}
-
-void ipc_get_workspaces_callback(swayc_t *workspace, void *data) {
- if (workspace->type == C_WORKSPACE) {
- json_object *workspace_json = ipc_json_describe_container(workspace);
- // override the default focused indicator because
- // it's set differently for the get_workspaces reply
- bool focused = root_container.focused == workspace->parent && workspace->parent->focused == workspace;
- json_object_object_del(workspace_json, "focused");
- json_object_object_add(workspace_json, "focused", json_object_new_boolean(focused));
- json_object_array_add((json_object *)data, workspace_json);
- }
-}
-
-void ipc_get_outputs_callback(swayc_t *container, void *data) {
- if (container->type == C_OUTPUT) {
- json_object_array_add((json_object *)data, ipc_json_describe_container(container));
- }
-}
-
-static void ipc_get_marks_callback(swayc_t *container, void *data) {
- json_object *object = (json_object *)data;
- if (container->marks) {
- for (int i = 0; i < container->marks->length; ++i) {
- char *mark = (char *)container->marks->items[i];
- json_object_array_add(object, json_object_new_string(mark));
- }
- }
-}
-
-void ipc_send_event(const char *json_string, enum ipc_command_type event) {
- static struct {
- enum ipc_command_type event;
- enum ipc_feature feature;
- } security_mappings[] = {
- { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE },
- { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT },
- { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE },
- { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW },
- { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING },
- { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT }
- };
-
- uint32_t security_mask = 0;
- for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) {
- if (security_mappings[i].event == event) {
- security_mask = security_mappings[i].feature;
- break;
- }
- }
-
- int i;
- struct ipc_client *client;
- for (i = 0; i < ipc_client_list->length; i++) {
- client = ipc_client_list->items[i];
- if (!(client->security_policy & security_mask)) {
- continue;
- }
- if ((client->subscribed_events & event_mask(event)) == 0) {
- continue;
- }
- client->current_command = event;
- if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) {
- sway_log_errno(L_INFO, "Unable to send reply to IPC client");
- ipc_client_disconnect(client);
- }
- }
-}
-
-void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) {
- sway_log(L_DEBUG, "Sending workspace::%s event", change);
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "change", json_object_new_string(change));
- if (strcmp("focus", change) == 0) {
- if (old) {
- json_object_object_add(obj, "old", ipc_json_describe_container_recursive(old));
- } else {
- json_object_object_add(obj, "old", NULL);
- }
- }
-
- if (new) {
- json_object_object_add(obj, "current", ipc_json_describe_container_recursive(new));
- } else {
- json_object_object_add(obj, "current", NULL);
- }
-
- const char *json_string = json_object_to_json_string(obj);
- ipc_send_event(json_string, IPC_EVENT_WORKSPACE);
-
- json_object_put(obj); // free
-}
-
-void ipc_event_window(swayc_t *window, const char *change) {
- sway_log(L_DEBUG, "Sending window::%s event", change);
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "change", json_object_new_string(change));
- json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window));
-
- const char *json_string = json_object_to_json_string(obj);
- ipc_send_event(json_string, IPC_EVENT_WINDOW);
-
- json_object_put(obj); // free
-}
-
-void ipc_event_barconfig_update(struct bar_config *bar) {
- sway_log(L_DEBUG, "Sending barconfig_update event");
- json_object *json = ipc_json_describe_bar_config(bar);
- const char *json_string = json_object_to_json_string(json);
- ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE);
-
- json_object_put(json); // free
-}
-
-void ipc_event_mode(const char *mode) {
- sway_log(L_DEBUG, "Sending mode::%s event", mode);
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "change", json_object_new_string(mode));
-
- const char *json_string = json_object_to_json_string(obj);
- ipc_send_event(json_string, IPC_EVENT_MODE);
-
- json_object_put(obj); // free
-}
-
-void ipc_event_modifier(uint32_t modifier, const char *state) {
- sway_log(L_DEBUG, "Sending modifier::%s event", state);
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "change", json_object_new_string(state));
-
- const char *modifier_name = get_modifier_name_by_mask(modifier);
- json_object_object_add(obj, "modifier", json_object_new_string(modifier_name));
-
- const char *json_string = json_object_to_json_string(obj);
- ipc_send_event(json_string, IPC_EVENT_MODIFIER);
-
- json_object_put(obj); // free
-}
-
-static void ipc_event_binding(json_object *sb_obj) {
- sway_log(L_DEBUG, "Sending binding::run event");
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "change", json_object_new_string("run"));
- json_object_object_add(obj, "binding", sb_obj);
-
- const char *json_string = json_object_to_json_string(obj);
- ipc_send_event(json_string, IPC_EVENT_BINDING);
-
- json_object_put(obj); // free
-}
-
-void ipc_event_binding_keyboard(struct sway_binding *sb) {
- json_object *sb_obj = json_object_new_object();
- json_object_object_add(sb_obj, "command", json_object_new_string(sb->command));
-
- const char *names[10];
-
- int len = get_modifier_names(names, sb->modifiers);
- int i;
- json_object *modifiers = json_object_new_array();
- for (i = 0; i < len; ++i) {
- json_object_array_add(modifiers, json_object_new_string(names[i]));
- }
-
- json_object_object_add(sb_obj, "event_state_mask", modifiers);
-
- json_object *input_codes = json_object_new_array();
- int input_code = 0;
- json_object *symbols = json_object_new_array();
- json_object *symbol = NULL;
-
- if (sb->bindcode) { // bindcode: populate input_codes
- uint32_t keycode;
- for (i = 0; i < sb->keys->length; ++i) {
- keycode = *(uint32_t *)sb->keys->items[i];
- json_object_array_add(input_codes, json_object_new_int(keycode));
- if (i == 0) {
- input_code = keycode;
- }
- }
- } else { // bindsym: populate symbols
- uint32_t keysym;
- char buffer[64];
- for (i = 0; i < sb->keys->length; ++i) {
- keysym = *(uint32_t *)sb->keys->items[i];
- if (xkb_keysym_get_name(keysym, buffer, 64) > 0) {
- json_object *str = json_object_new_string(buffer);
- json_object_array_add(symbols, str);
- if (i == 0) {
- symbol = str;
- }
- }
- }
- }
-
- json_object_object_add(sb_obj, "input_codes", input_codes);
- json_object_object_add(sb_obj, "input_code", json_object_new_int(input_code));
- json_object_object_add(sb_obj, "symbols", symbols);
- json_object_object_add(sb_obj, "symbol", symbol);
- json_object_object_add(sb_obj, "input_type", json_object_new_string("keyboard"));
-
- ipc_event_binding(sb_obj);
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <stdlib.h>
-#include <stdbool.h>
-#include <math.h>
-#include <wlc/wlc.h>
-#include "sway/extensions.h"
-#include "sway/config.h"
-#include "sway/container.h"
-#include "sway/workspace.h"
-#include "sway/focus.h"
-#include "sway/output.h"
-#include "sway/ipc-server.h"
-#include "sway/border.h"
-#include "sway/layout.h"
-#include "list.h"
-#include "log.h"
-
-swayc_t root_container;
-swayc_t *current_focus;
-list_t *scratchpad;
-
-int min_sane_h = 60;
-int min_sane_w = 100;
-
-void init_layout(void) {
- root_container.id = 0; // normally assigned in new_swayc()
- root_container.type = C_ROOT;
- root_container.layout = L_NONE;
- root_container.name = strdup("root");
- root_container.children = create_list();
- root_container.handle = -1;
- root_container.visible = true;
- current_focus = &root_container;
- scratchpad = create_list();
-}
-
-int index_child(const swayc_t *child) {
- swayc_t *parent = child->parent;
- int i, len;
- if (!child->is_floating) {
- len = parent->children->length;
- for (i = 0; i < len; ++i) {
- if (parent->children->items[i] == child) {
- break;
- }
- }
- } else {
- len = parent->floating->length;
- for (i = 0; i < len; ++i) {
- if (parent->floating->items[i] == child) {
- break;
- }
- }
- }
- if (!sway_assert(i < len, "Stray container")) {
- return -1;
- }
- return i;
-}
-
-void add_child(swayc_t *parent, swayc_t *child) {
- sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
- child->width, child->height, parent, parent->type, parent->width, parent->height);
- list_add(parent->children, child);
- child->parent = parent;
- // set focus for this container
- if (!parent->focused) {
- parent->focused = child;
- }
- if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
- child = new_container(child, parent->workspace_layout);
- }
-}
-
-static double *get_height(swayc_t *cont) {
- return &cont->height;
-}
-
-static double *get_width(swayc_t *cont) {
- return &cont->width;
-}
-
-void insert_child(swayc_t *parent, swayc_t *child, int index) {
- if (index > parent->children->length) {
- index = parent->children->length;
- }
- if (index < 0) {
- index = 0;
- }
- list_insert(parent->children, index, child);
- child->parent = parent;
- if (!parent->focused) {
- parent->focused = child;
- }
- if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
- child = new_container(child, parent->workspace_layout);
- }
- if (is_auto_layout(parent->layout)) {
- /* go through each group, adjust the size of the first child of each group */
- double *(*get_maj_dim)(swayc_t *cont);
- double *(*get_min_dim)(swayc_t *cont);
- if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
- get_maj_dim = get_width;
- get_min_dim = get_height;
- } else {
- get_maj_dim = get_height;
- get_min_dim = get_width;
- }
- for (int i = index; i < parent->children->length;) {
- int start = auto_group_start_index(parent, i);
- int end = auto_group_end_index(parent, i);
- swayc_t *first = parent->children->items[start];
- if (start + 1 < parent->children->length) {
- /* preserve the group's dimension along major axis */
- *get_maj_dim(first) = *get_maj_dim(parent->children->items[start + 1]);
- } else {
- /* new group, let the apply_layout handle it */
- first->height = first->width = 0;
- break;
- }
- double remaining = *get_min_dim(parent);
- for (int j = end - 1; j > start; --j) {
- swayc_t *sibling = parent->children->items[j];
- if (sibling == child) {
- /* the inserted child won't yet have its minor
- dimension set */
- remaining -= *get_min_dim(parent) / (end - start);
- } else {
- remaining -= *get_min_dim(sibling);
- }
- }
- *get_min_dim(first) = remaining;
- i = end;
- }
- }
-}
-
-void add_floating(swayc_t *ws, swayc_t *child) {
- sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
- child->width, child->height, ws, ws->type, ws->width, ws->height);
- if (!sway_assert(ws->type == C_WORKSPACE, "Must be of workspace type")) {
- return;
- }
- list_add(ws->floating, child);
- child->parent = ws;
- child->is_floating = true;
- if (!ws->focused) {
- ws->focused = child;
- }
- ipc_event_window(child, "floating");
-}
-
-swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) {
- swayc_t *parent = fixed->parent;
- if (fixed->is_floating) {
- if (active->is_floating) {
- int i = index_child(fixed);
- list_insert(parent->floating, i + 1, active);
- } else {
- list_add(parent->children, active);
- }
- } else {
- if (active->is_floating) {
- list_add(parent->floating, active);
- } else {
- int i = index_child(fixed);
- if (is_auto_layout(parent->layout)) {
- list_add(parent->children, active);
- } else {
- list_insert(parent->children, i + 1, active);
- }
- }
- }
- active->parent = parent;
- // focus new child
- parent->focused = active;
- return active->parent;
-}
-
-swayc_t *replace_child(swayc_t *child, swayc_t *new_child) {
- swayc_t *parent = child->parent;
- if (parent == NULL) {
- return NULL;
- }
- int i = index_child(child);
- if (child->is_floating) {
- parent->floating->items[i] = new_child;
- } else {
- parent->children->items[i] = new_child;
- }
- // Set parent and focus for new_child
- new_child->parent = child->parent;
- if (child->parent->focused == child) {
- child->parent->focused = new_child;
- }
- child->parent = NULL;
-
- // Set geometry for new child
- new_child->x = child->x;
- new_child->y = child->y;
- new_child->width = child->width;
- new_child->height = child->height;
-
- // reset geometry for child
- child->width = 0;
- child->height = 0;
-
- // deactivate child
- if (child->type == C_VIEW) {
- wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
- }
- return parent;
-}
-
-swayc_t *remove_child(swayc_t *child) {
- int i;
- swayc_t *parent = child->parent;
- if (child->is_floating) {
- // Special case for floating views
- for (i = 0; i < parent->floating->length; ++i) {
- if (parent->floating->items[i] == child) {
- list_del(parent->floating, i);
- break;
- }
- }
- i = 0;
- } else {
- for (i = 0; i < parent->children->length; ++i) {
- if (parent->children->items[i] == child) {
- list_del(parent->children, i);
- break;
- }
- }
- if (is_auto_layout(parent->layout) && parent->children->length) {
- /* go through each group, adjust the size of the last child of each group */
- double *(*get_maj_dim)(swayc_t *cont);
- double *(*get_min_dim)(swayc_t *cont);
- if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
- get_maj_dim = get_width;
- get_min_dim = get_height;
- } else {
- get_maj_dim = get_height;
- get_min_dim = get_width;
- }
- for (int j = parent->children->length - 1; j >= i;) {
- int start = auto_group_start_index(parent, j);
- int end = auto_group_end_index(parent, j);
- swayc_t *first = parent->children->items[start];
- if (i == start) {
- /* removed element was first child in the current group,
- use its size along the major axis */
- *get_maj_dim(first) = *get_maj_dim(child);
- } else if (start > i) {
- /* preserve the group's dimension along major axis */
- *get_maj_dim(first) = *get_maj_dim(parent->children->items[start - 1]);
- }
- if (end != parent->children->length) {
- double remaining = *get_min_dim(parent);
- for (int k = start; k < end - 1; ++k) {
- swayc_t *sibling = parent->children->items[k];
- remaining -= *get_min_dim(sibling);
- }
- /* last element of the group gets remaining size, elements
- that don't change groups keep their ratio */
- *get_min_dim((swayc_t *) parent->children->items[end - 1]) = remaining;
- } /* else last group, let apply_layout handle it */
- j = start - 1;
- }
- }
- }
- // Set focused to new container
- if (parent->focused == child) {
- if (parent->children->length > 0) {
- parent->focused = parent->children->items[i ? i-1:0];
- } else if (parent->floating && parent->floating->length) {
- parent->focused = parent->floating->items[parent->floating->length - 1];
- } else {
- parent->focused = NULL;
- }
- }
- child->parent = NULL;
- // deactivate view
- if (child->type == C_VIEW) {
- wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
- }
- return parent;
-}
-
-void swap_container(swayc_t *a, swayc_t *b) {
- if (!sway_assert(a&&b, "parameters must be non null") ||
- !sway_assert(a->parent && b->parent, "containers must have parents")) {
- return;
- }
- size_t a_index = index_child(a);
- size_t b_index = index_child(b);
- swayc_t *a_parent = a->parent;
- swayc_t *b_parent = b->parent;
- // Swap the pointers
- if (a->is_floating) {
- a_parent->floating->items[a_index] = b;
- } else {
- a_parent->children->items[a_index] = b;
- }
- if (b->is_floating) {
- b_parent->floating->items[b_index] = a;
- } else {
- b_parent->children->items[b_index] = a;
- }
- a->parent = b_parent;
- b->parent = a_parent;
- if (a_parent->focused == a) {
- a_parent->focused = b;
- }
- // don't want to double switch
- if (b_parent->focused == b && a_parent != b_parent) {
- b_parent->focused = a;
- }
-}
-
-void swap_geometry(swayc_t *a, swayc_t *b) {
- double x = a->x;
- double y = a->y;
- double w = a->width;
- double h = a->height;
- a->x = b->x;
- a->y = b->y;
- a->width = b->width;
- a->height = b->height;
- b->x = x;
- b->y = y;
- b->width = w;
- b->height = h;
-}
-
-static void swap_children(swayc_t *container, int a, int b) {
- if (a >= 0 && b >= 0 && a < container->children->length
- && b < container->children->length
- && a != b) {
- swayc_t *pa = (swayc_t *)container->children->items[a];
- swayc_t *pb = (swayc_t *)container->children->items[b];
- container->children->items[a] = container->children->items[b];
- container->children->items[b] = pa;
- if (is_auto_layout(container->layout)) {
- size_t ga = auto_group_index(container, a);
- size_t gb = auto_group_index(container, b);
- if (ga != gb) {
- swap_geometry(pa, pb);
- }
- }
- }
-}
-
-void move_container(swayc_t *container, enum movement_direction dir, int move_amt) {
- enum swayc_layouts layout = L_NONE;
- swayc_t *parent = container->parent;
- if (container->is_floating) {
- swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
- switch(dir) {
- case MOVE_LEFT:
- container->x = MAX(0, container->x - move_amt);
- break;
- case MOVE_RIGHT:
- container->x = MIN(output->width - container->width, container->x + move_amt);
- break;
- case MOVE_UP:
- container->y = MAX(0, container->y - move_amt);
- break;
- case MOVE_DOWN:
- container->y = MIN(output->height - container->height, container->y + move_amt);
- break;
- default:
- break;
- }
- update_geometry(container);
- return;
- }
- if (container->type != C_VIEW && container->type != C_CONTAINER) {
- return;
- }
- if (dir == MOVE_UP || dir == MOVE_DOWN) {
- layout = L_VERT;
- } else if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
- layout = L_HORIZ;
- } else if (dir == MOVE_FIRST) {
- // swap first child in auto layout with currently focused child
- if (is_auto_layout(parent->layout)) {
- int focused_idx = index_child(container);
- swayc_t *first = parent->children->items[0];
- if (focused_idx > 0) {
- list_swap(parent->children, 0, focused_idx);
- swap_geometry(first, container);
- }
- arrange_windows(parent->parent, -1, -1);
- ipc_event_window(container, "move");
- set_focused_container_for(parent->parent, container);
- }
- return;
- } else if (! (dir == MOVE_NEXT || dir == MOVE_PREV)) {
- return;
- }
- swayc_t *child = container;
- bool ascended = false;
-
- // View is wrapped in intermediate container which is needed for displaying
- // the titlebar. Moving only the view outside of its parent container would just
- // wrap it again under worspace. There would effectively be no movement,
- // just a change of wrapping container.
- if (child->type == C_VIEW &&
- parent->type == C_CONTAINER &&
- parent->children->length == 1 &&
- parent->parent->type == C_WORKSPACE) {
- child = parent;
- parent = parent->parent;
- }
-
- while (true) {
- sway_log(L_DEBUG, "container:%p, parent:%p, child %p,",
- container,parent,child);
- if (parent->layout == layout
- || (layout == L_NONE && (parent->type == C_CONTAINER || parent->type == C_WORKSPACE)) /* accept any layout for next/prev direction */
- || (parent->layout == L_TABBED && layout == L_HORIZ)
- || (parent->layout == L_STACKED && layout == L_VERT)
- || is_auto_layout(parent->layout)) {
- int diff;
- // If it has ascended (parent has moved up), no container is removed
- // so insert it at index, or index+1.
- // if it has not, the moved container is removed, so it needs to be
- // inserted at index-1, or index+1
- if (ascended) {
- diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? 0 : 1;
- } else {
- diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? -1 : 1;
- }
- int idx = index_child(child);
- int desired = idx + diff;
- if (dir == MOVE_NEXT || dir == MOVE_PREV) {
- // Next/Prev always wrap.
- if (desired < 0) {
- desired += parent->children->length;
- } else if (desired >= parent->children->length) {
- desired = 0;
- }
- }
- // when it has ascended, legal insertion position is 0:len
- // when it has not, legal insertion position is 0:len-1
- if (desired >= 0 && desired - ascended < parent->children->length) {
- if (!ascended) {
- child = parent->children->items[desired];
- // Move container into sibling container
- if (child->type == C_CONTAINER) {
- parent = child;
- // Insert it in first/last if matching layout, otherwise
- // insert it next to focused container
- if (parent->layout == layout
- || (parent->layout == L_TABBED && layout == L_HORIZ)
- || (parent->layout == L_STACKED && layout == L_VERT)
- || is_auto_layout(parent->layout)) {
- desired = (diff < 0) * parent->children->length;
- } else {
- desired = index_child(child->focused) + 1;
- }
- //reset geometry
- container->width = container->height = 0;
- }
- }
- if (container->parent == parent) {
- swap_children(parent, idx, desired);
- } else {
- swayc_t *old_parent = remove_child(container);
- insert_child(parent, container, desired);
- destroy_container(old_parent);
- sway_log(L_DEBUG,"Moving to %p %d", parent, desired);
- }
- break;
- }
- }
- // Change parent layout if we need to
- if (parent->children->length == 1 && parent->layout != layout && layout != L_NONE) {
- /* swayc_change_layout(parent, layout); */
- parent->layout = layout;
- continue;
- }
- if (parent->type == C_WORKSPACE) {
- // If moving to an adjacent output we need a starting position (since this
- // output might border to multiple outputs).
- struct wlc_point abs_pos;
- get_absolute_center_position(container, &abs_pos);
-
- swayc_t *output = swayc_adjacent_output(parent->parent, dir, &abs_pos, true);
-
- if (output) {
- sway_log(L_DEBUG, "Moving between outputs");
- swayc_t *old_parent = remove_child(container);
- destroy_container(old_parent);
-
- swayc_t *dest = output->focused;
- switch (dir) {
- case MOVE_LEFT:
- case MOVE_UP:
- // reset container geometry
- container->width = container->height = 0;
- add_child(dest, container);
- break;
- case MOVE_RIGHT:
- case MOVE_DOWN:
- // reset container geometry
- container->width = container->height = 0;
- insert_child(dest, container, 0);
- break;
- default:
- break;
- }
- // arrange new workspace
- arrange_windows(dest, -1, -1);
- set_focused_container(container);
- break;
- }
-
- // We simply cannot move any further.
- if (parent->layout == layout) {
- break;
- }
- // Create container around workspace to insert child into
- parent = new_container(parent, layout);
- // Previous line set the resulting container's layout to
- // workspace_layout. It should have been just layout.
- parent->layout = parent->parent->layout;
- }
- ascended = true;
- child = parent;
- parent = child->parent;
- }
- arrange_windows(parent->parent, -1, -1);
- ipc_event_window(container, "move");
- set_focused_container_for(parent->parent, container);
-}
-
-void move_container_to(swayc_t* container, swayc_t* destination) {
- if (container == destination || swayc_is_parent_of(container, destination)) {
- return;
- }
- swayc_t *parent = remove_child(container);
- // Send to new destination
- if (container->is_floating) {
- swayc_t *ws = swayc_active_workspace_for(destination);
- add_floating(ws, container);
-
- // If the workspace only has one child after adding one, it
- // means that the workspace was just initialized.
- if (ws->children->length + ws->floating->length == 1) {
- ipc_event_workspace(NULL, ws, "init");
- }
- } else if (destination->type == C_WORKSPACE) {
- // reset container geometry
- container->width = container->height = 0;
- add_child(destination, container);
-
- // If the workspace only has one child after adding one, it
- // means that the workspace was just initialized.
- if (destination->children->length + destination->floating->length == 1) {
- ipc_event_workspace(NULL, destination, "init");
- }
- } else {
- // reset container geometry
- container->width = container->height = 0;
- add_sibling(destination, container);
- }
- // Destroy old container if we need to
- parent = destroy_container(parent);
- // Refocus
- swayc_t *op1 = swayc_parent_by_type(destination, C_OUTPUT);
- swayc_t *op2 = swayc_parent_by_type(parent, C_OUTPUT);
- set_focused_container(get_focused_view(op1));
- arrange_windows(op1, -1, -1);
- update_visibility(op1);
- if (op1 != op2) {
- set_focused_container(get_focused_view(op2));
- arrange_windows(op2, -1, -1);
- update_visibility(op2);
- }
-}
-
-void move_workspace_to(swayc_t* workspace, swayc_t* destination) {
- if (workspace == destination || swayc_is_parent_of(workspace, destination)) {
- return;
- }
- swayc_t *src_op = remove_child(workspace);
- // reset container geometry
- workspace->width = workspace->height = 0;
- add_child(destination, workspace);
- sort_workspaces(destination);
- // Refocus destination (change to new workspace)
- set_focused_container(get_focused_view(workspace));
- arrange_windows(destination, -1, -1);
- update_visibility(destination);
-
- // make sure source output has a workspace
- if (src_op->children->length == 0) {
- char *ws_name = workspace_next_name(src_op->name);
- swayc_t *ws = new_workspace(src_op, ws_name);
- ws->is_focused = true;
- free(ws_name);
- }
- set_focused_container(get_focused_view(src_op));
- update_visibility(src_op);
-}
-
-static void adjust_border_geometry(swayc_t *c, struct wlc_geometry *g,
- const struct wlc_size *res, int left, int right, int top, int bottom) {
-
- g->size.w += left + right;
- if (g->origin.x - left < 0) {
- g->size.w += g->origin.x - left;
- } else if (g->origin.x + g->size.w - right > res->w) {
- g->size.w = res->w - g->origin.x + right;
- }
-
- g->size.h += top + bottom;
- if (g->origin.y - top < 0) {
- g->size.h += g->origin.y - top;
- } else if (g->origin.y + g->size.h - top > res->h) {
- g->size.h = res->h - g->origin.y + top;
- }
-
- g->origin.x = MIN((uint32_t)MAX(g->origin.x - left, 0), res->w);
- g->origin.y = MIN((uint32_t)MAX(g->origin.y - top, 0), res->h);
-
-}
-
-static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geometry) {
- struct wlc_geometry g = *geometry;
- c->actual_geometry = g;
-
- swayc_t *output = swayc_parent_by_type(c, C_OUTPUT);
- struct wlc_size res;
- output_get_scaled_size(output->handle, &res);
-
- switch (c->border_type) {
- case B_NONE:
- break;
- case B_PIXEL:
- adjust_border_geometry(c, &g, &res, c->border_thickness,
- c->border_thickness, c->border_thickness, c->border_thickness);
- break;
- case B_NORMAL:
- {
- int title_bar_height = config->font_height + 4; // borders + padding
-
- adjust_border_geometry(c, &g, &res, c->border_thickness,
- c->border_thickness, title_bar_height, c->border_thickness);
-
- struct wlc_geometry title_bar = {
- .origin = {
- .x = c->actual_geometry.origin.x - c->border_thickness,
- .y = c->actual_geometry.origin.y - title_bar_height
- },
- .size = {
- .w = c->actual_geometry.size.w + (2 * c->border_thickness),
- .h = title_bar_height
- }
- };
- c->title_bar_geometry = title_bar;
- break;
- }
- }
-
- c->border_geometry = g;
- *geometry = c->actual_geometry;
-
- update_container_border(c);
-}
-
-void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout) {
- switch (parent->layout) {
- case L_TABBED:
- case L_STACKED:
- if (prev_layout != L_TABBED && prev_layout != L_STACKED) {
- // cache current geometry for all non-float children
- int i;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *child = parent->children->items[i];
- child->cached_geometry.origin.x = child->x;
- child->cached_geometry.origin.y = child->y;
- child->cached_geometry.size.w = child->width;
- child->cached_geometry.size.h = child->height;
- }
- }
- break;
- default:
- if (prev_layout == L_TABBED || prev_layout == L_STACKED) {
- // recover cached geometry for all non-float children
- int i;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *child = parent->children->items[i];
- // only recoverer cached geometry if non-zero
- if (!wlc_geometry_equals(&child->cached_geometry, &wlc_geometry_zero)) {
- child->x = child->cached_geometry.origin.x;
- child->y = child->cached_geometry.origin.y;
- child->width = child->cached_geometry.size.w;
- child->height = child->cached_geometry.size.h;
- }
- }
- }
- break;
- }
-}
-
-static int update_gap_geometry(swayc_t *container, struct wlc_geometry *g) {
- swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE);
- swayc_t *op = ws->parent;
- int gap = container->is_floating ? 0 : swayc_gap(container);
- if (gap % 2 != 0) {
- // because gaps are implemented as "half sized margins" it's currently
- // not possible to align views properly with odd sized gaps.
- gap -= 1;
- }
-
- g->origin.x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1;
- g->origin.y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1;
- g->size.w = container->width > gap ? container->width - gap : 1;
- g->size.h = container->height > gap ? container->height - gap : 1;
-
- if ((!config->edge_gaps && gap > 0) || (config->smart_gaps && ws->children->length == 1)) {
- // Remove gap against the workspace edges. Because a pixel is not
- // divisable, depending on gap size and the number of siblings our view
- // might be at the workspace edge without being exactly so (thus test
- // with gap, and align correctly).
- if (container->x - gap <= ws->x) {
- g->origin.x = ws->x;
- g->size.w = container->width - gap/2;
- }
- if (container->y - gap <= ws->y) {
- g->origin.y = ws->y;
- g->size.h = container->height - gap/2;
- }
- if (container->x + container->width + gap >= ws->x + ws->width) {
- g->size.w = ws->x + ws->width - g->origin.x;
- }
- if (container->y + container->height + gap >= ws->y + ws->height) {
- g->size.h = ws->y + ws->height - g->origin.y;
- }
- }
-
- return gap;
-}
-
-void update_geometry(swayc_t *container) {
- if (container->type != C_VIEW && container->type != C_CONTAINER) {
- return;
- }
-
- swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE);
- swayc_t *op = workspace->parent;
- swayc_t *parent = container->parent;
-
- struct wlc_geometry geometry = {
- .origin = {
- .x = container->x < op->width ? container->x : op->width-1,
- .y = container->y < op->height ? container->y : op->height-1
- },
- .size = {
- .w = container->width,
- .h = container->height,
- }
- };
-
- int gap = 0;
-
- // apply inner gaps to non-tabbed/stacked containers
- swayc_t *p = swayc_tabbed_stacked_ancestor(container);
- if (p == NULL) {
- gap = update_gap_geometry(container, &geometry);
- }
-
- swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
- struct wlc_size size;
- output_get_scaled_size(output->handle, &size);
-
- if (swayc_is_fullscreen(container)) {
- geometry.origin.x = 0;
- geometry.origin.y = 0;
- geometry.size.w = size.w;
- geometry.size.h = size.h;
- if (op->focused == workspace) {
- wlc_view_bring_to_front(container->handle);
- }
-
- container->border_geometry = wlc_geometry_zero;
- container->title_bar_geometry = wlc_geometry_zero;
- border_clear(container->border);
- } else if (container->is_floating) { // allocate border for floating window
- update_border_geometry_floating(container, &geometry);
- } else if (!container->is_floating) { // allocate border for titled window
- container->border_geometry = geometry;
-
- int border_top = container->border_thickness;
- int border_bottom = container->border_thickness;
- int border_left = container->border_thickness;
- int border_right = container->border_thickness;
-
- // handle hide_edge_borders
- if (config->hide_edge_borders != E_NONE && (gap <= 0 || (config->smart_gaps && workspace->children->length == 1))) {
- if (config->hide_edge_borders == E_VERTICAL || config->hide_edge_borders == E_BOTH) {
- if (geometry.origin.x == workspace->x) {
- border_left = 0;
- }
-
- if (geometry.origin.x + geometry.size.w == workspace->x + workspace->width) {
- border_right = 0;
- }
- }
-
- if (config->hide_edge_borders == E_HORIZONTAL || config->hide_edge_borders == E_BOTH) {
- if (geometry.origin.y == workspace->y || should_hide_top_border(container, geometry.origin.y)) {
- border_top = 0;
- }
-
- if (geometry.origin.y + geometry.size.h == workspace->y + workspace->height) {
- border_bottom = 0;
- }
- }
-
- if (config->hide_edge_borders == E_SMART && workspace->children->length == 1) {
- border_top = 0;
- border_bottom = 0;
- border_left = 0;
- border_right = 0;
- }
- }
-
- int title_bar_height = config->font_height + 4; //borders + padding
-
- if (parent->layout == L_TABBED && parent->children->length > 1) {
- int i, x = 0, w, l, r;
- l = parent->children->length;
- w = geometry.size.w / l;
- r = geometry.size.w % l;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *view = parent->children->items[i];
- if (view == container) {
- x = w * i;
- if (i == l - 1) {
- w += r;
- }
- break;
- }
- }
-
- struct wlc_geometry title_bar = {
- .origin = {
- .x = container->border_geometry.origin.x + x,
- .y = container->border_geometry.origin.y
- },
- .size = {
- .w = w,
- .h = title_bar_height
- }
- };
- geometry.origin.x += border_left;
- geometry.origin.y += title_bar.size.h;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_bottom + title_bar.size.h);
- container->title_bar_geometry = title_bar;
- } else if (parent->layout == L_STACKED && parent->children->length > 1) {
- int i, y = 0;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *view = parent->children->items[i];
- if (view == container) {
- y = title_bar_height * i;
- }
- }
-
- struct wlc_geometry title_bar = {
- .origin = {
- .x = container->border_geometry.origin.x,
- .y = container->border_geometry.origin.y + y
- },
- .size = {
- .w = container->border_geometry.size.w,
- .h = title_bar_height
- }
- };
- title_bar_height = title_bar_height * parent->children->length;
- geometry.origin.x += border_left;
- geometry.origin.y += title_bar_height;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_bottom + title_bar_height);
- container->title_bar_geometry = title_bar;
- } else {
- switch (container->border_type) {
- case B_NONE:
- break;
- case B_PIXEL:
- geometry.origin.x += border_left;
- geometry.origin.y += border_top;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_top + border_bottom);
- break;
- case B_NORMAL:
- {
- struct wlc_geometry title_bar = {
- .origin = {
- .x = container->border_geometry.origin.x,
- .y = container->border_geometry.origin.y
- },
- .size = {
- .w = container->border_geometry.size.w,
- .h = title_bar_height
- }
- };
- geometry.origin.x += border_left;
- geometry.origin.y += title_bar.size.h;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_bottom + title_bar.size.h);
- container->title_bar_geometry = title_bar;
- break;
- }
- }
- }
-
- container->actual_geometry = geometry;
-
- if (container->type == C_VIEW) {
- update_container_border(container);
- }
- }
-
- if (container->type == C_VIEW) {
- wlc_view_set_geometry(container->handle, 0, &geometry);
- }
-}
-
-/**
- * Layout application prototypes
- */
-static void apply_horiz_layout(swayc_t *container, const double x,
- const double y, const double width,
- const double height, const int start,
- const int end);
-static void apply_vert_layout(swayc_t *container, const double x,
- const double y, const double width,
- const double height, const int start,
- const int end);
-static void apply_tabbed_or_stacked_layout(swayc_t *container, double x,
- double y, double width,
- double height);
-
-static void apply_auto_layout(swayc_t *container, const double x, const double y,
- const double width, const double height,
- enum swayc_layouts group_layout,
- bool master_first);
-
-static void arrange_windows_r(swayc_t *container, double width, double height) {
- int i;
- if (width == -1 || height == -1) {
- swayc_log(L_DEBUG, container, "Arranging layout for %p", container);
- width = container->width;
- height = container->height;
- }
- // pixels are indivisible. if we don't round the pixels, then the view
- // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
- // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
- width = floor(width);
- height = floor(height);
-
- sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
- container->name, container->width, container->height, container->x,
- container->y);
-
- double x = 0, y = 0;
- switch (container->type) {
- case C_ROOT:
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *output = container->children->items[i];
- sway_log(L_DEBUG, "Arranging output '%s' at %f,%f", output->name, output->x, output->y);
- arrange_windows_r(output, -1, -1);
- }
- return;
- case C_OUTPUT:
- {
- struct wlc_size resolution;
- output_get_scaled_size(container->handle, &resolution);
- width = resolution.w; height = resolution.h;
- // output must have correct size due to e.g. seamless mouse,
- // but a workspace might be smaller depending on panels.
- container->width = width;
- container->height = height;
- }
- // arrange all workspaces:
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- arrange_windows_r(child, -1, -1);
- }
- // Bring all unmanaged views to the front
- for (i = 0; i < container->unmanaged->length; ++i) {
- wlc_handle *handle = container->unmanaged->items[i];
- wlc_view_bring_to_front(*handle);
- }
- return;
- case C_WORKSPACE:
- {
- swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
- width = output->width, height = output->height;
- for (i = 0; i < desktop_shell.panels->length; ++i) {
- struct panel_config *config = desktop_shell.panels->items[i];
- if (config->output == output->handle) {
- struct wlc_size size = *wlc_surface_get_size(config->surface);
- sway_log(L_DEBUG, "-> Found panel for this workspace: %ux%u, position: %u", size.w, size.h, config->panel_position);
- switch (config->panel_position) {
- case DESKTOP_SHELL_PANEL_POSITION_TOP:
- y += size.h; height -= size.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
- height -= size.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_LEFT:
- x += size.w; width -= size.w;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
- width -= size.w;
- break;
- }
- }
- }
- int gap = swayc_gap(container);
- x = container->x = x + gap;
- y = container->y = y + gap;
- width = container->width = width - gap * 2;
- height = container->height = height - gap * 2;
- sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y);
- }
- // children are properly handled below
- break;
- case C_VIEW:
- {
- container->width = width;
- container->height = height;
- update_geometry(container);
- sway_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f", container->width,
- container->height, container->x, container->y);
- }
- return;
- default:
- container->width = width;
- container->height = height;
- x = container->x;
- y = container->y;
-
- // add gaps to top level tapped/stacked container
- if (container->parent->type == C_WORKSPACE &&
- (container->layout == L_TABBED || container->layout == L_STACKED)) {
- update_geometry(container);
- width = container->border_geometry.size.w;
- height = container->border_geometry.size.h;
- x = container->border_geometry.origin.x;
- y = container->border_geometry.origin.y;
- }
-
- // update container size if it's a direct child in a tabbed/stacked layout
- // if parent is a workspace, its actual_geometry won't be initialized
- if (swayc_tabbed_stacked_parent(container) != NULL &&
- container->parent->type != C_WORKSPACE) {
- // Use parent actual_geometry as a base for calculating
- // container geometry
- container->width = container->parent->actual_geometry.size.w;
- container->height = container->parent->actual_geometry.size.h;
- container->x = container->parent->actual_geometry.origin.x;
- container->y = container->parent->actual_geometry.origin.y;
-
- update_geometry(container);
- width = container->width = container->actual_geometry.size.w;
- height = container->height = container->actual_geometry.size.h;
- x = container->x = container->actual_geometry.origin.x;
- y = container->y = container->actual_geometry.origin.y;
- }
-
- break;
- }
-
- switch (container->layout) {
- case L_HORIZ:
- default:
- apply_horiz_layout(container, x, y, width, height, 0,
- container->children->length);
- break;
- case L_VERT:
- apply_vert_layout(container, x, y, width, height, 0,
- container->children->length);
- break;
- case L_TABBED:
- case L_STACKED:
- apply_tabbed_or_stacked_layout(container, x, y, width, height);
- break;
- case L_AUTO_LEFT:
- apply_auto_layout(container, x, y, width, height, L_VERT, true);
- break;
- case L_AUTO_RIGHT:
- apply_auto_layout(container, x, y, width, height, L_VERT, false);
- break;
- case L_AUTO_TOP:
- apply_auto_layout(container, x, y, width, height, L_HORIZ, true);
- break;
- case L_AUTO_BOTTOM:
- apply_auto_layout(container, x, y, width, height, L_HORIZ, false);
- break;
- }
-
- // Arrage floating layouts for workspaces last
- if (container->type == C_WORKSPACE) {
- for (int i = 0; i < container->floating->length; ++i) {
- swayc_t *view = container->floating->items[i];
- if (view->type == C_VIEW) {
- update_geometry(view);
- sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f",
- view->width, view->height, view->x, view->y);
- if (swayc_is_fullscreen(view)) {
- wlc_view_bring_to_front(view->handle);
- } else if (!container->focused ||
- !swayc_is_fullscreen(container->focused)) {
- wlc_view_bring_to_front(view->handle);
- }
- }
- }
- }
-}
-
-void apply_horiz_layout(swayc_t *container, const double x, const double y,
- const double width, const double height,
- const int start, const int end) {
- double scale = 0;
- // Calculate total width
- for (int i = start; i < end; ++i) {
- double *old_width = &((swayc_t *)container->children->items[i])->width;
- if (*old_width <= 0) {
- if (end - start > 1) {
- *old_width = width / (end - start - 1);
- } else {
- *old_width = width;
- }
- }
- scale += *old_width;
- }
- scale = width / scale;
-
- // Resize windows
- double child_x = x;
- if (scale > 0.1) {
- sway_log(L_DEBUG, "Arranging %p horizontally", container);
- swayc_t *focused = NULL;
- for (int i = start; i < end; ++i) {
- swayc_t *child = container->children->items[i];
- sway_log(L_DEBUG,
- "Calculating arrangement for %p:%d (will scale %f by %f)", child,
- child->type, width, scale);
- child->x = child_x;
- child->y = y;
-
- if (child == container->focused) {
- focused = child;
- }
-
- if (i == end - 1) {
- double remaining_width = x + width - child_x;
- arrange_windows_r(child, remaining_width, height);
- } else {
- arrange_windows_r(child, child->width * scale, height);
- }
- child_x += child->width;
- }
-
- // update focused view border last because it may
- // depend on the title bar geometry of its siblings.
- if (focused && container->children->length > 1) {
- update_container_border(focused);
- }
- }
-}
-
-void apply_vert_layout(swayc_t *container, const double x, const double y,
- const double width, const double height, const int start,
- const int end) {
- int i;
- double scale = 0;
- // Calculate total height
- for (i = start; i < end; ++i) {
- double *old_height = &((swayc_t *)container->children->items[i])->height;
- if (*old_height <= 0) {
- if (end - start > 1) {
- *old_height = height / (end - start - 1);
- } else {
- *old_height = height;
- }
- }
- scale += *old_height;
- }
- scale = height / scale;
-
- // Resize
- double child_y = y;
- if (scale > 0.1) {
- sway_log(L_DEBUG, "Arranging %p vertically", container);
- swayc_t *focused = NULL;
- for (i = start; i < end; ++i) {
- swayc_t *child = container->children->items[i];
- sway_log(L_DEBUG,
- "Calculating arrangement for %p:%d (will scale %f by %f)", child,
- child->type, height, scale);
- child->x = x;
- child->y = child_y;
-
- if (child == container->focused) {
- focused = child;
- }
-
- if (i == end - 1) {
- double remaining_height = y + height - child_y;
- arrange_windows_r(child, width, remaining_height);
- } else {
- arrange_windows_r(child, width, child->height * scale);
- }
- child_y += child->height;
- }
-
- // update focused view border last because it may
- // depend on the title bar geometry of its siblings.
- if (focused && container->children->length > 1) {
- update_container_border(focused);
- }
- }
-}
-
-void apply_tabbed_or_stacked_layout(swayc_t *container, double x, double y,
- double width, double height) {
- int i;
- swayc_t *focused = NULL;
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- child->x = x;
- child->y = y;
- if (child == container->focused) {
- focused = child;
- } else {
- arrange_windows_r(child, width, height);
- }
- }
-
- if (focused) {
- arrange_windows_r(focused, width, height);
- }
-}
-
-void apply_auto_layout(swayc_t *container, const double x, const double y,
- const double width, const double height,
- enum swayc_layouts group_layout,
- bool master_first) {
- // Auto layout "container" in width x height @ x, y
- // using "group_layout" for each of the groups in the container.
- // There is one "master" group, plus container->nb_slave_groups.
- // Each group is layed out side by side following the "major" axis.
- // The direction of the layout used for groups is the "minor" axis.
- // Example:
- //
- // ---- major axis -->
- // +---------+-----------+
- // | | | |
- // | master | slave 1 | |
- // | +-----------+ | minor axis (direction of group_layout)
- // | | | |
- // | | slave 2 | V
- // +---------+-----------+
- //
- // container with three children (one master and two slaves) and
- // a single slave group (containing slave 1 and 2). The master
- // group and slave group are layed out using L_VERT.
-
- size_t nb_groups = auto_group_count(container);
-
- // the target dimension of the container along the "major" axis, each
- // group in the container will be layed out using "group_layout" along
- // the "minor" axis.
- double dim_maj;
- double pos_maj;
-
- // x and y coords for the next group to be laid out.
- const double *group_x, *group_y;
-
- // pos of the next group to layout along the major axis
- double pos;
-
- // size of the next group along the major axis.
- double group_dim;
-
- // height and width of next group to be laid out.
- const double *group_h, *group_w;
-
- switch (group_layout) {
- default:
- sway_log(L_DEBUG, "Unknown layout type (%d) used in %s()",
- group_layout, __func__);
- /* fall through */
- case L_VERT:
- dim_maj = width;
- pos_maj = x;
-
- group_x = &pos;
- group_y = &y;
- group_w = &group_dim;
- group_h = &height;
- break;
- case L_HORIZ:
- dim_maj = height;
- pos_maj = y;
-
- group_x = &x;
- group_y = &pos;
- group_w = &width;
- group_h = &group_dim;
- break;
- }
-
- /* Determine the dimension of each of the groups in the layout.
- * Dimension will be width for a VERT layout and height for a HORIZ
- * layout. */
- double old_group_dim[nb_groups];
- double old_dim = 0;
- for (size_t group = 0; group < nb_groups; ++group) {
- int idx;
- if (auto_group_bounds(container, group, &idx, NULL)) {
- swayc_t *child = container->children->items[idx];
- double *dim = group_layout == L_HORIZ ? &child->height : &child->width;
- if (*dim <= 0) {
- // New child with uninitialized dimension
- *dim = dim_maj;
- if (nb_groups > 1) {
- // child gets a dimension proportional to existing groups,
- // it will be later scaled based on to the available size
- // in the major axis.
- *dim /= (nb_groups - 1);
- }
- }
- old_dim += *dim;
- old_group_dim[group] = *dim;
- }
- }
- double scale = dim_maj / old_dim;
-
- /* Apply layout to each group */
- pos = pos_maj;
-
- for (size_t group = 0; group < nb_groups; ++group) {
- int start, end; // index of first (inclusive) and last (exclusive) child in the group
- if (auto_group_bounds(container, group, &start, &end)) {
- // adjusted size of the group
- group_dim = old_group_dim[group] * scale;
- if (group == nb_groups - 1) {
- group_dim = pos_maj + dim_maj - pos; // remaining width
- }
- sway_log(L_DEBUG, "Arranging container %p column %zu, children [%d,%d[ (%fx%f+%f,%f)",
- container, group, start, end, *group_w, *group_h, *group_x, *group_y);
- switch (group_layout) {
- default:
- case L_VERT:
- apply_vert_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
- break;
- case L_HORIZ:
- apply_horiz_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
- break;
- }
-
- /* update position for next group */
- pos += group_dim;
- }
- }
-}
-
-void arrange_windows(swayc_t *container, double width, double height) {
- update_visibility(container);
- arrange_windows_r(container, width, height);
- layout_log(&root_container, 0);
-}
-
-void arrange_backgrounds(void) {
- struct background_config *bg;
- for (int i = 0; i < desktop_shell.backgrounds->length; ++i) {
- bg = desktop_shell.backgrounds->items[i];
- wlc_view_send_to_back(bg->handle);
- }
-}
-
-/**
- * Get swayc in the direction of newly entered output.
- */
-static swayc_t *get_swayc_in_output_direction(swayc_t *output, enum movement_direction dir) {
- if (!output) {
- return NULL;
- }
-
- swayc_t *ws = swayc_focus_by_type(output, C_WORKSPACE);
- if (ws && ws->children->length > 0) {
- switch (dir) {
- case MOVE_LEFT:
- // get most right child of new output
- return ws->children->items[ws->children->length-1];
- case MOVE_RIGHT:
- // get most left child of new output
- return ws->children->items[0];
- case MOVE_UP:
- case MOVE_DOWN:
- {
- swayc_t *focused_view = swayc_focus_by_type(ws, C_VIEW);
- if (focused_view && focused_view->parent) {
- swayc_t *parent = focused_view->parent;
- if (parent->layout == L_VERT) {
- if (dir == MOVE_UP) {
- // get child furthest down on new output
- return parent->children->items[parent->children->length-1];
- } else if (dir == MOVE_DOWN) {
- // get child furthest up on new output
- return parent->children->items[0];
- }
- }
- return focused_view;
- }
- break;
- }
- default:
- break;
- }
- }
-
- return output;
-}
-
-swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_direction dir, swayc_t *limit) {
- if (dir == MOVE_CHILD) {
- return container->focused;
- }
-
- swayc_t *parent = container->parent;
- if (dir == MOVE_PARENT) {
- if (parent->type == C_OUTPUT) {
- return NULL;
- } else {
- return parent;
- }
- }
-
- if (dir == MOVE_PREV || dir == MOVE_NEXT) {
- int focused_idx = index_child(container);
- if (focused_idx == -1) {
- return NULL;
- } else {
- int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) %
- parent->children->length;
- if (desired < 0) {
- desired += parent->children->length;
- }
- return parent->children->items[desired];
- }
- }
-
- // If moving to an adjacent output we need a starting position (since this
- // output might border to multiple outputs).
- struct wlc_point abs_pos;
- get_absolute_center_position(container, &abs_pos);
-
- if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
- sway_log(L_DEBUG, "Moving from fullscreen view, skipping to output");
- container = swayc_parent_by_type(container, C_OUTPUT);
- get_absolute_center_position(container, &abs_pos);
- swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
- return get_swayc_in_output_direction(output, dir);
- }
-
- if (container->type == C_WORKSPACE && container->fullscreen) {
- sway_log(L_DEBUG, "Moving to fullscreen view");
- return container->fullscreen;
- }
-
- swayc_t *wrap_candidate = NULL;
- while (true) {
- // Test if we can even make a difference here
- bool can_move = false;
- int desired;
- int idx = index_child(container);
- if (parent->type == C_ROOT) {
- swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
- if (!output || output == container) {
- return wrap_candidate;
- }
- sway_log(L_DEBUG, "Moving between outputs");
- return get_swayc_in_output_direction(output, dir);
- } else {
- if (is_auto_layout(parent->layout)) {
- bool is_major = parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
- ? dir == MOVE_LEFT || dir == MOVE_RIGHT
- : dir == MOVE_DOWN || dir == MOVE_UP;
- size_t gidx = auto_group_index(parent, idx);
- if (is_major) {
- size_t desired_grp = gidx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
- can_move = auto_group_bounds(parent, desired_grp, &desired, NULL);
- } else {
- desired = idx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
- int start, end;
- can_move = auto_group_bounds(parent, gidx, &start, &end)
- && desired >= start && desired < end;
- }
- } else {
- if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
- if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
- can_move = true;
- desired = idx + (dir == MOVE_LEFT ? -1 : 1);
- }
- } else {
- if (parent->layout == L_VERT || parent->layout == L_STACKED) {
- can_move = true;
- desired = idx + (dir == MOVE_UP ? -1 : 1);
- }
- }
- }
- }
-
- if (can_move) {
- if (container->is_floating) {
- if (desired < 0) {
- wrap_candidate = parent->floating->items[parent->floating->length-1];
- } else if (desired >= parent->floating->length){
- wrap_candidate = parent->floating->items[0];
- } else {
- wrap_candidate = parent->floating->items[desired];
- }
- if (wrap_candidate) {
- wlc_view_bring_to_front(wrap_candidate->handle);
- }
- return wrap_candidate;
- } else if (desired < 0 || desired >= parent->children->length) {
- can_move = false;
- int len = parent->children->length;
- if (!wrap_candidate && len > 1) {
- if (desired < 0) {
- wrap_candidate = parent->children->items[len-1];
- } else {
- wrap_candidate = parent->children->items[0];
- }
- if (config->force_focus_wrapping) {
- return wrap_candidate;
- }
- }
- } else {
- sway_log(L_DEBUG, "%s cont %d-%p dir %i sibling %d: %p", __func__,
- idx, container, dir, desired, parent->children->items[desired]);
- return parent->children->items[desired];
- }
- }
- if (!can_move) {
- container = parent;
- parent = parent->parent;
- if (!parent || container == limit) {
- // wrapping is the last chance
- return wrap_candidate;
- }
- }
- }
-}
-
-swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir) {
- return get_swayc_in_direction_under(container, dir, NULL);
-}
-
-void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge edge) {
- int i;
- bool layout_match = true;
- sway_log(L_DEBUG, "Resizing %p with amount: %f", container, amount);
- if (edge == WLC_RESIZE_EDGE_LEFT || edge == WLC_RESIZE_EDGE_RIGHT) {
- container->width += amount;
- layout_match = container->layout == L_HORIZ;
- } else if (edge == WLC_RESIZE_EDGE_TOP || edge == WLC_RESIZE_EDGE_BOTTOM) {
- container->height += amount;
- layout_match = container->layout == L_VERT;
- }
- if (container->type == C_VIEW) {
- update_geometry(container);
- return;
- }
- if (layout_match) {
- for (i = 0; i < container->children->length; i++) {
- recursive_resize(container->children->items[i], amount/container->children->length, edge);
- }
- } else {
- for (i = 0; i < container->children->length; i++) {
- recursive_resize(container->children->items[i], amount, edge);
- }
- }
-}
-
-enum swayc_layouts default_layout(swayc_t *output) {
- if (config->default_layout != L_NONE) {
- return config->default_layout;
- } else if (config->default_orientation != L_NONE) {
- return config->default_orientation;
- } else if (output->width >= output->height) {
- return L_HORIZ;
- } else {
- return L_VERT;
- }
-}
-
-bool is_auto_layout(enum swayc_layouts layout) {
- return (layout >= L_AUTO_FIRST) && (layout <= L_AUTO_LAST);
-}
-
-/**
- * Return the number of master elements in a container
- */
-static inline size_t auto_master_count(const swayc_t *container) {
- sway_assert(container->children->length >= 0, "Container %p has (negative) children %d",
- container, container->children->length);
- return MIN(container->nb_master, (size_t)container->children->length);
-}
-
-/**
- * Return the number of children in the slave groups. This corresponds to the children
- * that are not members of the master group.
- */
-static inline size_t auto_slave_count(const swayc_t *container) {
- return container->children->length - auto_master_count(container);
-}
-
-/**
- * Return the number of slave groups in the container.
- */
-size_t auto_slave_group_count(const swayc_t *container) {
- return MIN(container->nb_slave_groups, auto_slave_count(container));
-}
-
-/**
- * Return the combined number of master and slave groups in the container.
- */
-size_t auto_group_count(const swayc_t *container) {
- return auto_slave_group_count(container)
- + (container->children->length && container->nb_master ? 1 : 0);
-}
-
-/**
- * given the index of a container's child, return the index of the first child of the group
- * which index is a member of.
- */
-int auto_group_start_index(const swayc_t *container, int index) {
- if (index < 0 || ! is_auto_layout(container->layout)
- || (size_t)index < container->nb_master) {
- return 0;
- } else {
- size_t nb_slaves = auto_slave_count(container);
- size_t nb_slave_grp = auto_slave_group_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
- int start_idx;
- if (index < idx2) {
- start_idx = ((index - container->nb_master) / grp_sz) * grp_sz + container->nb_master;
- } else {
- start_idx = idx2 + ((index - idx2) / (grp_sz + 1)) * (grp_sz + 1);
- }
- return MIN(start_idx, container->children->length);
- }
-}
-
-/**
- * given the index of a container's child, return the index of the first child of the group
- * that follows the one which index is a member of.
- * This makes the function usable to walk through the groups in a container.
- */
-int auto_group_end_index(const swayc_t *container, int index) {
- if (index < 0 || ! is_auto_layout(container->layout)) {
- return container->children->length;
- } else {
- int nxt_idx;
- if ((size_t)index < container->nb_master) {
- nxt_idx = auto_master_count(container);
- } else {
- size_t nb_slaves = auto_slave_count(container);
- size_t nb_slave_grp = auto_slave_group_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
- if (index < idx2) {
- nxt_idx = ((index - container->nb_master) / grp_sz + 1) * grp_sz + container->nb_master;
- } else {
- nxt_idx = idx2 + ((index - idx2) / (grp_sz + 1) + 1) * (grp_sz + 1);
- }
- }
- return MIN(nxt_idx, container->children->length);
- }
-}
-
-/**
- * return the index of the Group containing <index>th child of <container>.
- * The index is the order of the group along the container's major axis (starting at 0).
- */
-size_t auto_group_index(const swayc_t *container, int index) {
- if (index < 0) {
- return 0;
- }
- bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
- size_t nb_slaves = auto_slave_count(container);
- if ((size_t)index < container->nb_master) {
- if (master_first || nb_slaves <= 0) {
- return 0;
- } else {
- return auto_slave_group_count(container);
- }
- } else {
- size_t nb_slave_grp = auto_slave_group_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
- size_t grp_idx;
- if (index < idx2) {
- grp_idx = (index - container->nb_master) / grp_sz;
- } else {
- grp_idx = (nb_slave_grp - remainder) + (index - idx2) / (grp_sz + 1) ;
- }
- return grp_idx + (master_first && container-> nb_master ? 1 : 0);
- }
-}
-
-/**
- * Return the first index (inclusive) and last index (exclusive) of the elements of a group in
- * an auto layout.
- * If the bounds of the given group can be calculated, they are returned in the start/end
- * parameters (int pointers) and the return value will be true.
- * The indexes are passed by reference and can be NULL.
- */
-bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end) {
- size_t nb_grp = auto_group_count(container);
- if (group_index >= nb_grp) {
- return false;
- }
- bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
- size_t nb_master = auto_master_count(container);
- size_t nb_slave_grp = auto_slave_group_count(container);
- int g_start, g_end;
- if (nb_master && (master_first ? group_index == 0 : group_index == nb_grp - 1)) {
- g_start = 0;
- g_end = nb_master;
- } else {
- size_t nb_slaves = auto_slave_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- size_t g0 = master_first && container->nb_master ? 1 : 0;
- size_t g1 = g0 + nb_slave_grp - remainder;
- if (group_index < g1) {
- g_start = container->nb_master + (group_index - g0) * grp_sz;
- g_end = g_start + grp_sz;
- } else {
- size_t g2 = group_index - g1;
- g_start = container->nb_master
- + (nb_slave_grp - remainder) * grp_sz
- + g2 * (grp_sz + 1);
- g_end = g_start + grp_sz + 1;
- }
- }
- if (start) {
- *start = g_start;
- }
- if (end) {
- *end = g_end;
- }
- return true;
-}
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 @@
#define _XOPEN_SOURCE 700
#define _POSIX_C_SOURCE 200112L
-#include <stdio.h>
-#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
#include <stdbool.h>
-#include <wlc/wlc.h>
-#include <sys/wait.h>
-#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <sys/un.h>
-#include <signal.h>
#include <unistd.h>
-#include <getopt.h>
#ifdef __linux__
#include <sys/capability.h>
#include <sys/prctl.h>
#endif
-#include "sway/extensions.h"
-#include "sway/layout.h"
+#include <wlr/util/log.h>
#include "sway/config.h"
-#include "sway/security.h"
-#include "sway/handlers.h"
-#include "sway/input.h"
+#include "sway/debug.h"
+#include "sway/server.h"
+#include "sway/tree/layout.h"
#include "sway/ipc-server.h"
#include "ipc-client.h"
#include "readline.h"
#include "stringop.h"
-#include "sway.h"
-#include "log.h"
#include "util.h"
static bool terminate_request = false;
static int exit_value = 0;
+struct sway_server server;
void sway_terminate(int exit_code) {
terminate_request = true;
exit_value = exit_code;
- wlc_terminate();
+ wl_display_terminate(server.wl_display);
}
void sig_handler(int signal) {
- close_views(&root_container);
+ //close_views(&root_container);
sway_terminate(EXIT_SUCCESS);
}
-static void wlc_log_handler(enum wlc_log_type type, const char *str) {
- if (type == WLC_LOG_ERROR) {
- sway_log(L_ERROR, "[wlc] %s", str);
- } else if (type == WLC_LOG_WARN) {
- sway_log(L_INFO, "[wlc] %s", str);
- } else {
- sway_log(L_DEBUG, "[wlc] %s", str);
- }
-}
-
void detect_raspi() {
bool raspi = false;
FILE *f = fopen("/sys/firmware/devicetree/base/model", "r");
@@ -98,23 +86,16 @@ void detect_proprietary() {
if (!f) {
return;
}
- bool nvidia = false, nvidia_modeset = false, nvidia_uvm = false, nvidia_drm = false;
while (!feof(f)) {
char *line;
if (!(line = read_line(f))) {
break;
}
if (strstr(line, "nvidia")) {
- nvidia = true;
- }
- if (strstr(line, "nvidia_modeset")) {
- nvidia_modeset = true;
- }
- if (strstr(line, "nvidia_uvm")) {
- nvidia_uvm = true;
- }
- if (strstr(line, "nvidia_drm")) {
- nvidia_drm = true;
+ fprintf(stderr, "\x1B[1;31mWarning: Proprietary Nvidia drivers are "
+ "NOT supported. Use Nouveau.\x1B[0m\n");
+ free(line);
+ break;
}
if (strstr(line, "fglrx")) {
fprintf(stderr, "\x1B[1;31mWarning: Proprietary AMD drivers do "
@@ -125,52 +106,6 @@ void detect_proprietary() {
free(line);
}
fclose(f);
- if (nvidia) {
- fprintf(stderr, "\x1B[1;31mWarning: Proprietary nvidia driver support "
- "is considered experimental. Nouveau is strongly recommended."
- "\x1B[0m\n");
- if (!nvidia_modeset || !nvidia_uvm || !nvidia_drm) {
- fprintf(stderr, "\x1B[1;31mWarning: You do not have all of the "
- "necessary kernel modules loaded for nvidia support. "
- "You need nvidia, nvidia_modeset, nvidia_uvm, and nvidia_drm."
- "\x1B[0m\n");
- }
-#ifdef __linux__
- f = fopen("/sys/module/nvidia_drm/parameters/modeset", "r");
- if (f) {
- char *line = read_line(f);
- if (line && strstr(line, "Y")) {
- // nvidia-drm.modeset is set to 0
- fprintf(stderr, "\x1B[1;31mWarning: You must load "
- "nvidia-drm with the modeset option on to use "
- "the proprietary driver. Consider adding "
- "nvidia-drm.modeset=1 to your kernel command line "
- "parameters.\x1B[0m\n");
- }
- fclose(f);
- free(line);
- } else {
- // nvidia-drm.modeset is not set
- fprintf(stderr, "\x1B[1;31mWarning: You must load "
- "nvidia-drm with the modeset option on to use "
- "the proprietary driver. Consider adding "
- "nvidia-drm.modeset=1 to your kernel command line "
- "parameters.\x1B[0m\n");
- }
-#else
- f = fopen("/proc/cmdline", "r");
- if (f) {
- char *line = read_line(f);
- if (line && !strstr(line, "nvidia-drm.modeset=1")) {
- fprintf(stderr, "\x1B[1;31mWarning: You must add "
- "nvidia-drm.modeset=1 to your kernel command line to use "
- "the proprietary driver.\x1B[0m\n");
- }
- fclose(f);
- free(line);
- }
-#endif
- }
}
void run_as_ipc_client(char *command, char *socket_path) {
@@ -184,27 +119,15 @@ void run_as_ipc_client(char *command, char *socket_path) {
static void log_env() {
const char *log_vars[] = {
"PATH",
- "LD_LOAD_PATH",
+ "LD_LIBRARY_PATH",
"LD_PRELOAD_PATH",
"LD_LIBRARY_PATH",
"SWAY_CURSOR_THEME",
"SWAY_CURSOR_SIZE",
- "SWAYSOCK",
- "WLC_DRM_DEVICE",
- "WLC_SHM",
- "WLC_OUTPUTS",
- "WLC_XWAYLAND",
- "WLC_LIBINPUT",
- "WLC_REPEAT_DELAY",
- "WLC_REPEAT_RATE",
- "XKB_DEFAULT_RULES",
- "XKB_DEFAULT_MODEL",
- "XKB_DEFAULT_LAYOUT",
- "XKB_DEFAULT_VARIANT",
- "XKB_DEFAULT_OPTIONS",
+ "SWAYSOCK"
};
for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) {
- sway_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i]));
+ wlr_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i]));
}
}
@@ -219,14 +142,14 @@ static void log_distro() {
for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) {
FILE *f = fopen(paths[i], "r");
if (f) {
- sway_log(L_INFO, "Contents of %s:", paths[i]);
+ wlr_log(L_INFO, "Contents of %s:", paths[i]);
while (!feof(f)) {
char *line;
if (!(line = read_line(f))) {
break;
}
if (*line) {
- sway_log(L_INFO, "%s", line);
+ wlr_log(L_INFO, "%s", line);
}
free(line);
}
@@ -236,9 +159,10 @@ static void log_distro() {
}
static void log_kernel() {
+ return;
FILE *f = popen("uname -a", "r");
if (!f) {
- sway_log(L_INFO, "Unable to determine kernel version");
+ wlr_log(L_INFO, "Unable to determine kernel version");
return;
}
while (!feof(f)) {
@@ -247,7 +171,7 @@ static void log_kernel() {
break;
}
if (*line) {
- sway_log(L_INFO, "%s", line);
+ wlr_log(L_INFO, "%s", line);
}
free(line);
}
@@ -258,14 +182,14 @@ static void security_sanity_check() {
// TODO: Notify users visually if this has issues
struct stat s;
if (stat("/proc", &s)) {
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"!! DANGER !! /proc is not available - sway CANNOT enforce security rules!");
}
#ifdef __linux__
cap_flag_value_t v;
cap_t cap = cap_get_proc();
if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) {
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users.");
}
if (cap) {
@@ -281,13 +205,13 @@ static void executable_sanity_check() {
stat(exe, &sb);
// We assume that cap_get_file returning NULL implies ENODATA
if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) {
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"sway executable has both the s(g)uid bit AND file caps set.");
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"This is strongly discouraged (and completely broken).");
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"Please clear one of them (either the suid bit, or the file caps).");
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"If unsure, strip the file caps.");
exit(EXIT_FAILURE);
}
@@ -295,6 +219,37 @@ static void executable_sanity_check() {
#endif
}
+static void drop_permissions(bool keep_caps) {
+ if (getuid() != geteuid() || getgid() != getegid()) {
+ if (setgid(getgid()) != 0) {
+ wlr_log(L_ERROR, "Unable to drop root");
+ exit(EXIT_FAILURE);
+ }
+ if (setuid(getuid()) != 0) {
+ wlr_log(L_ERROR, "Unable to drop root");
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (setuid(0) != -1) {
+ wlr_log(L_ERROR, "Root privileges can be restored.");
+ exit(EXIT_FAILURE);
+ }
+#ifdef __linux__
+ if (keep_caps) {
+ // Drop every cap except CAP_SYS_PTRACE
+ cap_t caps = cap_init();
+ cap_value_t keep = CAP_SYS_PTRACE;
+ wlr_log(L_INFO, "Dropping extra capabilities");
+ if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
+ cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
+ cap_set_proc(caps)) {
+ wlr_log(L_ERROR, "Failed to drop extra capabilities");
+ exit(EXIT_FAILURE);
+ }
+ }
+#endif
+}
+
int main(int argc, char **argv) {
static int verbose = 0, debug = 0, validate = 0;
@@ -334,7 +289,7 @@ int main(int argc, char **argv) {
int c;
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "hCdvVc:", long_options, &option_index);
+ c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index);
if (c == -1) {
break;
}
@@ -352,6 +307,9 @@ int main(int argc, char **argv) {
case 'd': // debug
debug = 1;
break;
+ case 'D': // extended debug options
+ enable_debug_tree = true;
+ break;
case 'v': // version
fprintf(stdout, "sway version " SWAY_VERSION "\n");
exit(EXIT_SUCCESS);
@@ -374,37 +332,24 @@ int main(int argc, char **argv) {
}
}
- // we need to setup logging before wlc_init in case it fails.
+ // TODO: switch logging over to wlroots?
if (debug) {
- init_log(L_DEBUG);
+ wlr_log_init(L_DEBUG, NULL);
} else if (verbose || validate) {
- init_log(L_INFO);
+ wlr_log_init(L_INFO, NULL);
} else {
- init_log(L_ERROR);
+ wlr_log_init(L_ERROR, NULL);
}
if (optind < argc) { // Behave as IPC client
if(optind != 1) {
- sway_log(L_ERROR, "Don't use options with the IPC client");
- exit(EXIT_FAILURE);
- }
- if (getuid() != geteuid() || getgid() != getegid()) {
- if (setgid(getgid()) != 0) {
- sway_log(L_ERROR, "Unable to drop root");
- exit(EXIT_FAILURE);
- }
- if (setuid(getuid()) != 0) {
- sway_log(L_ERROR, "Unable to drop root");
- exit(EXIT_FAILURE);
- }
- }
- if (setuid(0) != -1) {
- sway_log(L_ERROR, "Root privileges can be restored.");
+ wlr_log(L_ERROR, "Don't use options with the IPC client");
exit(EXIT_FAILURE);
}
+ drop_permissions(false);
char *socket_path = getenv("SWAYSOCK");
if (!socket_path) {
- sway_log(L_ERROR, "Unable to retrieve socket path");
+ wlr_log(L_ERROR, "Unable to retrieve socket path");
exit(EXIT_FAILURE);
}
char *command = join_args(argv + optind, argc - optind);
@@ -413,49 +358,25 @@ int main(int argc, char **argv) {
}
executable_sanity_check();
-#ifdef __linux__
bool suid = false;
+#ifdef __linux__
if (getuid() != geteuid() || getgid() != getegid()) {
// Retain capabilities after setuid()
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
- sway_log(L_ERROR, "Cannot keep caps after setuid()");
+ wlr_log(L_ERROR, "Cannot keep caps after setuid()");
exit(EXIT_FAILURE);
}
suid = true;
}
#endif
- wlc_log_set_handler(wlc_log_handler);
log_kernel();
log_distro();
- log_env();
detect_proprietary();
detect_raspi();
- input_devices = create_list();
-
- /* Changing code earlier than this point requires detailed review */
- /* (That code runs as root on systems without logind, and wlc_init drops to
- * another user.) */
- register_wlc_handlers();
- if (!wlc_init()) {
- return 1;
- }
- register_extensions();
-
#ifdef __linux__
- if (suid) {
- // Drop every cap except CAP_SYS_PTRACE
- cap_t caps = cap_init();
- cap_value_t keep = CAP_SYS_PTRACE;
- sway_log(L_INFO, "Dropping extra capabilities");
- if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
- cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
- cap_set_proc(caps)) {
- sway_log(L_ERROR, "Failed to drop extra capabilities");
- exit(EXIT_FAILURE);
- }
- }
+ drop_permissions(suid);
#endif
// handle SIGTERM signals
signal(SIGTERM, sig_handler);
@@ -463,11 +384,16 @@ int main(int argc, char **argv) {
// prevent ipc from crashing sway
signal(SIGPIPE, SIG_IGN);
- sway_log(L_INFO, "Starting sway version " SWAY_VERSION "\n");
+ wlr_log(L_INFO, "Starting sway version " SWAY_VERSION);
+
+ layout_init();
- init_layout();
+ if (!server_init(&server)) {
+ return 1;
+ }
- ipc_init();
+ ipc_init(&server);
+ log_env();
if (validate) {
bool valid = load_main_config(config_path, false);
@@ -484,11 +410,17 @@ int main(int argc, char **argv) {
security_sanity_check();
+ // TODO: wait for server to be ready
+ // TODO: consume config->cmd_queue
+ config->active = true;
+
if (!terminate_request) {
- wlc_run();
+ server_run(&server);
}
- list_free(input_devices);
+ wlr_log(L_INFO, "Shutting down sway");
+
+ server_fini(&server);
ipc_terminate();
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 @@
+sway_sources = files(
+ 'main.c',
+ 'server.c',
+ 'commands.c',
+ 'config.c',
+ 'criteria.c',
+ 'debug-tree.c',
+ 'ipc-json.c',
+ 'ipc-server.c',
+ 'security.c',
+
+ 'desktop/desktop.c',
+ 'desktop/output.c',
+ 'desktop/layer_shell.c',
+ 'desktop/wl_shell.c',
+ 'desktop/xdg_shell_v6.c',
+ 'desktop/xwayland.c',
+
+ 'input/input-manager.c',
+ 'input/seat.c',
+ 'input/cursor.c',
+ 'input/keyboard.c',
+
+ 'config/bar.c',
+ 'config/output.c',
+ 'config/seat.c',
+ 'config/input.c',
+
+ 'commands/bar.c',
+ 'commands/bind.c',
+ 'commands/default_orientation.c',
+ 'commands/exit.c',
+ 'commands/exec.c',
+ 'commands/exec_always.c',
+ 'commands/focus.c',
+ 'commands/focus_follows_mouse.c',
+ 'commands/kill.c',
+ 'commands/opacity.c',
+ 'commands/include.c',
+ 'commands/input.c',
+ 'commands/layout.c',
+ 'commands/mode.c',
+ 'commands/mouse_warping.c',
+ 'commands/move.c',
+ 'commands/output.c',
+ 'commands/reload.c',
+ 'commands/resize.c',
+ 'commands/seat.c',
+ 'commands/seat/attach.c',
+ 'commands/seat/cursor.c',
+ 'commands/seat/fallback.c',
+ 'commands/set.c',
+ 'commands/split.c',
+ 'commands/swaybg_command.c',
+ 'commands/workspace.c',
+ 'commands/ws_auto_back_and_forth.c',
+
+ 'commands/bar/activate_button.c',
+ 'commands/bar/binding_mode_indicator.c',
+ 'commands/bar/bindsym.c',
+ 'commands/bar/colors.c',
+ 'commands/bar/context_button.c',
+ 'commands/bar/font.c',
+ 'commands/bar/height.c',
+ 'commands/bar/hidden_state.c',
+ 'commands/bar/icon_theme.c',
+ 'commands/bar/id.c',
+ 'commands/bar/mode.c',
+ 'commands/bar/modifier.c',
+ 'commands/bar/output.c',
+ 'commands/bar/pango_markup.c',
+ 'commands/bar/position.c',
+ 'commands/bar/secondary_button.c',
+ 'commands/bar/separator_symbol.c',
+ 'commands/bar/status_command.c',
+ 'commands/bar/strip_workspace_numbers.c',
+ 'commands/bar/swaybar_command.c',
+ 'commands/bar/tray_output.c',
+ 'commands/bar/tray_padding.c',
+ 'commands/bar/workspace_buttons.c',
+ 'commands/bar/wrap_scroll.c',
+
+ 'commands/input/accel_profile.c',
+ 'commands/input/click_method.c',
+ 'commands/input/drag_lock.c',
+ 'commands/input/dwt.c',
+ 'commands/input/events.c',
+ 'commands/input/left_handed.c',
+ 'commands/input/map_to_output.c',
+ 'commands/input/middle_emulation.c',
+ 'commands/input/natural_scroll.c',
+ 'commands/input/pointer_accel.c',
+ 'commands/input/scroll_method.c',
+ 'commands/input/tap.c',
+ 'commands/input/xkb_layout.c',
+ 'commands/input/xkb_model.c',
+ 'commands/input/xkb_options.c',
+ 'commands/input/xkb_rules.c',
+ 'commands/input/xkb_variant.c',
+
+ 'tree/container.c',
+ 'tree/layout.c',
+ 'tree/view.c',
+ 'tree/workspace.c',
+ 'tree/output.c',
+)
+
+sway_deps = [
+ cairo,
+ gdk_pixbuf,
+ jsonc,
+ libcap,
+ libinput,
+ math,
+ pango,
+ pcre,
+ pixman,
+ server_protos,
+ wayland_server,
+ wlroots,
+ xkbcommon,
+]
+
+executable(
+ 'sway',
+ sway_sources,
+ include_directories: [sway_inc],
+ dependencies: sway_deps,
+ link_with: [lib_sway_common],
+ install: true
+)
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 @@
-#include <strings.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include "sway/output.h"
-#include "log.h"
-#include "list.h"
-
-void output_get_scaled_size(wlc_handle handle, struct wlc_size *size) {
- *size = *wlc_output_get_resolution(handle);
- uint32_t scale = wlc_output_get_scale(handle);
- size->w /= scale;
- size->h /= scale;
-}
-
-swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos) {
- swayc_t *output = NULL;
- // If there is no output directly next to the current one, use
- // swayc_opposite_output to wrap.
- if (strcasecmp(name, "left") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_LEFT, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_RIGHT, abs_pos);
- }
- } else if (strcasecmp(name, "right") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_RIGHT, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_LEFT, abs_pos);
- }
- } else if (strcasecmp(name, "up") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_UP, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_DOWN, abs_pos);
- }
- } else if (strcasecmp(name, "down") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_DOWN, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_UP, abs_pos);
- }
- } else {
- for(int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (c->type == C_OUTPUT && strcasecmp(c->name, name) == 0) {
- return c;
- }
- }
- }
- return output;
-}
-
-swayc_t *swayc_opposite_output(enum movement_direction dir,
- const struct wlc_point *abs_pos) {
-
- // Search through all the outputs and pick the output whose edge covers the
- // given position, and is at leftmost/rightmost/upmost/downmost side of the
- // screen (decided by the direction given).
- swayc_t *opposite = NULL;
- char *dir_text = NULL;
- switch(dir) {
- case MOVE_LEFT:
- case MOVE_RIGHT: ;
- for (int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
- if (!opposite) {
- opposite = c;
- } else if ((dir == MOVE_LEFT && c->x < opposite->x)
- || (dir == MOVE_RIGHT && c->x > opposite->x)) {
- opposite = c;
- }
- }
- }
- dir_text = dir == MOVE_LEFT ? "leftmost" : "rightmost";
- break;
- case MOVE_UP:
- case MOVE_DOWN: ;
- for (int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
- if (!opposite) {
- opposite = c;
- } else if ((dir == MOVE_UP && c->y < opposite->y)
- || (dir == MOVE_DOWN && c->y > opposite->y)) {
- opposite = c;
- }
- }
- }
- dir_text = dir == MOVE_UP ? "upmost" : "downmost";
- break;
- default:
- sway_abort("Function called with invalid argument.");
- break;
- }
- if (opposite) {
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s from y-position %i",
- opposite->name, opposite->width, opposite->height, opposite->x, opposite->y,
- dir_text, abs_pos->y);
- }
- return opposite;
-}
-
-// Position is where on the edge (as absolute position) the adjacent output should be searched for.
-swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir,
- const struct wlc_point *abs_pos, bool pick_closest) {
-
- if (!output) {
- output = swayc_active_output();
- }
- // In order to find adjacent outputs we need to test that the outputs are
- // aligned on one axis (decided by the direction given) and that the given
- // position is within the edge of the adjacent output. If no such output
- // exists we pick the adjacent output within the edge that is closest to
- // the given position, if any.
- swayc_t *adjacent = NULL;
- char *dir_text = NULL;
- switch(dir) {
- case MOVE_LEFT:
- case MOVE_RIGHT: ;
- double delta_y = 0;
- for(int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (c == output || c->type != C_OUTPUT) {
- continue;
- }
- bool x_aligned = dir == MOVE_LEFT ?
- c->x + c->width == output->x :
- c->x == output->x + output->width;
- if (!x_aligned) {
- continue;
- }
- if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
- delta_y = 0;
- adjacent = c;
- break;
- } else if (pick_closest) {
- // track closest adjacent output
- double top_y = c->y, bottom_y = c->y + c->height;
- if (top_y >= output->y && top_y <= output->y + output->height) {
- double delta = top_y - abs_pos->y;
- if (delta < 0) delta = -delta;
- if (delta < delta_y || !adjacent) {
- delta_y = delta;
- adjacent = c;
- }
- }
- // we check both points and pick the closest
- if (bottom_y >= output->y && bottom_y <= output->y + output->height) {
- double delta = bottom_y - abs_pos->y;
- if (delta < 0) delta = -delta;
- if (delta < delta_y || !adjacent) {
- delta_y = delta;
- adjacent = c;
- }
- }
- }
- }
- dir_text = dir == MOVE_LEFT ? "left of" : "right of";
- if (adjacent && delta_y == 0) {
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->y);
- } else if (adjacent) {
- // so we end up picking the closest adjacent output because
- // there is no directly adjacent to the given position
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i, delta: %.0f)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->y, delta_y);
- }
- break;
- case MOVE_UP:
- case MOVE_DOWN: ;
- double delta_x = 0;
- for(int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (c == output || c->type != C_OUTPUT) {
- continue;
- }
- bool y_aligned = dir == MOVE_UP ?
- c->y + c->height == output->y :
- c->y == output->y + output->height;
- if (!y_aligned) {
- continue;
- }
- if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
- delta_x = 0;
- adjacent = c;
- break;
- } else if (pick_closest) {
- // track closest adjacent output
- double left_x = c->x, right_x = c->x + c->width;
- if (left_x >= output->x && left_x <= output->x + output->width) {
- double delta = left_x - abs_pos->x;
- if (delta < 0) delta = -delta;
- if (delta < delta_x || !adjacent) {
- delta_x = delta;
- adjacent = c;
- }
- }
- // we check both points and pick the closest
- if (right_x >= output->x && right_x <= output->x + output->width) {
- double delta = right_x - abs_pos->x;
- if (delta < 0) delta = -delta;
- if (delta < delta_x || !adjacent) {
- delta_x = delta;
- adjacent = c;
- }
- }
- }
- }
- dir_text = dir == MOVE_UP ? "above" : "below";
- if (adjacent && delta_x == 0) {
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->x);
- } else if (adjacent) {
- // so we end up picking the closest adjacent output because
- // there is no directly adjacent to the given position
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i, delta: %.0f)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->x, delta_x);
- }
- break;
- default:
- sway_abort("Function called with invalid argument.");
- break;
- }
- return adjacent;
-}
-
-void get_absolute_position(swayc_t *container, struct wlc_point *point) {
- if (!container || !point)
- sway_abort("Need container and wlc_point (was %p, %p).", container, point);
-
- if (container->type == C_OUTPUT) {
- // Coordinates are already absolute.
- point->x = container->x;
- point->y = container->y;
- } else {
- swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
- if (container->type == C_WORKSPACE) {
- // Workspace coordinates are actually wrong/arbitrary, but should
- // be same as output.
- point->x = output->x;
- point->y = output->y;
- } else {
- point->x = output->x + container->x;
- point->y = output->y + container->y;
- }
- }
-}
-
-void get_absolute_center_position(swayc_t *container, struct wlc_point *point) {
- get_absolute_position(container, point);
- point->x += container->width/2;
- point->y += container->height/2;
-}
-
-static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
- swayc_t *a = *(void **)_a;
- swayc_t *b = *(void **)_b;
- int retval = 0;
-
- if (isdigit(a->name[0]) && isdigit(b->name[0])) {
- int a_num = strtol(a->name, NULL, 10);
- int b_num = strtol(b->name, NULL, 10);
- retval = (a_num < b_num) ? -1 : (a_num > b_num);
- } else if (isdigit(a->name[0])) {
- retval = -1;
- } else if (isdigit(b->name[0])) {
- retval = 1;
- }
-
- return retval;
-}
-
-void sort_workspaces(swayc_t *output) {
- list_stable_sort(output->children, sort_workspace_cmp_qsort);
-}
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 @@
#define _XOPEN_SOURCE 700
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
-#include <stdio.h>
-#include "sway/config.h"
#include "sway/security.h"
-#include "log.h"
-
-static bool validate_ipc_target(const char *program) {
- struct stat sb;
-
- sway_log(L_DEBUG, "Validating IPC target '%s'", program);
-
- if (!strcmp(program, "*")) {
- return true;
- }
- if (lstat(program, &sb) == -1) {
- return false;
- }
- if (!S_ISREG(sb.st_mode)) {
- sway_log(L_ERROR,
- "IPC target '%s' MUST be/point at an existing regular file",
- program);
- return false;
- }
- if (sb.st_uid != 0) {
-#ifdef NDEBUG
- sway_log(L_ERROR, "IPC target '%s' MUST be owned by root", program);
- return false;
-#else
- sway_log(L_INFO, "IPC target '%s' MUST be owned by root (waived for debug build)", program);
- return true;
-#endif
- }
- if (sb.st_mode & S_IWOTH) {
- sway_log(L_ERROR, "IPC target '%s' MUST NOT be world writable", program);
- return false;
- }
-
- return true;
-}
-
-struct feature_policy *alloc_feature_policy(const char *program) {
- uint32_t default_policy = 0;
-
- if (!validate_ipc_target(program)) {
- return NULL;
- }
- for (int i = 0; i < config->feature_policies->length; ++i) {
- struct feature_policy *policy = config->feature_policies->items[i];
- if (strcmp(policy->program, "*") == 0) {
- default_policy = policy->features;
- break;
- }
- }
-
- struct feature_policy *policy = malloc(sizeof(struct feature_policy));
- if (!policy) {
- return NULL;
- }
- policy->program = strdup(program);
- if (!policy->program) {
- free(policy);
- return NULL;
- }
- policy->features = default_policy;
-
- return policy;
-}
-
-struct ipc_policy *alloc_ipc_policy(const char *program) {
- uint32_t default_policy = 0;
-
- if (!validate_ipc_target(program)) {
- return NULL;
- }
- for (int i = 0; i < config->ipc_policies->length; ++i) {
- struct ipc_policy *policy = config->ipc_policies->items[i];
- if (strcmp(policy->program, "*") == 0) {
- default_policy = policy->features;
- break;
- }
- }
-
- struct ipc_policy *policy = malloc(sizeof(struct ipc_policy));
- if (!policy) {
- return NULL;
- }
- policy->program = strdup(program);
- if (!policy->program) {
- free(policy);
- return NULL;
- }
- policy->features = default_policy;
-
- return policy;
-}
struct command_policy *alloc_command_policy(const char *command) {
struct command_policy *policy = malloc(sizeof(struct command_policy));
@@ -111,118 +16,3 @@ struct command_policy *alloc_command_policy(const char *command) {
policy->context = 0;
return policy;
}
-
-static const char *get_pid_exe(pid_t pid) {
-#ifdef __FreeBSD__
- const char *fmt = "/proc/%d/file";
-#else
- const char *fmt = "/proc/%d/exe";
-#endif
- int pathlen = snprintf(NULL, 0, fmt, pid);
- char *path = malloc(pathlen + 1);
- if (path) {
- snprintf(path, pathlen + 1, fmt, pid);
- }
-
- static char link[2048];
-
- ssize_t len = !path ? -1 : readlink(path, link, sizeof(link));
- if (len < 0) {
- sway_log(L_INFO,
- "WARNING: unable to read %s for security check. Using default policy.",
- path);
- strcpy(link, "*");
- } else {
- link[len] = '\0';
- }
- free(path);
-
- return link;
-}
-
-struct feature_policy *get_feature_policy(const char *name) {
- struct feature_policy *policy = NULL;
-
- for (int i = 0; i < config->feature_policies->length; ++i) {
- struct feature_policy *p = config->feature_policies->items[i];
- if (strcmp(p->program, name) == 0) {
- policy = p;
- break;
- }
- }
- if (!policy) {
- policy = alloc_feature_policy(name);
- sway_assert(policy, "Unable to allocate security policy");
- if (policy) {
- list_add(config->feature_policies, policy);
- }
- }
- return policy;
-}
-
-uint32_t get_feature_policy_mask(pid_t pid) {
- uint32_t default_policy = 0;
- const char *link = get_pid_exe(pid);
-
- for (int i = 0; i < config->feature_policies->length; ++i) {
- struct feature_policy *policy = config->feature_policies->items[i];
- if (strcmp(policy->program, "*") == 0) {
- default_policy = policy->features;
- }
- if (strcmp(policy->program, link) == 0) {
- return policy->features;
- }
- }
-
- return default_policy;
-}
-
-uint32_t get_ipc_policy_mask(pid_t pid) {
- uint32_t default_policy = 0;
- const char *link = get_pid_exe(pid);
-
- for (int i = 0; i < config->ipc_policies->length; ++i) {
- struct ipc_policy *policy = config->ipc_policies->items[i];
- if (strcmp(policy->program, "*") == 0) {
- default_policy = policy->features;
- }
- if (strcmp(policy->program, link) == 0) {
- return policy->features;
- }
- }
-
- return default_policy;
-}
-
-uint32_t get_command_policy_mask(const char *cmd) {
- uint32_t default_policy = 0;
-
- for (int i = 0; i < config->command_policies->length; ++i) {
- struct command_policy *policy = config->command_policies->items[i];
- if (strcmp(policy->command, "*") == 0) {
- default_policy = policy->context;
- }
- if (strcmp(policy->command, cmd) == 0) {
- return policy->context;
- }
- }
-
- return default_policy;
-}
-
-const char *command_policy_str(enum command_context context) {
- switch (context) {
- case CONTEXT_ALL:
- return "all";
- case CONTEXT_CONFIG:
- return "config";
- case CONTEXT_BINDING:
- return "binding";
- case CONTEXT_IPC:
- return "IPC";
- case CONTEXT_CRITERIA:
- return "criteria";
- default:
- return "unknown";
- }
-}
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 @@
+#define _POSIX_C_SOURCE 200112L
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/backend.h>
+#include <wlr/backend/session.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_compositor.h>
+#include <wlr/types/wlr_gamma_control.h>
+#include <wlr/types/wlr_linux_dmabuf.h>
+#include <wlr/types/wlr_layer_shell.h>
+#include <wlr/types/wlr_primary_selection.h>
+#include <wlr/types/wlr_screenshooter.h>
+#include <wlr/types/wlr_server_decoration.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+#include <wlr/types/wlr_xdg_output.h>
+#include <wlr/types/wlr_wl_shell.h>
+#include <wlr/util/log.h>
+// TODO WLR: make Xwayland optional
+#include <wlr/xwayland.h>
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "sway/input/input-manager.h"
+#include "sway/server.h"
+#include "sway/tree/layout.h"
+
+static void server_ready(struct wl_listener *listener, void *data) {
+ wlr_log(L_DEBUG, "Compositor is ready, executing cmds in queue");
+ // Execute commands until there are none left
+ config->active = true;
+ while (config->cmd_queue->length) {
+ char *line = config->cmd_queue->items[0];
+ struct cmd_results *res = execute_command(line, NULL);
+ if (res->status != CMD_SUCCESS) {
+ wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error);
+ }
+ free_cmd_results(res);
+ free(line);
+ list_del(config->cmd_queue, 0);
+ }
+}
+
+bool server_init(struct sway_server *server) {
+ wlr_log(L_DEBUG, "Initializing Wayland server");
+
+ server->wl_display = wl_display_create();
+ server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
+ server->backend = wlr_backend_autocreate(server->wl_display);
+
+ struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend);
+ assert(renderer);
+
+ wl_display_init_shm(server->wl_display);
+
+ server->compositor = wlr_compositor_create(server->wl_display, renderer);
+ server->data_device_manager =
+ wlr_data_device_manager_create(server->wl_display);
+
+ wlr_screenshooter_create(server->wl_display);
+ wlr_gamma_control_manager_create(server->wl_display);
+ wlr_primary_selection_device_manager_create(server->wl_display);
+
+ server->new_output.notify = handle_new_output;
+ wl_signal_add(&server->backend->events.new_output, &server->new_output);
+
+ wlr_xdg_output_manager_create(server->wl_display,
+ root_container.sway_root->output_layout);
+
+ server->layer_shell = wlr_layer_shell_create(server->wl_display);
+ wl_signal_add(&server->layer_shell->events.new_surface,
+ &server->layer_shell_surface);
+ server->layer_shell_surface.notify = handle_layer_shell_surface;
+
+ server->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display);
+ wl_signal_add(&server->xdg_shell_v6->events.new_surface,
+ &server->xdg_shell_v6_surface);
+ server->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface;
+
+ server->wl_shell = wlr_wl_shell_create(server->wl_display);
+ wl_signal_add(&server->wl_shell->events.new_surface,
+ &server->wl_shell_surface);
+ server->wl_shell_surface.notify = handle_wl_shell_surface;
+
+ // TODO make xwayland optional
+ server->xwayland =
+ wlr_xwayland_create(server->wl_display, server->compositor);
+ wl_signal_add(&server->xwayland->events.new_surface,
+ &server->xwayland_surface);
+ server->xwayland_surface.notify = handle_xwayland_surface;
+ wl_signal_add(&server->xwayland->events.ready,
+ &server->xwayland_ready);
+ // TODO: call server_ready now if xwayland is not enabled
+ server->xwayland_ready.notify = server_ready;
+
+ // TODO: configurable cursor theme and size
+ server->xcursor_manager = wlr_xcursor_manager_create(NULL, 24);
+ wlr_xcursor_manager_load(server->xcursor_manager, 1);
+ struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
+ server->xcursor_manager, "left_ptr", 1);
+ if (xcursor != NULL) {
+ struct wlr_xcursor_image *image = xcursor->images[0];
+ wlr_xwayland_set_cursor(server->xwayland, image->buffer,
+ image->width * 4, image->width, image->height, image->hotspot_x,
+ image->hotspot_y);
+ }
+
+ // TODO: Integration with sway borders
+ struct wlr_server_decoration_manager *deco_manager =
+ wlr_server_decoration_manager_create(server->wl_display);
+ wlr_server_decoration_manager_set_default_mode(
+ deco_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
+
+ wlr_linux_dmabuf_create(server->wl_display, renderer);
+
+ server->socket = wl_display_add_socket_auto(server->wl_display);
+ if (!server->socket) {
+ wlr_log(L_ERROR, "Unable to open wayland socket");
+ wlr_backend_destroy(server->backend);
+ return false;
+ }
+
+ input_manager = input_manager_create(server);
+ return true;
+}
+
+void server_fini(struct sway_server *server) {
+ // TODO
+}
+
+void server_run(struct sway_server *server) {
+ wlr_log(L_INFO, "Running compositor on wayland display '%s'",
+ server->socket);
+ setenv("WAYLAND_DISPLAY", server->socket, true);
+ if (!wlr_backend_start(server->backend)) {
+ wlr_log(L_ERROR, "Failed to start backend");
+ wlr_backend_destroy(server->backend);
+ return;
+ }
+ wl_display_run(server->wl_display);
+}
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 @@
/////
-vim:set ts=4 sw=4 tw=82 noet:
+vim:set ft=asciidoc ts=4 sw=4 tw=82 noet:
/////
sway-input (5)
==============
@@ -11,12 +11,57 @@ sway-input - input configuration file and commands
Description
-----------
-Sway allows for configuration of libinput devices within the sway configuration file.
+Sway allows for configuration of devices within the sway configuration file.
sway-input commands must be used inside an _input { }_ block in the config.
To obtain a list of available device identifiers, run **swaymsg -t get_inputs**.
-Commands
---------
+Input Commands
+--------------
+
+Keyboard Configuration
+~~~~~~~~~~~~~~~~~~~~~~
+
+For more information on these xkb configuration options, see
+**xkeyboard-config**(7).
+
+**input** <identifier> xkb_layout <layout_name>::
+ Sets the layout of the keyboard like _us_ or _de_.
+
+**input** <identifier> xkb_model <model_name>::
+ Sets the model of the keyboard. This has an influence for some extra keys your
+ keyboard might have.
+
+**input** <identifier> xkb_options <options>::
+ Sets extra xkb configuration options for the keyboard.
+
+**input** <identifier> xkb_rules <rules>::
+ Sets files of rules to be used for keyboard mapping composition.
+
+**input** <identifier> xkb_variant <variant>::
+ Sets the variant of the keyboard like _dvorak_ or _colemak_.
+
+Mapping Configuration
+---------------------
+
+**input** <identifier> map_to_output <identifier>::
+ Maps inputs from this device to the specified output. Only meaningful if the
+ device is a pointer, touch, or drawing tablet device.
+
+**input** <identifier> map_to_region <WxH\@X,Y>::
+ Maps inputs from this device to the specified region of the global output
+ layout. Only meaningful if the device is a pointer, touch, or drawing tablet
+ device.
+
+**input** <identifier> map_region <WxH\@X,Y>::
+ Ignores inputs from this device that do not occur within the specified region.
+ Can be in millimeters (e.g. 10mmx20mm\@10mm,20mm) or in terms of 0..1 (e.g.
+ 0.5x0.5\@0,0). Not all devices support millimeters. Only meaningful if the
+ device is not a keyboard an provides events in absolute terms (such as a
+ drawing tablet or touch screen - most pointers provide events relative to the
+ previous frame).
+
+Libinput Configuration
+~~~~~~~~~~~~~~~~~~~~~~
**input** <identifier> accel_profile <adaptive|flat>::
Sets the pointer acceleration profile for the specified input device.
@@ -53,6 +98,27 @@ Commands
**input** <identifier> tap <enabled|disabled>::
Enables or disables tap for specified input device.
+Seat Configuration
+------------------
+
+Configure options for multiseat mode. sway-seat commands must be used inside a
+_seat { }_ block in the config.
+
+A _seat_ is a collection of input devices that act independently of each other.
+Seats are identified by name and the default seat is _seat0_ if no seats are
+configured. Each seat has an independent keyboard focus and a separate cursor that
+is controlled by the pointer devices of the seat. This is useful for multiple
+people using the desktop at the same time with their own devices (each sitting in
+their own "seat").
+
+**seat** <name> attach <input_identifier>::
+ Attach an input device to this seat by its input identifier. A special value
+ of _*_ will attach all devices to the seat.
+
+**seat** <name> fallback <true|false>::
+ Set this seat as the fallback seat. A fallback seat will attach any device not
+ explicitly attached to another seat (similar to a "default" seat).
+
See Also
--------
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 @@
/////
-vim:set ts=4 sw=4 tw=82 noet:
+vim:set ft=asciidoc ts=4 sw=4 tw=82 noet:
/////
:quotes.~:
@@ -93,27 +93,6 @@ The following environment variables have an effect on sway:
*SWAYSOCK*::
Specifies the path to the sway IPC socket.
-*WLC_DRM_DEVICE*::
- Specifies the device to use in DRM mode.
-
-*WLC_SHM*::
- Set 1 to force EGL clients to use shared memory.
-
-*WLC_OUTPUTS*::
- Number of fake outputs to use when running in X11 mode.
-
-*WLC_XWAYLAND*::
- Set to 0 to disable Xwayland support.
-
-*WLC_LIBINPUT*::
- Set to 1 to force libinput (even in X11 mode).
-
-*WLC_REPEAT_DELAY*::
- Configures the keyboard repeat delay.
-
-*WLC_REPEAT_RATE*::
- Configures the keyboard repeat rate.
-
*XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*, *XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*::
Configures the xkb keyboard settings. See xkeyboard-config(7).
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.
Sets variable $name to _value_. You can use the new variable in the arguments
of future commands.
+**swaybg_command** <command>::
+ Executes custom bg command, default is _swaybg_.
+
The following commands cannot be used directly in the configuration file.
They are expected to be used with **bindsym** or at runtime through **swaymsg**(1).
@@ -126,22 +129,21 @@ They are expected to be used with **bindsym** or at runtime through **swaymsg**(
**resize** <shrink|grow> <width|height> [<amount>] [px|ppt]::
Resizes the currently focused container or view by _amount_. _amount_ is
- optional: the default value is 10 (either px or ppt depending on the view
- type). The [px|ppt] parameter is optional. _px_ specifies that _amount_ refers
- to pixels; _ppt_ specifies that _amount_ refers to percentage points of the
- current dimension. Floating views use px dimensions by default (but can use
- ppt if specified); tiled views use ppt dimensions by default (but can use px
- if specified).
+ optional: the default value is 10 (either px or ppt depending on the view type).
+ The [px|ppt] parameter is optional. _px_ specifies that _amount_ refers to pixels;
+ _ppt_ specifies that _amount_ refers to percentage points of the current
+ size. Floating views use px by default (but can use ppt if specified); tiled
+ views use ppt by default (but can use px if specified).
**resize set** <width> [px] <height> [px]::
Sets the width and height of the currently focused container to _width_ pixels
and _height_ pixels. The [px] parameters are optional and have no effect. This
- command only accepts pixel dimensions.
+ command only accepts a size in pixels.
**resize set** <width|height> <amount> [px] [<width|height> <amount> [px]]::
Sets the _width_ and/or _height_ of the currently focused container to
_amount_. The [px] parameters are optional and have no effect. This command
- only accepts pixel dimensions.
+ only accepts a size in pixels.
**scratchpad show**::
Shows a window from the scratchpad. Repeatedly using this command will cycle
@@ -254,14 +256,14 @@ The default colors are:
*restart* is executed.
**floating_maximum_size** <width> x <height>::
- Specifies the maximum dimensions of floating windows.
- Uses the container dimensions as default.
- -1 x -1 will remove any restriction on dimensions.
+ Specifies the maximum size of floating windows.
+ Uses the container size as default.
+ -1 x -1 will remove any restriction on size.
0 x 0 has the same behavior as not setting any value.
If in conflict, this option has precedence over floating_minimum_size.
**floating_minimum_size** <width> x <height>::
- Specifies the minimum dimensions of floating windows.
+ Specifies the minimum size of floating windows.
Default parameters are 75 x 50.
-1 and 0 are invalid parameters, default will be used instead.
@@ -313,7 +315,7 @@ The default colors are:
**hide_edge_borders** <none|vertical|horizontal|both|smart>::
Hide window borders adjacent to the screen edges. Default is _none_.
-**input** <input device> <block of commands>::
+**input** <input_device> <block of commands>::
Append _{_ to this command, the following lines will be commands to configure
the named input device, and _}_ on its own line will close the block.
+
@@ -321,6 +323,18 @@ The default colors are:
+
See **sway-input**(5) for details.
+**seat** <seat_name> <block of commands>::
+ Append _{_ to this command, the following lines will be commands to configure
+ the named seat, and _}_ on its own line will close the block.
+ See **sway-input**(5) for details.
+
+**seat** <seat_name> cursor <move|set> <x> <y>::
+ Move cursor relatively to current position or set to absolute screen position.
+ A value of 0 causes the axis to be ignored in both commands.
+
+**seat** <seat_name> cursor <press|release> <left|right|1|2|3...>::
+ Simulate press of mouse button specified by left, right, or numerical code.
+
**kill**::
Kills (force-closes) the currently-focused container and all of its children.
@@ -349,35 +363,44 @@ The default colors are:
Prevents windows matching <criteria> from being focused automatically when
they're created. This does not apply to the first window in a workspace.
-**output** <name> <resolution|res> <WIDTHxHEIGHT>::
- Configures the specified output to use the given resolution.
+**output** <name> mode|resolution|res <WIDTHxHEIGHT>[@<RATE>[Hz]]::
+ Configures the specified output to use the given mode. Modes are a combination
+ of width and height (in pixels) and a refresh rate that your display can be
+ configured to use. For a list of available modes, use swaymsg -t get_outputs.
+ +
+ Examples:
+ +
+ output HDMI-A-1 mode 1920x1080
+
- _Note_: sway does not currently support setting the output mode. Your output's native
- resolution will be used and the screen will be scaled from the resolution
- specified to your native resolution.
+ output HDMI-A-1 mode 1920x1080@60Hz
-**output** <name> <position|pos> <X,Y>::
+**output** <name> position|pos <X,Y>::
Configures the specified output to be arranged at the given position.
-**output** <name> <scale> <I>::
+**output** <name> scale <I>::
Configures the specified output to be scaled up by the specified integer
- scaling factor (usually 2 for HiDPI screens).
+ scaling factor (usually 2 for HiDPI screens). Fractional scaling is supported.
-**output** <name> <background|bg> <file> <mode>::
+**output** <name> background|bg <file> <mode>::
Sets the wallpaper for the given output to the specified file, using the given
scaling mode (one of "stretch", "fill", "fit", "center", "tile").
-**output** <name> <background|bg> <color> solid_color::
+**output** <name> background|bg <color> solid_color::
Sets the background of the given output to the specified color. _color_ should
be specified as an _#rrggbb_ (no alpha) color.
+**output** <name> transform <transform>::
+ Sets the background transform to the given value. Can be one of "90", "180",
+ "270" for rotation; or "flipped", "flipped-90", "flipped-180", "flipped-270"
+ to apply a rotation and flip, or "normal" to apply no transform.
+
**output** <name> disable::
Disables the specified output.
**NOTES FOR THE OUTPUT COMMAND**::
You may combine output commands into one, like so:
+
- output HDMI-A-1 res 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch
+ output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch
+
You can get a list of output names like so:
+
@@ -397,6 +420,10 @@ The default colors are:
However, any mark that starts with an underscore will not be drawn even if the
option is on. The default option is _on_.
+**opacity** <value>::
+ Set the opacity of the window between 0 (completely transparent) and 1
+ (completely opaque).
+
**unmark** <identifier>::
**Unmark** will remove _identifier_ from the list of current marks on a window. If
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_wl_shell.h>
+#include "sway/config.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/ipc-server.h"
+#include "sway/output.h"
+#include "sway/server.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/view.h"
+#include "sway/tree/workspace.h"
+#include "log.h"
+
+static list_t *bfs_queue;
+
+static list_t *get_bfs_queue() {
+ if (!bfs_queue) {
+ bfs_queue = create_list();
+ if (!bfs_queue) {
+ wlr_log(L_ERROR, "could not allocate list for bfs queue");
+ return NULL;
+ }
+ }
+ bfs_queue->length = 0;
+
+ return bfs_queue;
+}
+
+const char *container_type_to_str(enum sway_container_type type) {
+ switch (type) {
+ case C_ROOT:
+ return "C_ROOT";
+ case C_OUTPUT:
+ return "C_OUTPUT";
+ case C_WORKSPACE:
+ return "C_WORKSPACE";
+ case C_CONTAINER:
+ return "C_CONTAINER";
+ case C_VIEW:
+ return "C_VIEW";
+ default:
+ return "C_UNKNOWN";
+ }
+}
+
+void container_create_notify(struct sway_container *container) {
+ // TODO send ipc event type based on the container type
+ wl_signal_emit(&root_container.sway_root->events.new_container, container);
+
+ if (container->type == C_VIEW || container->type == C_CONTAINER) {
+ ipc_event_window(container, "new");
+ }
+}
+
+static void container_close_notify(struct sway_container *container) {
+ if (container == NULL) {
+ return;
+ }
+ // TODO send ipc event type based on the container type
+ if (container->type == C_VIEW || container->type == C_WORKSPACE) {
+ ipc_event_window(container, "close");
+ }
+}
+
+struct sway_container *container_create(enum sway_container_type type) {
+ // next id starts at 1 because 0 is assigned to root_container in layout.c
+ static size_t next_id = 1;
+ struct sway_container *c = calloc(1, sizeof(struct sway_container));
+ if (!c) {
+ return NULL;
+ }
+ c->id = next_id++;
+ c->layout = L_NONE;
+ c->workspace_layout = L_NONE;
+ c->type = type;
+ c->alpha = 1.0f;
+
+ if (type != C_VIEW) {
+ c->children = create_list();
+ }
+
+ wl_signal_init(&c->events.destroy);
+ wl_signal_init(&c->events.reparent);
+
+ return c;
+}
+
+static void _container_destroy(struct sway_container *cont) {
+ if (cont == NULL) {
+ return;
+ }
+
+ wl_signal_emit(&cont->events.destroy, cont);
+ container_close_notify(cont);
+
+ struct sway_container *parent = cont->parent;
+ if (cont->children != NULL && cont->children->length) {
+ // remove children until there are no more, container_destroy calls
+ // container_remove_child, which removes child from this container
+ while (cont->children != NULL) {
+ struct sway_container *child = cont->children->items[0];
+ container_remove_child(child);
+ _container_destroy(child);
+ }
+ }
+ if (cont->marks) {
+ list_foreach(cont->marks, free);
+ list_free(cont->marks);
+ }
+ if (parent) {
+ parent = container_remove_child(cont);
+ }
+ if (cont->name) {
+ free(cont->name);
+ }
+ list_free(cont->children);
+ cont->children = NULL;
+ free(cont);
+}
+
+static struct sway_container *container_output_destroy(
+ struct sway_container *output) {
+ if (!sway_assert(output, "cannot destroy null output")) {
+ return NULL;
+ }
+
+ if (output->children->length > 0) {
+ // TODO save workspaces when there are no outputs.
+ // TODO also check if there will ever be no outputs except for exiting
+ // program
+ if (root_container.children->length > 1) {
+ int p = root_container.children->items[0] == output;
+ // Move workspace from this output to another output
+ while (output->children->length) {
+ struct sway_container *child = output->children->items[0];
+ container_remove_child(child);
+ container_add_child(root_container.children->items[p], child);
+ }
+ container_sort_workspaces(root_container.children->items[p]);
+ arrange_windows(root_container.children->items[p],
+ -1, -1);
+ }
+ }
+
+ wl_list_remove(&output->sway_output->destroy.link);
+ wl_list_remove(&output->sway_output->mode.link);
+ wl_list_remove(&output->sway_output->transform.link);
+ wl_list_remove(&output->sway_output->scale.link);
+
+ wl_list_remove(&output->sway_output->damage_destroy.link);
+ wl_list_remove(&output->sway_output->damage_frame.link);
+
+ wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
+ _container_destroy(output);
+ return &root_container;
+}
+
+static struct sway_container *container_workspace_destroy(
+ struct sway_container *workspace) {
+ if (!sway_assert(workspace, "cannot destroy null workspace")) {
+ return NULL;
+ }
+
+ // Do not destroy this if it's the last workspace on this output
+ struct sway_container *output = container_parent(workspace, C_OUTPUT);
+ if (output && output->children->length == 1) {
+ return NULL;
+ }
+
+ struct sway_container *parent = workspace->parent;
+ if (workspace->children->length == 0) {
+ // destroy the WS if there are no children (TODO check for floating)
+ wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
+ ipc_event_workspace(workspace, NULL, "empty");
+ } else {
+ // Move children to a different workspace on this output
+ struct sway_container *new_workspace = NULL;
+ // TODO move floating
+ for (int i = 0; i < output->children->length; i++) {
+ if (output->children->items[i] != workspace) {
+ new_workspace = output->children->items[i];
+ break;
+ }
+ }
+
+ wlr_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'",
+ workspace->name, new_workspace->name);
+ for (int i = 0; i < workspace->children->length; i++) {
+ container_move_to(workspace->children->items[i], new_workspace);
+ }
+ }
+
+ _container_destroy(workspace);
+
+ output_damage_whole(output->sway_output);
+
+ return parent;
+}
+
+static void container_root_finish(struct sway_container *con) {
+ wlr_log(L_ERROR, "TODO: destroy the root container");
+}
+
+bool container_reap_empty(struct sway_container *con) {
+ switch (con->type) {
+ case C_ROOT:
+ case C_OUTPUT:
+ // dont reap these
+ break;
+ case C_WORKSPACE:
+ if (!workspace_is_visible(con) && con->children->length == 0) {
+ wlr_log(L_DEBUG, "Destroying workspace via reaper");
+ container_workspace_destroy(con);
+ return true;
+ }
+ break;
+ case C_CONTAINER:
+ if (con->children->length == 0) {
+ _container_destroy(con);
+ return true;
+ }
+ case C_VIEW:
+ break;
+ case C_TYPES:
+ sway_assert(false, "container_reap_empty called on an invalid "
+ "container");
+ break;
+ }
+
+ return false;
+}
+
+struct sway_container *container_reap_empty_recursive(
+ struct sway_container *con) {
+ while (con) {
+ struct sway_container *next = con->parent;
+ if (!container_reap_empty(con)) {
+ break;
+ }
+ con = next;
+ }
+ return con;
+}
+
+struct sway_container *container_flatten(struct sway_container *container) {
+ while (container->type == C_CONTAINER && container->children->length == 1) {
+ struct sway_container *child = container->children->items[0];
+ struct sway_container *parent = container->parent;
+ container_replace_child(container, child);
+ container_destroy(container);
+ container = parent;
+ }
+ return container;
+}
+
+struct sway_container *container_destroy(struct sway_container *con) {
+ if (con == NULL) {
+ return NULL;
+ }
+
+ struct sway_container *parent = con->parent;
+
+ switch (con->type) {
+ case C_ROOT:
+ container_root_finish(con);
+ break;
+ case C_OUTPUT:
+ // dont try to reap the root after this
+ container_output_destroy(con);
+ break;
+ case C_WORKSPACE:
+ // dont try to reap the output after this
+ container_workspace_destroy(con);
+ break;
+ case C_CONTAINER:
+ if (con->children->length) {
+ for (int i = 0; i < con->children->length; ++i) {
+ struct sway_container *child = con->children->items[0];
+ container_remove_child(child);
+ container_add_child(parent, child);
+ }
+ }
+ _container_destroy(con);
+ break;
+ case C_VIEW:
+ _container_destroy(con);
+ break;
+ case C_TYPES:
+ wlr_log(L_ERROR, "container_destroy called on an invalid "
+ "container");
+ break;
+ }
+
+ return container_reap_empty_recursive(parent);
+}
+
+static void container_close_func(struct sway_container *container, void *data) {
+ if (container->type == C_VIEW) {
+ view_close(container->sway_view);
+ }
+}
+
+struct sway_container *container_close(struct sway_container *con) {
+ if (!sway_assert(con != NULL,
+ "container_close called with a NULL container")) {
+ return NULL;
+ }
+
+ struct sway_container *parent = con->parent;
+
+ if (con->type == C_VIEW) {
+ view_close(con->sway_view);
+ } else {
+ container_for_each_descendant_dfs(con, container_close_func, NULL);
+ }
+
+ return parent;
+}
+
+struct sway_container *container_view_create(struct sway_container *sibling,
+ struct sway_view *sway_view) {
+ if (!sway_assert(sibling,
+ "container_view_create called with NULL sibling/parent")) {
+ return NULL;
+ }
+ const char *title = view_get_title(sway_view);
+ struct sway_container *swayc = container_create(C_VIEW);
+ wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s",
+ swayc, title, sibling, sibling ? sibling->type : 0, sibling->name);
+ // Setup values
+ swayc->sway_view = sway_view;
+ swayc->name = title ? strdup(title) : NULL;
+ swayc->width = 0;
+ swayc->height = 0;
+
+ if (sibling->type == C_WORKSPACE) {
+ // Case of focused workspace, just create as child of it
+ container_add_child(sibling, swayc);
+ } else {
+ // Regular case, create as sibling of current container
+ container_add_sibling(sibling, swayc);
+ }
+ container_create_notify(swayc);
+ return swayc;
+}
+
+void container_descendants(struct sway_container *root,
+ enum sway_container_type type,
+ void (*func)(struct sway_container *item, void *data), void *data) {
+ for (int i = 0; i < root->children->length; ++i) {
+ struct sway_container *item = root->children->items[i];
+ if (item->type == type) {
+ func(item, data);
+ }
+ if (item->children && item->children->length) {
+ container_descendants(item, type, func, data);
+ }
+ }
+}
+
+struct sway_container *container_find(struct sway_container *container,
+ bool (*test)(struct sway_container *view, void *data), void *data) {
+ if (!container->children) {
+ return NULL;
+ }
+ // TODO: floating windows
+ for (int i = 0; i < container->children->length; ++i) {
+ struct sway_container *child = container->children->items[i];
+ if (test(child, data)) {
+ return child;
+ } else {
+ struct sway_container *res = container_find(child, test, data);
+ if (res) {
+ return res;
+ }
+ }
+ }
+ return NULL;
+}
+
+struct sway_container *container_parent(struct sway_container *container,
+ enum sway_container_type type) {
+ if (!sway_assert(container, "container is NULL")) {
+ return NULL;
+ }
+ if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
+ return NULL;
+ }
+ do {
+ container = container->parent;
+ } while (container && container->type != type);
+ return container;
+}
+
+struct sway_container *container_at(struct sway_container *parent,
+ double lx, double ly,
+ struct wlr_surface **surface, double *sx, double *sy) {
+ list_t *queue = get_bfs_queue();
+ if (!queue) {
+ return NULL;
+ }
+
+ list_add(queue, parent);
+
+ struct sway_container *swayc = NULL;
+ while (queue->length) {
+ swayc = queue->items[0];
+ list_del(queue, 0);
+ if (swayc->type == C_VIEW) {
+ struct sway_view *sview = swayc->sway_view;
+ struct sway_container *soutput = container_parent(swayc, C_OUTPUT);
+ struct wlr_box *output_box =
+ wlr_output_layout_get_box(
+ root_container.sway_root->output_layout,
+ soutput->sway_output->wlr_output);
+ double ox = lx - output_box->x;
+ double oy = ly - output_box->y;
+ double view_sx = ox - swayc->x;
+ double view_sy = oy - swayc->y;
+
+ double _sx, _sy;
+ struct wlr_surface *_surface;
+ switch (sview->type) {
+ case SWAY_VIEW_XWAYLAND:
+ _surface = wlr_surface_surface_at(sview->surface,
+ view_sx, view_sy, &_sx, &_sy);
+ break;
+ case SWAY_VIEW_WL_SHELL:
+ _surface = wlr_wl_shell_surface_surface_at(
+ sview->wlr_wl_shell_surface,
+ view_sx, view_sy, &_sx, &_sy);
+ break;
+ case SWAY_VIEW_XDG_SHELL_V6:
+ // the top left corner of the sway container is the
+ // coordinate of the top left corner of the window geometry
+ view_sx += sview->wlr_xdg_surface_v6->geometry.x;
+ view_sy += sview->wlr_xdg_surface_v6->geometry.y;
+
+ _surface = wlr_xdg_surface_v6_surface_at(
+ sview->wlr_xdg_surface_v6,
+ view_sx, view_sy, &_sx, &_sy);
+ break;
+ }
+ if (_surface) {
+ *sx = _sx;
+ *sy = _sy;
+ *surface = _surface;
+ return swayc;
+ }
+ } else {
+ list_cat(queue, swayc->children);
+ }
+ }
+
+ return NULL;
+}
+
+void container_for_each_descendant_dfs(struct sway_container *container,
+ void (*f)(struct sway_container *container, void *data),
+ void *data) {
+ if (container) {
+ if (container->children) {
+ for (int i = 0; i < container->children->length; ++i) {
+ struct sway_container *child =
+ container->children->items[i];
+ container_for_each_descendant_dfs(child, f, data);
+ }
+ }
+ f(container, data);
+ }
+}
+
+void container_for_each_descendant_bfs(struct sway_container *con,
+ void (*f)(struct sway_container *con, void *data), void *data) {
+ list_t *queue = get_bfs_queue();
+ if (!queue) {
+ return;
+ }
+
+ if (queue == NULL) {
+ wlr_log(L_ERROR, "could not allocate list");
+ return;
+ }
+
+ list_add(queue, con);
+
+ struct sway_container *current = NULL;
+ while (queue->length) {
+ current = queue->items[0];
+ list_del(queue, 0);
+ f(current, data);
+ // TODO floating containers
+ list_cat(queue, current->children);
+ }
+}
+
+bool container_has_anscestor(struct sway_container *descendant,
+ struct sway_container *anscestor) {
+ while (descendant->type != C_ROOT) {
+ descendant = descendant->parent;
+ if (descendant == anscestor) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool find_child_func(struct sway_container *con, void *data) {
+ struct sway_container *child = data;
+ return con == child;
+}
+
+bool container_has_child(struct sway_container *con,
+ struct sway_container *child) {
+ if (con == NULL || con->type == C_VIEW || con->children->length == 0) {
+ return false;
+ }
+ return container_find(con, find_child_func, child);
+}
+
+void container_damage_whole(struct sway_container *con) {
+ struct sway_container *output = con;
+ if (output->type != C_OUTPUT) {
+ output = container_parent(output, C_OUTPUT);
+ }
+ output_damage_whole_container(output->sway_output, con);
+}
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <ctype.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include "sway/debug.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/output.h"
+#include "sway/tree/workspace.h"
+#include "sway/tree/view.h"
+#include "sway/input/seat.h"
+#include "sway/ipc-server.h"
+#include "list.h"
+#include "log.h"
+
+struct sway_container root_container;
+
+static void output_layout_handle_change(struct wl_listener *listener,
+ void *data) {
+ struct wlr_output_layout *output_layout =
+ root_container.sway_root->output_layout;
+ const struct wlr_box *layout_box =
+ wlr_output_layout_get_box(output_layout, NULL);
+ root_container.x = layout_box->x;
+ root_container.y = layout_box->y;
+ root_container.width = layout_box->width;
+ root_container.height = layout_box->height;
+
+ arrange_windows(&root_container, layout_box->width, layout_box->height);
+}
+
+struct sway_container *container_set_layout(struct sway_container *container,
+ enum sway_container_layout layout) {
+ if (container->type == C_WORKSPACE) {
+ container->workspace_layout = layout;
+ if (layout == L_HORIZ || layout == L_VERT) {
+ container->layout = layout;
+ }
+ } else {
+ container->layout = layout;
+ }
+ return container;
+}
+
+void layout_init(void) {
+ root_container.id = 0; // normally assigned in new_swayc()
+ root_container.type = C_ROOT;
+ root_container.layout = L_NONE;
+ root_container.name = strdup("root");
+ root_container.children = create_list();
+ wl_signal_init(&root_container.events.destroy);
+
+ root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
+ root_container.sway_root->output_layout = wlr_output_layout_create();
+ wl_list_init(&root_container.sway_root->xwayland_unmanaged);
+ wl_signal_init(&root_container.sway_root->events.new_container);
+
+ root_container.sway_root->output_layout_change.notify =
+ output_layout_handle_change;
+ wl_signal_add(&root_container.sway_root->output_layout->events.change,
+ &root_container.sway_root->output_layout_change);
+}
+
+static int index_child(const struct sway_container *child) {
+ // TODO handle floating
+ struct sway_container *parent = child->parent;
+ int i, len;
+ len = parent->children->length;
+ for (i = 0; i < len; ++i) {
+ if (parent->children->items[i] == child) {
+ break;
+ }
+ }
+
+ if (!sway_assert(i < len, "Stray container")) {
+ return -1;
+ }
+ return i;
+}
+
+void container_insert_child(struct sway_container *parent,
+ struct sway_container *child, int i) {
+ struct sway_container *old_parent = child->parent;
+ if (old_parent) {
+ container_remove_child(child);
+ }
+ wlr_log(L_DEBUG, "Inserting id:%zd at index %d", child->id, i);
+ list_insert(parent->children, i, child);
+ child->parent = parent;
+ wl_signal_emit(&child->events.reparent, old_parent);
+}
+
+struct sway_container *container_add_sibling(struct sway_container *fixed,
+ struct sway_container *active) {
+ // TODO handle floating
+ struct sway_container *old_parent = NULL;
+ if (active->parent) {
+ old_parent = active->parent;
+ container_remove_child(active);
+ }
+ struct sway_container *parent = fixed->parent;
+ int i = index_child(fixed);
+ list_insert(parent->children, i + 1, active);
+ active->parent = parent;
+ wl_signal_emit(&active->events.reparent, old_parent);
+ return active->parent;
+}
+
+void container_add_child(struct sway_container *parent,
+ struct sway_container *child) {
+ wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)",
+ child, child->type, child->width, child->height,
+ parent, parent->type, parent->width, parent->height);
+ list_add(parent->children, child);
+ child->parent = parent;
+}
+
+struct sway_container *container_remove_child(struct sway_container *child) {
+ struct sway_container *parent = child->parent;
+ for (int i = 0; i < parent->children->length; ++i) {
+ if (parent->children->items[i] == child) {
+ list_del(parent->children, i);
+ break;
+ }
+ }
+ child->parent = NULL;
+ return parent;
+}
+
+void container_move_to(struct sway_container *container,
+ struct sway_container *destination) {
+ if (container == destination
+ || container_has_anscestor(container, destination)) {
+ return;
+ }
+ struct sway_container *old_parent = container_remove_child(container);
+ container->width = container->height = 0;
+ struct sway_container *new_parent;
+ if (destination->type == C_VIEW) {
+ new_parent = container_add_sibling(destination, container);
+ } else {
+ new_parent = destination;
+ container_add_child(destination, container);
+ }
+ wl_signal_emit(&container->events.reparent, old_parent);
+ if (container->type == C_WORKSPACE) {
+ struct sway_seat *seat = input_manager_get_default_seat(
+ input_manager);
+ if (old_parent->children->length == 0) {
+ char *ws_name = workspace_next_name(old_parent->name);
+ struct sway_container *ws =
+ workspace_create(old_parent, ws_name);
+ free(ws_name);
+ seat_set_focus(seat, ws);
+ }
+ container_sort_workspaces(new_parent);
+ seat_set_focus(seat, new_parent);
+ }
+ if (old_parent) {
+ arrange_windows(old_parent, -1, -1);
+ }
+ arrange_windows(new_parent, -1, -1);
+}
+
+static bool sway_dir_to_wlr(enum movement_direction dir,
+ enum wlr_direction *out) {
+ switch (dir) {
+ case MOVE_UP:
+ *out = WLR_DIRECTION_UP;
+ break;
+ case MOVE_DOWN:
+ *out = WLR_DIRECTION_DOWN;
+ break;
+ case MOVE_LEFT:
+ *out = WLR_DIRECTION_LEFT;
+ break;
+ case MOVE_RIGHT:
+ *out = WLR_DIRECTION_RIGHT;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static bool is_parallel(enum sway_container_layout layout,
+ enum movement_direction dir) {
+ switch (layout) {
+ case L_TABBED:
+ case L_STACKED:
+ case L_HORIZ:
+ return dir == MOVE_LEFT || dir == MOVE_RIGHT;
+ case L_VERT:
+ return dir == MOVE_UP || dir == MOVE_DOWN;
+ default:
+ return false;
+ }
+}
+
+static enum movement_direction invert_movement(enum movement_direction dir) {
+ switch (dir) {
+ case MOVE_LEFT:
+ return MOVE_RIGHT;
+ case MOVE_RIGHT:
+ return MOVE_LEFT;
+ case MOVE_UP:
+ return MOVE_DOWN;
+ case MOVE_DOWN:
+ return MOVE_UP;
+ default:
+ sway_assert(0, "This function expects left|right|up|down");
+ return MOVE_LEFT;
+ }
+}
+
+static int move_offs(enum movement_direction move_dir) {
+ return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
+}
+
+/* Gets the index of the most extreme member based on the movement offset */
+static int container_limit(struct sway_container *container,
+ enum movement_direction move_dir) {
+ return move_offs(move_dir) < 0 ? 0 : container->children->length;
+}
+
+/* Takes one child, sets it aside, wraps the rest of the children in a new
+ * container, switches the layout of the workspace, and drops the child back in.
+ * In other words, rejigger it. */
+static void workspace_rejigger(struct sway_container *ws,
+ struct sway_container *child, enum movement_direction move_dir) {
+ struct sway_container *original_parent = child->parent;
+ struct sway_container *new_parent =
+ container_split(ws, ws->layout);
+
+ container_remove_child(child);
+ for (int i = 0; i < ws->children->length; ++i) {
+ struct sway_container *_child = ws->children->items[i];
+ container_move_to(new_parent, _child);
+ }
+
+ int index = move_offs(move_dir);
+ container_insert_child(ws, child, index < 0 ? 0 : 1);
+ container_set_layout(ws,
+ move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT);
+
+ container_flatten(ws);
+ container_reap_empty_recursive(original_parent);
+ wl_signal_emit(&child->events.reparent, original_parent);
+ container_create_notify(new_parent);
+ arrange_windows(ws, -1, -1);
+}
+
+void container_move(struct sway_container *container,
+ enum movement_direction move_dir, int move_amt) {
+ if (!sway_assert(
+ container->type != C_CONTAINER || container->type != C_VIEW,
+ "Can only move containers and views")) {
+ return;
+ }
+ int offs = move_offs(move_dir);
+
+ struct sway_container *sibling = NULL;
+ struct sway_container *current = container;
+ struct sway_container *parent = current->parent;
+
+ if (parent != container_flatten(parent)) {
+ // Special case: we were the last one in this container, so flatten it
+ // and leave
+ update_debug_tree();
+ return;
+ }
+
+ while (!sibling) {
+ if (current->type == C_ROOT) {
+ return;
+ }
+
+ parent = current->parent;
+ wlr_log(L_DEBUG, "Visiting %p %s '%s'", current,
+ container_type_to_str(current->type), current->name);
+
+ int index = index_child(current);
+
+ switch (current->type) {
+ case C_OUTPUT: {
+ enum wlr_direction wlr_dir;
+ sway_dir_to_wlr(move_dir, &wlr_dir);
+ double ref_lx = current->x + current->width / 2;
+ double ref_ly = current->y + current->height / 2;
+ struct wlr_output *next = wlr_output_layout_adjacent_output(
+ root_container.sway_root->output_layout, wlr_dir,
+ current->sway_output->wlr_output, ref_lx, ref_ly);
+ if (!next) {
+ wlr_log(L_DEBUG, "Hit edge of output, nowhere else to go");
+ return;
+ }
+ struct sway_output *next_output = next->data;
+ current = next_output->swayc;
+ wlr_log(L_DEBUG, "Selected next output (%s)", current->name);
+ // Select workspace and get outta here
+ current = seat_get_focus_inactive(
+ config->handler_context.seat, current);
+ if (current->type != C_WORKSPACE) {
+ current = container_parent(current, C_WORKSPACE);
+ }
+ sibling = current;
+ break;
+ }
+ case C_WORKSPACE:
+ if (!is_parallel(current->layout, move_dir)) {
+ if (current->children->length > 2) {
+ wlr_log(L_DEBUG, "Rejiggering the workspace (%d kiddos)",
+ current->children->length);
+ workspace_rejigger(current, container, move_dir);
+ } else if (current->children->length == 2) {
+ wlr_log(L_DEBUG, "Changing workspace layout");
+ container_set_layout(current,
+ move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ?
+ L_HORIZ : L_VERT);
+ container_insert_child(current, container, offs < 0 ? 0 : 1);
+ arrange_windows(current, -1, -1);
+ }
+ return;
+ } else {
+ wlr_log(L_DEBUG, "Selecting output");
+ current = current->parent;
+ }
+ break;
+ case C_CONTAINER:
+ case C_VIEW:
+ if (is_parallel(parent->layout, move_dir)) {
+ if ((index == parent->children->length - 1 && offs > 0)
+ || (index == 0 && offs < 0)) {
+ if (current->parent == container->parent) {
+ wlr_log(L_DEBUG, "Hit limit, selecting parent");
+ current = current->parent;
+ } else {
+ wlr_log(L_DEBUG, "Hit limit, "
+ "promoting descendant to sibling");
+ // Special case
+ struct sway_container *old_parent = container->parent;
+ container_insert_child(current->parent, container,
+ index + (offs < 0 ? 0 : 1));
+ container->width = container->height = 0;
+ arrange_windows(current->parent, -1, -1);
+ arrange_windows(old_parent, -1, -1);
+ return;
+ }
+ } else {
+ sibling = parent->children->items[index + offs];
+ wlr_log(L_DEBUG, "Selecting sibling id:%zd", sibling->id);
+ }
+ } else {
+ wlr_log(L_DEBUG, "Moving up to find a parallel container");
+ current = current->parent;
+ }
+ break;
+ default:
+ sway_assert(0, "Not expecting to see container of type %s here",
+ container_type_to_str(current->type));
+ return;
+ }
+ }
+
+ // Part two: move stuff around
+ int index = index_child(container);
+ struct sway_container *old_parent = container->parent;
+
+ while (sibling) {
+ switch (sibling->type) {
+ case C_VIEW:
+ if (sibling->parent == container->parent) {
+ wlr_log(L_DEBUG, "Swapping siblings");
+ sibling->parent->children->items[index + offs] = container;
+ sibling->parent->children->items[index] = sibling;
+ arrange_windows(sibling->parent, -1, -1);
+ } else {
+ wlr_log(L_DEBUG, "Promoting to sibling of cousin");
+ container_insert_child(sibling->parent, container,
+ index_child(sibling) + (offs > 0 ? 0 : 1));
+ container->width = container->height = 0;
+ arrange_windows(sibling->parent, -1, -1);
+ arrange_windows(old_parent, -1, -1);
+ }
+ sibling = NULL;
+ break;
+ case C_WORKSPACE: // Note: only in the case of moving between outputs
+ case C_CONTAINER:
+ if (is_parallel(sibling->layout, move_dir)) {
+ int limit = container_limit(sibling, invert_movement(move_dir));
+ wlr_log(L_DEBUG, "limit: %d", limit);
+ wlr_log(L_DEBUG,
+ "Reparenting container (parallel) to index %d "
+ "(move dir: %d)", limit, move_dir);
+ container_insert_child(sibling, container, limit);
+ container->width = container->height = 0;
+ arrange_windows(sibling, -1, -1);
+ arrange_windows(old_parent, -1, -1);
+ sibling = NULL;
+ } else {
+ wlr_log(L_DEBUG, "Reparenting container (perpendicular)");
+ container_remove_child(container);
+ struct sway_container *focus_inactive = seat_get_focus_inactive(
+ config->handler_context.seat, sibling);
+ if (focus_inactive) {
+ while (focus_inactive->parent != sibling) {
+ focus_inactive = focus_inactive->parent;
+ }
+ wlr_log(L_DEBUG, "Focus inactive: id:%zd",
+ focus_inactive->id);
+ sibling = focus_inactive;
+ continue;
+ } else if (sibling->children->length) {
+ wlr_log(L_DEBUG, "No focus-inactive, adding arbitrarily");
+ container_add_sibling(sibling->children->items[0], container);
+ } else {
+ wlr_log(L_DEBUG, "No kiddos, adding container alone");
+ container_add_child(sibling, container);
+ }
+ container->width = container->height = 0;
+ arrange_windows(sibling, -1, -1);
+ arrange_windows(old_parent, -1, -1);
+ sibling = NULL;
+ }
+ break;
+ default:
+ sway_assert(0, "Not expecting to see container of type %s here",
+ container_type_to_str(sibling->type));
+ return;
+ }
+ }
+
+ if (old_parent) {
+ seat_set_focus(config->handler_context.seat, old_parent);
+ seat_set_focus(config->handler_context.seat, container);
+ }
+
+ struct sway_container *last_ws = old_parent;
+ struct sway_container *next_ws = container->parent;
+ if (last_ws && last_ws->type != C_WORKSPACE) {
+ last_ws = container_parent(last_ws, C_WORKSPACE);
+ }
+ if (next_ws && next_ws->type != C_WORKSPACE) {
+ next_ws = container_parent(next_ws, C_WORKSPACE);
+ }
+ if (last_ws && next_ws && last_ws != next_ws) {
+ ipc_event_workspace(last_ws, container, "focus");
+ }
+}
+
+enum sway_container_layout container_get_default_layout(
+ struct sway_container *con) {
+ if (con->type != C_OUTPUT) {
+ con = container_parent(con, C_OUTPUT);
+ }
+
+ if (!sway_assert(con != NULL,
+ "container_get_default_layout must be called on an attached"
+ " container below the root container")) {
+ return 0;
+ }
+
+ if (config->default_layout != L_NONE) {
+ return config->default_layout;
+ } else if (config->default_orientation != L_NONE) {
+ return config->default_orientation;
+ } else if (con->width >= con->height) {
+ return L_HORIZ;
+ } else {
+ return L_VERT;
+ }
+}
+
+static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
+ struct sway_container *a = *(void **)_a;
+ struct sway_container *b = *(void **)_b;
+ int retval = 0;
+
+ if (isdigit(a->name[0]) && isdigit(b->name[0])) {
+ int a_num = strtol(a->name, NULL, 10);
+ int b_num = strtol(b->name, NULL, 10);
+ retval = (a_num < b_num) ? -1 : (a_num > b_num);
+ } else if (isdigit(a->name[0])) {
+ retval = -1;
+ } else if (isdigit(b->name[0])) {
+ retval = 1;
+ }
+
+ return retval;
+}
+
+void container_sort_workspaces(struct sway_container *output) {
+ list_stable_sort(output->children, sort_workspace_cmp_qsort);
+}
+
+static void apply_horiz_layout(struct sway_container *container, const double x,
+ const double y, const double width,
+ const double height, const int start,
+ const int end);
+
+static void apply_vert_layout(struct sway_container *container, const double x,
+ const double y, const double width,
+ const double height, const int start,
+ const int end);
+
+void arrange_windows(struct sway_container *container,
+ double width, double height) {
+ if (config->reloading) {
+ return;
+ }
+ int i;
+ if (width == -1 || height == -1) {
+ width = container->width;
+ height = container->height;
+ }
+ // pixels are indivisible. if we don't round the pixels, then the view
+ // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
+ // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
+ width = floor(width);
+ height = floor(height);
+
+ wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
+ container->name, container->width, container->height, container->x,
+ container->y);
+
+ double x = 0, y = 0;
+ switch (container->type) {
+ case C_ROOT:
+ for (i = 0; i < container->children->length; ++i) {
+ struct sway_container *output = container->children->items[i];
+ const struct wlr_box *output_box = wlr_output_layout_get_box(
+ container->sway_root->output_layout,
+ output->sway_output->wlr_output);
+ output->x = output_box->x;
+ output->y = output_box->y;
+ output->width = output_box->width;
+ output->height = output_box->height;
+ wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f",
+ output->name, output->x, output->y);
+ arrange_windows(output, output_box->width, output_box->height);
+ }
+ return;
+ case C_OUTPUT:
+ // arrange all workspaces:
+ for (i = 0; i < container->children->length; ++i) {
+ struct sway_container *child = container->children->items[i];
+ arrange_windows(child, -1, -1);
+ }
+ return;
+ case C_WORKSPACE:
+ {
+ struct sway_container *output =
+ container_parent(container, C_OUTPUT);
+ struct wlr_box *area = &output->sway_output->usable_area;
+ wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d",
+ area->width, area->height, area->x, area->y);
+ container->width = width = area->width;
+ container->height = height = area->height;
+ container->x = x = area->x;
+ container->y = y = area->y;
+ wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f",
+ container->name, container->x, container->y);
+ }
+ // children are properly handled below
+ break;
+ case C_VIEW:
+ {
+ container->width = width;
+ container->height = height;
+ view_configure(container->sway_view, container->x, container->y,
+ container->width, container->height);
+ wlr_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f",
+ container->width, container->height,
+ container->x, container->y);
+ }
+ return;
+ default:
+ container->width = width;
+ container->height = height;
+ x = container->x;
+ y = container->y;
+ break;
+ }
+
+ switch (container->layout) {
+ case L_HORIZ:
+ apply_horiz_layout(container, x, y, width, height, 0,
+ container->children->length);
+ break;
+ case L_VERT:
+ apply_vert_layout(container, x, y, width, height, 0,
+ container->children->length);
+ break;
+ default:
+ wlr_log(L_DEBUG, "TODO: arrange layout type %d", container->layout);
+ apply_horiz_layout(container, x, y, width, height, 0,
+ container->children->length);
+ break;
+ }
+ container_damage_whole(container);
+ // TODO: Make this less shitty
+ update_debug_tree();
+}
+
+static void apply_horiz_layout(struct sway_container *container,
+ const double x, const double y,
+ const double width, const double height,
+ const int start, const int end) {
+ double scale = 0;
+ // Calculate total width
+ for (int i = start; i < end; ++i) {
+ double *old_width =
+ &((struct sway_container *)container->children->items[i])->width;
+ if (*old_width <= 0) {
+ if (end - start > 1) {
+ *old_width = width / (end - start - 1);
+ } else {
+ *old_width = width;
+ }
+ }
+ scale += *old_width;
+ }
+ scale = width / scale;
+
+ // Resize windows
+ double child_x = x;
+ if (scale > 0.1) {
+ wlr_log(L_DEBUG, "Arranging %p horizontally", container);
+ for (int i = start; i < end; ++i) {
+ struct sway_container *child = container->children->items[i];
+ wlr_log(L_DEBUG,
+ "Calculating arrangement for %p:%d (will scale %f by %f)",
+ child, child->type, width, scale);
+
+ if (child->type == C_VIEW) {
+ view_configure(child->sway_view, child_x, y, child->width,
+ child->height);
+ } else {
+ child->x = child_x;
+ child->y = y;
+ }
+
+ if (i == end - 1) {
+ double remaining_width = x + width - child_x;
+ arrange_windows(child, remaining_width, height);
+ } else {
+ arrange_windows(child, child->width * scale, height);
+ }
+ child_x += child->width;
+ }
+
+ // update focused view border last because it may
+ // depend on the title bar geometry of its siblings.
+ /* TODO WLR
+ if (focused && container->children->length > 1) {
+ update_container_border(focused);
+ }
+ */
+ }
+}
+
+void apply_vert_layout(struct sway_container *container,
+ const double x, const double y,
+ const double width, const double height, const int start,
+ const int end) {
+ int i;
+ double scale = 0;
+ // Calculate total height
+ for (i = start; i < end; ++i) {
+ double *old_height =
+ &((struct sway_container *)container->children->items[i])->height;
+ if (*old_height <= 0) {
+ if (end - start > 1) {
+ *old_height = height / (end - start - 1);
+ } else {
+ *old_height = height;
+ }
+ }
+ scale += *old_height;
+ }
+ scale = height / scale;
+
+ // Resize
+ double child_y = y;
+ if (scale > 0.1) {
+ wlr_log(L_DEBUG, "Arranging %p vertically", container);
+ for (i = start; i < end; ++i) {
+ struct sway_container *child = container->children->items[i];
+ wlr_log(L_DEBUG,
+ "Calculating arrangement for %p:%d (will scale %f by %f)",
+ child, child->type, height, scale);
+ if (child->type == C_VIEW) {
+ view_configure(child->sway_view, x, child_y, child->width,
+ child->height);
+ } else {
+ child->x = x;
+ child->y = child_y;
+ }
+
+ if (i == end - 1) {
+ double remaining_height = y + height - child_y;
+ arrange_windows(child, width, remaining_height);
+ } else {
+ arrange_windows(child, width, child->height * scale);
+ }
+ child_y += child->height;
+ }
+
+ // update focused view border last because it may
+ // depend on the title bar geometry of its siblings.
+ /* TODO WLR
+ if (focused && container->children->length > 1) {
+ update_container_border(focused);
+ }
+ */
+ }
+}
+
+/**
+ * Get swayc in the direction of newly entered output.
+ */
+static struct sway_container *get_swayc_in_output_direction(
+ struct sway_container *output, enum movement_direction dir,
+ struct sway_seat *seat) {
+ if (!output) {
+ return NULL;
+ }
+
+ struct sway_container *ws = seat_get_focus_inactive(seat, output);
+ if (ws->type != C_WORKSPACE) {
+ ws = container_parent(ws, C_WORKSPACE);
+ }
+
+ if (ws == NULL) {
+ wlr_log(L_ERROR, "got an output without a workspace");
+ return NULL;
+ }
+
+ if (ws->children->length > 0) {
+ switch (dir) {
+ case MOVE_LEFT:
+ // get most right child of new output
+ return ws->children->items[ws->children->length-1];
+ case MOVE_RIGHT:
+ // get most left child of new output
+ return ws->children->items[0];
+ case MOVE_UP:
+ case MOVE_DOWN: {
+ struct sway_container *focused =
+ seat_get_focus_inactive(seat, ws);
+ if (focused && focused->parent) {
+ struct sway_container *parent = focused->parent;
+ if (parent->layout == L_VERT) {
+ if (dir == MOVE_UP) {
+ // get child furthest down on new output
+ int idx = parent->children->length - 1;
+ return parent->children->items[idx];
+ } else if (dir == MOVE_DOWN) {
+ // get child furthest up on new output
+ return parent->children->items[0];
+ }
+ }
+ return focused;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return ws;
+}
+
+static void get_layout_center_position(struct sway_container *container,
+ int *x, int *y) {
+ // FIXME view coords are inconsistently referred to in layout/output systems
+ if (container->type == C_OUTPUT) {
+ *x = container->x + container->width/2;
+ *y = container->y + container->height/2;
+ } else {
+ struct sway_container *output = container_parent(container, C_OUTPUT);
+ if (container->type == C_WORKSPACE) {
+ // Workspace coordinates are actually wrong/arbitrary, but should
+ // be same as output.
+ *x = output->x;
+ *y = output->y;
+ } else {
+ *x = output->x + container->x;
+ *y = output->y + container->y;
+ }
+ }
+}
+
+static struct sway_container *sway_output_from_wlr(struct wlr_output *output) {
+ if (output == NULL) {
+ return NULL;
+ }
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *o = root_container.children->items[i];
+ if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) {
+ return o;
+ }
+ }
+ return NULL;
+}
+
+struct sway_container *container_get_in_direction(
+ struct sway_container *container, struct sway_seat *seat,
+ enum movement_direction dir) {
+ if (dir == MOVE_CHILD) {
+ return seat_get_focus_inactive(seat, container);
+ }
+
+ struct sway_container *parent = container->parent;
+ if (dir == MOVE_PARENT) {
+ if (parent->type == C_OUTPUT) {
+ return NULL;
+ } else {
+ return parent;
+ }
+ }
+
+ // TODO WLR fullscreen
+ /*
+ if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
+ wlr_log(L_DEBUG, "Moving from fullscreen view, skipping to output");
+ container = container_parent(container, C_OUTPUT);
+ get_layout_center_position(container, &abs_pos);
+ struct sway_container *output =
+ swayc_adjacent_output(container, dir, &abs_pos, true);
+ return get_swayc_in_output_direction(output, dir);
+ }
+ if (container->type == C_WORKSPACE && container->fullscreen) {
+ sway_log(L_DEBUG, "Moving to fullscreen view");
+ return container->fullscreen;
+ }
+ */
+
+ struct sway_container *wrap_candidate = NULL;
+ while (true) {
+ bool can_move = false;
+ int desired;
+ int idx = index_child(container);
+ if (parent->type == C_ROOT) {
+ enum wlr_direction wlr_dir = 0;
+ if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
+ "got invalid direction: %d", dir)) {
+ return NULL;
+ }
+ int lx, ly;
+ get_layout_center_position(container, &lx, &ly);
+ struct wlr_output_layout *layout =
+ root_container.sway_root->output_layout;
+ struct wlr_output *wlr_adjacent =
+ wlr_output_layout_adjacent_output(layout, wlr_dir,
+ container->sway_output->wlr_output, lx, ly);
+ struct sway_container *adjacent =
+ sway_output_from_wlr(wlr_adjacent);
+
+ if (!adjacent || adjacent == container) {
+ return wrap_candidate;
+ }
+ struct sway_container *next =
+ get_swayc_in_output_direction(adjacent, dir, seat);
+ if (next == NULL) {
+ return NULL;
+ }
+ if (next->children && next->children->length) {
+ // TODO consider floating children as well
+ return seat_get_focus_inactive_view(seat, next);
+ } else {
+ return next;
+ }
+ } else {
+ if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
+ if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
+ can_move = true;
+ desired = idx + (dir == MOVE_LEFT ? -1 : 1);
+ }
+ } else {
+ if (parent->layout == L_VERT || parent->layout == L_STACKED) {
+ can_move = true;
+ desired = idx + (dir == MOVE_UP ? -1 : 1);
+ }
+ }
+ }
+
+ if (can_move) {
+ // TODO handle floating
+ if (desired < 0 || desired >= parent->children->length) {
+ can_move = false;
+ int len = parent->children->length;
+ if (!wrap_candidate && len > 1) {
+ if (desired < 0) {
+ wrap_candidate = parent->children->items[len-1];
+ } else {
+ wrap_candidate = parent->children->items[0];
+ }
+ if (config->force_focus_wrapping) {
+ return wrap_candidate;
+ }
+ }
+ } else {
+ struct sway_container *desired_con = parent->children->items[desired];
+ wlr_log(L_DEBUG,
+ "cont %d-%p dir %i sibling %d: %p", idx,
+ container, dir, desired, desired_con);
+ struct sway_container *next = seat_get_focus_inactive_view(seat, desired_con);
+ return next;
+ }
+ }
+
+ if (!can_move) {
+ container = parent;
+ parent = parent->parent;
+ if (!parent) {
+ // wrapping is the last chance
+ return wrap_candidate;
+ }
+ }
+ }
+}
+
+struct sway_container *container_replace_child(struct sway_container *child,
+ struct sway_container *new_child) {
+ struct sway_container *parent = child->parent;
+ if (parent == NULL) {
+ return NULL;
+ }
+ int i = index_child(child);
+
+ // TODO floating
+ if (new_child->parent) {
+ container_remove_child(new_child);
+ }
+ parent->children->items[i] = new_child;
+ new_child->parent = parent;
+ child->parent = NULL;
+
+ // Set geometry for new child
+ new_child->x = child->x;
+ new_child->y = child->y;
+ new_child->width = child->width;
+ new_child->height = child->height;
+
+ // reset geometry for child
+ child->width = 0;
+ child->height = 0;
+
+ return parent;
+}
+
+struct sway_container *container_split(struct sway_container *child,
+ enum sway_container_layout layout) {
+ // TODO floating: cannot split a floating container
+ if (!sway_assert(child, "child cannot be null")) {
+ return NULL;
+ }
+ if (child->type == C_WORKSPACE && child->children->length == 0) {
+ // Special case: this just behaves like splitt
+ child->prev_layout = child->layout;
+ child->layout = layout;
+ arrange_windows(child, -1, -1);
+ return child;
+ }
+
+ struct sway_container *cont = container_create(C_CONTAINER);
+
+ wlr_log(L_DEBUG, "creating container %p around %p", cont, child);
+
+ cont->prev_layout = L_NONE;
+ cont->width = child->width;
+ cont->height = child->height;
+ cont->x = child->x;
+ cont->y = child->y;
+
+ if (child->type == C_WORKSPACE) {
+ struct sway_seat *seat = input_manager_get_default_seat(input_manager);
+ struct sway_container *workspace = child;
+ bool set_focus = (seat_get_focus(seat) == workspace);
+
+ while (workspace->children->length) {
+ struct sway_container *ws_child = workspace->children->items[0];
+ container_remove_child(ws_child);
+ container_add_child(cont, ws_child);
+ }
+
+ container_add_child(workspace, cont);
+ enum sway_container_layout old_layout = workspace->layout;
+ container_set_layout(workspace, layout);
+ cont->layout = old_layout;
+
+ if (set_focus) {
+ seat_set_focus(seat, cont);
+ }
+ } else {
+ cont->layout = layout;
+ container_replace_child(child, cont);
+ container_add_child(cont, child);
+ }
+
+ return cont;
+}
+
+void container_recursive_resize(struct sway_container *container,
+ double amount, enum resize_edge edge) {
+ bool layout_match = true;
+ wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount);
+ if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) {
+ container->width += amount;
+ layout_match = container->layout == L_HORIZ;
+ } else if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) {
+ container->height += amount;
+ layout_match = container->layout == L_VERT;
+ }
+ if (container->children) {
+ for (int i = 0; i < container->children->length; i++) {
+ struct sway_container *child = container->children->items[i];
+ double amt = layout_match ?
+ amount / container->children->length : amount;
+ container_recursive_resize(child, amt, edge);
+ }
+ }
+}
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <string.h>
+#include <strings.h>
+#include "sway/output.h"
+#include "sway/tree/output.h"
+#include "sway/tree/workspace.h"
+#include "log.h"
+
+struct sway_container *output_create(
+ struct sway_output *sway_output) {
+ struct wlr_box size;
+ wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
+ &size.height);
+
+ const char *name = sway_output->wlr_output->name;
+ char identifier[128];
+ output_get_identifier(identifier, sizeof(identifier), sway_output);
+
+ struct output_config *oc = NULL, *all = NULL;
+ for (int i = 0; i < config->output_configs->length; ++i) {
+ struct output_config *cur = config->output_configs->items[i];
+
+ if (strcasecmp(name, cur->name) == 0 ||
+ strcasecmp(identifier, cur->name) == 0) {
+ wlr_log(L_DEBUG, "Matched output config for %s", name);
+ oc = cur;
+ }
+ if (strcasecmp("*", cur->name) == 0) {
+ wlr_log(L_DEBUG, "Matched wildcard output config for %s", name);
+ all = cur;
+ }
+
+ if (oc && all) {
+ break;
+ }
+ }
+ if (!oc) {
+ oc = all;
+ }
+
+ if (oc && !oc->enabled) {
+ return NULL;
+ }
+
+ struct sway_container *output = container_create(C_OUTPUT);
+ output->sway_output = sway_output;
+ output->name = strdup(name);
+ if (output->name == NULL) {
+ container_destroy(output);
+ return NULL;
+ }
+
+ apply_output_config(oc, output);
+ container_add_child(&root_container, output);
+ load_swaybars();
+
+ // Create workspace
+ char *ws_name = workspace_next_name(output->name);
+ wlr_log(L_DEBUG, "Creating default workspace %s", ws_name);
+ struct sway_container *ws = workspace_create(output, ws_name);
+ // Set each seat's focus if not already set
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ if (!seat->has_focus) {
+ seat_set_focus(seat, ws);
+ }
+ }
+
+ free(ws_name);
+ container_create_notify(output);
+ return output;
+}
+
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 @@
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_output_layout.h>
+#include "log.h"
+#include "sway/output.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/view.h"
+
+void view_init(struct sway_view *view, enum sway_view_type type,
+ const struct sway_view_impl *impl) {
+ view->type = type;
+ view->impl = impl;
+ wl_signal_init(&view->events.unmap);
+}
+
+void view_destroy(struct sway_view *view) {
+ if (view == NULL) {
+ return;
+ }
+
+ if (view->surface != NULL) {
+ view_unmap(view);
+ }
+
+ container_destroy(view->swayc);
+
+ if (view->impl->destroy) {
+ view->impl->destroy(view);
+ } else {
+ free(view);
+ }
+}
+
+const char *view_get_title(struct sway_view *view) {
+ if (view->impl->get_prop) {
+ return view->impl->get_prop(view, VIEW_PROP_TITLE);
+ }
+ return NULL;
+}
+
+const char *view_get_app_id(struct sway_view *view) {
+ if (view->impl->get_prop) {
+ return view->impl->get_prop(view, VIEW_PROP_APP_ID);
+ }
+ return NULL;
+}
+
+const char *view_get_class(struct sway_view *view) {
+ if (view->impl->get_prop) {
+ return view->impl->get_prop(view, VIEW_PROP_CLASS);
+ }
+ return NULL;
+}
+
+const char *view_get_instance(struct sway_view *view) {
+ if (view->impl->get_prop) {
+ return view->impl->get_prop(view, VIEW_PROP_INSTANCE);
+ }
+ return NULL;
+}
+
+void view_configure(struct sway_view *view, double ox, double oy, int width,
+ int height) {
+ if (view->impl->configure) {
+ view->impl->configure(view, ox, oy, width, height);
+ }
+}
+
+void view_set_activated(struct sway_view *view, bool activated) {
+ if (view->impl->set_activated) {
+ view->impl->set_activated(view, activated);
+ }
+}
+
+void view_close(struct sway_view *view) {
+ if (view->impl->close) {
+ view->impl->close(view);
+ }
+}
+
+void view_damage(struct sway_view *view, bool whole) {
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *cont = root_container.children->items[i];
+ if (cont->type == C_OUTPUT) {
+ output_damage_view(cont->sway_output, view, whole);
+ }
+ }
+}
+
+static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
+ struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
+
+ box->x = output->x + view->swayc->x;
+ box->y = output->y + view->swayc->y;
+ box->width = view->width;
+ box->height = view->height;
+}
+
+void view_for_each_surface(struct sway_view *view,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ if (view->impl->for_each_surface) {
+ view->impl->for_each_surface(view, iterator, user_data);
+ } else {
+ wlr_surface_for_each_surface(view->surface, iterator, user_data);
+ }
+}
+
+static void view_subsurface_create(struct sway_view *view,
+ struct wlr_subsurface *subsurface);
+
+static void view_init_subsurfaces(struct sway_view *view,
+ struct wlr_surface *surface);
+
+static void view_handle_surface_new_subsurface(struct wl_listener *listener,
+ void *data) {
+ struct sway_view *view =
+ wl_container_of(listener, view, surface_new_subsurface);
+ struct wlr_subsurface *subsurface = data;
+ view_subsurface_create(view, subsurface);
+}
+
+static void surface_send_enter_iterator(struct wlr_surface *surface,
+ int x, int y, void *data) {
+ struct wlr_output *wlr_output = data;
+ wlr_surface_send_enter(surface, wlr_output);
+}
+
+static void surface_send_leave_iterator(struct wlr_surface *surface,
+ int x, int y, void *data) {
+ struct wlr_output *wlr_output = data;
+ wlr_surface_send_leave(surface, wlr_output);
+}
+
+static void view_handle_container_reparent(struct wl_listener *listener,
+ void *data) {
+ struct sway_view *view =
+ wl_container_of(listener, view, container_reparent);
+ struct sway_container *old_parent = data;
+
+ struct sway_container *old_output = old_parent;
+ if (old_output != NULL && old_output->type != C_OUTPUT) {
+ old_output = container_parent(old_output, C_OUTPUT);
+ }
+
+ struct sway_container *new_output = view->swayc->parent;
+ if (new_output != NULL && new_output->type != C_OUTPUT) {
+ new_output = container_parent(new_output, C_OUTPUT);
+ }
+
+ if (old_output == new_output) {
+ return;
+ }
+
+ if (old_output != NULL) {
+ view_for_each_surface(view, surface_send_leave_iterator,
+ old_output->sway_output->wlr_output);
+ }
+ if (new_output != NULL) {
+ view_for_each_surface(view, surface_send_enter_iterator,
+ new_output->sway_output->wlr_output);
+ }
+}
+
+void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
+ if (!sway_assert(view->surface == NULL, "cannot map mapped view")) {
+ return;
+ }
+
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus = seat_get_focus_inactive(seat,
+ &root_container);
+ struct sway_container *cont = container_view_create(focus, view);
+
+ view->surface = wlr_surface;
+ view->swayc = cont;
+
+ view_init_subsurfaces(view, wlr_surface);
+ wl_signal_add(&wlr_surface->events.new_subsurface,
+ &view->surface_new_subsurface);
+ view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
+
+ wl_signal_add(&view->swayc->events.reparent, &view->container_reparent);
+ view->container_reparent.notify = view_handle_container_reparent;
+
+ arrange_windows(cont->parent, -1, -1);
+ input_manager_set_focus(input_manager, cont);
+
+ view_damage(view, true);
+ view_handle_container_reparent(&view->container_reparent, NULL);
+}
+
+void view_unmap(struct sway_view *view) {
+ if (!sway_assert(view->surface != NULL, "cannot unmap unmapped view")) {
+ return;
+ }
+
+ wl_signal_emit(&view->events.unmap, view);
+
+ view_damage(view, true);
+
+ wl_list_remove(&view->surface_new_subsurface.link);
+ wl_list_remove(&view->container_reparent.link);
+
+ struct sway_container *parent = container_destroy(view->swayc);
+
+ view->swayc = NULL;
+ view->surface = NULL;
+
+ arrange_windows(parent, -1, -1);
+}
+
+void view_update_position(struct sway_view *view, double ox, double oy) {
+ if (view->swayc->x == ox && view->swayc->y == oy) {
+ return;
+ }
+
+ view_damage(view, true);
+ view->swayc->x = ox;
+ view->swayc->y = oy;
+ view_damage(view, true);
+}
+
+void view_update_size(struct sway_view *view, int width, int height) {
+ if (view->width == width && view->height == height) {
+ return;
+ }
+
+ view_damage(view, true);
+ view->width = width;
+ view->height = height;
+ view_damage(view, true);
+}
+
+
+static void view_subsurface_create(struct sway_view *view,
+ struct wlr_subsurface *subsurface) {
+ struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child));
+ if (child == NULL) {
+ wlr_log(L_ERROR, "Allocation failed");
+ return;
+ }
+ view_child_init(child, NULL, view, subsurface->surface);
+}
+
+static void view_child_handle_surface_commit(struct wl_listener *listener,
+ void *data) {
+ struct sway_view_child *child =
+ wl_container_of(listener, child, surface_commit);
+ // TODO: only accumulate damage from the child
+ view_damage(child->view, false);
+}
+
+static void view_child_handle_surface_new_subsurface(
+ struct wl_listener *listener, void *data) {
+ struct sway_view_child *child =
+ wl_container_of(listener, child, surface_new_subsurface);
+ struct wlr_subsurface *subsurface = data;
+ view_subsurface_create(child->view, subsurface);
+}
+
+static void view_child_handle_surface_destroy(struct wl_listener *listener,
+ void *data) {
+ struct sway_view_child *child =
+ wl_container_of(listener, child, surface_destroy);
+ view_child_destroy(child);
+}
+
+static void view_child_handle_view_unmap(struct wl_listener *listener,
+ void *data) {
+ struct sway_view_child *child =
+ wl_container_of(listener, child, view_unmap);
+ view_child_destroy(child);
+}
+
+static void view_init_subsurfaces(struct sway_view *view,
+ struct wlr_surface *surface) {
+ struct wlr_subsurface *subsurface;
+ wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) {
+ view_subsurface_create(view, subsurface);
+ }
+}
+
+void view_child_init(struct sway_view_child *child,
+ const struct sway_view_child_impl *impl, struct sway_view *view,
+ struct wlr_surface *surface) {
+ child->impl = impl;
+ child->view = view;
+ child->surface = surface;
+
+ wl_signal_add(&surface->events.commit, &child->surface_commit);
+ child->surface_commit.notify = view_child_handle_surface_commit;
+ wl_signal_add(&surface->events.new_subsurface,
+ &child->surface_new_subsurface);
+ child->surface_new_subsurface.notify =
+ view_child_handle_surface_new_subsurface;
+ wl_signal_add(&surface->events.destroy, &child->surface_destroy);
+ child->surface_destroy.notify = view_child_handle_surface_destroy;
+ wl_signal_add(&view->events.unmap, &child->view_unmap);
+ child->view_unmap.notify = view_child_handle_view_unmap;
+
+ struct sway_container *output = child->view->swayc->parent;
+ if (output != NULL) {
+ if (output->type != C_OUTPUT) {
+ output = container_parent(output, C_OUTPUT);
+ }
+ wlr_surface_send_enter(child->surface, output->sway_output->wlr_output);
+ }
+
+ view_init_subsurfaces(child->view, surface);
+
+ // TODO: only damage the whole child
+ view_damage(child->view, true);
+}
+
+void view_child_destroy(struct sway_view_child *child) {
+ // TODO: only damage the whole child
+ view_damage(child->view, true);
+
+ wl_list_remove(&child->surface_commit.link);
+ wl_list_remove(&child->surface_destroy.link);
+ wl_list_remove(&child->view_unmap.link);
+
+ if (child->impl && child->impl->destroy) {
+ child->impl->destroy(child);
+ } else {
+ free(child);
+ }
+}
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 @@
+#define _XOPEN_SOURCE 500
+#include <ctype.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include "stringop.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/ipc-server.h"
+#include "sway/tree/container.h"
+#include "sway/tree/workspace.h"
+#include "log.h"
+#include "util.h"
+
+static struct sway_container *get_workspace_initial_output(const char *name) {
+ struct sway_container *parent;
+ // Search for workspace<->output pair
+ int e = config->workspace_outputs->length;
+ for (int i = 0; i < config->workspace_outputs->length; ++i) {
+ struct workspace_output *wso = config->workspace_outputs->items[i];
+ if (strcasecmp(wso->workspace, name) == 0) {
+ // Find output to use if it exists
+ e = root_container.children->length;
+ for (i = 0; i < e; ++i) {
+ parent = root_container.children->items[i];
+ if (strcmp(parent->name, wso->output) == 0) {
+ return parent;
+ }
+ }
+ break;
+ }
+ }
+ // Otherwise put it on the focused output
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus =
+ seat_get_focus_inactive(seat, &root_container);
+ parent = focus;
+ parent = container_parent(parent, C_OUTPUT);
+ return parent;
+}
+
+struct sway_container *workspace_create(struct sway_container *output,
+ const char *name) {
+ if (output == NULL) {
+ output = get_workspace_initial_output(name);
+ }
+
+ wlr_log(L_DEBUG, "Added workspace %s for output %s", name, output->name);
+ struct sway_container *workspace = container_create(C_WORKSPACE);
+
+ workspace->x = output->x;
+ workspace->y = output->y;
+ workspace->width = output->width;
+ workspace->height = output->height;
+ workspace->name = !name ? NULL : strdup(name);
+ workspace->prev_layout = L_NONE;
+ workspace->layout = container_get_default_layout(output);
+ workspace->workspace_layout = workspace->layout;
+
+ container_add_child(output, workspace);
+ container_sort_workspaces(output);
+ container_create_notify(workspace);
+
+ return workspace;
+}
+
+char *prev_workspace_name = NULL;
+struct workspace_by_number_data {
+ int len;
+ const char *cset;
+ const char *name;
+};
+
+void next_name_map(struct sway_container *ws, void *data) {
+ int *count = data;
+ ++count;
+}
+
+static bool workspace_valid_on_output(const char *output_name,
+ const char *ws_name) {
+ int i;
+ for (i = 0; i < config->workspace_outputs->length; ++i) {
+ struct workspace_output *wso = config->workspace_outputs->items[i];
+ if (strcasecmp(wso->workspace, ws_name) == 0) {
+ if (strcasecmp(wso->output, output_name) != 0) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+char *workspace_next_name(const char *output_name) {
+ wlr_log(L_DEBUG, "Workspace: Generating new workspace name for output %s",
+ output_name);
+ int l = 1;
+ // Scan all workspace bindings to find the next available workspace name,
+ // if none are found/available then default to a number
+ struct sway_mode *mode = config->current_mode;
+
+ // TODO: iterate over keycode bindings too
+ int order = INT_MAX;
+ char *target = NULL;
+ for (int i = 0; i < mode->keysym_bindings->length; ++i) {
+ struct sway_binding *binding = mode->keysym_bindings->items[i];
+ char *cmdlist = strdup(binding->command);
+ char *dup = cmdlist;
+ char *name = NULL;
+
+ // workspace n
+ char *cmd = argsep(&cmdlist, " ");
+ if (cmdlist) {
+ name = argsep(&cmdlist, ",;");
+ }
+
+ if (strcmp("workspace", cmd) == 0 && name) {
+ wlr_log(L_DEBUG, "Got valid workspace command for target: '%s'", name);
+ char *_target = strdup(name);
+ strip_quotes(_target);
+ while (isspace(*_target)) {
+ memmove(_target, _target+1, strlen(_target+1));
+ }
+
+ // Make sure that the command references an actual workspace
+ // not a command about workspaces
+ if (strcmp(_target, "next") == 0 ||
+ strcmp(_target, "prev") == 0 ||
+ strcmp(_target, "next_on_output") == 0 ||
+ strcmp(_target, "prev_on_output") == 0 ||
+ strcmp(_target, "number") == 0 ||
+ strcmp(_target, "back_and_forth") == 0 ||
+ strcmp(_target, "current") == 0)
+ {
+ free(_target);
+ free(dup);
+ continue;
+ }
+
+ // If the command is workspace number <name>, isolate the name
+ if (strncmp(_target, "number ", strlen("number ")) == 0) {
+ size_t length = strlen(_target) - strlen("number ") + 1;
+ char *temp = malloc(length);
+ strncpy(temp, _target + strlen("number "), length - 1);
+ temp[length - 1] = '\0';
+ free(_target);
+ _target = temp;
+ wlr_log(L_DEBUG, "Isolated name from workspace number: '%s'", _target);
+
+ // Make sure the workspace number doesn't already exist
+ if (workspace_by_number(_target)) {
+ free(_target);
+ free(dup);
+ continue;
+ }
+ }
+
+ // Make sure that the workspace doesn't already exist
+ if (workspace_by_name(_target)) {
+ free(_target);
+ free(dup);
+ continue;
+ }
+
+ // make sure that the workspace can appear on the given
+ // output
+ if (!workspace_valid_on_output(output_name, _target)) {
+ free(_target);
+ free(dup);
+ continue;
+ }
+
+ if (binding->order < order) {
+ order = binding->order;
+ free(target);
+ target = _target;
+ wlr_log(L_DEBUG, "Workspace: Found free name %s", _target);
+ }
+ }
+ free(dup);
+ }
+ if (target != NULL) {
+ return target;
+ }
+ // As a fall back, get the current number of active workspaces
+ // and return that + 1 for the next workspace's name
+ int ws_num = root_container.children->length;
+ if (ws_num >= 10) {
+ l = 2;
+ } else if (ws_num >= 100) {
+ l = 3;
+ }
+ char *name = malloc(l + 1);
+ if (!name) {
+ wlr_log(L_ERROR, "Could not allocate workspace name");
+ return NULL;
+ }
+ sprintf(name, "%d", ws_num++);
+ return name;
+}
+
+static bool _workspace_by_number(struct sway_container *view, void *data) {
+ if (view->type != C_WORKSPACE) {
+ return false;
+ }
+ struct workspace_by_number_data *wbnd = data;
+ int a = strspn(view->name, wbnd->cset);
+ return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0;
+}
+
+struct sway_container *workspace_by_number(const char* name) {
+ struct workspace_by_number_data wbnd = {0, "1234567890", name};
+ wbnd.len = strspn(name, wbnd.cset);
+ if (wbnd.len <= 0) {
+ return NULL;
+ }
+ return container_find(&root_container,
+ _workspace_by_number, (void *) &wbnd);
+}
+
+static bool _workspace_by_name(struct sway_container *view, void *data) {
+ return (view->type == C_WORKSPACE) &&
+ (strcasecmp(view->name, (char *) data) == 0);
+}
+
+struct sway_container *workspace_by_name(const char *name) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *current_workspace = NULL, *current_output = NULL;
+ struct sway_container *focus = seat_get_focus(seat);
+ if (focus) {
+ current_workspace = container_parent(focus, C_WORKSPACE);
+ current_output = container_parent(focus, C_OUTPUT);
+ }
+ if (strcmp(name, "prev") == 0) {
+ return workspace_prev(current_workspace);
+ } else if (strcmp(name, "prev_on_output") == 0) {
+ return workspace_output_prev(current_output);
+ } else if (strcmp(name, "next") == 0) {
+ return workspace_next(current_workspace);
+ } else if (strcmp(name, "next_on_output") == 0) {
+ return workspace_output_next(current_output);
+ } else if (strcmp(name, "current") == 0) {
+ return current_workspace;
+ } else {
+ return container_find(&root_container, _workspace_by_name,
+ (void *)name);
+ }
+}
+
+/**
+ * Get the previous or next workspace on the specified output. Wraps around at
+ * the end and beginning. If next is false, the previous workspace is returned,
+ * otherwise the next one is returned.
+ */
+struct sway_container *workspace_output_prev_next_impl(
+ struct sway_container *output, bool next) {
+ if (!sway_assert(output->type == C_OUTPUT,
+ "Argument must be an output, is %d", output->type)) {
+ return NULL;
+ }
+
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus = seat_get_focus_inactive(seat, output);
+ struct sway_container *workspace = (focus->type == C_WORKSPACE ?
+ focus :
+ container_parent(focus, C_WORKSPACE));
+
+ int i;
+ for (i = 0; i < output->children->length; i++) {
+ if (output->children->items[i] == workspace) {
+ return output->children->items[
+ wrap(i + (next ? 1 : -1), output->children->length)];
+ }
+ }
+
+ // Doesn't happen, at worst the for loop returns the previously active
+ // workspace
+ return NULL;
+}
+
+/**
+ * Get the previous or next workspace. If the first/last workspace on an output
+ * is active, proceed to the previous/next output's previous/next workspace. If
+ * next is false, the previous workspace is returned, otherwise the next one is
+ * returned.
+ */
+struct sway_container *workspace_prev_next_impl(
+ struct sway_container *workspace, bool next) {
+ if (!sway_assert(workspace->type == C_WORKSPACE,
+ "Argument must be a workspace, is %d", workspace->type)) {
+ return NULL;
+ }
+
+ struct sway_container *current_output = workspace->parent;
+ int offset = next ? 1 : -1;
+ int start = next ? 0 : 1;
+ int end;
+ if (next) {
+ end = current_output->children->length - 1;
+ } else {
+ end = current_output->children->length;
+ }
+ int i;
+ for (i = start; i < end; i++) {
+ if (current_output->children->items[i] == workspace) {
+ return current_output->children->items[i + offset];
+ }
+ }
+
+ // Given workspace is the first/last on the output, jump to the
+ // previous/next output
+ int num_outputs = root_container.children->length;
+ for (i = 0; i < num_outputs; i++) {
+ if (root_container.children->items[i] == current_output) {
+ struct sway_container *next_output = root_container.children->items[
+ wrap(i + offset, num_outputs)];
+ return workspace_output_prev_next_impl(next_output, next);
+ }
+ }
+
+ // Doesn't happen, at worst the for loop returns the previously active
+ // workspace on the active output
+ return NULL;
+}
+
+struct sway_container *workspace_output_next(struct sway_container *current) {
+ return workspace_output_prev_next_impl(current, true);
+}
+
+struct sway_container *workspace_next(struct sway_container *current) {
+ return workspace_prev_next_impl(current, true);
+}
+
+struct sway_container *workspace_output_prev(struct sway_container *current) {
+ return workspace_output_prev_next_impl(current, false);
+}
+
+struct sway_container *workspace_prev(struct sway_container *current) {
+ return workspace_prev_next_impl(current, false);
+}
+
+bool workspace_switch(struct sway_container *workspace) {
+ if (!workspace) {
+ return false;
+ }
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus =
+ seat_get_focus_inactive(seat, &root_container);
+ if (!seat || !focus) {
+ return false;
+ }
+ struct sway_container *active_ws = focus;
+ if (active_ws->type != C_WORKSPACE) {
+ active_ws = container_parent(focus, C_WORKSPACE);
+ }
+
+ if (config->auto_back_and_forth
+ && active_ws == workspace
+ && prev_workspace_name) {
+ struct sway_container *new_ws = workspace_by_name(prev_workspace_name);
+ workspace = new_ws ?
+ new_ws :
+ workspace_create(NULL, prev_workspace_name);
+ }
+
+ if (!prev_workspace_name || (strcmp(prev_workspace_name, active_ws->name)
+ && active_ws != workspace)) {
+ free(prev_workspace_name);
+ prev_workspace_name = malloc(strlen(active_ws->name) + 1);
+ if (!prev_workspace_name) {
+ wlr_log(L_ERROR, "Unable to allocate previous workspace name");
+ return false;
+ }
+ strcpy(prev_workspace_name, active_ws->name);
+ }
+
+ // TODO: Deal with sticky containers
+
+ wlr_log(L_DEBUG, "Switching to workspace %p:%s",
+ workspace, workspace->name);
+ struct sway_container *next = seat_get_focus_inactive(seat, workspace);
+ if (next == NULL) {
+ next = workspace;
+ }
+ seat_set_focus(seat, next);
+ struct sway_container *output = container_parent(workspace, C_OUTPUT);
+ arrange_windows(output, -1, -1);
+ return true;
+}
+
+bool workspace_is_visible(struct sway_container *ws) {
+ struct sway_container *output = container_parent(ws, C_OUTPUT);
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus = seat_get_focus_inactive(seat, output);
+ if (focus->type != C_WORKSPACE) {
+ focus = container_parent(focus, C_WORKSPACE);
+ }
+ return focus == ws;
+}
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 @@
-#define _XOPEN_SOURCE 500
-#include <stdlib.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <ctype.h>
-#include <wlc/wlc.h>
-#include <string.h>
-#include <strings.h>
-#include <sys/types.h>
-#include "sway/ipc-server.h"
-#include "sway/extensions.h"
-#include "sway/workspace.h"
-#include "sway/layout.h"
-#include "sway/container.h"
-#include "sway/handlers.h"
-#include "sway/config.h"
-#include "sway/focus.h"
-#include "stringop.h"
-#include "util.h"
-#include "list.h"
-#include "log.h"
-#include "ipc.h"
-
-char *prev_workspace_name = NULL;
-struct workspace_by_number_data {
- int len;
- const char *cset;
- const char *name;
-};
-
-static bool workspace_valid_on_output(const char *output_name, const char *ws_name) {
- int i;
- for (i = 0; i < config->workspace_outputs->length; ++i) {
- struct workspace_output *wso = config->workspace_outputs->items[i];
- if (strcasecmp(wso->workspace, ws_name) == 0) {
- if (strcasecmp(wso->output, output_name) != 0) {
- return false;
- }
- }
- }
-
- return true;
-}
-
-char *workspace_next_name(const char *output_name) {
- sway_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", output_name);
- int i;
- int l = 1;
- // Scan all workspace bindings to find the next available workspace name,
- // if none are found/available then default to a number
- struct sway_mode *mode = config->current_mode;
-
- int order = INT_MAX;
- char *target = NULL;
- for (i = 0; i < mode->bindings->length; ++i) {
- struct sway_binding *binding = mode->bindings->items[i];
- char *cmdlist = strdup(binding->command);
- char *dup = cmdlist;
- char *name = NULL;
-
- // workspace n
- char *cmd = argsep(&cmdlist, " ");
- if (cmdlist) {
- name = argsep(&cmdlist, ",;");
- }
-
- if (strcmp("workspace", cmd) == 0 && name) {
- sway_log(L_DEBUG, "Got valid workspace command for target: '%s'", name);
- char *_target = strdup(name);
- strip_quotes(_target);
- while (isspace(*_target))
- _target++;
-
- // Make sure that the command references an actual workspace
- // not a command about workspaces
- if (strcmp(_target, "next") == 0 ||
- strcmp(_target, "prev") == 0 ||
- strcmp(_target, "next_on_output") == 0 ||
- strcmp(_target, "prev_on_output") == 0 ||
- strcmp(_target, "number") == 0 ||
- strcmp(_target, "back_and_forth") == 0 ||
- strcmp(_target, "current") == 0)
- {
- free(_target);
- free(dup);
- continue;
- }
-
- // Make sure that the workspace doesn't already exist
- if (workspace_by_name(_target)) {
- free(_target);
- free(dup);
- continue;
- }
-
- // make sure that the workspace can appear on the given
- // output
- if (!workspace_valid_on_output(output_name, _target)) {
- free(_target);
- free(dup);
- continue;
- }
-
- if (binding->order < order) {
- order = binding->order;
- free(target);
- target = _target;
- sway_log(L_DEBUG, "Workspace: Found free name %s", _target);
- }
- }
- free(dup);
- }
- if (target != NULL) {
- return target;
- }
- // As a fall back, get the current number of active workspaces
- // and return that + 1 for the next workspace's name
- int ws_num = root_container.children->length;
- if (ws_num >= 10) {
- l = 2;
- } else if (ws_num >= 100) {
- l = 3;
- }
- char *name = malloc(l + 1);
- if (!name) {
- sway_log(L_ERROR, "Could not allocate workspace name");
- return NULL;
- }
- sprintf(name, "%d", ws_num++);
- return name;
-}
-
-swayc_t *workspace_create(const char* name) {
- swayc_t *parent;
- // Search for workspace<->output pair
- int i, e = config->workspace_outputs->length;
- for (i = 0; i < e; ++i) {
- struct workspace_output *wso = config->workspace_outputs->items[i];
- if (strcasecmp(wso->workspace, name) == 0)
- {
- // Find output to use if it exists
- e = root_container.children->length;
- for (i = 0; i < e; ++i) {
- parent = root_container.children->items[i];
- if (strcmp(parent->name, wso->output) == 0) {
- return new_workspace(parent, name);
- }
- }
- break;
- }
- }
- // Otherwise create a new one
- parent = get_focused_container(&root_container);
- parent = swayc_parent_by_type(parent, C_OUTPUT);
- return new_workspace(parent, name);
-}
-
-static bool _workspace_by_name(swayc_t *view, void *data) {
- return (view->type == C_WORKSPACE) &&
- (strcasecmp(view->name, (char *) data) == 0);
-}
-
-swayc_t *workspace_by_name(const char* name) {
- if (strcmp(name, "prev") == 0) {
- return workspace_prev();
- }
- else if (strcmp(name, "prev_on_output") == 0) {
- return workspace_output_prev();
- }
- else if (strcmp(name, "next") == 0) {
- return workspace_next();
- }
- else if (strcmp(name, "next_on_output") == 0) {
- return workspace_output_next();
- }
- else if (strcmp(name, "current") == 0) {
- return swayc_active_workspace();
- }
- else {
- return swayc_by_test(&root_container, _workspace_by_name, (void *) name);
- }
-}
-
-static bool _workspace_by_number(swayc_t *view, void *data) {
- if (view->type != C_WORKSPACE) {
- return false;
- }
- struct workspace_by_number_data *wbnd = data;
- int a = strspn(view->name, wbnd->cset);
- return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0;
-}
-swayc_t *workspace_by_number(const char* name) {
- struct workspace_by_number_data wbnd = {0, "1234567890", name};
- wbnd.len = strspn(name, wbnd.cset);
- if (wbnd.len <= 0) {
- return NULL;
- }
- return swayc_by_test(&root_container, _workspace_by_number, (void *) &wbnd);
-}
-
-/**
- * Get the previous or next workspace on the specified output.
- * Wraps around at the end and beginning.
- * If next is false, the previous workspace is returned, otherwise the next one is returned.
- */
-swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) {
- if (!sway_assert(output->type == C_OUTPUT, "Argument must be an output, is %d", output->type)) {
- return NULL;
- }
-
- int i;
- for (i = 0; i < output->children->length; i++) {
- if (output->children->items[i] == output->focused) {
- return output->children->items[wrap(i + (next ? 1 : -1), output->children->length)];
- }
- }
-
- // Doesn't happen, at worst the for loop returns the previously active workspace
- return NULL;
-}
-
-/**
- * Get the previous or next workspace. If the first/last workspace on an output is active,
- * proceed to the previous/next output's previous/next workspace.
- * If next is false, the previous workspace is returned, otherwise the next one is returned.
- */
-swayc_t *workspace_prev_next_impl(swayc_t *workspace, bool next) {
- if (!sway_assert(workspace->type == C_WORKSPACE, "Argument must be a workspace, is %d", workspace->type)) {
- return NULL;
- }
-
- swayc_t *current_output = workspace->parent;
- int offset = next ? 1 : -1;
- int start = next ? 0 : 1;
- int end = next ? (current_output->children->length) - 1 : current_output->children->length;
- int i;
- for (i = start; i < end; i++) {
- if (current_output->children->items[i] == workspace) {
- return current_output->children->items[i + offset];
- }
- }
-
- // Given workspace is the first/last on the output, jump to the previous/next output
- int num_outputs = root_container.children->length;
- for (i = 0; i < num_outputs; i++) {
- if (root_container.children->items[i] == current_output) {
- swayc_t *next_output = root_container.children->items[wrap(i + offset, num_outputs)];
- return workspace_output_prev_next_impl(next_output, next);
- }
- }
-
- // Doesn't happen, at worst the for loop returns the previously active workspace on the active output
- return NULL;
-}
-
-swayc_t *workspace_output_next() {
- return workspace_output_prev_next_impl(swayc_active_output(), true);
-}
-
-swayc_t *workspace_next() {
- return workspace_prev_next_impl(swayc_active_workspace(), true);
-}
-
-swayc_t *workspace_output_prev() {
- return workspace_output_prev_next_impl(swayc_active_output(), false);
-}
-
-swayc_t *workspace_prev() {
- return workspace_prev_next_impl(swayc_active_workspace(), false);
-}
-
-bool workspace_switch(swayc_t *workspace) {
- if (!workspace) {
- return false;
- }
- swayc_t *active_ws = swayc_active_workspace();
- if (config->auto_back_and_forth && active_ws == workspace && prev_workspace_name) {
- swayc_t *new_ws = workspace_by_name(prev_workspace_name);
- workspace = new_ws ? new_ws : workspace_create(prev_workspace_name);
- }
-
- if (!prev_workspace_name
- || (strcmp(prev_workspace_name, active_ws->name)
- && active_ws != workspace)) {
- free(prev_workspace_name);
- prev_workspace_name = malloc(strlen(active_ws->name) + 1);
- if (!prev_workspace_name) {
- sway_log(L_ERROR, "Unable to allocate previous workspace name");
- return false;
- }
- strcpy(prev_workspace_name, active_ws->name);
- }
-
- // move sticky containers
- if (swayc_parent_by_type(active_ws, C_OUTPUT) == swayc_parent_by_type(workspace, C_OUTPUT)) {
- // don't change list while traversing it, use intermediate list instead
- list_t *stickies = create_list();
- for (int i = 0; i < active_ws->floating->length; i++) {
- swayc_t *cont = active_ws->floating->items[i];
- if (cont->sticky) {
- list_add(stickies, cont);
- }
- }
- for (int i = 0; i < stickies->length; i++) {
- swayc_t *cont = stickies->items[i];
- sway_log(L_DEBUG, "Moving sticky container %p to %p:%s",
- cont, workspace, workspace->name);
- swayc_t *parent = remove_child(cont);
- add_floating(workspace, cont);
- // Destroy old container if we need to
- destroy_container(parent);
- }
- list_free(stickies);
- }
- sway_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name);
- if (!set_focused_container(get_focused_view(workspace))) {
- return false;
- }
- swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
- arrange_backgrounds();
- arrange_windows(output, -1, -1);
- return true;
-}
-
-swayc_t *workspace_for_pid(pid_t pid) {
- int i;
- swayc_t *ws = NULL;
- struct pid_workspace *pw = NULL;
-
- sway_log(L_DEBUG, "looking for workspace for pid %d", pid);
-
- // leaving this here as it's useful for debugging
- // sway_log(L_DEBUG, "all pid_workspaces");
- // for (int k = 0; k < config->pid_workspaces->length; k++) {
- // pw = config->pid_workspaces->items[k];
- // sway_log(L_DEBUG, "pid %d workspace %s time_added %li", *pw->pid, pw->workspace, *pw->time_added);
- // }
-
- do {
- for (i = 0; i < config->pid_workspaces->length; i++) {
- pw = config->pid_workspaces->items[i];
- pid_t *pw_pid = pw->pid;
-
- if (pid == *pw_pid) {
- sway_log(L_DEBUG, "found pid_workspace for pid %d, workspace %s", pid, pw->workspace);
- break; // out of for loop
- }
-
- pw = NULL;
- }
-
- if (pw) {
- break; // out of do-while loop
- }
-
- pid = get_parent_pid(pid);
- // no sense in looking for matches for pid 0.
- // also, if pid == getpid(), that is the compositor's
- // pid, which definitely isn't helpful
- } while (pid > 0 && pid != getpid());
-
- if (pw) {
- ws = workspace_by_name(pw->workspace);
-
- if (!ws) {
- sway_log(L_DEBUG, "Creating workspace %s for pid %d because it disappeared", pw->workspace, pid);
- ws = workspace_create(pw->workspace);
- }
-
- list_del(config->pid_workspaces, i);
- }
-
- return ws;
-}