aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sway/commands.h4
-rw-r--r--include/sway/config.h24
-rw-r--r--include/sway/debug.h2
-rw-r--r--include/sway/ipc-json.h2
-rw-r--r--include/sway/tree/container.h3
-rw-r--r--include/sway/tree/view.h7
-rw-r--r--include/sway/tree/workspace.h9
-rw-r--r--include/swaybar/bar.h7
-rw-r--r--include/swaybar/config.h2
-rw-r--r--include/swaybar/i3bar.h34
-rw-r--r--include/swaybar/ipc.h2
-rw-r--r--include/swaybar/render.h4
-rw-r--r--include/swaybar/status_line.h27
-rw-r--r--include/swaybar/tray/dbus.h18
-rw-r--r--include/swaybar/tray/sni_watcher.h10
-rw-r--r--include/swaybar/tray/tray.h32
-rw-r--r--include/swaylock/swaylock.h3
-rw-r--r--meson.build22
-rw-r--r--meson_options.txt3
-rw-r--r--sway/commands.c18
-rw-r--r--sway/commands/bar/binding_mode_indicator.c6
-rw-r--r--sway/commands/gaps.c306
-rw-r--r--sway/commands/hide_edge_borders.c16
-rw-r--r--sway/commands/input.c1
-rw-r--r--sway/commands/input/drag.c26
-rw-r--r--sway/commands/layout.c3
-rw-r--r--sway/commands/output/background.c18
-rw-r--r--sway/commands/resize.c30
-rw-r--r--sway/commands/smart_borders.c25
-rw-r--r--sway/commands/workspace.c74
-rw-r--r--sway/config.c12
-rw-r--r--sway/config/input.c4
-rw-r--r--sway/config/output.c4
-rw-r--r--sway/config/seat.c2
-rw-r--r--sway/criteria.c4
-rw-r--r--sway/debug-tree.c2
-rw-r--r--sway/desktop/transaction.c55
-rw-r--r--sway/input/cursor.c11
-rw-r--r--sway/input/input-manager.c7
-rw-r--r--sway/input/seat.c63
-rw-r--r--sway/ipc-json.c2
-rw-r--r--sway/main.c81
-rw-r--r--sway/meson.build4
-rw-r--r--sway/sway-bar.5.scd3
-rw-r--r--sway/sway-input.5.scd7
-rw-r--r--sway/sway.5.scd90
-rw-r--r--sway/tree/container.c37
-rw-r--r--sway/tree/output.c28
-rw-r--r--sway/tree/root.c15
-rw-r--r--sway/tree/view.c73
-rw-r--r--sway/tree/workspace.c79
-rw-r--r--swaybar/bar.c55
-rw-r--r--swaybar/config.c2
-rw-r--r--swaybar/i3bar.c7
-rw-r--r--swaybar/ipc.c19
-rw-r--r--swaybar/main.c5
-rw-r--r--swaybar/meson.build1
-rw-r--r--swaybar/render.c134
-rw-r--r--swaybar/status_line.c42
-rw-r--r--swaybg/meson.build1
-rw-r--r--swayidle/main.c4
-rw-r--r--swayidle/meson.build1
-rw-r--r--swaylock/main.c7
-rw-r--r--swaylock/meson.build51
-rw-r--r--swaylock/pam.c62
-rw-r--r--swaylock/password.c51
-rw-r--r--swaylock/shadow.c128
-rw-r--r--swaymsg/meson.build1
-rw-r--r--swaynag/config.c2
-rw-r--r--swaynag/meson.build1
70 files changed, 1131 insertions, 764 deletions
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 1654eb48..64f707f4 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -95,7 +95,7 @@ struct cmd_results *add_color(const char *name,
/**
* TODO: Move this function and its dependent functions to container.c.
*/
-void container_resize_tiled(struct sway_container *parent, enum wlr_edges edge,
+bool container_resize_tiled(struct sway_container *parent, enum wlr_edges edge,
int amount);
sway_cmd cmd_assign;
@@ -160,6 +160,7 @@ sway_cmd cmd_scratchpad;
sway_cmd cmd_seamless_mouse;
sway_cmd cmd_set;
sway_cmd cmd_show_marks;
+sway_cmd cmd_smart_borders;
sway_cmd cmd_smart_gaps;
sway_cmd cmd_split;
sway_cmd cmd_splith;
@@ -217,6 +218,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 35f0e708..98a18b76 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 {
@@ -251,7 +253,8 @@ enum edge_border_types {
E_VERTICAL, /**< hide vertical edge borders */
E_HORIZONTAL, /**< hide horizontal edge borders */
E_BOTH, /**< hide vertical and horizontal edge borders */
- E_SMART /**< hide both if precisely one window is present in workspace */
+ E_SMART, /**< hide both if precisely one window is present in workspace */
+ E_SMART_NO_GAPS, /**< hide both if one window and gaps to edge is zero */
};
enum command_context {
@@ -327,7 +330,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;
@@ -381,6 +384,7 @@ struct sway_config {
int border_thickness;
int floating_border_thickness;
enum edge_border_types hide_edge_borders;
+ enum edge_border_types saved_edge_borders;
// border colors
struct {
@@ -450,8 +454,6 @@ 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);
@@ -472,7 +474,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);
@@ -518,6 +520,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 6019602c..da6592b4 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/view.h b/include/sway/tree/view.h
index 2c7b4c2b..028be536 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -265,6 +265,13 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
int height);
/**
+ * Whether or not the view is the only visible view in its tree. If the view
+ * is tiling, there may be floating views. If the view is floating, there may
+ * be tiling views or views in a different floating container.
+ */
+bool view_is_only_visible(struct sway_view *view);
+
+/**
* Configure the view's position and size based on the container's position and
* size, taking borders into consideration.
*/
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 29e96159..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;
@@ -84,6 +83,8 @@ struct swaybar_output {
enum wl_output_subpixel subpixel;
struct pool_buffer buffers[2];
struct pool_buffer *current_buffer;
+ bool dirty;
+ bool frame_scheduled;
};
struct swaybar_workspace {
@@ -95,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..ca88b0c5 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;
@@ -43,6 +23,7 @@ struct status_line {
struct wl_list blocks; // i3bar_block::link
bool click_events;
+ bool clicked;
char *buffer;
size_t buffer_size;
size_t buffer_index;
@@ -55,11 +36,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 2f0cd34d..970e3cc9 100644
--- a/include/swaylock/swaylock.h
+++ b/include/swaylock/swaylock.h
@@ -101,5 +101,8 @@ void render_frame(struct swaylock_surface *surface);
void render_frames(struct swaylock_state *state);
void damage_surface(struct swaylock_surface *surface);
void damage_state(struct swaylock_state *state);
+void initialize_pw_backend(void);
+bool attempt_password(struct swaylock_password *pw);
+void clear_password_buffer(struct swaylock_password *pw);
#endif
diff --git a/meson.build b/meson.build
index 76eaff20..1e7ce281 100644
--- a/meson.build
+++ b/meson.build
@@ -42,7 +42,6 @@ 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', required: false)
systemd = dependency('libsystemd', required: false)
@@ -74,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()
@@ -124,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')
@@ -139,10 +152,7 @@ subdir('swaybg')
subdir('swaybar')
subdir('swayidle')
subdir('swaynag')
-
-if libpam.found()
- subdir('swaylock')
-endif
+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 bff230f7..72db8ab9 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -110,6 +110,7 @@ static struct cmd_handler handlers[] = {
{ "seat", cmd_seat },
{ "set", cmd_set },
{ "show_marks", cmd_show_marks },
+ { "smart_borders", cmd_smart_borders },
{ "smart_gaps", cmd_smart_gaps },
{ "tiling_drag", cmd_tiling_drag },
{ "workspace", cmd_workspace },
@@ -391,14 +392,16 @@ 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 (handler->handle != cmd_exec && handler->handle != cmd_exec_always
+ && handler->handle != cmd_bindsym
+ && handler->handle != cmd_bindcode
+ && handler->handle != cmd_set
+ && (*argv[i] == '\"' || *argv[i] == '\'')) {
+ strip_quotes(argv[i]);
+ }
argv[i] = do_var_replacement(argv[i]);
unescape_string(argv[i]);
}
- // Strip quotes for first argument.
- // TODO This part needs to be handled much better
- if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
- strip_quotes(argv[1]);
- }
if (handler->handle) {
results = handler->handle(argc-1, argv+1);
} else {
@@ -422,11 +425,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/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/hide_edge_borders.c b/sway/commands/hide_edge_borders.c
index 0a5c7f28..ea261fb1 100644
--- a/sway/commands/hide_edge_borders.c
+++ b/sway/commands/hide_edge_borders.c
@@ -1,15 +1,8 @@
#include "sway/commands.h"
#include "sway/config.h"
-#include "sway/tree/container.h"
-#include "sway/tree/root.h"
+#include "sway/tree/arrange.h"
#include "sway/tree/view.h"
-static void _configure_view(struct sway_container *con, void *data) {
- if (con->view) {
- view_autoconfigure(con->view);
- }
-}
-
struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) {
@@ -26,13 +19,16 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
config->hide_edge_borders = E_BOTH;
} else if (strcmp(argv[0], "smart") == 0) {
config->hide_edge_borders = E_SMART;
+ } else if (strcmp(argv[0], "smart_no_gaps") == 0) {
+ config->hide_edge_borders = E_SMART_NO_GAPS;
} else {
return cmd_results_new(CMD_INVALID, "hide_edge_borders",
"Expected 'hide_edge_borders "
- "<none|vertical|horizontal|both|smart>'");
+ "<none|vertical|horizontal|both|smart|smart_no_gaps>'");
}
+ config->saved_edge_borders = config->hide_edge_borders;
- root_for_each_container(_configure_view, NULL);
+ arrange_root();
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/input.c b/sway/commands/input.c
index 9091da2a..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 },
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/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/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/smart_borders.c b/sway/commands/smart_borders.c
new file mode 100644
index 00000000..fcb4040e
--- /dev/null
+++ b/sway/commands/smart_borders.c
@@ -0,0 +1,25 @@
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "sway/tree/arrange.h"
+#include "sway/tree/view.h"
+#include "util.h"
+
+struct cmd_results *cmd_smart_borders(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "smart_borders", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+
+ enum edge_border_types saved = config->hide_edge_borders;
+ if (strcmp(argv[0], "no_gaps") == 0) {
+ config->hide_edge_borders = E_SMART_NO_GAPS;
+ } else {
+ config->hide_edge_borders = parse_boolean(argv[0], true) ?
+ E_SMART : config->saved_edge_borders;
+ }
+ config->saved_edge_borders = saved;
+
+ arrange_root();
+
+ 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..048b57de 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;
@@ -244,6 +249,7 @@ static void config_defaults(struct sway_config *config) {
config->border_thickness = 2;
config->floating_border_thickness = 2;
config->hide_edge_borders = E_NONE;
+ config->saved_edge_borders = E_NONE;
// border colors
set_color(config->border_colors.focused.border, 0x4C7899);
@@ -804,7 +810,7 @@ char *do_var_replacement(char *str) {
// would compare two structs in full, while this method only compares the
// workspace.
int workspace_output_cmp_workspace(const void *a, const void *b) {
- const struct workspace_output *wsa = a, *wsb = b;
+ const struct workspace_config *wsa = a, *wsb = b;
return lenient_strcmp(wsa->workspace, wsb->workspace);
}
diff --git a/sway/config/input.c b/sway/config/input.c
index 6b43a5b9..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;
@@ -46,6 +47,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
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;
}
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/criteria.c b/sway/criteria.c
index 0193233e..575e8bcf 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -339,6 +339,10 @@ static enum criteria_token token_from_name(char *name) {
return T_URGENT;
} else if (strcmp(name, "workspace") == 0) {
return T_WORKSPACE;
+ } else if (strcmp(name, "tiling") == 0) {
+ return T_TILING;
+ } else if (strcmp(name, "floating") == 0) {
+ return T_FLOATING;
}
return T_INVALID;
}
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 4624d824..e717ee35 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -31,14 +31,14 @@ struct sway_transaction_instruction {
struct sway_transaction *transaction;
struct sway_node *node;
union {
- struct sway_output_state *output_state;
- struct sway_workspace_state *workspace_state;
- struct sway_container_state *container_state;
+ struct sway_output_state output_state;
+ struct sway_workspace_state workspace_state;
+ struct sway_container_state container_state;
};
uint32_t serial;
};
-static struct sway_transaction *transaction_create() {
+static struct sway_transaction *transaction_create(void) {
struct sway_transaction *transaction =
calloc(1, sizeof(struct sway_transaction));
if (!sway_assert(transaction, "Unable to allocate transaction")) {
@@ -86,14 +86,7 @@ static void transaction_destroy(struct sway_transaction *transaction) {
static void copy_output_state(struct sway_output *output,
struct sway_transaction_instruction *instruction) {
- struct sway_output_state *state =
- calloc(1, sizeof(struct sway_output_state));
- if (!state) {
- wlr_log(WLR_ERROR, "Could not allocate output state");
- return;
- }
- instruction->output_state = state;
-
+ struct sway_output_state *state = &instruction->output_state;
state->workspaces = create_list();
list_cat(state->workspaces, output->workspaces);
@@ -102,13 +95,7 @@ static void copy_output_state(struct sway_output *output,
static void copy_workspace_state(struct sway_workspace *ws,
struct sway_transaction_instruction *instruction) {
- struct sway_workspace_state *state =
- calloc(1, sizeof(struct sway_workspace_state));
- if (!state) {
- wlr_log(WLR_ERROR, "Could not allocate workspace state");
- return;
- }
- instruction->workspace_state = state;
+ struct sway_workspace_state *state = &instruction->workspace_state;
state->fullscreen = ws->fullscreen;
state->x = ws->x;
@@ -138,13 +125,7 @@ static void copy_workspace_state(struct sway_workspace *ws,
static void copy_container_state(struct sway_container *container,
struct sway_transaction_instruction *instruction) {
- struct sway_container_state *state =
- calloc(1, sizeof(struct sway_container_state));
- if (!state) {
- wlr_log(WLR_ERROR, "Could not allocate container state");
- return;
- }
- instruction->container_state = state;
+ struct sway_container_state *state = &instruction->container_state;
state->layout = container->layout;
state->con_x = container->x;
@@ -300,15 +281,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 +315,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 +370,7 @@ static bool should_configure(struct sway_node *node,
return false;
}
struct sway_container_state *cstate = &node->sway_container->current;
- struct sway_container_state *istate = instruction->container_state;
+ struct sway_container_state *istate = &instruction->container_state;
#ifdef HAVE_XWAYLAND
// Xwayland views are position-aware and need to be reconfigured
// when their position changes.
@@ -417,10 +398,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
@@ -512,8 +493,8 @@ void transaction_notify_view_ready_by_size(struct sway_view *view,
int width, int height) {
struct sway_transaction_instruction *instruction =
view->container->node.instruction;
- if (instruction->container_state->view_width == width &&
- instruction->container_state->view_height == height) {
+ if (instruction->container_state.view_width == width &&
+ instruction->container_state.view_height == height) {
set_instruction_ready(instruction);
}
}
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index eab102fd..afad6f6f 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;
@@ -1007,8 +1007,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);
}
}
@@ -1176,11 +1175,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 f39fe29c..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)",
diff --git a/sway/input/seat.c b/sway/input/seat.c
index a9c564e7..e10b6409 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -144,32 +144,43 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
struct sway_node *parent = node_get_parent(node);
struct sway_node *focus = seat_get_focus(seat);
- bool set_focus =
- focus != NULL &&
- (focus == node || node_has_ancestor(focus, node)) &&
- node->type == N_CONTAINER;
+ if (node->type == N_WORKSPACE) {
+ seat_node_destroy(seat_node);
+ return;
+ }
+
+ // Even though the container being destroyed might be nowhere near the
+ // focused container, we still need to set focus_inactive on a sibling of
+ // the container being destroyed.
+ bool needs_new_focus = focus &&
+ (focus == node || node_has_ancestor(focus, node));
seat_node_destroy(seat_node);
- if (set_focus) {
- struct sway_node *next_focus = NULL;
- while (next_focus == NULL) {
- struct sway_container *con =
- seat_get_focus_inactive_view(seat, parent);
- next_focus = con ? &con->node : NULL;
+ // Find new focus_inactive (ie. sibling, or workspace if no siblings left)
+ struct sway_node *next_focus = NULL;
+ while (next_focus == NULL) {
+ struct sway_container *con =
+ seat_get_focus_inactive_view(seat, parent);
+ next_focus = con ? &con->node : NULL;
- if (next_focus == NULL && parent->type == N_WORKSPACE) {
- next_focus = parent;
- break;
- }
-
- parent = node_get_parent(parent);
+ if (next_focus == NULL && parent->type == N_WORKSPACE) {
+ next_focus = parent;
+ break;
}
- // the structure change might have caused it to move up to the top of
+ parent = node_get_parent(parent);
+ }
+
+ if (needs_new_focus) {
+ // The structure change might have caused it to move up to the top of
// the focus stack without sending focus notifications to the view
seat_send_focus(next_focus, seat);
seat_set_focus(seat, next_focus);
+ } else {
+ // Setting focus_inactive
+ seat_set_focus_warp(seat, next_focus, false, false);
+ seat_set_focus_warp(seat, focus, false, false);
}
}
@@ -368,11 +379,20 @@ static void seat_update_capabilities(struct sway_seat *seat) {
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,
@@ -552,8 +572,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);
}
@@ -752,6 +771,12 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node,
seat->has_focus = true;
+ if (config->smart_gaps) {
+ // When smart gaps is on, gaps may change when the focus changes so
+ // the workspace needs to be arranged
+ arrange_workspace(new_workspace);
+ }
+
update_debug_tree();
}
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index df24b812..bc36f9b1 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/main.c b/sway/main.c
index 990f5f3a..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);
diff --git a/sway/meson.build b/sway/meson.build
index 2a3c5b5e..00ebcb40 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -79,6 +79,7 @@ sway_sources = files(
'commands/seat/fallback.c',
'commands/set.c',
'commands/show_marks.c',
+ 'commands/smart_borders.c',
'commands/smart_gaps.c',
'commands/split.c',
'commands/sticky.c',
@@ -120,6 +121,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',
@@ -165,7 +167,6 @@ sway_deps = [
cairo,
gdk_pixbuf,
jsonc,
- libcap,
libinput,
math,
pango,
@@ -188,5 +189,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..3202d517 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.
-*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.
+ 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>).
-*hide\_edge\_borders* none|vertical|horizontal|both|smart
+*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|smart\_no\_gaps
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
@@ -451,6 +456,12 @@ The default colors are:
*kill*
Kills (closes) the currently focused container and all of its children.
+*smart\_borders* on|no\_gaps|off
+ If smart\_borders are _on_, borders will only be enabled if the workspace
+ only has one visible child (identical to _hide\_edge\_borders_ smart). If
+ smart\_borders is set to _no\_gaps_, borders will only be enabled if the
+ workspace only has one visible child and gaps greater than zero.
+
*smart\_gaps* on|off
If smart\_gaps are _on_ gaps will only be enabled if a workspace has more
than one child.
@@ -465,10 +476,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 +542,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 +584,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/container.c b/sway/tree/container.c
index 9b671c1d..a069b177 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -593,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;
}
@@ -829,9 +829,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) {
@@ -1020,15 +1027,33 @@ void container_add_gaps(struct sway_container *c) {
if (!c->view && c->layout != L_TABBED && c->layout != L_STACKED) {
return;
}
- // Children of tabbed/stacked containers re-use the gaps of the container
- enum sway_container_layout layout = container_parent_layout(c);
- if (layout == L_TABBED || layout == L_STACKED) {
- return;
+ // Descendants of tabbed/stacked containers re-use the gaps of the container
+ struct sway_container *temp = c;
+ while (temp) {
+ enum sway_container_layout layout = container_parent_layout(temp);
+ if (layout == L_TABBED || layout == L_STACKED) {
+ return;
+ }
+ temp = temp->parent;
+ }
+ // If smart gaps is on, don't add gaps if there is only one view visible
+ if (config->smart_gaps) {
+ struct sway_view *view = c->view;
+ if (!view) {
+ struct sway_seat *seat =
+ input_manager_get_default_seat(input_manager);
+ struct sway_container *focus =
+ seat_get_focus_inactive_view(seat, &c->node);
+ view = focus ? focus->view : NULL;
+ }
+ if (view && view_is_only_visible(view)) {
+ return;
+ }
}
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 06933dc4..c3176325 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -113,6 +113,20 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
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;
@@ -130,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 d6f67bd7..6748e9c9 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -273,6 +273,12 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *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(
@@ -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 9ffcf206..9c7c44e9 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -165,6 +165,34 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
return 0;
}
+bool view_is_only_visible(struct sway_view *view) {
+ bool only_view = true;
+ struct sway_container *con = view->container;
+ while (con) {
+ enum sway_container_layout layout = container_parent_layout(con);
+ if (layout != L_TABBED && layout != L_STACKED) {
+ list_t *siblings = container_get_siblings(con);
+ if (siblings && siblings->length > 1) {
+ only_view = false;
+ break;
+ }
+ }
+ con = con->parent;
+ }
+ return only_view;
+}
+
+static bool gaps_to_edge(struct sway_view *view) {
+ struct sway_container *con = view->container;
+ while (con) {
+ if (con->current_gaps > 0) {
+ return true;
+ }
+ con = con->parent;
+ }
+ return view->container->workspace->current_gaps > 0;
+}
+
void view_autoconfigure(struct sway_view *view) {
if (!view->container->workspace) {
// Hidden in the scratchpad
@@ -181,39 +209,28 @@ void view_autoconfigure(struct sway_view *view) {
}
struct sway_workspace *ws = view->container->workspace;
-
- bool other_views = false;
- if (config->hide_edge_borders == E_SMART) {
- struct sway_container *con = view->container;
- while (con) {
- enum sway_container_layout layout = container_parent_layout(con);
- if (layout != L_TABBED && layout != L_STACKED) {
- list_t *siblings = container_get_siblings(con);
- if (siblings && siblings->length > 1) {
- other_views = true;
- break;
- }
- }
- con = con->parent;
- }
- }
-
struct sway_container *con = view->container;
+ bool smart = config->hide_edge_borders == E_SMART ||
+ config->hide_edge_borders == E_SMART_NO_GAPS;
+ bool other_views = smart && !view_is_only_visible(view);
+ bool no_gaps = config->hide_edge_borders != E_SMART_NO_GAPS
+ || !gaps_to_edge(view);
+
view->border_top = view->border_bottom = true;
view->border_left = view->border_right = true;
if (config->hide_edge_borders == E_BOTH
|| config->hide_edge_borders == E_VERTICAL
- || (config->hide_edge_borders == E_SMART && !other_views)) {
- view->border_left = con->x != ws->x;
- int right_x = con->x + con->width;
+ || (smart && !other_views && no_gaps)) {
+ view->border_left = con->x - con->current_gaps != ws->x;
+ int right_x = con->x + con->width + con->current_gaps;
view->border_right = right_x != ws->x + ws->width;
}
if (config->hide_edge_borders == E_BOTH
|| config->hide_edge_borders == E_HORIZONTAL
- || (config->hide_edge_borders == E_SMART && !other_views)) {
- view->border_top = con->y != ws->y;
- int bottom_y = con->y + con->height;
+ || (smart && !other_views && no_gaps)) {
+ view->border_top = con->y - con->current_gaps != ws->y;
+ int bottom_y = con->y + con->height + con->current_gaps;
view->border_bottom = bottom_y != ws->y + ws->height;
}
@@ -1004,12 +1021,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) {
@@ -1023,10 +1044,6 @@ bool view_is_visible(struct sway_view *view) {
!container_is_fullscreen_or_child(view->container)) {
return false;
}
- // Check the workspace is visible
- if (!is_sticky) {
- return workspace_is_visible(workspace);
- }
return true;
}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 16031e87..e9e5dfa2 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
@@ -62,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);
@@ -121,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,
@@ -231,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;
}
}
@@ -629,19 +640,25 @@ void workspace_add_gaps(struct sway_workspace *ws) {
if (ws->current_gaps > 0) {
return;
}
- bool should_apply =
- config->edge_gaps || (config->smart_gaps && ws->tiling->length > 1);
- if (!should_apply) {
+ if (!config->edge_gaps) {
return;
}
+ if (config->smart_gaps) {
+ struct sway_seat *seat = input_manager_get_default_seat(input_manager);
+ struct sway_container *focus =
+ seat_get_focus_inactive_view(seat, &ws->node);
+ if (focus && focus->view && view_is_only_visible(focus->view)) {
+ 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 ab307fd4..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"
@@ -71,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) {
@@ -78,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,
@@ -324,27 +335,22 @@ static void output_geometry(void *data, struct wl_output *wl_output, int32_t x,
const char *make, const char *model, int32_t transform) {
struct swaybar_output *output = data;
output->subpixel = subpixel;
- if (output->surface) {
- render_frame(output->bar, output);
- }
}
-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 = {
@@ -380,7 +386,7 @@ static void xdg_output_handle_done(void *data,
wl_list_insert(&bar->outputs, &output->link);
add_layer_surface(output);
- render_frame(bar, output);
+ set_output_dirty(output);
}
}
@@ -469,23 +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) {
- if (output->surface != NULL) {
- 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);
}
@@ -525,7 +531,8 @@ void bar_setup(struct swaybar *bar,
assert(pointer->cursor_surface);
ipc_get_workspaces(bar);
- render_all_frames(bar);
+ set_bar_dirty(bar);
+ return true;
}
static void display_in(int fd, short mask, void *data) {
@@ -539,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);
}
}
@@ -547,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);
}
}
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 325aa61a..1d754808 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) {
@@ -258,7 +260,7 @@ 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) {
- wlr_log(WLR_DEBUG, "block %s clicked", block->name ? block->name : "(nil)");
+ wlr_log(WLR_DEBUG, "block %s clicked", block->name);
if (!block->name || !status->click_events) {
return HOTSPOT_PROCESS;
}
@@ -274,10 +276,11 @@ enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
json_object_object_add(event_json, "button", json_object_new_int(button));
json_object_object_add(event_json, "x", json_object_new_int(x));
json_object_object_add(event_json, "y", json_object_new_int(y));
- if (dprintf(status->write_fd, "%s,\n",
+ if (dprintf(status->write_fd, "%s%s\n", status->clicked ? "," : "",
json_object_to_json_string(event_json)) < 0) {
status_error(status, "[failed to write click event]");
}
+ status->clicked = true;
json_object_put(event_json);
return HOTSPOT_IGNORE;
}
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 9413dc57..dc31a5ea 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -10,6 +10,7 @@
#include "pool-buffer.h"
#include "swaybar/bar.h"
#include "swaybar/config.h"
+#include "swaybar/i3bar.h"
#include "swaybar/ipc.h"
#include "swaybar/render.h"
#include "swaybar/status_line.h"
@@ -20,47 +21,47 @@ static const double WS_VERTICAL_PADDING = 1.5;
static const double BORDER_WIDTH = 1;
static uint32_t render_status_line_error(cairo_t *cairo,
- struct swaybar_output *output, struct swaybar_config *config,
- const char *error, double *x, uint32_t surface_height) {
+ struct swaybar_output *output, double *x) {
+ const char *error = output->bar->status->text;
if (!error) {
return 0;
}
- uint32_t height = surface_height * output->scale;
+ uint32_t height = output->height * output->scale;
cairo_set_source_u32(cairo, 0xFF0000FF);
int margin = 3 * output->scale;
int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
+ char *font = output->bar->config->font;
int text_width, text_height;
- get_text_size(cairo, config->font, &text_width, &text_height, NULL,
+ get_text_size(cairo, font, &text_width, &text_height, NULL,
output->scale, false, "%s", error);
uint32_t ideal_height = text_height + ws_vertical_padding * 2;
uint32_t ideal_surface_height = ideal_height / output->scale;
- if (surface_height < ideal_surface_height) {
+ if (output->height < ideal_surface_height) {
return ideal_surface_height;
}
*x -= text_width + margin;
double text_y = height / 2.0 - text_height / 2.0;
cairo_move_to(cairo, *x, (int)floor(text_y));
- pango_printf(cairo, config->font, output->scale, false, "%s", error);
+ pango_printf(cairo, font, output->scale, false, "%s", error);
*x -= margin;
- return surface_height;
+ return output->height;
}
static uint32_t render_status_line_text(cairo_t *cairo,
- struct swaybar_output *output, struct swaybar_config *config,
- const char *text, bool focused, double *x, uint32_t surface_height) {
+ struct swaybar_output *output, double *x) {
+ const char *text = output->bar->status->text;
if (!text) {
return 0;
}
- uint32_t height = surface_height * output->scale;
-
- cairo_set_source_u32(cairo, focused ?
+ struct swaybar_config *config = output->bar->config;
+ cairo_set_source_u32(cairo, output->focused ?
config->colors.focused_statusline : config->colors.statusline);
int text_width, text_height;
@@ -72,17 +73,18 @@ static uint32_t render_status_line_text(cairo_t *cairo,
uint32_t ideal_height = text_height + ws_vertical_padding * 2;
uint32_t ideal_surface_height = ideal_height / output->scale;
- if (surface_height < ideal_surface_height) {
+ if (output->height < ideal_surface_height) {
return ideal_surface_height;
}
*x -= text_width + margin;
+ uint32_t height = output->height * output->scale;
double text_y = height / 2.0 - text_height / 2.0;
cairo_move_to(cairo, *x, (int)floor(text_y));
pango_printf(cairo, config->font, output->scale,
config->pango_markup, "%s", text);
*x -= margin;
- return surface_height;
+ return output->height;
}
static void render_sharp_line(cairo_t *cairo, uint32_t color,
@@ -122,12 +124,11 @@ static void i3bar_block_unref_callback(void *data) {
static uint32_t render_status_block(cairo_t *cairo,
struct swaybar_output *output, struct i3bar_block *block, double *x,
- uint32_t surface_height, bool focused, bool edge) {
+ bool edge) {
if (!block->full_text || !*block->full_text) {
return 0;
}
- uint32_t height = surface_height * output->scale;
struct swaybar_config *config = output->bar->config;
int text_width, text_height;
@@ -145,7 +146,7 @@ static uint32_t render_status_block(cairo_t *cairo,
double block_width = width;
uint32_t ideal_height = text_height + ws_vertical_padding * 2;
uint32_t ideal_surface_height = ideal_height / output->scale;
- if (surface_height < ideal_surface_height) {
+ if (output->height < ideal_surface_height) {
return ideal_surface_height;
}
@@ -166,7 +167,7 @@ static uint32_t render_status_block(cairo_t *cairo,
output->scale, false, "%s", config->sep_symbol);
uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
uint32_t _ideal_surface_height = _ideal_height / output->scale;
- if (surface_height < _ideal_surface_height) {
+ if (output->height < _ideal_surface_height) {
return _ideal_surface_height;
}
if (sep_width > block->separator_block_width) {
@@ -178,6 +179,7 @@ static uint32_t render_status_block(cairo_t *cairo,
*x -= margin;
}
+ uint32_t height = output->height * output->scale;
if (output->bar->status->click_events) {
struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
hotspot->x = *x;
@@ -241,7 +243,7 @@ static uint32_t render_status_block(cairo_t *cairo,
}
if (!edge && block->separator) {
- if (focused) {
+ if (output->focused) {
cairo_set_source_u32(cairo, config->colors.focused_separator);
} else {
cairo_set_source_u32(cairo, config->colors.separator);
@@ -260,19 +262,16 @@ static uint32_t render_status_block(cairo_t *cairo,
cairo_stroke(cairo);
}
}
- return surface_height;
+ return output->height;
}
static uint32_t render_status_line_i3bar(cairo_t *cairo,
- struct swaybar_config *config, struct swaybar_output *output,
- struct status_line *status, bool focused,
- double *x, uint32_t surface_height) {
+ struct swaybar_output *output, double *x) {
uint32_t max_height = 0;
bool edge = true;
struct i3bar_block *block;
- wl_list_for_each(block, &status->blocks, link) {
- uint32_t h = render_status_block(cairo, output,
- block, x, surface_height, focused, edge);
+ wl_list_for_each(block, &output->bar->status->blocks, link) {
+ uint32_t h = render_status_block(cairo, output, block, x, edge);
max_height = h > max_height ? h : max_height;
edge = false;
}
@@ -280,19 +279,15 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
}
static uint32_t render_status_line(cairo_t *cairo,
- struct swaybar_config *config, struct swaybar_output *output,
- struct status_line *status, bool focused,
- double *x, uint32_t surface_height) {
+ struct swaybar_output *output, double *x) {
+ struct status_line *status = output->bar->status;
switch (status->protocol) {
case PROTOCOL_ERROR:
- return render_status_line_error(cairo, output, config,
- status->text, x, surface_height);
+ return render_status_line_error(cairo, output, x);
case PROTOCOL_TEXT:
- return render_status_line_text(cairo, output, config,
- status->text, focused, x, surface_height);
+ return render_status_line_text(cairo, output, x);
case PROTOCOL_I3BAR:
- return render_status_line_i3bar(cairo, config, output,
- status, focused, x, surface_height);
+ return render_status_line_i3bar(cairo, output, x);
case PROTOCOL_UNDEF:
return 0;
}
@@ -300,10 +295,9 @@ static uint32_t render_status_line(cairo_t *cairo,
}
static uint32_t render_binding_mode_indicator(cairo_t *cairo,
- struct swaybar_output *output, struct swaybar_config *config,
- const char *mode, double x, uint32_t surface_height) {
- uint32_t height = surface_height * output->scale;
-
+ struct swaybar_output *output, double x) {
+ struct swaybar_config *config = output->bar->config;
+ const char *mode = config->mode;
int text_width, text_height;
get_text_size(cairo, config->font, &text_width, &text_height, NULL,
output->scale, config->mode_pango_markup,
@@ -316,11 +310,12 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
uint32_t ideal_height = text_height + ws_vertical_padding * 2
+ border_width * 2;
uint32_t ideal_surface_height = ideal_height / output->scale;
- if (surface_height < ideal_surface_height) {
+ if (output->height < ideal_surface_height) {
return ideal_surface_height;
}
uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
+ uint32_t height = output->height * output->scale;
cairo_set_source_u32(cairo, config->colors.binding_mode.background);
cairo_rectangle(cairo, x, 0, width, height);
cairo_fill(cairo);
@@ -340,7 +335,7 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
pango_printf(cairo, config->font, output->scale, config->mode_pango_markup,
"%s", mode);
- return surface_height;
+ return output->height;
}
static const char *strip_workspace_number(const char *ws_name) {
@@ -366,8 +361,9 @@ static enum hotspot_event_handling workspace_hotspot_callback(struct swaybar_out
}
static uint32_t render_workspace_button(cairo_t *cairo,
- struct swaybar_output *output, struct swaybar_config *config,
- struct swaybar_workspace *ws, double *x, uint32_t surface_height) {
+ struct swaybar_output *output,
+ struct swaybar_workspace *ws, double *x) {
+ struct swaybar_config *config = output->bar->config;
const char *name = ws->name;
if (config->strip_workspace_numbers) {
name = strip_workspace_number(ws->name);
@@ -384,7 +380,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,
box_colors = config->colors.inactive_workspace;
}
- uint32_t height = surface_height * output->scale;
+ uint32_t height = output->height * output->scale;
int text_width, text_height;
get_text_size(cairo, config->font, &text_width, &text_height, NULL,
@@ -397,7 +393,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,
uint32_t ideal_height = ws_vertical_padding * 2 + text_height
+ border_width * 2;
uint32_t ideal_surface_height = ideal_height / output->scale;
- if (surface_height < ideal_surface_height) {
+ if (output->height < ideal_surface_height) {
return ideal_surface_height;
}
@@ -434,11 +430,11 @@ static uint32_t render_workspace_button(cairo_t *cairo,
wl_list_insert(&output->hotspots, &hotspot->link);
*x += width;
- return surface_height;
+ return output->height;
}
-static uint32_t render_to_cairo(cairo_t *cairo,
- struct swaybar *bar, struct swaybar_output *output) {
+static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
+ struct swaybar *bar = output->bar;
struct swaybar_config *config = bar->config;
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
if (output->focused) {
@@ -458,29 +454,41 @@ 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;
@@ -506,9 +514,10 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
cairo_paint(cairo);
cairo_restore(cairo);
- uint32_t height = render_to_cairo(cairo, bar, output);
- if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) {
- height = bar->config->height;
+ uint32_t height = render_to_cairo(cairo, output);
+ int config_height = output->bar->config->height;
+ if (config_height >= 0 && height < (uint32_t)config_height) {
+ height = config_height;
}
if (height != output->height) {
// Reconfigure surface
@@ -519,11 +528,13 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
wl_surface_commit(output->surface);
} else if (height > 0) {
// Replay recording into shm and send it off
- output->current_buffer = get_next_buffer(bar->shm,
+ output->current_buffer = get_next_buffer(output->bar->shm,
output->buffers,
output->width * output->scale,
output->height * output->scale);
if (!output->current_buffer) {
+ cairo_surface_destroy(recorder);
+ cairo_destroy(cairo);
return;
}
cairo_t *shm = output->current_buffer->cairo;
@@ -541,6 +552,11 @@ 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);
}
cairo_surface_destroy(recorder);
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 48b43248..ed6dc7c8 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -1,12 +1,15 @@
#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
+#include <sys/ioctl.h>
#include <json-c/json.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <wlr/util/log.h>
+#include "swaybar/bar.h"
#include "swaybar/config.h"
+#include "swaybar/i3bar.h"
#include "swaybar/event_loop.h"
#include "swaybar/status_line.h"
#include "readline.h"
@@ -34,18 +37,35 @@ bool status_handle_readable(struct status_line *status) {
switch (status->protocol) {
case PROTOCOL_UNDEF:
errno = 0;
- read_bytes = getline(&status->buffer,
- &status->buffer_size, status->read);
- if (errno == EAGAIN) {
- clearerr(status->read);
- } else if (errno) {
+ int available_bytes;
+ if (ioctl(status->read_fd, FIONREAD, &available_bytes) == -1) {
+ wlr_log(WLR_ERROR, "Unable to read status command output size");
status_error(status, "[error reading from status command]");
return true;
}
+ if ((size_t)available_bytes + 1 > status->buffer_size) {
+ // need room for leading '\0' too
+ status->buffer_size = available_bytes + 1;
+ status->buffer = realloc(status->buffer, status->buffer_size);
+ }
+ if (status->buffer == NULL) {
+ wlr_log_errno(WLR_ERROR, "Unable to read status line");
+ status_error(status, "[error reading from status command]");
+ return true;
+ }
+
+ read_bytes = read(status->read_fd, status->buffer, available_bytes);
+ if (read_bytes != available_bytes) {
+ status_error(status, "[error reading from status command]");
+ return true;
+ }
+ status->buffer[available_bytes] = 0;
+
// the header must be sent completely the first time round
+ char *newline = strchr(status->buffer, '\n');
json_object *header, *version;
- if (status->buffer[read_bytes - 1] == '\n'
+ if (newline != NULL
&& (header = json_tokener_parse(status->buffer))
&& json_object_object_get_ex(header, "version", &version)
&& json_object_get_int(version) == 1) {
@@ -67,13 +87,9 @@ bool status_handle_readable(struct status_line *status) {
wl_list_init(&status->blocks);
status->tokener = json_tokener_new();
- read_bytes = getdelim(&status->buffer, &status->buffer_size, EOF, status->read);
- if (read_bytes > 0) {
- status->buffer_index = read_bytes;
- return i3bar_handle_readable(status);
- } else {
- return false;
- }
+ status->buffer_index = strlen(newline + 1);
+ memmove(status->buffer, newline + 1, status->buffer_index + 1);
+ return i3bar_handle_readable(status);
}
wlr_log(WLR_DEBUG, "Using text protocol.");
diff --git a/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 c25c8eec..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");
@@ -845,6 +845,9 @@ static int load_config(char *path, struct swaylock_state *state,
static struct swaylock_state state;
int main(int argc, char **argv) {
+ wlr_log_init(WLR_DEBUG, NULL);
+ initialize_pw_backend();
+
enum line_mode line_mode = LM_LINE;
state.args = (struct swaylock_args){
.mode = BACKGROUND_MODE_SOLID_COLOR,
@@ -857,8 +860,6 @@ int main(int argc, char **argv) {
wl_list_init(&state.images);
set_default_colors(&state.args.colors);
- wlr_log_init(WLR_DEBUG, NULL);
-
char *config_path = NULL;
int result = parse_options(argc, argv, NULL, NULL, &config_path);
if (result != 0) {
diff --git a/swaylock/meson.build b/swaylock/meson.build
index 675b8c69..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/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
)