Invoke mouse bindings

The mouse binding logic is inspired/copied from the
keyboard binding logic; we store a sorted list of the
currently pressed buttons, and trigger a binding when
the currently pressed (or just recently pressed, in
the case of a release binding) buttons, as well as
modifiers/container region, match those of a given
binding.

As the code to execute a binding is not very keyboard
specific, keyboard_execute_command is renamed to
seat_execute_command and moved to where the other
binding handling functions are. The call to
transaction_commit_dirty has been lifted out.
This commit is contained in:
frsfnrrg 2018-07-23 21:38:29 -04:00
parent 754372c3de
commit 94dd8823a0
5 changed files with 140 additions and 28 deletions

View file

@ -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);