diff options
| -rw-r--r-- | include/sway/config.h | 23 | ||||
| -rw-r--r-- | include/sway/input/cursor.h | 6 | ||||
| -rw-r--r-- | sway/commands/bind.c | 168 | ||||
| -rw-r--r-- | sway/config.c | 7 | ||||
| -rw-r--r-- | sway/input/cursor.c | 102 | ||||
| -rw-r--r-- | sway/input/keyboard.c | 42 | 
6 files changed, 271 insertions, 77 deletions
diff --git a/include/sway/config.h b/include/sway/config.h index b8da29c5..bcd503a4 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -22,14 +22,28 @@ struct sway_variable {  	char *value;  }; + +enum binding_input_type { +	BINDING_KEYCODE, +	BINDING_KEYSYM, +	BINDING_MOUSE, +}; + +enum binding_flags { +	BINDING_RELEASE=1, +	BINDING_LOCKED=2, // keyboard only +	BINDING_BORDER=4, // mouse only; trigger on container border +	BINDING_CONTENTS=8, // mouse only; trigger on container contents +	BINDING_TITLEBAR=16 // mouse only; trigger on container titlebar +}; +  /**   * A key binding and an associated command.   */  struct sway_binding { +	enum binding_input_type type;  	int order; -	bool release; -	bool locked; -	bool bindcode; +	uint32_t flags;  	list_t *keys; // sorted in ascending order  	uint32_t modifiers;  	char *command; @@ -50,6 +64,7 @@ struct sway_mode {  	char *name;  	list_t *keysym_bindings;  	list_t *keycode_bindings; +	list_t *mouse_bindings;  	bool pango;  }; @@ -482,6 +497,8 @@ void free_sway_binding(struct sway_binding *sb);  struct sway_binding *sway_binding_dup(struct sway_binding *sb); +void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); +  void load_swaybars();  void invoke_swaybar(struct bar_config *bar); diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index b0a3a7c5..7ec45120 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -3,6 +3,8 @@  #include <stdint.h>  #include "sway/input/seat.h" +#define SWAY_CURSOR_PRESSED_BUTTONS_CAP 32 +  struct sway_cursor {  	struct sway_seat *seat;  	struct wlr_cursor *cursor; @@ -29,6 +31,10 @@ struct sway_cursor {  	uint32_t tool_buttons;  	struct wl_listener request_set_cursor; + +	// Mouse binding state +	uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; +	size_t pressed_button_count;  };  void sway_cursor_destroy(struct sway_cursor *cursor); diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 83e9e432..133fd089 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -34,11 +34,14 @@ void free_sway_binding(struct sway_binding *binding) {   */  static bool binding_key_compare(struct sway_binding *binding_a,  		struct sway_binding *binding_b) { -	if (binding_a->release != binding_b->release) { +	if (binding_a->type != binding_b->type) {  		return false;  	} -	if (binding_a->bindcode != binding_b->bindcode) { +	uint32_t conflict_generating_flags = BINDING_RELEASE | BINDING_BORDER +			| BINDING_CONTENTS | BINDING_TITLEBAR; +	if ((binding_a->flags & conflict_generating_flags) != +			(binding_b->flags & conflict_generating_flags)) {  		return false;  	} @@ -69,6 +72,66 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) {  	return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0);  } + +/** + * From a keycode, bindcode, or bindsym name and the most likely binding type, + * identify the appropriate numeric value corresponding to the key. Return NULL + * and set *key_val if successful, otherwise return a specific error. Change + * the value of *type if the initial type guess was incorrect and if this + * was the first identified key. + */ +static struct cmd_results *identify_key(const char* name, bool first_key, +		uint32_t* key_val, enum binding_input_type* type) { +	if (*type == BINDING_KEYCODE) { +		// check for keycode +		xkb_keycode_t keycode = strtol(name, NULL, 10); +		if (!xkb_keycode_is_legal_ext(keycode)) { +			return cmd_results_new(CMD_INVALID, "bindcode", +					"Invalid keycode '%s'", name); +		} +		*key_val = keycode; +	} else { +		// check for keysym +		xkb_keysym_t keysym = xkb_keysym_from_name(name, +				XKB_KEYSYM_CASE_INSENSITIVE); + +		// Check for mouse binding +		uint32_t button = 0; +		if (strncasecmp(name, "button", strlen("button")) == 0 && +				strlen(name) == strlen("button0")) { +			button = name[strlen("button")] - '1' + BTN_LEFT; +		} + +		if (*type == BINDING_KEYSYM) { +			if (button) { +				if (first_key) { +					*type = BINDING_MOUSE; +					*key_val = button; +				} else { +					return cmd_results_new(CMD_INVALID, "bindsym", +							"Mixed button '%s' into key sequence", name); +				} +			} else if (keysym) { +				*key_val = keysym; +			} else { +				return cmd_results_new(CMD_INVALID, "bindsym", +						"Unknown key '%s'", name); +			} +		} else { +			if (button) { +				*key_val = button; +			} else if (keysym) { +				return cmd_results_new(CMD_INVALID, "bindsym", +						"Mixed keysym '%s' into button sequence", name); +			} else { +				return cmd_results_new(CMD_INVALID, "bindsym", +						"Unknown button '%s'", name); +			} +		} +	} +	return NULL; +} +  static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,  		bool bindcode) {  	const char *bindtype = bindcode ? "bindcode" : "bindsym"; @@ -85,22 +148,34 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,  	}  	binding->keys = create_list();  	binding->modifiers = 0; -	binding->release = false; -	binding->locked = false; -	binding->bindcode = bindcode; +	binding->flags = 0; +	binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM; + +	bool exclude_titlebar = false;  	// Handle --release and --locked  	while (argc > 0) {  		if (strcmp("--release", argv[0]) == 0) { -			binding->release = true; +			binding->flags |= BINDING_RELEASE;  		} else if (strcmp("--locked", argv[0]) == 0) { -			binding->locked = true; +			binding->flags |= BINDING_LOCKED; +		} else if (strcmp("--whole-window", argv[0]) == 0) { +			binding->flags |= BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR; +		} else if (strcmp("--border", argv[0]) == 0) { +			binding->flags |= BINDING_BORDER; +		} else if (strcmp("--exclude-titlebar", argv[0]) == 0) { +			exclude_titlebar = true;  		} else {  			break;  		}  		argv++;  		argc--;  	} +	if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR) +			|| exclude_titlebar) { +		binding->type = BINDING_MOUSE; +	} +  	if (argc < 2) {  		free_sway_binding(binding);  		return cmd_results_new(CMD_FAILURE, bindtype, @@ -119,64 +194,47 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,  			continue;  		} -		xkb_keycode_t keycode; -		xkb_keysym_t keysym; -		if (bindcode) { -			// parse keycode -			keycode = (int)strtol(split->items[i], NULL, 10); -			if (!xkb_keycode_is_legal_ext(keycode)) { -				error = -					cmd_results_new(CMD_INVALID, "bindcode", -						"Invalid keycode '%s'", (char *)split->items[i]); -				free_sway_binding(binding); -				list_free(split); -				return error; -			} -		} else { -			// Check for xkb key -			 keysym = xkb_keysym_from_name(split->items[i], -					XKB_KEYSYM_CASE_INSENSITIVE); - -			// Check for mouse binding -			if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && -					strlen(split->items[i]) == strlen("button0")) { -				keysym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT; -			} -			if (!keysym) { -				struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", -						"Unknown key '%s'", (char *)split->items[i]); -				free_sway_binding(binding); -				free_flat_list(split); -				return ret; -			} +		// Identify the key and possibly change binding->type +		uint32_t key_val = 0; +		error = identify_key(split->items[i], binding->keys->length == 0, +				     &key_val, &binding->type); +		if (error) { +			free_sway_binding(binding); +			list_free(split); +			return error;  		} +  		uint32_t *key = calloc(1, sizeof(uint32_t));  		if (!key) {  			free_sway_binding(binding);  			free_flat_list(split);  			return cmd_results_new(CMD_FAILURE, bindtype, -					"Unable to allocate binding"); +					"Unable to allocate binding key");  		} - -		if (bindcode) { -			*key = (uint32_t)keycode; -		} else { -			*key = (uint32_t)keysym; -		} - +		*key = key_val;  		list_add(binding->keys, key);  	}  	free_flat_list(split);  	binding->order = binding_order++; +	// refine region of interest for mouse binding once we are certain +	// that this is one +	if (exclude_titlebar) { +		binding->flags &= ~BINDING_TITLEBAR; +	} else if (binding->type == BINDING_MOUSE) { +		binding->flags |= BINDING_TITLEBAR; +	} +  	// sort ascending  	list_qsort(binding->keys, key_qsort_cmp);  	list_t *mode_bindings; -	if (bindcode) { +	if (binding->type == BINDING_KEYCODE) {  		mode_bindings = config->current_mode->keycode_bindings; -	} else { +	} else if (binding->type == BINDING_KEYSYM) {  		mode_bindings = config->current_mode->keysym_bindings; +	} else { +		mode_bindings = config->current_mode->mouse_bindings;  	}  	// overwrite the binding if it already exists @@ -209,3 +267,19 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {  struct cmd_results *cmd_bindcode(int argc, char **argv) {  	return cmd_bindsym_or_bindcode(argc, argv, true);  } + + +/** + * Execute the command associated to a binding + */ +void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { +	wlr_log(WLR_DEBUG, "running command for binding: %s", +		binding->command); +	config->handler_context.seat = seat; +	struct cmd_results *results = execute_command(binding->command, NULL); +	if (results->status != CMD_SUCCESS) { +		wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", +			binding->command, results->error); +	} +	free_cmd_results(results); +} diff --git a/sway/config.c b/sway/config.c index ed624bfa..c2310ff7 100644 --- a/sway/config.c +++ b/sway/config.c @@ -56,6 +56,12 @@ static void free_mode(struct sway_mode *mode) {  		}  		list_free(mode->keycode_bindings);  	} +	if (mode->mouse_bindings) { +		for (i = 0; i < mode->mouse_bindings->length; i++) { +			free_sway_binding(mode->mouse_bindings->items[i]); +		} +		list_free(mode->mouse_bindings); +	}  	free(mode);  } @@ -172,6 +178,7 @@ static void config_defaults(struct sway_config *config) {  	strcpy(config->current_mode->name, "default");  	if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup;  	if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; +	if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;  	list_add(config->modes, config->current_mode);  	config->floating_mod = 0; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 65d04cac..f1481936 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -469,6 +469,83 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor,  	seat_pointer_notify_button(seat, time_msec, button, state);  } +/** + * Remove a button (and duplicates) to the sorted list of currently pressed buttons + */ +static void state_erase_button(struct sway_cursor *cursor, uint32_t button) { +	size_t j = 0; +	for (size_t i = 0; i < cursor->pressed_button_count; ++i) { +		if (i > j) { +			cursor->pressed_buttons[j] = cursor->pressed_buttons[i]; +		} +		if (cursor->pressed_buttons[i] != button) { +			++j; +		} +	} +	while (cursor->pressed_button_count > j) { +		--cursor->pressed_button_count; +		cursor->pressed_buttons[cursor->pressed_button_count] = 0; +	} +} + +/** + * Add a button to the sorted list of currently pressed buttons, if there + * is space. + */ +static void state_add_button(struct sway_cursor *cursor, uint32_t button) { +	if (cursor->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) { +		return; +	} +	size_t i = 0; +	while (i < cursor->pressed_button_count && cursor->pressed_buttons[i] < button) { +		++i; +	} +	size_t j = cursor->pressed_button_count; +	while (j > i) { +		cursor->pressed_buttons[j] = cursor->pressed_buttons[j - 1]; +		--j; +	} +	cursor->pressed_buttons[i] = button; +	cursor->pressed_button_count++; +} + +/** + * Return the mouse binding which matches modifier, click location, release, + * and pressed button state, otherwise return null. + */ +static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *cursor, +		list_t *bindings, uint32_t modifiers, bool release, bool on_titlebar, +				     bool on_border, bool on_content) { +	uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) | +			(on_border ? BINDING_BORDER : 0) | +			(on_content ? BINDING_CONTENTS : 0); + +	for (int i = 0; i < bindings->length; ++i) { +		struct sway_binding *binding = bindings->items[i]; +		if (modifiers ^ binding->modifiers || +				cursor->pressed_button_count != (size_t)binding->keys->length || +				release != (binding->flags & BINDING_RELEASE) || +				!(click_region & binding->flags)) { +			continue; +		} + +		bool match = true; +		for (size_t j = 0; j < cursor->pressed_button_count; j++) { +			uint32_t key = *(uint32_t *)binding->keys->items[j]; +			if (key != cursor->pressed_buttons[j]) { +				match = false; +				break; +			} +		} +		if (!match) { +			continue; +		} + +		return binding; +	} +	return NULL; +} +  void dispatch_cursor_button(struct sway_cursor *cursor,  		uint32_t time_msec, uint32_t button, enum wlr_button_state state) {  	if (cursor->seat->operation != OP_NONE && @@ -485,6 +562,31 @@ void dispatch_cursor_button(struct sway_cursor *cursor,  	double sx, sy;  	struct sway_container *cont = container_at_coords(cursor->seat,  			cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + +	// Handle mouse bindings +	bool on_border = find_resize_edge(cont, cursor) != WLR_EDGE_NONE; +	bool on_contents = !on_border && surface; +	bool on_titlebar = !on_border && !surface; +	struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat); +	uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + +	struct sway_binding *binding = NULL; +	if (state == WLR_BUTTON_PRESSED) { +		state_add_button(cursor, button); +		binding = get_active_mouse_binding(cursor, +			config->current_mode->mouse_bindings, modifiers, false, +			on_titlebar, on_border, on_contents); +	} else { +		binding = get_active_mouse_binding(cursor, +			config->current_mode->mouse_bindings, modifiers, true, +			on_titlebar, on_border, on_contents); +		state_erase_button(cursor, button); +	} +	if (binding) { +		seat_execute_command(cursor->seat, binding); +		// TODO: do we want to pass on the event? +	} +  	if (surface && wlr_surface_is_layer_surface(surface)) {  		struct wlr_layer_surface *layer =  			wlr_layer_surface_from_wlr_surface(surface); diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index ede38519..49241db8 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -3,11 +3,11 @@  #include <wlr/backend/multi.h>  #include <wlr/backend/session.h>  #include <wlr/types/wlr_idle.h> +#include "sway/commands.h"  #include "sway/desktop/transaction.h" -#include "sway/input/seat.h" -#include "sway/input/keyboard.h"  #include "sway/input/input-manager.h" -#include "sway/commands.h" +#include "sway/input/keyboard.h" +#include "sway/input/seat.h"  #include "log.h"  /** @@ -88,11 +88,13 @@ static void get_active_binding(const struct sway_shortcut_state *state,  		uint32_t modifiers, bool release, bool locked) {  	for (int i = 0; i < bindings->length; ++i) {  		struct sway_binding *binding = bindings->items[i]; +		bool binding_locked = binding->flags & BINDING_LOCKED; +		bool binding_release = binding->flags & BINDING_RELEASE;  		if (modifiers ^ binding->modifiers ||  				state->npressed != (size_t)binding->keys->length || -				locked > binding->locked || -				release != binding->release) { +				release != binding_release || +				locked > binding_locked) {  			continue;  		} @@ -119,23 +121,6 @@ static void get_active_binding(const struct sway_shortcut_state *state,  }  /** - * Execute the command associated to a binding - */ -static void keyboard_execute_command(struct sway_keyboard *keyboard, -		struct sway_binding *binding) { -	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(WLR_DEBUG, "could not run command for binding: %s (%s)", -			binding->command, results->error); -	} -	free_cmd_results(results); -} - -/**   * Execute a built-in, hardcoded compositor binding. These are triggered from a   * single keysym.   * @@ -211,12 +196,13 @@ static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,  static void handle_keyboard_key(struct wl_listener *listener, void *data) {  	struct sway_keyboard *keyboard =  		wl_container_of(listener, keyboard, keyboard_key); -	struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; +	struct sway_seat* seat = keyboard->seat_device->sway_seat; +	struct wlr_seat *wlr_seat = seat->wlr_seat;  	struct wlr_input_device *wlr_device =  		keyboard->seat_device->input_device->wlr_device; -	wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); +	wlr_idle_notify_activity(seat->input->server->idle, wlr_seat);  	struct wlr_event_keyboard_key *event = data; -	bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; +	bool input_inhibited = seat->exclusive_client != NULL;  	// Identify new keycode, raw keysym(s), and translated keysym(s)  	xkb_keycode_t keycode = event->keycode + 8; @@ -266,7 +252,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {  	// Execute stored release binding once no longer active  	if (keyboard->held_binding && binding_released != keyboard->held_binding &&  			event->state == WLR_KEY_RELEASED) { -		keyboard_execute_command(keyboard, keyboard->held_binding); +		seat_execute_command(seat, keyboard->held_binding);  		handled = true;  	}  	if (binding_released != keyboard->held_binding) { @@ -290,7 +276,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {  				raw_modifiers, false, input_inhibited);  		if (binding_pressed) { -			keyboard_execute_command(keyboard, binding_pressed); +			seat_execute_command(seat, binding_pressed);  			handled = true;  		}  	} @@ -312,6 +298,8 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {  		wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,  				event->keycode, event->state);  	} + +	transaction_commit_dirty();  }  static void handle_keyboard_modifiers(struct wl_listener *listener,  | 
