diff options
38 files changed, 568 insertions, 356 deletions
diff --git a/include/sway/commands.h b/include/sway/commands.h index 1654eb48..370a1f7a 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -95,7 +95,7 @@ struct cmd_results *add_color(const char *name, /** * TODO: Move this function and its dependent functions to container.c. */ -void container_resize_tiled(struct sway_container *parent, enum wlr_edges edge, +bool container_resize_tiled(struct sway_container *parent, enum wlr_edges edge, int amount); sway_cmd cmd_assign; diff --git a/include/sway/config.h b/include/sway/config.h index 35f0e708..af5c7a18 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -167,13 +167,12 @@ struct output_config { }; /** - * Maps a workspace name to an output name. - * - * Set via `workspace <x> output <y>` + * Stores configuration for a workspace, regardless of whether the workspace + * exists. */ -struct workspace_output { - char *output; +struct workspace_config { char *workspace; + char *output; }; struct bar_config { @@ -327,7 +326,7 @@ struct sway_config { list_t *modes; list_t *bars; list_t *cmd_queue; - list_t *workspace_outputs; + list_t *workspace_configs; list_t *output_configs; list_t *input_configs; list_t *seat_configs; @@ -518,6 +517,8 @@ struct bar_config *default_bar_config(void); void free_bar_config(struct bar_config *bar); +void free_workspace_config(struct workspace_config *wsc); + /** * Updates the value of config->font_height based on the max title height * reported by each container. If recalculate is true, the containers will diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index e4b616d1..c8220b39 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -48,6 +48,8 @@ struct sway_workspace { extern char *prev_workspace_name; +struct workspace_config *workspace_find_config(const char *ws_name); + struct sway_output *workspace_get_initial_output(const char *name); struct sway_workspace *workspace_create(struct sway_output *output, diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 29e96159..20992014 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -54,7 +54,6 @@ struct swaybar { struct wl_seat *seat; struct swaybar_config *config; - struct swaybar_output *focused_output; struct swaybar_pointer pointer; struct status_line *status; @@ -95,9 +94,7 @@ struct swaybar_workspace { bool urgent; }; -void bar_setup(struct swaybar *bar, - const char *socket_path, - const char *bar_id); +bool bar_setup(struct swaybar *bar, const char *socket_path, const char *bar_id); void bar_run(struct swaybar *bar); void bar_teardown(struct swaybar *bar); diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h new file mode 100644 index 00000000..12d9b317 --- /dev/null +++ b/include/swaybar/i3bar.h @@ -0,0 +1,34 @@ +#ifndef _SWAYBAR_I3BAR_H +#define _SWAYBAR_I3BAR_H + +#include "bar.h" +#include "status_line.h" + +struct i3bar_block { + struct wl_list link; + int ref_count; + char *full_text, *short_text, *align; + bool urgent; + uint32_t *color; + int min_width; + char *name, *instance; + bool separator; + int separator_block_width; + bool markup; + // Airblader features + uint32_t background; + uint32_t border; + int border_top; + int border_bottom; + int border_left; + int border_right; +}; + +void i3bar_block_unref(struct i3bar_block *block); +bool i3bar_handle_readable(struct status_line *status); +enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, + struct i3bar_block *block, int x, int y, enum x11_button button); +enum x11_button wl_button_to_x11_button(uint32_t button); +enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value); + +#endif diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h index a1696bcf..81e48a6b 100644 --- a/include/swaybar/ipc.h +++ b/include/swaybar/ipc.h @@ -3,7 +3,7 @@ #include <stdbool.h> #include "swaybar/bar.h" -void ipc_initialize(struct swaybar *bar, const char *bar_id); +bool ipc_initialize(struct swaybar *bar, const char *bar_id); bool handle_ipc_readable(struct swaybar *bar); void ipc_get_workspaces(struct swaybar *bar); void ipc_send_workspace_command(struct swaybar *bar, const char *ws); diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h index d3eabdf6..e6c7dac2 100644 --- a/include/swaybar/status_line.h +++ b/include/swaybar/status_line.h @@ -13,26 +13,6 @@ enum status_protocol { PROTOCOL_I3BAR, }; -struct i3bar_block { - struct wl_list link; - int ref_count; - char *full_text, *short_text, *align; - bool urgent; - uint32_t *color; - int min_width; - char *name, *instance; - bool separator; - int separator_block_width; - bool markup; - // Airblader features - uint32_t background; - uint32_t border; - int border_top; - int border_bottom; - int border_left; - int border_right; -}; - struct status_line { pid_t pid; int read_fd, write_fd; @@ -55,11 +35,5 @@ struct status_line *status_line_init(char *cmd); void status_error(struct status_line *status, const char *text); bool status_handle_readable(struct status_line *status); void status_line_free(struct status_line *status); -bool i3bar_handle_readable(struct status_line *status); -enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, - struct i3bar_block *block, int x, int y, enum x11_button button); -void i3bar_block_unref(struct i3bar_block *block); -enum x11_button wl_button_to_x11_button(uint32_t button); -enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value); #endif diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index 2f0cd34d..970e3cc9 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -101,5 +101,8 @@ void render_frame(struct swaylock_surface *surface); void render_frames(struct swaylock_state *state); void damage_surface(struct swaylock_surface *surface); void damage_state(struct swaylock_state *state); +void initialize_pw_backend(void); +bool attempt_password(struct swaylock_password *pw); +void clear_password_buffer(struct swaylock_password *pw); #endif diff --git a/meson.build b/meson.build index 253a4e96..de6573ea 100644 --- a/meson.build +++ b/meson.build @@ -22,6 +22,10 @@ datadir = get_option('datadir') sysconfdir = get_option('sysconfdir') prefix = get_option('prefix') +if is_freebsd + add_project_arguments('-D_C11_SOURCE', language: 'c') +endif + swayidle_deps = [] jsonc = dependency('json-c', version: '>=0.13') @@ -40,7 +44,7 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) pixman = dependency('pixman-1') libcap = dependency('libcap', required: false) libinput = dependency('libinput', version: '>=1.6.0') -libpam = cc.find_library('pam') +libpam = cc.find_library('pam', required: false) systemd = dependency('libsystemd', required: false) elogind = dependency('libelogind', required: false) math = cc.find_library('m') @@ -70,6 +74,11 @@ if elogind.found() swayidle_deps += elogind endif +if not systemd.found() and not elogind.found() + warning('The sway binary must be setuid when compiled without (e)logind') + warning('You must do this manually post-install: chmod a+s /path/to/sway') +endif + scdoc = find_program('scdoc', required: false) if scdoc.found() @@ -133,9 +142,9 @@ subdir('swaymsg') subdir('client') subdir('swaybg') subdir('swaybar') -subdir('swaylock') subdir('swayidle') subdir('swaynag') +subdir('swaylock') config = configuration_data() config.set('sysconfdir', join_paths(prefix, sysconfdir)) diff --git a/sway/commands.c b/sway/commands.c index bff230f7..03761c52 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -391,14 +391,12 @@ struct cmd_results *config_command(char *exec) { // Var replacement, for all but first argument of set // TODO commands for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { + if (*argv[i] == '\"' || *argv[i] == '\'') { + strip_quotes(argv[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 - if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) { - strip_quotes(argv[1]); - } if (handler->handle) { results = handler->handle(argc-1, argv+1); } else { @@ -422,11 +420,6 @@ struct cmd_results *config_subcommand(char **argv, int argc, char *input = argv[0] ? argv[0] : "(empty)"; return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); } - // 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]); - } if (handler->handle) { return handler->handle(argc - 1, argv + 1); } diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c index 0c48bee9..f18b8d7c 100644 --- a/sway/commands/bar/binding_mode_indicator.c +++ b/sway/commands/bar/binding_mode_indicator.c @@ -21,7 +21,9 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { config->current_bar->binding_mode_indicator = false; wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s", config->current_bar->id); + } else { + return cmd_results_new(CMD_INVALID, "binding_mode_indicator", + "Invalid value %s", argv[0]); } - return cmd_results_new(CMD_INVALID, "binding_mode_indicator", - "Invalid value %s", argv[0]); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 9e370d43..30fb47c4 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -123,19 +123,13 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { } free(src); } else { - // Escape spaces and quotes in the final path for swaybg + // Escape double quotes in the final path for swaybg for (size_t i = 0; i < strlen(src); i++) { - switch (src[i]) { - case ' ': - case '\'': - case '\"': - src = realloc(src, strlen(src) + 2); - memmove(src + i + 1, src + i, strlen(src + i) + 1); - *(src + i) = '\\'; - i++; - break; - default: - break; + if (src[i] == '"') { + src = realloc(src, strlen(src) + 2); + memmove(src + i + 1, src + i, strlen(src + i) + 1); + *(src + i) = '\\'; + i++; } } diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 99e9dbda..1343b165 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -179,11 +179,11 @@ static void container_recursive_resize(struct sway_container *container, } } -static void resize_tiled(struct sway_container *parent, int amount, +static bool resize_tiled(struct sway_container *parent, int amount, enum resize_axis axis) { struct sway_container *focused = parent; if (!parent) { - return; + return false; } enum sway_container_layout parallel_layout = @@ -216,7 +216,7 @@ static void resize_tiled(struct sway_container *parent, int amount, } if (!parent) { // Can't resize in this direction - return; + return false; } // Implement up/down/left/right direction by zeroing one of the weights, @@ -248,22 +248,22 @@ static void resize_tiled(struct sway_container *parent, int amount, if (sibling_pos < parent_pos && minor_weight) { double pixels = -amount / minor_weight; if (major_weight && (sibling_size + pixels / 2) < min_sane) { - return; // Too small + return false; // Too small } else if (!major_weight && sibling_size + pixels < min_sane) { - return; // Too small + return false; // Too small } } else if (sibling_pos > parent_pos && major_weight) { double pixels = -amount / major_weight; if (minor_weight && (sibling_size + pixels / 2) < min_sane) { - return; // Too small + return false; // Too small } else if (!minor_weight && sibling_size + pixels < min_sane) { - return; // Too small + return false; // Too small } } } else { double pixels = amount; if (parent_size + pixels < min_sane) { - return; // Too small + return false; // Too small } } } @@ -317,9 +317,10 @@ static void resize_tiled(struct sway_container *parent, int amount, } else { arrange_workspace(parent->workspace); } + return true; } -void container_resize_tiled(struct sway_container *parent, +bool container_resize_tiled(struct sway_container *parent, enum wlr_edges edge, int amount) { enum resize_axis axis = RESIZE_AXIS_INVALID; switch (edge) { @@ -338,7 +339,7 @@ void container_resize_tiled(struct sway_container *parent, case WLR_EDGE_NONE: break; } - resize_tiled(parent, amount, axis); + return resize_tiled(parent, amount, axis); } /** @@ -395,6 +396,10 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis, case RESIZE_AXIS_INVALID: return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); } + if (grow_x == 0 && grow_y == 0) { + return cmd_results_new(CMD_INVALID, "resize", + "Cannot resize any further"); + } con->x += grow_x; con->y += grow_y; con->width += grow_width; @@ -442,7 +447,10 @@ static struct cmd_results *resize_adjust_tiled(enum resize_axis axis, } } - resize_tiled(current, amount->amount, axis); + if (!resize_tiled(current, amount->amount, axis)) { + return cmd_results_new(CMD_INVALID, "resize", + "Cannot resize any further"); + } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index f026a39d..2ce7872d 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -10,6 +10,26 @@ #include "log.h" #include "stringop.h" +static struct workspace_config *workspace_config_find_or_create(char *ws_name) { + struct workspace_config *wsc = workspace_find_config(ws_name); + if (wsc) { + return wsc; + } + wsc = calloc(1, sizeof(struct workspace_config)); + if (!wsc) { + return NULL; + } + wsc->workspace = strdup(ws_name); + list_add(config->workspace_configs, wsc); + return wsc; +} + +void free_workspace_config(struct workspace_config *wsc) { + free(wsc->workspace); + free(wsc->output); + free(wsc); +} + struct cmd_results *cmd_workspace(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) { @@ -28,21 +48,15 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) { return error; } - struct workspace_output *wso = calloc(1, sizeof(struct workspace_output)); - if (!wso) { + char *ws_name = join_args(argv, argc - 2); + struct workspace_config *wsc = workspace_config_find_or_create(ws_name); + free(ws_name); + if (!wsc) { return cmd_results_new(CMD_FAILURE, "workspace output", "Unable to allocate workspace output"); } - wso->workspace = join_args(argv, argc - 2); - wso->output = strdup(argv[output_location + 1]); - int i = -1; - if ((i = list_seq_find(config->workspace_outputs, workspace_output_cmp_workspace, wso)) != -1) { - struct workspace_output *old = config->workspace_outputs->items[i]; - free(old); // workspaces can only be assigned to a single output - list_del(config->workspace_outputs, i); - } - wlr_log(WLR_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); - list_add(config->workspace_outputs, wso); + free(wsc->output); + wsc->output = strdup(argv[output_location + 1]); } else { if (config->reading || !config->active) { return cmd_results_new(CMD_DEFER, "workspace", NULL); diff --git a/sway/config.c b/sway/config.c index 830fb65f..1e08559d 100644 --- a/sway/config.c +++ b/sway/config.c @@ -95,7 +95,12 @@ void free_config(struct sway_config *config) { list_free(config->bars); } list_free(config->cmd_queue); - list_free(config->workspace_outputs); + if (config->workspace_configs) { + for (int i = 0; i < config->workspace_configs->length; i++) { + free_workspace_config(config->workspace_configs->items[i]); + } + list_free(config->workspace_configs); + } if (config->output_configs) { for (int i = 0; i < config->output_configs->length; i++) { free_output_config(config->output_configs->items[i]); @@ -175,7 +180,7 @@ static void config_defaults(struct sway_config *config) { if (!(config->symbols = create_list())) goto cleanup; if (!(config->modes = create_list())) goto cleanup; if (!(config->bars = create_list())) goto cleanup; - if (!(config->workspace_outputs = create_list())) goto cleanup; + if (!(config->workspace_configs = create_list())) goto cleanup; if (!(config->criteria = create_list())) goto cleanup; if (!(config->no_focus = create_list())) goto cleanup; if (!(config->input_configs = create_list())) goto cleanup; @@ -804,7 +809,7 @@ char *do_var_replacement(char *str) { // would compare two structs in full, while this method only compares the // workspace. int workspace_output_cmp_workspace(const void *a, const void *b) { - const struct workspace_output *wsa = a, *wsb = b; + const struct workspace_config *wsa = a, *wsb = b; return lenient_strcmp(wsa->workspace, wsb->workspace); } diff --git a/sway/config/input.c b/sway/config/input.c index fffc8518..6b43a5b9 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -52,6 +52,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->dwt != INT_MIN) { dst->dwt = src->dwt; } + if (src->left_handed != INT_MIN) { + dst->left_handed = src->left_handed; + } if (src->middle_emulation != INT_MIN) { dst->middle_emulation = src->middle_emulation; } diff --git a/sway/config/output.c b/sway/config/output.c index 74d79130..6f337b66 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -237,7 +237,7 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_log(WLR_DEBUG, "Setting background for output %d to %s", output_i, oc->background); - size_t len = snprintf(NULL, 0, "%s %d %s %s %s", + size_t len = snprintf(NULL, 0, "%s %d \"%s\" %s %s", config->swaybg_command ? config->swaybg_command : "swaybg", output_i, oc->background, oc->background_option, oc->background_fallback ? oc->background_fallback : ""); @@ -246,7 +246,7 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_log(WLR_DEBUG, "Unable to allocate swaybg command"); return; } - snprintf(command, len + 1, "%s %d %s %s %s", + snprintf(command, len + 1, "%s %d \"%s\" %s %s", config->swaybg_command ? config->swaybg_command : "swaybg", output_i, oc->background, oc->background_option, oc->background_fallback ? oc->background_fallback : ""); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 797f6b4c..34d99d52 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -31,14 +31,14 @@ struct sway_transaction_instruction { struct sway_transaction *transaction; struct sway_node *node; union { - struct sway_output_state *output_state; - struct sway_workspace_state *workspace_state; - struct sway_container_state *container_state; + struct sway_output_state output_state; + struct sway_workspace_state workspace_state; + struct sway_container_state container_state; }; uint32_t serial; }; -static struct sway_transaction *transaction_create() { +static struct sway_transaction *transaction_create(void) { struct sway_transaction *transaction = calloc(1, sizeof(struct sway_transaction)); if (!sway_assert(transaction, "Unable to allocate transaction")) { @@ -86,14 +86,7 @@ static void transaction_destroy(struct sway_transaction *transaction) { static void copy_output_state(struct sway_output *output, struct sway_transaction_instruction *instruction) { - struct sway_output_state *state = - calloc(1, sizeof(struct sway_output_state)); - if (!state) { - wlr_log(WLR_ERROR, "Could not allocate output state"); - return; - } - instruction->output_state = state; - + struct sway_output_state *state = &instruction->output_state; state->workspaces = create_list(); list_cat(state->workspaces, output->workspaces); @@ -102,13 +95,7 @@ static void copy_output_state(struct sway_output *output, static void copy_workspace_state(struct sway_workspace *ws, struct sway_transaction_instruction *instruction) { - struct sway_workspace_state *state = - calloc(1, sizeof(struct sway_workspace_state)); - if (!state) { - wlr_log(WLR_ERROR, "Could not allocate workspace state"); - return; - } - instruction->workspace_state = state; + struct sway_workspace_state *state = &instruction->workspace_state; state->fullscreen = ws->fullscreen; state->x = ws->x; @@ -138,13 +125,7 @@ static void copy_workspace_state(struct sway_workspace *ws, static void copy_container_state(struct sway_container *container, struct sway_transaction_instruction *instruction) { - struct sway_container_state *state = - calloc(1, sizeof(struct sway_container_state)); - if (!state) { - wlr_log(WLR_ERROR, "Could not allocate container state"); - return; - } - instruction->container_state = state; + struct sway_container_state *state = &instruction->container_state; state->layout = container->layout; state->con_x = container->x; @@ -301,15 +282,15 @@ static void transaction_apply(struct sway_transaction *transaction) { case N_ROOT: break; case N_OUTPUT: - apply_output_state(node->sway_output, instruction->output_state); + apply_output_state(node->sway_output, &instruction->output_state); break; case N_WORKSPACE: apply_workspace_state(node->sway_workspace, - instruction->workspace_state); + &instruction->workspace_state); break; case N_CONTAINER: apply_container_state(node->sway_container, - instruction->container_state); + &instruction->container_state); break; } @@ -335,7 +316,7 @@ static bool transaction_same_nodes(struct sway_transaction *a, return true; } -static void transaction_progress_queue() { +static void transaction_progress_queue(void) { if (!server.transactions->length) { return; } @@ -390,7 +371,7 @@ static bool should_configure(struct sway_node *node, return false; } struct sway_container_state *cstate = &node->sway_container->current; - struct sway_container_state *istate = instruction->container_state; + struct sway_container_state *istate = &instruction->container_state; #ifdef HAVE_XWAYLAND // Xwayland views are position-aware and need to be reconfigured // when their position changes. @@ -418,10 +399,10 @@ static void transaction_commit(struct sway_transaction *transaction) { struct sway_node *node = instruction->node; if (should_configure(node, instruction)) { instruction->serial = view_configure(node->sway_container->view, - instruction->container_state->view_x, - instruction->container_state->view_y, - instruction->container_state->view_width, - instruction->container_state->view_height); + instruction->container_state.view_x, + instruction->container_state.view_y, + instruction->container_state.view_width, + instruction->container_state.view_height); ++transaction->num_waiting; // From here on we are rendering a saved buffer of the view, which @@ -513,8 +494,8 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, int width, int height) { struct sway_transaction_instruction *instruction = view->container->node.instruction; - if (instruction->container_state->view_width == width && - instruction->container_state->view_height == height) { + if (instruction->container_state.view_width == width && + instruction->container_state.view_height == height) { set_instruction_ready(instruction); } } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 8c9f65ab..2d5d351f 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -897,7 +897,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle moving a tiling container if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED && - !is_floating_or_child && !cont->is_fullscreen) { + !is_floating_or_child && cont && !cont->is_fullscreen) { seat_pointer_notify_button(seat, time_msec, button, state); seat_begin_move_tiling(seat, cont, button); return; diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index f696646f..f39fe29c 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -233,7 +233,8 @@ static void handle_new_input(struct wl_listener *listener, void *data) { wlr_log(WLR_DEBUG, "adding device: '%s'", input_device->identifier); - if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { + if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER || + input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { input_manager_libinput_config_pointer(input_device); } @@ -393,7 +394,8 @@ void input_manager_apply_input_config(struct sway_input_manager *input, wl_list_for_each(input_device, &input->devices, link) { if (strcmp(input_device->identifier, input_config->identifier) == 0 || wildcard) { - if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { + if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER || + input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { input_manager_libinput_config_pointer(input_device); } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 99959c97..2d915502 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -1,8 +1,5 @@ // 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 +#define _POSIX_C_SOURCE 200112L #ifdef __linux__ #include <linux/input-event-codes.h> #elif __FreeBSD__ @@ -89,10 +86,16 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { } void ipc_init(struct sway_server *server) { - ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + ipc_socket = socket(AF_UNIX, SOCK_STREAM, 0); if (ipc_socket == -1) { sway_abort("Unable to create IPC socket"); } + if (fcntl(ipc_socket, F_SETFD, FD_CLOEXEC) == -1) { + sway_abort("Unable to set CLOEXEC on IPC socket"); + } + if (fcntl(ipc_socket, F_SETFL, O_NONBLOCK) == -1) { + sway_abort("Unable to set NONBLOCK on IPC socket"); + } ipc_sockaddr = ipc_user_sockaddr(); diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index a61e2829..00b9386e 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -6,8 +6,7 @@ sway-bar - bar configuration file and commands # DESCRIPTION -Sway allows configuring swaybar in the sway configuration file. Swaybar -commands must be used inside a _bar { }_ block in the config file. +Sway allows configuring swaybar in the sway configuration file. # COMMANDS diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 707c36af..5736d70a 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -7,7 +7,6 @@ sway-input - input configuration file and commands # DESCRIPTION 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*. # INPUT COMMANDS @@ -116,8 +115,7 @@ The following commands may only be used in the configuration file. ## SEAT CONFIGURATION -Configure options for multiseat mode. sway-seat commands must be used inside a -_seat { }_ block in the config. +Configure options for multiseat mode. 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 diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 927bf55c..1526eee3 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -19,6 +19,24 @@ bindsym Shift+XF86AudioRaiseVolume exec \\ pactl set-sink-volume @DEFAULT_SINK@ -1% ``` +Commands can also be given as a block in the form *command { <subcommands...> +}*. Anything before the opening *{* will be prepended to the lines inside the +block. For example: + +``` +output eDP-1 { + background ~/wallpaper.png + resolution 1920x1080 +} +``` + +is identical to + +``` +output eDP-1 background ~/wallpaper.png +output eDP-1 resolution 1920x1080 +``` + These commands can be executed in your config file, via *swaymsg*(1), or via the bindsym command. @@ -37,10 +55,8 @@ which you may only select one. *[...]* is used for optional arguments, and The following commands may only be used in the configuration file. -*bar {* <commands...> *}* - _commands..._ after *{* will be interpreted as bar commands. For - details, see *sway-bar*(5). A newline is required between *{* and the - first command, and *}* must be alone on a line. +*bar* [<bar-id>] <bar-subcommands...> + For details on bar subcommands, see *sway-bar*(5). *default\_orientation* horizontal|vertical|auto Sets the default container layout for tiled containers. @@ -51,10 +67,6 @@ The following commands may only be used in the configuration file. *wordexp*(3) for details). The same include file can only be included once; subsequent attempts will be ignored. -*set* $<name> <value> - Sets variable $_name_ to _value_. You can use the new variable in the - arguments of future commands. - *swaybg\_command* <command> Executes custom background _command_. Default is _swaybg_. Refer to *output* below for more information. @@ -424,20 +436,15 @@ The default colors are: *hide\_edge\_borders* none|vertical|horizontal|both|smart Hides window borders adjacent to the screen edges. Default is _none_. -*input* <input\_device> *{* <commands...> *}* - _commands..._ after *{* will be interpreted as input commands applying to - the specified input device. For details, see *sway-input*(5). A newline is - required between *{* and the first command, and *}* must be alone on a - line. +*input* <input\_device> <input-subcommands...> + For details on input subcommands, see *sway-input*(5). \* may be used in lieu of a specific device name to configure all input devices. A list of input device names may be obtained via *swaymsg -t get\_inputs*. -*seat* <seat> *{* <commands...> *}* - _commands..._ after *{* will be interpreted as seat commands applying to - the specified seat. For details, see *sway-input*(5). A newline is required - between *{* and the first command, and *}* must be alone on a line. +*seat* <seat> <seat-subcommands...> + For details on seat subcommands, see *sway-input*(5). *seat* <seat> cursor move|set <x> <y> Move specified seat's cursor relative to current position or wrap to @@ -465,10 +472,8 @@ The default colors are: *mode* <mode> Switches to the specified mode. The default mode _default_. -*mode* [--pango\_markup] <mode> *{* <commands...> *}* - _commands..._ after *{* will be added to the specified mode. A newline is - required between *{* and the first command, and *}* must be alone on a - line. Only *bindsym* and *bindcode* commands are permitted in mode blocks. +*mode* [--pango\_markup] <mode> <mode-subcommands...> + The only two valid _mode-subcommands..._ are *bindsym* and *bindcode*. If _--pango\_markup_ is given, then _mode_ will be interpreted as pango markup. @@ -533,8 +538,15 @@ You may combine output commands into one, like so: output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch You can get a list of output names with *swaymsg -t get\_outputs*. You may also -match any output by using the output name "\*". Be sure to add this output -config after the others, or it will be matched instead of the others. +match any output by using the output name "\*". + +*set* $<name> <value> + Sets variable $_name_ to _value_. You can use the new variable in the + arguments of future commands. When the variable is used, it can be escaped + with an additional $ (ie $$_name_) to have the replacement happen at run + time instead of when reading the config. However, it does not always make + sense for the variable to be replaced at run time since some arguments do + need to be known at config time. *show\_marks* yes|no If *show\_marks* is yes, marks will be displayed in the window borders. diff --git a/sway/tree/container.c b/sway/tree/container.c index 53b127b7..baaa82fd 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -67,12 +67,10 @@ void container_destroy(struct sway_container *con) { list_free(con->outputs); if (con->view) { - struct sway_view *view = con->view; - view->container = NULL; - free(view->title_format); - view->title_format = NULL; - - if (view->destroying) { + if (con->view->container == con) { + con->view->container = NULL; + } + if (con->view->destroying) { view_destroy(con->view); } } @@ -985,7 +983,8 @@ void container_discover_outputs(struct sway_container *con) { } } struct sway_output *new_output = container_get_effective_output(con); - double old_scale = old_output ? old_output->wlr_output->scale : -1; + double old_scale = old_output && old_output->enabled ? + old_output->wlr_output->scale : -1; double new_scale = new_output ? new_output->wlr_output->scale : -1; if (old_scale != new_scale) { container_update_title_textures(con); diff --git a/sway/tree/view.c b/sway/tree/view.c index e370443c..a024f325 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -989,12 +989,16 @@ bool view_is_visible(struct sway_view *view) { floater = floater->parent; } bool is_sticky = container_is_floating(floater) && floater->is_sticky; + if (!is_sticky && !workspace_is_visible(workspace)) { + return false; + } // Check view isn't in a tabbed or stacked container on an inactive tab struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *con = view->container; while (con) { enum sway_container_layout layout = container_parent_layout(con); - if (layout == L_TABBED || layout == L_STACKED) { + if ((layout == L_TABBED || layout == L_STACKED) + && !container_is_floating(con)) { struct sway_node *parent = con->parent ? &con->parent->node : &con->workspace->node; if (seat_get_active_tiling_child(seat, parent) != &con->node) { @@ -1008,10 +1012,6 @@ bool view_is_visible(struct sway_view *view) { !container_is_fullscreen_or_child(view->container)) { return false; } - // Check the workspace is visible - if (!is_sticky) { - return workspace_is_visible(workspace); - } return true; } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 16031e87..d65a3e4c 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -20,17 +20,23 @@ #include "log.h" #include "util.h" +struct workspace_config *workspace_find_config(const char *ws_name) { + for (int i = 0; i < config->workspace_configs->length; ++i) { + struct workspace_config *wsc = config->workspace_configs->items[i]; + if (strcmp(wsc->workspace, ws_name) == 0) { + return wsc; + } + } + return NULL; +} + struct sway_output *workspace_get_initial_output(const char *name) { - // Search for workspace<->output pair - 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 - struct sway_output *output = output_by_name(wso->output); - if (output) { - return output; - } - break; + // Check workspace configs for a workspace<->output pair + struct workspace_config *wsc = workspace_find_config(name); + if (wsc && wsc->output) { + struct sway_output *output = output_by_name(wsc->output); + if (output) { + return output; } } // Otherwise put it on the focused output @@ -121,17 +127,8 @@ void next_name_map(struct sway_container *ws, void *data) { 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; + struct workspace_config *wsc = workspace_find_config(ws_name); + return !wsc || !wsc->output || strcmp(wsc->output, output_name) == 0; } static void workspace_name_from_binding(const struct sway_binding * binding, @@ -231,13 +228,13 @@ char *workspace_next_name(const char *output_name) { workspace_name_from_binding(mode->keycode_bindings->items[i], output_name, &order, &target); } - for (int i = 0; i < config->workspace_outputs->length; ++i) { + for (int i = 0; i < config->workspace_configs->length; ++i) { // Unlike with bindings, this does not guarantee order - const struct workspace_output *wso = config->workspace_outputs->items[i]; - if (strcmp(wso->output, output_name) == 0 - && workspace_by_name(wso->workspace) == NULL) { + const struct workspace_config *wsc = config->workspace_configs->items[i]; + if (wsc->output && strcmp(wsc->output, output_name) == 0 + && workspace_by_name(wsc->workspace) == NULL) { free(target); - target = strdup(wso->workspace); + target = strdup(wsc->workspace); break; } } diff --git a/swaybar/bar.c b/swaybar/bar.c index ab307fd4..15e81976 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -16,12 +16,13 @@ #else #include <linux/input-event-codes.h> #endif -#include "swaybar/render.h" +#include "swaybar/bar.h" #include "swaybar/config.h" #include "swaybar/event_loop.h" -#include "swaybar/status_line.h" -#include "swaybar/bar.h" +#include "swaybar/i3bar.h" #include "swaybar/ipc.h" +#include "swaybar/status_line.h" +#include "swaybar/render.h" #include "ipc-client.h" #include "list.h" #include "log.h" @@ -478,14 +479,16 @@ static void render_all_frames(struct swaybar *bar) { } } -void bar_setup(struct swaybar *bar, +bool bar_setup(struct swaybar *bar, const char *socket_path, const char *bar_id) { bar_init(bar); init_event_loop(); bar->ipc_socketfd = ipc_open_socket(socket_path); bar->ipc_event_socketfd = ipc_open_socket(socket_path); - ipc_initialize(bar, bar_id); + if (!ipc_initialize(bar, bar_id)) { + return false; + } if (bar->config->status_command) { bar->status = status_line_init(bar->config->status_command); } @@ -526,6 +529,7 @@ void bar_setup(struct swaybar *bar, ipc_get_workspaces(bar); render_all_frames(bar); + return true; } static void display_in(int fd, short mask, void *data) { diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 325aa61a..8e9b038b 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c @@ -6,7 +6,9 @@ #include <string.h> #include <unistd.h> #include <wlr/util/log.h> +#include "swaybar/bar.h" #include "swaybar/config.h" +#include "swaybar/i3bar.h" #include "swaybar/status_line.h" void i3bar_block_unref(struct i3bar_block *block) { diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 0e60c10c..7c53a44f 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -141,9 +141,16 @@ static void ipc_parse_colors( } } -static void ipc_parse_config( +static bool ipc_parse_config( struct swaybar_config *config, const char *payload) { json_object *bar_config = json_tokener_parse(payload); + json_object *success; + if (json_object_object_get_ex(bar_config, "success", &success) + && !json_object_get_boolean(success)) { + wlr_log(WLR_ERROR, "No bar with that ID. Use 'swaymsg -t get_bar_config to get the available bar configs."); + json_object_put(bar_config); + return false; + } json_object *markup, *mode, *hidden_bar, *position, *status_command; json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers; json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; @@ -226,10 +233,10 @@ static void ipc_parse_config( } json_object_put(bar_config); + return true; } void ipc_get_workspaces(struct swaybar *bar) { - bar->focused_output = NULL; struct swaybar_output *output; wl_list_for_each(output, &bar->outputs, link) { free_workspaces(&output->workspaces); @@ -312,11 +319,14 @@ static void ipc_get_outputs(struct swaybar *bar) { free(res); } -void ipc_initialize(struct swaybar *bar, const char *bar_id) { +bool ipc_initialize(struct swaybar *bar, const char *bar_id) { uint32_t len = strlen(bar_id); char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len); - ipc_parse_config(bar->config, res); + if (!ipc_parse_config(bar->config, res)) { + free(res); + return false; + } free(res); ipc_get_outputs(bar); @@ -324,6 +334,7 @@ void ipc_initialize(struct swaybar *bar, const char *bar_id) { len = strlen(subscribe); free(ipc_single_command(bar->ipc_event_socketfd, IPC_SUBSCRIBE, subscribe, &len)); + return true; } bool handle_ipc_readable(struct swaybar *bar) { diff --git a/swaybar/main.c b/swaybar/main.c index 60e4b37c..d2c579db 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -96,7 +96,10 @@ int main(int argc, char **argv) { signal(SIGTERM, sig_handler); - bar_setup(&swaybar, socket_path, bar_id); + if (!bar_setup(&swaybar, socket_path, bar_id)) { + free(socket_path); + return 1; + } free(socket_path); free(bar_id); diff --git a/swaybar/render.c b/swaybar/render.c index 9413dc57..90e5bac7 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -10,6 +10,7 @@ #include "pool-buffer.h" #include "swaybar/bar.h" #include "swaybar/config.h" +#include "swaybar/i3bar.h" #include "swaybar/ipc.h" #include "swaybar/render.h" #include "swaybar/status_line.h" @@ -20,47 +21,47 @@ static const double WS_VERTICAL_PADDING = 1.5; static const double BORDER_WIDTH = 1; static uint32_t render_status_line_error(cairo_t *cairo, - struct swaybar_output *output, struct swaybar_config *config, - const char *error, double *x, uint32_t surface_height) { + struct swaybar_output *output, double *x) { + const char *error = output->bar->status->text; if (!error) { return 0; } - uint32_t height = surface_height * output->scale; + uint32_t height = output->height * output->scale; cairo_set_source_u32(cairo, 0xFF0000FF); int margin = 3 * output->scale; int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; + char *font = output->bar->config->font; int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, + get_text_size(cairo, font, &text_width, &text_height, NULL, output->scale, false, "%s", error); uint32_t ideal_height = text_height + ws_vertical_padding * 2; uint32_t ideal_surface_height = ideal_height / output->scale; - if (surface_height < ideal_surface_height) { + if (output->height < ideal_surface_height) { return ideal_surface_height; } *x -= text_width + margin; double text_y = height / 2.0 - text_height / 2.0; cairo_move_to(cairo, *x, (int)floor(text_y)); - pango_printf(cairo, config->font, output->scale, false, "%s", error); + pango_printf(cairo, font, output->scale, false, "%s", error); *x -= margin; - return surface_height; + return output->height; } static uint32_t render_status_line_text(cairo_t *cairo, - struct swaybar_output *output, struct swaybar_config *config, - const char *text, bool focused, double *x, uint32_t surface_height) { + struct swaybar_output *output, double *x) { + const char *text = output->bar->status->text; if (!text) { return 0; } - uint32_t height = surface_height * output->scale; - - cairo_set_source_u32(cairo, focused ? + struct swaybar_config *config = output->bar->config; + cairo_set_source_u32(cairo, output->focused ? config->colors.focused_statusline : config->colors.statusline); int text_width, text_height; @@ -72,17 +73,18 @@ static uint32_t render_status_line_text(cairo_t *cairo, uint32_t ideal_height = text_height + ws_vertical_padding * 2; uint32_t ideal_surface_height = ideal_height / output->scale; - if (surface_height < ideal_surface_height) { + if (output->height < ideal_surface_height) { return ideal_surface_height; } *x -= text_width + margin; + uint32_t height = output->height * output->scale; double text_y = height / 2.0 - text_height / 2.0; cairo_move_to(cairo, *x, (int)floor(text_y)); pango_printf(cairo, config->font, output->scale, config->pango_markup, "%s", text); *x -= margin; - return surface_height; + return output->height; } static void render_sharp_line(cairo_t *cairo, uint32_t color, @@ -122,12 +124,11 @@ static void i3bar_block_unref_callback(void *data) { static uint32_t render_status_block(cairo_t *cairo, struct swaybar_output *output, struct i3bar_block *block, double *x, - uint32_t surface_height, bool focused, bool edge) { + bool edge) { if (!block->full_text || !*block->full_text) { return 0; } - uint32_t height = surface_height * output->scale; struct swaybar_config *config = output->bar->config; int text_width, text_height; @@ -145,7 +146,7 @@ static uint32_t render_status_block(cairo_t *cairo, double block_width = width; uint32_t ideal_height = text_height + ws_vertical_padding * 2; uint32_t ideal_surface_height = ideal_height / output->scale; - if (surface_height < ideal_surface_height) { + if (output->height < ideal_surface_height) { return ideal_surface_height; } @@ -166,7 +167,7 @@ static uint32_t render_status_block(cairo_t *cairo, output->scale, false, "%s", config->sep_symbol); uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; uint32_t _ideal_surface_height = _ideal_height / output->scale; - if (surface_height < _ideal_surface_height) { + if (output->height < _ideal_surface_height) { return _ideal_surface_height; } if (sep_width > block->separator_block_width) { @@ -178,6 +179,7 @@ static uint32_t render_status_block(cairo_t *cairo, *x -= margin; } + uint32_t height = output->height * output->scale; if (output->bar->status->click_events) { struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); hotspot->x = *x; @@ -241,7 +243,7 @@ static uint32_t render_status_block(cairo_t *cairo, } if (!edge && block->separator) { - if (focused) { + if (output->focused) { cairo_set_source_u32(cairo, config->colors.focused_separator); } else { cairo_set_source_u32(cairo, config->colors.separator); @@ -260,19 +262,16 @@ static uint32_t render_status_block(cairo_t *cairo, cairo_stroke(cairo); } } - return surface_height; + return output->height; } static uint32_t render_status_line_i3bar(cairo_t *cairo, - struct swaybar_config *config, struct swaybar_output *output, - struct status_line *status, bool focused, - double *x, uint32_t surface_height) { + struct swaybar_output *output, double *x) { uint32_t max_height = 0; bool edge = true; struct i3bar_block *block; - wl_list_for_each(block, &status->blocks, link) { - uint32_t h = render_status_block(cairo, output, - block, x, surface_height, focused, edge); + wl_list_for_each(block, &output->bar->status->blocks, link) { + uint32_t h = render_status_block(cairo, output, block, x, edge); max_height = h > max_height ? h : max_height; edge = false; } @@ -280,19 +279,15 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo, } static uint32_t render_status_line(cairo_t *cairo, - struct swaybar_config *config, struct swaybar_output *output, - struct status_line *status, bool focused, - double *x, uint32_t surface_height) { + struct swaybar_output *output, double *x) { + struct status_line *status = output->bar->status; switch (status->protocol) { case PROTOCOL_ERROR: - return render_status_line_error(cairo, output, config, - status->text, x, surface_height); + return render_status_line_error(cairo, output, x); case PROTOCOL_TEXT: - return render_status_line_text(cairo, output, config, - status->text, focused, x, surface_height); + return render_status_line_text(cairo, output, x); case PROTOCOL_I3BAR: - return render_status_line_i3bar(cairo, config, output, - status, focused, x, surface_height); + return render_status_line_i3bar(cairo, output, x); case PROTOCOL_UNDEF: return 0; } @@ -300,10 +295,9 @@ static uint32_t render_status_line(cairo_t *cairo, } static uint32_t render_binding_mode_indicator(cairo_t *cairo, - struct swaybar_output *output, struct swaybar_config *config, - const char *mode, double x, uint32_t surface_height) { - uint32_t height = surface_height * output->scale; - + struct swaybar_output *output, double x) { + struct swaybar_config *config = output->bar->config; + const char *mode = config->mode; int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, NULL, output->scale, config->mode_pango_markup, @@ -316,11 +310,12 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, uint32_t ideal_height = text_height + ws_vertical_padding * 2 + border_width * 2; uint32_t ideal_surface_height = ideal_height / output->scale; - if (surface_height < ideal_surface_height) { + if (output->height < ideal_surface_height) { return ideal_surface_height; } uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; + uint32_t height = output->height * output->scale; cairo_set_source_u32(cairo, config->colors.binding_mode.background); cairo_rectangle(cairo, x, 0, width, height); cairo_fill(cairo); @@ -340,7 +335,7 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); pango_printf(cairo, config->font, output->scale, config->mode_pango_markup, "%s", mode); - return surface_height; + return output->height; } static const char *strip_workspace_number(const char *ws_name) { @@ -366,8 +361,9 @@ static enum hotspot_event_handling workspace_hotspot_callback(struct swaybar_out } static uint32_t render_workspace_button(cairo_t *cairo, - struct swaybar_output *output, struct swaybar_config *config, - struct swaybar_workspace *ws, double *x, uint32_t surface_height) { + struct swaybar_output *output, + struct swaybar_workspace *ws, double *x) { + struct swaybar_config *config = output->bar->config; const char *name = ws->name; if (config->strip_workspace_numbers) { name = strip_workspace_number(ws->name); @@ -384,7 +380,7 @@ static uint32_t render_workspace_button(cairo_t *cairo, box_colors = config->colors.inactive_workspace; } - uint32_t height = surface_height * output->scale; + uint32_t height = output->height * output->scale; int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, NULL, @@ -397,7 +393,7 @@ static uint32_t render_workspace_button(cairo_t *cairo, uint32_t ideal_height = ws_vertical_padding * 2 + text_height + border_width * 2; uint32_t ideal_surface_height = ideal_height / output->scale; - if (surface_height < ideal_surface_height) { + if (output->height < ideal_surface_height) { return ideal_surface_height; } @@ -434,11 +430,11 @@ static uint32_t render_workspace_button(cairo_t *cairo, wl_list_insert(&output->hotspots, &hotspot->link); *x += width; - return surface_height; + return output->height; } -static uint32_t render_to_cairo(cairo_t *cairo, - struct swaybar *bar, struct swaybar_output *output) { +static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { + struct swaybar *bar = output->bar; struct swaybar_config *config = bar->config; cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); if (output->focused) { @@ -458,22 +454,19 @@ static uint32_t render_to_cairo(cairo_t *cairo, */ double x = output->width * output->scale; if (bar->status) { - uint32_t h = render_status_line(cairo, config, output, - bar->status, output->focused, &x, output->height); + uint32_t h = render_status_line(cairo, output, &x); max_height = h > max_height ? h : max_height; } x = 0; if (config->workspace_buttons) { struct swaybar_workspace *ws; wl_list_for_each_reverse(ws, &output->workspaces, link) { - uint32_t h = render_workspace_button(cairo, - output, config, ws, &x, output->height); + uint32_t h = render_workspace_button(cairo, output, ws, &x); max_height = h > max_height ? h : max_height; } } if (config->binding_mode_indicator && config->mode) { - uint32_t h = render_binding_mode_indicator(cairo, - output, config, config->mode, x, output->height); + uint32_t h = render_binding_mode_indicator(cairo, output, x); max_height = h > max_height ? h : max_height; } @@ -506,9 +499,10 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); cairo_paint(cairo); cairo_restore(cairo); - uint32_t height = render_to_cairo(cairo, bar, output); - if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) { - height = bar->config->height; + uint32_t height = render_to_cairo(cairo, output); + int config_height = output->bar->config->height; + if (config_height >= 0 && height < (uint32_t)config_height) { + height = config_height; } if (height != output->height) { // Reconfigure surface @@ -519,7 +513,7 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { wl_surface_commit(output->surface); } else if (height > 0) { // Replay recording into shm and send it off - output->current_buffer = get_next_buffer(bar->shm, + output->current_buffer = get_next_buffer(output->bar->shm, output->buffers, output->width * output->scale, output->height * output->scale); diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 48b43248..ed6dc7c8 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -1,12 +1,15 @@ #define _POSIX_C_SOURCE 200809L #include <fcntl.h> +#include <sys/ioctl.h> #include <json-c/json.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <wlr/util/log.h> +#include "swaybar/bar.h" #include "swaybar/config.h" +#include "swaybar/i3bar.h" #include "swaybar/event_loop.h" #include "swaybar/status_line.h" #include "readline.h" @@ -34,18 +37,35 @@ bool status_handle_readable(struct status_line *status) { switch (status->protocol) { case PROTOCOL_UNDEF: errno = 0; - read_bytes = getline(&status->buffer, - &status->buffer_size, status->read); - if (errno == EAGAIN) { - clearerr(status->read); - } else if (errno) { + int available_bytes; + if (ioctl(status->read_fd, FIONREAD, &available_bytes) == -1) { + wlr_log(WLR_ERROR, "Unable to read status command output size"); status_error(status, "[error reading from status command]"); return true; } + if ((size_t)available_bytes + 1 > status->buffer_size) { + // need room for leading '\0' too + status->buffer_size = available_bytes + 1; + status->buffer = realloc(status->buffer, status->buffer_size); + } + if (status->buffer == NULL) { + wlr_log_errno(WLR_ERROR, "Unable to read status line"); + status_error(status, "[error reading from status command]"); + return true; + } + + read_bytes = read(status->read_fd, status->buffer, available_bytes); + if (read_bytes != available_bytes) { + status_error(status, "[error reading from status command]"); + return true; + } + status->buffer[available_bytes] = 0; + // the header must be sent completely the first time round + char *newline = strchr(status->buffer, '\n'); json_object *header, *version; - if (status->buffer[read_bytes - 1] == '\n' + if (newline != NULL && (header = json_tokener_parse(status->buffer)) && json_object_object_get_ex(header, "version", &version) && json_object_get_int(version) == 1) { @@ -67,13 +87,9 @@ bool status_handle_readable(struct status_line *status) { wl_list_init(&status->blocks); status->tokener = json_tokener_new(); - read_bytes = getdelim(&status->buffer, &status->buffer_size, EOF, status->read); - if (read_bytes > 0) { - status->buffer_index = read_bytes; - return i3bar_handle_readable(status); - } else { - return false; - } + status->buffer_index = strlen(newline + 1); + memmove(status->buffer, newline + 1, status->buffer_index + 1); + return i3bar_handle_readable(status); } wlr_log(WLR_DEBUG, "Using text protocol."); diff --git a/swaylock/main.c b/swaylock/main.c index c25c8eec..693cbc10 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -845,6 +845,9 @@ static int load_config(char *path, struct swaylock_state *state, static struct swaylock_state state; int main(int argc, char **argv) { + wlr_log_init(WLR_DEBUG, NULL); + initialize_pw_backend(); + enum line_mode line_mode = LM_LINE; state.args = (struct swaylock_args){ .mode = BACKGROUND_MODE_SOLID_COLOR, @@ -857,8 +860,6 @@ int main(int argc, char **argv) { wl_list_init(&state.images); set_default_colors(&state.args.colors); - wlr_log_init(WLR_DEBUG, NULL); - char *config_path = NULL; int result = parse_options(argc, argv, NULL, NULL, &config_path); if (result != 0) { diff --git a/swaylock/meson.build b/swaylock/meson.build index 675b8c69..6c87d173 100644 --- a/swaylock/meson.build +++ b/swaylock/meson.build @@ -1,25 +1,37 @@ sysconfdir = get_option('sysconfdir') -executable( - 'swaylock', [ - 'main.c', - 'password.c', - 'render.c', - 'seat.c' - ], +dependencies = [ + cairo, + client_protos, + gdk_pixbuf, + math, + pango, + pangocairo, + xkbcommon, + wayland_client, + wlroots, +] + +sources = [ + 'main.c', + 'password.c', + 'render.c', + 'seat.c' +] + +if libpam.found() + sources += ['pam.c'] + dependencies += [libpam] +else + warning('The swaylock binary must be setuid when compiled without libpam') + warning('You must do this manually post-install: chmod a+s /path/to/swaylock') + sources += ['shadow.c'] +endif + +executable('swaylock', + sources, include_directories: [sway_inc], - dependencies: [ - cairo, - client_protos, - gdk_pixbuf, - libpam, - math, - pango, - pangocairo, - xkbcommon, - wayland_client, - wlroots, - ], + dependencies: dependencies, link_with: [lib_sway_common, lib_sway_client], install: true ) diff --git a/swaylock/pam.c b/swaylock/pam.c new file mode 100644 index 00000000..cac95a85 --- /dev/null +++ b/swaylock/pam.c @@ -0,0 +1,62 @@ +#define _XOPEN_SOURCE 500 +#include <pwd.h> +#include <security/pam_appl.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wlr/util/log.h> +#include "swaylock/swaylock.h" + +void initialize_pw_backend(void) { + // TODO: only call pam_start once. keep the same handle the whole time +} + +static int function_conversation(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *data) { + struct swaylock_password *pw = data; + /* PAM expects an array of responses, one for each message */ + struct pam_response *pam_reply = calloc( + num_msg, sizeof(struct pam_response)); + *resp = pam_reply; + for (int i = 0; i < num_msg; ++i) { + switch (msg[i]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + case PAM_PROMPT_ECHO_ON: + pam_reply[i].resp = strdup(pw->buffer); // PAM clears and frees this + break; + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + break; + } + } + return PAM_SUCCESS; +} + +bool attempt_password(struct swaylock_password *pw) { + struct passwd *passwd = getpwuid(getuid()); + char *username = passwd->pw_name; + const struct pam_conv local_conversation = { + function_conversation, pw + }; + pam_handle_t *local_auth_handle = NULL; + int pam_err; + if ((pam_err = pam_start("swaylock", username, + &local_conversation, &local_auth_handle)) != PAM_SUCCESS) { + wlr_log(WLR_ERROR, "PAM returned error %d", pam_err); + } + if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) { + wlr_log(WLR_ERROR, "pam_authenticate failed"); + goto fail; + } + // TODO: only call pam_end once we succeed at authing. refresh tokens beforehand + if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) { + wlr_log(WLR_ERROR, "pam_end failed"); + goto fail; + } + clear_password_buffer(pw); + return true; +fail: + clear_password_buffer(pw); + return false; +} diff --git a/swaylock/password.c b/swaylock/password.c index 7c686b34..6a956bcb 100644 --- a/swaylock/password.c +++ b/swaylock/password.c @@ -1,7 +1,6 @@ #define _XOPEN_SOURCE 500 #include <assert.h> #include <pwd.h> -#include <security/pam_appl.h> #include <stdlib.h> #include <string.h> #include <unistd.h> @@ -11,27 +10,6 @@ #include "swaylock/seat.h" #include "unicode.h" -static int function_conversation(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *data) { - struct swaylock_password *pw = data; - /* PAM expects an array of responses, one for each message */ - struct pam_response *pam_reply = calloc( - num_msg, sizeof(struct pam_response)); - *resp = pam_reply; - for (int i = 0; i < num_msg; ++i) { - switch (msg[i]->msg_style) { - case PAM_PROMPT_ECHO_OFF: - case PAM_PROMPT_ECHO_ON: - pam_reply[i].resp = strdup(pw->buffer); // PAM clears and frees this - break; - case PAM_ERROR_MSG: - case PAM_TEXT_INFO: - break; - } - } - return PAM_SUCCESS; -} - void clear_password_buffer(struct swaylock_password *pw) { // Use volatile keyword so so compiler can't optimize this out. volatile char *buffer = pw->buffer; @@ -42,35 +20,6 @@ void clear_password_buffer(struct swaylock_password *pw) { pw->len = 0; } -static bool attempt_password(struct swaylock_password *pw) { - struct passwd *passwd = getpwuid(getuid()); - char *username = passwd->pw_name; - const struct pam_conv local_conversation = { - function_conversation, pw - }; - pam_handle_t *local_auth_handle = NULL; - int pam_err; - // TODO: only call pam_start once. keep the same handle the whole time - if ((pam_err = pam_start("swaylock", username, - &local_conversation, &local_auth_handle)) != PAM_SUCCESS) { - wlr_log(WLR_ERROR, "PAM returned error %d", pam_err); - } - if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) { - wlr_log(WLR_ERROR, "pam_authenticate failed"); - goto fail; - } - // TODO: only call pam_end once we succeed at authing. refresh tokens beforehand - if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) { - wlr_log(WLR_ERROR, "pam_end failed"); - goto fail; - } - clear_password_buffer(pw); - return true; -fail: - clear_password_buffer(pw); - return false; -} - static bool backspace(struct swaylock_password *pw) { if (pw->len != 0) { pw->buffer[--pw->len] = 0; diff --git a/swaylock/shadow.c b/swaylock/shadow.c new file mode 100644 index 00000000..1f10514c --- /dev/null +++ b/swaylock/shadow.c @@ -0,0 +1,128 @@ +#define _XOPEN_SOURCE +#include <pwd.h> +#include <shadow.h> +#include <stdbool.h> +#include <sys/types.h> +#include <unistd.h> +#include <wlr/util/log.h> +#include "swaylock/swaylock.h" + +static int comm[2][2]; + +void run_child(void) { + /* This code runs as root */ + struct passwd *pwent = getpwuid(getuid()); + if (!pwent) { + wlr_log_errno(WLR_ERROR, "failed to getpwuid"); + exit(EXIT_FAILURE); + } + char *encpw = pwent->pw_passwd; + if (strcmp(encpw, "x") == 0) { + struct spwd *swent = getspnam(pwent->pw_name); + if (!swent) { + wlr_log_errno(WLR_ERROR, "failed to getspnam"); + exit(EXIT_FAILURE); + } + encpw = swent->sp_pwdp; + } + wlr_log(WLR_DEBUG, "prepared to authorize user %s", pwent->pw_name); + + size_t size; + char *buf; + while (1) { + ssize_t amt; + amt = read(comm[0][0], &size, sizeof(size)); + if (amt == 0) { + break; + } else if (amt < 0) { + wlr_log_errno(WLR_ERROR, "read pw request"); + } + wlr_log(WLR_DEBUG, "received pw check request"); + buf = malloc(size); + if (!buf) { + wlr_log_errno(WLR_ERROR, "failed to malloc pw buffer"); + exit(EXIT_FAILURE); + } + size_t offs = 0; + do { + amt = read(comm[0][0], &buf[offs], size - offs); + if (amt <= 0) { + wlr_log_errno(WLR_ERROR, "failed to read pw"); + exit(EXIT_FAILURE); + } + offs += (size_t)amt; + } while (offs < size); + bool result = false; + char *c = crypt(buf, encpw); + if (c == NULL) { + wlr_log_errno(WLR_ERROR, "crypt"); + } + result = strcmp(c, encpw) == 0; + if (write(comm[1][1], &result, sizeof(result)) != sizeof(result)) { + wlr_log_errno(WLR_ERROR, "failed to write pw check result"); + exit(EXIT_FAILURE); + } + free(buf); + } + exit(EXIT_SUCCESS); +} + +void initialize_pw_backend(void) { + if (geteuid() != 0) { + wlr_log(WLR_ERROR, "swaylock needs to be setuid to read /etc/shadow"); + exit(EXIT_FAILURE); + } + if (pipe(comm[0]) != 0) { + wlr_log_errno(WLR_ERROR, "failed to create pipe"); + exit(EXIT_FAILURE); + } + if (pipe(comm[1]) != 0) { + wlr_log_errno(WLR_ERROR, "failed to create pipe"); + exit(EXIT_FAILURE); + } + pid_t child = fork(); + if (child == 0) { + close(comm[0][1]); + close(comm[1][0]); + run_child(); + } else if (child < 0) { + wlr_log_errno(WLR_ERROR, "failed to fork"); + exit(EXIT_FAILURE); + } + close(comm[0][0]); + close(comm[1][1]); + if (setgid(getgid()) != 0) { + wlr_log_errno(WLR_ERROR, "Unable to drop root"); + exit(EXIT_FAILURE); + } + if (setuid(getuid()) != 0) { + wlr_log_errno(WLR_ERROR, "Unable to drop root"); + exit(EXIT_FAILURE); + } +} + +bool attempt_password(struct swaylock_password *pw) { + bool result = false; + size_t len = pw->len + 1; + size_t offs = 0; + if (write(comm[0][1], &len, sizeof(len)) < 0) { + wlr_log_errno(WLR_ERROR, "Failed to request pw check"); + goto ret; + } + do { + ssize_t amt = write(comm[0][1], &pw->buffer[offs], len - offs); + if (amt < 0) { + wlr_log_errno(WLR_ERROR, "Failed to write pw buffer"); + goto ret; + } + offs += amt; + } while (offs < len); + if (read(comm[1][0], &result, sizeof(result)) != sizeof(result)) { + wlr_log_errno(WLR_ERROR, "Failed to read pw result"); + goto ret; + } + wlr_log(WLR_DEBUG, "pw result: %d", result); +ret: + clear_password_buffer(pw); + return result; +} |