diff options
131 files changed, 2642 insertions, 992 deletions
| diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8a6c0208..f450563a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -107,7 +107,7 @@ int main(int argc, const char **argv) {  	}  	int desired_output = atoi(argv[1]); -	sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length); +	sway_log(WLR_INFO, "Using output %d of %d", desired_output, registry->outputs->length);  	int i;  	struct output_state *output = registry->outputs->items[desired_output];  	struct window *window = window_setup(registry, 100, 100, false); @@ -3,9 +3,9 @@  Use `sway_log(importance, fmt, ...)` to log. The following importances are  available: -* `L_DEBUG`: Debug messages, only shows with `sway -d` -* `L_INFO`: Informational messages -* `L_ERROR`: Error messages +* `WLR_DEBUG`: Debug messages, only shows with `sway -d` +* `WLR_INFO`: Informational messages +* `WLR_ERROR`: Error messages  `sway_log` is a macro that calls `_sway_log` with the current filename and line  number, which are written into the log with your message. diff --git a/common/background-image.c b/common/background-image.c index e5fb4433..f3d2551e 100644 --- a/common/background-image.c +++ b/common/background-image.c @@ -18,7 +18,7 @@ enum background_mode parse_background_mode(const char *mode) {  	} else if (strcmp(mode, "solid_color") == 0) {  		return BACKGROUND_MODE_SOLID_COLOR;  	} -	wlr_log(L_ERROR, "Unsupported background mode: %s", mode); +	wlr_log(WLR_ERROR, "Unsupported background mode: %s", mode);  	return BACKGROUND_MODE_INVALID;  } @@ -28,7 +28,7 @@ cairo_surface_t *load_background_image(const char *path) {  	GError *err = NULL;  	GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);  	if (!pixbuf) { -		wlr_log(L_ERROR, "Failed to load background image (%s).", +		wlr_log(WLR_ERROR, "Failed to load background image (%s).",  				err->message);  		return false;  	} @@ -38,11 +38,11 @@ cairo_surface_t *load_background_image(const char *path) {  	image = cairo_image_surface_create_from_png(path);  #endif //HAVE_GDK_PIXBUF  	if (!image) { -		wlr_log(L_ERROR, "Failed to read background image."); +		wlr_log(WLR_ERROR, "Failed to read background image.");  		return NULL;  	}  	if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { -		wlr_log(L_ERROR, "Failed to read background image: %s." +		wlr_log(WLR_ERROR, "Failed to read background image: %s."  #ifndef HAVE_GDK_PIXBUF  				"\nSway was compiled without gdk_pixbuf support, so only"  				"\nPNG images can be loaded. This is the likely cause." diff --git a/common/ipc-client.c b/common/ipc-client.c index a88df080..4d2d88cc 100644 --- a/common/ipc-client.c +++ b/common/ipc-client.c @@ -97,7 +97,7 @@ struct ipc_response *ipc_recv_response(int socketfd) {  error_2:  	free(response);  error_1: -	wlr_log(L_ERROR, "Unable to allocate memory for IPC response"); +	wlr_log(WLR_ERROR, "Unable to allocate memory for IPC response");  	return NULL;  } diff --git a/common/list.c b/common/list.c index 39cc10e1..66d52f70 100644 --- a/common/list.c +++ b/common/list.c @@ -2,6 +2,7 @@  #include <stdio.h>  #include <stdlib.h>  #include <string.h> +#include "log.h"  list_t *create_list(void) {  	list_t *list = malloc(sizeof(list_t)); @@ -82,6 +83,20 @@ void list_swap(list_t *list, int src, int dest) {  	list->items[dest] = tmp;  } +void list_move_to_end(list_t *list, void *item) { +	int i; +	for (i = 0; i < list->length; ++i) { +		if (list->items[i] == item) { +			break; +		} +	} +	if (!sway_assert(i < list->length, "Item not found in list")) { +		return; +	} +	list_del(list, i); +	list_add(list, item); +} +  static void list_rotate(list_t *list, int from, int to) {  	void *tmp = list->items[to]; diff --git a/common/log.c b/common/log.c index 2cc7289c..847f3952 100644 --- a/common/log.c +++ b/common/log.c @@ -8,7 +8,7 @@ void sway_terminate(int code);  void _sway_abort(const char *format, ...) {  	va_list args;  	va_start(args, format); -	_wlr_vlog(L_ERROR, format, args); +	_wlr_vlog(WLR_ERROR, format, args);  	va_end(args);  	sway_terminate(EXIT_FAILURE);  } @@ -20,7 +20,7 @@ bool _sway_assert(bool condition, const char *format, ...) {  	va_list args;  	va_start(args, format); -	_wlr_vlog(L_ERROR, format, args); +	_wlr_vlog(WLR_ERROR, format, args);  	va_end(args);  #ifndef NDEBUG diff --git a/common/pango.c b/common/pango.c index c88e50ce..92703f80 100644 --- a/common/pango.c +++ b/common/pango.c @@ -81,7 +81,7 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,  			pango_layout_set_markup(layout, buf, -1);  			free(buf);  		} else { -			wlr_log(L_ERROR, "pango_parse_markup '%s' -> error %s", text, +			wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", text,  					error->message);  			g_error_free(error);  			markup = false; // fallback to plain text diff --git a/common/readline.c b/common/readline.c index 1c396a90..a2c69018 100644 --- a/common/readline.c +++ b/common/readline.c @@ -9,7 +9,7 @@ char *read_line(FILE *file) {  	char *string = malloc(size);  	char lastChar = '\0';  	if (!string) { -		wlr_log(L_ERROR, "Unable to allocate memory for read_line"); +		wlr_log(WLR_ERROR, "Unable to allocate memory for read_line");  		return NULL;  	}  	while (1) { @@ -30,7 +30,7 @@ char *read_line(FILE *file) {  			char *new_string = realloc(string, size *= 2);  			if (!new_string) {  				free(string); -				wlr_log(L_ERROR, "Unable to allocate memory for read_line"); +				wlr_log(WLR_ERROR, "Unable to allocate memory for read_line");  				return NULL;  			}  			string = new_string; diff --git a/common/util.c b/common/util.c index 678926ed..e8a88772 100644 --- a/common/util.c +++ b/common/util.c @@ -113,7 +113,7 @@ uint32_t parse_color(const char *color) {  	int len = strlen(color);  	if (len != 6 && len != 8) { -		wlr_log(L_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color); +		wlr_log(WLR_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color);  		return 0xFFFFFFFF;  	}  	uint32_t res = (uint32_t)strtoul(color, NULL, 16); diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index 6bb03279..2e39deb6 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -22,7 +22,6 @@ types=(  'get_marks'  'get_bar_config'  'get_version' -'get_clipboard'  )  _arguments -s \ diff --git a/include/ipc.h b/include/ipc.h index 8172c782..0010718b 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -13,11 +13,12 @@ enum ipc_command_type {  	IPC_GET_MARKS = 5,  	IPC_GET_BAR_CONFIG = 6,  	IPC_GET_VERSION = 7, +	IPC_GET_BINDING_MODES = 8, +	IPC_GET_CONFIG = 9,  	// sway-specific command types  	IPC_GET_INPUTS = 100, -	IPC_GET_CLIPBOARD = 101, -	IPC_GET_SEATS = 102, +	IPC_GET_SEATS = 101,  	// Events sent from sway to clients. Events have the highest bits set.  	IPC_EVENT_WORKSPACE = ((1<<31) | 0), diff --git a/include/list.h b/include/list.h index 7eead4ac..5a0d7d80 100644 --- a/include/list.h +++ b/include/list.h @@ -24,4 +24,6 @@ int list_seq_find(list_t *list, int compare(const void *item, const void *cmp_to  void list_stable_sort(list_t *list, int compare(const void *a, const void *b));  // swap two elements in a list  void list_swap(list_t *list, int src, int dest); +// move item to end of list +void list_move_to_end(list_t *list, void *item);  #endif diff --git a/include/log.h b/include/log.h index a9748127..dd526143 100644 --- a/include/log.h +++ b/include/log.h @@ -3,13 +3,19 @@  #include <stdbool.h>  #include <wlr/util/log.h> +#ifdef __GNUC__ +#define ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end))) +#else +#define ATTRIB_PRINTF(start, end) +#endif +  void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2);  #define sway_abort(FMT, ...) \ -    _sway_abort("[%s:%d] " FMT, wlr_strip_path(__FILE__), __LINE__, ##__VA_ARGS__) +    _sway_abort("[%s:%d] " FMT, _wlr_strip_path(__FILE__), __LINE__, ##__VA_ARGS__)  bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3);  #define sway_assert(COND, FMT, ...) \ -	_sway_assert(COND, "[%s:%d] %s:" FMT, wlr_strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__) +	_sway_assert(COND, "[%s:%d] %s:" FMT, _wlr_strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)  void error_handler(int sig); diff --git a/include/sway/commands.h b/include/sway/commands.h index 6d17144a..1e93e2a3 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -95,7 +95,6 @@ sway_cmd cmd_client_unfocused;  sway_cmd cmd_client_urgent;  sway_cmd cmd_client_placeholder;  sway_cmd cmd_client_background; -sway_cmd cmd_clipboard;  sway_cmd cmd_commands;  sway_cmd cmd_debuglog;  sway_cmd cmd_default_border; @@ -153,6 +152,7 @@ sway_cmd cmd_swaybg_command;  sway_cmd cmd_swap;  sway_cmd cmd_title_format;  sway_cmd cmd_unmark; +sway_cmd cmd_urgent;  sway_cmd cmd_workspace;  sway_cmd cmd_ws_auto_back_and_forth;  sway_cmd cmd_workspace_layout; @@ -208,8 +208,10 @@ sway_cmd input_cmd_natural_scroll;  sway_cmd input_cmd_pointer_accel;  sway_cmd input_cmd_repeat_delay;  sway_cmd input_cmd_repeat_rate; +sway_cmd input_cmd_scroll_button;  sway_cmd input_cmd_scroll_method;  sway_cmd input_cmd_tap; +sway_cmd input_cmd_tap_button_map;  sway_cmd input_cmd_xkb_layout;  sway_cmd input_cmd_xkb_model;  sway_cmd input_cmd_xkb_options; diff --git a/include/sway/config.h b/include/sway/config.h index ac668c24..f660a269 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -75,9 +75,11 @@ struct input_config {  	float pointer_accel;  	int repeat_delay;  	int repeat_rate; +	int scroll_button;  	int scroll_method;  	int send_events;  	int tap; +	int tap_button_map;  	char *xkb_layout;  	char *xkb_model; @@ -271,11 +273,10 @@ enum ipc_feature {  	IPC_FEATURE_EVENT_WINDOW = 2048,  	IPC_FEATURE_EVENT_BINDING = 4096,  	IPC_FEATURE_EVENT_INPUT = 8192, -	IPC_FEATURE_GET_CLIPBOARD = 16384, -	IPC_FEATURE_GET_SEATS = 32768, +	IPC_FEATURE_GET_SEATS = 16384,  	IPC_FEATURE_ALL_COMMANDS = -		1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 16384 | 32768, +		1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 16384,  	IPC_FEATURE_ALL_EVENTS = 256 | 512 | 1024 | 2048 | 4096 | 8192,  	IPC_FEATURE_ALL = IPC_FEATURE_ALL_COMMANDS | IPC_FEATURE_ALL_EVENTS, @@ -341,6 +342,7 @@ struct sway_config {  	int gaps_outer;  	list_t *config_chain; +	const char *current_config_path;  	const char *current_config;  	enum sway_container_border border; @@ -496,7 +498,4 @@ void config_update_font_height(bool recalculate);  /* Global config singleton. */  extern struct sway_config *config; -/* Config file currently being read */ -extern const char *current_config_path; -  #endif diff --git a/include/sway/criteria.h b/include/sway/criteria.h index bd3ca0ac..6a8337c5 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -6,9 +6,10 @@  #include "tree/view.h"  enum criteria_type { -	CT_COMMAND = 1 << 0, -	CT_ASSIGN_OUTPUT = 1 << 1, +	CT_COMMAND          = 1 << 0, +	CT_ASSIGN_OUTPUT    = 1 << 1,  	CT_ASSIGN_WORKSPACE = 1 << 2, +	CT_NO_FOCUS         = 1 << 3,  };  struct criteria { diff --git a/include/sway/debug.h b/include/sway/debug.h index 2430d319..38d4eccd 100644 --- a/include/sway/debug.h +++ b/include/sway/debug.h @@ -1,7 +1,15 @@  #ifndef SWAY_DEBUG_H  #define SWAY_DEBUG_H +// Tree  extern bool enable_debug_tree;  void update_debug_tree(); +// Damage +extern const char *damage_debug; + +// Transactions +extern int txn_timeout_ms; +extern bool txn_debug; +  #endif diff --git a/include/sway/desktop.h b/include/sway/desktop.h index f1ad759a..348fb187 100644 --- a/include/sway/desktop.h +++ b/include/sway/desktop.h @@ -1,4 +1,8 @@  #include <wlr/types/wlr_surface.h> +struct sway_container; +  void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,  	bool whole); + +void desktop_damage_whole_container(struct sway_container *con); diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 7ab80eb8..cee4afed 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -6,34 +6,25 @@  /**   * Transactions enable us to perform atomic layout updates.   * - * When we want to make adjustments to the layout, we create a transaction. - * A transaction contains a list of affected containers and their new state. + * A transaction contains a list of containers and their new state.   * A state might contain a new size, or new border settings, or new parent/child   * relationships.   * - * Calling transaction_commit() makes sway notify of all the affected clients - * with their new sizes. We then wait for all the views to respond with their - * new surface sizes. When all are ready, or when a timeout has passed, we apply - * the updates all at the same time. - */ - -struct sway_transaction; - -/** - * Create a new transaction. - */ -struct sway_transaction *transaction_create(void); - -/** - * Add a container's pending state to the transaction. + * Committing a transaction makes sway notify of all the affected clients with + * their new sizes. We then wait for all the views to respond with their new + * surface sizes. When all are ready, or when a timeout has passed, we apply the + * updates all at the same time. + * + * When we want to make adjustments to the layout, we change the pending state + * in containers, mark them as dirty and call transaction_commit_dirty(). This + * create and commits a transaction from the dirty containers.   */ -void transaction_add_container(struct sway_transaction *transaction, -		struct sway_container *container);  /** - * Submit a transaction to the client views for configuration. + * Find all dirty containers, create and commit a transaction containing them, + * and unmark them as dirty.   */ -void transaction_commit(struct sway_transaction *transaction); +void transaction_commit_dirty(void);  /**   * Notify the transaction system that a view is ready for the new layout. diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 0e440701..1f7792ba 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -119,17 +119,6 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat,  		struct sway_container *container);  /** - * Return the immediate child of container which was most recently focused, with - * fallback to selecting the child in the parent's `current` (rendered) children - * list. - * - * This is useful for when a tabbed container and its children are destroyed but - * still being rendered, and we have to render an appropriate child. - */ -struct sway_container *seat_get_active_current_child(struct sway_seat *seat, -		struct sway_container *container); - -/**   * Iterate over the focus-inactive children of the container calling the   * function on each.   */ diff --git a/include/sway/server.h b/include/sway/server.h index a3e32898..a017d1c4 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -47,10 +47,7 @@ struct sway_server {  	bool debug_txn_timings;  	list_t *transactions; - -	// When a view is being destroyed and is waiting for a transaction to -	// complete it will be stored here. -	list_t *destroying_containers; +	list_t *dirty_containers;  };  struct sway_server server; diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h index 58235642..d6abcc81 100644 --- a/include/sway/tree/arrange.h +++ b/include/sway/tree/arrange.h @@ -11,26 +11,8 @@ void remove_gaps(struct sway_container *c);  void add_gaps(struct sway_container *c);  /** - * Arrange layout for all the children of the given container, and add them to - * the given transaction. - * - * Use this function if you need to arrange multiple sections of the tree in one - * transaction. - * - * You must set the desired state of the container before calling - * arrange_windows, then don't change any state-tracked properties in the - * container until you've called transaction_commit. + * Arrange layout for all the children of the given container.   */ -void arrange_windows(struct sway_container *container, -		struct sway_transaction *transaction); - -/** - * Arrange layout for the given container and commit the transaction. - * - * This function is a wrapper around arrange_windows, and handles creating and - * committing the transaction for you. Use this function if you're only doing - * one arrange operation. - */ -void arrange_and_commit(struct sway_container *container); +void arrange_windows(struct sway_container *container);  #endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 728daa84..ca7a3288 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -68,6 +68,9 @@ struct sway_container_state {  	struct sway_container *parent;  	list_t *children; +	struct sway_container *focused_inactive_child; +	bool focused; +  	// View properties  	double view_x, view_y;  	double view_width, view_height; @@ -144,6 +147,10 @@ struct sway_container {  	bool destroying; +	// If true, indicates that the container has pending state that differs from +	// the current. +	bool dirty; +  	struct {  		struct wl_signal destroy;  		// Raised after the tree updates, but before arrange_windows @@ -297,4 +304,18 @@ bool container_is_floating(struct sway_container *container);   */  void container_get_box(struct sway_container *container, struct wlr_box *box); +/** + * Move a floating container to a new layout-local position. + */ +void container_floating_move_to(struct sway_container *con, +		double lx, double ly); + +/** + * Mark a container as dirty if it isn't already. Dirty containers will be + * included in the next transaction then unmarked as dirty. + */ +void container_set_dirty(struct sway_container *container); + +bool container_has_urgent_child(struct sway_container *container); +  #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 7dc8ac46..e270f851 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -35,6 +35,7 @@ struct sway_view_impl {  	void (*set_tiled)(struct sway_view *view, bool tiled);  	void (*set_fullscreen)(struct sway_view *view, bool fullscreen);  	bool (*wants_floating)(struct sway_view *view); +	bool (*has_client_side_decorations)(struct sway_view *view);  	void (*for_each_surface)(struct sway_view *view,  		wlr_surface_iterator_func_t iterator, void *user_data);  	void (*close)(struct sway_view *view); @@ -68,6 +69,11 @@ struct sway_view {  	bool border_bottom;  	bool border_left;  	bool border_right; +	bool using_csd; + +	struct timespec urgent; +	bool allow_request_urgent; +	struct wl_event_source *urgent_timer;  	bool destroying; @@ -304,4 +310,8 @@ void view_update_marks_textures(struct sway_view *view);   */  bool view_is_visible(struct sway_view *view); +void view_set_urgent(struct sway_view *view, bool enable); + +bool view_is_urgent(struct sway_view *view); +  #endif diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index c72a4ac0..bc95317a 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -10,6 +10,7 @@ struct sway_workspace {  	struct sway_view *fullscreen;  	struct sway_container *floating;  	list_t *output_priority; +	bool urgent;  };  extern char *prev_workspace_name; @@ -42,4 +43,7 @@ void workspace_output_add_priority(struct sway_container *workspace,  struct sway_container *workspace_output_get_highest_available(  		struct sway_container *ws, struct sway_container *exclude); + +void workspace_detect_urgent(struct sway_container *workspace); +  #endif diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index af478f33..f1ff25b2 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -16,11 +16,24 @@ struct swaybar_pointer {  	int x, y;  }; +enum x11_button { +	NONE, +	LEFT, +	MIDDLE, +	RIGHT, +	SCROLL_UP, +	SCROLL_DOWN, +	SCROLL_LEFT, +	SCROLL_RIGHT, +	BACK, +	FORWARD, +}; +  struct swaybar_hotspot {  	struct wl_list link;  	int x, y, width, height;  	void (*callback)(struct swaybar_output *output, -			int x, int y, uint32_t button, void *data); +			int x, int y, enum x11_button button, void *data);  	void (*destroy)(void *data);  	void *data;  }; diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h index bf12a842..2eaf8140 100644 --- a/include/swaybar/status_line.h +++ b/include/swaybar/status_line.h @@ -72,7 +72,9 @@ bool status_handle_readable(struct status_line *status);  void status_line_free(struct status_line *status);  bool i3bar_handle_readable(struct status_line *status);  void i3bar_block_send_click(struct status_line *status, -		struct i3bar_block *block, int x, int y, uint32_t button); +		struct i3bar_block *block, int x, int y, enum x11_button button);  void i3bar_block_free(struct i3bar_block *block); +enum x11_button wl_button_to_x11_button(uint32_t button); +enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value);  #endif diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index cf80a6ba..950cfaaf 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -19,9 +19,31 @@ enum auth_state {  	AUTH_STATE_INVALID,  }; +struct swaylock_colorset { +	uint32_t input; +	uint32_t cleared; +	uint32_t verifying; +	uint32_t wrong; +}; + +struct swaylock_colors { +	uint32_t background; +	uint32_t bs_highlight; +	uint32_t key_highlight; +	uint32_t separator; +	struct swaylock_colorset inside; +	struct swaylock_colorset line; +	struct swaylock_colorset ring; +	struct swaylock_colorset text; +}; +  struct swaylock_args { -	uint32_t color; +	struct swaylock_colors colors;  	enum background_mode mode; +	char *font; +	uint32_t radius; +	uint32_t thickness; +	bool ignore_empty;  	bool show_indicator;  	bool daemonize;  }; diff --git a/sway/commands.c b/sway/commands.c index ef477f38..a3e6a500 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -98,8 +98,11 @@ static struct cmd_handler handlers[] = {  	{ "client.unfocused", cmd_client_unfocused },  	{ "client.urgent", cmd_client_urgent },  	{ "default_border", cmd_default_border }, +	{ "default_floating_border", cmd_default_floating_border },  	{ "exec", cmd_exec },  	{ "exec_always", cmd_exec_always }, +	{ "floating_maximum_size", cmd_floating_maximum_size }, +	{ "floating_minimum_size", cmd_floating_minimum_size },  	{ "focus_follows_mouse", cmd_focus_follows_mouse },  	{ "focus_wrapping", cmd_focus_wrapping },  	{ "font", cmd_font }, @@ -112,6 +115,7 @@ static struct cmd_handler handlers[] = {  	{ "input", cmd_input },  	{ "mode", cmd_mode },  	{ "mouse_warping", cmd_mouse_warping }, +	{ "no_focus", cmd_no_focus },  	{ "output", cmd_output },  	{ "seat", cmd_seat },  	{ "set", cmd_set }, @@ -151,6 +155,7 @@ static struct cmd_handler command_handlers[] = {  	{ "swap", cmd_swap },  	{ "title_format", cmd_title_format },  	{ "unmark", cmd_unmark }, +	{ "urgent", cmd_urgent },  };  static int handler_compare(const void *_a, const void *_b) { @@ -163,7 +168,7 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers,  		int handlers_size) {  	struct cmd_handler d = { .command=line };  	struct cmd_handler *res = NULL; -	wlr_log(L_DEBUG, "find_handler(%s)", line); +	wlr_log(WLR_DEBUG, "find_handler(%s)", line);  	bool config_loading = config->reading || !config->active; @@ -248,10 +253,10 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {  			cmd = argsep(&cmdlist, ",");  			cmd += strspn(cmd, whitespace);  			if (strcmp(cmd, "") == 0) { -				wlr_log(L_INFO, "Ignoring empty command."); +				wlr_log(WLR_INFO, "Ignoring empty command.");  				continue;  			} -			wlr_log(L_INFO, "Handling command '%s'", cmd); +			wlr_log(WLR_INFO, "Handling command '%s'", cmd);  			//TODO better handling of argv  			int argc;  			char **argv = split_args(cmd, &argc); @@ -344,7 +349,7 @@ struct cmd_results *config_command(char *exec) {  	// Start block  	if (argc > 1 && strcmp(argv[argc - 1], "{") == 0) { -		char *block = join_args(argv, argc - 1);  +		char *block = join_args(argv, argc - 1);  		results = cmd_results_new(CMD_BLOCK, block, NULL);  		free(block);  		goto cleanup; @@ -355,7 +360,7 @@ struct cmd_results *config_command(char *exec) {  		results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);  		goto cleanup;  	} -	wlr_log(L_INFO, "handling config command '%s'", exec); +	wlr_log(WLR_INFO, "handling config command '%s'", exec);  	struct cmd_handler *handler = find_handler(argv[0], NULL, 0);  	if (!handler) {  		char *input = argv[0] ? argv[0] : "(empty)"; @@ -388,7 +393,7 @@ cleanup:  struct cmd_results *config_subcommand(char **argv, int argc,  		struct cmd_handler *handlers, size_t handlers_size) {  	char *command = join_args(argv, argc); -	wlr_log(L_DEBUG, "Subcommand: %s", command); +	wlr_log(WLR_DEBUG, "Subcommand: %s", command);  	free(command);  	struct cmd_handler *handler = find_handler(argv[0], handlers, @@ -479,7 +484,7 @@ struct cmd_results *config_commands_command(char *exec) {  	}  	policy->context = context; -	wlr_log(L_INFO, "Set command policy for %s to %d", +	wlr_log(WLR_INFO, "Set command policy for %s to %d",  			policy->command, policy->context);  	results = cmd_results_new(CMD_SUCCESS, NULL, NULL); @@ -493,7 +498,7 @@ struct cmd_results *cmd_results_new(enum cmd_status status,  		const char *input, const char *format, ...) {  	struct cmd_results *results = malloc(sizeof(struct cmd_results));  	if (!results) { -		wlr_log(L_ERROR, "Unable to allocate command results"); +		wlr_log(WLR_ERROR, "Unable to allocate command results");  		return NULL;  	}  	results->status = status; diff --git a/sway/commands/assign.c b/sway/commands/assign.c index a90498ce..0bc0929a 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -45,7 +45,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) {  	criteria->target = join_args(argv, target_len);  	list_add(config->criteria, criteria); -	wlr_log(L_DEBUG, "assign: '%s' -> '%s' added", criteria->raw, +	wlr_log(WLR_DEBUG, "assign: '%s' -> '%s' added", criteria->raw,  			criteria->target);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/bar.c b/sway/commands/bar.c index d84ce808..f6a70c17 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -63,13 +63,13 @@ struct cmd_results *cmd_bar(int argc, char **argv) {  			for (int i = 0; i < config->bars->length; ++i) {  				struct bar_config *item = config->bars->items[i];  				if (strcmp(item->id, argv[0]) == 0) { -					wlr_log(L_DEBUG, "Selecting bar: %s", argv[0]); +					wlr_log(WLR_DEBUG, "Selecting bar: %s", argv[0]);  					bar = item;  					break;  				}  			}  			if (!bar) { -				wlr_log(L_DEBUG, "Creating bar: %s", argv[0]); +				wlr_log(WLR_DEBUG, "Creating bar: %s", argv[0]);  				bar = default_bar_config();  				if (!bar) {  					return cmd_results_new(CMD_FAILURE, "bar", @@ -108,7 +108,7 @@ struct cmd_results *cmd_bar(int argc, char **argv) {  		// Set current bar  		config->current_bar = bar; -		wlr_log(L_DEBUG, "Creating bar %s", bar->id); +		wlr_log(WLR_DEBUG, "Creating bar %s", bar->id);  	}  	return config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers)); diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c index 3ba5f33f..0c48bee9 100644 --- a/sway/commands/bar/binding_mode_indicator.c +++ b/sway/commands/bar/binding_mode_indicator.c @@ -15,11 +15,11 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {  	}  	if (strcasecmp("yes", argv[0]) == 0) {  		config->current_bar->binding_mode_indicator = true; -		wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", +		wlr_log(WLR_DEBUG, "Enabling binding mode indicator on bar: %s",  				config->current_bar->id);  	} else if (strcasecmp("no", argv[0]) == 0) {  		config->current_bar->binding_mode_indicator = false; -		wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", +		wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s",  				config->current_bar->id);  	}  	return cmd_results_new(CMD_INVALID, "binding_mode_indicator", diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index f036cbc3..2aa4e895 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c @@ -15,7 +15,7 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) {  	char *font = join_args(argv, argc);  	free(config->current_bar->font);  	config->current_bar->font = font; -	wlr_log(L_DEBUG, "Settings font '%s' for bar: %s", +	wlr_log(WLR_DEBUG, "Settings font '%s' for bar: %s",  			config->current_bar->font, config->current_bar->id);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/bar/height.c b/sway/commands/bar/height.c index 3160caed..18258526 100644 --- a/sway/commands/bar/height.c +++ b/sway/commands/bar/height.c @@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_height(int argc, char **argv) {  				"Invalid height value: %s", argv[0]);  	}  	config->current_bar->height = height; -	wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s", +	wlr_log(WLR_DEBUG, "Setting bar height to %d on bar: %s",  			height, config->current_bar->id);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c index 6641f184..502ce2c4 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c @@ -27,7 +27,7 @@ static struct cmd_results *bar_set_hidden_state(struct bar_config *bar,  		if (!config->reading) {  			ipc_event_barconfig_update(bar);  		} -		wlr_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", +		wlr_log(WLR_DEBUG, "Setting hidden_state: '%s' for bar: %s",  				bar->hidden_state, bar->id);  	}  	// free old mode diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c index 6ce86fef..65fa69fd 100644 --- a/sway/commands/bar/id.c +++ b/sway/commands/bar/id.c @@ -24,7 +24,7 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) {  		}  	} -	wlr_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name); +	wlr_log(WLR_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name);  	// free old bar id  	free(config->current_bar->id); diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 34bb0a4f..28e2d77b 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -28,7 +28,7 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode  		if (!config->reading) {  			ipc_event_barconfig_update(bar);  		} -		wlr_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); +		wlr_log(WLR_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id);  	}  	// free old mode diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c index 02f845e6..09025fff 100644 --- a/sway/commands/bar/modifier.c +++ b/sway/commands/bar/modifier.c @@ -30,7 +30,7 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) {  	}  	free_flat_list(split);  	config->current_bar->modifier = mod; -	wlr_log(L_DEBUG, +	wlr_log(WLR_DEBUG,  			"Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c index f7ca0aa4..72754e05 100644 --- a/sway/commands/bar/output.c +++ b/sway/commands/bar/output.c @@ -42,7 +42,7 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {  	if (add_output) {  		list_add(outputs, strdup(output)); -		wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'", +		wlr_log(WLR_DEBUG, "Adding bar: '%s' to output '%s'",  				config->current_bar->id, output);  	}  	return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c index 480af724..857571fb 100644 --- a/sway/commands/bar/pango_markup.c +++ b/sway/commands/bar/pango_markup.c @@ -13,11 +13,11 @@ struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) {  	}  	if (strcasecmp("enabled", argv[0]) == 0) {  		config->current_bar->pango_markup = true; -		wlr_log(L_DEBUG, "Enabling pango markup for bar: %s", +		wlr_log(WLR_DEBUG, "Enabling pango markup for bar: %s",  				config->current_bar->id);  	} else if (strcasecmp("disabled", argv[0]) == 0) {  		config->current_bar->pango_markup = false; -		wlr_log(L_DEBUG, "Disabling pango markup for bar: %s", +		wlr_log(WLR_DEBUG, "Disabling pango markup for bar: %s",  				config->current_bar->id);  	} else {  		error = cmd_results_new(CMD_INVALID, "pango_markup", diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c index 9c580483..44bb4ae3 100644 --- a/sway/commands/bar/position.c +++ b/sway/commands/bar/position.c @@ -15,8 +15,9 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) {  	char *valid[] = { "top", "bottom", "left", "right" };  	for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) {  		if (strcasecmp(valid[i], argv[0]) == 0) { -			wlr_log(L_DEBUG, "Setting bar position '%s' for bar: %s", +			wlr_log(WLR_DEBUG, "Setting bar position '%s' for bar: %s",  					argv[0], config->current_bar->id); +			free(config->current_bar->position);  			config->current_bar->position = strdup(argv[0]);  			return cmd_results_new(CMD_SUCCESS, NULL, NULL);  		} diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c index 1e08df6d..392ab730 100644 --- a/sway/commands/bar/separator_symbol.c +++ b/sway/commands/bar/separator_symbol.c @@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) {  	}  	free(config->current_bar->separator_symbol);  	config->current_bar->separator_symbol = strdup(argv[0]); -	wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", +	wlr_log(WLR_DEBUG, "Settings separator_symbol '%s' for bar: %s",  			config->current_bar->separator_symbol, config->current_bar->id);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c index 5e199cde..6f6f81a3 100644 --- a/sway/commands/bar/status_command.c +++ b/sway/commands/bar/status_command.c @@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) {  	}  	free(config->current_bar->status_command);  	config->current_bar->status_command = join_args(argv, argc); -	wlr_log(L_DEBUG, "Feeding bar with status command: %s", +	wlr_log(WLR_DEBUG, "Feeding bar with status command: %s",  			config->current_bar->status_command);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c index 4f24a356..4e47d047 100644 --- a/sway/commands/bar/strip_workspace_numbers.c +++ b/sway/commands/bar/strip_workspace_numbers.c @@ -15,11 +15,11 @@ struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {  	}  	if (strcasecmp("yes", argv[0]) == 0) {  		config->current_bar->strip_workspace_numbers = true; -		wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s", +		wlr_log(WLR_DEBUG, "Stripping workspace numbers on bar: %s",  				config->current_bar->id);  	} else if (strcasecmp("no", argv[0]) == 0) {  		config->current_bar->strip_workspace_numbers = false; -		wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s", +		wlr_log(WLR_DEBUG, "Enabling workspace numbers on bar: %s",  				config->current_bar->id);  	} else {  		return cmd_results_new(CMD_INVALID, diff --git a/sway/commands/bar/swaybar_command.c b/sway/commands/bar/swaybar_command.c index 520cdd11..04e78e77 100644 --- a/sway/commands/bar/swaybar_command.c +++ b/sway/commands/bar/swaybar_command.c @@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) {  	}  	free(config->current_bar->swaybar_command);  	config->current_bar->swaybar_command = join_args(argv, argc); -	wlr_log(L_DEBUG, "Using custom swaybar command: %s", +	wlr_log(WLR_DEBUG, "Using custom swaybar command: %s",  			config->current_bar->swaybar_command);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c index 6edc3a0d..a4079b2a 100644 --- a/sway/commands/bar/workspace_buttons.c +++ b/sway/commands/bar/workspace_buttons.c @@ -14,11 +14,11 @@ struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) {  	}  	if (strcasecmp("yes", argv[0]) == 0) {  		config->current_bar->workspace_buttons = true; -		wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s", +		wlr_log(WLR_DEBUG, "Enabling workspace buttons on bar: %s",  				config->current_bar->id);  	} else if (strcasecmp("no", argv[0]) == 0) {  		config->current_bar->workspace_buttons = false; -		wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s", +		wlr_log(WLR_DEBUG, "Disabling workspace buttons on bar: %s",  				config->current_bar->id);  	} else {  		return cmd_results_new(CMD_INVALID, "workspace_buttons", diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c index 7386f82c..701de00a 100644 --- a/sway/commands/bar/wrap_scroll.c +++ b/sway/commands/bar/wrap_scroll.c @@ -13,11 +13,11 @@ struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) {  	}  	if (strcasecmp("yes", argv[0]) == 0) {  		config->current_bar->wrap_scroll = true; -		wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s", +		wlr_log(WLR_DEBUG, "Enabling wrap scroll on bar: %s",  				config->current_bar->id);  	} else if (strcasecmp("no", argv[0]) == 0) {  		config->current_bar->wrap_scroll = false; -		wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s", +		wlr_log(WLR_DEBUG, "Disabling wrap scroll on bar: %s",  				config->current_bar->id);  	} else {  		return cmd_results_new(CMD_INVALID, diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 821f9cd1..83e9e432 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -184,7 +184,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,  	for (int i = 0; i < mode_bindings->length; ++i) {  		struct sway_binding *config_binding = mode_bindings->items[i];  		if (binding_key_compare(binding, config_binding)) { -			wlr_log(L_DEBUG, "overwriting old binding with command '%s'", +			wlr_log(WLR_DEBUG, "overwriting old binding with command '%s'",  				config_binding->command);  			free_sway_binding(config_binding);  			mode_bindings->items[i] = binding; @@ -196,7 +196,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,  		list_add(mode_bindings, binding);  	} -	wlr_log(L_DEBUG, "%s - Bound %s to command %s", +	wlr_log(WLR_DEBUG, "%s - Bound %s to command %s",  		bindtype, argv[0], binding->command);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/border.c b/sway/commands/border.c index 6db85395..9c19e20a 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c @@ -42,7 +42,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {  		container_set_geometry_from_floating_view(view->swayc);  	} -	arrange_and_commit(view->swayc); +	arrange_windows(view->swayc);  	struct sway_seat *seat = input_manager_current_seat(input_manager);  	if (seat->cursor) { diff --git a/sway/commands/default_floating_border.c b/sway/commands/default_floating_border.c new file mode 100644 index 00000000..1bfc24af --- /dev/null +++ b/sway/commands/default_floating_border.c @@ -0,0 +1,29 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/container.h" + +struct cmd_results *cmd_default_floating_border(int argc, char **argv) { +	struct cmd_results *error = NULL; +	if ((error = checkarg(argc, "default_floating_border", +					EXPECTED_AT_LEAST, 1))) { +		return error; +	} + +	if (strcmp(argv[0], "none") == 0) { +		config->floating_border = B_NONE; +	} else if (strcmp(argv[0], "normal") == 0) { +		config->floating_border = B_NORMAL; +	} else if (strcmp(argv[0], "pixel") == 0) { +		config->floating_border = B_PIXEL; +	} else { +		return cmd_results_new(CMD_INVALID, "default_floating_border", +				"Expected 'default_floating_border <none|normal|pixel>' " +				"or 'default_floating_border <normal|pixel> <px>'"); +	} +	if (argc == 2) { +		config->floating_border_thickness = atoi(argv[1]); +	} + +	return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/exec.c b/sway/commands/exec.c index 363d5bef..7fc54123 100644 --- a/sway/commands/exec.c +++ b/sway/commands/exec.c @@ -8,7 +8,7 @@ struct cmd_results *cmd_exec(int argc, char **argv) {  	if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL);  	if (config->reloading) {  		char *args = join_args(argv, argc); -		wlr_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); +		wlr_log(WLR_DEBUG, "Ignoring 'exec %s' due to reload", args);  		free(args);  		return cmd_results_new(CMD_SUCCESS, NULL, NULL);  	} diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 1c99de97..c7727857 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -20,7 +20,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {  	char *tmp = NULL;  	if (strcmp((char*)*argv, "--no-startup-id") == 0) { -		wlr_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored."); +		wlr_log(WLR_INFO, "exec switch '--no-startup-id' not supported, ignored.");  		if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) {  			return error;  		} @@ -35,11 +35,11 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {  	strncpy(cmd, tmp, sizeof(cmd) - 1);  	cmd[sizeof(cmd) - 1] = 0;  	free(tmp); -	wlr_log(L_DEBUG, "Executing %s", cmd); +	wlr_log(WLR_DEBUG, "Executing %s", cmd);  	int fd[2];  	if (pipe(fd) != 0) { -		wlr_log(L_ERROR, "Unable to create pipe for fork"); +		wlr_log(WLR_ERROR, "Unable to create pipe for fork");  	}  	pid_t pid, child; @@ -73,7 +73,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {  	// cleanup child process  	waitpid(pid, NULL, 0);  	if (child > 0) { -		wlr_log(L_DEBUG, "Child process created with pid %d", child); +		wlr_log(WLR_DEBUG, "Child process created with pid %d", child);  		// TODO: add PID to active workspace  	} else {  		return cmd_results_new(CMD_FAILURE, "exec_always", diff --git a/sway/commands/floating.c b/sway/commands/floating.c index e6003521..6ab56c3b 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -37,7 +37,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) {  	container_set_floating(container, wants_floating);  	struct sway_container *workspace = container_parent(container, C_WORKSPACE); -	arrange_and_commit(workspace); +	arrange_windows(workspace);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c new file mode 100644 index 00000000..0af78908 --- /dev/null +++ b/sway/commands/floating_minmax_size.c @@ -0,0 +1,53 @@ +#include <errno.h> +#include <math.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <wlr/util/log.h> +#include "sway/commands.h" +#include "log.h" + +static const char* min_usage = +	"Expected 'floating_minimum_size <width> x <height>'"; + +static const char* max_usage = +	"Expected 'floating_maximum_size <width> x <height>'"; + +static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, +		const char *usage, int *config_width, int *config_height) { +	struct cmd_results *error; +	if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 3))) { +		return error; +	} + +	char *err; +	int width = (int)strtol(argv[0], &err, 10); +	if (*err) { +		return cmd_results_new(CMD_INVALID, cmd_name, usage); +	} + +	if (strcmp(argv[1], "x") != 0) { +		return cmd_results_new(CMD_INVALID, cmd_name, usage); +	} + +	int height = (int)strtol(argv[2], &err, 10); +	if (*err) { +		return cmd_results_new(CMD_INVALID, cmd_name, usage); +	} + +	*config_width = width; +	*config_height = height; + +	return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +struct cmd_results *cmd_floating_minimum_size(int argc, char **argv) { +	return handle_command(argc, argv, "floating_minimum_size", min_usage, +			&config->floating_minimum_width, &config->floating_minimum_height); +} + +struct cmd_results *cmd_floating_maximum_size(int argc, char **argv) { +	return handle_command(argc, argv, "floating_maximum_size", max_usage, +			&config->floating_maximum_width, &config->floating_maximum_height); +} diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 74d9d535..b24d5007 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -1,10 +1,12 @@  #include <strings.h>  #include <wlr/util/log.h>  #include "log.h" +#include "sway/commands.h"  #include "sway/input/input-manager.h"  #include "sway/input/seat.h" +#include "sway/tree/arrange.h"  #include "sway/tree/view.h" -#include "sway/commands.h" +#include "sway/tree/workspace.h"  static bool parse_movement_direction(const char *name,  		enum movement_direction *out) { @@ -27,6 +29,21 @@ static bool parse_movement_direction(const char *name,  	return true;  } +static struct cmd_results *focus_mode(struct sway_container *con, +		struct sway_seat *seat, bool floating) { +	struct sway_container *ws = con->type == C_WORKSPACE ? +		con : container_parent(con, C_WORKSPACE); +	struct sway_container *new_focus = ws; +	if (floating) { +		new_focus = ws->sway_workspace->floating; +		if (new_focus->children->length == 0) { +			return cmd_results_new(CMD_SUCCESS, NULL, NULL); +		} +	} +	seat_set_focus(seat, seat_get_active_child(seat, new_focus)); +	return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} +  struct cmd_results *cmd_focus(int argc, char **argv) {  	struct sway_container *con = config->handler_context.current_container;  	struct sway_seat *seat = config->handler_context.seat; @@ -40,11 +57,20 @@ struct cmd_results *cmd_focus(int argc, char **argv) {  		return cmd_results_new(CMD_SUCCESS, NULL, NULL);  	} -	// TODO mode_toggle +	if (strcmp(argv[0], "floating") == 0) { +		return focus_mode(con, seat, true); +	} else if (strcmp(argv[0], "tiling") == 0) { +		return focus_mode(con, seat, false); +	} else if (strcmp(argv[0], "mode_toggle") == 0) { +		return focus_mode(con, seat, !container_is_floating(con)); +	} + +	// TODO: focus output <direction|name>  	enum movement_direction direction = 0;  	if (!parse_movement_direction(argv[0], &direction)) {  		return cmd_results_new(CMD_INVALID, "focus", -				"Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'"); +			"Expected 'focus <direction|parent|child|mode_toggle|floating|tiling>' " +			"or 'focus output <direction|name>'");  	}  	struct sway_container *next_focus = container_get_in_direction( diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c index 8c425a1d..ac4d6563 100644 --- a/sway/commands/for_window.c +++ b/sway/commands/for_window.c @@ -24,7 +24,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) {  	criteria->cmdlist = join_args(argv + 1, argc - 1);  	list_add(config->criteria, criteria); -	wlr_log(L_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist); +	wlr_log(WLR_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 1a4d8b41..0b5beaa2 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -34,7 +34,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {  	view_set_fullscreen(view, wants_fullscreen);  	struct sway_container *workspace = container_parent(container, C_WORKSPACE); -	arrange_and_commit(workspace->parent); +	arrange_windows(workspace->parent);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 801fb179..3906eb70 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -43,7 +43,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {  			return cmd_results_new(CMD_INVALID, "gaps",  				"gaps edge_gaps on|off|toggle");  		} -		arrange_and_commit(&root_container); +		arrange_windows(&root_container);  	} else {  		int amount_idx = 0; // the current index in argv  		enum gaps_op op = GAPS_OP_SET; @@ -124,7 +124,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {  		if (amount_idx == 0) { // gaps <amount>  			config->gaps_inner = val;  			config->gaps_outer = val; -			arrange_and_commit(&root_container); +			arrange_windows(&root_container);  			return cmd_results_new(CMD_SUCCESS, NULL, NULL);  		}  		// Other variants. The middle-length variant (gaps inner|outer <amount>) @@ -155,7 +155,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {  			} else {  				config->gaps_outer = total;  			} -			arrange_and_commit(&root_container); +			arrange_windows(&root_container);  		} else {  			struct sway_container *c =  				config->handler_context.current_container; @@ -169,7 +169,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {  				c->gaps_outer = total;  			} -			arrange_and_commit(c->parent ? c->parent : &root_container); +			arrange_windows(c->parent ? c->parent : &root_container);  		}  	} diff --git a/sway/commands/input.c b/sway/commands/input.c index 678c57c4..5b203ea0 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -20,8 +20,10 @@ static struct cmd_handler input_handlers[] = {  	{ "pointer_accel", input_cmd_pointer_accel },  	{ "repeat_delay", input_cmd_repeat_delay },  	{ "repeat_rate", input_cmd_repeat_rate }, +	{ "scroll_button", input_cmd_scroll_button },  	{ "scroll_method", input_cmd_scroll_method },  	{ "tap", input_cmd_tap }, +	{ "tap_button_map", input_cmd_tap_button_map },  	{ "xkb_layout", input_cmd_xkb_layout },  	{ "xkb_model", input_cmd_xkb_model },  	{ "xkb_options", input_cmd_xkb_options }, @@ -35,7 +37,7 @@ struct cmd_results *cmd_input(int argc, char **argv) {  		return error;  	} -	wlr_log(L_DEBUG, "entering input block: %s", argv[0]); +	wlr_log(WLR_DEBUG, "entering input block: %s", argv[0]);  	config->handler_context.input_config = new_input_config(argv[0]);  	if (!config->handler_context.input_config) { diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index e2ccdc94..abfe3b12 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c @@ -16,7 +16,7 @@ struct cmd_results *input_cmd_events(int argc, char **argv) {  		return cmd_results_new(CMD_FAILURE, "events",  			"No input device defined.");  	} -	wlr_log(L_DEBUG, "events for device: %s", +	wlr_log(WLR_DEBUG, "events for device: %s",  		current_input_config->identifier);  	struct input_config *new_config =  		new_input_config(current_input_config->identifier); diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c new file mode 100644 index 00000000..350fcca2 --- /dev/null +++ b/sway/commands/input/scroll_button.c @@ -0,0 +1,44 @@ +#include <string.h> +#include <strings.h> +#include <errno.h> +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" + +struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { +	struct cmd_results *error = NULL; +	if ((error = checkarg(argc, "scroll_button", EXPECTED_AT_LEAST, 1))) { +		return error; +	} +	struct input_config *current_input_config = +		config->handler_context.input_config; +	if (!current_input_config) { +		return cmd_results_new(CMD_FAILURE, "scroll_button", +			"No input device defined."); +	} +	struct input_config *new_config = +		new_input_config(current_input_config->identifier); + +	errno = 0; +	char *endptr; +	int scroll_button = strtol(*argv, &endptr, 10); +	if (endptr == *argv && scroll_button == 0) { +		free_input_config(new_config); +		return cmd_results_new(CMD_INVALID, "scroll_button", +				"Scroll button identifier must be an integer."); +	} +	if (errno == ERANGE) { +		free_input_config(new_config); +		return cmd_results_new(CMD_INVALID, "scroll_button", +				"Scroll button identifier out of range."); +	} +	if (scroll_button < 0) { +		free_input_config(new_config); +		return cmd_results_new(CMD_INVALID, "scroll_button", +				"Scroll button identifier cannot be negative."); +	} +	new_config->scroll_button = scroll_button; + +	apply_input_config(new_config); +	return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c index 7d027d5d..a8d1a10c 100644 --- a/sway/commands/input/tap.c +++ b/sway/commands/input/tap.c @@ -28,7 +28,7 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) {  			"Expected 'tap <enabled|disabled>'");  	} -	wlr_log(L_DEBUG, "apply-tap for device: %s", +	wlr_log(WLR_DEBUG, "apply-tap for device: %s",  		current_input_config->identifier);  	apply_input_config(new_config);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/tap_button_map.c b/sway/commands/input/tap_button_map.c new file mode 100644 index 00000000..bdbba472 --- /dev/null +++ b/sway/commands/input/tap_button_map.c @@ -0,0 +1,33 @@ +#include <string.h> +#include <strings.h> +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" + +struct cmd_results *input_cmd_tap_button_map(int argc, char **argv) { +	struct cmd_results *error = NULL; +	if ((error = checkarg(argc, "tap_button_map", EXPECTED_AT_LEAST, 1))) { +		return error; +	} +	struct input_config *current_input_config = +		config->handler_context.input_config; +	if (!current_input_config) { +		return cmd_results_new(CMD_FAILURE, "tap_button_map", +				"No input device defined."); +	} +	struct input_config *new_config = +		new_input_config(current_input_config->identifier); + +	if (strcasecmp(argv[0], "lrm") == 0) { +		new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; +	} else if (strcasecmp(argv[0], "lmr") == 0) { +		new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR; +	} else { +		free_input_config(new_config); +		return cmd_results_new(CMD_INVALID, "tap_button_map", +			"Expected 'tap_button_map <lrm|lmr>'"); +	} + +	apply_input_config(new_config); +	return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c index 867e65d3..9fa5a344 100644 --- a/sway/commands/input/xkb_layout.c +++ b/sway/commands/input/xkb_layout.c @@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {  	new_config->xkb_layout = strdup(argv[0]); -	wlr_log(L_DEBUG, "apply-xkb_layout for device: %s layout: %s", +	wlr_log(WLR_DEBUG, "apply-xkb_layout for device: %s layout: %s",  		current_input_config->identifier, new_config->xkb_layout);  	apply_input_config(new_config);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c index e8c8e04e..0d082625 100644 --- a/sway/commands/input/xkb_model.c +++ b/sway/commands/input/xkb_model.c @@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_model(int argc, char **argv) {  	new_config->xkb_model = strdup(argv[0]); -	wlr_log(L_DEBUG, "apply-xkb_model for device: %s model: %s", +	wlr_log(WLR_DEBUG, "apply-xkb_model for device: %s model: %s",  		current_input_config->identifier, new_config->xkb_model);  	apply_input_config(new_config);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c index e9ddd6e3..3059d941 100644 --- a/sway/commands/input/xkb_options.c +++ b/sway/commands/input/xkb_options.c @@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_options(int argc, char **argv) {  	new_config->xkb_options = strdup(argv[0]); -	wlr_log(L_DEBUG, "apply-xkb_options for device: %s options: %s", +	wlr_log(WLR_DEBUG, "apply-xkb_options for device: %s options: %s",  		current_input_config->identifier, new_config->xkb_options);  	apply_input_config(new_config);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c index 926d0ac1..560f088e 100644 --- a/sway/commands/input/xkb_rules.c +++ b/sway/commands/input/xkb_rules.c @@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {  	new_config->xkb_rules = strdup(argv[0]); -	wlr_log(L_DEBUG, "apply-xkb_rules for device: %s rules: %s", +	wlr_log(WLR_DEBUG, "apply-xkb_rules for device: %s rules: %s",  		current_input_config->identifier, new_config->xkb_rules);  	apply_input_config(new_config);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c index 0e3ffd41..0aa03440 100644 --- a/sway/commands/input/xkb_variant.c +++ b/sway/commands/input/xkb_variant.c @@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {  	new_config->xkb_variant = strdup(argv[0]); -	wlr_log(L_DEBUG, "apply-xkb_variant for device: %s variant: %s", +	wlr_log(WLR_DEBUG, "apply-xkb_variant for device: %s variant: %s",  		current_input_config->identifier, new_config->xkb_variant);  	apply_input_config(new_config);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 9945fa5c..c446f1f9 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -49,7 +49,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {  	}  	container_notify_subtree_changed(parent); -	arrange_and_commit(parent); +	arrange_windows(parent);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/mode.c b/sway/commands/mode.c index d2c14468..b460fcb5 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -65,7 +65,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) {  		return error;  	}  	if ((config->reading && argc > 1) || (!config->reading && argc == 1)) { -		wlr_log(L_DEBUG, "Switching to mode `%s' (pango=%d)", +		wlr_log(WLR_DEBUG, "Switching to mode `%s' (pango=%d)",  				mode->name, mode->pango);  	}  	// Set current mode diff --git a/sway/commands/move.c b/sway/commands/move.c index a4fae388..6ec050a8 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -1,11 +1,12 @@  #define _XOPEN_SOURCE 500  #include <string.h>  #include <strings.h> +#include <wlr/types/wlr_cursor.h>  #include <wlr/types/wlr_output.h>  #include <wlr/types/wlr_output_layout.h>  #include <wlr/util/log.h>  #include "sway/commands.h" -#include "sway/desktop/transaction.h" +#include "sway/input/cursor.h"  #include "sway/input/seat.h"  #include "sway/output.h"  #include "sway/tree/arrange.h" @@ -103,10 +104,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,  		// TODO: Ideally we would arrange the surviving parent after reaping,  		// but container_reap_empty does not return it, so we arrange the  		// workspace instead. -		struct sway_transaction *txn = transaction_create(); -		arrange_windows(old_ws, txn); -		arrange_windows(destination->parent, txn); -		transaction_commit(txn); +		arrange_windows(old_ws); +		arrange_windows(destination->parent);  		return cmd_results_new(CMD_SUCCESS, NULL, NULL);  	} else if (strcasecmp(argv[1], "to") == 0 @@ -142,10 +141,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,  		// TODO: Ideally we would arrange the surviving parent after reaping,  		// but container_reap_empty does not return it, so we arrange the  		// workspace instead. -		struct sway_transaction *txn = transaction_create(); -		arrange_windows(old_ws, txn); -		arrange_windows(focus->parent, txn); -		transaction_commit(txn); +		arrange_windows(old_ws); +		arrange_windows(focus->parent);  		return cmd_results_new(CMD_SUCCESS, NULL, NULL);  	} @@ -175,20 +172,56 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current,  	}  	container_move_to(current, destination); -	struct sway_transaction *txn = transaction_create(); -	arrange_windows(source, txn); -	arrange_windows(destination, txn); -	transaction_commit(txn); +	arrange_windows(source); +	arrange_windows(destination);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  }  static struct cmd_results *move_in_direction(struct sway_container *container, -		enum movement_direction direction, int move_amt) { +		enum movement_direction direction, int argc, char **argv) { +	int move_amt = 10; +	if (argc > 1) { +		char *inv; +		move_amt = (int)strtol(argv[1], &inv, 10); +		if (*inv != '\0' && strcasecmp(inv, "px") != 0) { +			return cmd_results_new(CMD_FAILURE, "move", +					"Invalid distance specified"); +		} +	} +  	if (container->type == C_WORKSPACE) {  		return cmd_results_new(CMD_FAILURE, "move",  				"Cannot move workspaces in a direction");  	} +	if (container_is_floating(container)) { +		if (container->type == C_VIEW && container->sway_view->is_fullscreen) { +			return cmd_results_new(CMD_FAILURE, "move", +					"Cannot move fullscreen floating container"); +		} +		double lx = container->x; +		double ly = container->y; +		switch (direction) { +		case MOVE_LEFT: +			lx -= move_amt; +			break; +		case MOVE_RIGHT: +			lx += move_amt; +			break; +		case MOVE_UP: +			ly -= move_amt; +			break; +		case MOVE_DOWN: +			ly += move_amt; +			break; +		case MOVE_PARENT: +		case MOVE_CHILD: +			return cmd_results_new(CMD_FAILURE, "move", +					"Cannot move floating container to parent or child"); +		} +		container_floating_move_to(container, lx, ly); +		return cmd_results_new(CMD_SUCCESS, NULL, NULL); +	}  	// For simplicity, we'll arrange the entire workspace. The reason for this  	// is moving the container might reap the old parent, and container_move  	// does not return a surviving parent. @@ -198,41 +231,86 @@ static struct cmd_results *move_in_direction(struct sway_container *container,  	container_move(container, direction, move_amt);  	struct sway_container *new_ws = container_parent(container, C_WORKSPACE); -	struct sway_transaction *txn = transaction_create(); -	arrange_windows(old_ws, txn); +	arrange_windows(old_ws);  	if (new_ws != old_ws) { -		arrange_windows(new_ws, txn); +		arrange_windows(new_ws);  	} -	transaction_commit(txn);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } +static const char* expected_position_syntax = +	"Expected 'move [absolute] position <x> <y>' or " +	"'move [absolute] position mouse'"; + +static struct cmd_results *move_to_position(struct sway_container *container, +		int argc, char **argv) { +	if (!container_is_floating(container)) { +		return cmd_results_new(CMD_FAILURE, "move", +				"Only floating containers " +				"can be moved to an absolute position"); +	} +	if (!argc) { +		return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); +	} +	if (strcmp(argv[0], "absolute") == 0) { +		--argc; +		++argv; +	} +	if (!argc) { +		return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); +	} +	if (strcmp(argv[0], "position") == 0) { +		--argc; +		++argv; +	} +	if (!argc) { +		return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); +	} +	if (strcmp(argv[0], "mouse") == 0) { +		struct sway_seat *seat = config->handler_context.seat; +		if (!seat->cursor) { +			return cmd_results_new(CMD_FAILURE, "move", "No cursor device"); +		} +		double lx = seat->cursor->cursor->x - container->width / 2; +		double ly = seat->cursor->cursor->y - container->height / 2; +		container_floating_move_to(container, lx, ly); +		return cmd_results_new(CMD_SUCCESS, NULL, NULL); +	} +	if (argc != 2) { +		return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); +	} +	double lx, ly; +	char *inv; +	lx = (double)strtol(argv[0], &inv, 10); +	if (*inv != '\0' && strcasecmp(inv, "px") != 0) { +		return cmd_results_new(CMD_FAILURE, "move", +				"Invalid position specified"); +	} +	ly = (double)strtol(argv[1], &inv, 10); +	if (*inv != '\0' && strcasecmp(inv, "px") != 0) { +		return cmd_results_new(CMD_FAILURE, "move", +				"Invalid position specified"); +	} +	container_floating_move_to(container, lx, ly); +	return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} +  struct cmd_results *cmd_move(int argc, char **argv) {  	struct cmd_results *error = NULL; -	int move_amt = 10;  	if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {  		return error;  	}  	struct sway_container *current = config->handler_context.current_container; -	if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) { -		char *inv; -		move_amt = (int)strtol(argv[1], &inv, 10); -		if (*inv != '\0' && strcasecmp(inv, "px") != 0) { -			return cmd_results_new(CMD_FAILURE, "move", -					"Invalid distance specified"); -		} -	} -  	if (strcasecmp(argv[0], "left") == 0) { -		return move_in_direction(current, MOVE_LEFT, move_amt); +		return move_in_direction(current, MOVE_LEFT, argc, argv);  	} else if (strcasecmp(argv[0], "right") == 0) { -		return move_in_direction(current, MOVE_RIGHT, move_amt); +		return move_in_direction(current, MOVE_RIGHT, argc, argv);  	} else if (strcasecmp(argv[0], "up") == 0) { -		return move_in_direction(current, MOVE_UP, move_amt); +		return move_in_direction(current, MOVE_UP, argc, argv);  	} else if (strcasecmp(argv[0], "down") == 0) { -		return move_in_direction(current, MOVE_DOWN, move_amt); +		return move_in_direction(current, MOVE_DOWN, argc, argv);  	} else if (strcasecmp(argv[0], "container") == 0  			|| strcasecmp(argv[0], "window") == 0) {  		return cmd_move_container(current, argc, argv); @@ -244,8 +322,9 @@ struct cmd_results *cmd_move(int argc, char **argv) {  		// TODO: scratchpad  		return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");  	} else if (strcasecmp(argv[0], "position") == 0) { -		// TODO: floating -		return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); +		return move_to_position(current, argc, argv); +	} else if (strcasecmp(argv[0], "absolute") == 0) { +		return move_to_position(current, argc, argv);  	} else {  		return cmd_results_new(CMD_INVALID, "move", expected_syntax);  	} diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c new file mode 100644 index 00000000..61a8de7e --- /dev/null +++ b/sway/commands/no_focus.c @@ -0,0 +1,26 @@ +#define _XOPEN_SOURCE 500 +#include <string.h> +#include "sway/commands.h" +#include "sway/criteria.h" +#include "list.h" +#include "log.h" + +struct cmd_results *cmd_no_focus(int argc, char **argv) { +	struct cmd_results *error = NULL; +	if ((error = checkarg(argc, "no_focus", EXPECTED_AT_LEAST, 1))) { +		return error; +	} + +	char *err_str = NULL; +	struct criteria *criteria = criteria_parse(argv[0], &err_str); +	if (!criteria) { +		error = cmd_results_new(CMD_INVALID, "no_focus", err_str); +		free(err_str); +		return error; +	} + +	criteria->type = CT_NO_FOCUS; +	list_add(config->criteria, criteria); + +	return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/output.c b/sway/commands/output.c index f955bf90..15bbd687 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -29,7 +29,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {  	struct output_config *output = new_output_config(argv[0]);  	if (!output) { -		wlr_log(L_ERROR, "Failed to allocate output config"); +		wlr_log(WLR_ERROR, "Failed to allocate output config");  		return NULL;  	}  	argc--; argv++; @@ -71,7 +71,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {  		list_add(config->output_configs, output);  	} -	wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " +	wlr_log(WLR_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "  		"position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)",  		output->name, output->enabled, output->width, output->height,  		output->refresh_rate, output->x, output->y, output->scale, @@ -85,7 +85,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {  	struct sway_output *sway_output;  	wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) {  		output_get_identifier(identifier, sizeof(identifier), sway_output); -		wlr_log(L_DEBUG, "Checking identifier %s", identifier); +		wlr_log(WLR_DEBUG, "Checking identifier %s", identifier);  		if (all || strcmp(sway_output->wlr_output->name, output->name) == 0  				|| strcmp(identifier, output->name) == 0) {  			if (!sway_output->swayc) { diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 65b5f902..4ed56c2a 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -72,7 +72,7 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {  		src = strdup(p.we_wordv[0]);  		wordfree(&p);  		if (!src) { -			wlr_log(L_ERROR, "Failed to duplicate string"); +			wlr_log(WLR_ERROR, "Failed to duplicate string");  			return cmd_results_new(CMD_FAILURE, "output",  				"Unable to allocate resource");  		} @@ -80,9 +80,9 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {  		if (config->reading && *src != '/') {  			// src file is inside configuration dir -			char *conf = strdup(config->current_config); +			char *conf = strdup(config->current_config_path);  			if (!conf) { -				wlr_log(L_ERROR, "Failed to duplicate string"); +				wlr_log(WLR_ERROR, "Failed to duplicate string");  				free(src);  				return cmd_results_new(CMD_FAILURE, "output",  						"Unable to allocate resources"); @@ -94,7 +94,7 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {  			if (!src) {  				free(rel_path);  				free(conf); -				wlr_log(L_ERROR, "Unable to allocate memory"); +				wlr_log(WLR_ERROR, "Unable to allocate memory");  				return cmd_results_new(CMD_FAILURE, "output",  						"Unable to allocate resources");  			} diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 9fc213c4..cea6a94b 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -7,11 +7,11 @@ struct cmd_results *cmd_reload(int argc, char **argv) {  	if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {  		return error;  	} -	if (!load_main_config(config->current_config, true)) { +	if (!load_main_config(config->current_config_path, true)) {  		return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");  	}  	load_swaybars(); -	arrange_and_commit(&root_container); +	arrange_windows(&root_container);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 104a3392..a380ff9c 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -68,7 +68,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {  				"Workspace already exists");  	} -	wlr_log(L_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); +	wlr_log(WLR_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name);  	free(workspace->name);  	workspace->name = new_name; diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 6357343e..e657864c 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -1,4 +1,5 @@  #include <errno.h> +#include <limits.h>  #include <math.h>  #include <stdbool.h>  #include <stdlib.h> @@ -7,6 +8,7 @@  #include <wlr/util/log.h>  #include "sway/commands.h"  #include "sway/tree/arrange.h" +#include "sway/tree/view.h"  #include "log.h"  static const int MIN_SANE_W = 100, MIN_SANE_H = 60; @@ -21,9 +23,18 @@ enum resize_unit {  enum resize_axis {  	RESIZE_AXIS_HORIZONTAL,  	RESIZE_AXIS_VERTICAL, +	RESIZE_AXIS_UP, +	RESIZE_AXIS_DOWN, +	RESIZE_AXIS_LEFT, +	RESIZE_AXIS_RIGHT,  	RESIZE_AXIS_INVALID,  }; +struct resize_amount { +	int amount; +	enum resize_unit unit; +}; +  static enum resize_unit parse_resize_unit(const char *unit) {  	if (strcasecmp(unit, "px") == 0) {  		return RESIZE_UNIT_PX; @@ -37,6 +48,69 @@ static enum resize_unit parse_resize_unit(const char *unit) {  	return RESIZE_UNIT_INVALID;  } +// Parse arguments such as "10", "10px" or "10 px". +// Returns the number of arguments consumed. +static int parse_resize_amount(int argc, char **argv, +		struct resize_amount *amount) { +	char *err; +	amount->amount = (int)strtol(argv[0], &err, 10); +	if (*err) { +		// e.g. 10px +		amount->unit = parse_resize_unit(err); +		return 1; +	} +	if (argc == 1) { +		amount->unit = RESIZE_UNIT_DEFAULT; +		return 1; +	} +	// Try the second argument +	amount->unit = parse_resize_unit(argv[1]); +	if (amount->unit == RESIZE_UNIT_INVALID) { +		amount->unit = RESIZE_UNIT_DEFAULT; +		return 1; +	} +	return 2; +} + +static void calculate_constraints(int *min_width, int *max_width, +		int *min_height, int *max_height) { +	struct sway_container *con = config->handler_context.current_container; + +	if (config->floating_minimum_width == -1) { // no minimum +		*min_width = 0; +	} else if (config->floating_minimum_width == 0) { // automatic +		*min_width = 75; +	} else { +		*min_width = config->floating_minimum_width; +	} + +	if (config->floating_minimum_height == -1) { // no minimum +		*min_height = 0; +	} else if (config->floating_minimum_height == 0) { // automatic +		*min_height = 50; +	} else { +		*min_height = config->floating_minimum_height; +	} + +	if (config->floating_maximum_width == -1) { // no maximum +		*max_width = INT_MAX; +	} else if (config->floating_maximum_width == 0) { // automatic +		struct sway_container *ws = container_parent(con, C_WORKSPACE); +		*max_width = ws->width; +	} else { +		*max_width = config->floating_maximum_width; +	} + +	if (config->floating_maximum_height == -1) { // no maximum +		*max_height = INT_MAX; +	} else if (config->floating_maximum_height == 0) { // automatic +		struct sway_container *ws = container_parent(con, C_WORKSPACE); +		*max_height = ws->height; +	} else { +		*max_height = config->floating_maximum_height; +	} +} +  static enum resize_axis parse_resize_axis(const char *axis) {  	if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) {  		return RESIZE_AXIS_HORIZONTAL; @@ -44,6 +118,18 @@ static enum resize_axis parse_resize_axis(const char *axis) {  	if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) {  		return RESIZE_AXIS_VERTICAL;  	} +	if (strcasecmp(axis, "up") == 0) { +		return RESIZE_AXIS_UP; +	} +	if (strcasecmp(axis, "down") == 0) { +		return RESIZE_AXIS_DOWN; +	} +	if (strcasecmp(axis, "left") == 0) { +		return RESIZE_AXIS_LEFT; +	} +	if (strcasecmp(axis, "right") == 0) { +		return RESIZE_AXIS_RIGHT; +	}  	return RESIZE_AXIS_INVALID;  } @@ -95,7 +181,7 @@ static void resize_tiled(int amount, enum resize_axis axis) {  		return;  	} -	wlr_log(L_DEBUG, +	wlr_log(WLR_DEBUG,  			"Found the proper parent: %p. It has %d l conts, and %d r conts",  			parent->parent, minor_weight, major_weight); @@ -182,105 +268,315 @@ static void resize_tiled(int amount, enum resize_axis axis) {  		}  	} -	arrange_and_commit(parent->parent); +	arrange_windows(parent->parent);  } -static void resize(int amount, enum resize_axis axis, enum resize_unit unit) { -	struct sway_container *current = config->handler_context.current_container; -	if (unit == RESIZE_UNIT_DEFAULT) { -		// Default for tiling; TODO floating should be px -		unit = RESIZE_UNIT_PPT; +/** + * Implement `resize <grow|shrink>` for a floating container. + */ +static struct cmd_results *resize_adjust_floating(enum resize_axis axis, +		struct resize_amount *amount) { +	struct sway_container *con = config->handler_context.current_container; +	int grow_width = 0, grow_height = 0; +	switch (axis) { +	case RESIZE_AXIS_HORIZONTAL: +	case RESIZE_AXIS_LEFT: +	case RESIZE_AXIS_RIGHT: +		grow_width = amount->amount; +		break; +	case RESIZE_AXIS_VERTICAL: +	case RESIZE_AXIS_UP: +	case RESIZE_AXIS_DOWN: +		grow_height = amount->amount; +		break; +	case RESIZE_AXIS_INVALID: +		return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); +	} +	// Make sure we're not adjusting beyond floating min/max size +	int min_width, max_width, min_height, max_height; +	calculate_constraints(&min_width, &max_width, &min_height, &max_height); +	if (con->width + grow_width < min_width) { +		grow_width = min_width - con->width; +	} else if (con->width + grow_width > max_width) { +		grow_width = max_width - con->width;  	} +	if (con->height + grow_height < min_height) { +		grow_height = min_height - con->height; +	} else if (con->height + grow_height > max_height) { +		grow_height = max_height - con->height; +	} +	int grow_x = 0, grow_y = 0; +	switch (axis) { +	case RESIZE_AXIS_HORIZONTAL: +		grow_x = -grow_width / 2; +		break; +	case RESIZE_AXIS_VERTICAL: +		grow_y = -grow_height / 2; +		break; +	case RESIZE_AXIS_UP: +		grow_y = -grow_height; +		break; +	case RESIZE_AXIS_LEFT: +		grow_x = -grow_width; +		break; +	case RESIZE_AXIS_DOWN: +	case RESIZE_AXIS_RIGHT: +		break; +	case RESIZE_AXIS_INVALID: +		return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); +	} +	con->x += grow_x; +	con->y += grow_y; +	con->width += grow_width; +	con->height += grow_height; + +	if (con->type == C_VIEW) { +		struct sway_view *view = con->sway_view; +		view->x += grow_x; +		view->y += grow_y; +		view->width += grow_width; +		view->height += grow_height; +	} + +	arrange_windows(con); + +	return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} -	if (unit == RESIZE_UNIT_PPT) { -		float pct = amount / 100.0f; +/** + * Implement `resize <grow|shrink>` for a tiled container. + */ +static struct cmd_results *resize_adjust_tiled(enum resize_axis axis, +		struct resize_amount *amount) { +	struct sway_container *current = config->handler_context.current_container; + +	if (amount->unit == RESIZE_UNIT_DEFAULT) { +		amount->unit = RESIZE_UNIT_PPT; +	} +	if (amount->unit == RESIZE_UNIT_PPT) { +		float pct = amount->amount / 100.0f; +		// TODO: Make left/right/up/down resize in that direction?  		switch (axis) { +		case RESIZE_AXIS_LEFT: +		case RESIZE_AXIS_RIGHT:  		case RESIZE_AXIS_HORIZONTAL: -			amount = (float)current->width * pct; +			amount->amount = (float)current->width * pct;  			break; +		case RESIZE_AXIS_UP: +		case RESIZE_AXIS_DOWN:  		case RESIZE_AXIS_VERTICAL: -			amount = (float)current->height * pct; +			amount->amount = (float)current->height * pct;  			break; -		default: -			sway_assert(0, "invalid resize axis"); -			return; +		case RESIZE_AXIS_INVALID: +			return cmd_results_new(CMD_INVALID, "resize", +					"Invalid resize axis/direction");  		}  	} -	return resize_tiled(amount, axis); +	resize_tiled(amount->amount, axis); +	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } -struct cmd_results *cmd_resize(int argc, char **argv) { -	struct sway_container *current = config->handler_context.current_container; -	if (!current) { -		return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); -	} -	if (current->type != C_VIEW && current->type != C_CONTAINER) { -		return cmd_results_new(CMD_INVALID, "resize", -				"Can only resize views/containers"); +/** + * Implement `resize set` for a tiled container. + */ +static struct cmd_results *resize_set_tiled(struct sway_container *con, +		struct resize_amount *width, struct resize_amount *height) { +	return cmd_results_new(CMD_INVALID, "resize", +			"'resize set' is not implemented for tiled views"); +} + +/** + * Implement `resize set` for a floating container. + */ +static struct cmd_results *resize_set_floating(struct sway_container *con, +		struct resize_amount *width, struct resize_amount *height) { +	int min_width, max_width, min_height, max_height; +	calculate_constraints(&min_width, &max_width, &min_height, &max_height); +	width->amount = fmax(min_width, fmin(width->amount, max_width)); +	height->amount = fmax(min_height, fmin(height->amount, max_height)); +	int grow_width = width->amount - con->width; +	int grow_height = height->amount - con->height; +	con->x -= grow_width / 2; +	con->y -= grow_height / 2; +	con->width = width->amount; +	con->height = height->amount; + +	if (con->type == C_VIEW) { +		struct sway_view *view = con->sway_view; +		view->x -= grow_width / 2; +		view->y -= grow_height / 2; +		view->width += grow_width; +		view->height += grow_height;  	} +	arrange_windows(con); + +	return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +/** + * resize set <args> + * + * args: <width> [px|ppt] <height> [px|ppt] + */ +static struct cmd_results *cmd_resize_set(int argc, char **argv) {  	struct cmd_results *error;  	if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {  		return error;  	} - -	if (strcasecmp(argv[0], "set") == 0) { -		// TODO -		//return cmd_resize_set(argc - 1, &argv[1]); -		return cmd_results_new(CMD_INVALID, "resize", "resize set unimplemented"); +	const char *usage = "Expected 'resize set <width> <height>'"; + +	// Width +	struct resize_amount width; +	int num_consumed_args = parse_resize_amount(argc, argv, &width); +	argc -= num_consumed_args; +	argv += num_consumed_args; +	if (width.unit == RESIZE_UNIT_INVALID) { +		return cmd_results_new(CMD_INVALID, "resize", usage); +	} +	if (!argc) { +		return cmd_results_new(CMD_INVALID, "resize", usage);  	} -	// TODO: resize grow|shrink left|right|up|down +	// Height +	struct resize_amount height; +	num_consumed_args = parse_resize_amount(argc, argv, &height); +	argc -= num_consumed_args; +	argv += num_consumed_args; +	if (height.unit == RESIZE_UNIT_INVALID) { +		return cmd_results_new(CMD_INVALID, "resize", usage); +	} -	const char *usage = "Expected 'resize <shrink|grow> " -		"<width|height> [<amount>] [px|ppt]'"; +	// If 0, don't resize that dimension +	struct sway_container *con = config->handler_context.current_container; +	if (width.amount <= 0) { +		width.amount = con->width; +	} +	if (height.amount <= 0) { +		height.amount = con->height; +	} -	int multiplier = 0; -	if (strcasecmp(*argv, "grow") == 0) { -		multiplier = 1; -	} else if (strcasecmp(*argv, "shrink") == 0) { -		multiplier = -1; -	} else { -		return cmd_results_new(CMD_INVALID, "resize", usage); +	if (container_is_floating(con)) { +		return resize_set_floating(con, &width, &height);  	} -	--argc; ++argv; +	return resize_set_tiled(con, &width, &height); +} +/** + * resize <grow|shrink> <args> + * + * args: <direction> + * args: <direction> <amount> <unit> + * args: <direction> <amount> <unit> or <amount> <other_unit> + */ +static struct cmd_results *cmd_resize_adjust(int argc, char **argv, +		int multiplier) { +	const char *usage = "Expected 'resize grow|shrink <direction> " +		"[<amount> px|ppt [or <amount> px|ppt]]'";  	enum resize_axis axis = parse_resize_axis(*argv);  	if (axis == RESIZE_AXIS_INVALID) {  		return cmd_results_new(CMD_INVALID, "resize", usage);  	}  	--argc; ++argv; -	int amount = 10; // Default amount -	enum resize_unit unit = RESIZE_UNIT_DEFAULT; - +	// First amount +	struct resize_amount first_amount;  	if (argc) { -		char *err; -		amount = (int)strtol(*argv, &err, 10); -		if (*err) { -			// e.g. `resize grow width 10px` -			unit = parse_resize_unit(err); -			if (unit == RESIZE_UNIT_INVALID) { -				return cmd_results_new(CMD_INVALID, "resize", usage); -			} +		int num_consumed_args = parse_resize_amount(argc, argv, &first_amount); +		argc -= num_consumed_args; +		argv += num_consumed_args; +		if (first_amount.unit == RESIZE_UNIT_INVALID) { +			return cmd_results_new(CMD_INVALID, "resize", usage);  		} -		--argc; ++argv; +	} else { +		first_amount.amount = 10; +		first_amount.unit = RESIZE_UNIT_DEFAULT;  	} +	// "or"  	if (argc) { -		unit = parse_resize_unit(*argv); -		if (unit == RESIZE_UNIT_INVALID) { +		if (strcmp(*argv, "or") != 0) {  			return cmd_results_new(CMD_INVALID, "resize", usage);  		}  		--argc; ++argv;  	} +	// Second amount +	struct resize_amount second_amount;  	if (argc) { -		// Provied too many args, the bastard -		return cmd_results_new(CMD_INVALID, "resize", usage); +		int num_consumed_args = parse_resize_amount(argc, argv, &second_amount); +		argc -= num_consumed_args; +		argv += num_consumed_args; +		if (second_amount.unit == RESIZE_UNIT_INVALID) { +			return cmd_results_new(CMD_INVALID, "resize", usage); +		} +	} else { +		second_amount.unit = RESIZE_UNIT_INVALID;  	} -	resize(amount * multiplier, axis, unit); -	return cmd_results_new(CMD_SUCCESS, NULL, NULL); +	first_amount.amount *= multiplier; +	second_amount.amount *= multiplier; + +	struct sway_container *con = config->handler_context.current_container; +	if (container_is_floating(con)) { +		// Floating containers can only resize in px. Choose an amount which +		// uses px, with fallback to an amount that specified no unit. +		if (first_amount.unit == RESIZE_UNIT_PX) { +			return resize_adjust_floating(axis, &first_amount); +		} else if (second_amount.unit == RESIZE_UNIT_PX) { +			return resize_adjust_floating(axis, &second_amount); +		} else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { +			return resize_adjust_floating(axis, &first_amount); +		} else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { +			return resize_adjust_floating(axis, &second_amount); +		} else { +			return cmd_results_new(CMD_INVALID, "resize", +					"Floating containers cannot use ppt measurements"); +		} +	} + +	// For tiling, prefer ppt -> default -> px +	if (first_amount.unit == RESIZE_UNIT_PPT) { +		return resize_adjust_tiled(axis, &first_amount); +	} else if (second_amount.unit == RESIZE_UNIT_PPT) { +		return resize_adjust_tiled(axis, &second_amount); +	} else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { +		return resize_adjust_tiled(axis, &first_amount); +	} else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { +		return resize_adjust_tiled(axis, &second_amount); +	} else { +		return resize_adjust_tiled(axis, &first_amount); +	} +} + +struct cmd_results *cmd_resize(int argc, char **argv) { +	struct sway_container *current = config->handler_context.current_container; +	if (!current) { +		return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); +	} +	if (current->type != C_VIEW && current->type != C_CONTAINER) { +		return cmd_results_new(CMD_INVALID, "resize", +				"Can only resize views/containers"); +	} + +	struct cmd_results *error; +	if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { +		return error; +	} + +	if (strcasecmp(argv[0], "set") == 0) { +		return cmd_resize_set(argc - 1, &argv[1]); +	} +	if (strcasecmp(argv[0], "grow") == 0) { +		return cmd_resize_adjust(argc - 1, &argv[1], 1); +	} +	if (strcasecmp(argv[0], "shrink") == 0) { +		return cmd_resize_adjust(argc - 1, &argv[1], -1); +	} + +	const char *usage = "Expected 'resize <shrink|grow> " +		"<width|height|up|down|left|right> [<amount>] [px|ppt]'"; + +	return cmd_results_new(CMD_INVALID, "resize", usage);  } diff --git a/sway/commands/set.c b/sway/commands/set.c index 84e9b792..ea388d3b 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c @@ -32,7 +32,7 @@ struct cmd_results *cmd_set(int argc, char **argv) {  	}  	if (argv[0][0] != '$') { -		wlr_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]); +		wlr_log(WLR_INFO, "Warning: variable '%s' doesn't start with $", argv[0]);  		size_t size = snprintf(NULL, 0, "$%s", argv[0]);  		tmp = malloc(size + 1); diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c index f687e78e..7d27e571 100644 --- a/sway/commands/smart_gaps.c +++ b/sway/commands/smart_gaps.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {  			"Expected 'smart_gaps <on|off>' ");  	} -	arrange_and_commit(&root_container); +	arrange_windows(&root_container);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/split.c b/sway/commands/split.c index c40f4d9f..313799da 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -16,7 +16,7 @@ static struct cmd_results *do_split(int layout) {  	}  	struct sway_container *parent = container_split(con, layout);  	container_create_notify(parent); -	arrange_and_commit(parent->parent); +	arrange_windows(parent->parent);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/swap.c b/sway/commands/swap.c index e052058f..2fc88308 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -1,7 +1,6 @@  #include <strings.h>  #include <wlr/util/log.h>  #include "sway/commands.h" -#include "sway/desktop/transaction.h"  #include "sway/tree/arrange.h"  #include "sway/tree/layout.h"  #include "sway/tree/view.h" @@ -79,14 +78,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) {  	container_swap(current, other); -	struct sway_transaction *txn = transaction_create(); -	arrange_windows(current->parent, txn); - +	arrange_windows(current->parent);  	if (other->parent != current->parent) { -		arrange_windows(other->parent, txn); +		arrange_windows(other->parent);  	} -	transaction_commit(txn); -  	return cmd_results_new(CMD_SUCCESS, NULL, NULL);  } diff --git a/sway/commands/swaybg_command.c b/sway/commands/swaybg_command.c index 770d4821..36f7fdcd 100644 --- a/sway/commands/swaybg_command.c +++ b/sway/commands/swaybg_command.c @@ -13,7 +13,7 @@ struct cmd_results *cmd_swaybg_command(int argc, char **argv) {  		free(config->swaybg_command);  	}  	config->swaybg_command = join_args(argv, argc); -	wlr_log(L_DEBUG, "Using custom swaybg command: %s", +	wlr_log(WLR_DEBUG, "Using custom swaybg command: %s",  			config->swaybg_command);  	return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c new file mode 100644 index 00000000..d199858a --- /dev/null +++ b/sway/commands/urgent.c @@ -0,0 +1,36 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/layout.h" + +struct cmd_results *cmd_urgent(int argc, char **argv) { +	struct cmd_results *error = NULL; +	if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { +		return error; +	} +	struct sway_container *container = +		config->handler_context.current_container; +	if (container->type != C_VIEW) { +		return cmd_results_new(CMD_INVALID, "urgent", +				"Only views can be urgent"); +	} +	struct sway_view *view = container->sway_view; + +	if (strcmp(argv[0], "enable") == 0) { +		view_set_urgent(view, true); +	} else if (strcmp(argv[0], "disable") == 0) { +		view_set_urgent(view, false); +	} else if (strcmp(argv[0], "allow") == 0) { +		view->allow_request_urgent = true; +	} else if (strcmp(argv[0], "deny") == 0) { +		view->allow_request_urgent = false; +	} else { +		return cmd_results_new(CMD_INVALID, "urgent", +				"Expected 'urgent <enable|disable|allow|deny>'"); +	} + +	return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index d15be571..e8b37182 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -51,7 +51,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {  			free(old); // workspaces can only be assigned to a single output  			list_del(config->workspace_outputs, i);  		} -		wlr_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); +		wlr_log(WLR_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output);  		list_add(config->workspace_outputs, wso);  	} else {  		if (config->reading || !config->active) { diff --git a/sway/config.c b/sway/config.c index 89b7d349..c620e4c7 100644 --- a/sway/config.c +++ b/sway/config.c @@ -24,6 +24,7 @@  #include "sway/input/seat.h"  #include "sway/commands.h"  #include "sway/config.h" +#include "sway/criteria.h"  #include "sway/tree/arrange.h"  #include "sway/tree/layout.h"  #include "sway/tree/workspace.h" @@ -105,7 +106,12 @@ void free_config(struct sway_config *config) {  		}  		list_free(config->seat_configs);  	} -	list_free(config->criteria); +	if (config->criteria) { +		for (int i = 0; i < config->criteria->length; ++i) { +			criteria_destroy(config->criteria->items[i]); +		} +		list_free(config->criteria); +	}  	list_free(config->no_focus);  	list_free(config->active_bar_modifiers);  	list_free(config->config_chain); @@ -117,6 +123,7 @@ void free_config(struct sway_config *config) {  	free(config->floating_scroll_left_cmd);  	free(config->floating_scroll_right_cmd);  	free(config->font); +	free((char *)config->current_config_path);  	free((char *)config->current_config);  	free(config);  } @@ -205,6 +212,7 @@ static void config_defaults(struct sway_config *config) {  	if (!(config->active_bar_modifiers = create_list())) goto cleanup;  	if (!(config->config_chain = create_list())) goto cleanup; +	config->current_config_path = NULL;  	config->current_config = NULL;  	// borders @@ -276,12 +284,12 @@ static char *get_config_path(void) {  		char *home = getenv("HOME");  		char *config_home = malloc(strlen(home) + strlen("/.config") + 1);  		if (!config_home) { -			wlr_log(L_ERROR, "Unable to allocate $HOME/.config"); +			wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config");  		} else {  			strcpy(config_home, home);  			strcat(config_home, "/.config");  			setenv("XDG_CONFIG_HOME", config_home, 1); -			wlr_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); +			wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);  			free(config_home);  		}  	} @@ -304,16 +312,13 @@ static char *get_config_path(void) {  	return NULL; // Not reached  } -const char *current_config_path; -  static bool load_config(const char *path, struct sway_config *config) {  	if (path == NULL) { -		wlr_log(L_ERROR, "Unable to find a config file!"); +		wlr_log(WLR_ERROR, "Unable to find a config file!");  		return false;  	} -	wlr_log(L_INFO, "Loading config from %s", path); -	current_config_path = path; +	wlr_log(WLR_INFO, "Loading config from %s", path);  	struct stat sb;  	if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { @@ -322,7 +327,7 @@ static bool load_config(const char *path, struct sway_config *config) {  	FILE *f = fopen(path, "r");  	if (!f) { -		wlr_log(L_ERROR, "Unable to open %s for reading", path); +		wlr_log(WLR_ERROR, "Unable to open %s for reading", path);  		return false;  	} @@ -330,10 +335,9 @@ static bool load_config(const char *path, struct sway_config *config) {  	fclose(f);  	if (!config_load_success) { -		wlr_log(L_ERROR, "Error(s) loading config!"); +		wlr_log(WLR_ERROR, "Error(s) loading config!");  	} -	current_config_path = NULL;  	return true;  } @@ -353,12 +357,12 @@ bool load_main_config(const char *file, bool is_active) {  	config_defaults(config);  	if (is_active) { -		wlr_log(L_DEBUG, "Performing configuration file reload"); +		wlr_log(WLR_DEBUG, "Performing configuration file reload");  		config->reloading = true;  		config->active = true;  	} -	config->current_config = path; +	config->current_config_path = path;  	list_add(config->config_chain, path);  	config->reading = true; @@ -369,7 +373,7 @@ bool load_main_config(const char *file, bool is_active) {  	/*  	DIR *dir = opendir(SYSCONFDIR "/sway/security.d");  	if (!dir) { -		wlr_log(L_ERROR, +		wlr_log(WLR_ERROR,  			"%s does not exist, sway will have no security configuration"  			" and will probably be broken", SYSCONFDIR "/sway/security.d");  	} else { @@ -398,7 +402,7 @@ bool load_main_config(const char *file, bool is_active) {  			if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||  					(((s.st_mode & 0777) != 0644) &&  					(s.st_mode & 0777) != 0444)) { -				wlr_log(L_ERROR, +				wlr_log(WLR_ERROR,  					"Refusing to load %s - it must be owned by root "  					"and mode 644 or 444", _path);  				success = false; @@ -428,7 +432,7 @@ bool load_main_config(const char *file, bool is_active) {  static bool load_include_config(const char *path, const char *parent_dir,  		struct sway_config *config) {  	// save parent config -	const char *parent_config = config->current_config; +	const char *parent_config = config->current_config_path;  	char *full_path;  	int len = strlen(path); @@ -436,7 +440,7 @@ static bool load_include_config(const char *path, const char *parent_dir,  		len = len + strlen(parent_dir) + 2;  		full_path = malloc(len * sizeof(char));  		if (!full_path) { -			wlr_log(L_ERROR, +			wlr_log(WLR_ERROR,  				"Unable to allocate full path to included config");  			return false;  		} @@ -449,7 +453,7 @@ static bool load_include_config(const char *path, const char *parent_dir,  	free(full_path);  	if (real_path == NULL) { -		wlr_log(L_DEBUG, "%s not found.", path); +		wlr_log(WLR_DEBUG, "%s not found.", path);  		return false;  	} @@ -458,7 +462,7 @@ static bool load_include_config(const char *path, const char *parent_dir,  	for (j = 0; j < config->config_chain->length; ++j) {  		char *old_path = config->config_chain->items[j];  		if (strcmp(real_path, old_path) == 0) { -			wlr_log(L_DEBUG, +			wlr_log(WLR_DEBUG,  				"%s already included once, won't be included again.",  				real_path);  			free(real_path); @@ -466,25 +470,25 @@ static bool load_include_config(const char *path, const char *parent_dir,  		}  	} -	config->current_config = real_path; +	config->current_config_path = real_path;  	list_add(config->config_chain, real_path);  	int index = config->config_chain->length - 1;  	if (!load_config(real_path, config)) {  		free(real_path); -		config->current_config = parent_config; +		config->current_config_path = parent_config;  		list_del(config->config_chain, index);  		return false;  	} -	// restore current_config -	config->current_config = parent_config; +	// restore current_config_path +	config->current_config_path = parent_config;  	return true;  }  bool load_include_configs(const char *path, struct sway_config *config) {  	char *wd = getcwd(NULL, 0); -	char *parent_path = strdup(config->current_config); +	char *parent_path = strdup(config->current_config_path);  	const char *parent_dir = dirname(parent_path);  	if (chdir(parent_dir) < 0) { @@ -512,7 +516,7 @@ bool load_include_configs(const char *path, struct sway_config *config) {  	// restore wd  	if (chdir(wd) < 0) {  		free(wd); -		wlr_log(L_ERROR, "failed to restore working directory"); +		wlr_log(WLR_ERROR, "failed to restore working directory");  		return false;  	} @@ -527,13 +531,13 @@ static int detect_brace_on_following_line(FILE *file, char *line,  		char *peeked = NULL;  		long position = 0;  		do { -			wlr_log(L_DEBUG, "Peeking line %d", line_number + lines + 1); +			wlr_log(WLR_DEBUG, "Peeking line %d", line_number + lines + 1);  			free(peeked);  			peeked = peek_line(file, lines, &position);  			if (peeked) {  				peeked = strip_whitespace(peeked);  			} -			wlr_log(L_DEBUG, "Peeked line: `%s`", peeked); +			wlr_log(WLR_DEBUG, "Peeked line: `%s`", peeked);  			lines++;  		} while (peeked && strlen(peeked) == 0); @@ -552,7 +556,7 @@ static char *expand_line(const char *block, const char *line, bool add_brace) {  		+ (add_brace ? 2 : 0) + 1;  	char *expanded = calloc(1, size);  	if (!expanded) { -		wlr_log(L_ERROR, "Cannot allocate expanded line buffer"); +		wlr_log(WLR_ERROR, "Cannot allocate expanded line buffer");  		return NULL;  	}  	snprintf(expanded, size, "%s%s%s%s", block ? block : "", @@ -561,10 +565,33 @@ static char *expand_line(const char *block, const char *line, bool add_brace) {  }  bool read_config(FILE *file, struct sway_config *config) { +	bool reading_main_config = false; +	char *this_config = NULL; +	size_t config_size = 0; +	if (config->current_config == NULL) { +		reading_main_config = true; + +		int ret_seek = fseek(file, 0, SEEK_END); +		long ret_tell = ftell(file); +		if (ret_seek == -1 || ret_tell == -1) { +			wlr_log(WLR_ERROR, "Unable to get size of config file"); +			return false; +		} +		config_size = ret_tell; +		rewind(file); + +		config->current_config = this_config = calloc(1, config_size + 1); +		if (this_config == NULL) { +			wlr_log(WLR_ERROR, "Unable to allocate buffer for config contents"); +			return false; +		} +	} +  	bool success = true;  	int line_number = 0;  	char *line;  	list_t *stack = create_list(); +	size_t read = 0;  	while (!feof(file)) {  		char *block = stack->length ? stack->items[0] : NULL;  		line = read_line(file); @@ -572,7 +599,26 @@ bool read_config(FILE *file, struct sway_config *config) {  			continue;  		}  		line_number++; -		wlr_log(L_DEBUG, "Read line %d: %s", line_number, line); +		wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line); + +		if (reading_main_config) { +			size_t length = strlen(line); + +			if (read + length > config_size) { +				wlr_log(WLR_ERROR, "Config file changed during reading"); +				list_foreach(stack, free); +				list_free(stack); +				free(line); +				return false; +			} + +			strcpy(this_config + read, line); +			if (line_number != 1) { +				this_config[read - 1] = '\n'; +			} +			read += length + 1; +		} +  		line = strip_whitespace(line);  		if (line[0] == '#') {  			free(line); @@ -586,15 +632,16 @@ bool read_config(FILE *file, struct sway_config *config) {  				line_number);  		if (brace_detected > 0) {  			line_number += brace_detected; -			wlr_log(L_DEBUG, "Detected open brace on line %d", line_number); +			wlr_log(WLR_DEBUG, "Detected open brace on line %d", line_number);  		}  		char *expanded = expand_line(block, line, brace_detected > 0);  		if (!expanded) {  			list_foreach(stack, free);  			list_free(stack); +			free(line);  			return false;  		} -		wlr_log(L_DEBUG, "Expanded line: %s", expanded); +		wlr_log(WLR_DEBUG, "Expanded line: %s", expanded);  		struct cmd_results *res;  		if (block && strcmp(block, "<commands>") == 0) {  			// Special case @@ -606,23 +653,23 @@ bool read_config(FILE *file, struct sway_config *config) {  		switch(res->status) {  		case CMD_FAILURE:  		case CMD_INVALID: -			wlr_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, -				line, res->error, config->current_config); +			wlr_log(WLR_ERROR, "Error on line %i '%s': %s (%s)", line_number, +				line, res->error, config->current_config_path);  			success = false;  			break;  		case CMD_DEFER: -			wlr_log(L_DEBUG, "Deferring command `%s'", line); +			wlr_log(WLR_DEBUG, "Deferring command `%s'", line);  			list_add(config->cmd_queue, strdup(line));  			break;  		case CMD_BLOCK_COMMANDS: -			wlr_log(L_DEBUG, "Entering commands block"); +			wlr_log(WLR_DEBUG, "Entering commands block");  			list_insert(stack, 0, "<commands>");  			break;  		case CMD_BLOCK: -			wlr_log(L_DEBUG, "Entering block '%s'", res->input); +			wlr_log(WLR_DEBUG, "Entering block '%s'", res->input);  			list_insert(stack, 0, strdup(res->input));  			if (strcmp(res->input, "bar") == 0) {  				config->current_bar = NULL; @@ -631,7 +678,7 @@ bool read_config(FILE *file, struct sway_config *config) {  		case CMD_BLOCK_END:  			if (!block) { -				wlr_log(L_DEBUG, "Unmatched '}' on line %i", line_number); +				wlr_log(WLR_DEBUG, "Unmatched '}' on line %i", line_number);  				success = false;  				break;  			} @@ -639,7 +686,7 @@ bool read_config(FILE *file, struct sway_config *config) {  				config->current_bar = NULL;  			} -			wlr_log(L_DEBUG, "Exiting block '%s'", block); +			wlr_log(WLR_DEBUG, "Exiting block '%s'", block);  			list_del(stack, 0);  			free(block);  			memset(&config->handler_context, 0, @@ -682,7 +729,7 @@ char *do_var_replacement(char *str) {  				int vvlen = strlen(var->value);  				char *newstr = malloc(strlen(str) - vnlen + vvlen + 1);  				if (!newstr) { -					wlr_log(L_ERROR, +					wlr_log(WLR_ERROR,  						"Unable to allocate replacement "  						"during variable expansion");  					break; @@ -744,6 +791,6 @@ void config_update_font_height(bool recalculate) {  	}  	if (config->font_height != prev_max_height) { -		arrange_and_commit(&root_container); +		arrange_windows(&root_container);  	}  } diff --git a/sway/config/bar.c b/sway/config/bar.c index ee062c6a..3a74331e 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -16,10 +16,10 @@  #include "log.h"  static void terminate_swaybar(pid_t pid) { -	wlr_log(L_DEBUG, "Terminating swaybar %d", pid); +	wlr_log(WLR_DEBUG, "Terminating swaybar %d", pid);  	int ret = kill(-pid, SIGTERM);  	if (ret != 0) { -		wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid); +		wlr_log_errno(WLR_ERROR, "Unable to terminate swaybar %d", pid);  	} else {  		int status;  		waitpid(pid, &status, 0); @@ -167,7 +167,7 @@ void invoke_swaybar(struct bar_config *bar) {  	// Pipe to communicate errors  	int filedes[2];  	if (pipe(filedes) == -1) { -		wlr_log(L_ERROR, "Pipe setup failed! Cannot fork into bar"); +		wlr_log(WLR_ERROR, "Pipe setup failed! Cannot fork into bar");  		return;  	} @@ -197,17 +197,17 @@ void invoke_swaybar(struct bar_config *bar) {  		execvp(cmd[0], cmd);  		exit(1);  	} -	wlr_log(L_DEBUG, "Spawned swaybar %d", bar->pid); +	wlr_log(WLR_DEBUG, "Spawned swaybar %d", bar->pid);  	close(filedes[0]);  	size_t len;  	if (read(filedes[1], &len, sizeof(size_t)) == sizeof(size_t)) {  		char *buf = malloc(len);  		if(!buf) { -			wlr_log(L_ERROR, "Cannot allocate error string"); +			wlr_log(WLR_ERROR, "Cannot allocate error string");  			return;  		}  		if (read(filedes[1], buf, len)) { -			wlr_log(L_ERROR, "%s", buf); +			wlr_log(WLR_ERROR, "%s", buf);  		}  		free(buf);  	} @@ -244,7 +244,7 @@ void load_swaybars() {  			if (bar->pid != 0) {  				terminate_swaybar(bar->pid);  			} -			wlr_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); +			wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);  			invoke_swaybar(bar);  		}  	} diff --git a/sway/config/input.c b/sway/config/input.c index 17303ccc..8d687a6d 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -8,17 +8,18 @@  struct input_config *new_input_config(const char* identifier) {  	struct input_config *input = calloc(1, sizeof(struct input_config));  	if (!input) { -		wlr_log(L_DEBUG, "Unable to allocate input config"); +		wlr_log(WLR_DEBUG, "Unable to allocate input config");  		return NULL;  	} -	wlr_log(L_DEBUG, "new_input_config(%s)", identifier); +	wlr_log(WLR_DEBUG, "new_input_config(%s)", identifier);  	if (!(input->identifier = strdup(identifier))) {  		free(input); -		wlr_log(L_DEBUG, "Unable to allocate input config"); +		wlr_log(WLR_DEBUG, "Unable to allocate input config");  		return NULL;  	}  	input->tap = INT_MIN; +	input->tap_button_map = INT_MIN;  	input->drag_lock = INT_MIN;  	input->dwt = INT_MIN;  	input->send_events = INT_MIN; @@ -27,6 +28,7 @@ struct input_config *new_input_config(const char* identifier) {  	input->natural_scroll = INT_MIN;  	input->accel_profile = INT_MIN;  	input->pointer_accel = FLT_MIN; +	input->scroll_button = INT_MIN;  	input->scroll_method = INT_MIN;  	input->left_handed = INT_MIN;  	input->repeat_delay = INT_MIN; @@ -70,12 +72,18 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {  	if (src->scroll_method != INT_MIN) {  		dst->scroll_method = src->scroll_method;  	} +	if (src->scroll_button != INT_MIN) { +		dst->scroll_button = src->scroll_button; +	}  	if (src->send_events != INT_MIN) {  		dst->send_events = src->send_events;  	}  	if (src->tap != INT_MIN) {  		dst->tap = src->tap;  	} +	if (src->tap_button_map != INT_MIN) { +		dst->tap_button_map = src->tap_button_map; +	}  	if (src->xkb_layout) {  		free(dst->xkb_layout);  		dst->xkb_layout = strdup(src->xkb_layout); @@ -112,7 +120,7 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {  struct input_config *copy_input_config(struct input_config *ic) {  	struct input_config *copy = calloc(1, sizeof(struct input_config));  	if (copy == NULL) { -		wlr_log(L_ERROR, "could not allocate input config"); +		wlr_log(WLR_ERROR, "could not allocate input config");  		return NULL;  	}  	merge_input_config(copy, ic); diff --git a/sway/config/output.c b/sway/config/output.c index 648ded27..1bf9e5f1 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -90,7 +90,7 @@ static void set_mode(struct wlr_output *output, int width, int height,  		float refresh_rate) {  	int mhz = (int)(refresh_rate * 1000);  	if (wl_list_empty(&output->modes)) { -		wlr_log(L_DEBUG, "Assigning custom mode to %s", output->name); +		wlr_log(WLR_DEBUG, "Assigning custom mode to %s", output->name);  		wlr_output_set_custom_mode(output, width, height, mhz);  		return;  	} @@ -106,9 +106,9 @@ static void set_mode(struct wlr_output *output, int width, int height,  		}  	}  	if (!best) { -		wlr_log(L_ERROR, "Configured mode for %s not available", output->name); +		wlr_log(WLR_ERROR, "Configured mode for %s not available", output->name);  	} else { -		wlr_log(L_DEBUG, "Assigning configured mode to %s", output->name); +		wlr_log(WLR_DEBUG, "Assigning configured mode to %s", output->name);  		wlr_output_set_mode(output, best);  	}  } @@ -116,7 +116,7 @@ static void set_mode(struct wlr_output *output, int width, int height,  void terminate_swaybg(pid_t pid) {  	int ret = kill(pid, SIGTERM);  	if (ret != 0) { -		wlr_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid); +		wlr_log(WLR_ERROR, "Unable to terminate swaybg [pid: %d]", pid);  	} else {  		int status;  		waitpid(pid, &status, 0); @@ -144,22 +144,22 @@ void apply_output_config(struct output_config *oc, struct sway_container *output  	}  	if (oc && oc->width > 0 && oc->height > 0) { -		wlr_log(L_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, +		wlr_log(WLR_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width,  			oc->height, oc->refresh_rate);  		set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate);  	}  	if (oc && oc->scale > 0) { -		wlr_log(L_DEBUG, "Set %s scale to %f", oc->name, oc->scale); +		wlr_log(WLR_DEBUG, "Set %s scale to %f", oc->name, oc->scale);  		wlr_output_set_scale(wlr_output, oc->scale);  	}  	if (oc && oc->transform >= 0) { -		wlr_log(L_DEBUG, "Set %s transform to %d", oc->name, oc->transform); +		wlr_log(WLR_DEBUG, "Set %s transform to %d", oc->name, oc->transform);  		wlr_output_set_transform(wlr_output, oc->transform);  	}  	// Find position for it  	if (oc && (oc->x != -1 || oc->y != -1)) { -		wlr_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); +		wlr_log(WLR_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);  		wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y);  	} else {  		wlr_output_layout_add_auto(output_layout, wlr_output); @@ -187,7 +187,7 @@ void apply_output_config(struct output_config *oc, struct sway_container *output  			terminate_swaybg(output->sway_output->bg_pid);  		} -		wlr_log(L_DEBUG, "Setting background for output %d to %s", +		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", @@ -195,28 +195,30 @@ void apply_output_config(struct output_config *oc, struct sway_container *output  				output_i, oc->background, oc->background_option);  		char *command = malloc(len + 1);  		if (!command) { -			wlr_log(L_DEBUG, "Unable to allocate swaybg command"); +			wlr_log(WLR_DEBUG, "Unable to allocate swaybg command");  			return;  		}  		snprintf(command, len + 1, "%s %d %s %s",  				config->swaybg_command ? config->swaybg_command : "swaybg",  				output_i, oc->background, oc->background_option); -		wlr_log(L_DEBUG, "-> %s", command); +		wlr_log(WLR_DEBUG, "-> %s", command);  		char *const cmd[] = { "sh", "-c", command, NULL };  		output->sway_output->bg_pid = fork();  		if (output->sway_output->bg_pid == 0) {  			execvp(cmd[0], cmd); +		} else { +			free(command);  		}  	}  	if (oc && oc->dpms_state != DPMS_IGNORE) {  		switch (oc->dpms_state) {  		case DPMS_ON: -			wlr_log(L_DEBUG, "Turning on screen"); +			wlr_log(WLR_DEBUG, "Turning on screen");  			wlr_output_enable(wlr_output, true);  			break;  		case DPMS_OFF: -			wlr_log(L_DEBUG, "Turning off screen"); +			wlr_log(WLR_DEBUG, "Turning off screen");  			wlr_output_enable(wlr_output, false);  			break;  		case DPMS_IGNORE: diff --git a/sway/config/seat.c b/sway/config/seat.c index bd8b45c8..83dac4c0 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c @@ -7,11 +7,11 @@  struct seat_config *new_seat_config(const char* name) {  	struct seat_config *seat = calloc(1, sizeof(struct seat_config));  	if (!seat) { -		wlr_log(L_DEBUG, "Unable to allocate seat config"); +		wlr_log(WLR_DEBUG, "Unable to allocate seat config");  		return NULL;  	} -	wlr_log(L_DEBUG, "new_seat_config(%s)", name); +	wlr_log(WLR_DEBUG, "new_seat_config(%s)", name);  	seat->name = strdup(name);  	if (!sway_assert(seat->name, "could not allocate name for seat")) {  		free(seat); @@ -34,7 +34,7 @@ struct seat_attachment_config *seat_attachment_config_new() {  	struct seat_attachment_config *attachment =  		calloc(1, sizeof(struct seat_attachment_config));  	if (!attachment) { -		wlr_log(L_DEBUG, "cannot allocate attachment config"); +		wlr_log(WLR_DEBUG, "cannot allocate attachment config");  		return NULL;  	}  	return attachment; diff --git a/sway/criteria.c b/sway/criteria.c index d9f09ecc..e2b248de 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -37,7 +37,7 @@ void criteria_destroy(struct criteria *criteria) {  	pcre_free(criteria->con_mark);  	pcre_free(criteria->window_role);  	free(criteria->workspace); - +	free(criteria->cmdlist);  	free(criteria->raw);  	free(criteria);  } @@ -46,6 +46,31 @@ static int regex_cmp(const char *item, const pcre *regex) {  	return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0);  } +static int cmp_urgent(const void *_a, const void *_b) { +	struct sway_view *a = *(void **)_a; +	struct sway_view *b = *(void **)_b; + +	if (a->urgent.tv_sec < b->urgent.tv_sec) { +		return -1; +	} else if (a->urgent.tv_sec > b->urgent.tv_sec) { +		return 1; +	} +	if (a->urgent.tv_nsec < b->urgent.tv_nsec) { +		return -1; +	} else if (a->urgent.tv_nsec > b->urgent.tv_nsec) { +		return 1; +	} +	return 0; +} + +static void find_urgent_iterator(struct sway_container *swayc, void *data) { +	if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) { +		return; +	} +	list_t *urgent_views = data; +	list_add(urgent_views, swayc->sway_view); +} +  static bool criteria_matches_view(struct criteria *criteria,  		struct sway_view *view) {  	if (criteria->title) { @@ -133,8 +158,23 @@ static bool criteria_matches_view(struct criteria *criteria,  	}  	if (criteria->urgent) { -		// TODO -		return false; +		if (!view_is_urgent(view)) { +			return false; +		} +		list_t *urgent_views = create_list(); +		container_for_each_descendant_dfs(&root_container, +				find_urgent_iterator, urgent_views); +		list_stable_sort(urgent_views, cmp_urgent); +		struct sway_view *target; +		if (criteria->urgent == 'o') { // oldest +			target = urgent_views->items[0]; +		} else { // latest +			target = urgent_views->items[urgent_views->length - 1]; +		} +		list_free(urgent_views); +		if (view != target) { +			return false; +		}  	}  	if (criteria->workspace) { @@ -507,7 +547,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {  			}  			unescape(value);  		} -		wlr_log(L_DEBUG, "Found pair: %s=%s", name, value); +		wlr_log(WLR_DEBUG, "Found pair: %s=%s", name, value);  		if (!parse_token(criteria, name, value)) {  			*error_arg = error;  			goto cleanup; diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index e495790c..6575519d 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -13,3 +13,12 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,  		}  	}  } + +void desktop_damage_whole_container(struct sway_container *con) { +	for (int i = 0; i < root_container.children->length; ++i) { +		struct sway_container *cont = root_container.children->items[i]; +		if (cont->type == C_OUTPUT) { +			output_damage_whole_container(cont->sway_output, con); +		} +	} +} diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index c02ca26e..da17d0f2 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -9,7 +9,7 @@  static void handle_destroy(struct wl_listener *listener, void *data) {  	struct sway_idle_inhibitor_v1 *inhibitor =  		wl_container_of(listener, inhibitor, destroy); -	wlr_log(L_DEBUG, "Sway idle inhibitor destroyed"); +	wlr_log(WLR_DEBUG, "Sway idle inhibitor destroyed");  	wl_list_remove(&inhibitor->link);  	wl_list_remove(&inhibitor->destroy.link);  	idle_inhibit_v1_check_active(inhibitor->manager); @@ -20,7 +20,7 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {  	struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data;  	struct sway_idle_inhibit_manager_v1 *manager =  		wl_container_of(listener, manager, new_idle_inhibitor_v1); -	wlr_log(L_DEBUG, "New sway idle inhibitor"); +	wlr_log(WLR_DEBUG, "New sway idle inhibitor");  	struct sway_idle_inhibitor_v1 *inhibitor =  		calloc(1, sizeof(struct sway_idle_inhibitor_v1)); @@ -67,6 +67,7 @@ struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create(  	manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display);  	if (!manager->wlr_manager) { +		free(manager);  		return NULL;  	}  	manager->idle = idle; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index de1fe349..a7d96717 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -12,7 +12,6 @@  #include "sway/layers.h"  #include "sway/output.h"  #include "sway/server.h" -#include "sway/tree/arrange.h"  #include "sway/tree/layout.h"  #include "log.h" @@ -174,9 +173,9 @@ void arrange_layers(struct sway_output *output) {  	if (memcmp(&usable_area, &output->usable_area,  				sizeof(struct wlr_box)) != 0) { -		wlr_log(L_DEBUG, "Usable area changed, rearranging output"); +		wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");  		memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); -		arrange_and_commit(output->swayc); +		container_set_dirty(output->swayc);  	}  	// Arrange non-exlusive surfaces from top->bottom @@ -269,7 +268,7 @@ static void unmap(struct sway_layer_surface *sway_layer) {  static void handle_destroy(struct wl_listener *listener, void *data) {  	struct sway_layer_surface *sway_layer =  		wl_container_of(listener, sway_layer, destroy); -	wlr_log(L_DEBUG, "Layer surface destroyed (%s)", +	wlr_log(WLR_DEBUG, "Layer surface destroyed (%s)",  		sway_layer->layer_surface->namespace);  	if (sway_layer->layer_surface->mapped) {  		unmap(sway_layer); @@ -316,7 +315,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {  	struct wlr_layer_surface *layer_surface = data;  	struct sway_server *server =  		wl_container_of(listener, server, layer_shell_surface); -	wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d " +	wlr_log(WLR_DEBUG, "new layer surface: namespace %s layer %d anchor %d "  			"size %dx%d margin %d,%d,%d,%d",  		layer_surface->namespace, layer_surface->layer, layer_surface->layer,  		layer_surface->client_pending.desired_width, @@ -326,12 +325,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {  		layer_surface->client_pending.margin.bottom,  		layer_surface->client_pending.margin.left); -	struct sway_layer_surface *sway_layer = -		calloc(1, sizeof(struct sway_layer_surface)); -	if (!sway_layer) { -		return; -	} -  	if (!layer_surface->output) {  		// Assign last active output  		struct sway_container *output = NULL; @@ -353,6 +346,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {  		layer_surface->output = output->sway_output->wlr_output;  	} +	struct sway_layer_surface *sway_layer = +		calloc(1, sizeof(struct sway_layer_surface)); +	if (!sway_layer) { +		return; +	} +  	sway_layer->surface_commit.notify = handle_surface_commit;  	wl_signal_add(&layer_surface->surface->events.commit,  		&sway_layer->surface_commit); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8b50bc44..a9808406 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -204,11 +204,11 @@ bool output_has_opaque_lockscreen(struct sway_output *output,  		};  		pixman_region32_t surface_opaque_box;  		pixman_region32_init(&surface_opaque_box); -		pixman_region32_copy(&surface_opaque_box, &wlr_surface->current.opaque); +		pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region);  		pixman_region32_translate(&surface_opaque_box, -				sway_layer_surface->geo.x, sway_layer_surface->geo.y); -		bool contains = pixman_region32_contains_rectangle( -				&wlr_surface->current.opaque, &output_box); +			sway_layer_surface->geo.x, sway_layer_surface->geo.y); +		bool contains = pixman_region32_contains_rectangle(&surface_opaque_box, +			&output_box);  		pixman_region32_fini(&surface_opaque_box);  		if (contains) {  			return true; @@ -492,19 +492,21 @@ static void handle_destroy(struct wl_listener *listener, void *data) {  	output->wlr_output->data = NULL;  	free(output); -	arrange_and_commit(&root_container); +	arrange_windows(&root_container);  }  static void handle_mode(struct wl_listener *listener, void *data) {  	struct sway_output *output = wl_container_of(listener, output, mode);  	arrange_layers(output); -	arrange_and_commit(output->swayc); +	arrange_windows(output->swayc); +	transaction_commit_dirty();  }  static void handle_transform(struct wl_listener *listener, void *data) {  	struct sway_output *output = wl_container_of(listener, output, transform);  	arrange_layers(output); -	arrange_and_commit(output->swayc); +	arrange_windows(output->swayc); +	transaction_commit_dirty();  }  static void handle_scale_iterator(struct sway_container *view, void *data) { @@ -515,7 +517,8 @@ static void handle_scale(struct wl_listener *listener, void *data) {  	struct sway_output *output = wl_container_of(listener, output, scale);  	arrange_layers(output);  	container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); -	arrange_and_commit(output->swayc); +	arrange_windows(output->swayc); +	transaction_commit_dirty();  }  struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { @@ -525,7 +528,7 @@ struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) {  void handle_new_output(struct wl_listener *listener, void *data) {  	struct sway_server *server = wl_container_of(listener, server, new_output);  	struct wlr_output *wlr_output = data; -	wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); +	wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);  	struct sway_output *output = calloc(1, sizeof(struct sway_output));  	if (!output) { @@ -584,5 +587,6 @@ void output_enable(struct sway_output *output) {  	output->damage_destroy.notify = damage_handle_destroy;  	arrange_layers(output); -	arrange_and_commit(&root_container); +	arrange_windows(&root_container); +	transaction_commit_dirty();  } diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 43948f29..4c85e516 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -15,6 +15,7 @@  #include <wlr/util/region.h>  #include "log.h"  #include "sway/config.h" +#include "sway/debug.h"  #include "sway/input/input-manager.h"  #include "sway/input/seat.h"  #include "sway/layers.h" @@ -255,6 +256,10 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,  		render_view_surfaces(view, output, damage, view->swayc->alpha);  	} +	if (view->using_csd) { +		return; +	} +  	struct wlr_box box;  	float output_scale = output->wlr_output->scale;  	float color[4]; @@ -542,9 +547,6 @@ static void render_container(struct sway_output *output,  static void render_container_simple(struct sway_output *output,  		pixman_region32_t *damage, struct sway_container *con,  		bool parent_focused) { -	struct sway_seat *seat = input_manager_current_seat(input_manager); -	struct sway_container *focus = seat_get_focus(seat); -  	for (int i = 0; i < con->current.children->length; ++i) {  		struct sway_container *child = con->current.children->items[i]; @@ -555,11 +557,15 @@ static void render_container_simple(struct sway_output *output,  			struct wlr_texture *marks_texture;  			struct sway_container_state *state = &child->current; -			if (focus == child || parent_focused) { +			if (view_is_urgent(view)) { +				colors = &config->border_colors.urgent; +				title_texture = child->title_urgent; +				marks_texture = view->marks_urgent; +			} else if (state->focused || parent_focused) {  				colors = &config->border_colors.focused;  				title_texture = child->title_focused;  				marks_texture = view->marks_focused; -			} else if (seat_get_focus_inactive(seat, con) == child) { +			} else if (con->current.focused_inactive_child == child) {  				colors = &config->border_colors.focused_inactive;  				title_texture = child->title_focused_inactive;  				marks_texture = view->marks_focused_inactive; @@ -569,17 +575,19 @@ static void render_container_simple(struct sway_output *output,  				marks_texture = view->marks_unfocused;  			} -			if (state->border == B_NORMAL) { -				render_titlebar(output, damage, child, state->swayc_x, -						state->swayc_y, state->swayc_width, colors, -						title_texture, marks_texture); -			} else { -				render_top_border(output, damage, child, colors); +			if (!view->using_csd) { +				if (state->border == B_NORMAL) { +					render_titlebar(output, damage, child, state->swayc_x, +							state->swayc_y, state->swayc_width, colors, +							title_texture, marks_texture); +				} else { +					render_top_border(output, damage, child, colors); +				}  			}  			render_view(output, damage, child, colors);  		} else {  			render_container(output, damage, child, -					parent_focused || focus == child); +					parent_focused || child->current.focused);  		}  	}  } @@ -593,26 +601,34 @@ static void render_container_tabbed(struct sway_output *output,  	if (!con->current.children->length) {  		return;  	} -	struct sway_seat *seat = input_manager_current_seat(input_manager); -	struct sway_container *focus = seat_get_focus(seat); -	struct sway_container *current = seat_get_active_current_child(seat, con); -	struct border_colors *current_colors = &config->border_colors.unfocused;  	struct sway_container_state *pstate = &con->current; +	struct sway_container *current = pstate->focused_inactive_child; +	struct border_colors *current_colors = &config->border_colors.unfocused; + +	double width_gap_adjustment = 2 * pstate->current_gaps; +	int tab_width = +		(pstate->swayc_width - width_gap_adjustment) / pstate->children->length;  	// Render tabs -	for (int i = 0; i < con->current.children->length; ++i) { -		struct sway_container *child = con->current.children->items[i]; +	for (int i = 0; i < pstate->children->length; ++i) { +		struct sway_container *child = pstate->children->items[i];  		struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;  		struct sway_container_state *cstate = &child->current;  		struct border_colors *colors;  		struct wlr_texture *title_texture;  		struct wlr_texture *marks_texture; - -		if (focus == child || parent_focused) { +		bool urgent = view ? +			view_is_urgent(view) : container_has_urgent_child(child); + +		if (urgent) { +			colors = &config->border_colors.urgent; +			title_texture = child->title_urgent; +			marks_texture = view ? view->marks_urgent : NULL; +		} else if (cstate->focused || parent_focused) {  			colors = &config->border_colors.focused;  			title_texture = child->title_focused;  			marks_texture = view ? view->marks_focused : NULL; -		} else if (child == current) { +		} else if (child == pstate->focused_inactive_child) {  			colors = &config->border_colors.focused_inactive;  			title_texture = child->title_focused_inactive;  			marks_texture = view ? view->marks_focused_inactive : NULL; @@ -622,11 +638,12 @@ static void render_container_tabbed(struct sway_output *output,  			marks_texture = view ? view->marks_unfocused : NULL;  		} -		int tab_width = pstate->swayc_width / pstate->children->length; -		int x = pstate->swayc_x + tab_width * i; +		int x = cstate->swayc_x + tab_width * i; +  		// Make last tab use the remaining width of the parent  		if (i == pstate->children->length - 1) { -			tab_width = pstate->swayc_width - tab_width * i; +			tab_width = +				pstate->swayc_width - width_gap_adjustment - tab_width * i;  		}  		render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, @@ -638,13 +655,11 @@ static void render_container_tabbed(struct sway_output *output,  	}  	// Render surface and left/right/bottom borders -	if (current) { -		if (current->type == C_VIEW) { -			render_view(output, damage, current, current_colors); -		} else { -			render_container(output, damage, current, -					parent_focused || current == focus); -		} +	if (current->type == C_VIEW) { +		render_view(output, damage, current, current_colors); +	} else { +		render_container(output, damage, current, +				parent_focused || current->current.focused);  	}  } @@ -657,26 +672,32 @@ static void render_container_stacked(struct sway_output *output,  	if (!con->current.children->length) {  		return;  	} -	struct sway_seat *seat = input_manager_current_seat(input_manager); -	struct sway_container *focus = seat_get_focus(seat); -	struct sway_container *current = seat_get_active_current_child(seat, con); -	struct border_colors *current_colors = &config->border_colors.unfocused;  	struct sway_container_state *pstate = &con->current; +	struct sway_container *current = pstate->focused_inactive_child; +	struct border_colors *current_colors = &config->border_colors.unfocused; + +	size_t titlebar_height = container_titlebar_height();  	// Render titles -	for (int i = 0; i < con->current.children->length; ++i) { -		struct sway_container *child = con->current.children->items[i]; +	for (int i = 0; i < pstate->children->length; ++i) { +		struct sway_container *child = pstate->children->items[i];  		struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;  		struct sway_container_state *cstate = &child->current;  		struct border_colors *colors;  		struct wlr_texture *title_texture;  		struct wlr_texture *marks_texture; - -		if (focus == child || parent_focused) { +		bool urgent = view ? +			view_is_urgent(view) : container_has_urgent_child(child); + +		if (urgent) { +			colors = &config->border_colors.urgent; +			title_texture = child->title_urgent; +			marks_texture = view ? view->marks_urgent : NULL; +		} else if (cstate->focused || parent_focused) {  			colors = &config->border_colors.focused;  			title_texture = child->title_focused;  			marks_texture = view ? view->marks_focused : NULL; -		} else if (child == current) { +		} else if (child == pstate->focused_inactive_child) {  			colors = &config->border_colors.focused_inactive;  			title_texture = child->title_focused_inactive;  			marks_texture = view ? view->marks_focused_inactive : NULL; @@ -686,7 +707,7 @@ static void render_container_stacked(struct sway_output *output,  			marks_texture = view ? view->marks_unfocused : NULL;  		} -		int y = pstate->swayc_y + container_titlebar_height() * i; +		int y = cstate->swayc_y + titlebar_height * i;  		render_titlebar(output, damage, child, cstate->swayc_x, y,  				cstate->swayc_width, colors, title_texture, marks_texture); @@ -696,13 +717,11 @@ static void render_container_stacked(struct sway_output *output,  	}  	// Render surface and left/right/bottom borders -	if (current) { -		if (current->type == C_VIEW) { -			render_view(output, damage, current, current_colors); -		} else { -			render_container(output, damage, current, -					parent_focused || current == focus); -		} +	if (current->type == C_VIEW) { +		render_view(output, damage, current, current_colors); +	} else { +		render_container(output, damage, current, +				parent_focused || current->current.focused);  	}  } @@ -730,13 +749,15 @@ static void render_floating_container(struct sway_output *soutput,  		pixman_region32_t *damage, struct sway_container *con) {  	if (con->type == C_VIEW) {  		struct sway_view *view = con->sway_view; -		struct sway_seat *seat = input_manager_current_seat(input_manager); -		struct sway_container *focus = seat_get_focus(seat);  		struct border_colors *colors;  		struct wlr_texture *title_texture;  		struct wlr_texture *marks_texture; -		if (focus == con) { +		if (view_is_urgent(view)) { +			colors = &config->border_colors.urgent; +			title_texture = con->title_urgent; +			marks_texture = view->marks_urgent; +		} else if (con->current.focused) {  			colors = &config->border_colors.focused;  			title_texture = con->title_focused;  			marks_texture = view->marks_focused; @@ -746,12 +767,14 @@ static void render_floating_container(struct sway_output *soutput,  			marks_texture = view->marks_unfocused;  		} -		if (con->current.border == B_NORMAL) { -			render_titlebar(soutput, damage, con, con->current.swayc_x, -					con->current.swayc_y, con->current.swayc_width, colors, -					title_texture, marks_texture); -		} else if (con->current.border != B_NONE) { -			render_top_border(soutput, damage, con, colors); +		if (!view->using_csd) { +			if (con->current.border == B_NORMAL) { +				render_titlebar(soutput, damage, con, con->current.swayc_x, +						con->current.swayc_y, con->current.swayc_width, colors, +						title_texture, marks_texture); +			} else if (con->current.border != B_NONE) { +				render_top_border(soutput, damage, con, colors); +			}  		}  		render_view(soutput, damage, con, colors);  	} else { @@ -779,6 +802,8 @@ static void render_floating(struct sway_output *soutput,  	}  } +const char *damage_debug = NULL; +  void output_render(struct sway_output *output, struct timespec *when,  		pixman_region32_t *damage) {  	struct wlr_output *wlr_output = output->wlr_output; @@ -798,7 +823,6 @@ void output_render(struct sway_output *output, struct timespec *when,  		goto renderer_end;  	} -	const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG");  	if (damage_debug != NULL) {  		if (strcmp(damage_debug, "highlight") == 0) {  			wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); @@ -837,7 +861,11 @@ void output_render(struct sway_output *output, struct timespec *when,  		}  		// TODO: handle views smaller than the output -		render_view_surfaces(fullscreen_view, output, damage, 1.0f); +		if (fullscreen_view->swayc->instructions->length) { +			render_saved_view(fullscreen_view, output, damage, 1.0f); +		} else { +			render_view_surfaces(fullscreen_view, output, damage, 1.0f); +		}  		if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {  			render_unmanaged(output, damage, @@ -858,9 +886,7 @@ void output_render(struct sway_output *output, struct timespec *when,  		render_layer(output, damage,  			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); -		struct sway_seat *seat = input_manager_current_seat(input_manager); -		struct sway_container *focus = seat_get_focus(seat); -		render_container(output, damage, workspace, focus == workspace); +		render_container(output, damage, workspace, workspace->current.focused);  		render_floating(output, damage);  		render_unmanaged(output, damage, diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 179af617..fcfb0b51 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -19,14 +19,14 @@   * How long we should wait for views to respond to the configure before giving   * up and applying the transaction anyway.   */ -#define TIMEOUT_MS 200 +int txn_timeout_ms = 200;  /**   * If enabled, sway will always wait for the transaction timeout before   * applying it, rather than applying it when the views are ready. This allows us   * to observe the rendered state while a transaction is in progress.   */ -#define TRANSACTION_DEBUG false +bool txn_debug = false;  struct sway_transaction {  	struct wl_event_source *timer; @@ -47,7 +47,7 @@ struct sway_transaction_instruction {  	bool ready;  }; -struct sway_transaction *transaction_create() { +static struct sway_transaction *transaction_create() {  	struct sway_transaction *transaction =  		calloc(1, sizeof(struct sway_transaction));  	transaction->instructions = create_list(); @@ -139,25 +139,18 @@ static void copy_pending_state(struct sway_container *container,  		state->children = create_list();  		list_cat(state->children, container->children);  	} -} -static bool transaction_has_container(struct sway_transaction *transaction, -		struct sway_container *container) { -	for (int i = 0; i < transaction->instructions->length; ++i) { -		struct sway_transaction_instruction *instruction = -			transaction->instructions->items[i]; -		if (instruction->container == container) { -			return true; -		} +	struct sway_seat *seat = input_manager_current_seat(input_manager); +	state->focused = seat_get_focus(seat) == container; + +	if (container->type != C_VIEW) { +		state->focused_inactive_child = +			seat_get_active_child(seat, container);  	} -	return false;  } -void transaction_add_container(struct sway_transaction *transaction, +static void transaction_add_container(struct sway_transaction *transaction,  		struct sway_container *container) { -	if (transaction_has_container(transaction, container)) { -		return; -	}  	struct sway_transaction_instruction *instruction =  		calloc(1, sizeof(struct sway_transaction_instruction));  	instruction->transaction = transaction; @@ -175,7 +168,7 @@ void transaction_add_container(struct sway_transaction *transaction,   * Apply a transaction to the "current" state of the tree.   */  static void transaction_apply(struct sway_transaction *transaction) { -	wlr_log(L_DEBUG, "Applying transaction %p", transaction); +	wlr_log(WLR_DEBUG, "Applying transaction %p", transaction);  	if (server.debug_txn_timings) {  		struct timespec now;  		clock_gettime(CLOCK_MONOTONIC, &now); @@ -186,7 +179,7 @@ static void transaction_apply(struct sway_transaction *transaction) {  		float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 +  			(now.tv_nsec - commit->tv_nsec) / 1000000.0;  		float ms_total = ms_arranging + ms_waiting; -		wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " +		wlr_log(WLR_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, "  			"%.1fms total (%.1f frames if 60Hz)", transaction,  			ms_arranging, ms_waiting, ms_total, ms_total / (1000.0f / 60));  	} @@ -210,10 +203,12 @@ static void transaction_apply(struct sway_transaction *transaction) {  			.width = instruction->state.swayc_width,  			.height = instruction->state.swayc_height,  		}; -		for (int j = 0; j < root_container.children->length; ++j) { -			struct sway_container *output = root_container.children->items[j]; -			output_damage_box(output->sway_output, &old_box); -			output_damage_box(output->sway_output, &new_box); +		for (int j = 0; j < root_container.current.children->length; ++j) { +			struct sway_container *output = root_container.current.children->items[j]; +			if (output->sway_output) { +				output_damage_box(output->sway_output, &old_box); +				output_damage_box(output->sway_output, &new_box); +			}  		}  		// There are separate children lists for each instruction state, the @@ -251,7 +246,7 @@ static void transaction_progress_queue() {  static int handle_timeout(void *data) {  	struct sway_transaction *transaction = data; -	wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)", +	wlr_log(WLR_DEBUG, "Transaction %p timed out (%li waiting)",  			transaction, transaction->num_waiting);  	transaction->num_waiting = 0;  	transaction_progress_queue(); @@ -285,8 +280,8 @@ static bool should_configure(struct sway_container *con,  	return true;  } -void transaction_commit(struct sway_transaction *transaction) { -	wlr_log(L_DEBUG, "Transaction %p committing with %i instructions", +static void transaction_commit(struct sway_transaction *transaction) { +	wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions",  			transaction, transaction->instructions->length);  	transaction->num_waiting = 0;  	for (int i = 0; i < transaction->instructions->length; ++i) { @@ -319,7 +314,7 @@ void transaction_commit(struct sway_transaction *transaction) {  	} else {  		// There are no other transactions in progress, and this one has nothing  		// to wait for, so we can skip the queue. -		wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction); +		wlr_log(WLR_DEBUG, "Transaction %p has nothing to wait for", transaction);  		transaction_apply(transaction);  		transaction_destroy(transaction);  		idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); @@ -330,7 +325,7 @@ void transaction_commit(struct sway_transaction *transaction) {  		// Set up a timer which the views must respond within  		transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,  				handle_timeout, transaction); -		wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); +		wl_event_source_timer_update(transaction->timer, txn_timeout_ms);  	}  	// The debug tree shows the pending/live tree. Here is a good place to @@ -350,7 +345,7 @@ static void set_instruction_ready(  		struct timespec *start = &transaction->commit_time;  		float ms = (now.tv_sec - start->tv_sec) * 1000 +  			(now.tv_nsec - start->tv_nsec) / 1000000.0; -		wlr_log(L_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", +		wlr_log(WLR_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)",  				transaction,  				transaction->num_configures - transaction->num_waiting + 1,  				transaction->num_configures, ms, @@ -361,11 +356,11 @@ static void set_instruction_ready(  	// If all views are ready, apply the transaction.  	// If the transaction has timed out then its num_waiting will be 0 already.  	if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { -#if !TRANSACTION_DEBUG -		wlr_log(L_DEBUG, "Transaction %p is ready", transaction); -		wl_event_source_timer_update(transaction->timer, 0); -		transaction_progress_queue(); -#endif +		if (!txn_debug) { +			wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); +			wl_event_source_timer_update(transaction->timer, 0); +			transaction_progress_queue(); +		}  	}  } @@ -418,3 +413,17 @@ struct wlr_texture *transaction_get_saved_texture(struct sway_view *view,  	*height = instruction->saved_buffer_height;  	return instruction->saved_buffer->texture;  } + +void transaction_commit_dirty(void) { +	if (!server.dirty_containers->length) { +		return; +	} +	struct sway_transaction *transaction = transaction_create(); +	for (int i = 0; i < server.dirty_containers->length; ++i) { +		struct sway_container *container = server.dirty_containers->items[i]; +		transaction_add_container(transaction, container); +		container->dirty = false; +	} +	server.dirty_containers->length = 0; +	transaction_commit(transaction); +} diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index ac35a8d1..98c16faf 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -45,6 +45,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {  	view_child_destroy(&popup->child);  } +static void popup_unconstrain(struct sway_xdg_popup *popup) { +	struct sway_view *view = popup->child.view; +	struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; + +	struct sway_container *output = container_parent(view->swayc, C_OUTPUT); + +	// the output box expressed in the coordinate system of the toplevel parent +	// of the popup +	struct wlr_box output_toplevel_sx_box = { +		.x = output->x - view->x, +		.y = output->y - view->y, +		.width = output->width, +		.height = output->height, +	}; + +	wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); +} +  static struct sway_xdg_popup *popup_create(  		struct wlr_xdg_popup *wlr_popup, struct sway_view *view) {  	struct wlr_xdg_surface *xdg_surface = wlr_popup->base; @@ -55,12 +73,15 @@ static struct sway_xdg_popup *popup_create(  		return NULL;  	}  	view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); +	popup->wlr_xdg_surface = xdg_surface;  	wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);  	popup->new_popup.notify = popup_handle_new_popup;  	wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);  	popup->destroy.notify = popup_handle_destroy; +	popup_unconstrain(popup); +  	return popup;  } @@ -223,7 +244,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)  	view_set_fullscreen(view, e->fullscreen);  	struct sway_container *output = container_parent(view->swayc, C_OUTPUT); -	arrange_and_commit(output); +	arrange_windows(output); +	transaction_commit_dirty();  }  static void handle_unmap(struct wl_listener *listener, void *data) { @@ -260,10 +282,11 @@ static void handle_map(struct wl_listener *listener, void *data) {  	if (xdg_surface->toplevel->client_pending.fullscreen) {  		view_set_fullscreen(view, true);  		struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); -		arrange_and_commit(ws); +		arrange_windows(ws);  	} else { -		arrange_and_commit(view->swayc->parent); +		arrange_windows(view->swayc->parent);  	} +	transaction_commit_dirty();  	xdg_shell_view->commit.notify = handle_commit;  	wl_signal_add(&xdg_surface->surface->events.commit, @@ -304,11 +327,11 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {  	struct wlr_xdg_surface *xdg_surface = data;  	if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { -		wlr_log(L_DEBUG, "New xdg_shell popup"); +		wlr_log(WLR_DEBUG, "New xdg_shell popup");  		return;  	} -	wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", +	wlr_log(WLR_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",  		xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);  	wlr_xdg_surface_ping(xdg_surface); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 56bbb244..4d76f0a7 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -44,6 +44,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {  	view_child_destroy(&popup->child);  } +static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) { +	struct sway_view *view = popup->child.view; +	struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup; + +	struct sway_container *output = container_parent(view->swayc, C_OUTPUT); + +	// the output box expressed in the coordinate system of the toplevel parent +	// of the popup +	struct wlr_box output_toplevel_sx_box = { +		.x = output->x - view->x, +		.y = output->y - view->y, +		.width = output->width, +		.height = output->height, +	}; + +	wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); +} +  static struct sway_xdg_popup_v6 *popup_create(  		struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) {  	struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base; @@ -54,12 +72,15 @@ static struct sway_xdg_popup_v6 *popup_create(  		return NULL;  	}  	view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); +	popup->wlr_xdg_surface_v6 = xdg_surface;  	wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);  	popup->new_popup.notify = popup_handle_new_popup;  	wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);  	popup->destroy.notify = popup_handle_destroy; +	popup_unconstrain(popup); +  	return popup;  } @@ -218,7 +239,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)  	view_set_fullscreen(view, e->fullscreen);  	struct sway_container *output = container_parent(view->swayc, C_OUTPUT); -	arrange_and_commit(output); +	arrange_windows(output); +	transaction_commit_dirty();  }  static void handle_unmap(struct wl_listener *listener, void *data) { @@ -255,10 +277,11 @@ static void handle_map(struct wl_listener *listener, void *data) {  	if (xdg_surface->toplevel->client_pending.fullscreen) {  		view_set_fullscreen(view, true);  		struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); -		arrange_and_commit(ws); +		arrange_windows(ws);  	} else { -		arrange_and_commit(view->swayc->parent); +		arrange_windows(view->swayc->parent);  	} +	transaction_commit_dirty();  	xdg_shell_v6_view->commit.notify = handle_commit;  	wl_signal_add(&xdg_surface->surface->events.commit, @@ -295,11 +318,11 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {  	struct wlr_xdg_surface_v6 *xdg_surface = data;  	if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { -		wlr_log(L_DEBUG, "New xdg_shell_v6 popup"); +		wlr_log(WLR_DEBUG, "New xdg_shell_v6 popup");  		return;  	} -	wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", +	wlr_log(WLR_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'",  		xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);  	wlr_xdg_surface_v6_ping(xdg_surface); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index b2874cfe..9df7977d 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -119,7 +119,7 @@ static struct sway_xwayland_unmanaged *create_unmanaged(  	struct sway_xwayland_unmanaged *surface =  		calloc(1, sizeof(struct sway_xwayland_unmanaged));  	if (surface == NULL) { -		wlr_log(L_ERROR, "Allocation failed"); +		wlr_log(WLR_ERROR, "Allocation failed");  		return NULL;  	} @@ -246,6 +246,14 @@ static bool wants_floating(struct sway_view *view) {  	return false;  } +static bool has_client_side_decorations(struct sway_view *view) { +	if (xwayland_view_from_view(view) == NULL) { +		return false; +	} +	struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; +	return surface->decorations != WLR_XWAYLAND_SURFACE_DECORATIONS_ALL; +} +  static void _close(struct sway_view *view) {  	if (xwayland_view_from_view(view) == NULL) {  		return; @@ -269,6 +277,7 @@ static const struct sway_view_impl view_impl = {  	.set_tiled = set_tiled,  	.set_fullscreen = set_fullscreen,  	.wants_floating = wants_floating, +	.has_client_side_decorations = has_client_side_decorations,  	.close = _close,  	.destroy = destroy,  }; @@ -288,6 +297,10 @@ static void handle_commit(struct wl_listener *listener, void *data) {  	}  	view_damage_from(view); + +	if (view->allow_request_urgent) { +		view_set_urgent(view, (bool)xsurface->hints_urgency); +	}  }  static void handle_unmap(struct wl_listener *listener, void *data) { @@ -324,10 +337,11 @@ static void handle_map(struct wl_listener *listener, void *data) {  	if (xsurface->fullscreen) {  		view_set_fullscreen(view, true);  		struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); -		arrange_and_commit(ws); +		arrange_windows(ws);  	} else { -		arrange_and_commit(view->swayc->parent); +		arrange_windows(view->swayc->parent);  	} +	transaction_commit_dirty();  }  static void handle_destroy(struct wl_listener *listener, void *data) { @@ -383,7 +397,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)  	view_set_fullscreen(view, xsurface->fullscreen);  	struct sway_container *output = container_parent(view->swayc, C_OUTPUT); -	arrange_and_commit(output); +	arrange_windows(output); +	transaction_commit_dirty();  }  static void handle_set_title(struct wl_listener *listener, void *data) { @@ -432,12 +447,12 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {  	if (wlr_xwayland_surface_is_unmanaged(xsurface) ||  			xsurface->override_redirect) { -		wlr_log(L_DEBUG, "New xwayland unmanaged surface"); +		wlr_log(WLR_DEBUG, "New xwayland unmanaged surface");  		create_unmanaged(xsurface);  		return;  	} -	wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'", +	wlr_log(WLR_DEBUG, "New xwayland surface title='%s' class='%s'",  		xsurface->title, xsurface->class);  	struct sway_xwayland_view *xwayland_view = @@ -490,7 +505,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) {  	xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL);  	int err = xcb_connection_has_error(xcb_conn);  	if (err) { -		wlr_log(L_ERROR, "XCB connect failed: %d", err); +		wlr_log(WLR_ERROR, "XCB connect failed: %d", err);  		return;  	} @@ -509,7 +524,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) {  		free(reply);  		if (error != NULL) { -			wlr_log(L_ERROR, "could not resolve atom %s, X11 error code %d", +			wlr_log(WLR_ERROR, "could not resolve atom %s, X11 error code %d",  				atom_map[i], error->error_code);  			free(error);  			break; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index a2f11557..7a9f3ed7 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -10,6 +10,7 @@  #include <wlr/types/wlr_idle.h>  #include "list.h"  #include "log.h" +#include "sway/desktop/transaction.h"  #include "sway/input/cursor.h"  #include "sway/layers.h"  #include "sway/output.h" @@ -219,6 +220,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,  		struct sway_drag_icon *drag_icon = wlr_drag_icon->data;  		drag_icon_update_position(drag_icon);  	} +	transaction_commit_dirty();  }  static void handle_cursor_motion(struct wl_listener *listener, void *data) { @@ -278,6 +280,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,  	wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,  			time_msec, button, state); +	transaction_commit_dirty();  }  static void handle_cursor_button(struct wl_listener *listener, void *data) { @@ -474,7 +477,7 @@ static void handle_request_set_cursor(struct wl_listener *listener,  	// TODO: check cursor mode  	if (focused_client == NULL ||  			event->seat_client->client != focused_client) { -		wlr_log(L_DEBUG, "denying request to set cursor from unfocused client"); +		wlr_log(WLR_DEBUG, "denying request to set cursor from unfocused client");  		return;  	} diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 98f7d7cf..0b7cb766 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -61,7 +61,7 @@ static char *get_device_identifier(struct wlr_input_device *device) {  	int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1;  	char *identifier = malloc(len);  	if (!identifier) { -		wlr_log(L_ERROR, "Unable to allocate unique input device name"); +		wlr_log(WLR_ERROR, "Unable to allocate unique input device name");  		return NULL;  	} @@ -104,77 +104,89 @@ static void input_manager_libinput_config_pointer(  	}  	libinput_device = wlr_libinput_get_device_handle(wlr_device); -	wlr_log(L_DEBUG, "input_manager_libinput_config_pointer(%s)", +	wlr_log(WLR_DEBUG, "input_manager_libinput_config_pointer(%s)",  		ic->identifier);  	if (ic->accel_profile != INT_MIN) { -		wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)", +		wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)",  			ic->identifier, ic->accel_profile);  		libinput_device_config_accel_set_profile(libinput_device,  			ic->accel_profile);  	}  	if (ic->click_method != INT_MIN) { -		wlr_log(L_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)", +		wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)",  			ic->identifier, ic->click_method);  		libinput_device_config_click_set_method(libinput_device,  			ic->click_method);  	}  	if (ic->drag_lock != INT_MIN) { -		wlr_log(L_DEBUG, +		wlr_log(WLR_DEBUG,  			"libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",  			ic->identifier, ic->click_method);  		libinput_device_config_tap_set_drag_lock_enabled(libinput_device,  			ic->drag_lock);  	}  	if (ic->dwt != INT_MIN) { -		wlr_log(L_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)", +		wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)",  			ic->identifier, ic->dwt);  		libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt);  	}  	if (ic->left_handed != INT_MIN) { -		wlr_log(L_DEBUG, +		wlr_log(WLR_DEBUG,  			"libinput_config_pointer(%s) left_handed_set_enabled(%d)",  			ic->identifier, ic->left_handed);  		libinput_device_config_left_handed_set(libinput_device,  			ic->left_handed);  	}  	if (ic->middle_emulation != INT_MIN) { -		wlr_log(L_DEBUG, +		wlr_log(WLR_DEBUG,  			"libinput_config_pointer(%s) middle_emulation_set_enabled(%d)",  			ic->identifier, ic->middle_emulation);  		libinput_device_config_middle_emulation_set_enabled(libinput_device,  			ic->middle_emulation);  	}  	if (ic->natural_scroll != INT_MIN) { -		wlr_log(L_DEBUG, +		wlr_log(WLR_DEBUG,  			"libinput_config_pointer(%s) natural_scroll_set_enabled(%d)",  			ic->identifier, ic->natural_scroll);  		libinput_device_config_scroll_set_natural_scroll_enabled(  			libinput_device, ic->natural_scroll);  	}  	if (ic->pointer_accel != FLT_MIN) { -		wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)", +		wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)",  			ic->identifier, ic->pointer_accel);  		libinput_device_config_accel_set_speed(libinput_device,  			ic->pointer_accel);  	} +	if (ic->scroll_button != INT_MIN) { +		wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) scroll_set_button(%d)", +			ic->identifier, ic->scroll_button); +		libinput_device_config_scroll_set_button(libinput_device, +			ic->scroll_button); +	}  	if (ic->scroll_method != INT_MIN) { -		wlr_log(L_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)", +		wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)",  			ic->identifier, ic->scroll_method);  		libinput_device_config_scroll_set_method(libinput_device,  			ic->scroll_method);  	}  	if (ic->send_events != INT_MIN) { -		wlr_log(L_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)", +		wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)",  			ic->identifier, ic->send_events);  		libinput_device_config_send_events_set_mode(libinput_device,  			ic->send_events);  	}  	if (ic->tap != INT_MIN) { -		wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)", +		wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)",  			ic->identifier, ic->tap);  		libinput_device_config_tap_set_enabled(libinput_device, ic->tap);  	} +	if (ic->tap_button_map != INT_MIN) { +		wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)", +			ic->identifier, ic->tap); +		libinput_device_config_tap_set_button_map(libinput_device, +			ic->tap_button_map); +	}  }  static void handle_device_destroy(struct wl_listener *listener, void *data) { @@ -187,7 +199,7 @@ static void handle_device_destroy(struct wl_listener *listener, void *data) {  		return;  	} -	wlr_log(L_DEBUG, "removing device: '%s'", +	wlr_log(WLR_DEBUG, "removing device: '%s'",  		input_device->identifier);  	struct sway_seat *seat = NULL; @@ -217,7 +229,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) {  	input_device->identifier = get_device_identifier(device);  	wl_list_insert(&input->devices, &input_device->link); -	wlr_log(L_DEBUG, "adding device: '%s'", +	wlr_log(WLR_DEBUG, "adding device: '%s'",  		input_device->identifier);  	if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { @@ -229,7 +241,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) {  	struct sway_seat *seat = NULL;  	if (!input_has_seat_configuration(input)) { -		wlr_log(L_DEBUG, "no seat configuration, using default seat"); +		wlr_log(WLR_DEBUG, "no seat configuration, using default seat");  		seat = input_manager_get_seat(input, default_seat);  		seat_add_device(seat, input_device);  		return; @@ -259,7 +271,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) {  	}  	if (!added) { -		wlr_log(L_DEBUG, +		wlr_log(WLR_DEBUG,  			"device '%s' is not configured on any seats",  			input_device->identifier);  	} @@ -282,7 +294,7 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data)  		seat_set_exclusive_client(seat, NULL);  		struct sway_container *previous = seat_get_focus(seat);  		if (previous) { -			wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, +			wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous,  					container_type_to_str(previous->type), previous->name);  			// Hack to get seat to re-focus the return value of get_focus  			seat_set_focus(seat, previous->parent); @@ -359,7 +371,7 @@ void input_manager_apply_input_config(struct sway_input_manager *input,  void input_manager_apply_seat_config(struct sway_input_manager *input,  		struct seat_config *seat_config) { -	wlr_log(L_DEBUG, "applying new seat config for seat %s", +	wlr_log(WLR_DEBUG, "applying new seat config for seat %s",  		seat_config->name);  	struct sway_seat *seat = input_manager_get_seat(input, seat_config->name);  	if (!seat) { diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 182536de..ede38519 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -3,6 +3,7 @@  #include <wlr/backend/multi.h>  #include <wlr/backend/session.h>  #include <wlr/types/wlr_idle.h> +#include "sway/desktop/transaction.h"  #include "sway/input/seat.h"  #include "sway/input/keyboard.h"  #include "sway/input/input-manager.h" @@ -108,7 +109,7 @@ static void get_active_binding(const struct sway_shortcut_state *state,  		}  		if (*current_binding && *current_binding != binding) { -			wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", +			wlr_log(WLR_DEBUG, "encountered duplicate bindings %d and %d",  					(*current_binding)->order, binding->order);  		} else {  			*current_binding = binding; @@ -122,12 +123,13 @@ static void get_active_binding(const struct sway_shortcut_state *state,   */  static void keyboard_execute_command(struct sway_keyboard *keyboard,  		struct sway_binding *binding) { -	wlr_log(L_DEBUG, "running command for binding: %s", +	wlr_log(WLR_DEBUG, "running command for binding: %s",  		binding->command);  	config->handler_context.seat = keyboard->seat_device->sway_seat;  	struct cmd_results *results = execute_command(binding->command, NULL); +	transaction_commit_dirty();  	if (results->status != CMD_SUCCESS) { -		wlr_log(L_DEBUG, "could not run command for binding: %s (%s)", +		wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",  			binding->command, results->error);  	}  	free_cmd_results(results); @@ -386,7 +388,7 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {  		xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);  	if (!keymap) { -		wlr_log(L_DEBUG, "cannot configure keyboard: keymap does not exist"); +		wlr_log(WLR_DEBUG, "cannot configure keyboard: keymap does not exist");  		xkb_context_unref(context);  		return;  	} diff --git a/sway/input/seat.c b/sway/input/seat.c index 6c5abcd8..12b1fab5 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -75,7 +75,7 @@ static void seat_send_activate(struct sway_container *con,  		struct sway_seat *seat) {  	if (con->type == C_VIEW) {  		if (!seat_is_input_allowed(seat, con->sway_view->surface)) { -			wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited"); +			wlr_log(WLR_DEBUG, "Refusing to set focus, input is inhibited");  			return;  		}  		view_set_activated(con->sway_view, true); @@ -219,7 +219,7 @@ static struct sway_seat_container *seat_container_from_container(  	seat_con = calloc(1, sizeof(struct sway_seat_container));  	if (seat_con == NULL) { -		wlr_log(L_ERROR, "could not allocate seat container"); +		wlr_log(WLR_ERROR, "could not allocate seat container");  		return NULL;  	} @@ -301,7 +301,7 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) {  	struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon));  	if (icon == NULL) { -		wlr_log(L_ERROR, "Allocation failed"); +		wlr_log(WLR_ERROR, "Allocation failed");  		return;  	}  	icon->seat = seat; @@ -391,7 +391,7 @@ static void seat_apply_input_config(struct sway_seat *seat,  	struct input_config *ic = input_device_get_config(  			sway_device->input_device);  	if (ic != NULL) { -		wlr_log(L_DEBUG, "Applying input config to %s", +		wlr_log(WLR_DEBUG, "Applying input config to %s",  			sway_device->input_device->identifier);  		mapped_to_output = ic->mapped_to_output; @@ -401,7 +401,7 @@ static void seat_apply_input_config(struct sway_seat *seat,  		mapped_to_output = sway_device->input_device->wlr_device->output_name;  	}  	if (mapped_to_output != NULL) { -		wlr_log(L_DEBUG, "Mapping input device %s to output %s", +		wlr_log(WLR_DEBUG, "Mapping input device %s to output %s",  			sway_device->input_device->identifier, mapped_to_output);  		struct sway_container *output = NULL;  		for (int i = 0; i < root_container.children->length; ++i) { @@ -415,7 +415,7 @@ static void seat_apply_input_config(struct sway_seat *seat,  			wlr_cursor_map_input_to_output(seat->cursor->cursor,  				sway_device->input_device->wlr_device,  				output->sway_output->wlr_output); -			wlr_log(L_DEBUG, "Mapped to output %s", output->name); +			wlr_log(WLR_DEBUG, "Mapped to output %s", output->name);  		}  	}  } @@ -495,7 +495,7 @@ void seat_configure_device(struct sway_seat *seat,  			seat_configure_tablet_tool(seat, seat_device);  			break;  		case WLR_INPUT_DEVICE_TABLET_PAD: -			wlr_log(L_DEBUG, "TODO: configure tablet pad"); +			wlr_log(WLR_DEBUG, "TODO: configure tablet pad");  			break;  	}  } @@ -510,11 +510,11 @@ void seat_add_device(struct sway_seat *seat,  	struct sway_seat_device *seat_device =  		calloc(1, sizeof(struct sway_seat_device));  	if (!seat_device) { -		wlr_log(L_DEBUG, "could not allocate seat device"); +		wlr_log(WLR_DEBUG, "could not allocate seat device");  		return;  	} -	wlr_log(L_DEBUG, "adding device %s to seat %s", +	wlr_log(WLR_DEBUG, "adding device %s to seat %s",  		input_device->identifier, seat->wlr_seat->name);  	seat_device->sway_seat = seat; @@ -533,7 +533,7 @@ void seat_remove_device(struct sway_seat *seat,  		return;  	} -	wlr_log(L_DEBUG, "removing device %s from seat %s", +	wlr_log(WLR_DEBUG, "removing device %s from seat %s",  		input_device->identifier, seat->wlr_seat->name);  	seat_device_destroy(seat_device); @@ -594,6 +594,12 @@ static void seat_send_unfocus(struct sway_container *container,  	}  } +static int handle_urgent_timeout(void *data) { +	struct sway_view *view = data; +	view_set_urgent(view, false); +	return 0; +} +  void seat_set_focus_warp(struct sway_seat *seat,  		struct sway_container *container, bool warp) {  	if (seat->focused_layer) { @@ -649,6 +655,7 @@ void seat_set_focus_warp(struct sway_seat *seat,  		while (parent) {  			wl_list_remove(&parent->link);  			wl_list_insert(&seat->focus_stack, &parent->link); +			container_set_dirty(parent->container);  			parent =  				seat_container_from_container(seat, @@ -661,9 +668,33 @@ void seat_set_focus_warp(struct sway_seat *seat,  		if (last_focus) {  			seat_send_unfocus(last_focus, seat);  		} -  		seat_send_focus(container, seat); -		container_damage_whole(container->parent); + +		container_set_dirty(container); +		container_set_dirty(container->parent); // for focused_inactive_child +		if (last_focus) { +			container_set_dirty(last_focus); +		} +	} + +	// If urgent, start a timer to unset it +	if (container && container->type == C_VIEW && +			view_is_urgent(container->sway_view) && +			!container->sway_view->urgent_timer) { +		struct sway_view *view = container->sway_view; +		view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, +				handle_urgent_timeout, view); +		wl_event_source_timer_update(view->urgent_timer, 1000); +	} + +	// If we've focused a floating container, bring it to the front. +	// We do this by putting it at the end of the floating list. +	// This must happen for both the pending and current children lists. +	if (container && container_is_floating(container)) { +		list_move_to_end(container->parent->children, container); +		if (container_has_ancestor(container, container->current.parent)) { +			list_move_to_end(container->parent->current.children, container); +		}  	}  	// clean up unfocused empty workspace on new output @@ -707,10 +738,6 @@ void seat_set_focus_warp(struct sway_seat *seat,  		}  	} -	if (last_focus) { -		container_damage_whole(last_focus); -	} -  	if (last_workspace && last_workspace != new_workspace) {  		cursor_send_pointer_motion(seat->cursor, 0, true);  	} @@ -752,7 +779,7 @@ void seat_set_focus_layer(struct sway_seat *seat,  		struct sway_container *previous =  			seat_get_focus_inactive(seat, &root_container);  		if (previous) { -			wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, +			wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous,  					container_type_to_str(previous->type), previous->name);  			// Hack to get seat to re-focus the return value of get_focus  			seat_set_focus(seat, previous->parent); @@ -830,18 +857,6 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat,  	return NULL;  } -struct sway_container *seat_get_active_current_child(struct sway_seat *seat, -		struct sway_container *container) { -	struct sway_seat_container *current = NULL; -	wl_list_for_each(current, &seat->focus_stack, link) { -		if (current->container->current.parent == container && -				current->container->current.layout != L_FLOATING) { -			return current->container; -		} -	} -	return NULL; -} -  struct sway_container *seat_get_focus(struct sway_seat *seat) {  	if (!seat->has_focus) {  		return NULL; diff --git a/sway/ipc-json.c b/sway/ipc-json.c index b9289e25..c49ea47e 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -2,6 +2,7 @@  #include <stdio.h>  #include <ctype.h>  #include "log.h" +#include "sway/config.h"  #include "sway/ipc-json.h"  #include "sway/tree/container.h"  #include "sway/tree/workspace.h" @@ -41,6 +42,7 @@ json_object *ipc_json_get_version() {  	json_object_object_add(version, "major", json_object_new_int(major));  	json_object_object_add(version, "minor", json_object_new_int(minor));  	json_object_object_add(version, "patch", json_object_new_int(patch)); +	json_object_object_add(version, "loaded_config_file_name", json_object_new_string(config->current_config_path));  	return version;  } @@ -168,7 +170,8 @@ static void ipc_json_describe_workspace(struct sway_container *workspace,  	json_object_object_add(object, "output", workspace->parent ?  			json_object_new_string(workspace->parent->name) : NULL);  	json_object_object_add(object, "type", json_object_new_string("workspace")); -	json_object_object_add(object, "urgent", json_object_new_boolean(false)); +	json_object_object_add(object, "urgent", +			json_object_new_boolean(workspace->sway_workspace->urgent));  	json_object_object_add(object, "representation", workspace->formatted_title ?  			json_object_new_string(workspace->formatted_title) : NULL); @@ -194,6 +197,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object  		json_object_object_add(object, "layout",  			json_object_new_string(ipc_json_layout_description(c->layout)));  	} + +	bool urgent = c->type == C_VIEW ? +		view_is_urgent(c->sway_view) : container_has_urgent_child(c); +	json_object_object_add(object, "urgent", json_object_new_boolean(urgent));  }  static void focus_inactive_children_iterator(struct sway_container *c, void *data) { diff --git a/sway/ipc-server.c b/sway/ipc-server.c index abdaa237..be703915 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -17,6 +17,8 @@  #include <unistd.h>  #include <wayland-server.h>  #include "sway/commands.h" +#include "sway/config.h" +#include "sway/desktop/transaction.h"  #include "sway/ipc-json.h"  #include "sway/ipc-server.h"  #include "sway/output.h" @@ -138,32 +140,32 @@ struct sockaddr_un *ipc_user_sockaddr(void) {  int ipc_handle_connection(int fd, uint32_t mask, void *data) {  	(void) fd;  	struct sway_server *server = data; -	wlr_log(L_DEBUG, "Event on IPC listening socket"); +	wlr_log(WLR_DEBUG, "Event on IPC listening socket");  	assert(mask == WL_EVENT_READABLE);  	int client_fd = accept(ipc_socket, NULL, NULL);  	if (client_fd == -1) { -		wlr_log_errno(L_ERROR, "Unable to accept IPC client connection"); +		wlr_log_errno(WLR_ERROR, "Unable to accept IPC client connection");  		return 0;  	}  	int flags;  	if ((flags = fcntl(client_fd, F_GETFD)) == -1  			|| fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { -		wlr_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket"); +		wlr_log_errno(WLR_ERROR, "Unable to set CLOEXEC on IPC client socket");  		close(client_fd);  		return 0;  	}  	if ((flags = fcntl(client_fd, F_GETFL)) == -1  			|| fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { -		wlr_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket"); +		wlr_log_errno(WLR_ERROR, "Unable to set NONBLOCK on IPC client socket");  		close(client_fd);  		return 0;  	}  	struct ipc_client *client = malloc(sizeof(struct ipc_client));  	if (!client) { -		wlr_log(L_ERROR, "Unable to allocate ipc client"); +		wlr_log(WLR_ERROR, "Unable to allocate ipc client");  		close(client_fd);  		return 0;  	} @@ -179,12 +181,12 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) {  	client->write_buffer_len = 0;  	client->write_buffer = malloc(client->write_buffer_size);  	if (!client->write_buffer) { -		wlr_log(L_ERROR, "Unable to allocate ipc client write buffer"); +		wlr_log(WLR_ERROR, "Unable to allocate ipc client write buffer");  		close(client_fd);  		return 0;  	} -	wlr_log(L_DEBUG, "New client: fd %d", client_fd); +	wlr_log(WLR_DEBUG, "New client: fd %d", client_fd);  	list_add(ipc_client_list, client);  	return 0;  } @@ -195,22 +197,22 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {  	struct ipc_client *client = data;  	if (mask & WL_EVENT_ERROR) { -		wlr_log(L_ERROR, "IPC Client socket error, removing client"); +		wlr_log(WLR_ERROR, "IPC Client socket error, removing client");  		ipc_client_disconnect(client);  		return 0;  	}  	if (mask & WL_EVENT_HANGUP) { -		wlr_log(L_DEBUG, "Client %d hung up", client->fd); +		wlr_log(WLR_DEBUG, "Client %d hung up", client->fd);  		ipc_client_disconnect(client);  		return 0;  	} -	wlr_log(L_DEBUG, "Client %d readable", client->fd); +	wlr_log(WLR_DEBUG, "Client %d readable", client->fd);  	int read_available;  	if (ioctl(client_fd, FIONREAD, &read_available) == -1) { -		wlr_log_errno(L_INFO, "Unable to read IPC socket buffer size"); +		wlr_log_errno(WLR_INFO, "Unable to read IPC socket buffer size");  		ipc_client_disconnect(client);  		return 0;  	} @@ -232,13 +234,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {  	// Should be fully available, because read_available >= ipc_header_size  	ssize_t received = recv(client_fd, buf, ipc_header_size, 0);  	if (received == -1) { -		wlr_log_errno(L_INFO, "Unable to receive header from IPC client"); +		wlr_log_errno(WLR_INFO, "Unable to receive header from IPC client");  		ipc_client_disconnect(client);  		return 0;  	}  	if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { -		wlr_log(L_DEBUG, "IPC header check failed"); +		wlr_log(WLR_DEBUG, "IPC header check failed");  		ipc_client_disconnect(client);  		return 0;  	} @@ -272,7 +274,7 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event)  		}  		client->current_command = event;  		if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { -			wlr_log_errno(L_INFO, "Unable to send reply to IPC client"); +			wlr_log_errno(WLR_INFO, "Unable to send reply to IPC client");  			/* ipc_send_reply destroys client on error, which also  			 * removes it from the list, so we need to process  			 * current index again */ @@ -286,7 +288,7 @@ void ipc_event_workspace(struct sway_container *old,  	if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) {  		return;  	} -	wlr_log(L_DEBUG, "Sending workspace::%s event", change); +	wlr_log(WLR_DEBUG, "Sending workspace::%s event", change);  	json_object *obj = json_object_new_object();  	json_object_object_add(obj, "change", json_object_new_string(change));  	if (strcmp("focus", change) == 0) { @@ -314,7 +316,7 @@ void ipc_event_window(struct sway_container *window, const char *change) {  	if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) {  		return;  	} -	wlr_log(L_DEBUG, "Sending window::%s event", change); +	wlr_log(WLR_DEBUG, "Sending window::%s event", change);  	json_object *obj = json_object_new_object();  	json_object_object_add(obj, "change", json_object_new_string(change));  	json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); @@ -328,7 +330,7 @@ void ipc_event_barconfig_update(struct bar_config *bar) {  	if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) {  		return;  	} -	wlr_log(L_DEBUG, "Sending barconfig_update event"); +	wlr_log(WLR_DEBUG, "Sending barconfig_update event");  	json_object *json = ipc_json_describe_bar_config(bar);  	const char *json_string = json_object_to_json_string(json); @@ -340,7 +342,7 @@ void ipc_event_mode(const char *mode, bool pango) {  	if (!ipc_has_event_listeners(IPC_EVENT_MODE)) {  		return;  	} -	wlr_log(L_DEBUG, "Sending mode::%s event", mode); +	wlr_log(WLR_DEBUG, "Sending mode::%s event", mode);  	json_object *obj = json_object_new_object();  	json_object_object_add(obj, "change", json_object_new_string(mode));  	json_object_object_add(obj, "pango_markup", @@ -355,13 +357,13 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {  	struct ipc_client *client = data;  	if (mask & WL_EVENT_ERROR) { -		wlr_log(L_ERROR, "IPC Client socket error, removing client"); +		wlr_log(WLR_ERROR, "IPC Client socket error, removing client");  		ipc_client_disconnect(client);  		return 0;  	}  	if (mask & WL_EVENT_HANGUP) { -		wlr_log(L_DEBUG, "Client %d hung up", client->fd); +		wlr_log(WLR_DEBUG, "Client %d hung up", client->fd);  		ipc_client_disconnect(client);  		return 0;  	} @@ -370,14 +372,14 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {  		return 0;  	} -	wlr_log(L_DEBUG, "Client %d writable", client->fd); +	wlr_log(WLR_DEBUG, "Client %d writable", client->fd);  	ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);  	if (written == -1 && errno == EAGAIN) {  		return 0;  	} else if (written == -1) { -		wlr_log_errno(L_INFO, "Unable to send data from queue to IPC client"); +		wlr_log_errno(WLR_INFO, "Unable to send data from queue to IPC client");  		ipc_client_disconnect(client);  		return 0;  	} @@ -400,7 +402,7 @@ void ipc_client_disconnect(struct ipc_client *client) {  	shutdown(client->fd, SHUT_RDWR); -	wlr_log(L_INFO, "IPC Client %d disconnected", client->fd); +	wlr_log(WLR_INFO, "IPC Client %d disconnected", client->fd);  	wl_event_source_remove(client->event_source);  	if (client->writable_event_source) {  		wl_event_source_remove(client->writable_event_source); @@ -461,7 +463,7 @@ void ipc_client_handle_command(struct ipc_client *client) {  	char *buf = malloc(client->payload_length + 1);  	if (!buf) { -		wlr_log_errno(L_INFO, "Unable to allocate IPC payload"); +		wlr_log_errno(WLR_INFO, "Unable to allocate IPC payload");  		ipc_client_disconnect(client);  		return;  	} @@ -470,7 +472,7 @@ void ipc_client_handle_command(struct ipc_client *client) {  		ssize_t received = recv(client->fd, buf, client->payload_length, 0);  		if (received == -1)  		{ -			wlr_log_errno(L_INFO, "Unable to receive payload from IPC client"); +			wlr_log_errno(WLR_INFO, "Unable to receive payload from IPC client");  			ipc_client_disconnect(client);  			free(buf);  			return; @@ -483,6 +485,7 @@ void ipc_client_handle_command(struct ipc_client *client) {  	case IPC_COMMAND:  	{  		struct cmd_results *results = execute_command(buf, NULL); +		transaction_commit_dirty();  		char *json = cmd_results_to_json(results);  		int length = strlen(json);  		client_valid = ipc_send_reply(client, json, (uint32_t)length); @@ -533,7 +536,7 @@ void ipc_client_handle_command(struct ipc_client *client) {  		struct json_object *request = json_tokener_parse(buf);  		if (request == NULL) {  			client_valid = ipc_send_reply(client, "{\"success\": false}", 18); -			wlr_log_errno(L_INFO, "Failed to read request"); +			wlr_log_errno(WLR_INFO, "Failed to read request");  			goto exit_cleanup;  		} @@ -556,7 +559,7 @@ void ipc_client_handle_command(struct ipc_client *client) {  				client_valid =  					ipc_send_reply(client, "{\"success\": false}", 18);  				json_object_put(request); -				wlr_log_errno(L_INFO, "Failed to parse request"); +				wlr_log_errno(WLR_INFO, "Failed to parse request");  				goto exit_cleanup;  			}  		} @@ -667,8 +670,33 @@ void ipc_client_handle_command(struct ipc_client *client) {  		goto exit_cleanup;  	} +	case IPC_GET_BINDING_MODES: +	{ +		json_object *modes = json_object_new_array(); +		for (int i = 0; i < config->modes->length; i++) { +			struct sway_mode *mode = config->modes->items[i]; +			json_object_array_add(modes, json_object_new_string(mode->name)); +		} +		const char *json_string = json_object_to_json_string(modes); +		client_valid = +			ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); +		json_object_put(modes); // free +		goto exit_cleanup; +	} + +	case IPC_GET_CONFIG: +	{ +		json_object *json = json_object_new_object(); +		json_object_object_add(json, "config", json_object_new_string(config->current_config)); +		const char *json_string = json_object_to_json_string(json); +		client_valid = +			ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); +		json_object_put(json); // free +		goto exit_cleanup; +    } +  	default: -		wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command); +		wlr_log(WLR_INFO, "Unknown IPC command type %i", client->current_command);  		goto exit_cleanup;  	} @@ -696,14 +724,14 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay  	}  	if (client->write_buffer_size > 4e6) { // 4 MB -		wlr_log(L_ERROR, "Client write buffer too big, disconnecting client"); +		wlr_log(WLR_ERROR, "Client write buffer too big, disconnecting client");  		ipc_client_disconnect(client);  		return false;  	}  	char *new_buffer = realloc(client->write_buffer, client->write_buffer_size);  	if (!new_buffer) { -		wlr_log(L_ERROR, "Unable to reallocate ipc client write buffer"); +		wlr_log(WLR_ERROR, "Unable to reallocate ipc client write buffer");  		ipc_client_disconnect(client);  		return false;  	} @@ -720,6 +748,6 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay  				ipc_client_handle_writable, client);  	} -	wlr_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); +	wlr_log(WLR_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload);  	return true;  } diff --git a/sway/main.c b/sway/main.c index ec7353be..1a55b519 100644 --- a/sway/main.c +++ b/sway/main.c @@ -20,6 +20,7 @@  #include "sway/commands.h"  #include "sway/config.h"  #include "sway/debug.h" +#include "sway/desktop/transaction.h"  #include "sway/server.h"  #include "sway/tree/layout.h"  #include "sway/ipc-server.h" @@ -129,7 +130,7 @@ static void log_env() {  		"SWAYSOCK"  	};  	for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) { -		wlr_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i])); +		wlr_log(WLR_INFO, "%s=%s", log_vars[i], getenv(log_vars[i]));  	}  } @@ -144,14 +145,14 @@ static void log_distro() {  	for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) {  		FILE *f = fopen(paths[i], "r");  		if (f) { -			wlr_log(L_INFO, "Contents of %s:", paths[i]); +			wlr_log(WLR_INFO, "Contents of %s:", paths[i]);  			while (!feof(f)) {  				char *line;  				if (!(line = read_line(f))) {  					break;  				}  				if (*line) { -					wlr_log(L_INFO, "%s", line); +					wlr_log(WLR_INFO, "%s", line);  				}  				free(line);  			} @@ -163,7 +164,7 @@ static void log_distro() {  static void log_kernel() {  	FILE *f = popen("uname -a", "r");  	if (!f) { -		wlr_log(L_INFO, "Unable to determine kernel version"); +		wlr_log(WLR_INFO, "Unable to determine kernel version");  		return;  	}  	while (!feof(f)) { @@ -172,7 +173,7 @@ static void log_kernel() {  			break;  		}  		if (*line) { -			wlr_log(L_INFO, "%s", line); +			wlr_log(WLR_INFO, "%s", line);  		}  		free(line);  	} @@ -183,14 +184,14 @@ static void security_sanity_check() {  	// TODO: Notify users visually if this has issues  	struct stat s;  	if (stat("/proc", &s)) { -		wlr_log(L_ERROR, +		wlr_log(WLR_ERROR,  			"!! DANGER !! /proc is not available - sway CANNOT enforce security rules!");  	}  #ifdef __linux__  	cap_flag_value_t v;  	cap_t cap = cap_get_proc();  	if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) { -		wlr_log(L_ERROR, +		wlr_log(WLR_ERROR,  			"!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users.");  	}  	if (cap) { @@ -206,13 +207,13 @@ static void executable_sanity_check() {  		stat(exe, &sb);  		// We assume that cap_get_file returning NULL implies ENODATA  		if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) { -			wlr_log(L_ERROR, +			wlr_log(WLR_ERROR,  				"sway executable has both the s(g)uid bit AND file caps set."); -			wlr_log(L_ERROR, +			wlr_log(WLR_ERROR,  				"This is strongly discouraged (and completely broken)."); -			wlr_log(L_ERROR, +			wlr_log(WLR_ERROR,  				"Please clear one of them (either the suid bit, or the file caps)."); -			wlr_log(L_ERROR, +			wlr_log(WLR_ERROR,  				"If unsure, strip the file caps.");  			exit(EXIT_FAILURE);  		} @@ -223,16 +224,16 @@ static void executable_sanity_check() {  static void drop_permissions(bool keep_caps) {  	if (getuid() != geteuid() || getgid() != getegid()) {  		if (setgid(getgid()) != 0) { -			wlr_log(L_ERROR, "Unable to drop root"); +			wlr_log(WLR_ERROR, "Unable to drop root");  			exit(EXIT_FAILURE);  		}  		if (setuid(getuid()) != 0) { -			wlr_log(L_ERROR, "Unable to drop root"); +			wlr_log(WLR_ERROR, "Unable to drop root");  			exit(EXIT_FAILURE);  		}  	}  	if (setuid(0) != -1) { -		wlr_log(L_ERROR, "Root privileges can be restored."); +		wlr_log(WLR_ERROR, "Root privileges can be restored.");  		exit(EXIT_FAILURE);  	}  #ifdef __linux__ @@ -240,17 +241,29 @@ static void drop_permissions(bool keep_caps) {  		// Drop every cap except CAP_SYS_PTRACE  		cap_t caps = cap_init();  		cap_value_t keep = CAP_SYS_PTRACE; -		wlr_log(L_INFO, "Dropping extra capabilities"); +		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(L_ERROR, "Failed to drop extra capabilities"); +			wlr_log(WLR_ERROR, "Failed to drop extra capabilities");  			exit(EXIT_FAILURE);  		}  	}  #endif  } +void enable_debug_flag(const char *flag) { +	if (strcmp(flag, "render-tree") == 0) { +		enable_debug_tree = true; +	} else if (strncmp(flag, "damage=", 7) == 0) { +		damage_debug = &flag[7]; +	} else if (strcmp(flag, "txn-debug") == 0) { +		txn_debug = true; +	} else if (strncmp(flag, "txn-timeout=", 12) == 0) { +		txn_timeout_ms = atoi(&flag[12]); +	} +} +  int main(int argc, char **argv) {  	static int verbose = 0, debug = 0, validate = 0; @@ -290,7 +303,7 @@ int main(int argc, char **argv) {  	int c;  	while (1) {  		int option_index = 0; -		c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index); +		c = getopt_long(argc, argv, "hCdD:vVc:", long_options, &option_index);  		if (c == -1) {  			break;  		} @@ -309,7 +322,7 @@ int main(int argc, char **argv) {  			debug = 1;  			break;  		case 'D': // extended debug options -			enable_debug_tree = true; +			enable_debug_flag(optarg);  			break;  		case 'v': // version  			fprintf(stdout, "sway version " SWAY_VERSION "\n"); @@ -335,22 +348,22 @@ int main(int argc, char **argv) {  	// TODO: switch logging over to wlroots?  	if (debug) { -		wlr_log_init(L_DEBUG, NULL); +		wlr_log_init(WLR_DEBUG, NULL);  	} else if (verbose || validate) { -		wlr_log_init(L_INFO, NULL); +		wlr_log_init(WLR_INFO, NULL);  	} else { -		wlr_log_init(L_ERROR, NULL); +		wlr_log_init(WLR_ERROR, NULL);  	}  	if (optind < argc) { // Behave as IPC client  		if(optind != 1) { -			wlr_log(L_ERROR, "Don't use options with the IPC client"); +			wlr_log(WLR_ERROR, "Don't use options with the IPC client");  			exit(EXIT_FAILURE);  		}  		drop_permissions(false);  		char *socket_path = getenv("SWAYSOCK");  		if (!socket_path) { -			wlr_log(L_ERROR, "Unable to retrieve socket path"); +			wlr_log(WLR_ERROR, "Unable to retrieve socket path");  			exit(EXIT_FAILURE);  		}  		char *command = join_args(argv + optind, argc - optind); @@ -369,7 +382,7 @@ int main(int argc, char **argv) {  	if (getuid() != geteuid() || getgid() != getegid()) {  		// Retain capabilities after setuid()  		if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { -			wlr_log(L_ERROR, "Cannot keep caps after setuid()"); +			wlr_log(WLR_ERROR, "Cannot keep caps after setuid()");  			exit(EXIT_FAILURE);  		}  		suid = true; @@ -390,7 +403,7 @@ int main(int argc, char **argv) {  	// prevent ipc from crashing sway  	signal(SIGPIPE, SIG_IGN); -	wlr_log(L_INFO, "Starting sway version " SWAY_VERSION); +	wlr_log(WLR_INFO, "Starting sway version " SWAY_VERSION);  	layout_init(); @@ -423,18 +436,19 @@ int main(int argc, char **argv) {  		char *line = config->cmd_queue->items[0];  		struct cmd_results *res = execute_command(line, NULL);  		if (res->status != CMD_SUCCESS) { -			wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error); +			wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error);  		}  		free_cmd_results(res);  		free(line);  		list_del(config->cmd_queue, 0);  	} +	transaction_commit_dirty();  	if (!terminate_request) {  		server_run(&server);  	} -	wlr_log(L_INFO, "Shutting down sway"); +	wlr_log(WLR_INFO, "Shutting down sway");  	server_fini(&server); diff --git a/sway/meson.build b/sway/meson.build index e492aeee..c58d3470 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -35,11 +35,13 @@ sway_sources = files(  	'commands/border.c',  	'commands/client.c',  	'commands/default_border.c', +	'commands/default_floating_border.c',  	'commands/default_orientation.c',  	'commands/exit.c',  	'commands/exec.c',  	'commands/exec_always.c',  	'commands/floating.c', +	'commands/floating_minmax_size.c',  	'commands/focus.c',  	'commands/focus_follows_mouse.c',  	'commands/focus_wrapping.c', @@ -58,6 +60,7 @@ sway_sources = files(  	'commands/mode.c',  	'commands/mouse_warping.c',  	'commands/move.c', +	'commands/no_focus.c',  	'commands/output.c',  	'commands/reload.c',  	'commands/rename.c', @@ -75,6 +78,7 @@ sway_sources = files(  	'commands/swap.c',  	'commands/title_format.c',  	'commands/unmark.c', +	'commands/urgent.c',  	'commands/workspace.c',  	'commands/workspace_layout.c',  	'commands/ws_auto_back_and_forth.c', @@ -117,8 +121,10 @@ sway_sources = files(  	'commands/input/pointer_accel.c',  	'commands/input/repeat_delay.c',  	'commands/input/repeat_rate.c', +	'commands/input/scroll_button.c',  	'commands/input/scroll_method.c',  	'commands/input/tap.c', +	'commands/input/tap_button_map.c',  	'commands/input/xkb_layout.c',  	'commands/input/xkb_model.c',  	'commands/input/xkb_options.c', diff --git a/sway/server.c b/sway/server.c index cd15f454..f904b177 100644 --- a/sway/server.c +++ b/sway/server.c @@ -14,7 +14,6 @@  #include <wlr/types/wlr_linux_dmabuf.h>  #include <wlr/types/wlr_primary_selection.h>  #include <wlr/types/wlr_screencopy_v1.h> -#include <wlr/types/wlr_screenshooter.h>  #include <wlr/types/wlr_server_decoration.h>  #include <wlr/types/wlr_xcursor_manager.h>  #include <wlr/types/wlr_xdg_output.h> @@ -29,20 +28,20 @@  #include "sway/xwayland.h"  bool server_privileged_prepare(struct sway_server *server) { -	wlr_log(L_DEBUG, "Preparing Wayland server initialization"); +	wlr_log(WLR_DEBUG, "Preparing Wayland server initialization");  	server->wl_display = wl_display_create();  	server->wl_event_loop = wl_display_get_event_loop(server->wl_display);  	server->backend = wlr_backend_autocreate(server->wl_display, NULL);  	if (!server->backend) { -		wlr_log(L_ERROR, "Unable to create backend"); +		wlr_log(WLR_ERROR, "Unable to create backend");  		return false;  	}  	return true;  }  bool server_init(struct sway_server *server) { -	wlr_log(L_DEBUG, "Initializing Wayland server"); +	wlr_log(WLR_DEBUG, "Initializing Wayland server");  	struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend);  	assert(renderer); @@ -53,7 +52,6 @@ bool server_init(struct sway_server *server) {  	server->data_device_manager =  		wlr_data_device_manager_create(server->wl_display); -	wlr_screenshooter_create(server->wl_display);  	wlr_gamma_control_manager_create(server->wl_display);  	wlr_primary_selection_device_manager_create(server->wl_display); @@ -116,7 +114,7 @@ bool server_init(struct sway_server *server) {  	server->socket = wl_display_add_socket_auto(server->wl_display);  	if (!server->socket) { -		wlr_log(L_ERROR, "Unable to open wayland socket"); +		wlr_log(WLR_ERROR, "Unable to open wayland socket");  		wlr_backend_destroy(server->backend);  		return false;  	} @@ -125,8 +123,7 @@ bool server_init(struct sway_server *server) {  	if (debug != NULL && strcmp(debug, "txn_timings") == 0) {  		server->debug_txn_timings = true;  	} -	server->destroying_containers = create_list(); - +	server->dirty_containers = create_list();  	server->transactions = create_list();  	input_manager = input_manager_create(server); @@ -136,15 +133,15 @@ bool server_init(struct sway_server *server) {  void server_fini(struct sway_server *server) {  	// TODO: free sway-specific resources  	wl_display_destroy(server->wl_display); -	list_free(server->destroying_containers); +	list_free(server->dirty_containers);  	list_free(server->transactions);  }  void server_run(struct sway_server *server) { -	wlr_log(L_INFO, "Running compositor on wayland display '%s'", +	wlr_log(WLR_INFO, "Running compositor on wayland display '%s'",  			server->socket);  	if (!wlr_backend_start(server->backend)) { -		wlr_log(L_ERROR, "Failed to start backend"); +		wlr_log(WLR_ERROR, "Failed to start backend");  		wlr_backend_destroy(server->backend);  		return;  	} diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index cf7a6385..b6391431 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -92,9 +92,20 @@ For more information on these xkb configuration options, see  *input* <identifier> scroll\_method none|two\_finger|edge|on\_button\_down  	Changes the scroll method for the specified input device. +*input* <identifier> scroll\_button <button\_identifier> +	Sets button used for scroll\_method on\_button\_down. The button identifier +	can be obtained from `libinput debug-events`. +	If set to 0, it disables the scroll\_button on\_button\_down. +  *input* <identifier> tap enabled|disabled  	Enables or disables tap for specified input device. +*input* <identifier> tap_button_map lrm|lmr +	Specifies which button mapping to use for tapping. _lrm_ treats 1 finger as +	left click, 2 fingers as right click, and 3 fingers as middle click. _lmr_ +	treats 1 finger as left click, 2 fingers as middle click, and 3 fingers as +	right click. +  ## SEAT CONFIGURATION  Configure options for multiseat mode. sway-seat commands must be used inside a diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 7c553d3f..d369d7b6 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -92,6 +92,12 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).  *focus* output <name>  	Moves focus to the named output. +*focus tiling* +	Sets focus to the last focused tiling container. + +*focus floating* +	Sets focus to the last focused floating container. +  *focus* mode\_toggle  	Moves focus between the floating and tiled layers. @@ -493,6 +499,11 @@ config after the others, or it will be matched instead of the others.  	*unmark* will remove _identifier_ from the list of current marks on a  	window. If _identifier_ is omitted, all marks are removed. +*urgent* enable|disable|allow|deny +	Using _enable_ or _disable_ manually sets or unsets the window's urgent +	state. Using _allow_ or _deny_ controls the window's ability to set itself +	as urgent. By default, windows are allowed to set their own urgency. +  *workspace* [number] <name>  	Switches to the specified workspace. The string "number" is optional and is  	used to sort workspaces. diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 582b2891..533cf71c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -47,11 +47,11 @@ static void apply_horiz_layout(struct sway_container *parent) {  	double scale = parent->width / total_width;  	// Resize windows -	wlr_log(L_DEBUG, "Arranging %p horizontally", parent); +	wlr_log(WLR_DEBUG, "Arranging %p horizontally", parent);  	double child_x = parent->x;  	for (size_t i = 0; i < num_children; ++i) {  		struct sway_container *child = parent->children->items[i]; -		wlr_log(L_DEBUG, +		wlr_log(WLR_DEBUG,  				"Calculating arrangement for %p:%d (will scale %f by %f)",  				child, child->type, child->width, scale);  		child->x = child_x; @@ -99,11 +99,11 @@ static void apply_vert_layout(struct sway_container *parent) {  	double scale = parent_height / total_height;  	// Resize -	wlr_log(L_DEBUG, "Arranging %p vertically", parent); +	wlr_log(WLR_DEBUG, "Arranging %p vertically", parent);  	double child_y = parent->y + parent_offset;  	for (size_t i = 0; i < num_children; ++i) {  		struct sway_container *child = parent->children->items[i]; -		wlr_log(L_DEBUG, +		wlr_log(WLR_DEBUG,  				"Calculating arrangement for %p:%d (will scale %f by %f)",  				child, child->type, child->height, scale);  		child->x = parent->x; @@ -144,42 +144,26 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) {  	}  } -/** - * If a container has been deleted from the pending tree state, we must add it - * to the transaction so it can be freed afterwards. To do this, we iterate the - * server's destroying_containers list and add all of them. We may add more than - * what we need to, but this is easy and has no negative consequences. - */ -static void add_deleted_containers(struct sway_transaction *transaction) { -	for (int i = 0; i < server.destroying_containers->length; ++i) { -		struct sway_container *child = server.destroying_containers->items[i]; -		transaction_add_container(transaction, child); -	} -} - -static void arrange_children_of(struct sway_container *parent, -		struct sway_transaction *transaction); +static void arrange_children_of(struct sway_container *parent); -static void arrange_floating(struct sway_container *floating, -		struct sway_transaction *transaction) { +static void arrange_floating(struct sway_container *floating) {  	for (int i = 0; i < floating->children->length; ++i) {  		struct sway_container *floater = floating->children->items[i];  		if (floater->type == C_VIEW) {  			view_autoconfigure(floater->sway_view);  		} else { -			arrange_children_of(floater, transaction); +			arrange_children_of(floater);  		} -		transaction_add_container(transaction, floater); +		container_set_dirty(floater);  	} -	transaction_add_container(transaction, floating); +	container_set_dirty(floating);  } -static void arrange_children_of(struct sway_container *parent, -		struct sway_transaction *transaction) { +static void arrange_children_of(struct sway_container *parent) {  	if (config->reloading) {  		return;  	} -	wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, +	wlr_log(WLR_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent,  		parent->name, parent->width, parent->height, parent->x, parent->y);  	// Calculate x, y, width and height of children @@ -198,7 +182,7 @@ static void arrange_children_of(struct sway_container *parent,  		apply_horiz_layout(parent);  		break;  	case L_FLOATING: -		arrange_floating(parent, transaction); +		arrange_floating(parent);  		break;  	} @@ -213,20 +197,19 @@ static void arrange_children_of(struct sway_container *parent,  		if (child->type == C_VIEW) {  			view_autoconfigure(child->sway_view);  		} else { -			arrange_children_of(child, transaction); +			arrange_children_of(child);  		} -		transaction_add_container(transaction, child); +		container_set_dirty(child);  	}  } -static void arrange_workspace(struct sway_container *workspace, -		struct sway_transaction *transaction) { +static void arrange_workspace(struct sway_container *workspace) {  	if (config->reloading) {  		return;  	}  	struct sway_container *output = workspace->parent;  	struct wlr_box *area = &output->sway_output->usable_area; -	wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", +	wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d",  			area->width, area->height, area->x, area->y);  	remove_gaps(workspace);  	workspace->width = area->width; @@ -234,15 +217,14 @@ static void arrange_workspace(struct sway_container *workspace,  	workspace->x = output->x + area->x;  	workspace->y = output->y + area->y;  	add_gaps(workspace); -	transaction_add_container(transaction, workspace); -	wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, +	container_set_dirty(workspace); +	wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,  			workspace->x, workspace->y); -	arrange_floating(workspace->sway_workspace->floating, transaction); -	arrange_children_of(workspace, transaction); +	arrange_floating(workspace->sway_workspace->floating); +	arrange_children_of(workspace);  } -static void arrange_output(struct sway_container *output, -		struct sway_transaction *transaction) { +static void arrange_output(struct sway_container *output) {  	if (config->reloading) {  		return;  	} @@ -253,16 +235,16 @@ static void arrange_output(struct sway_container *output,  	output->y = output_box->y;  	output->width = output_box->width;  	output->height = output_box->height; -	transaction_add_container(transaction, output); -	wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f", +	container_set_dirty(output); +	wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f",  			output->name, output->x, output->y);  	for (int i = 0; i < output->children->length; ++i) {  		struct sway_container *workspace = output->children->items[i]; -		arrange_workspace(workspace, transaction); +		arrange_workspace(workspace);  	}  } -static void arrange_root(struct sway_transaction *transaction) { +static void arrange_root() {  	if (config->reloading) {  		return;  	} @@ -274,48 +256,40 @@ static void arrange_root(struct sway_transaction *transaction) {  	root_container.y = layout_box->y;  	root_container.width = layout_box->width;  	root_container.height = layout_box->height; -	transaction_add_container(transaction, &root_container); +	container_set_dirty(&root_container);  	for (int i = 0; i < root_container.children->length; ++i) {  		struct sway_container *output = root_container.children->items[i]; -		arrange_output(output, transaction); +		arrange_output(output);  	}  } -void arrange_windows(struct sway_container *container, -		struct sway_transaction *transaction) { +void arrange_windows(struct sway_container *container) {  	switch (container->type) {  	case C_ROOT: -		arrange_root(transaction); +		arrange_root();  		break;  	case C_OUTPUT: -		arrange_output(container, transaction); +		arrange_output(container);  		break;  	case C_WORKSPACE: -		arrange_workspace(container, transaction); +		arrange_workspace(container);  		break;  	case C_CONTAINER: -		arrange_children_of(container, transaction); -		transaction_add_container(transaction, container); +		arrange_children_of(container); +		container_set_dirty(container);  		break;  	case C_VIEW:  		view_autoconfigure(container->sway_view); -		transaction_add_container(transaction, container); +		container_set_dirty(container);  		break;  	case C_TYPES:  		break;  	} -	add_deleted_containers(transaction); -} - -void arrange_and_commit(struct sway_container *container) { -	struct sway_transaction *transaction = transaction_create(); -	arrange_windows(container, transaction); -	transaction_commit(transaction);  }  void remove_gaps(struct sway_container *c) {  	if (c->current_gaps == 0) { -		wlr_log(L_DEBUG, "Removing gaps: not gapped: %p", c); +		wlr_log(WLR_DEBUG, "Removing gaps: not gapped: %p", c);  		return;  	} @@ -326,12 +300,12 @@ void remove_gaps(struct sway_container *c) {  	c->current_gaps = 0; -	wlr_log(L_DEBUG, "Removing gaps %p", c); +	wlr_log(WLR_DEBUG, "Removing gaps %p", c);  }  void add_gaps(struct sway_container *c) {  	if (c->current_gaps > 0 || c->type == C_CONTAINER) { -		wlr_log(L_DEBUG, "Not adding gaps: %p", c); +		wlr_log(WLR_DEBUG, "Not adding gaps: %p", c);  		return;  	} @@ -348,5 +322,5 @@ void add_gaps(struct sway_container *c) {  	c->height -= 2 * gaps;  	c->current_gaps = gaps; -	wlr_log(L_DEBUG, "Adding gaps: %p", c); +	wlr_log(WLR_DEBUG, "Adding gaps: %p", c);  } diff --git a/sway/tree/container.c b/sway/tree/container.c index 7cea43fa..02384199 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -11,11 +11,14 @@  #include "cairo.h"  #include "pango.h"  #include "sway/config.h" +#include "sway/desktop.h" +#include "sway/desktop/transaction.h"  #include "sway/input/input-manager.h"  #include "sway/input/seat.h"  #include "sway/ipc-server.h"  #include "sway/output.h"  #include "sway/server.h" +#include "sway/tree/arrange.h"  #include "sway/tree/layout.h"  #include "sway/tree/view.h"  #include "sway/tree/workspace.h" @@ -28,7 +31,7 @@ static list_t *get_bfs_queue() {  	if (!bfs_queue) {  		bfs_queue = create_list();  		if (!bfs_queue) { -			wlr_log(L_ERROR, "could not allocate list for bfs queue"); +			wlr_log(WLR_ERROR, "could not allocate list for bfs queue");  			return NULL;  		}  	} @@ -156,14 +159,6 @@ void container_free(struct sway_container *cont) {  	wlr_texture_destroy(cont->title_focused_inactive);  	wlr_texture_destroy(cont->title_unfocused);  	wlr_texture_destroy(cont->title_urgent); - -	for (int i = 0; i < server.destroying_containers->length; ++i) { -		if (server.destroying_containers->items[i] == cont) { -			list_del(server.destroying_containers, i); -			break; -		} -	} -  	list_free(cont->instructions);  	list_free(cont->children);  	list_free(cont->current.children); @@ -218,7 +213,7 @@ static struct sway_container *container_workspace_destroy(  		return NULL;  	} -	wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); +	wlr_log(WLR_DEBUG, "destroying workspace '%s'", workspace->name);  	if (!workspace_is_empty(workspace)) {  		// Move children to a different workspace on this output @@ -230,7 +225,7 @@ static struct sway_container *container_workspace_destroy(  			}  		} -		wlr_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'", +		wlr_log(WLR_DEBUG, "moving children to different workspace '%s' -> '%s'",  			workspace->name, new_workspace->name);  		for (int i = 0; i < workspace->children->length; i++) {  			container_move_to(workspace->children->items[i], new_workspace); @@ -296,7 +291,7 @@ static struct sway_container *container_output_destroy(  	output->sway_output->swayc = NULL;  	output->sway_output = NULL; -	wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); +	wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name);  	return &root_container;  } @@ -323,13 +318,13 @@ static struct sway_container *container_destroy_noreaping(  		// Workspaces will refuse to be destroyed if they're the last workspace  		// on their output.  		if (!container_workspace_destroy(con)) { -			wlr_log(L_ERROR, "workspace doesn't want to destroy"); +			wlr_log(WLR_ERROR, "workspace doesn't want to destroy");  			return NULL;  		}  	}  	con->destroying = true; -	list_add(server.destroying_containers, con); +	container_set_dirty(con);  	if (!con->parent) {  		return NULL; @@ -350,7 +345,7 @@ bool container_reap_empty(struct sway_container *con) {  		break;  	case C_WORKSPACE:  		if (!workspace_is_visible(con) && workspace_is_empty(con)) { -			wlr_log(L_DEBUG, "Destroying workspace via reaper"); +			wlr_log(WLR_DEBUG, "Destroying workspace via reaper");  			container_destroy_noreaping(con);  			return true;  		} @@ -443,7 +438,7 @@ struct sway_container *container_view_create(struct sway_container *sibling,  	}  	const char *title = view_get_title(sway_view);  	struct sway_container *swayc = container_create(C_VIEW); -	wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s", +	wlr_log(WLR_DEBUG, "Adding new view %p:%s to container %p %d %s",  		swayc, title, sibling, sibling ? sibling->type : 0, sibling->name);  	// Setup values  	swayc->sway_view = sway_view; @@ -686,16 +681,23 @@ struct sway_container *floating_container_at(double lx, double ly,  void container_for_each_descendant_dfs(struct sway_container *container,  		void (*f)(struct sway_container *container, void *data),  		void *data) { -	if (container) { -		if (container->children)  { -			for (int i = 0; i < container->children->length; ++i) { -				struct sway_container *child = -					container->children->items[i]; -				container_for_each_descendant_dfs(child, f, data); -			} +	if (!container) { +		return; +	} +	if (container->children)  { +		for (int i = 0; i < container->children->length; ++i) { +			struct sway_container *child = container->children->items[i]; +			container_for_each_descendant_dfs(child, f, data); +		} +	} +	if (container->type == C_WORKSPACE)  { +		struct sway_container *floating = container->sway_workspace->floating; +		for (int i = 0; i < floating->children->length; ++i) { +			struct sway_container *child = floating->children->items[i]; +			container_for_each_descendant_dfs(child, f, data);  		} -		f(container, data);  	} +	f(container, data);  }  void container_for_each_descendant_bfs(struct sway_container *con, @@ -706,7 +708,7 @@ void container_for_each_descendant_bfs(struct sway_container *con,  	}  	if (queue == NULL) { -		wlr_log(L_ERROR, "could not allocate list"); +		wlr_log(WLR_ERROR, "could not allocate list");  		return;  	} @@ -972,9 +974,14 @@ void container_set_geometry_from_floating_view(struct sway_container *con) {  		return;  	}  	struct sway_view *view = con->sway_view; -	size_t border_width = view->border_thickness * (view->border != B_NONE); -	size_t top = -		view->border == B_NORMAL ? container_titlebar_height() : border_width; +	size_t border_width = 0; +	size_t top = 0; + +	if (!view->using_csd) { +		border_width = view->border_thickness * (view->border != B_NONE); +		top = view->border == B_NORMAL ? +			container_titlebar_height() : border_width; +	}  	con->x = view->x - border_width;  	con->y = view->y - top; @@ -996,3 +1003,103 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) {  	box->width = container->width;  	box->height = container->height;  } + +/** + * Translate the container's position as well as all children. + */ +static void container_floating_translate(struct sway_container *con, +		double x_amount, double y_amount) { +	con->x += x_amount; +	con->y += y_amount; +	con->current.swayc_x += x_amount; +	con->current.swayc_y += y_amount; +	if (con->type == C_VIEW) { +		con->sway_view->x += x_amount; +		con->sway_view->y += y_amount; +		con->current.view_x += x_amount; +		con->current.view_y += y_amount; +	} else { +		for (int i = 0; i < con->children->length; ++i) { +			struct sway_container *child = con->children->items[i]; +			container_floating_translate(child, x_amount, y_amount); +		} +	} +} + +/** + * Choose an output for the floating container's new position. + * + * If the center of the container intersects an output then we'll choose that + * one, otherwise we'll choose whichever output is closest to the container's + * center. + */ +static struct sway_container *container_floating_find_output( +		struct sway_container *con) { +	double center_x = con->x + con->width / 2; +	double center_y = con->y + con->height / 2; +	struct sway_container *closest_output = NULL; +	double closest_distance = DBL_MAX; +	for (int i = 0; i < root_container.children->length; ++i) { +		struct sway_container *output = root_container.children->items[i]; +		struct wlr_box output_box; +		double closest_x, closest_y; +		container_get_box(output, &output_box); +		wlr_box_closest_point(&output_box, center_x, center_y, +				&closest_x, &closest_y); +		if (center_x == closest_x && center_y == closest_y) { +			// The center of the floating container is on this output +			return output; +		} +		double x_dist = closest_x - center_x; +		double y_dist = closest_y - center_y; +		double distance = x_dist * x_dist + y_dist * y_dist; +		if (distance < closest_distance) { +			closest_output = output; +			closest_distance = distance; +		} +	} +	return closest_output; +} + +void container_floating_move_to(struct sway_container *con, +		double lx, double ly) { +	if (!sway_assert(container_is_floating(con), +			"Expected a floating container")) { +		return; +	} +	desktop_damage_whole_container(con); +	container_floating_translate(con, lx - con->x, ly - con->y); +	desktop_damage_whole_container(con); +	struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); +	struct sway_container *new_output = container_floating_find_output(con); +	if (!sway_assert(new_output, "Unable to find any output")) { +		return; +	} +	struct sway_container *new_workspace = +		output_get_active_workspace(new_output->sway_output); +	if (old_workspace != new_workspace) { +		container_remove_child(con); +		container_add_child(new_workspace->sway_workspace->floating, con); +		arrange_windows(old_workspace); +		arrange_windows(new_workspace); +		workspace_detect_urgent(old_workspace); +		workspace_detect_urgent(new_workspace); +	} +} + +void container_set_dirty(struct sway_container *container) { +	if (container->dirty) { +		return; +	} +	container->dirty = true; +	list_add(server.dirty_containers, container); +} + +static bool find_urgent_iterator(struct sway_container *con, +		void *data) { +	return con->type == C_VIEW && view_is_urgent(con->sway_view); +} + +bool container_has_urgent_child(struct sway_container *container) { +	return container_find(container, find_urgent_iterator, NULL); +} diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 14631ad4..197a2fc8 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -22,7 +22,8 @@ struct sway_container root_container;  static void output_layout_handle_change(struct wl_listener *listener,  		void *data) { -	arrange_and_commit(&root_container); +	arrange_windows(&root_container); +	transaction_commit_dirty();  }  void layout_init(void) { @@ -101,7 +102,7 @@ void container_insert_child(struct sway_container *parent,  	if (old_parent) {  		container_remove_child(child);  	} -	wlr_log(L_DEBUG, "Inserting id:%zd at index %d", child->id, i); +	wlr_log(WLR_DEBUG, "Inserting id:%zd at index %d", child->id, i);  	list_insert(parent->children, i, child);  	child->parent = parent;  	container_handle_fullscreen_reparent(child, old_parent); @@ -127,7 +128,7 @@ struct sway_container *container_add_sibling(struct sway_container *fixed,  void container_add_child(struct sway_container *parent,  		struct sway_container *child) { -	wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", +	wlr_log(WLR_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)",  			child, child->type, child->width, child->height,  			parent, parent->type, parent->width, parent->height);  	struct sway_container *old_parent = child->parent; @@ -168,7 +169,12 @@ void container_move_to(struct sway_container *container,  	struct sway_container *old_parent = container_remove_child(container);  	container->width = container->height = 0;  	container->saved_width = container->saved_height = 0; -	struct sway_container *new_parent; + +	struct sway_container *new_parent, *new_parent_focus; +	struct sway_seat *seat = input_manager_get_default_seat(input_manager); + +	// Get the focus of the destination before we change it. +	new_parent_focus = seat_get_focus_inactive(seat, destination);  	if (destination->type == C_VIEW) {  		new_parent = container_add_sibling(destination, container);  	} else { @@ -176,17 +182,20 @@ void container_move_to(struct sway_container *container,  		container_add_child(destination, container);  	}  	wl_signal_emit(&container->events.reparent, old_parent); +  	if (container->type == C_WORKSPACE) {  		// If moving a workspace to a new output, maybe create a new workspace  		// on the previous output -		struct sway_seat *seat = input_manager_get_default_seat(input_manager);  		if (old_parent->children->length == 0) {  			char *ws_name = workspace_next_name(old_parent->name); -			struct sway_container *ws = -				workspace_create(old_parent, ws_name); +			struct sway_container *ws = workspace_create(old_parent, ws_name);  			free(ws_name);  			seat_set_focus(seat, ws);  		} + +		// Try to remove an empty workspace from the destination output. +		container_reap_empty_recursive(new_parent_focus); +  		container_sort_workspaces(new_parent);  		seat_set_focus(seat, new_parent);  		workspace_output_raise_priority(container, old_parent, new_parent); @@ -216,6 +225,15 @@ void container_move_to(struct sway_container *container,  			}  		}  	} +	// Update workspace urgent state +	struct sway_container *old_workspace = old_parent; +	if (old_workspace->type != C_WORKSPACE) { +		old_workspace = container_parent(old_workspace, C_WORKSPACE); +	} +	if (new_workspace != old_workspace) { +		workspace_detect_urgent(new_workspace); +		workspace_detect_urgent(old_workspace); +	}  }  static bool sway_dir_to_wlr(enum movement_direction dir, @@ -311,13 +329,13 @@ static void move_out_of_tabs_stacks(struct sway_container *container,  		int offs) {  	if (container->parent == current->parent  			&& current->parent->children->length == 1) { -		wlr_log(L_DEBUG, "Changing layout of %zd", current->parent->id); +		wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id);  		current->parent->layout = move_dir ==  			MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;  		return;  	} -	wlr_log(L_DEBUG, "Moving out of tab/stack into a split"); +	wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split");  	bool is_workspace = current->parent->type == C_WORKSPACE;  	struct sway_container *new_parent = container_split(current->parent,  		move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); @@ -362,7 +380,7 @@ void container_move(struct sway_container *container,  		}  		parent = current->parent; -		wlr_log(L_DEBUG, "Visiting %p %s '%s'", current, +		wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current,  				container_type_to_str(current->type), current->name);  		int index = index_child(current); @@ -380,12 +398,12 @@ void container_move(struct sway_container *container,  				root_container.sway_root->output_layout, wlr_dir,  				current->sway_output->wlr_output, ref_lx, ref_ly);  			if (!next) { -				wlr_log(L_DEBUG, "Hit edge of output, nowhere else to go"); +				wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go");  				return;  			}  			struct sway_output *next_output = next->data;  			current = next_output->swayc; -			wlr_log(L_DEBUG, "Selected next output (%s)", current->name); +			wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name);  			// Select workspace and get outta here  			current = seat_get_focus_inactive(  					config->handler_context.seat, current); @@ -398,20 +416,20 @@ void container_move(struct sway_container *container,  		case C_WORKSPACE:  			if (!is_parallel(current->layout, move_dir)) {  				if (current->children->length >= 2) { -					wlr_log(L_DEBUG, "Rejiggering the workspace (%d kiddos)", +					wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)",  							current->children->length);  					workspace_rejigger(current, container, move_dir);  					return;  				} else { -					wlr_log(L_DEBUG, "Selecting output"); +					wlr_log(WLR_DEBUG, "Selecting output");  					current = current->parent;  				}  			} else if (current->layout == L_TABBED  					|| current->layout == L_STACKED) { -				wlr_log(L_DEBUG, "Rejiggering out of tabs/stacks"); +				wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks");  				workspace_rejigger(current, container, move_dir);  			} else { -				wlr_log(L_DEBUG, "Selecting output"); +				wlr_log(WLR_DEBUG, "Selecting output");  				current = current->parent;  			}  			break; @@ -427,11 +445,11 @@ void container_move(struct sway_container *container,  									move_dir, offs);  							return;  						} else { -							wlr_log(L_DEBUG, "Hit limit, selecting parent"); +							wlr_log(WLR_DEBUG, "Hit limit, selecting parent");  							current = current->parent;  						}  					} else { -						wlr_log(L_DEBUG, "Hit limit, " +						wlr_log(WLR_DEBUG, "Hit limit, "  								"promoting descendant to sibling");  						// Special case  						container_insert_child(current->parent, container, @@ -441,14 +459,14 @@ void container_move(struct sway_container *container,  					}  				} else {  					sibling = parent->children->items[index + offs]; -					wlr_log(L_DEBUG, "Selecting sibling id:%zd", sibling->id); +					wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id);  				}  			} else if (parent->layout == L_TABBED  					|| parent->layout == L_STACKED) {  				move_out_of_tabs_stacks(container, current, move_dir, offs);  				return;  			} else { -				wlr_log(L_DEBUG, "Moving up to find a parallel container"); +				wlr_log(WLR_DEBUG, "Moving up to find a parallel container");  				current = current->parent;  			}  			break; @@ -467,11 +485,11 @@ void container_move(struct sway_container *container,  		switch (sibling->type) {  		case C_VIEW:  			if (sibling->parent == container->parent) { -				wlr_log(L_DEBUG, "Swapping siblings"); +				wlr_log(WLR_DEBUG, "Swapping siblings");  				sibling->parent->children->items[index + offs] = container;  				sibling->parent->children->items[index] = sibling;  			} else { -				wlr_log(L_DEBUG, "Promoting to sibling of cousin"); +				wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");  				container_insert_child(sibling->parent, container,  						index_child(sibling) + (offs > 0 ? 0 : 1));  				container->width = container->height = 0; @@ -482,31 +500,31 @@ void container_move(struct sway_container *container,  		case C_CONTAINER:  			if (is_parallel(sibling->layout, move_dir)) {  				int limit = container_limit(sibling, invert_movement(move_dir)); -				wlr_log(L_DEBUG, "limit: %d", limit); -				wlr_log(L_DEBUG, +				wlr_log(WLR_DEBUG, "limit: %d", limit); +				wlr_log(WLR_DEBUG,  						"Reparenting container (parallel) to index %d "  						"(move dir: %d)", limit, move_dir);  				container_insert_child(sibling, container, limit);  				container->width = container->height = 0;  				sibling = NULL;  			} else { -				wlr_log(L_DEBUG, "Reparenting container (perpendicular)"); +				wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");  				struct sway_container *focus_inactive = seat_get_focus_inactive(  						config->handler_context.seat, sibling);  				if (focus_inactive && focus_inactive != sibling) {  					while (focus_inactive->parent != sibling) {  						focus_inactive = focus_inactive->parent;  					} -					wlr_log(L_DEBUG, "Focus inactive: id:%zd", +					wlr_log(WLR_DEBUG, "Focus inactive: id:%zd",  							focus_inactive->id);  					sibling = focus_inactive;  					continue;  				} else if (sibling->children->length) { -					wlr_log(L_DEBUG, "No focus-inactive, adding arbitrarily"); +					wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily");  					container_remove_child(container);  					container_add_sibling(sibling->children->items[0], container);  				} else { -					wlr_log(L_DEBUG, "No kiddos, adding container alone"); +					wlr_log(WLR_DEBUG, "No kiddos, adding container alone");  					container_remove_child(container);  					container_add_child(sibling, container);  				} @@ -539,6 +557,8 @@ void container_move(struct sway_container *container,  	}  	if (last_ws && next_ws && last_ws != next_ws) {  		ipc_event_workspace(last_ws, container, "focus"); +		workspace_detect_urgent(last_ws); +		workspace_detect_urgent(next_ws);  	}  } @@ -603,7 +623,7 @@ static struct sway_container *get_swayc_in_output_direction(  	}  	if (ws == NULL) { -		wlr_log(L_ERROR, "got an output without a workspace"); +		wlr_log(WLR_ERROR, "got an output without a workspace");  		return NULL;  	} @@ -775,7 +795,7 @@ struct sway_container *container_get_in_direction(  			} else {  				struct sway_container *desired_con =  					parent->children->items[desired]; -				wlr_log(L_DEBUG, +				wlr_log(WLR_DEBUG,  					"cont %d-%p dir %i sibling %d: %p", idx,  					container, dir, desired, desired_con);  				return seat_get_focus_inactive_view(seat, desired_con); @@ -840,7 +860,7 @@ struct sway_container *container_split(struct sway_container *child,  	struct sway_container *cont = container_create(C_CONTAINER); -	wlr_log(L_DEBUG, "creating container %p around %p", cont, child); +	wlr_log(WLR_DEBUG, "creating container %p around %p", cont, child);  	remove_gaps(child); @@ -888,7 +908,7 @@ struct sway_container *container_split(struct sway_container *child,  void container_recursive_resize(struct sway_container *container,  		double amount, enum resize_edge edge) {  	bool layout_match = true; -	wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount); +	wlr_log(WLR_DEBUG, "Resizing %p with amount: %f", container, amount);  	if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) {  		container->width += amount;  		layout_match = container->layout == L_HORIZ; @@ -978,7 +998,7 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {  		return;  	} -	wlr_log(L_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); +	wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id);  	int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen;  	int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; diff --git a/sway/tree/output.c b/sway/tree/output.c index e2927cdb..da535c18 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -43,11 +43,11 @@ struct sway_container *output_create(  		if (strcasecmp(name, cur->name) == 0 ||  				strcasecmp(identifier, cur->name) == 0) { -			wlr_log(L_DEBUG, "Matched output config for %s", name); +			wlr_log(WLR_DEBUG, "Matched output config for %s", name);  			oc = cur;  		}  		if (strcasecmp("*", cur->name) == 0) { -			wlr_log(L_DEBUG, "Matched wildcard output config for %s", name); +			wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);  			all = cur;  		} @@ -86,7 +86,7 @@ struct sway_container *output_create(  	if (!output->children->length) {  		// Create workspace  		char *ws_name = workspace_next_name(output->name); -		wlr_log(L_DEBUG, "Creating default workspace %s", ws_name); +		wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name);  		struct sway_container *ws = workspace_create(output, ws_name);  		// Set each seat's focus if not already set  		struct sway_seat *seat = NULL; diff --git a/sway/tree/view.c b/sway/tree/view.c index 3ef79fa8..fc31699c 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -25,6 +25,7 @@ void view_init(struct sway_view *view, enum sway_view_type type,  	view->impl = impl;  	view->executed_criteria = create_list();  	view->marks = create_list(); +	view->allow_request_urgent = true;  	wl_signal_init(&view->events.unmap);  } @@ -150,12 +151,43 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,  void view_init_floating(struct sway_view *view) {  	struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); -	int max_width = ws->width * 0.6666; -	int max_height = ws->height * 0.6666; -	view->width = -		view->natural_width > max_width ? max_width : view->natural_width; -	view->height = -		view->natural_height > max_height ? max_height : view->natural_height; +	int min_width, min_height; +	int max_width, max_height; + +	if (config->floating_minimum_width == -1) { // no minimum +		min_width = 0; +	} else if (config->floating_minimum_width == 0) { // automatic +		min_width = 75; +	} else { +		min_width = config->floating_minimum_width; +	} + +	if (config->floating_minimum_height == -1) { // no minimum +		min_height = 0; +	} else if (config->floating_minimum_height == 0) { // automatic +		min_height = 50; +	} else { +		min_height = config->floating_minimum_height; +	} + +	if (config->floating_maximum_width == -1) { // no maximum +		max_width = INT_MAX; +	} else if (config->floating_maximum_width == 0) { // automatic +		max_width = ws->width * 0.6666; +	} else { +		max_width = config->floating_maximum_width; +	} + +	if (config->floating_maximum_height == -1) { // no maximum +		max_height = INT_MAX; +	} else if (config->floating_maximum_height == 0) { // automatic +		max_height = ws->height * 0.6666; +	} else { +		max_height = config->floating_maximum_height; +	} + +	view->width = fmax(min_width, fmin(view->natural_width, max_width)); +	view->height = fmax(min_height, fmin(view->natural_height, max_height));  	view->x = ws->x + (ws->width - view->width) / 2;  	view->y = ws->y + (ws->height - view->height) / 2; @@ -284,7 +316,15 @@ void view_set_activated(struct sway_view *view, bool activated) {  }  void view_set_tiled(struct sway_view *view, bool tiled) { -	view->border = tiled ? config->border : B_NONE; +	if (!tiled) { +		view->using_csd = true; +		if (view->impl->has_client_side_decorations) { +			view->using_csd = view->impl->has_client_side_decorations(view); +		} +	} else { +		view->using_csd = false; +	} +  	if (view->impl->set_tiled) {  		view->impl->set_tiled(view, tiled);  	} @@ -462,27 +502,45 @@ void view_execute_criteria(struct sway_view *view) {  	list_t *criterias = criteria_for_view(view, CT_COMMAND);  	for (int i = 0; i < criterias->length; i++) {  		struct criteria *criteria = criterias->items[i]; -		wlr_log(L_DEBUG, "Checking criteria %s", criteria->raw); +		wlr_log(WLR_DEBUG, "Checking criteria %s", criteria->raw);  		if (view_has_executed_criteria(view, criteria)) { -			wlr_log(L_DEBUG, "Criteria already executed"); +			wlr_log(WLR_DEBUG, "Criteria already executed");  			continue;  		} -		wlr_log(L_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", +		wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",  				criteria->raw, view, criteria->cmdlist); +		seat_set_focus(seat, view->swayc);  		list_add(view->executed_criteria, criteria);  		struct cmd_results *res = execute_command(criteria->cmdlist, NULL);  		if (res->status != CMD_SUCCESS) { -			wlr_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); +			wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error);  		}  		free_cmd_results(res); -		// view must be focused for commands to affect it, -		// so always refocus in-between command lists -		seat_set_focus(seat, view->swayc);  	}  	list_free(criterias);  	seat_set_focus(seat, prior_focus);  } +static bool should_focus(struct sway_view *view) { +	// If the view is the only one in the focused workspace, it'll get focus +	// regardless of any no_focus criteria. +	struct sway_container *parent = view->swayc->parent; +	struct sway_seat *seat = input_manager_current_seat(input_manager); +	if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { +		size_t num_children = parent->children->length + +			parent->sway_workspace->floating->children->length; +		if (num_children == 1) { +			return true; +		} +	} + +	// Check no_focus criteria +	list_t *criterias = criteria_for_view(view, CT_NO_FOCUS); +	size_t len = criterias->length; +	list_free(criterias); +	return len == 0; +} +  void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {  	if (!sway_assert(view->surface == NULL, "cannot map mapped view")) {  		return; @@ -519,8 +577,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {  	view->surface = wlr_surface;  	view->swayc = cont; -	view->border = config->border; -	view->border_thickness = config->border_thickness;  	view_init_subsurfaces(view, wlr_surface);  	wl_signal_add(&wlr_surface->events.new_subsurface, @@ -531,14 +587,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {  	view->container_reparent.notify = view_handle_container_reparent;  	if (view->impl->wants_floating && view->impl->wants_floating(view)) { +		view->border = config->floating_border; +		view->border_thickness = config->floating_border_thickness;  		container_set_floating(view->swayc, true);  	} else { +		view->border = config->border; +		view->border_thickness = config->border_thickness;  		view_set_tiled(view, true);  	} -	input_manager_set_focus(input_manager, cont); -	if (workspace) { -		workspace_switch(workspace); +	if (should_focus(view)) { +		input_manager_set_focus(input_manager, cont); +		if (workspace) { +			workspace_switch(workspace); +		}  	}  	view_update_title(view, false); @@ -554,16 +616,27 @@ void view_unmap(struct sway_view *view) {  	wl_list_remove(&view->surface_new_subsurface.link);  	wl_list_remove(&view->container_reparent.link); +	if (view->urgent_timer) { +		wl_event_source_remove(view->urgent_timer); +		view->urgent_timer = NULL; +	} + +	struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + +	struct sway_container *parent;  	if (view->is_fullscreen) { -		struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);  		ws->sway_workspace->fullscreen = NULL; -		container_destroy(view->swayc); +		parent = container_destroy(view->swayc); -		arrange_and_commit(ws->parent); +		arrange_windows(ws->parent);  	} else { -		struct sway_container *parent = container_destroy(view->swayc); -		arrange_and_commit(parent); +		parent = container_destroy(view->swayc); +		arrange_windows(parent); +	} +	if (parent->type >= C_WORKSPACE) { // if the workspace still exists +		workspace_detect_urgent(ws);  	} +	transaction_commit_dirty();  	view->surface = NULL;  } @@ -601,7 +674,7 @@ static void view_subsurface_create(struct sway_view *view,  		struct wlr_subsurface *subsurface) {  	struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child));  	if (child == NULL) { -		wlr_log(L_ERROR, "Allocation failed"); +		wlr_log(WLR_ERROR, "Allocation failed");  		return;  	}  	view_child_init(child, NULL, view, subsurface->surface); @@ -721,8 +794,9 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {  		return NULL;  	} -	wlr_log(L_DEBUG, "Surface of unknown type (role %s): %p", -		wlr_surface->role, wlr_surface); +	const char *role = wlr_surface->role ? wlr_surface->role->name : NULL; +	wlr_log(WLR_DEBUG, "Surface of unknown type (role %s): %p", +		role, wlr_surface);  	return NULL;  } @@ -789,7 +863,7 @@ static char *escape_title(char *buffer) {  	char *escaped_title = calloc(length + 1, sizeof(char));  	int result = escape_markup_text(buffer, escaped_title, length);  	if (result != length) { -		wlr_log(L_ERROR, "Could not escape title: %s", buffer); +		wlr_log(WLR_ERROR, "Could not escape title: %s", buffer);  		free(escaped_title);  		return buffer;  	} @@ -1010,3 +1084,32 @@ bool view_is_visible(struct sway_view *view) {  	}  	return true;  } + +void view_set_urgent(struct sway_view *view, bool enable) { +	if (view_is_urgent(view) == enable) { +		return; +	} +	if (enable) { +		struct sway_seat *seat = input_manager_current_seat(input_manager); +		if (seat_get_focus(seat) == view->swayc) { +			return; +		} +		clock_gettime(CLOCK_MONOTONIC, &view->urgent); +	} else { +		view->urgent = (struct timespec){ 0 }; +		if (view->urgent_timer) { +			wl_event_source_remove(view->urgent_timer); +			view->urgent_timer = NULL; +		} +	} +	container_damage_whole(view->swayc); + +	ipc_event_window(view->swayc, "urgent"); + +	struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); +	workspace_detect_urgent(ws); +} + +bool view_is_urgent(struct sway_view *view) { +	return view->urgent.tv_sec || view->urgent.tv_nsec; +} diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 51f0fcb4..622f01ec 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -11,6 +11,7 @@  #include "sway/ipc-server.h"  #include "sway/tree/arrange.h"  #include "sway/tree/container.h" +#include "sway/tree/view.h"  #include "sway/tree/workspace.h"  #include "list.h"  #include "log.h" @@ -49,7 +50,7 @@ struct sway_container *workspace_create(struct sway_container *output,  		output = get_workspace_initial_output(name);  	} -	wlr_log(L_DEBUG, "Added workspace %s for output %s", name, output->name); +	wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name);  	struct sway_container *workspace = container_create(C_WORKSPACE);  	workspace->x = output->x; @@ -107,7 +108,7 @@ static bool workspace_valid_on_output(const char *output_name,  }  char *workspace_next_name(const char *output_name) { -	wlr_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", +	wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s",  			output_name);  	// Scan all workspace bindings to find the next available workspace name,  	// if none are found/available then default to a number @@ -135,7 +136,7 @@ char *workspace_next_name(const char *output_name) {  			while (isspace(*_target)) {  				memmove(_target, _target+1, strlen(_target+1));  			} -			wlr_log(L_DEBUG, "Got valid workspace command for target: '%s'", +			wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'",  					_target);  			// Make sure that the command references an actual workspace @@ -161,7 +162,7 @@ char *workspace_next_name(const char *output_name) {  				temp[length - 1] = '\0';  				free(_target);  				_target = temp; -				wlr_log(L_DEBUG, "Isolated name from workspace number: '%s'", _target); +				wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target);  				// Make sure the workspace number doesn't already exist  				if (workspace_by_number(_target)) { @@ -190,7 +191,7 @@ char *workspace_next_name(const char *output_name) {  				order = binding->order;  				free(target);  				target = _target; -				wlr_log(L_DEBUG, "Workspace: Found free name %s", _target); +				wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target);  			} else {  				free(_target);  			} @@ -387,7 +388,7 @@ bool workspace_switch(struct sway_container *workspace) {  		free(prev_workspace_name);  		prev_workspace_name = malloc(strlen(active_ws->name) + 1);  		if (!prev_workspace_name) { -			wlr_log(L_ERROR, "Unable to allocate previous workspace name"); +			wlr_log(WLR_ERROR, "Unable to allocate previous workspace name");  			return false;  		}  		strcpy(prev_workspace_name, active_ws->name); @@ -409,7 +410,7 @@ bool workspace_switch(struct sway_container *workspace) {  		}  	} -	wlr_log(L_DEBUG, "Switching to workspace %p:%s", +	wlr_log(WLR_DEBUG, "Switching to workspace %p:%s",  		workspace, workspace->name);  	struct sway_container *next = seat_get_focus_inactive(seat, workspace);  	if (next == NULL) { @@ -427,7 +428,7 @@ bool workspace_switch(struct sway_container *workspace) {  	}  	seat_set_focus(seat, next);  	struct sway_container *output = container_parent(workspace, C_OUTPUT); -	arrange_and_commit(output); +	arrange_windows(output);  	return true;  } @@ -518,3 +519,13 @@ struct sway_container *workspace_output_get_highest_available(  	return NULL;  } + +void workspace_detect_urgent(struct sway_container *workspace) { +	bool new_urgent = container_has_urgent_child(workspace); + +	if (workspace->sway_workspace->urgent != new_urgent) { +		workspace->sway_workspace->urgent = new_urgent; +		ipc_event_workspace(NULL, workspace, "urgent"); +		container_damage_whole(workspace); +	} +} diff --git a/swaybar/bar.c b/swaybar/bar.c index 5b8028e5..94bc48bc 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -46,7 +46,7 @@ static void swaybar_output_free(struct swaybar_output *output) {  	if (!output) {  		return;  	} -	wlr_log(L_DEBUG, "Removing output %s", output->name); +	wlr_log(WLR_DEBUG, "Removing output %s", output->name);  	zwlr_layer_surface_v1_destroy(output->layer_surface);  	wl_surface_destroy(output->surface);  	wl_output_destroy(output->output); @@ -147,7 +147,7 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,  				&& x < hotspot->x + hotspot->width  				&& y < hotspot->y + hotspot->height) {  			hotspot->callback(output, pointer->x, pointer->y, -					button, hotspot->data); +					wl_button_to_x11_button(button), hotspot->data);  		}  	}  } @@ -155,11 +155,26 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,  static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,  		uint32_t time, uint32_t axis, wl_fixed_t value) {  	struct swaybar *bar = data; +	struct swaybar_pointer *pointer = &bar->pointer;  	struct swaybar_output *output = bar->pointer.current;  	if (!sway_assert(output, "axis with no active output")) {  		return;  	} +	struct swaybar_hotspot *hotspot; +	wl_list_for_each(hotspot, &output->hotspots, link) { +		double x = pointer->x * output->scale; +		double y = pointer->y * output->scale; +		if (x >= hotspot->x +				&& y >= hotspot->y +				&& x < hotspot->x + hotspot->width +				&& y < hotspot->y + hotspot->height) { +			hotspot->callback(output, pointer->x, pointer->y, +					wl_axis_to_x11_button(axis, value), hotspot->data); +			return; +		} +	} +  	double amt = wl_fixed_to_double(value);  	if (amt == 0.0) {  		return; diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 141612a6..78b183ad 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c @@ -1,5 +1,6 @@  #define _POSIX_C_SOURCE 200809L  #include <json-c/json.h> +#include <linux/input-event-codes.h>  #include <stdlib.h>  #include <string.h>  #include <unistd.h> @@ -31,7 +32,7 @@ static bool i3bar_parse_json(struct status_line *status, const char *text) {  		status_error(status, "[failed to parse i3bar json]");  		return false;  	} -	wlr_log(L_DEBUG, "Got i3bar json: '%s'", text); +	wlr_log(WLR_DEBUG, "Got i3bar json: '%s'", text);  	for (size_t i = 0; i < json_object_array_length(results); ++i) {  		json_object *full_text, *short_text, *color, *min_width, *align, *urgent;  		json_object *name, *instance, *separator, *separator_block_width; @@ -192,8 +193,8 @@ bool i3bar_handle_readable(struct status_line *status) {  }  void i3bar_block_send_click(struct status_line *status, -		struct i3bar_block *block, int x, int y, uint32_t button) { -	wlr_log(L_DEBUG, "block %s clicked", block->name ? block->name : "(nil)"); +		struct i3bar_block *block, int x, int y, enum x11_button button) { +	wlr_log(WLR_DEBUG, "block %s clicked", block->name ? block->name : "(nil)");  	if (!block->name || !status->i3bar_state.click_events) {  		return;  	} @@ -215,3 +216,32 @@ void i3bar_block_send_click(struct status_line *status,  	}  	json_object_put(event_json);  } + +enum x11_button wl_button_to_x11_button(uint32_t button) { +	switch (button) { +	case BTN_LEFT: +		return LEFT; +	case BTN_MIDDLE: +		return MIDDLE; +	case BTN_RIGHT: +		return RIGHT; +	case BTN_SIDE: +		return BACK; +	case BTN_EXTRA: +		return FORWARD; +	default: +		return NONE; +	} +} + +enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value) { +	switch (axis) { +	case WL_POINTER_AXIS_VERTICAL_SCROLL: +		return wl_fixed_to_double(value) < 0 ? SCROLL_UP : SCROLL_DOWN; +	case WL_POINTER_AXIS_HORIZONTAL_SCROLL: +		return wl_fixed_to_double(value) < 0 ? SCROLL_LEFT : SCROLL_RIGHT; +	default: +		wlr_log(WLR_DEBUG, "Unexpected axis value on mouse scroll"); +		return NONE; +	} +} diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 959fa095..c2d05920 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -115,6 +115,18 @@ static void ipc_parse_colors(  		config->colors.inactive_workspace.text = parse_color(  				json_object_get_string(inactive_workspace_text));  	} +	if (urgent_workspace_border) { +		config->colors.urgent_workspace.border = parse_color( +				json_object_get_string(urgent_workspace_border)); +	} +	if (urgent_workspace_bg) { +		config->colors.urgent_workspace.background = parse_color( +				json_object_get_string(urgent_workspace_bg)); +	} +	if (urgent_workspace_text) { +		config->colors.urgent_workspace.text = parse_color( +				json_object_get_string(urgent_workspace_text)); +	}  	if (binding_mode_border) {  		config->colors.binding_mode.border = parse_color(  				json_object_get_string(binding_mode_border)); @@ -327,7 +339,7 @@ bool handle_ipc_readable(struct swaybar *bar) {  		json_object *result = json_tokener_parse(resp->payload);  		if (!result) {  			free_ipc_response(resp); -			wlr_log(L_ERROR, "failed to parse payload as json"); +			wlr_log(WLR_ERROR, "failed to parse payload as json");  			return false;  		}  		json_object *json_change, *json_pango_markup; @@ -340,7 +352,7 @@ bool handle_ipc_readable(struct swaybar *bar) {  				bar->config->mode = strdup(change);  			}  		} else { -			wlr_log(L_ERROR, "failed to parse response"); +			wlr_log(WLR_ERROR, "failed to parse response");  			json_object_put(result);  			free_ipc_response(resp);  			return false; diff --git a/swaybar/main.c b/swaybar/main.c index c897e1c9..60e4b37c 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -75,13 +75,13 @@ int main(int argc, char **argv) {  	}  	if (debug) { -		wlr_log_init(L_DEBUG, NULL); +		wlr_log_init(WLR_DEBUG, NULL);  	} else { -		wlr_log_init(L_ERROR, NULL); +		wlr_log_init(WLR_ERROR, NULL);  	}  	if (!bar_id) { -		wlr_log(L_ERROR, "No bar_id passed. " +		wlr_log(WLR_ERROR, "No bar_id passed. "  				"Provide --bar_id or let sway start swaybar");  		return 1;  	} @@ -89,7 +89,7 @@ int main(int argc, char **argv) {  	if (!socket_path) {  		socket_path = get_socketpath();  		if (!socket_path) { -			wlr_log(L_ERROR, "Unable to retrieve socket path"); +			wlr_log(WLR_ERROR, "Unable to retrieve socket path");  			return 1;  		}  	} diff --git a/swaybar/render.c b/swaybar/render.c index 2ebd338e..d210e25a 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -109,7 +109,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,  }  static void block_hotspot_callback(struct swaybar_output *output, -			int x, int y, uint32_t button, void *data) { +			int x, int y, enum x11_button button, void *data) {  	struct i3bar_block *block = data;  	struct status_line *status = output->bar->status;  	i3bar_block_send_click(status, block, x, y, button); @@ -349,7 +349,7 @@ static const char *strip_workspace_number(const char *ws_name) {  }  static void workspace_hotspot_callback(struct swaybar_output *output, -			int x, int y, uint32_t button, void *data) { +			int x, int y, enum x11_button button, void *data) {  	ipc_send_workspace_command(output->bar, (const char *)data);  } @@ -503,6 +503,9 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {  				output->buffers,  				output->width * output->scale,  				output->height * output->scale); +		if (!output->current_buffer) { +			return; +		}  		cairo_t *shm = output->current_buffer->cairo;  		cairo_save(shm); diff --git a/swaybar/status_line.c b/swaybar/status_line.c index e0e7414a..bc47580b 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -49,14 +49,14 @@ bool status_handle_readable(struct status_line *status) {  				json_object *version;  				if (json_object_object_get_ex(proto, "version", &version)  							&& json_object_get_int(version) == 1) { -					wlr_log(L_DEBUG, "Switched to i3bar protocol."); +					wlr_log(WLR_DEBUG, "Switched to i3bar protocol.");  					status->protocol = PROTOCOL_I3BAR;  				}  				json_object *click_events;  				if (json_object_object_get_ex(  							proto, "click_events", &click_events)  						&& json_object_get_boolean(click_events)) { -					wlr_log(L_DEBUG, "Enabled click events."); +					wlr_log(WLR_DEBUG, "Enabled click events.");  					status->i3bar_state.click_events = true;  					const char *events_array = "[\n";  					ssize_t len = strlen(events_array); @@ -91,7 +91,7 @@ struct status_line *status_line_init(char *cmd) {  	int pipe_read_fd[2];  	int pipe_write_fd[2];  	if (pipe(pipe_read_fd) != 0 || pipe(pipe_write_fd) != 0) { -		wlr_log(L_ERROR, "Unable to create pipes for status_command fork"); +		wlr_log(WLR_ERROR, "Unable to create pipes for status_command fork");  		exit(1);  	} diff --git a/swaybg/main.c b/swaybg/main.c index 5b6c378c..f8e7e7ef 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -48,7 +48,7 @@ struct swaybg_state {  bool is_valid_color(const char *color) {  	int len = strlen(color);  	if (len != 7 || color[0] != '#') { -		wlr_log(L_ERROR, "%s is not a valid color for swaybg. " +		wlr_log(WLR_ERROR, "%s is not a valid color for swaybg. "  				"Color should be specified as #rrggbb (no alpha).", color);  		return false;  	} @@ -68,6 +68,9 @@ static void render_frame(struct swaybg_state *state) {  		buffer_height = state->height * state->scale;  	state->current_buffer = get_next_buffer(state->shm,  			state->buffers, buffer_width, buffer_height); +	if (!state->current_buffer) { +		return; +	}  	cairo_t *cairo = state->current_buffer->cairo;  	if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {  		cairo_set_source_u32(cairo, state->context.color); @@ -185,10 +188,10 @@ int main(int argc, const char **argv) {  	struct swaybg_args args = {0};  	struct swaybg_state state = {0};  	state.args = &args; -	wlr_log_init(L_DEBUG, NULL); +	wlr_log_init(WLR_DEBUG, NULL);  	if (argc != 4) { -		wlr_log(L_ERROR, "Do not run this program manually. " +		wlr_log(WLR_ERROR, "Do not run this program manually. "  				"See man 5 sway and look for output options.");  		return 1;  	} diff --git a/swayidle/main.c b/swayidle/main.c index 7666578f..678d622f 100644 --- a/swayidle/main.c +++ b/swayidle/main.c @@ -1,22 +1,21 @@  #define _XOPEN_SOURCE 500 +#include <errno.h>  #include <getopt.h> -#include <signal.h>  #include <pthread.h> +#include <signal.h>  #include <stdio.h>  #include <stdlib.h> -#include <errno.h>  #include <string.h>  #include <sys/wait.h>  #include <unistd.h>  #include <wayland-client-protocol.h>  #include <wayland-client.h> +#include <wayland-server.h>  #include <wayland-util.h>  #include <wlr/config.h>  #include <wlr/util/log.h> -#include <wlr/types/wlr_output_layout.h> -#include <wlr/types/wlr_output.h> -#include "idle-client-protocol.h"  #include "config.h" +#include "idle-client-protocol.h"  #include "list.h"  #ifdef SWAY_IDLE_HAS_SYSTEMD  #include <systemd/sd-bus.h> @@ -36,7 +35,6 @@ struct swayidle_state {  	struct wl_display *display;  	struct org_kde_kwin_idle_timeout *idle_timer;  	struct org_kde_kwin_idle_timeout *lock_timer; -	struct wlr_output_layout *layout;  	struct wl_event_loop *event_loop;  	list_t *timeout_cmds;  } state; @@ -59,24 +57,24 @@ static void cmd_exec(void *data) {  		return;  	}  	char *param = (char *)data; -	wlr_log(L_DEBUG, "Cmd exec %s", param); +	wlr_log(WLR_DEBUG, "Cmd exec %s", param);  	pid_t pid = fork();  	if (pid == 0) {  		pid = fork();  		if (pid == 0) {  			char *const cmd[] = { "sh", "-c", param, NULL, };  			execvp(cmd[0], cmd); -			wlr_log_errno(L_ERROR, "execve failed!"); +			wlr_log_errno(WLR_ERROR, "execve failed!");  			exit(1);  		} else if (pid < 0) { -			wlr_log_errno(L_ERROR, "fork failed"); +			wlr_log_errno(WLR_ERROR, "fork failed");  			exit(1);  		}  		exit(0);  	} else if (pid < 0) { -		wlr_log_errno(L_ERROR, "fork failed"); +		wlr_log_errno(WLR_ERROR, "fork failed");  	} else { -		wlr_log(L_DEBUG, "Spawned process %s", param); +		wlr_log(WLR_DEBUG, "Spawned process %s", param);  		waitpid(pid, NULL, 0);  	}  } @@ -86,7 +84,7 @@ static int lock_fd = -1;  static int ongoing_fd = -1;  static int release_lock(void *data) { -	wlr_log(L_INFO, "Releasing sleep lock %d", ongoing_fd); +	wlr_log(WLR_INFO, "Releasing sleep lock %d", ongoing_fd);  	if (ongoing_fd >= 0) {  		close(ongoing_fd);  	} @@ -101,7 +99,7 @@ void acquire_sleep_lock() {  	int ret = sd_bus_default_system(&bus);  	if (ret < 0) { -		wlr_log(L_ERROR, "Failed to open D-Bus connection: %s", +		wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s",  				strerror(-ret));  		return;  	} @@ -112,17 +110,17 @@ void acquire_sleep_lock() {  			&error, &msg, "ssss", "sleep", "swayidle",  			"Setup Up Lock Screen", "delay");  	if (ret < 0) { -		wlr_log(L_ERROR, "Failed to send Inhibit signal: %s", +		wlr_log(WLR_ERROR, "Failed to send Inhibit signal: %s",  				strerror(-ret));  	} else {  		ret = sd_bus_message_read(msg, "h", &lock_fd);  		if (ret < 0) { -			wlr_log(L_ERROR, +			wlr_log(WLR_ERROR,  					"Failed to parse D-Bus response for Inhibit: %s",  					strerror(-ret));  		}  	} -	wlr_log(L_INFO, "Got sleep lock: %d", lock_fd); +	wlr_log(WLR_INFO, "Got sleep lock: %d", lock_fd);  }  static int prepare_for_sleep(sd_bus_message *msg, void *userdata, @@ -131,10 +129,10 @@ static int prepare_for_sleep(sd_bus_message *msg, void *userdata,  	int going_down = 1;  	int ret = sd_bus_message_read(msg, "b", &going_down);  	if (ret < 0) { -		wlr_log(L_ERROR, "Failed to parse D-Bus response for Inhibit: %s", +		wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s",  				strerror(-ret));  	} -	wlr_log(L_DEBUG, "PrepareForSleep signal received %d", going_down); +	wlr_log(WLR_DEBUG, "PrepareForSleep signal received %d", going_down);  	if (!going_down) {  		acquire_sleep_lock();  		return 0; @@ -151,7 +149,7 @@ static int prepare_for_sleep(sd_bus_message *msg, void *userdata,  			wl_event_loop_add_timer(state.event_loop, release_lock, NULL);  		wl_event_source_timer_update(source, 1000);  	} -	wlr_log(L_DEBUG, "Prepare for sleep done"); +	wlr_log(WLR_DEBUG, "Prepare for sleep done");  	return 0;  } @@ -165,10 +163,10 @@ static int dbus_event(int fd, uint32_t mask, void *data) {  void setup_sleep_listener() {  	struct sd_bus *bus; -	 +  	int ret = sd_bus_default_system(&bus);  	if (ret < 0) { -		wlr_log(L_ERROR, "Failed to open D-Bus connection: %s", +		wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s",  				strerror(-ret));  		return;  	} @@ -183,7 +181,7 @@ void setup_sleep_listener() {  			"/org/freedesktop/login1");  	ret = sd_bus_add_match(bus, NULL, str, prepare_for_sleep, NULL);  	if (ret < 0) { -		wlr_log(L_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); +		wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));  		return;  	}  	acquire_sleep_lock(); @@ -214,7 +212,7 @@ static const struct wl_registry_listener registry_listener = {  static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) {  	struct swayidle_timeout_cmd *cmd = data; -	wlr_log(L_DEBUG, "idle state"); +	wlr_log(WLR_DEBUG, "idle state");  	if (cmd && cmd->idle_cmd && cmd->idle_cmd->callback) {  		cmd->idle_cmd->callback(cmd->idle_cmd->param);  	} @@ -222,7 +220,7 @@ static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) {  static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) {  	struct swayidle_timeout_cmd *cmd = data; -	wlr_log(L_DEBUG, "active state"); +	wlr_log(WLR_DEBUG, "active state");  	if (cmd && cmd->resume_cmd && cmd->resume_cmd->callback) {  		cmd->resume_cmd->callback(cmd->resume_cmd->param);  	} @@ -235,12 +233,12 @@ static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {  struct swayidle_cmd *parse_command(int argc, char **argv) {  	if (argc < 1) { -		wlr_log(L_ERROR, "Too few parameters for command in parse_command"); +		wlr_log(WLR_ERROR, "Too few parameters for command in parse_command");  		return NULL;  	}  	struct swayidle_cmd *cmd = calloc(1, sizeof(struct swayidle_cmd)); -	wlr_log(L_DEBUG, "Command: %s", argv[0]); +	wlr_log(WLR_DEBUG, "Command: %s", argv[0]);  	cmd->callback = cmd_exec;  	cmd->param = argv[0];  	return cmd; @@ -248,7 +246,7 @@ struct swayidle_cmd *parse_command(int argc, char **argv) {  int parse_timeout(int argc, char **argv) {  	if (argc < 3) { -		wlr_log(L_ERROR, "Too few parameters to timeout command. " +		wlr_log(WLR_ERROR, "Too few parameters to timeout command. "  				"Usage: timeout <seconds> <command>");  		exit(-1);  	} @@ -256,7 +254,7 @@ int parse_timeout(int argc, char **argv) {  	char *endptr;  	int seconds = strtoul(argv[1], &endptr, 10);  	if (errno != 0 || *endptr != '\0') { -		wlr_log(L_ERROR, "Invalid timeout parameter '%s', it should be a " +		wlr_log(WLR_ERROR, "Invalid timeout parameter '%s', it should be a "  				"numeric value representing seconds", optarg);  		exit(-1);  	} @@ -264,13 +262,13 @@ int parse_timeout(int argc, char **argv) {  		calloc(1, sizeof(struct swayidle_timeout_cmd));  	cmd->timeout = seconds * 1000; -	wlr_log(L_DEBUG, "Register idle timeout at %d ms", cmd->timeout); -	wlr_log(L_DEBUG, "Setup idle"); +	wlr_log(WLR_DEBUG, "Register idle timeout at %d ms", cmd->timeout); +	wlr_log(WLR_DEBUG, "Setup idle");  	cmd->idle_cmd = parse_command(argc - 2, &argv[2]);  	int result = 3;  	if (argc >= 5 && !strcmp("resume", argv[3])) { -		wlr_log(L_DEBUG, "Setup resume"); +		wlr_log(WLR_DEBUG, "Setup resume");  		cmd->resume_cmd = parse_command(argc - 4, &argv[4]);  		result = 5;  	} @@ -280,14 +278,14 @@ int parse_timeout(int argc, char **argv) {  int parse_sleep(int argc, char **argv) {  	if (argc < 2) { -		wlr_log(L_ERROR, "Too few parameters to before-sleep command. " +		wlr_log(WLR_ERROR, "Too few parameters to before-sleep command. "  				"Usage: before-sleep <command>");  		exit(-1);  	}  	lock_cmd = parse_command(argc - 1, &argv[1]);  	if (lock_cmd) { -		wlr_log(L_DEBUG, "Setup sleep lock: %s", lock_cmd->param); +		wlr_log(WLR_DEBUG, "Setup sleep lock: %s", lock_cmd->param);  	}  	return 2; @@ -314,10 +312,10 @@ int parse_args(int argc, char *argv[]) {  	}  	if (debug) { -		wlr_log_init(L_DEBUG, NULL); -		wlr_log(L_DEBUG, "Loglevel debug"); +		wlr_log_init(WLR_DEBUG, NULL); +		wlr_log(WLR_DEBUG, "Loglevel debug");  	} else { -		wlr_log_init(L_INFO, NULL); +		wlr_log_init(WLR_INFO, NULL);  	} @@ -326,13 +324,13 @@ int parse_args(int argc, char *argv[]) {  	int i = optind;  	while (i < argc) {  		if (!strcmp("timeout", argv[i])) { -			wlr_log(L_DEBUG, "Got timeout"); +			wlr_log(WLR_DEBUG, "Got timeout");  			i += parse_timeout(argc - i, &argv[i]);  		} else if (!strcmp("before-sleep", argv[i])) { -			wlr_log(L_DEBUG, "Got before-sleep"); +			wlr_log(WLR_DEBUG, "Got before-sleep");  			i += parse_sleep(argc - i, &argv[i]);  		} else { -			wlr_log(L_ERROR, "Unsupported command '%s'", argv[i]); +			wlr_log(WLR_ERROR, "Unsupported command '%s'", argv[i]);  			exit(-1);  		}  	} @@ -358,16 +356,16 @@ static int display_event(int fd, uint32_t mask, void *data) {  		sway_terminate(0);  	}  	if (wl_display_dispatch(state.display) < 0) { -		wlr_log_errno(L_ERROR, "wl_display_dispatch failed, exiting"); +		wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting");  		sway_terminate(0); -	}; +	}  	return 0;  }  void register_idle_timeout(void *item) {  	struct swayidle_timeout_cmd *cmd = item;  	if (cmd == NULL || !cmd->timeout) { -		wlr_log(L_ERROR, "Invalid idle cmd, will not register"); +		wlr_log(WLR_ERROR, "Invalid idle cmd, will not register");  		return;  	}  	state.idle_timer = @@ -376,7 +374,7 @@ void register_idle_timeout(void *item) {  		org_kde_kwin_idle_timeout_add_listener(state.idle_timer,  				&idle_timer_listener, cmd);  	} else { -		wlr_log(L_ERROR, "Could not create idle timer"); +		wlr_log(WLR_ERROR, "Could not create idle timer");  	}  } @@ -390,22 +388,21 @@ int main(int argc, char *argv[]) {  	state.display = wl_display_connect(NULL);  	if (state.display == NULL) { -		wlr_log(L_ERROR, "Failed to create display"); +		wlr_log(WLR_ERROR, "Failed to create display");  		return -3;  	}  	struct wl_registry *registry = wl_display_get_registry(state.display);  	wl_registry_add_listener(registry, ®istry_listener, NULL);  	wl_display_roundtrip(state.display); -	state.layout = wlr_output_layout_create();  	state.event_loop = wl_event_loop_create();  	if (idle_manager == NULL) { -		wlr_log(L_ERROR, "Display doesn't support idle protocol"); +		wlr_log(WLR_ERROR, "Display doesn't support idle protocol");  		return -4;  	}  	if (seat == NULL) { -		wlr_log(L_ERROR, "Seat error"); +		wlr_log(WLR_ERROR, "Seat error");  		return -5;  	} @@ -417,7 +414,7 @@ int main(int argc, char *argv[]) {  	}  #endif  	if (!should_run) { -		wlr_log(L_INFO, "No command specified! Nothing to do, will exit"); +		wlr_log(WLR_INFO, "No command specified! Nothing to do, will exit");  		sway_terminate(0);  	}  	list_foreach(state.timeout_cmds, register_idle_timeout); diff --git a/swaylock/main.c b/swaylock/main.c index 73c2b5d6..ae5b86b9 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -21,6 +21,7 @@  #include "pool-buffer.h"  #include "cairo.h"  #include "log.h" +#include "readline.h"  #include "stringop.h"  #include "util.h"  #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" @@ -34,7 +35,7 @@ void sway_terminate(int exit_code) {  static void daemonize() {  	int fds[2];  	if (pipe(fds) != 0) { -		wlr_log(L_ERROR, "Failed to pipe"); +		wlr_log(WLR_ERROR, "Failed to pipe");  		exit(1);  	}  	if (fork() == 0) { @@ -58,7 +59,7 @@ static void daemonize() {  		close(fds[1]);  		uint8_t success;  		if (read(fds[0], &success, 1) != 1 || !success) { -			wlr_log(L_ERROR, "Failed to daemonize"); +			wlr_log(WLR_ERROR, "Failed to daemonize");  			exit(1);  		}  		close(fds[0]); @@ -89,7 +90,7 @@ static bool surface_is_opaque(struct swaylock_surface *surface) {  	if (surface->image) {  		return cairo_surface_get_content(surface->image) == CAIRO_CONTENT_COLOR;  	} -	return (surface->state->args.color & 0xff) == 0xff; +	return (surface->state->args.colors.background & 0xff) == 0xff;  }  static void create_layer_surface(struct swaylock_surface *surface) { @@ -238,7 +239,7 @@ static void handle_xdg_output_logical_position(void *data,  static void handle_xdg_output_name(void *data, struct zxdg_output_v1 *output,  		const char *name) { -	wlr_log(L_DEBUG, "output name is %s", name); +	wlr_log(WLR_DEBUG, "output name is %s", name);  	struct swaylock_surface *surface = data;  	surface->xdg_output = output;  	surface->output_name = strdup(name); @@ -354,10 +355,10 @@ static void load_image(char *arg, struct swaylock_state *state) {  	}  	if (exists) {  		if (image->output_name) { -			wlr_log(L_ERROR, "Multiple images defined for output %s", +			wlr_log(WLR_ERROR, "Multiple images defined for output %s",  				image->output_name);  		} else { -			wlr_log(L_ERROR, "Multiple default images defined"); +			wlr_log(WLR_ERROR, "Multiple default images defined");  		}  	} @@ -377,71 +378,232 @@ static void load_image(char *arg, struct swaylock_state *state) {  	}  	wl_list_insert(&state->images, &image->link);  	state->args.mode = BACKGROUND_MODE_FILL; -	wlr_log(L_DEBUG, "Loaded image %s for output %s", +	wlr_log(WLR_DEBUG, "Loaded image %s for output %s",  			image->path, image->output_name ? image->output_name : "*");  } -static struct swaylock_state state; +static void set_default_colors(struct swaylock_colors *colors) { +	colors->background = 0xFFFFFFFF; +	colors->bs_highlight = 0xDB3300FF; +	colors->key_highlight = 0x33DB00FF; +	colors->separator = 0x000000FF; +	colors->inside = (struct swaylock_colorset){ +		.input = 0x000000C0, +		.cleared = 0xE5A445C0, +		.verifying = 0x0072FFC0, +		.wrong = 0xFA0000C0, +	}; +	colors->line = (struct swaylock_colorset){ +		.input = 0x000000FF, +		.cleared = 0x000000FF, +		.verifying = 0x000000FF, +		.wrong = 0x000000FF, +	}; +	colors->ring = (struct swaylock_colorset){ +		.input = 0x337D00FF, +		.cleared = 0xE5A445FF, +		.verifying = 0x3300FFFF, +		.wrong = 0x7D3300FF, +	}; +	colors->text = (struct swaylock_colorset){ +		.input = 0xE5A445FF, +		.cleared = 0x000000FF, +		.verifying = 0x000000FF, +		.wrong = 0x000000FF, +	}; +} + +enum line_mode { +	LM_LINE, +	LM_INSIDE, +	LM_RING, +}; + +static int parse_options(int argc, char **argv, struct swaylock_state *state, +		enum line_mode *line_mode) { +	enum long_option_codes { +		LO_BS_HL_COLOR = 256, +		LO_FONT, +		LO_IND_RADIUS, +		LO_IND_THICKNESS, +		LO_INSIDE_COLOR, +		LO_INSIDE_CLEAR_COLOR, +		LO_INSIDE_VER_COLOR, +		LO_INSIDE_WRONG_COLOR, +		LO_KEY_HL_COLOR, +		LO_LINE_COLOR, +		LO_LINE_CLEAR_COLOR, +		LO_LINE_VER_COLOR, +		LO_LINE_WRONG_COLOR, +		LO_RING_COLOR, +		LO_RING_CLEAR_COLOR, +		LO_RING_VER_COLOR, +		LO_RING_WRONG_COLOR, +		LO_SEP_COLOR, +		LO_TEXT_COLOR, +		LO_TEXT_CLEAR_COLOR, +		LO_TEXT_VER_COLOR, +		LO_TEXT_WRONG_COLOR, +	}; -int main(int argc, char **argv) {  	static struct option long_options[] = { -		{"help", no_argument, NULL, 'h'}, +		{"config", required_argument, NULL, 'C'},  		{"color", required_argument, NULL, 'c'}, +		{"ignore-empty-password", no_argument, NULL, 'e'}, +		{"daemonize", no_argument, NULL, 'f'}, +		{"help", no_argument, NULL, 'h'},  		{"image", required_argument, NULL, 'i'}, +		{"line-uses-inside", no_argument, NULL, 'n'}, +		{"socket", required_argument, NULL, 'p'}, +		{"line-uses-ring", no_argument, NULL, 'r'},  		{"scaling", required_argument, NULL, 's'},  		{"tiling", no_argument, NULL, 't'}, -		{"version", no_argument, NULL, 'v'}, -		{"socket", required_argument, NULL, 'p'},  		{"no-unlock-indicator", no_argument, NULL, 'u'}, -		{"daemonize", no_argument, NULL, 'f'}, +		{"version", no_argument, NULL, 'v'}, +		{"bs-hl-color", required_argument, NULL, LO_BS_HL_COLOR}, +		{"font", required_argument, NULL, LO_FONT}, +		{"indicator-radius", required_argument, NULL, LO_IND_RADIUS}, +		{"indicator-thickness", required_argument, NULL, LO_IND_THICKNESS}, +		{"inside-color", required_argument, NULL, LO_INSIDE_COLOR}, +		{"inside-clear-color", required_argument, NULL, LO_INSIDE_CLEAR_COLOR}, +		{"inside-ver-color", required_argument, NULL, LO_INSIDE_VER_COLOR}, +		{"inside-wrong-color", required_argument, NULL, LO_INSIDE_WRONG_COLOR}, +		{"key-hl-color", required_argument, NULL, LO_KEY_HL_COLOR}, +		{"line-color", required_argument, NULL, LO_LINE_COLOR}, +		{"line-clear-color", required_argument, NULL, LO_LINE_CLEAR_COLOR}, +		{"line-ver-color", required_argument, NULL, LO_LINE_VER_COLOR}, +		{"line-wrong-color", required_argument, NULL, LO_LINE_WRONG_COLOR}, +		{"ring-color", required_argument, NULL, LO_RING_COLOR}, +		{"ring-clear-color", required_argument, NULL, LO_RING_CLEAR_COLOR}, +		{"ring-ver-color", required_argument, NULL, LO_RING_VER_COLOR}, +		{"ring-wrong-color", required_argument, NULL, LO_RING_WRONG_COLOR}, +		{"separator-color", required_argument, NULL, LO_SEP_COLOR}, +		{"text-color", required_argument, NULL, LO_TEXT_COLOR}, +		{"text-clear-color", required_argument, NULL, LO_TEXT_CLEAR_COLOR}, +		{"text-ver-color", required_argument, NULL, LO_TEXT_VER_COLOR}, +		{"text-wrong-color", required_argument, NULL, LO_TEXT_WRONG_COLOR},  		{0, 0, 0, 0}  	};  	const char usage[] =  		"Usage: swaylock [options...]\n"  		"\n" -		"  -h, --help                     Show help message and quit.\n" -		"  -c, --color <rrggbb[aa]>       Turn the screen into the given color instead of white.\n" -		"  -s, --scaling                  Scaling mode: stretch, fill, fit, center, tile.\n" -		"  -t, --tiling                   Same as --scaling=tile.\n" -		"  -v, --version                  Show the version number and quit.\n" -		"  -i, --image [<output>:]<path>  Display the given image.\n" -		"  -u, --no-unlock-indicator      Disable the unlock indicator.\n" -		"  -f, --daemonize                Detach from the controlling terminal after locking.\n"; - -	state.args = (struct swaylock_args){ -		.mode = BACKGROUND_MODE_SOLID_COLOR, -		.color = 0xFFFFFFFF, -		.show_indicator = true, -	}; -	wl_list_init(&state.images); - -	wlr_log_init(L_DEBUG, NULL); +		"  -C, --config <config_file>     " +			"Path to the config file.\n" +		"  -c, --color <color>            " +			"Turn the screen into the given color instead of white.\n" +		"  -e, --ignore-empty-password    " +			"When an empty password is provided, do not validate it.\n" +		"  -f, --daemonize                " +			"Detach from the controlling terminal after locking.\n" +		"  -h, --help                     " +			"Show help message and quit.\n" +		"  -i, --image [<output>:]<path>  " +			"Display the given image.\n" +		"  -s, --scaling <mode>           " +			"Scaling mode: stretch, fill, fit, center, tile.\n" +		"  -t, --tiling                   " +			"Same as --scaling=tile.\n" +		"  -u, --no-unlock-indicator      " +			"Disable the unlock indicator.\n" +		"  -v, --version                  " +			"Show the version number and quit.\n" +		"  --bs-hl-color <color>          " +			"Sets the color of backspace highlight segments.\n" +		"  --font <font>                  " +			"Sets the font of the text.\n" +		"  --indicator-radius <radius>    " +			"Sets the indicator radius.\n" +		"  --indicator-thickness <thick>  " +			"Sets the indicator thickness.\n" +		"  --inside-color <color>         " +			"Sets the color of the inside of the indicator.\n" +		"  --inside-clear-color <color>   " +			"Sets the color of the inside of the indicator when cleared.\n" +		"  --inside-ver-color <color>     " +			"Sets the color of the inside of the indicator when verifying.\n" +		"  --inside-wrong-color <color>   " +			"Sets the color of the inside of the indicator when invalid.\n" +		"  --key-hl-color <color>         " +			"Sets the color of the key press highlight segments.\n" +		"  --line-color <color>           " +			"Sets the color of the line between the inside and ring.\n" +		"  --line-clear-color <color>     " +			"Sets the color of the line between the inside and ring when " +			"cleared.\n" +		"  --line-ver-color <color>       " +			"Sets the color of the line between the inside and ring when " +			"verifying.\n" +		"  --line-wrong-color <color>     " +			"Sets the color of the line between the inside and ring when " +			"invalid.\n" +		"  -n, --line-uses-inside         " +			"Use the inside color for the line between the inside and ring.\n" +		"  -r, --line-uses-ring           " +			"Use the ring color for the line between the inside and ring.\n" +		"  --ring-color <color>           " +			"Sets the color of the ring of the indicator.\n" +		"  --ring-clear-color <color>     " +			"Sets the color of the ring of the indicator when cleared.\n" +		"  --ring-ver-color <color>       " +			"Sets the color of the ring of the indicator when verifying.\n" +		"  --ring-wrong-color <color>     " +			"Sets the color of the ring of the indicator when invalid.\n" +		"  --separator-color <color>      " +			"Sets the color of the lines that separate highlight segments.\n" +		"  --text-color <color>           " +			"Sets the color of the text.\n" +		"  --text-clear-color <color>     " +			"Sets the color of the text when cleared.\n" +		"  --text-ver-color <color>       " +			"Sets the color of the text when verifying.\n" +		"  --text-wrong-color <color>     " +			"Sets the color of the text when invalid.\n" +		"\n" +		"All <color> options are of the form <rrggbb[aa]>.\n";  	int c; +	optind = 1;  	while (1) { -		int option_index = 0; -		c = getopt_long(argc, argv, "hc:i:s:tvuf", long_options, &option_index); +		int opt_idx = 0; +		c = getopt_long(argc, argv, "c:efhi:nrs:tuvC:", long_options, &opt_idx);  		if (c == -1) {  			break;  		}  		switch (c) { -		case 'c': { -			state.args.color = parse_color(optarg); -			state.args.mode = BACKGROUND_MODE_SOLID_COLOR; +		case 'C': +			// Config file. This will have already been handled so just ignore. +			break; +		case 'c': +			state->args.colors.background = parse_color(optarg); +			state->args.mode = BACKGROUND_MODE_SOLID_COLOR; +			break; +		case 'e': +			state->args.ignore_empty = true; +			break; +		case 'f': +			state->args.daemonize = true;  			break; -		}  		case 'i': -			load_image(optarg, &state); +			load_image(optarg, state); +			break; +		case 'n': +			*line_mode = LM_INSIDE; +			break; +		case 'r': +			*line_mode = LM_RING;  			break;  		case 's': -			state.args.mode = parse_background_mode(optarg); -			if (state.args.mode == BACKGROUND_MODE_INVALID) { +			state->args.mode = parse_background_mode(optarg); +			if (state->args.mode == BACKGROUND_MODE_INVALID) {  				return 1;  			}  			break;  		case 't': -			state.args.mode = BACKGROUND_MODE_TILE; +			state->args.mode = BACKGROUND_MODE_TILE; +			break; +		case 'u': +			state->args.show_indicator = false;  			break;  		case 'v':  #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE @@ -451,11 +613,72 @@ int main(int argc, char **argv) {  			fprintf(stdout, "version unknown\n");  #endif  			return 0; -		case 'u': -			state.args.show_indicator = false; +		case LO_BS_HL_COLOR: +			state->args.colors.bs_highlight = parse_color(optarg);  			break; -		case 'f': -			state.args.daemonize = true; +		case LO_FONT: +			free(state->args.font); +			state->args.font = strdup(optarg); +			break; +		case LO_IND_RADIUS: +			state->args.radius = strtol(optarg, NULL, 0); +			break; +		case LO_IND_THICKNESS: +			state->args.thickness = strtol(optarg, NULL, 0); +			break; +		case LO_INSIDE_COLOR: +			state->args.colors.inside.input = parse_color(optarg); +			break; +		case LO_INSIDE_CLEAR_COLOR: +			state->args.colors.inside.cleared = parse_color(optarg); +			break; +		case LO_INSIDE_VER_COLOR: +			state->args.colors.inside.verifying = parse_color(optarg); +			break; +		case LO_INSIDE_WRONG_COLOR: +			state->args.colors.inside.wrong = parse_color(optarg); +			break; +		case LO_KEY_HL_COLOR: +			state->args.colors.key_highlight = parse_color(optarg); +			break; +		case LO_LINE_COLOR: +			state->args.colors.line.input = parse_color(optarg); +			break; +		case LO_LINE_CLEAR_COLOR: +			state->args.colors.line.cleared = parse_color(optarg); +			break; +		case LO_LINE_VER_COLOR: +			state->args.colors.line.verifying = parse_color(optarg); +			break; +		case LO_LINE_WRONG_COLOR: +			state->args.colors.line.wrong = parse_color(optarg); +			break; +		case LO_RING_COLOR: +			state->args.colors.ring.input = parse_color(optarg); +			break; +		case LO_RING_CLEAR_COLOR: +			state->args.colors.ring.cleared = parse_color(optarg); +			break; +		case LO_RING_VER_COLOR: +			state->args.colors.ring.verifying = parse_color(optarg); +			break; +		case LO_RING_WRONG_COLOR: +			state->args.colors.ring.wrong = parse_color(optarg); +			break; +		case LO_SEP_COLOR: +			state->args.colors.separator = parse_color(optarg); +			break; +		case LO_TEXT_COLOR: +			state->args.colors.text.input = parse_color(optarg); +			break; +		case LO_TEXT_CLEAR_COLOR: +			state->args.colors.text.cleared = parse_color(optarg); +			break; +		case LO_TEXT_VER_COLOR: +			state->args.colors.text.verifying = parse_color(optarg); +			break; +		case LO_TEXT_WRONG_COLOR: +			state->args.colors.text.wrong = parse_color(optarg);  			break;  		default:  			fprintf(stderr, "%s", usage); @@ -463,6 +686,149 @@ int main(int argc, char **argv) {  		}  	} +	return 0; +} + +static bool file_exists(const char *path) { +	return path && access(path, R_OK) != -1; +} + +static char *get_config_path(void) { +	static const char *config_paths[] = { +		"$HOME/.swaylock/config", +		"$XDG_CONFIG_HOME/swaylock/config", +		SYSCONFDIR "/swaylock/config", +	}; + +	if (!getenv("XDG_CONFIG_HOME")) { +		char *home = getenv("HOME"); +		char *config_home = malloc(strlen(home) + strlen("/.config") + 1); +		if (!config_home) { +			wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); +		} else { +			strcpy(config_home, home); +			strcat(config_home, "/.config"); +			setenv("XDG_CONFIG_HOME", config_home, 1); +			wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); +			free(config_home); +		} +	} + +	wordexp_t p; +	char *path; +	for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { +		if (wordexp(config_paths[i], &p, 0) == 0) { +			path = strdup(p.we_wordv[0]); +			wordfree(&p); +			if (file_exists(path)) { +				return path; +			} +			free(path); +		} +	} + +	return NULL; +} + +static int load_config(char *path, struct swaylock_state *state, +		enum line_mode *line_mode) { +	FILE *config = fopen(path, "r"); +	if (!config) { +		wlr_log(WLR_ERROR, "Failed to read config. Running without it."); +		return 0; +	} +	char *line; +	int line_number = 0; +	while (!feof(config)) { +		line = read_line(config); +		if (!line) { +			continue; +		} + +		line_number++; +		if (line[0] == '#') { +			free(line); +			continue; +		} +		if (strlen(line) == 0) { +			free(line); +			continue; +		} + +		wlr_log(WLR_DEBUG, "Config Line #%d: %s", line_number, line); +		char flag[strlen(line) + 3]; +		sprintf(flag, "--%s", line); +		char *argv[] = {"swaylock", flag}; +		int result = parse_options(2, argv, state, line_mode); +		if (result != 0) { +			free(line); +			fclose(config); +			return result; +		} +		free(line); +	} +	fclose(config); +	return 0; +} + +static struct swaylock_state state; + +int main(int argc, char **argv) { +	enum line_mode line_mode = LM_LINE; +	state.args = (struct swaylock_args){ +		.mode = BACKGROUND_MODE_SOLID_COLOR, +		.font = strdup("sans-serif"), +		.radius = 50, +		.thickness = 10, +		.ignore_empty = false, +		.show_indicator = true, +	}; +	wl_list_init(&state.images); +	set_default_colors(&state.args.colors); + +	wlr_log_init(WLR_DEBUG, NULL); + +	char *config_path = NULL; +	static struct option long_options[] = { +		{"config", required_argument, NULL, 'C'}, +		{0, 0, 0, 0}, +	}; +	while (1) { +		int c = getopt_long(argc, argv, "C:", long_options, NULL); +		if (c == -1) { +			break; +		} else if (c == 'C') { +			config_path = strdup(optarg); +			break; +		} +	} +	if (!config_path) { +		config_path = get_config_path(); +	} + +	if (config_path) { +		wlr_log(WLR_DEBUG, "Found config at %s", config_path); +		int config_status = load_config(config_path, &state, &line_mode); +		free(config_path); +		if (config_status != 0) { +			return config_status; +		} +	} + +	if (argc > 1) { +		wlr_log(WLR_DEBUG, "Parsing CLI Args"); +		int result = parse_options(argc, argv, &state, &line_mode); +		if (result != 0) { +			return result; +		} +	} + +	if (line_mode == LM_INSIDE) { +		state.args.colors.line = state.args.colors.inside; +	} else if (line_mode == LM_RING) { +		state.args.colors.line = state.args.colors.ring; +	} +  #ifdef __linux__  	// Most non-linux platforms require root to mlock()  	if (mlock(state.password.buffer, sizeof(state.password.buffer)) != 0) { @@ -480,13 +846,13 @@ int main(int argc, char **argv) {  	wl_display_roundtrip(state.display);  	assert(state.compositor && state.layer_shell && state.shm);  	if (!state.input_inhibit_manager) { -		wlr_log(L_ERROR, "Compositor does not support the input inhibitor " +		wlr_log(WLR_ERROR, "Compositor does not support the input inhibitor "  				"protocol, refusing to run insecurely");  		return 1;  	}  	if (wl_list_empty(&state.surfaces)) { -		wlr_log(L_DEBUG, "Exiting - no outputs to show on."); +		wlr_log(WLR_DEBUG, "Exiting - no outputs to show on.");  		return 0;  	} @@ -502,7 +868,7 @@ int main(int argc, char **argv) {  		}  		wl_display_roundtrip(state.display);  	} else { -		wlr_log(L_INFO, "Compositor does not support zxdg output manager, " +		wlr_log(WLR_INFO, "Compositor does not support zxdg output manager, "  				"images assigned to named outputs will not work");  	} @@ -520,5 +886,7 @@ int main(int argc, char **argv) {  	while (wl_display_dispatch(state.display) != -1 && state.run_display) {  		// This space intentionally left blank  	} + +	free(state.args.font);  	return 0;  } diff --git a/swaylock/password.c b/swaylock/password.c index d844ec98..7c686b34 100644 --- a/swaylock/password.c +++ b/swaylock/password.c @@ -53,15 +53,15 @@ static bool attempt_password(struct swaylock_password *pw) {  	// 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(L_ERROR, "PAM returned error %d", pam_err); +		wlr_log(WLR_ERROR, "PAM returned error %d", pam_err);  	}  	if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) { -		wlr_log(L_ERROR, "pam_authenticate failed"); +		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(L_ERROR, "pam_end failed"); +		wlr_log(WLR_ERROR, "pam_end failed");  		goto fail;  	}  	clear_password_buffer(pw); @@ -95,6 +95,10 @@ void swaylock_handle_key(struct swaylock_state *state,  	switch (keysym) {  	case XKB_KEY_KP_Enter: /* fallthrough */  	case XKB_KEY_Return: +		if (state->args.ignore_empty && state->password.len == 0) { +			break; +		} +  		state->auth_state = AUTH_STATE_VALIDATING;  		damage_state(state);  		while (wl_display_dispatch(state->display) != -1 && state->run_display) { diff --git a/swaylock/render.c b/swaylock/render.c index ea23d0d8..66c55965 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -7,11 +7,22 @@  #include "swaylock/swaylock.h"  #define M_PI 3.14159265358979323846 -const int ARC_RADIUS = 50; -const int ARC_THICKNESS = 10;  const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;  const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; +static void set_color_for_state(cairo_t *cairo, struct swaylock_state *state, +		struct swaylock_colorset *colorset) { +	if (state->auth_state == AUTH_STATE_VALIDATING) { +		cairo_set_source_u32(cairo, colorset->verifying); +	} else if (state->auth_state == AUTH_STATE_INVALID) { +		cairo_set_source_u32(cairo, colorset->wrong); +	} else if (state->auth_state == AUTH_STATE_CLEAR) { +		cairo_set_source_u32(cairo, colorset->cleared); +	} else { +		cairo_set_source_u32(cairo, colorset->input); +	} +} +  void render_frame(struct swaylock_surface *surface) {  	struct swaylock_state *state = surface->state; @@ -33,7 +44,7 @@ void render_frame(struct swaylock_surface *surface) {  	cairo_save(cairo);  	cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);  	if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR || !surface->image) { -		cairo_set_source_u32(cairo, state->args.color); +		cairo_set_source_u32(cairo, state->args.colors.background);  		cairo_paint(cairo);  	} else {  		render_background_image(cairo, surface->image, @@ -42,49 +53,25 @@ void render_frame(struct swaylock_surface *surface) {  	cairo_restore(cairo);  	cairo_identity_matrix(cairo); -	int arc_radius = ARC_RADIUS * surface->scale; -	int arc_thickness = ARC_THICKNESS * surface->scale; +	int arc_radius = state->args.radius * surface->scale; +	int arc_thickness = state->args.thickness * surface->scale;  	float type_indicator_border_thickness =  		TYPE_INDICATOR_BORDER_THICKNESS * surface->scale;  	if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) {  		// Draw circle  		cairo_set_line_width(cairo, arc_thickness); -		cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, 0, 2 * M_PI); -		switch (state->auth_state) { -		case AUTH_STATE_INPUT: -		case AUTH_STATE_INPUT_NOP: -		case AUTH_STATE_BACKSPACE: { -			cairo_set_source_rgba(cairo, 0, 0, 0, 0.75); -			cairo_fill_preserve(cairo); -			cairo_set_source_rgb(cairo, 51.0 / 255, 125.0 / 255, 0); -			cairo_stroke(cairo); -		} break; -		case AUTH_STATE_VALIDATING: { -			cairo_set_source_rgba(cairo, 0, 114.0 / 255, 255.0 / 255, 0.75); -			cairo_fill_preserve(cairo); -			cairo_set_source_rgb(cairo, 51.0 / 255, 0, 250.0 / 255); -			cairo_stroke(cairo); -		} break; -		case AUTH_STATE_INVALID: { -			cairo_set_source_rgba(cairo, 250.0 / 255, 0, 0, 0.75); -			cairo_fill_preserve(cairo); -			cairo_set_source_rgb(cairo, 125.0 / 255, 51.0 / 255, 0); -			cairo_stroke(cairo); -		} break; -		case AUTH_STATE_CLEAR: { -			cairo_set_source_rgba(cairo, 229.0/255, 164.0/255, 69.0/255, 0.75); -			cairo_fill_preserve(cairo); -			cairo_set_source_rgb(cairo, 229.0/255, 164.0/255, 69.0/255); -			cairo_stroke(cairo); -		} break; -		default: break; -		} +		cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, +				0, 2 * M_PI); +		set_color_for_state(cairo, state, &state->args.colors.inside); +		cairo_fill_preserve(cairo); +		set_color_for_state(cairo, state, &state->args.colors.ring); +		cairo_stroke(cairo);  		// Draw a message  		char *text = NULL; -		cairo_set_source_rgb(cairo, 0, 0, 0); -		cairo_select_font_face(cairo, "sans-serif", +		set_color_for_state(cairo, state, &state->args.colors.text); +		cairo_select_font_face(cairo, state->args.font,  				CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);  		cairo_set_font_size(cairo, arc_radius / 3.0f);  		switch (state->auth_state) { @@ -101,9 +88,10 @@ void render_frame(struct swaylock_surface *surface) {  		case AUTH_STATE_INPUT_NOP:  			if (state->xkb.caps_lock) {  				text = "Caps Lock"; -				cairo_set_source_rgb(cairo, 229.0/255, 164.0/255, 69.0/255);  			} -		default: break; +			break; +		default: +			break;  		}  		if (text) { @@ -131,14 +119,14 @@ void render_frame(struct swaylock_surface *surface) {  					arc_radius, highlight_start,  					highlight_start + TYPE_INDICATOR_RANGE);  			if (state->auth_state == AUTH_STATE_INPUT) { -				cairo_set_source_rgb(cairo, 51.0 / 255, 219.0 / 255, 0); +				cairo_set_source_u32(cairo, state->args.colors.key_highlight);  			} else { -				cairo_set_source_rgb(cairo, 219.0 / 255, 51.0 / 255, 0); +				cairo_set_source_u32(cairo, state->args.colors.bs_highlight);  			}  			cairo_stroke(cairo);  			// Draw borders -			cairo_set_source_rgb(cairo, 0, 0, 0); +			cairo_set_source_u32(cairo, state->args.colors.separator);  			cairo_arc(cairo, buffer_width / 2, buffer_height / 2,  					arc_radius, highlight_start,  					highlight_start + type_indicator_border_thickness); @@ -152,7 +140,7 @@ void render_frame(struct swaylock_surface *surface) {  		}  		// Draw inner + outer border of the circle -		cairo_set_source_rgb(cairo, 0, 0, 0); +		set_color_for_state(cairo, state, &state->args.colors.line);  		cairo_set_line_width(cairo, 2.0 * surface->scale);  		cairo_arc(cairo, buffer_width / 2, buffer_height / 2,  				arc_radius - arc_thickness / 2, 0, 2 * M_PI); diff --git a/swaylock/seat.c b/swaylock/seat.c index 6c66d220..c2630d87 100644 --- a/swaylock/seat.c +++ b/swaylock/seat.c @@ -12,13 +12,13 @@ static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,  	struct swaylock_state *state = data;  	if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {  		close(fd); -		wlr_log(L_ERROR, "Unknown keymap format %d, aborting", format); +		wlr_log(WLR_ERROR, "Unknown keymap format %d, aborting", format);  		exit(1);  	}  	char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);  	if (map_shm == MAP_FAILED) {  		close(fd); -		wlr_log(L_ERROR, "Unable to initialize keymap shm, aborting"); +		wlr_log(WLR_ERROR, "Unable to initialize keymap shm, aborting");  		exit(1);  	}  	struct xkb_keymap *keymap = xkb_keymap_new_from_string( diff --git a/swaylock/swaylock.1.scd b/swaylock/swaylock.1.scd index 1b3366f0..3107124f 100644 --- a/swaylock/swaylock.1.scd +++ b/swaylock/swaylock.1.scd @@ -12,23 +12,34 @@ Locks your Wayland session.  # OPTIONS -*-h, --help* -	Show help message and quit. +*-C, --config* <path> +	The config file to use. By default, the following paths are checked: +	_$HOME/.swaylock/config_, _$XDG\_CONFIG\_HOME/swaylock/config_, and +	_SYSCONFDIR/swaylock/config_. All flags aside from this one are valid +	options in the configuration file using the format _long-option=value_. +	For options such as _ignore-empty-password_, just supply the _long-option_. +	All leading dashes should be omitted and the equals sign is required for +	flags that take an argument.  *-c, --color* <rrggbb[aa]>  	Turn the screen into the given color. If -i is used, this sets the  	background of the image to the given color. Defaults to white (FFFFFF), or  	transparent (00000000) if an image is in use. +*-e, --ignore-empty-password* +	When an empty password is provided by the user, do not validate it. +  *-f, --daemonize* -	Fork into the background after spawning. Note: this is the default behavior -	of i3lock. +	Detach from the controlling terminal after locking. + +*-h, --help* +	Show help message and quit.  *-i, --image* [<output>:]<path>  	Display the given image, optionally only on the given output. Use -c to set  	a background color. -*--scaling* +*-s, --scaling*  	Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_.  *-t, --tiling* @@ -37,37 +48,57 @@ Locks your Wayland session.  *-u, --no-unlock-indicator*  	Disable the unlock indicator. -*-f, --daemonize* -	Detach from the controlling terminal after locking. -  *-v, --version*  	Show the version number and quit.  # APPEARANCE -*--bshlcolor* <rrggbb[aa]> +*--bs-hl-color* <rrggbb[aa]>  	Sets the color of backspace highlight segments.  *--font* <font>  	Sets the font of the text inside the indicator. -*--insidecolor* <rrggbb[aa]> +*--indicator-radius* <radius> +	Sets the radius of the indicator to _radius_ pixels. The default value is +	50. + +*--indicator-thickness* <thickness> +	Sets the thickness of the indicator to _thickness_ pixels. The default value +	is 10. + +*--inside-color* <rrggbb[aa]>  	Sets the color of the inside of the indicator when typing or idle. -*--insidevercolor* <rrggbb[aa]> +*--inside-clear-color* <rrggbb[aa]> +	Sets the color of the inside of the indicator when cleared. + +*--inside-ver-color* <rrggbb[aa]>  	Sets the color of the inside of the indicator when verifying. -*--insidewrongcolor* <rrggbb[aa]> +*--inside-wrong-color* <rrggbb[aa]>  	Sets the color of the inside of the indicator when invalid. -*--keyhlcolor* <rrggbb[aa]> -	Sets the color of keypress highlight segments. +*--key-hl-color* <rrggbb[aa]> +	Sets the color of key press highlight segments. + +*--line-color* <rrggbb[aa]> +	Sets the color of the lines that separate the inside and outside of the +	indicator when typing or idle. + +*--line-clear-color* <rrggbb[aa]> +	Sets the color of the lines that separate the inside and outside of the +	indicator when cleared. + +*--line-ver-color* <rrggbb[aa]> +	Sets the color of the lines that separate the inside and outside of the +	indicator when verifying. -*--linecolor* <rrggbb[aa]> +*--line-wrong-color* <rrggbb[aa]>  	Sets the color of the lines that separate the inside and outside of the -	indicator. +	indicator when invalid. -*-s, --line-uses-inside* +*-n, --line-uses-inside*  	Use the color of the inside of the indicator for the line separating the  	inside and outside of the indicator. @@ -75,28 +106,32 @@ Locks your Wayland session.  	Use the outer ring's color for the line separating the inside and outside of  	the indicator. -*--ringcolor* <rrggbb[aa]> +*--ring-color* <rrggbb[aa]>  	Sets the color of the outside of the indicator when typing or idle. -*--ringvercolor* <rrggbb[aa]> +*--ring-clear-color* <rrggbb[aa]> +	Sets the color of the outside of the indicator when cleared. + +*--ring-ver-color* <rrggbb[aa]>  	Sets the color of the outside of the indicator when verifying. -*--ringwrongcolor* <rrggbb[aa]> +*--ring-wrong-color* <rrggbb[aa]>  	Sets the color of the outside of the indicator when invalid. -*--separatorcolor* <rrggbb[aa]> -	Sets the color of the lines that seperate highlight segments. +*--separator-color* <rrggbb[aa]> +	Sets the color of the lines that separate highlight segments. -*--textcolor* <rrggbb[aa]> -	Sets the color of the text inside the indicator. +*--text-color* <rrggbb[aa]> +	Sets the color of the text inside the indicator when typing or idle. -*--indicator-radius* <radius> -	Sets the radius of the indicator to _radius_ pixels. The default value is -	50. +*--text-clear-color* <rrggbb[aa]> +	Sets the color of the text inside the indicator when cleared. -*--indicator-thickness* <thickness> -	Sets the thickness of the indicator to _thickness_ pixels. The default value -	is 10. +*--text-ver-color* <rrggbb[aa]> +	Sets the color of the text inside the indicator when verifying. + +*--text-wrong-color* <rrggbb[aa]> +	Sets the color of the text inside the indicator when invalid.  # AUTHORS diff --git a/swaymsg/main.c b/swaymsg/main.c index 4283bf00..c4141ca5 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -240,44 +240,17 @@ static void pretty_print_version(json_object *v) {  	printf("sway version %s\n", json_object_get_string(ver));  } -static void pretty_print_clipboard(json_object *v) { -	if (success(v, true)) { -		if (json_object_is_type(v, json_type_array)) { -			for (size_t i = 0; i < json_object_array_length(v); ++i) { -				json_object *o = json_object_array_get_idx(v, i); -				printf("%s\n", json_object_get_string(o)); -			} -		} else { -			// NOTE: could be extended to print all received types -			// instead just the first one when sways ipc server -			// supports it -			struct json_object_iterator iter = json_object_iter_begin(v); -			struct json_object_iterator end = json_object_iter_end(v); -			if (!json_object_iter_equal(&iter, &end)) { -				json_object *obj = json_object_iter_peek_value(&iter); -				if (success(obj, false)) { -					json_object *content; -					json_object_object_get_ex(obj, "content", &content); -					printf("%s\n", json_object_get_string(content)); -				} else { -					json_object *error; -					json_object_object_get_ex(obj, "error", &error); -					printf("Error: %s\n", json_object_get_string(error)); -				} -			} -		} -	} else { -		json_object *error; -		json_object_object_get_ex(v, "error", &error); -		printf("Error: %s\n", json_object_get_string(error)); -	} +static void pretty_print_config(json_object *c) { +	json_object *config; +	json_object_object_get_ex(c, "config", &config); +	printf("%s\n", json_object_get_string(config));  }  static void pretty_print(int type, json_object *resp) {  	if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES &&  			type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && -			type != IPC_GET_VERSION && type != IPC_GET_CLIPBOARD && -			type != IPC_GET_SEATS) { +			type != IPC_GET_VERSION && type != IPC_GET_SEATS && +			type != IPC_GET_CONFIG) {  		printf("%s\n", json_object_to_json_string_ext(resp,  			JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));  		return; @@ -288,8 +261,8 @@ static void pretty_print(int type, json_object *resp) {  		return;  	} -	if (type == IPC_GET_CLIPBOARD) { -		pretty_print_clipboard(resp); +	if (type == IPC_GET_CONFIG) { +		pretty_print_config(resp);  		return;  	} @@ -323,7 +296,7 @@ int main(int argc, char **argv) {  	char *socket_path = NULL;  	char *cmdtype = NULL; -	wlr_log_init(L_INFO, NULL); +	wlr_log_init(WLR_INFO, NULL);  	static struct option long_options[] = {  		{"help", no_argument, NULL, 'h'}, @@ -407,8 +380,10 @@ int main(int argc, char **argv) {  		type = IPC_GET_BAR_CONFIG;  	} else if (strcasecmp(cmdtype, "get_version") == 0) {  		type = IPC_GET_VERSION; -	} else if (strcasecmp(cmdtype, "get_clipboard") == 0) { -		type = IPC_GET_CLIPBOARD; +	} else if (strcasecmp(cmdtype, "get_binding_modes") == 0) { +		type = IPC_GET_BINDING_MODES; +	} else if (strcasecmp(cmdtype, "get_config") == 0) { +		type = IPC_GET_CONFIG;  	} else {  		sway_abort("Unknown message type %s", cmdtype);  	} diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index 1aa6a1b0..a6e279da 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -59,8 +59,8 @@ _swaymsg_ [options...] [message]  *get\_version*  	Get JSON-encoded version information for the running instance of sway. -*get\_clipboard* -	Get JSON-encoded information about the clipboard. -	Returns the current clipboard mime-types if called without -	arguments, otherwise returns the clipboard data in the requested -	formats. Encodes the data using base64 for non-text mime types. +*get\_binding\_modes* +	Gets a JSON-encoded list of currently configured binding modes. + +*get\_config* +	Gets a JSON-encoded copy of the current configuration. | 
