diff options
107 files changed, 1477 insertions, 1266 deletions
diff --git a/README.ja.md b/README.ja.md index 75d29c73..b0488c53 100644 --- a/README.ja.md +++ b/README.ja.md @@ -5,7 +5,7 @@ i3互換な[Wayland](http://wayland.freedesktop.org/)コンポジタです。 [FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。 [IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)もあります。 -**注意**: Swayは現在*凍結中*であり、Swayとwlrootsの統合が完了するまで、新たな機能は追加されません。バグフィックスは行われます。詳しくは[この記事](https://drewdevault.com/2017/10/09/Future-of-sway.html)をご覧ください。wlrootsとの統合状況については、[このチケット](https://github.com/swaywm/sway/issues/1390)をご覧ください。 +**注意**: Swayは現在*凍結中*であり、wlcからwlrootsへの移植が完了するまで新たな機能は追加されません。2018年9月以降に発見されるバグは0.15では対応されません。詳しくは[この記事](https://drewdevault.com/2017/10/09/Future-of-sway.html)をご覧ください。wlrootsとの統合状況については、[このチケット](https://github.com/swaywm/sway/issues/1390)をご覧ください。 [](https://sr.ht/ICd5.png) diff --git a/common/background-image.c b/common/background-image.c index f3d2551e..5ede55e3 100644 --- a/common/background-image.c +++ b/common/background-image.c @@ -58,6 +58,7 @@ void render_background_image(cairo_t *cairo, cairo_surface_t *image, double width = cairo_image_surface_get_width(image); double height = cairo_image_surface_get_height(image); + cairo_save(cairo); switch (mode) { case BACKGROUND_MODE_STRETCH: cairo_scale(cairo, @@ -116,4 +117,5 @@ void render_background_image(cairo_t *cairo, cairo_surface_t *image, break; } cairo_paint(cairo); + cairo_restore(cairo); } diff --git a/common/cairo.c b/common/cairo.c index c267c77c..e8231484 100644 --- a/common/cairo.c +++ b/common/cairo.c @@ -13,6 +13,22 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { (color >> (0*8) & 0xFF) / 255.0); } +cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel) { + switch (subpixel) { + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: + return CAIRO_SUBPIXEL_ORDER_RGB; + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: + return CAIRO_SUBPIXEL_ORDER_BGR; + case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: + return CAIRO_SUBPIXEL_ORDER_VRGB; + case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: + return CAIRO_SUBPIXEL_ORDER_VBGR; + default: + return CAIRO_SUBPIXEL_ORDER_DEFAULT; + } + return CAIRO_SUBPIXEL_ORDER_DEFAULT; +} + cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height) { int image_width = cairo_image_surface_get_width(image); diff --git a/common/pango.c b/common/pango.c index ea71ac4a..ba74692e 100644 --- a/common/pango.c +++ b/common/pango.c @@ -6,67 +6,47 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include "cairo.h" #include "log.h" +#include "stringop.h" -int escape_markup_text(const char *src, char *dest, int dest_length) { - int length = 0; +size_t escape_markup_text(const char *src, char *dest) { + size_t length = 0; + if (dest) { + dest[0] = '\0'; + } while (src[0]) { switch (src[0]) { case '&': length += 5; - if (dest && dest_length - length >= 0) { - dest += sprintf(dest, "%s", "&"); - } else { - dest_length = -1; - } + lenient_strcat(dest, "&"); break; case '<': length += 4; - if (dest && dest_length - length >= 0) { - dest += sprintf(dest, "%s", "<"); - } else { - dest_length = -1; - } + lenient_strcat(dest, "<"); break; case '>': length += 4; - if (dest && dest_length - length >= 0) { - dest += sprintf(dest, "%s", ">"); - } else { - dest_length = -1; - } + lenient_strcat(dest, ">"); break; case '\'': length += 6; - if (dest && dest_length - length >= 0) { - dest += sprintf(dest, "%s", "'"); - } else { - dest_length = -1; - } + lenient_strcat(dest, "'"); break; case '"': length += 6; - if (dest && dest_length - length >= 0) { - dest += sprintf(dest, "%s", """); - } else { - dest_length = -1; - } + lenient_strcat(dest, """); break; default: - length += 1; - if (dest && dest_length - length >= 0) { - *(dest++) = *src; - } else { - dest_length = -1; + if (dest) { + dest[length] = *src; + dest[length + 1] = '\0'; } + length += 1; } src++; } - // if we could not fit the escaped string in dest, return -1 - if (dest && dest_length == -1) { - return -1; - } return length; } @@ -78,7 +58,7 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, char *buf; GError *error = NULL; if (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) { - pango_layout_set_markup(layout, buf, -1); + pango_layout_set_text(layout, buf, -1); free(buf); } else { wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", text, @@ -134,6 +114,10 @@ void pango_printf(cairo_t *cairo, const char *font, va_end(args); PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); + cairo_font_options_t *fo = cairo_font_options_create(); + cairo_get_font_options(cairo, fo); + pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); + cairo_font_options_destroy(fo); pango_cairo_update_layout(cairo, layout); pango_cairo_show_layout(cairo, layout); g_object_unref(layout); diff --git a/contrib/awesome.config b/contrib/awesome.config deleted file mode 100644 index 41e17c91..00000000 --- a/contrib/awesome.config +++ /dev/null @@ -1,63 +0,0 @@ -# -# Replicate some of Awesome's default layout manipulation configuration for Sway -# -# Differences: -# - Layout switching doesn't use the spacebar (i.e. i3/Sway behavior to switch to/from floating windows) -# and uses the 'A' key instead (as in auto) -# - Resizing windows uses i3/Sway's more versatile Mod4+r -# - no tags -# - no Maximize/Minize, alternatives to Maximize would be to switch to Stacked/Tabbed layouts -# via Mod4+w or Mod4+s. -# - kill focused client is available on Mod4+Shift+q (instead of Mod4+Shift+c, which maps to Sway's -# config reload) -# - probably many more ... - -# Awesome-style container traversal using Vim-like binding -set $next j -set $prev k - -# -# Moving around: -# - # Move your focus around - bindsym $mod+$next focus next - bindsym $mod+$prev focus prev - - # _move_ the focused window with the same, but add Shift - bindsym $mod+Shift+$next move next - bindsym $mod+Shift+$prev move prev - -# -# Layout: -# - workspace_layout auto left - - # This is usually bound to $mod+space, but this works well in practice by keeping - # all the layout switching keys grouped together. - bindsym $mod+a layout auto next - bindsym $mod+Shift+a layout auto prev - - # Promote a child to master position in an auto layout - bindsym $mod+Control+Return move first - - # Increase/decrease number of master elements in auto layout - bindsym $mod+Shift+h layout auto master inc 1 - bindsym $mod+Shift+l layout auto master inc -1 - - # Increase/decrease number of slave element groups in auto layout - bindsym $mod+Control+h layout auto ncol inc 1 - bindsym $mod+Control+l layout auto ncol inc -1 - -# -# Resizing containers: -# Again, not really the way Awesome works well, but in spirit with i3/Sway and it works well. -# -mode "resize" { - bindsym Left resize shrink width 20 px - bindsym Down resize grow height 20 px - bindsym Up resize shrink height 20 px - bindsym Right resize grow width 20 px -} -bindsym $mod+r mode "resize" - -new_window pixel 1 diff --git a/include/cairo.h b/include/cairo.h index 31672705..86530b60 100644 --- a/include/cairo.h +++ b/include/cairo.h @@ -2,8 +2,10 @@ #define _SWAY_CAIRO_H #include <stdint.h> #include <cairo/cairo.h> +#include <wlr/types/wlr_output.h> void cairo_set_source_u32(cairo_t *cairo, uint32_t color); +cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel); cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height); diff --git a/include/pango.h b/include/pango.h index 09a535a5..6ab83c16 100644 --- a/include/pango.h +++ b/include/pango.h @@ -6,17 +6,13 @@ #include <cairo/cairo.h> #include <pango/pangocairo.h> -/* Utility function which escape characters a & < > ' ". +/** + * Utility function which escape characters a & < > ' ". * - * If the dest parameter is NULL, then the function returns the length of - * of the escaped src string. The dest_length doesn't matter. - * - * If the dest parameter is not NULL then the fuction escapes the src string - * an puts the escaped string in dest and returns the lenght of the escaped string. - * The dest_length parameter is the size of dest array. If the size of dest is not - * enough, then the function returns -1. + * The function returns the length of the escaped string, optionally writing the + * escaped string to dest if provided. */ -int escape_markup_text(const char *src, char *dest, int dest_length); +size_t escape_markup_text(const char *src, char *dest); PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text, double scale, bool markup); void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, diff --git a/include/sway/commands.h b/include/sway/commands.h index 226cf932..afa65340 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -4,6 +4,8 @@ #include <wlr/util/edges.h> #include "config.h" +struct sway_container; + typedef struct cmd_results *sway_cmd(int argc, char **argv); struct cmd_handler { @@ -50,8 +52,13 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers, int handlers_size); /** * Parse and executes a command. + * + * If the command string contains criteria then the command will be executed on + * all matching containers. Otherwise, it'll run on the `con` container. If + * `con` is NULL then it'll run on the currently focused container. */ -struct cmd_results *execute_command(char *command, struct sway_seat *seat); +struct cmd_results *execute_command(char *command, struct sway_seat *seat, + struct sway_container *con); /** * Parse and handles a command during config file loading. * @@ -88,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; @@ -210,6 +217,7 @@ sway_cmd bar_colors_cmd_urgent_workspace; sway_cmd input_cmd_seat; sway_cmd input_cmd_accel_profile; sway_cmd input_cmd_click_method; +sway_cmd input_cmd_drag; sway_cmd input_cmd_drag_lock; sway_cmd input_cmd_dwt; sway_cmd input_cmd_events; diff --git a/include/sway/config.h b/include/sway/config.h index b53c1f1f..eab48aed 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -93,6 +93,7 @@ struct input_config { int accel_profile; int click_method; + int drag; int drag_lock; int dwt; int left_handed; @@ -167,13 +168,14 @@ 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; + int gaps_inner; + int gaps_outer; }; struct bar_config { @@ -327,7 +329,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; @@ -450,20 +452,16 @@ void free_sway_variable(struct sway_variable *var); */ char *do_var_replacement(char *str); -struct cmd_results *check_security_config(); - int input_identifier_cmp(const void *item, const void *data); struct input_config *new_input_config(const char* identifier); void merge_input_config(struct input_config *dst, struct input_config *src); -struct input_config *copy_input_config(struct input_config *ic); +struct input_config *store_input_config(struct input_config *ic); void free_input_config(struct input_config *ic); -void apply_input_config(struct input_config *input); - int seat_name_cmp(const void *item, const void *data); struct seat_config *new_seat_config(const char* name); @@ -474,7 +472,7 @@ struct seat_config *copy_seat_config(struct seat_config *seat); void free_seat_config(struct seat_config *ic); -struct seat_attachment_config *seat_attachment_config_new(); +struct seat_attachment_config *seat_attachment_config_new(void); struct seat_attachment_config *seat_config_get_attachment( struct seat_config *seat_config, char *identifier); @@ -512,9 +510,7 @@ void free_sway_binding(struct sway_binding *sb); void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); -void load_swaybars(); - -void invoke_swaybar(struct bar_config *bar); +void load_swaybars(void); void terminate_swaybg(pid_t pid); @@ -522,6 +518,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/debug.h b/include/sway/debug.h index bf3a5f6d..0e9bb056 100644 --- a/include/sway/debug.h +++ b/include/sway/debug.h @@ -17,6 +17,6 @@ struct sway_debug { extern struct sway_debug debug; -void update_debug_tree(); +void update_debug_tree(void); #endif diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h index fef243e3..1cbfd15d 100644 --- a/include/sway/ipc-json.h +++ b/include/sway/ipc-json.h @@ -4,7 +4,7 @@ #include "sway/tree/container.h" #include "sway/input/input-manager.h" -json_object *ipc_json_get_version(); +json_object *ipc_json_get_version(void); json_object *ipc_json_describe_disabled_output(struct sway_output *o); json_object *ipc_json_describe_node(struct sway_node *node); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 5e281a2f..b865a0f2 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -94,9 +94,6 @@ struct sway_container { // The gaps currently applied to the container. double current_gaps; - bool has_gaps; - double gaps_inner; - double gaps_outer; struct sway_workspace *workspace; // NULL when hidden in the scratchpad struct sway_container *parent; // NULL if container in root of workspace diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index e4b616d1..efdae5a1 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -32,10 +32,9 @@ struct sway_workspace { enum sway_container_layout layout; enum sway_container_layout prev_split_layout; - double current_gaps; - bool has_gaps; - double gaps_inner; - double gaps_outer; + int current_gaps; + int gaps_inner; + int gaps_outer; struct sway_output *output; // NULL if no outputs are connected list_t *floating; // struct sway_container @@ -48,6 +47,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 4065fb8b..de234111 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; @@ -81,8 +80,11 @@ struct swaybar_output { uint32_t width, height; int32_t scale; + enum wl_output_subpixel subpixel; struct pool_buffer buffers[2]; struct pool_buffer *current_buffer; + bool dirty; + bool frame_scheduled; }; struct swaybar_workspace { @@ -94,9 +96,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/config.h b/include/swaybar/config.h index 6739c28a..5f5688cf 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -50,7 +50,7 @@ struct swaybar_config { } colors; }; -struct swaybar_config *init_config(); +struct swaybar_config *init_config(void); void free_config(struct swaybar_config *config); uint32_t parse_position(const char *position); 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/render.h b/include/swaybar/render.h index 071e2298..ebdc69e4 100644 --- a/include/swaybar/render.h +++ b/include/swaybar/render.h @@ -1,10 +1,8 @@ #ifndef _SWAYBAR_RENDER_H #define _SWAYBAR_RENDER_H -struct swaybar; struct swaybar_output; -struct swaybar_config; -void render_frame(struct swaybar *bar, struct swaybar_output *output); +void render_frame(struct swaybar_output *output); #endif 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/swaybar/tray/dbus.h b/include/swaybar/tray/dbus.h deleted file mode 100644 index eb9cfea7..00000000 --- a/include/swaybar/tray/dbus.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _SWAYBAR_DBUS_H -#define _SWAYBAR_DBUS_H - -#include <stdbool.h> -#include <dbus/dbus.h> -extern DBusConnection *conn; - -/** - * Should be called in main loop to dispatch events - */ -void dispatch_dbus(); - -/** - * Initializes async dbus communication - */ -int dbus_init(); - -#endif /* _SWAYBAR_DBUS_H */ diff --git a/include/swaybar/tray/sni_watcher.h b/include/swaybar/tray/sni_watcher.h deleted file mode 100644 index 25ddfcd2..00000000 --- a/include/swaybar/tray/sni_watcher.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _SWAYBAR_SNI_WATCHER_H -#define _SWAYBAR_SNI_WATCHER_H - -/** - * Starts the sni_watcher, the watcher is practically a black box and should - * only be accessed though functions described in its spec - */ -int init_sni_watcher(); - -#endif /* _SWAYBAR_SNI_WATCHER_H */ diff --git a/include/swaybar/tray/tray.h b/include/swaybar/tray/tray.h deleted file mode 100644 index 2d0662be..00000000 --- a/include/swaybar/tray/tray.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef _SWAYBAR_TRAY_H -#define _SWAYBAR_TRAY_H - -#include <stdint.h> -#include <stdbool.h> -#include "swaybar/tray/dbus.h" -#include "swaybar/tray/sni.h" -#include "swaybar/bar.h" -#include "list.h" - -extern struct tray *tray; - -struct tray { - list_t *items; -}; - -/** - * Processes a mouse event on the bar - */ -void tray_mouse_event(struct output *output, int x, int y, - uint32_t button, uint32_t state); - -uint32_t tray_render(struct output *output, struct config *config); - -void tray_upkeep(struct bar *bar); - -/** - * Initializes the tray with D-Bus - */ -void init_tray(struct bar *bar); - -#endif /* _SWAYBAR_TRAY_H */ diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index 950cfaaf..970e3cc9 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -82,6 +82,7 @@ struct swaylock_surface { bool frame_pending, dirty; uint32_t width, height; int32_t scale; + enum wl_output_subpixel subpixel; char *output_name; struct wl_list link; }; @@ -100,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..1e7ce281 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') @@ -38,9 +42,8 @@ pango = dependency('pango') pangocairo = dependency('pangocairo') 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 +73,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() @@ -120,7 +128,16 @@ else endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') -add_project_arguments('-D_LD_LIBRARY_PATH="@0@"'.format(get_option('ld-library-path')), language: 'c') +if get_option('use_rpath') + if get_option('custom_rpath') == '' + # default to platform specific libdir, one level up from the binary + rpathdir = join_paths('$ORIGIN', '..', '$LIB') + else + rpathdir = get_option('custom_rpath') + endif +else + rpathdir = '' +endif sway_inc = include_directories('include') @@ -133,9 +150,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/meson_options.txt b/meson_options.txt index 50d646fd..2db852fc 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,5 +1,6 @@ option('sway-version', type : 'string', description: 'The version string reported in `sway --version`.') -option('ld-library-path', type: 'string', value: '', description: 'The LD_LIBRARY_PATH environment variable.') +option('use_rpath', type: 'boolean', value: false, description: 'install binaries with rpath set') +option('custom_rpath', type: 'string', value: '', description: 'override rpath with a custom one') option('default-wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.') diff --git a/sway/commands.c b/sway/commands.c index 07169f1e..03761c52 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -55,22 +55,6 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type return error; } -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) { - // merge existing config - struct input_config *ic = config->input_configs->items[i]; - merge_input_config(ic, input); - free_input_config(input); - input = ic; - } else { - list_add(config->input_configs, input); - } - - input_manager_apply_input_config(input_manager, input); -} - void apply_seat_config(struct seat_config *seat_config) { int i; i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name); @@ -237,7 +221,8 @@ static void set_config_node(struct sway_node *node) { } } -struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { +struct cmd_results *execute_command(char *_exec, struct sway_seat *seat, + struct sway_container *con) { // 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. concatenate all errors there.) @@ -256,6 +241,15 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { } } + // This is the container or workspace which this command will run on. + // Ignored if the command string contains criteria. + struct sway_node *node; + if (con) { + node = &con->node; + } else { + node = seat_get_focus_inactive(seat, &root->node); + } + config->handler_context.seat = seat; head = exec; @@ -318,9 +312,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { } if (!config->handler_context.using_criteria) { - // without criteria, the command acts upon the focused - // container - set_config_node(seat_get_focus_inactive(seat, &root->node)); + set_config_node(node); struct cmd_results *res = handler->handle(argc-1, argv+1); if (res->status != CMD_SUCCESS) { free_argv(argc, argv); @@ -399,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 { @@ -430,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/bind.c b/sway/commands/bind.c index 047018e0..820c2a6a 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -321,7 +321,7 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) } config->handler_context.seat = seat; - struct cmd_results *results = execute_command(binding->command, NULL); + struct cmd_results *results = execute_command(binding->command, NULL, NULL); if (results->status == CMD_SUCCESS) { ipc_event_binding(binding_copy); } else { diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index d676e475..2e0876a9 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -1,4 +1,5 @@ #include <string.h> +#include <strings.h> #include "sway/commands.h" #include "sway/config.h" #include "sway/tree/arrange.h" @@ -13,172 +14,173 @@ enum gaps_op { GAPS_OP_SUBTRACT }; -enum gaps_scope { - GAPS_SCOPE_ALL, - GAPS_SCOPE_WORKSPACE, - GAPS_SCOPE_CURRENT +struct gaps_data { + bool inner; + enum gaps_op operation; + int amount; }; -struct cmd_results *cmd_gaps(int argc, char **argv) { - struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1); - if (error) { +// gaps edge_gaps on|off|toggle +static struct cmd_results *gaps_edge_gaps(int argc, char **argv) { + struct cmd_results *error; + if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) { return error; } - if (strcmp(argv[0], "edge_gaps") == 0) { - if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) { - return error; - } - - if (strcmp(argv[1], "on") == 0) { - config->edge_gaps = true; - } else if (strcmp(argv[1], "off") == 0) { - config->edge_gaps = false; - } else if (strcmp(argv[1], "toggle") == 0) { - if (!config->active) { - return cmd_results_new(CMD_INVALID, "gaps", - "Cannot toggle gaps while not running."); - } - config->edge_gaps = !config->edge_gaps; - } else { + if (strcmp(argv[1], "on") == 0) { + config->edge_gaps = true; + } else if (strcmp(argv[1], "off") == 0) { + config->edge_gaps = false; + } else if (strcmp(argv[1], "toggle") == 0) { + if (!config->active) { return cmd_results_new(CMD_INVALID, "gaps", - "gaps edge_gaps on|off|toggle"); + "Cannot toggle gaps while not running."); } - arrange_root(); + config->edge_gaps = !config->edge_gaps; } else { - int amount_idx = 0; // the current index in argv - enum gaps_op op = GAPS_OP_SET; - enum gaps_scope scope = GAPS_SCOPE_ALL; - bool inner = true; - - if (strcmp(argv[0], "inner") == 0) { - amount_idx++; - inner = true; - } else if (strcmp(argv[0], "outer") == 0) { - amount_idx++; - inner = false; - } + return cmd_results_new(CMD_INVALID, "gaps", + "gaps edge_gaps on|off|toggle"); + } + arrange_root(); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} - // If one of the long variants of the gaps command is used - // (which starts with inner|outer) check the number of args - if (amount_idx > 0) { // if we've seen inner|outer - if (argc > 2) { // check the longest variant - error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); - if (error) { - return error; - } - } else { // check the next longest format - error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); - if (error) { - return error; - } - } - } else { - error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 1); - if (error) { - return error; - } - } +// gaps inner|outer <px> +static struct cmd_results *gaps_set_defaults(int argc, char **argv) { + struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); + if (error) { + return error; + } - if (argc == 4) { - // Long format: all|workspace|current. - if (strcmp(argv[amount_idx], "all") == 0) { - amount_idx++; - scope = GAPS_SCOPE_ALL; - } else if (strcmp(argv[amount_idx], "workspace") == 0) { - amount_idx++; - scope = GAPS_SCOPE_WORKSPACE; - } else if (strcmp(argv[amount_idx], "current") == 0) { - amount_idx++; - scope = GAPS_SCOPE_CURRENT; - } - - // Long format: set|plus|minus - if (strcmp(argv[amount_idx], "set") == 0) { - amount_idx++; - op = GAPS_OP_SET; - } else if (strcmp(argv[amount_idx], "plus") == 0) { - amount_idx++; - op = GAPS_OP_ADD; - } else if (strcmp(argv[amount_idx], "minus") == 0) { - amount_idx++; - op = GAPS_OP_SUBTRACT; - } - } + bool inner; + if (strcasecmp(argv[0], "inner") == 0) { + inner = true; + } else if (strcasecmp(argv[0], "outer") == 0) { + inner = false; + } else { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer <px>'"); + } - char *end; - double val = strtod(argv[amount_idx], &end); - - if (strlen(end) && val == 0.0) { // invalid <amount> - // guess which variant of the command was attempted - if (argc == 1) { - return cmd_results_new(CMD_INVALID, "gaps", "gaps <amount>"); - } - if (argc == 2) { - return cmd_results_new(CMD_INVALID, "gaps", - "gaps inner|outer <amount>"); - } - return cmd_results_new(CMD_INVALID, "gaps", - "gaps inner|outer all|workspace|current set|plus|minus <amount>"); - } + char *end; + int amount = strtol(argv[1], &end, 10); + if (strlen(end) && strcasecmp(end, "px") != 0) { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer <px>'"); + } + if (amount < 0) { + amount = 0; + } - if (amount_idx == 0) { // gaps <amount> - config->gaps_inner = val; - config->gaps_outer = val; - arrange_root(); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); - } - // Other variants. The middle-length variant (gaps inner|outer <amount>) - // just defaults the scope to "all" and defaults the op to "set". - - double total; - switch (op) { - case GAPS_OP_SUBTRACT: { - total = (inner ? config->gaps_inner : config->gaps_outer) - val; - if (total < 0) { - total = 0; - } - break; - } - case GAPS_OP_ADD: { - total = (inner ? config->gaps_inner : config->gaps_outer) + val; - break; - } - case GAPS_OP_SET: { - total = val; - break; - } - } + if (inner) { + config->gaps_inner = amount; + } else { + config->gaps_outer = amount; + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} - if (scope == GAPS_SCOPE_ALL) { - if (inner) { - config->gaps_inner = total; - } else { - config->gaps_outer = total; - } - arrange_root(); - } else { - if (scope == GAPS_SCOPE_WORKSPACE) { - struct sway_workspace *ws = config->handler_context.workspace; - ws->has_gaps = true; - if (inner) { - ws->gaps_inner = total; - } else { - ws->gaps_outer = total; - } - arrange_workspace(ws); - } else { - struct sway_container *c = config->handler_context.container; - c->has_gaps = true; - if (inner) { - c->gaps_inner = total; - } else { - c->gaps_outer = total; - } - arrange_workspace(c->workspace); - } - } +static void configure_gaps(struct sway_workspace *ws, void *_data) { + struct gaps_data *data = _data; + int *prop = data->inner ? &ws->gaps_inner : &ws->gaps_outer; + + switch (data->operation) { + case GAPS_OP_SET: + *prop = data->amount; + break; + case GAPS_OP_ADD: + *prop += data->amount; + break; + case GAPS_OP_SUBTRACT: + *prop -= data->amount; + break; + } + if (*prop < 0) { + *prop = 0; + } + arrange_workspace(ws); +} + +// gaps inner|outer current|all set|plus|minus <px> +static struct cmd_results *gaps_set_runtime(int argc, char **argv) { + struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); + if (error) { + return error; + } + + struct gaps_data data; + + if (strcasecmp(argv[0], "inner") == 0) { + data.inner = true; + } else if (strcasecmp(argv[0], "outer") == 0) { + data.inner = false; + } else { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); + } + + bool all; + if (strcasecmp(argv[1], "current") == 0) { + all = false; + } else if (strcasecmp(argv[1], "all") == 0) { + all = true; + } else { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); + } + + if (strcasecmp(argv[2], "set") == 0) { + data.operation = GAPS_OP_SET; + } else if (strcasecmp(argv[2], "plus") == 0) { + data.operation = GAPS_OP_ADD; + } else if (strcasecmp(argv[2], "minus") == 0) { + data.operation = GAPS_OP_SUBTRACT; + } else { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); + } + + char *end; + data.amount = strtol(argv[3], &end, 10); + if (strlen(end) && strcasecmp(end, "px") != 0) { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); + } + + if (all) { + root_for_each_workspace(configure_gaps, &data); + } else { + configure_gaps(config->handler_context.workspace, &data); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } + +// gaps edge_gaps on|off|toggle +// gaps inner|outer <px> - sets defaults for workspaces +// gaps inner|outer current|all set|plus|minus <px> - runtime only +struct cmd_results *cmd_gaps(int argc, char **argv) { + struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2); + if (error) { + return error; + } + + if (strcmp(argv[0], "edge_gaps") == 0) { + return gaps_edge_gaps(argc, argv); + } + + if (argc == 2) { + return gaps_set_defaults(argc, argv); + } + if (argc == 4) { + if (config->active) { + return gaps_set_runtime(argc, argv); + } else { + return cmd_results_new(CMD_INVALID, "gaps", + "This syntax can only be used when sway is running"); + } + } + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer <px>' or " + "'gaps inner|outer current|all set|plus|minus <px>'"); +} diff --git a/sway/commands/input.c b/sway/commands/input.c index 84888fbb..2889d47d 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -9,6 +9,7 @@ static struct cmd_handler input_handlers[] = { { "accel_profile", input_cmd_accel_profile }, { "click_method", input_cmd_click_method }, + { "drag", input_cmd_drag }, { "drag_lock", input_cmd_drag_lock }, { "dwt", input_cmd_dwt }, { "events", input_cmd_events }, @@ -66,7 +67,15 @@ struct cmd_results *cmd_input(int argc, char **argv) { input_handlers, sizeof(input_handlers)); } - free_input_config(config->handler_context.input_config); + if (!res || res->status == CMD_SUCCESS) { + struct input_config *ic = + store_input_config(config->handler_context.input_config); + + input_manager_apply_input_config(input_manager, ic); + } else { + 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 a4108ec3..f7016790 100644 --- a/sway/commands/input/accel_profile.c +++ b/sway/commands/input/accel_profile.c @@ -9,25 +9,20 @@ struct cmd_results *input_cmd_accel_profile(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "accel_profile", "No input device defined."); } - 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; + ic->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; } else if (strcasecmp(argv[0], "flat") == 0) { - new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; + ic->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; } else { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "accel_profile", "Expected 'accel_profile <adaptive|flat>'"); } - 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 5d0d8cc2..4d7e1c93 100644 --- a/sway/commands/input/click_method.c +++ b/sway/commands/input/click_method.c @@ -10,27 +10,22 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "click_method", "No input device defined."); } - 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; + ic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE; } else if (strcasecmp(argv[0], "button_areas") == 0) { - new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; + ic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; } else if (strcasecmp(argv[0], "clickfinger") == 0) { - new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; + ic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; } else { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "click_method", "Expected 'click_method <none|button_areas|clickfinger'"); } - apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/input/drag.c b/sway/commands/input/drag.c new file mode 100644 index 00000000..e325df29 --- /dev/null +++ b/sway/commands/input/drag.c @@ -0,0 +1,26 @@ +#include <string.h> +#include <strings.h> +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" +#include "util.h" + +struct cmd_results *input_cmd_drag(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "drag", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct input_config *ic = config->handler_context.input_config; + if (!ic) { + return cmd_results_new(CMD_FAILURE, + "drag", "No input device defined."); + } + + if (parse_boolean(argv[0], true)) { + ic->drag = LIBINPUT_CONFIG_DRAG_ENABLED; + } else { + ic->drag = LIBINPUT_CONFIG_DRAG_DISABLED; + } + + 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 f9ddeef2..db5d5afa 100644 --- a/sway/commands/input/drag_lock.c +++ b/sway/commands/input/drag_lock.c @@ -10,21 +10,17 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "drag_lock", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); if (parse_boolean(argv[0], true)) { - new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; + ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; } else { - new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; + ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; } - 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 15134268..0c3881dd 100644 --- a/sway/commands/input/dwt.c +++ b/sway/commands/input/dwt.c @@ -10,20 +10,16 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); if (parse_boolean(argv[0], true)) { - new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED; + ic->dwt = LIBINPUT_CONFIG_DWT_ENABLED; } else { - new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; + ic->dwt = LIBINPUT_CONFIG_DWT_DISABLED; } - 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 abfe3b12..e7ed69c6 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c @@ -10,30 +10,23 @@ struct cmd_results *input_cmd_events(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "events", "No input device defined."); } - wlr_log(WLR_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; + ic->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; + ic->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) { - new_config->send_events = + ic->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; } else { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "events", "Expected 'events <enabled|disabled|disabled_on_external_mouse>'"); } - 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 e770043a..2e0f757b 100644 --- a/sway/commands/input/left_handed.c +++ b/sway/commands/input/left_handed.c @@ -10,17 +10,13 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "left_handed", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); - new_config->left_handed = parse_boolean(argv[0], true); + ic->left_handed = parse_boolean(argv[0], true); - apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c index 40f04214..53608a67 100644 --- a/sway/commands/input/map_from_region.c +++ b/sway/commands/input/map_from_region.c @@ -38,50 +38,44 @@ struct cmd_results *input_cmd_map_from_region(int argc, char **argv) { if ((error = checkarg(argc, "map_from_region", EXPECTED_EQUAL_TO, 2))) { return error; } - struct input_config *current_input_config = - config->handler_context.input_config; - if (!current_input_config) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "map_from_region", "No input device defined"); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); - - new_config->mapped_from_region = + ic->mapped_from_region = calloc(1, sizeof(struct input_config_mapped_from_region)); bool mm1, mm2; - if (!parse_coords(argv[0], &new_config->mapped_from_region->x1, - &new_config->mapped_from_region->y1, &mm1)) { - free(new_config->mapped_from_region); - free_input_config(new_config); + if (!parse_coords(argv[0], &ic->mapped_from_region->x1, + &ic->mapped_from_region->y1, &mm1)) { + free(ic->mapped_from_region); + ic->mapped_from_region = NULL; return cmd_results_new(CMD_FAILURE, "map_from_region", "Invalid top-left coordinates"); } - if (!parse_coords(argv[1], &new_config->mapped_from_region->x2, - &new_config->mapped_from_region->y2, &mm2)) { - free(new_config->mapped_from_region); - free_input_config(new_config); + if (!parse_coords(argv[1], &ic->mapped_from_region->x2, + &ic->mapped_from_region->y2, &mm2)) { + free(ic->mapped_from_region); + ic->mapped_from_region = NULL; return cmd_results_new(CMD_FAILURE, "map_from_region", "Invalid bottom-right coordinates"); } - if (new_config->mapped_from_region->x1 > new_config->mapped_from_region->x2 || - new_config->mapped_from_region->y1 > new_config->mapped_from_region->y2) { - free(new_config->mapped_from_region); - free_input_config(new_config); + if (ic->mapped_from_region->x1 > ic->mapped_from_region->x2 || + ic->mapped_from_region->y1 > ic->mapped_from_region->y2) { + free(ic->mapped_from_region); + ic->mapped_from_region = NULL; return cmd_results_new(CMD_FAILURE, "map_from_region", "Invalid rectangle"); } if (mm1 != mm2) { - free(new_config->mapped_from_region); - free_input_config(new_config); + free(ic->mapped_from_region); + ic->mapped_from_region = NULL; return cmd_results_new(CMD_FAILURE, "map_from_region", "Both coordinates must be in the same unit"); } - new_config->mapped_from_region->mm = mm1; - - apply_input_config(new_config); + ic->mapped_from_region->mm = mm1; 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 index 68439bec..8b16c557 100644 --- a/sway/commands/input/map_to_output.c +++ b/sway/commands/input/map_to_output.c @@ -11,17 +11,13 @@ struct cmd_results *input_cmd_map_to_output(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { 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_to_output = strdup(argv[0]); - apply_input_config(new_config); + ic->mapped_to_output = strdup(argv[0]); 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 414d4d2b..80d26838 100644 --- a/sway/commands/input/middle_emulation.c +++ b/sway/commands/input/middle_emulation.c @@ -10,22 +10,17 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "middle_emulation", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); if (parse_boolean(argv[0], true)) { - new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; + ic->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; } else { - new_config->middle_emulation = - LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; + ic->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; } - 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 77c3ff00..e2a93500 100644 --- a/sway/commands/input/natural_scroll.c +++ b/sway/commands/input/natural_scroll.c @@ -10,17 +10,13 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "natural_scoll", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); - new_config->natural_scroll = parse_boolean(argv[0], true); + ic->natural_scroll = parse_boolean(argv[0], true); - 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 8bbd0724..df487b1c 100644 --- a/sway/commands/input/pointer_accel.c +++ b/sway/commands/input/pointer_accel.c @@ -9,23 +9,18 @@ struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "pointer_accel", "No input device defined."); } - 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) { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "pointer_accel", "Input out of range [-1, 1]"); } - new_config->pointer_accel = pointer_accel; + ic->pointer_accel = pointer_accel; - apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/input/repeat_delay.c b/sway/commands/input/repeat_delay.c index c9ddbf0e..d94b3e4d 100644 --- a/sway/commands/input/repeat_delay.c +++ b/sway/commands/input/repeat_delay.c @@ -9,23 +9,18 @@ struct cmd_results *input_cmd_repeat_delay(int argc, char **argv) { if ((error = checkarg(argc, "repeat_delay", EXPECTED_EQUAL_TO, 1))) { return error; } - struct input_config *current_input_config = - config->handler_context.input_config; - if (!current_input_config) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "repeat_delay", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); int repeat_delay = atoi(argv[0]); if (repeat_delay < 0) { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "repeat_delay", "Repeat delay cannot be negative"); } - new_config->repeat_delay = repeat_delay; + ic->repeat_delay = repeat_delay; - apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/input/repeat_rate.c b/sway/commands/input/repeat_rate.c index 56878176..ebec4cdb 100644 --- a/sway/commands/input/repeat_rate.c +++ b/sway/commands/input/repeat_rate.c @@ -9,23 +9,18 @@ struct cmd_results *input_cmd_repeat_rate(int argc, char **argv) { if ((error = checkarg(argc, "repeat_rate", EXPECTED_EQUAL_TO, 1))) { return error; } - struct input_config *current_input_config = - config->handler_context.input_config; - if (!current_input_config) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "repeat_rate", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); int repeat_rate = atoi(argv[0]); if (repeat_rate < 0) { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "repeat_rate", "Repeat rate cannot be negative"); } - new_config->repeat_rate = repeat_rate; + ic->repeat_rate = repeat_rate; - apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c index 350fcca2..1958f23c 100644 --- a/sway/commands/input/scroll_button.c +++ b/sway/commands/input/scroll_button.c @@ -10,35 +10,28 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { if ((error = checkarg(argc, "scroll_button", EXPECTED_AT_LEAST, 1))) { return error; } - struct input_config *current_input_config = - config->handler_context.input_config; - if (!current_input_config) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "scroll_button", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); errno = 0; char *endptr; int scroll_button = strtol(*argv, &endptr, 10); if (endptr == *argv && scroll_button == 0) { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "scroll_button", "Scroll button identifier must be an integer."); } if (errno == ERANGE) { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "scroll_button", "Scroll button identifier out of range."); } if (scroll_button < 0) { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "scroll_button", "Scroll button identifier cannot be negative."); } - new_config->scroll_button = scroll_button; + ic->scroll_button = scroll_button; - 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 4c6ac6b6..c116b052 100644 --- a/sway/commands/input/scroll_method.c +++ b/sway/commands/input/scroll_method.c @@ -9,29 +9,24 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "scroll_method", "No input device defined."); } - 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; + ic->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; } else if (strcasecmp(argv[0], "two_finger") == 0) { - new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; + ic->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; } else if (strcasecmp(argv[0], "edge") == 0) { - new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE; + ic->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE; } else if (strcasecmp(argv[0], "on_button_down") == 0) { - new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; + ic->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; } else { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "scroll_method", "Expected 'scroll_method <none|two_finger|edge|on_button_down>'"); } - 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 ac3b8237..c455b696 100644 --- a/sway/commands/input/tap.c +++ b/sway/commands/input/tap.c @@ -11,22 +11,16 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) { 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) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "tap", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); if (parse_boolean(argv[0], true)) { - new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED; + ic->tap = LIBINPUT_CONFIG_TAP_ENABLED; } else { - new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; + ic->tap = LIBINPUT_CONFIG_TAP_DISABLED; } - wlr_log(WLR_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/tap_button_map.c b/sway/commands/input/tap_button_map.c index bdbba472..dff2985b 100644 --- a/sway/commands/input/tap_button_map.c +++ b/sway/commands/input/tap_button_map.c @@ -9,25 +9,20 @@ struct cmd_results *input_cmd_tap_button_map(int argc, char **argv) { if ((error = checkarg(argc, "tap_button_map", EXPECTED_AT_LEAST, 1))) { return error; } - struct input_config *current_input_config = - config->handler_context.input_config; - if (!current_input_config) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "tap_button_map", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); if (strcasecmp(argv[0], "lrm") == 0) { - new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; + ic->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; } else if (strcasecmp(argv[0], "lmr") == 0) { - new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR; + ic->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR; } else { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "tap_button_map", "Expected 'tap_button_map <lrm|lmr>'"); } - apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/input/xkb_capslock.c b/sway/commands/input/xkb_capslock.c index 5442c463..669b4ea9 100644 --- a/sway/commands/input/xkb_capslock.c +++ b/sway/commands/input/xkb_capslock.c @@ -9,25 +9,20 @@ struct cmd_results *input_cmd_xkb_capslock(int argc, char **argv) { if ((error = checkarg(argc, "xkb_capslock", EXPECTED_AT_LEAST, 1))) { return error; } - struct input_config *current_input_config = - config->handler_context.input_config; - if (!current_input_config) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "xkb_capslock", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); if (strcasecmp(argv[0], "enabled") == 0) { - new_config->xkb_capslock = 1; + ic->xkb_capslock = 1; } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->xkb_capslock = 0; + ic->xkb_capslock = 0; } else { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "xkb_capslock", "Expected 'xkb_capslock <enabled|disabled>'"); } - 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 index 9fa5a344..5fccd4a3 100644 --- a/sway/commands/input/xkb_layout.c +++ b/sway/commands/input/xkb_layout.c @@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) { 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 *ic = config->handler_context.input_config; + if (!ic) { + 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]); + ic->xkb_layout = strdup(argv[0]); - wlr_log(WLR_DEBUG, "apply-xkb_layout for device: %s layout: %s", - current_input_config->identifier, new_config->xkb_layout); - apply_input_config(new_config); + wlr_log(WLR_DEBUG, "set-xkb_layout for config: %s layout: %s", + ic->identifier, ic->xkb_layout); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c index 0d082625..c4d04638 100644 --- a/sway/commands/input/xkb_model.c +++ b/sway/commands/input/xkb_model.c @@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_model(int argc, char **argv) { 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 *ic = config->handler_context.input_config; + if (!ic) { + 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]); + ic->xkb_model = strdup(argv[0]); - wlr_log(WLR_DEBUG, "apply-xkb_model for device: %s model: %s", - current_input_config->identifier, new_config->xkb_model); - apply_input_config(new_config); + wlr_log(WLR_DEBUG, "set-xkb_model for config: %s model: %s", + ic->identifier, ic->xkb_model); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c index 39675366..1367da44 100644 --- a/sway/commands/input/xkb_numlock.c +++ b/sway/commands/input/xkb_numlock.c @@ -9,25 +9,20 @@ struct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) { if ((error = checkarg(argc, "xkb_numlock", EXPECTED_AT_LEAST, 1))) { return error; } - struct input_config *current_input_config = - config->handler_context.input_config; - if (!current_input_config) { + struct input_config *ic = config->handler_context.input_config; + if (!ic) { return cmd_results_new(CMD_FAILURE, "xkb_numlock", "No input device defined."); } - struct input_config *new_config = - new_input_config(current_input_config->identifier); if (strcasecmp(argv[0], "enabled") == 0) { - new_config->xkb_numlock = 1; + ic->xkb_numlock = 1; } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->xkb_numlock = 0; + ic->xkb_numlock = 0; } else { - free_input_config(new_config); return cmd_results_new(CMD_INVALID, "xkb_numlock", "Expected 'xkb_numlock <enabled|disabled>'"); } - 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 index 3059d941..794ab6e9 100644 --- a/sway/commands/input/xkb_options.c +++ b/sway/commands/input/xkb_options.c @@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_options(int argc, char **argv) { 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 *ic = config->handler_context.input_config; + if (!ic) { + 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]); + ic->xkb_options = strdup(argv[0]); - wlr_log(WLR_DEBUG, "apply-xkb_options for device: %s options: %s", - current_input_config->identifier, new_config->xkb_options); - apply_input_config(new_config); + wlr_log(WLR_DEBUG, "set-xkb_options for config: %s options: %s", + ic->identifier, ic->xkb_options); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c index 560f088e..257c3288 100644 --- a/sway/commands/input/xkb_rules.c +++ b/sway/commands/input/xkb_rules.c @@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) { 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 *ic = config->handler_context.input_config; + if (!ic) { + 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]); + ic->xkb_rules = strdup(argv[0]); - wlr_log(WLR_DEBUG, "apply-xkb_rules for device: %s rules: %s", - current_input_config->identifier, new_config->xkb_rules); - apply_input_config(new_config); + wlr_log(WLR_DEBUG, "set-xkb_rules for config: %s rules: %s", + ic->identifier, ic->xkb_rules); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c index 0aa03440..3832dc8e 100644 --- a/sway/commands/input/xkb_variant.c +++ b/sway/commands/input/xkb_variant.c @@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) { 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 *ic = config->handler_context.input_config; + if (!ic) { + 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]); + ic->xkb_variant = strdup(argv[0]); - wlr_log(WLR_DEBUG, "apply-xkb_variant for device: %s variant: %s", - current_input_config->identifier, new_config->xkb_variant); - apply_input_config(new_config); + wlr_log(WLR_DEBUG, "set-xkb_variant for config: %s variant: %s", + ic->identifier, ic->xkb_variant); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/layout.c b/sway/commands/layout.c index ef3ec1cb..c2ce2e78 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -138,15 +138,14 @@ struct cmd_results *cmd_layout(int argc, char **argv) { } container->layout = new_layout; container_update_representation(container); - arrange_container(container); } else { if (old_layout != L_TABBED && old_layout != L_STACKED) { workspace->prev_split_layout = old_layout; } workspace->layout = new_layout; workspace_update_representation(workspace); - arrange_workspace(workspace); } + arrange_workspace(workspace); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/move.c b/sway/commands/move.c index 849a18ad..fc2f1cc1 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -259,50 +259,24 @@ static void container_move_to_container(struct sway_container *container, * In other words, rejigger it. */ static void workspace_rejigger(struct sway_workspace *ws, struct sway_container *child, enum movement_direction move_dir) { + if (!child->parent && ws->tiling->length == 1) { + ws->layout = + move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; + workspace_update_representation(ws); + return; + } container_detach(child); - workspace_wrap_children(ws); + struct sway_container *new_parent = workspace_wrap_children(ws); int index = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? 0 : 1; workspace_insert_tiling(ws, child, index); + container_flatten(new_parent); ws->layout = move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; workspace_update_representation(ws); child->width = child->height = 0; } -static void move_out_of_tabs_stacks(struct sway_container *container, - struct sway_container *current, enum movement_direction move_dir, - int offs) { - enum sway_container_layout layout = move_dir == - MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; - list_t *siblings = container_get_siblings(container); - if (container == current && siblings->length == 1) { - wlr_log(WLR_DEBUG, "Changing layout of parent"); - if (container->parent) { - container->parent->layout = layout; - container_update_representation(container); - } else { - container->workspace->layout = layout; - workspace_update_representation(container->workspace); - } - return; - } - - wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); - if (current->parent) { - struct sway_container *new_parent = - container_split(current->parent, layout); - container_insert_child(new_parent, container, offs < 0 ? 0 : 1); - container_reap_empty(new_parent); - container_flatten(new_parent); - } else { - // Changing a workspace - struct sway_workspace *workspace = container->workspace; - workspace_split(workspace, layout); - workspace_insert_tiling(workspace, container, offs < 0 ? 0 : 1); - } -} - // Returns true if moved static bool container_move_in_direction(struct sway_container *container, enum movement_direction move_dir) { @@ -334,7 +308,6 @@ static bool container_move_in_direction(struct sway_container *container, int offs = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1; while (current) { - struct sway_container *parent = current->parent; list_t *siblings = container_get_siblings(current); enum sway_container_layout layout = container_parent_layout(current); int index = list_find(siblings, current); @@ -343,15 +316,8 @@ static bool container_move_in_direction(struct sway_container *container, if (is_parallel(layout, move_dir)) { if (desired == -1 || desired == siblings->length) { if (current->parent == container->parent) { - if (!(parent && parent->is_fullscreen) && - (layout == L_TABBED || layout == L_STACKED)) { - move_out_of_tabs_stacks(container, current, - move_dir, offs); - return true; - } else { - current = current->parent; - continue; - } + current = current->parent; + continue; } else { // Special case if (current->parent) { @@ -369,10 +335,6 @@ static bool container_move_in_direction(struct sway_container *container, siblings->items[desired], move_dir); return true; } - } else if (!(parent && parent->is_fullscreen) && - (layout == L_TABBED || layout == L_STACKED)) { - move_out_of_tabs_stacks(container, current, move_dir, offs); - return true; } current = current->parent; @@ -388,10 +350,8 @@ static bool container_move_in_direction(struct sway_container *container, // Maybe rejigger the workspace struct sway_workspace *ws = container->workspace; if (!is_parallel(ws->layout, move_dir)) { - if (ws->tiling->length >= 2) { - workspace_rejigger(ws, container, move_dir); - return true; - } + workspace_rejigger(ws, container, move_dir); + return true; } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) { workspace_rejigger(ws, container, move_dir); return true; 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..63f29641 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -10,6 +10,28 @@ #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); + wsc->gaps_inner = -1; + wsc->gaps_outer = -1; + 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))) { @@ -17,6 +39,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } int output_location = -1; + int gaps_location = -1; for (int i = 0; i < argc; ++i) { if (strcasecmp(argv[i], "output") == 0) { @@ -24,25 +47,54 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { break; } } + for (int i = 0; i < argc; ++i) { + if (strcasecmp(argv[i], "gaps") == 0) { + gaps_location = i; + break; + } + } if (output_location >= 0) { 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); + free(wsc->output); + wsc->output = strdup(argv[output_location + 1]); + } else if (gaps_location >= 0) { + if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, gaps_location + 3))) { + return error; + } + char *ws_name = join_args(argv, argc - 3); + struct workspace_config *wsc = workspace_config_find_or_create(ws_name); + free(ws_name); + if (!wsc) { + return cmd_results_new(CMD_FAILURE, "workspace gaps", + "Unable to allocate workspace output"); + } + int *prop = NULL; + if (strcasecmp(argv[gaps_location + 1], "inner") == 0) { + prop = &wsc->gaps_inner; + } else if (strcasecmp(argv[gaps_location + 1], "outer") == 0) { + prop = &wsc->gaps_outer; + } else { + return cmd_results_new(CMD_FAILURE, "workspace gaps", + "Expected 'workspace <ws> gaps inner|outer <px>'"); + } + char *end; + int val = strtol(argv[gaps_location + 2], &end, 10); + + if (strlen(end)) { + free(end); + return cmd_results_new(CMD_FAILURE, "workspace gaps", + "Expected 'workspace <ws> gaps inner|outer <px>'"); } - wlr_log(WLR_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); - list_add(config->workspace_outputs, wso); + *prop = val >= 0 ? val : 0; } 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/bar.c b/sway/config/bar.c index f83b37d1..48a632fb 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -165,7 +165,7 @@ cleanup: return NULL; } -void invoke_swaybar(struct bar_config *bar) { +static void invoke_swaybar(struct bar_config *bar) { // Pipe to communicate errors int filedes[2]; if (pipe(filedes) == -1) { @@ -219,27 +219,13 @@ void invoke_swaybar(struct bar_config *bar) { close(filedes[1]); } -void load_swaybars() { +void load_swaybars(void) { 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, "*") || output_by_name(o)) { - apply = true; - break; - } - } - } else { - apply = true; - } - if (apply) { - if (bar->pid != 0) { - terminate_swaybar(bar->pid); - } - wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); - invoke_swaybar(bar); + if (bar->pid != 0) { + terminate_swaybar(bar->pid); } + wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); + invoke_swaybar(bar); } } diff --git a/sway/config/input.c b/sway/config/input.c index ad5b96c8..794d5194 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -20,6 +20,7 @@ struct input_config *new_input_config(const char* identifier) { input->tap = INT_MIN; input->tap_button_map = INT_MIN; + input->drag = INT_MIN; input->drag_lock = INT_MIN; input->dwt = INT_MIN; input->send_events = INT_MIN; @@ -40,22 +41,24 @@ struct input_config *new_input_config(const char* identifier) { } 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 != INT_MIN) { + dst->drag = src->drag; + } if (src->drag_lock != INT_MIN) { dst->drag_lock = src->drag_lock; } 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; } @@ -125,14 +128,51 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { } } -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(WLR_ERROR, "could not allocate input config"); - return NULL; +static void merge_wildcard_on_all(struct input_config *wildcard) { + for (int i = 0; i < config->input_configs->length; i++) { + struct input_config *ic = config->input_configs->items[i]; + if (strcmp(wildcard->identifier, ic->identifier) != 0) { + wlr_log(WLR_DEBUG, "Merging input * config on %s", ic->identifier); + merge_input_config(ic, wildcard); + } } - merge_input_config(copy, ic); - return copy; +} + +struct input_config *store_input_config(struct input_config *ic) { + bool wildcard = strcmp(ic->identifier, "*") == 0; + if (wildcard) { + merge_wildcard_on_all(ic); + } + + int i = list_seq_find(config->input_configs, input_identifier_cmp, + ic->identifier); + if (i >= 0) { + wlr_log(WLR_DEBUG, "Merging on top of existing input config"); + struct input_config *current = config->input_configs->items[i]; + merge_input_config(current, ic); + free_input_config(ic); + ic = current; + } else if (!wildcard) { + wlr_log(WLR_DEBUG, "Adding non-wildcard input config"); + i = list_seq_find(config->input_configs, input_identifier_cmp, "*"); + if (i >= 0) { + wlr_log(WLR_DEBUG, "Merging on top of input * config"); + struct input_config *current = new_input_config(ic->identifier); + merge_input_config(current, config->input_configs->items[i]); + merge_input_config(current, ic); + free_input_config(ic); + ic = current; + } + list_add(config->input_configs, ic); + } else { + // New wildcard config. Just add it + wlr_log(WLR_DEBUG, "Adding input * config"); + list_add(config->input_configs, ic); + } + + wlr_log(WLR_DEBUG, "Config stored for input %s", ic->identifier); + + return ic; } void free_input_config(struct input_config *ic) { 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/config/seat.c b/sway/config/seat.c index 83dac4c0..46456caf 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c @@ -30,7 +30,7 @@ struct seat_config *new_seat_config(const char* name) { return seat; } -struct seat_attachment_config *seat_attachment_config_new() { +struct seat_attachment_config *seat_attachment_config_new(void) { struct seat_attachment_config *attachment = calloc(1, sizeof(struct seat_attachment_config)); if (!attachment) { diff --git a/sway/debug-tree.c b/sway/debug-tree.c index 9644f4e5..16b479f9 100644 --- a/sway/debug-tree.c +++ b/sway/debug-tree.c @@ -120,7 +120,7 @@ static int draw_node(cairo_t *cairo, struct sway_node *node, return height; } -void update_debug_tree() { +void update_debug_tree(void) { if (!debug.render_tree) { return; } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index d747e279..34d99d52 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -6,6 +6,7 @@ #include <string.h> #include <time.h> #include <wlr/types/wlr_buffer.h> +#include "sway/config.h" #include "sway/debug.h" #include "sway/desktop.h" #include "sway/desktop/idle_inhibit_v1.h" @@ -30,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")) { @@ -85,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); @@ -101,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; @@ -137,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; @@ -300,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; } @@ -334,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; } @@ -389,7 +371,17 @@ 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. + if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { + if (cstate->view_x != istate->view_x || + cstate->view_y != istate->view_y) { + return true; + } + } +#endif if (cstate->view_width == istate->view_width && cstate->view_height == istate->view_height) { return false; @@ -407,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 @@ -502,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/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 00448be7..6d1ccdd7 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -401,7 +401,7 @@ static void handle_map(struct wl_listener *listener, void *data) { } else { if (view->container->parent) { arrange_container(view->container->parent); - } else { + } else if (view->container->workspace) { arrange_workspace(view->container->workspace); } } diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index d2c9a68b..95ca396c 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -398,7 +398,7 @@ static void handle_map(struct wl_listener *listener, void *data) { } else { if (view->container->parent) { arrange_container(view->container->parent); - } else { + } else if (view->container->workspace) { arrange_workspace(view->container->workspace); } } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 3619f202..a12ac854 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -394,7 +394,7 @@ static void handle_map(struct wl_listener *listener, void *data) { } else { if (view->container->parent) { arrange_container(view->container->parent); - } else { + } else if (view->container->workspace) { arrange_workspace(view->container->workspace); } } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 85951c09..3ddc27a0 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -30,7 +30,7 @@ // when dragging to the edge of a layout container. #define DROP_LAYOUT_BORDER 30 -static uint32_t get_current_time_msec() { +static uint32_t get_current_time_msec(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); return now.tv_nsec / 1000; @@ -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; @@ -911,9 +911,10 @@ void dispatch_cursor_button(struct sway_cursor *cursor, return; } - // Handle clicking a container surface + // Handle clicking a container surface or decorations if (cont) { - seat_set_focus_container(seat, cont); + node = seat_get_focus_inactive(seat, &cont->node); + seat_set_focus(seat, node); seat_pointer_notify_button(seat, time_msec, button, state); return; } @@ -930,12 +931,52 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) { transaction_commit_dirty(); } +static void dispatch_cursor_axis(struct sway_cursor *cursor, + struct wlr_event_pointer_axis *event) { + struct sway_seat *seat = cursor->seat; + + // Determine what's under the cursor + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct sway_container *cont = node && node->type == N_CONTAINER ? + node->sway_container : NULL; + enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; + bool on_border = edge != WLR_EDGE_NONE; + bool on_titlebar = cont && !on_border && !surface; + + // Scrolling on a tabbed or stacked title bar + if (on_titlebar) { + enum sway_container_layout layout = container_parent_layout(cont); + if (layout == L_TABBED || layout == L_STACKED) { + struct sway_node *active = + seat_get_active_tiling_child(seat, node_get_parent(node)); + list_t *siblings = container_get_siblings(cont); + int desired = list_find(siblings, active->sway_container) + + event->delta_discrete; + if (desired < 0) { + desired = 0; + } else if (desired >= siblings->length) { + desired = siblings->length - 1; + } + struct sway_container *new_focus = siblings->items[desired]; + node = seat_get_focus_inactive(seat, &new_focus->node); + seat_set_focus(seat, node); + return; + } + } + + wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, + event->orientation, event->delta, event->delta_discrete, event->source); +} + static void handle_cursor_axis(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_pointer_axis *event = data; - wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, - event->orientation, event->delta, event->delta_discrete, event->source); + dispatch_cursor_axis(cursor, event); + transaction_commit_dirty(); } static void handle_touch_down(struct wl_listener *listener, void *data) { @@ -965,8 +1006,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { if (seat_is_input_allowed(seat, surface)) { wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, event->touch_id, sx, sy); - cursor->image_client = NULL; - wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); + cursor_set_image(cursor, NULL, NULL); } } @@ -1134,11 +1174,13 @@ static void handle_request_set_cursor(struct wl_listener *listener, void cursor_set_image(struct sway_cursor *cursor, const char *image, struct wl_client *client) { - if (!cursor->image || strcmp(cursor->image, image) != 0) { + if (!image) { + wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); + } else if (!cursor->image || strcmp(cursor->image, image) != 0) { wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, cursor->cursor); - cursor->image = image; } + cursor->image = image; cursor->image_client = client; } diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index b4352c6a..32f0355e 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -120,6 +120,13 @@ static void input_manager_libinput_config_pointer( libinput_device_config_click_set_method(libinput_device, ic->click_method); } + if (ic->drag != INT_MIN) { + wlr_log(WLR_DEBUG, + "libinput_config_pointer(%s) tap_set_drag_enabled(%d)", + ic->identifier, ic->click_method); + libinput_device_config_tap_set_drag_enabled(libinput_device, + ic->drag); + } if (ic->drag_lock != INT_MIN) { wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", @@ -233,7 +240,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); } @@ -389,9 +397,12 @@ void input_manager_set_focus(struct sway_input_manager *input, void input_manager_apply_input_config(struct sway_input_manager *input, struct input_config *input_config) { struct sway_input_device *input_device = NULL; + bool wildcard = strcmp(input_config->identifier, "*") == 0; 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) { + if (strcmp(input_device->identifier, input_config->identifier) == 0 + || wildcard) { + 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); } @@ -480,13 +491,16 @@ struct sway_seat *input_manager_get_default_seat( } struct input_config *input_device_get_config(struct sway_input_device *device) { + struct input_config *wildcard_config = NULL; 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; + } else if (strcmp(input_config->identifier, "*") == 0) { + wildcard_config = input_config; } } - return NULL; + return wildcard_config; } diff --git a/sway/input/seat.c b/sway/input/seat.c index 49fe46ba..4817eae7 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -348,17 +348,42 @@ struct sway_seat *seat_create(struct sway_input_manager *input, 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); - - wl_list_insert(&input->seats, &seat->link); return seat; } +static void seat_update_capabilities(struct sway_seat *seat) { + uint32_t caps = 0; + struct sway_seat_device *seat_device; + wl_list_for_each(seat_device, &seat->devices, link) { + switch (seat_device->input_device->wlr_device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + break; + case WLR_INPUT_DEVICE_POINTER: + caps |= WL_SEAT_CAPABILITY_POINTER; + break; + case WLR_INPUT_DEVICE_TOUCH: + caps |= WL_SEAT_CAPABILITY_TOUCH; + break; + case WLR_INPUT_DEVICE_TABLET_TOOL: + caps |= WL_SEAT_CAPABILITY_POINTER; + break; + case WLR_INPUT_DEVICE_TABLET_PAD: + break; + } + } + wlr_seat_set_capabilities(seat->wlr_seat, caps); + + // Hide cursor if seat doesn't have pointer capability + if ((caps & WL_SEAT_CAPABILITY_POINTER) == 0) { + cursor_set_image(seat->cursor, NULL, NULL); + } else { + cursor_set_image(seat->cursor, "left_ptr", NULL); + } +} + static void seat_apply_input_config(struct sway_seat *seat, struct sway_seat_device *sway_device) { const char *mapped_to_output = NULL; @@ -489,6 +514,8 @@ void seat_add_device(struct sway_seat *seat, wl_list_insert(&seat->devices, &seat_device->link); seat_configure_device(seat, input_device); + + seat_update_capabilities(seat); } void seat_remove_device(struct sway_seat *seat, @@ -503,6 +530,8 @@ void seat_remove_device(struct sway_seat *seat, input_device->identifier, seat->wlr_seat->name); seat_device_destroy(seat_device); + + seat_update_capabilities(seat); } void seat_configure_xcursor(struct sway_seat *seat) { @@ -532,8 +561,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { output->name, (double)output->scale); } - wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager, - "left_ptr", seat->cursor->cursor); + cursor_set_image(seat->cursor, "left_ptr", NULL); wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, seat->cursor->cursor->y); } diff --git a/sway/ipc-json.c b/sway/ipc-json.c index f054ac9f..45915094 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -42,7 +42,7 @@ static const char *ipc_json_orientation_description(enum sway_container_layout l return "none"; } -json_object *ipc_json_get_version() { +json_object *ipc_json_get_version(void) { int major = 0, minor = 0, patch = 0; json_object *version = json_object_new_object(); diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 8ae265f6..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(); @@ -580,7 +583,7 @@ void ipc_client_handle_command(struct ipc_client *client) { switch (client->current_command) { case IPC_COMMAND: { - struct cmd_results *results = execute_command(buf, NULL); + struct cmd_results *results = execute_command(buf, NULL, NULL); transaction_commit_dirty(); char *json = cmd_results_to_json(results); int length = strlen(json); diff --git a/sway/main.c b/sway/main.c index fb4f0d8c..dea4a31c 100644 --- a/sway/main.c +++ b/sway/main.c @@ -12,10 +12,6 @@ #include <sys/wait.h> #include <sys/un.h> #include <unistd.h> -#ifdef __linux__ -#include <sys/capability.h> -#include <sys/prctl.h> -#endif #include <wlr/util/log.h> #include "sway/commands.h" #include "sway/config.h" @@ -45,7 +41,7 @@ void sig_handler(int signal) { sway_terminate(EXIT_SUCCESS); } -void detect_raspi() { +void detect_raspi(void) { bool raspi = false; FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); if (!f) { @@ -85,7 +81,7 @@ void detect_raspi() { } } -void detect_proprietary() { +void detect_proprietary(void) { FILE *f = fopen("/proc/modules", "r"); if (!f) { return; @@ -120,7 +116,7 @@ void run_as_ipc_client(char *command, char *socket_path) { close(socketfd); } -static void log_env() { +static void log_env(void) { const char *log_vars[] = { "PATH", "LD_LIBRARY_PATH", @@ -135,7 +131,7 @@ static void log_env() { } } -static void log_distro() { +static void log_distro(void) { const char *paths[] = { "/etc/lsb-release", "/etc/os-release", @@ -162,7 +158,7 @@ static void log_distro() { } } -static void log_kernel() { +static void log_kernel(void) { FILE *f = popen("uname -a", "r"); if (!f) { wlr_log(WLR_INFO, "Unable to determine kernel version"); @@ -181,28 +177,8 @@ static void log_kernel() { pclose(f); } -static void executable_sanity_check() { -#ifdef __linux__ - struct stat sb; - char *exe = realpath("/proc/self/exe", NULL); - 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)) { - wlr_log(WLR_ERROR, - "sway executable has both the s(g)uid bit AND file caps set."); - wlr_log(WLR_ERROR, - "This is strongly discouraged (and completely broken)."); - wlr_log(WLR_ERROR, - "Please clear one of them (either the suid bit, or the file caps)."); - wlr_log(WLR_ERROR, - "If unsure, strip the file caps."); - exit(EXIT_FAILURE); - } - free(exe); -#endif -} -static void drop_permissions(bool keep_caps) { +static void drop_permissions(void) { if (getuid() != geteuid() || getgid() != getegid()) { if (setgid(getgid()) != 0) { wlr_log(WLR_ERROR, "Unable to drop root"); @@ -217,20 +193,6 @@ static void drop_permissions(bool keep_caps) { wlr_log(WLR_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(WLR_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(WLR_ERROR, "Failed to drop extra capabilities"); - exit(EXIT_FAILURE); - } - } -#endif } void enable_debug_flag(const char *flag) { @@ -279,14 +241,6 @@ int main(int argc, char **argv) { " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" "\n"; - // Security: - unsetenv("LD_PRELOAD"); -#ifdef _LD_LIBRARY_PATH - setenv("LD_LIBRARY_PATH", _LD_LIBRARY_PATH, 1); -#else - unsetenv("LD_LIBRARY_PATH"); -#endif - int c; while (1) { int option_index = 0; @@ -347,7 +301,7 @@ int main(int argc, char **argv) { wlr_log(WLR_ERROR, "Don't use options with the IPC client"); exit(EXIT_FAILURE); } - drop_permissions(false); + drop_permissions(); char *socket_path = getenv("SWAYSOCK"); if (!socket_path) { wlr_log(WLR_ERROR, "Unable to retrieve socket path"); @@ -358,34 +312,17 @@ int main(int argc, char **argv) { return 0; } - executable_sanity_check(); - bool suid = false; - if (!server_privileged_prepare(&server)) { return 1; } -#if defined(__linux__) || defined(__FreeBSD__) - if (getuid() != geteuid() || getgid() != getegid()) { -#ifdef __linux__ - // Retain capabilities after setuid() - if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { - wlr_log(WLR_ERROR, "Cannot keep caps after setuid()"); - exit(EXIT_FAILURE); - } -#endif - suid = true; - } -#endif - log_kernel(); log_distro(); detect_proprietary(); detect_raspi(); -#if defined(__linux__) || defined(__FreeBSD__) - drop_permissions(suid); -#endif + drop_permissions(); + // handle SIGTERM signals signal(SIGTERM, sig_handler); @@ -424,11 +361,12 @@ int main(int argc, char **argv) { } config->active = true; + load_swaybars(); // Execute commands until there are none left wlr_log(WLR_DEBUG, "Running deferred commands"); while (config->cmd_queue->length) { char *line = config->cmd_queue->items[0]; - struct cmd_results *res = execute_command(line, NULL); + struct cmd_results *res = execute_command(line, NULL, NULL); if (res->status != CMD_SUCCESS) { wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error); } diff --git a/sway/meson.build b/sway/meson.build index d67a4c64..6eb9a9d7 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -119,6 +119,7 @@ sway_sources = files( 'commands/input/accel_profile.c', 'commands/input/click_method.c', + 'commands/input/drag.c', 'commands/input/drag_lock.c', 'commands/input/dwt.c', 'commands/input/events.c', @@ -164,7 +165,6 @@ sway_deps = [ cairo, gdk_pixbuf, jsonc, - libcap, libinput, math, pango, @@ -187,5 +187,6 @@ executable( include_directories: [sway_inc], dependencies: sway_deps, link_with: [lib_sway_common], + install_rpath : rpathdir, install: true ) 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..14f2a007 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 @@ -68,6 +67,9 @@ The following commands may only be used in the configuration file. *input* <identifier> click\_method none|button\_areas|clickfinger Changes the click method for the specified device. +*input* <identifier> drag enabled|disabled + Enables or disables tap-and-drag for specified input device. + *input* <identifier> drag\_lock enabled|disabled Enables or disables drag lock for specified input device. @@ -116,8 +118,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..aa5b38ab 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. @@ -407,37 +419,30 @@ The default colors are: inner gap is nonzero. When _off_, gaps will only be added between views. _toggle_ cannot be used in the configuration file. -*gaps* <amount> - Sets _amount_ pixels of gap between windows and around each workspace. - *gaps* inner|outer <amount> - Sets default _amount_ pixels of _inner_ or _outer_ gap, where the former - affects spacing between views and the latter affects the space around each - workspace. + Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner + affects spacing around each view and outer affects the spacing around each + workspace. Outer gaps are in addition to inner gaps. + + This affects new workspaces only, and is used when the workspace doesn't + have its own gaps settings (see: workspace <ws> gaps inner|outer <amount>). -*gaps* inner|outer all|workspace|current set|plus|minus <amount> - Changes the gaps for the _inner_ or _outer_ gap. _all_ changes the gaps for - all views or workspace, _workspace_ changes gaps for all views in current - workspace (or current workspace), and _current_ changes gaps for the current - view or workspace. +*gaps* inner|outer all|current set|plus|minus <amount> + Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the + _current_ workspace. *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 +470,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 +536,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. @@ -568,6 +578,10 @@ config after the others, or it will be matched instead of the others. *workspace* back_and_forth Switches to the previously focused workspace. +*workspace* <name> gaps inner|outer <amount> + Specifies that workspace _name_ should have the given gaps settings when it + is created. + *workspace* <name> output <output> Specifies that workspace _name_ should be shown on the specified _output_. diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index d50be25d..373460a2 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -186,6 +186,7 @@ void arrange_workspace(struct sway_workspace *workspace) { area->width, area->height, area->x, area->y); workspace_remove_gaps(workspace); + bool first_arrange = workspace->width == 0 && workspace->height == 0; double prev_x = workspace->x; double prev_y = workspace->y; workspace->width = area->width; @@ -196,7 +197,7 @@ void arrange_workspace(struct sway_workspace *workspace) { // Adjust any floating containers double diff_x = workspace->x - prev_x; double diff_y = workspace->y - prev_y; - if (diff_x != 0 || diff_y != 0) { + if (!first_arrange && (diff_x != 0 || diff_y != 0)) { for (int i = 0; i < workspace->floating->length; ++i) { struct sway_container *floater = workspace->floating->items[i]; container_floating_translate(floater, diff_x, diff_y); diff --git a/sway/tree/container.c b/sway/tree/container.c index 47687744..788300cc 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); } } @@ -215,8 +213,7 @@ static struct sway_container *container_at_tabbed(struct sway_node *parent, child_index = children->length - 1; } struct sway_container *child = children->items[child_index]; - struct sway_node *node = seat_get_focus_inactive(seat, &child->node); - return node->sway_container; + return child; } // Surfaces @@ -243,8 +240,7 @@ static struct sway_container *container_at_stacked(struct sway_node *parent, int child_index = (ly - box.y) / title_height; if (child_index < children->length) { struct sway_container *child = children->items[child_index]; - struct sway_node *node = seat_get_focus_inactive(seat, &child->node); - return node->sway_container; + return child; } // Surfaces @@ -465,11 +461,17 @@ static void update_title_texture(struct sway_container *con, cairo_surface_t *surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, width, height); cairo_t *cairo = cairo_create(surface); + cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); + cairo_font_options_t *fo = cairo_font_options_create(); + cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); + cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); + cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->wlr_output->subpixel)); + cairo_set_font_options(cairo, fo); + cairo_font_options_destroy(fo); cairo_set_source_rgba(cairo, class->background[0], class->background[1], class->background[2], class->background[3]); cairo_paint(cairo); PangoContext *pango = pango_cairo_create_context(cairo); - cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); cairo_set_source_rgba(cairo, class->text[0], class->text[1], class->text[2], class->text[3]); cairo_move_to(cairo, 0, 0); @@ -591,7 +593,7 @@ void container_update_representation(struct sway_container *con) { } } -size_t container_titlebar_height() { +size_t container_titlebar_height(void) { return config->font_height + TITLEBAR_V_PADDING * 2; } @@ -821,9 +823,16 @@ void container_floating_move_to_center(struct sway_container *con) { return; } struct sway_workspace *ws = con->workspace; + bool full = con->is_fullscreen; + if (full) { + container_set_fullscreen(con, false); + } double new_lx = ws->x + (ws->width - con->width) / 2; double new_ly = ws->y + (ws->height - con->height) / 2; container_floating_translate(con, new_lx - con->x, new_ly - con->y); + if (full) { + container_set_fullscreen(con, true); + } } static bool find_urgent_iterator(struct sway_container *con, void *data) { @@ -981,7 +990,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); @@ -1019,7 +1029,7 @@ void container_add_gaps(struct sway_container *c) { struct sway_workspace *ws = c->workspace; - c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner; + c->current_gaps = ws->gaps_inner; c->x += c->current_gaps; c->y += c->current_gaps; c->width -= 2 * c->current_gaps; diff --git a/sway/tree/output.c b/sway/tree/output.c index 1976ad51..c3176325 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -109,12 +109,24 @@ void output_enable(struct sway_output *output, struct output_config *oc) { wl_signal_emit(&root->events.new_node, &output->node); - load_swaybars(); - arrange_layers(output); arrange_root(); } +static void evacuate_sticky(struct sway_workspace *old_ws, + struct sway_output *new_output) { + struct sway_workspace *new_ws = output_get_active_workspace(new_output); + while (old_ws->floating->length) { + struct sway_container *sticky = old_ws->floating->items[0]; + container_detach(sticky); + workspace_add_floating(new_ws, sticky); + container_handle_fullscreen_reparent(sticky); + container_floating_move_to_center(sticky); + ipc_event_window(sticky, "move"); + } + workspace_detect_urgent(new_ws); +} + static void output_evacuate(struct sway_output *output) { if (!output->workspaces->length) { return; @@ -132,17 +144,21 @@ static void output_evacuate(struct sway_output *output) { workspace_detach(workspace); - if (workspace_is_empty(workspace)) { - workspace_begin_destroy(workspace); - continue; - } - struct sway_output *new_output = workspace_output_get_highest_available(workspace, output); if (!new_output) { new_output = fallback_output; } + if (workspace_is_empty(workspace)) { + // If floating is not empty, there are sticky containers to move + if (workspace->floating->length) { + evacuate_sticky(workspace, new_output); + } + workspace_begin_destroy(workspace); + continue; + } + if (new_output) { workspace_output_add_priority(workspace, new_output); output_add_workspace(new_output, workspace); diff --git a/sway/tree/root.c b/sway/tree/root.c index ecc04ddb..6748e9c9 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -265,14 +265,20 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data), // Scratchpad for (int i = 0; i < root->scratchpad->length; ++i) { struct sway_container *container = root->scratchpad->items[i]; - // If the container has a parent then it's visible on a workspace + // If the container has a workspace then it's visible on a workspace // and will have been iterated in the previous for loop. So we only // iterate the hidden scratchpad containers here. - if (!container->parent) { + if (!container->workspace) { f(container, data); container_for_each_child(container, f, data); } } + + // Saved workspaces + for (int i = 0; i < root->saved_workspaces->length; ++i) { + struct sway_workspace *ws = root->saved_workspaces->items[i]; + workspace_for_each_container(ws, f, data); + } } struct sway_output *root_find_output( @@ -311,7 +317,7 @@ struct sway_container *root_find_container( // Scratchpad for (int i = 0; i < root->scratchpad->length; ++i) { struct sway_container *container = root->scratchpad->items[i]; - if (!container->parent) { + if (!container->workspace) { if (test(container, data)) { return container; } @@ -320,6 +326,15 @@ struct sway_container *root_find_container( } } } + + // Saved workspaces + for (int i = 0; i < root->saved_workspaces->length; ++i) { + struct sway_workspace *ws = root->saved_workspaces->items[i]; + if ((result = workspace_find_container(ws, test, data))) { + return result; + } + } + return NULL; } diff --git a/sway/tree/view.c b/sway/tree/view.c index 4398f518..a024f325 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -391,8 +391,6 @@ static bool view_has_executed_criteria(struct sway_view *view, } void view_execute_criteria(struct sway_view *view) { - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_node *prior_focus = seat_get_focus(seat); list_t *criterias = criteria_for_view(view, CT_COMMAND); for (int i = 0; i < criterias->length; i++) { struct criteria *criteria = criterias->items[i]; @@ -403,16 +401,12 @@ void view_execute_criteria(struct sway_view *view) { } wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", criteria->raw, view, criteria->cmdlist); - seat_set_focus_container(seat, view->container); list_add(view->executed_criteria, criteria); - struct cmd_results *res = execute_command(criteria->cmdlist, NULL); - if (res->status != CMD_SUCCESS) { - wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error); - } + struct cmd_results *res = execute_command( + criteria->cmdlist, NULL, view->container); free_cmd_results(res); } list_free(criterias); - seat_set_focus(seat, prior_focus); } static struct sway_workspace *select_workspace(struct sway_view *view) { @@ -785,14 +779,9 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { } static char *escape_title(char *buffer) { - int length = escape_markup_text(buffer, NULL, 0); + size_t length = escape_markup_text(buffer, NULL); char *escaped_title = calloc(length + 1, sizeof(char)); - int result = escape_markup_text(buffer, escaped_title, length); - if (result != length) { - wlr_log(WLR_ERROR, "Could not escape title: %s", buffer); - free(escaped_title); - return buffer; - } + escape_markup_text(buffer, escaped_title); free(buffer); return escaped_title; } @@ -1000,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) { @@ -1019,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 18746430..9dd5c815 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 @@ -54,10 +60,6 @@ struct sway_workspace *workspace_create(struct sway_output *output, return NULL; } node_init(&ws->node, N_WORKSPACE, ws); - ws->x = output->lx; - ws->y = output->ly; - ws->width = output->width; - ws->height = output->height; ws->name = name ? strdup(name) : NULL; ws->prev_split_layout = L_NONE; ws->layout = output_get_default_layout(output); @@ -66,6 +68,20 @@ struct sway_workspace *workspace_create(struct sway_output *output, ws->output_priority = create_list(); workspace_output_add_priority(ws, output); + ws->gaps_outer = config->gaps_outer; + ws->gaps_inner = config->gaps_inner; + if (name) { + struct workspace_config *wsc = workspace_find_config(name); + if (wsc) { + if (wsc->gaps_outer != -1) { + ws->gaps_outer = wsc->gaps_outer; + } + if (wsc->gaps_inner != -1) { + ws->gaps_inner = wsc->gaps_inner; + } + } + } + output_add_workspace(output, ws); output_sort_workspaces(output); @@ -125,17 +141,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, @@ -235,13 +242,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; } } @@ -389,13 +396,11 @@ bool workspace_switch(struct sway_workspace *workspace, struct sway_output *next_output = workspace->output; struct sway_workspace *next_output_prev_ws = output_get_active_workspace(next_output); - bool has_sticky = false; if (workspace != next_output_prev_ws) { for (int i = 0; i < next_output_prev_ws->floating->length; ++i) { struct sway_container *floater = next_output_prev_ws->floating->items[i]; if (floater->is_sticky) { - has_sticky = true; container_detach(floater); workspace_add_floating(workspace, floater); if (&floater->node == focus) { @@ -414,14 +419,6 @@ bool workspace_switch(struct sway_workspace *workspace, if (next == NULL) { next = &workspace->node; } - if (has_sticky) { - // If there's a sticky container, we might be setting focus to the same - // container that's already focused, so seat_set_focus is effectively a - // no op. We therefore need to send the IPC event and clean up the old - // workspace here. - ipc_event_workspace(active_ws, workspace, "focus"); - workspace_consider_destroy(active_ws); - } seat_set_focus(seat, next); arrange_workspace(workspace); cursor_send_pointer_motion(seat->cursor, 0, true); @@ -649,13 +646,13 @@ void workspace_add_gaps(struct sway_workspace *ws) { return; } - ws->current_gaps = ws->has_gaps ? ws->gaps_outer : config->gaps_outer; + ws->current_gaps = ws->gaps_outer; if (ws->layout == L_TABBED || ws->layout == L_STACKED) { // We have to add inner gaps for this, because children of tabbed and // stacked containers don't apply their own gaps - they assume the // tabbed/stacked container is using gaps. - ws->current_gaps += ws->has_gaps ? ws->gaps_inner : config->gaps_inner; + ws->current_gaps += ws->gaps_inner; } ws->x += ws->current_gaps; diff --git a/swaybar/bar.c b/swaybar/bar.c index 3ae730f7..388c24c4 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" @@ -48,8 +49,13 @@ static void swaybar_output_free(struct swaybar_output *output) { return; } wlr_log(WLR_DEBUG, "Removing output %s", output->name); - zwlr_layer_surface_v1_destroy(output->layer_surface); - wl_surface_destroy(output->surface); + if (output->layer_surface != NULL) { + zwlr_layer_surface_v1_destroy(output->layer_surface); + } + if (output->surface != NULL) { + wl_surface_destroy(output->surface); + } + zxdg_output_v1_destroy(output->xdg_output); wl_output_destroy(output->output); destroy_buffer(&output->buffers[0]); destroy_buffer(&output->buffers[1]); @@ -66,6 +72,16 @@ static void swaybar_output_free(struct swaybar_output *output) { free(output); } +static void set_output_dirty(struct swaybar_output *output) { + if (output->frame_scheduled) { + output->dirty = true; + return; + } + if (output->surface) { + render_frame(output); + } +} + static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t width, uint32_t height) { @@ -73,7 +89,7 @@ static void layer_surface_configure(void *data, output->width = width; output->height = height; zwlr_layer_surface_v1_ack_configure(surface, serial); - render_frame(output->bar, output); + set_output_dirty(output); } static void layer_surface_closed(void *_output, @@ -283,28 +299,58 @@ const struct wl_seat_listener seat_listener = { .name = seat_handle_name, }; -static void output_geometry(void *data, struct wl_output *output, int32_t x, +static void add_layer_surface(struct swaybar_output *output) { + if (output->surface != NULL) { + return; + } + struct swaybar *bar = output->bar; + + output->surface = wl_compositor_create_surface(bar->compositor); + assert(output->surface); + output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + bar->layer_shell, output->surface, output->output, + ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); + assert(output->layer_surface); + zwlr_layer_surface_v1_add_listener(output->layer_surface, + &layer_surface_listener, output); + zwlr_layer_surface_v1_set_anchor(output->layer_surface, + bar->config->position); +} + +static bool bar_uses_output(struct swaybar *bar, const char *name) { + if (bar->config->all_outputs) { + return true; + } + struct config_output *coutput; + wl_list_for_each(coutput, &bar->config->outputs, link) { + if (strcmp(coutput->name, name) == 0) { + return true; + } + } + return false; +} + +static void output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, const char *make, const char *model, int32_t transform) { - // Who cares + struct swaybar_output *output = data; + output->subpixel = subpixel; } -static void output_mode(void *data, struct wl_output *output, uint32_t flags, +static void output_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { // Who cares } -static void output_done(void *data, struct wl_output *output) { - // Who cares +static void output_done(void *data, struct wl_output *wl_output) { + struct swaybar_output *output = data; + set_output_dirty(output); } static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) { struct swaybar_output *output = data; output->scale = factor; - if (output->surface) { - render_frame(output->bar, output); - } } struct wl_output_listener output_listener = { @@ -326,7 +372,22 @@ static void xdg_output_handle_logical_size(void *data, static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { - // Who cares + struct swaybar_output *output = data; + struct swaybar *bar = output->bar; + + assert(output->name != NULL); + if (!bar_uses_output(bar, output->name)) { + swaybar_output_free(output); + return; + } + + if (wl_list_empty(&output->link)) { + wl_list_remove(&output->link); + wl_list_insert(&bar->outputs, &output->link); + + add_layer_surface(output); + set_output_dirty(output); + } } static void xdg_output_handle_name(void *data, @@ -349,17 +410,15 @@ struct zxdg_output_v1_listener xdg_output_listener = { .description = xdg_output_handle_description, }; -static bool bar_uses_output(struct swaybar *bar, const char *name) { - if (bar->config->all_outputs) { - return true; - } - struct config_output *coutput; - wl_list_for_each(coutput, &bar->config->outputs, link) { - if (strcmp(coutput->name, name) == 0) { - return true; - } +static void add_xdg_output(struct swaybar_output *output) { + if (output->xdg_output != NULL) { + return; } - return false; + assert(output->bar->xdg_output_manager != NULL); + output->xdg_output = zxdg_output_manager_v1_get_xdg_output( + output->bar->xdg_output_manager, output->output); + zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, + output); } static void handle_global(void *data, struct wl_registry *registry, @@ -386,7 +445,10 @@ static void handle_global(void *data, struct wl_registry *registry, output->wl_name = name; wl_list_init(&output->workspaces); wl_list_init(&output->hotspots); - wl_list_insert(&bar->outputs, &output->link); + wl_list_init(&output->link); + if (bar->xdg_output_manager != NULL) { + add_xdg_output(output); + } } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { bar->layer_shell = wl_registry_bind( registry, name, &zwlr_layer_shell_v1_interface, 1); @@ -413,21 +475,23 @@ static const struct wl_registry_listener registry_listener = { .global_remove = handle_global_remove, }; -static void render_all_frames(struct swaybar *bar) { +static void set_bar_dirty(struct swaybar *bar) { struct swaybar_output *output; wl_list_for_each(output, &bar->outputs, link) { - render_frame(bar, output); + set_output_dirty(output); } } -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); } @@ -443,23 +507,10 @@ void bar_setup(struct swaybar *bar, struct swaybar_output *output; wl_list_for_each(output, &bar->outputs, link) { - output->xdg_output = zxdg_output_manager_v1_get_xdg_output( - bar->xdg_output_manager, output->output); - zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, - output); + add_xdg_output(output); } wl_display_roundtrip(bar->display); - struct swaybar_output *output_tmp; - wl_list_for_each_safe(output, output_tmp, &bar->outputs, link) { - if (!bar_uses_output(bar, output->name)) { - zxdg_output_v1_destroy(output->xdg_output); - wl_output_destroy(output->output); - wl_list_remove(&output->link); - free(output); - } - } - struct swaybar_pointer *pointer = &bar->pointer; int max_scale = 1; @@ -479,20 +530,9 @@ void bar_setup(struct swaybar *bar, pointer->cursor_surface = wl_compositor_create_surface(bar->compositor); assert(pointer->cursor_surface); - wl_list_for_each(output, &bar->outputs, link) { - output->surface = wl_compositor_create_surface(bar->compositor); - assert(output->surface); - output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( - bar->layer_shell, output->surface, output->output, - ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); - assert(output->layer_surface); - zwlr_layer_surface_v1_add_listener(output->layer_surface, - &layer_surface_listener, output); - zwlr_layer_surface_v1_set_anchor(output->layer_surface, - bar->config->position); - } ipc_get_workspaces(bar); - render_all_frames(bar); + set_bar_dirty(bar); + return true; } static void display_in(int fd, short mask, void *data) { @@ -506,7 +546,7 @@ static void display_in(int fd, short mask, void *data) { static void ipc_in(int fd, short mask, void *data) { struct swaybar *bar = data; if (handle_ipc_readable(bar)) { - render_all_frames(bar); + set_bar_dirty(bar); } } @@ -514,10 +554,10 @@ static void status_in(int fd, short mask, void *data) { struct swaybar *bar = data; if (mask & (POLLHUP | POLLERR)) { status_error(bar->status, "[error reading from status command]"); - render_all_frames(bar); + set_bar_dirty(bar); remove_event(fd); } else if (status_handle_readable(bar->status)) { - render_all_frames(bar); + set_bar_dirty(bar); } } @@ -529,6 +569,7 @@ void bar_run(struct swaybar *bar) { } while (1) { event_loop_poll(); + wl_display_flush(bar->display); } } diff --git a/swaybar/config.c b/swaybar/config.c index db7b0db6..4e851cca 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -22,7 +22,7 @@ uint32_t parse_position(const char *position) { } } -struct swaybar_config *init_config() { +struct swaybar_config *init_config(void) { struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config)); config->status_command = NULL; config->pango_markup = false; diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 88404703..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) { @@ -117,7 +119,9 @@ bool i3bar_handle_readable(struct status_line *status) { memmove(status->buffer, &status->buffer[c], status->buffer_index); break; } else if (!isspace(status->buffer[c])) { - status_error(status, "[invalid json]"); + wlr_log(WLR_DEBUG, "Invalid i3bar json: expected '[' but encountered '%c'", + status->buffer[c]); + status_error(status, "[invalid i3bar json]"); return true; } } @@ -155,6 +159,8 @@ bool i3bar_handle_readable(struct status_line *status) { ++buffer_pos; break; } else if (!isspace(status->buffer[buffer_pos])) { + wlr_log(WLR_DEBUG, "Invalid i3bar json: expected ',' but encountered '%c'", + status->buffer[buffer_pos]); status_error(status, "[invalid i3bar json]"); return true; } @@ -166,7 +172,8 @@ bool i3bar_handle_readable(struct status_line *status) { } else { test_object = json_tokener_parse_ex(status->tokener, &status->buffer[buffer_pos], status->buffer_index - buffer_pos); - if (json_tokener_get_error(status->tokener) == json_tokener_success) { + enum json_tokener_error err = json_tokener_get_error(status->tokener); + if (err == json_tokener_success) { if (json_object_get_type(test_object) == json_type_array) { if (last_object) { json_object_put(last_object); @@ -198,12 +205,14 @@ bool i3bar_handle_readable(struct status_line *status) { continue; // look for comma without reading more input } buffer_pos = status->buffer_index = 0; - } else if (json_tokener_get_error(status->tokener) == json_tokener_continue) { + } else if (err == json_tokener_continue) { + json_tokener_reset(status->tokener); if (status->buffer_index < status->buffer_size) { // move the object to the start of the buffer status->buffer_index -= buffer_pos; memmove(status->buffer, &status->buffer[buffer_pos], status->buffer_index); + buffer_pos = 0; } else { // expand buffer status->buffer_size *= 2; @@ -217,6 +226,10 @@ bool i3bar_handle_readable(struct status_line *status) { } } } else { + char last_char = status->buffer[status->buffer_index - 1]; + status->buffer[status->buffer_index - 1] = '\0'; + wlr_log(WLR_DEBUG, "Failed to parse i3bar json - %s: '%s%c'", + json_tokener_error_desc(err), &status->buffer[buffer_pos], last_char); status_error(status, "[failed to parse i3bar json]"); return true; } 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/meson.build b/swaybar/meson.build index d65edb11..7a02a33f 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build @@ -24,5 +24,6 @@ executable( wlroots, ], link_with: [lib_sway_common, lib_sway_client], + install_rpath : rpathdir, install: true ) diff --git a/swaybar/render.c b/swaybar/render.c index 97690338..dc31a5ea 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200809L +#include <assert.h> #include <limits.h> #include <stdlib.h> #include <stdint.h> @@ -9,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" @@ -19,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; @@ -71,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, @@ -121,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; @@ -144,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; } @@ -165,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) { @@ -177,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; @@ -240,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); @@ -259,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; } @@ -279,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; } @@ -299,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, @@ -315,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); @@ -339,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) { @@ -365,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); @@ -383,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, @@ -396,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; } @@ -433,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) { @@ -457,29 +454,43 @@ 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; } return max_height > output->height ? max_height : output->height; } -void render_frame(struct swaybar *bar, struct swaybar_output *output) { +static void output_frame_handle_done(void *data, struct wl_callback *callback, + uint32_t time) { + wl_callback_destroy(callback); + struct swaybar_output *output = data; + output->frame_scheduled = false; + if (output->dirty) { + render_frame(output); + output->dirty = false; + } +} + +static const struct wl_callback_listener output_frame_listener = { + .done = output_frame_handle_done +}; + +void render_frame(struct swaybar_output *output) { + assert(output->surface != NULL); + struct swaybar_hotspot *hotspot, *tmp; wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) { if (hotspot->destroy) { @@ -492,13 +503,21 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { cairo_surface_t *recorder = cairo_recording_surface_create( CAIRO_CONTENT_COLOR_ALPHA, NULL); cairo_t *cairo = cairo_create(recorder); + cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); + cairo_font_options_t *fo = cairo_font_options_create(); + cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); + cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); + cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->subpixel)); + cairo_set_font_options(cairo, fo); + cairo_font_options_destroy(fo); cairo_save(cairo); 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 @@ -507,14 +526,15 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { // TODO: this could infinite loop if the compositor assigns us a // different height than what we asked for wl_surface_commit(output->surface); - wl_display_roundtrip(bar->display); } 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); if (!output->current_buffer) { + cairo_surface_destroy(recorder); + cairo_destroy(cairo); return; } cairo_t *shm = output->current_buffer->cairo; @@ -532,8 +552,12 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { output->current_buffer->buffer, 0, 0); wl_surface_damage(output->surface, 0, 0, output->width, output->height); + + struct wl_callback *frame_callback = wl_surface_frame(output->surface); + wl_callback_add_listener(frame_callback, &output_frame_listener, output); + output->frame_scheduled = true; + wl_surface_commit(output->surface); - wl_display_roundtrip(bar->display); } cairo_surface_destroy(recorder); cairo_destroy(cairo); diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 401bf6f6..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,8 +87,8 @@ bool status_handle_readable(struct status_line *status) { wl_list_init(&status->blocks); status->tokener = json_tokener_new(); - status->buffer_index = getdelim(&status->buffer, - &status->buffer_size, EOF, status->read); + status->buffer_index = strlen(newline + 1); + memmove(status->buffer, newline + 1, status->buffer_index + 1); return i3bar_handle_readable(status); } diff --git a/swaybg/main.c b/swaybg/main.c index 5b0d0458..742669ef 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -73,6 +73,10 @@ static void render_frame(struct swaybg_state *state) { return; } cairo_t *cairo = state->current_buffer->cairo; + cairo_save(cairo); + cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); + cairo_paint(cairo); + cairo_restore(cairo); if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { cairo_set_source_u32(cairo, state->context.color); cairo_paint(cairo); diff --git a/swaybg/meson.build b/swaybg/meson.build index 8704de6d..095c5488 100644 --- a/swaybg/meson.build +++ b/swaybg/meson.build @@ -14,5 +14,6 @@ executable( wlroots, ], link_with: [lib_sway_common, lib_sway_client], + install_rpath : rpathdir, install: true ) diff --git a/swayidle/main.c b/swayidle/main.c index 678d622f..5b6c95a7 100644 --- a/swayidle/main.c +++ b/swayidle/main.c @@ -92,7 +92,7 @@ static int release_lock(void *data) { return 0; } -void acquire_sleep_lock() { +void acquire_sleep_lock(void) { sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; struct sd_bus *bus; @@ -161,7 +161,7 @@ static int dbus_event(int fd, uint32_t mask, void *data) { return 1; } -void setup_sleep_listener() { +void setup_sleep_listener(void) { struct sd_bus *bus; int ret = sd_bus_default_system(&bus); diff --git a/swayidle/meson.build b/swayidle/meson.build index f62545f8..6c3ac119 100644 --- a/swayidle/meson.build +++ b/swayidle/meson.build @@ -14,5 +14,6 @@ executable( swayidle_deps, ], link_with: [lib_sway_common, lib_sway_client], + install_rpath : rpathdir, install: true ) diff --git a/swaylock/main.c b/swaylock/main.c index 668a8742..ed8c5607 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -32,7 +32,7 @@ void sway_terminate(int exit_code) { exit(exit_code); } -static void daemonize() { +static void daemonize(void) { int fds[2]; if (pipe(fds) != 0) { wlr_log(WLR_ERROR, "Failed to pipe"); @@ -195,11 +195,15 @@ void damage_state(struct swaylock_state *state) { } } -static void handle_wl_output_geometry(void *data, struct wl_output *output, +static void handle_wl_output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, const char *make, const char *model, int32_t transform) { - // Who cares + struct swaylock_surface *surface = data; + surface->subpixel = subpixel; + if (surface->state->run_display) { + damage_surface(surface); + } } static void handle_wl_output_mode(void *data, struct wl_output *output, @@ -841,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, @@ -853,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..6605340b 100644 --- a/swaylock/meson.build +++ b/swaylock/meson.build @@ -1,26 +1,39 @@ 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_rpath : rpathdir, 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/render.c b/swaylock/render.c index 66c55965..fa8832bd 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -39,6 +39,13 @@ void render_frame(struct swaylock_surface *surface) { } cairo_t *cairo = surface->current_buffer->cairo; + cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); + cairo_font_options_t *fo = cairo_font_options_create(); + cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); + cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); + cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(surface->subpixel)); + cairo_set_font_options(cairo, fo); + cairo_font_options_destroy(fo); cairo_identity_matrix(cairo); cairo_save(cairo); 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; +} diff --git a/swaymsg/meson.build b/swaymsg/meson.build index 8638b838..7318349d 100644 --- a/swaymsg/meson.build +++ b/swaymsg/meson.build @@ -4,5 +4,6 @@ executable( include_directories: [sway_inc], dependencies: [jsonc, wlroots], link_with: [lib_sway_common], + install_rpath : rpathdir, install: true ) diff --git a/swaynag/config.c b/swaynag/config.c index 4d0824c9..cd34dcc2 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -11,7 +11,7 @@ #include "util.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" -static char *read_from_stdin() { +static char *read_from_stdin(void) { char *buffer = NULL; while (!feof(stdin)) { char *line = read_line(stdin); diff --git a/swaynag/meson.build b/swaynag/meson.build index 2ba3ed95..223a0bc7 100644 --- a/swaynag/meson.build +++ b/swaynag/meson.build @@ -19,5 +19,6 @@ executable( wlroots, ], link_with: [lib_sway_common, lib_sway_client], + install_rpath : rpathdir, install: true ) |