Merge pull request #2303 from frsfnrrg/mouse-bindings

Implement mouse bindings
master
Ryan Dwyer 6 years ago committed by GitHub
commit 1a9179dbab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -22,14 +22,28 @@ struct sway_variable {
char *value; 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. * A key binding and an associated command.
*/ */
struct sway_binding { struct sway_binding {
enum binding_input_type type;
int order; int order;
bool release; uint32_t flags;
bool locked;
bool bindcode;
list_t *keys; // sorted in ascending order list_t *keys; // sorted in ascending order
uint32_t modifiers; uint32_t modifiers;
char *command; char *command;
@ -50,6 +64,7 @@ struct sway_mode {
char *name; char *name;
list_t *keysym_bindings; list_t *keysym_bindings;
list_t *keycode_bindings; list_t *keycode_bindings;
list_t *mouse_bindings;
bool pango; bool pango;
}; };
@ -482,6 +497,8 @@ void free_sway_binding(struct sway_binding *sb);
struct sway_binding *sway_binding_dup(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 load_swaybars();
void invoke_swaybar(struct bar_config *bar); void invoke_swaybar(struct bar_config *bar);

@ -3,6 +3,8 @@
#include <stdint.h> #include <stdint.h>
#include "sway/input/seat.h" #include "sway/input/seat.h"
#define SWAY_CURSOR_PRESSED_BUTTONS_CAP 32
struct sway_cursor { struct sway_cursor {
struct sway_seat *seat; struct sway_seat *seat;
struct wlr_cursor *cursor; struct wlr_cursor *cursor;
@ -29,6 +31,10 @@ struct sway_cursor {
uint32_t tool_buttons; uint32_t tool_buttons;
struct wl_listener request_set_cursor; 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); void sway_cursor_destroy(struct sway_cursor *cursor);

@ -34,11 +34,14 @@ void free_sway_binding(struct sway_binding *binding) {
*/ */
static bool binding_key_compare(struct sway_binding *binding_a, static bool binding_key_compare(struct sway_binding *binding_a,
struct sway_binding *binding_b) { struct sway_binding *binding_b) {
if (binding_a->release != binding_b->release) { if (binding_a->type != binding_b->type) {
return false; 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; 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); 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, static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
bool bindcode) { bool bindcode) {
const char *bindtype = bindcode ? "bindcode" : "bindsym"; 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->keys = create_list();
binding->modifiers = 0; binding->modifiers = 0;
binding->release = false; binding->flags = 0;
binding->locked = false; binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM;
binding->bindcode = bindcode;
bool exclude_titlebar = false;
// Handle --release and --locked // Handle --release and --locked
while (argc > 0) { while (argc > 0) {
if (strcmp("--release", argv[0]) == 0) { if (strcmp("--release", argv[0]) == 0) {
binding->release = true; binding->flags |= BINDING_RELEASE;
} else if (strcmp("--locked", argv[0]) == 0) { } 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 { } else {
break; break;
} }
argv++; argv++;
argc--; argc--;
} }
if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR)
|| exclude_titlebar) {
binding->type = BINDING_MOUSE;
}
if (argc < 2) { if (argc < 2) {
free_sway_binding(binding); free_sway_binding(binding);
return cmd_results_new(CMD_FAILURE, bindtype, return cmd_results_new(CMD_FAILURE, bindtype,
@ -119,64 +194,47 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
continue; continue;
} }
xkb_keycode_t keycode; // Identify the key and possibly change binding->type
xkb_keysym_t keysym; uint32_t key_val = 0;
if (bindcode) { error = identify_key(split->items[i], binding->keys->length == 0,
// parse keycode &key_val, &binding->type);
keycode = (int)strtol(split->items[i], NULL, 10); if (error) {
if (!xkb_keycode_is_legal_ext(keycode)) { free_sway_binding(binding);
error = list_free(split);
cmd_results_new(CMD_INVALID, "bindcode", return error;
"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;
}
} }
uint32_t *key = calloc(1, sizeof(uint32_t)); uint32_t *key = calloc(1, sizeof(uint32_t));
if (!key) { if (!key) {
free_sway_binding(binding); free_sway_binding(binding);
free_flat_list(split); free_flat_list(split);
return cmd_results_new(CMD_FAILURE, bindtype, return cmd_results_new(CMD_FAILURE, bindtype,
"Unable to allocate binding"); "Unable to allocate binding key");
} }
*key = key_val;
if (bindcode) {
*key = (uint32_t)keycode;
} else {
*key = (uint32_t)keysym;
}
list_add(binding->keys, key); list_add(binding->keys, key);
} }
free_flat_list(split); free_flat_list(split);
binding->order = binding_order++; 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 // sort ascending
list_qsort(binding->keys, key_qsort_cmp); list_qsort(binding->keys, key_qsort_cmp);
list_t *mode_bindings; list_t *mode_bindings;
if (bindcode) { if (binding->type == BINDING_KEYCODE) {
mode_bindings = config->current_mode->keycode_bindings; mode_bindings = config->current_mode->keycode_bindings;
} else { } else if (binding->type == BINDING_KEYSYM) {
mode_bindings = config->current_mode->keysym_bindings; mode_bindings = config->current_mode->keysym_bindings;
} else {
mode_bindings = config->current_mode->mouse_bindings;
} }
// overwrite the binding if it already exists // 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) { struct cmd_results *cmd_bindcode(int argc, char **argv) {
return cmd_bindsym_or_bindcode(argc, argv, true); 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);
}

@ -56,6 +56,12 @@ static void free_mode(struct sway_mode *mode) {
} }
list_free(mode->keycode_bindings); 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); free(mode);
} }
@ -172,6 +178,7 @@ static void config_defaults(struct sway_config *config) {
strcpy(config->current_mode->name, "default"); strcpy(config->current_mode->name, "default");
if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup;
if (!(config->current_mode->keycode_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); list_add(config->modes, config->current_mode);
config->floating_mod = 0; config->floating_mod = 0;

@ -469,6 +469,83 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor,
seat_pointer_notify_button(seat, time_msec, button, state); 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, void dispatch_cursor_button(struct sway_cursor *cursor,
uint32_t time_msec, uint32_t button, enum wlr_button_state state) { uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
if (cursor->seat->operation != OP_NONE && if (cursor->seat->operation != OP_NONE &&
@ -485,6 +562,31 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
double sx, sy; double sx, sy;
struct sway_container *cont = container_at_coords(cursor->seat, struct sway_container *cont = container_at_coords(cursor->seat,
cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); 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)) { if (surface && wlr_surface_is_layer_surface(surface)) {
struct wlr_layer_surface *layer = struct wlr_layer_surface *layer =
wlr_layer_surface_from_wlr_surface(surface); wlr_layer_surface_from_wlr_surface(surface);

@ -3,11 +3,11 @@
#include <wlr/backend/multi.h> #include <wlr/backend/multi.h>
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#include <wlr/types/wlr_idle.h> #include <wlr/types/wlr_idle.h>
#include "sway/commands.h"
#include "sway/desktop/transaction.h" #include "sway/desktop/transaction.h"
#include "sway/input/seat.h"
#include "sway/input/keyboard.h"
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
#include "sway/commands.h" #include "sway/input/keyboard.h"
#include "sway/input/seat.h"
#include "log.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) { uint32_t modifiers, bool release, bool locked) {
for (int i = 0; i < bindings->length; ++i) { for (int i = 0; i < bindings->length; ++i) {
struct sway_binding *binding = bindings->items[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 || if (modifiers ^ binding->modifiers ||
state->npressed != (size_t)binding->keys->length || state->npressed != (size_t)binding->keys->length ||
locked > binding->locked || release != binding_release ||
release != binding->release) { locked > binding_locked) {
continue; continue;
} }
@ -118,23 +120,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 * Execute a built-in, hardcoded compositor binding. These are triggered from a
* single keysym. * 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) { static void handle_keyboard_key(struct wl_listener *listener, void *data) {
struct sway_keyboard *keyboard = struct sway_keyboard *keyboard =
wl_container_of(listener, keyboard, keyboard_key); 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 = struct wlr_input_device *wlr_device =
keyboard->seat_device->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; 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) // Identify new keycode, raw keysym(s), and translated keysym(s)
xkb_keycode_t keycode = event->keycode + 8; 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 // Execute stored release binding once no longer active
if (keyboard->held_binding && binding_released != keyboard->held_binding && if (keyboard->held_binding && binding_released != keyboard->held_binding &&
event->state == WLR_KEY_RELEASED) { event->state == WLR_KEY_RELEASED) {
keyboard_execute_command(keyboard, keyboard->held_binding); seat_execute_command(seat, keyboard->held_binding);
handled = true; handled = true;
} }
if (binding_released != keyboard->held_binding) { 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); raw_modifiers, false, input_inhibited);
if (binding_pressed) { if (binding_pressed) {
keyboard_execute_command(keyboard, binding_pressed); seat_execute_command(seat, binding_pressed);
handled = true; 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, wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
event->keycode, event->state); event->keycode, event->state);
} }
transaction_commit_dirty();
} }
static void handle_keyboard_modifiers(struct wl_listener *listener, static void handle_keyboard_modifiers(struct wl_listener *listener,

Loading…
Cancel
Save