cmd_bind{sym,code}: Implement per-device bindings

bindsym --input-device=<identifier> ...
bindcode --input-device=<identifier> ...
This commit is contained in:
Brian Ashworth 2018-10-18 13:13:40 -04:00
parent 30dbb8eba0
commit 2e637b7368
9 changed files with 77 additions and 36 deletions

View file

@ -43,6 +43,7 @@ enum binding_flags {
struct sway_binding { struct sway_binding {
enum binding_input_type type; enum binding_input_type type;
int order; int order;
char *input;
uint32_t flags; uint32_t flags;
list_t *keys; // sorted in ascending order list_t *keys; // sorted in ascending order
uint32_t modifiers; uint32_t modifiers;

View file

@ -42,8 +42,9 @@ void sway_cursor_destroy(struct sway_cursor *cursor);
struct sway_cursor *sway_cursor_create(struct sway_seat *seat); struct sway_cursor *sway_cursor_create(struct sway_seat *seat);
void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
bool allow_refocusing); bool allow_refocusing);
void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, void dispatch_cursor_button(struct sway_cursor *cursor,
uint32_t button, enum wlr_button_state state); struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
enum wlr_button_state state);
void cursor_set_image(struct sway_cursor *cursor, const char *image, void cursor_set_image(struct sway_cursor *cursor, const char *image,
struct wl_client *client); struct wl_client *client);

View file

@ -63,5 +63,6 @@ struct sway_seat *input_manager_current_seat(struct sway_input_manager *input);
struct input_config *input_device_get_config(struct sway_input_device *device); struct input_config *input_device_get_config(struct sway_input_device *device);
char *input_device_get_identifier(struct wlr_input_device *device);
#endif #endif

View file

@ -26,6 +26,7 @@ void free_sway_binding(struct sway_binding *binding) {
if (binding->keys) { if (binding->keys) {
free_flat_list(binding->keys); free_flat_list(binding->keys);
} }
free(binding->input);
free(binding->command); free(binding->command);
free(binding); free(binding);
} }
@ -37,6 +38,10 @@ 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 (strcmp(binding_a->input, binding_b->input) != 0) {
return false;
}
if (binding_a->type != binding_b->type) { if (binding_a->type != binding_b->type) {
return false; return false;
} }
@ -149,6 +154,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
return cmd_results_new(CMD_FAILURE, bindtype, return cmd_results_new(CMD_FAILURE, bindtype,
"Unable to allocate binding"); "Unable to allocate binding");
} }
binding->input = strdup("*");
binding->keys = create_list(); binding->keys = create_list();
binding->modifiers = 0; binding->modifiers = 0;
binding->flags = 0; binding->flags = 0;
@ -168,6 +174,10 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
binding->flags |= BINDING_BORDER; binding->flags |= BINDING_BORDER;
} else if (strcmp("--exclude-titlebar", argv[0]) == 0) { } else if (strcmp("--exclude-titlebar", argv[0]) == 0) {
exclude_titlebar = true; exclude_titlebar = true;
} else if (strncmp("--input-device=", argv[0],
strlen("--input-device=")) == 0) {
free(binding->input);
binding->input = strdup(argv[0] + strlen("--input-device="));
} else { } else {
break; break;
} }
@ -257,8 +267,8 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
list_add(mode_bindings, binding); list_add(mode_bindings, binding);
} }
wlr_log(WLR_DEBUG, "%s - Bound %s to command %s", wlr_log(WLR_DEBUG, "%s - Bound %s to command `%s` for device '%s'",
bindtype, argv[0], binding->command); bindtype, argv[0], binding->command, binding->input);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

View file

@ -80,6 +80,6 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor,
return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
} }
} }
dispatch_cursor_button(cursor, 0, button, state); dispatch_cursor_button(cursor, NULL, 0, button, state);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

View file

@ -727,19 +727,23 @@ static void state_add_button(struct sway_cursor *cursor, uint32_t button) {
* Return the mouse binding which matches modifier, click location, release, * Return the mouse binding which matches modifier, click location, release,
* and pressed button state, otherwise return null. * and pressed button state, otherwise return null.
*/ */
static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *cursor, static struct sway_binding* get_active_mouse_binding(
list_t *bindings, uint32_t modifiers, bool release, bool on_titlebar, const struct sway_cursor *cursor, list_t *bindings, uint32_t modifiers,
bool on_border, bool on_content) { bool release, bool on_titlebar, bool on_border, bool on_content,
const char *identifier) {
uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) | uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) |
(on_border ? BINDING_BORDER : 0) | (on_border ? BINDING_BORDER : 0) |
(on_content ? BINDING_CONTENTS : 0); (on_content ? BINDING_CONTENTS : 0);
struct sway_binding *current = NULL;
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];
if (modifiers ^ binding->modifiers || if (modifiers ^ binding->modifiers ||
cursor->pressed_button_count != (size_t)binding->keys->length || cursor->pressed_button_count != (size_t)binding->keys->length ||
release != (binding->flags & BINDING_RELEASE) || release != (binding->flags & BINDING_RELEASE) ||
!(click_region & binding->flags)) { !(click_region & binding->flags) ||
(strcmp(binding->input, identifier) != 0 &&
strcmp(binding->input, "*") != 0)) {
continue; continue;
} }
@ -755,13 +759,20 @@ static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *c
continue; continue;
} }
return binding; if (!current || strcmp(current->input, "*") == 0) {
current = binding;
if (strcmp(current->input, identifier) == 0) {
// If a binding is found for the exact input, quit searching
break;
}
}
} }
return NULL; return current;
} }
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) { struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
enum wlr_button_state state) {
if (time_msec == 0) { if (time_msec == 0) {
time_msec = get_current_time_msec(); time_msec = get_current_time_msec();
} }
@ -797,18 +808,21 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
char *device_identifier = device ? input_device_get_identifier(device)
: strdup("*");
struct sway_binding *binding = NULL; struct sway_binding *binding = NULL;
if (state == WLR_BUTTON_PRESSED) { if (state == WLR_BUTTON_PRESSED) {
state_add_button(cursor, button); state_add_button(cursor, button);
binding = get_active_mouse_binding(cursor, binding = get_active_mouse_binding(cursor,
config->current_mode->mouse_bindings, modifiers, false, config->current_mode->mouse_bindings, modifiers, false,
on_titlebar, on_border, on_contents); on_titlebar, on_border, on_contents, device_identifier);
} else { } else {
binding = get_active_mouse_binding(cursor, binding = get_active_mouse_binding(cursor,
config->current_mode->mouse_bindings, modifiers, true, config->current_mode->mouse_bindings, modifiers, true,
on_titlebar, on_border, on_contents); on_titlebar, on_border, on_contents, device_identifier);
state_erase_button(cursor, button); state_erase_button(cursor, button);
} }
free(device_identifier);
if (binding) { if (binding) {
seat_execute_command(seat, binding); seat_execute_command(seat, binding);
return; return;
@ -942,7 +956,7 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, button); struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_pointer_button *event = data; struct wlr_event_pointer_button *event = data;
dispatch_cursor_button(cursor, dispatch_cursor_button(cursor, event->device,
event->time_msec, event->button, event->state); event->time_msec, event->button, event->state);
transaction_commit_dirty(); transaction_commit_dirty();
} }
@ -1128,7 +1142,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_tablet_tool_tip *event = data; struct wlr_event_tablet_tool_tip *event = data;
dispatch_cursor_button(cursor, event->time_msec, dispatch_cursor_button(cursor, event->device, event->time_msec,
BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ?
WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED); WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED);
transaction_commit_dirty(); transaction_commit_dirty();
@ -1143,14 +1157,14 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
switch (event->state) { switch (event->state) {
case WLR_BUTTON_PRESSED: case WLR_BUTTON_PRESSED:
if (cursor->tool_buttons == 0) { if (cursor->tool_buttons == 0) {
dispatch_cursor_button(cursor, dispatch_cursor_button(cursor, event->device,
event->time_msec, BTN_RIGHT, event->state); event->time_msec, BTN_RIGHT, event->state);
} }
cursor->tool_buttons++; cursor->tool_buttons++;
break; break;
case WLR_BUTTON_RELEASED: case WLR_BUTTON_RELEASED:
if (cursor->tool_buttons == 1) { if (cursor->tool_buttons == 1) {
dispatch_cursor_button(cursor, dispatch_cursor_button(cursor, event->device,
event->time_msec, BTN_RIGHT, event->state); event->time_msec, BTN_RIGHT, event->state);
} }
cursor->tool_buttons--; cursor->tool_buttons--;

View file

@ -45,7 +45,7 @@ struct sway_seat *input_manager_get_seat(
return seat_create(input, seat_name); return seat_create(input, seat_name);
} }
static char *get_device_identifier(struct wlr_input_device *device) { char *input_device_get_identifier(struct wlr_input_device *device) {
int vendor = device->vendor; int vendor = device->vendor;
int product = device->product; int product = device->product;
char *name = strdup(device->name); char *name = strdup(device->name);
@ -278,7 +278,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
device->data = input_device; device->data = input_device;
input_device->wlr_device = device; input_device->wlr_device = device;
input_device->identifier = get_device_identifier(device); input_device->identifier = input_device_get_identifier(device);
wl_list_insert(&input->devices, &input_device->link); wl_list_insert(&input->devices, &input_device->link);
wlr_log(WLR_DEBUG, "adding device: '%s'", wlr_log(WLR_DEBUG, "adding device: '%s'",
@ -375,7 +375,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) {
device->data = input_device; device->data = input_device;
input_device->wlr_device = device; input_device->wlr_device = device;
input_device->identifier = get_device_identifier(device); input_device->identifier = input_device_get_identifier(device);
wl_list_insert(&input_manager->devices, &input_device->link); wl_list_insert(&input_manager->devices, &input_device->link);
wlr_log(WLR_DEBUG, "adding virtual keyboard: '%s'", wlr_log(WLR_DEBUG, "adding virtual keyboard: '%s'",

View file

@ -87,7 +87,7 @@ static void update_shortcut_state(struct sway_shortcut_state *state,
*/ */
static void get_active_binding(const struct sway_shortcut_state *state, static void get_active_binding(const struct sway_shortcut_state *state,
list_t *bindings, struct sway_binding **current_binding, list_t *bindings, struct sway_binding **current_binding,
uint32_t modifiers, bool release, bool locked) { uint32_t modifiers, bool release, bool locked, const char *input) {
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_locked = binding->flags & BINDING_LOCKED;
@ -96,7 +96,9 @@ static void get_active_binding(const struct sway_shortcut_state *state,
if (modifiers ^ binding->modifiers || if (modifiers ^ binding->modifiers ||
state->npressed != (size_t)binding->keys->length || state->npressed != (size_t)binding->keys->length ||
release != binding_release || release != binding_release ||
locked > binding_locked) { locked > binding_locked ||
(strcmp(binding->input, input) != 0 &&
strcmp(binding->input, "*") != 0)) {
continue; continue;
} }
@ -112,13 +114,19 @@ static void get_active_binding(const struct sway_shortcut_state *state,
continue; continue;
} }
if (*current_binding && *current_binding != binding) { if (*current_binding && *current_binding != binding &&
strcmp((*current_binding)->input, binding->input) == 0) {
wlr_log(WLR_DEBUG, "encountered duplicate bindings %d and %d", wlr_log(WLR_DEBUG, "encountered duplicate bindings %d and %d",
(*current_binding)->order, binding->order); (*current_binding)->order, binding->order);
} else { } else if (!*current_binding ||
strcmp((*current_binding)->input, "*") == 0) {
*current_binding = binding; *current_binding = binding;
if (strcmp((*current_binding)->input, input) == 0) {
// If a binding is found for the exact input, quit searching
return;
}
} }
return;
} }
} }
@ -202,6 +210,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
struct wlr_seat *wlr_seat = seat->wlr_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;
char *device_identifier = input_device_get_identifier(wlr_device);
wlr_idle_notify_activity(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 = seat->exclusive_client != NULL; bool input_inhibited = seat->exclusive_client != NULL;
@ -242,13 +251,13 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
struct sway_binding *binding_released = NULL; struct sway_binding *binding_released = NULL;
get_active_binding(&keyboard->state_keycodes, get_active_binding(&keyboard->state_keycodes,
config->current_mode->keycode_bindings, &binding_released, config->current_mode->keycode_bindings, &binding_released,
code_modifiers, true, input_inhibited); code_modifiers, true, input_inhibited, device_identifier);
get_active_binding(&keyboard->state_keysyms_translated, get_active_binding(&keyboard->state_keysyms_translated,
config->current_mode->keysym_bindings, &binding_released, config->current_mode->keysym_bindings, &binding_released,
translated_modifiers, true, input_inhibited); translated_modifiers, true, input_inhibited, device_identifier);
get_active_binding(&keyboard->state_keysyms_raw, get_active_binding(&keyboard->state_keysyms_raw,
config->current_mode->keysym_bindings, &binding_released, config->current_mode->keysym_bindings, &binding_released,
raw_modifiers, true, input_inhibited); raw_modifiers, true, input_inhibited, device_identifier);
// 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 &&
@ -268,13 +277,14 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
if (event->state == WLR_KEY_PRESSED) { if (event->state == WLR_KEY_PRESSED) {
get_active_binding(&keyboard->state_keycodes, get_active_binding(&keyboard->state_keycodes,
config->current_mode->keycode_bindings, &binding, config->current_mode->keycode_bindings, &binding,
code_modifiers, false, input_inhibited); code_modifiers, false, input_inhibited, device_identifier);
get_active_binding(&keyboard->state_keysyms_translated, get_active_binding(&keyboard->state_keysyms_translated,
config->current_mode->keysym_bindings, &binding, config->current_mode->keysym_bindings, &binding,
translated_modifiers, false, input_inhibited); translated_modifiers, false, input_inhibited,
device_identifier);
get_active_binding(&keyboard->state_keysyms_raw, get_active_binding(&keyboard->state_keysyms_raw,
config->current_mode->keysym_bindings, &binding, config->current_mode->keysym_bindings, &binding,
raw_modifiers, false, input_inhibited); raw_modifiers, false, input_inhibited, device_identifier);
if (binding) { if (binding) {
seat_execute_command(seat, binding); seat_execute_command(seat, binding);
@ -315,6 +325,8 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
} }
transaction_commit_dirty(); transaction_commit_dirty();
free(device_identifier);
} }
static int handle_keyboard_repeat(void *data) { static int handle_keyboard_repeat(void *data) {

View file

@ -257,20 +257,22 @@ runtime.
for\_window <criteria> move container to output <output> for\_window <criteria> move container to output <output>
*bindsym* [--release|--locked] <key combo> <command> *bindsym* [--release|--locked] [--input-device=<device>] <key combo> <command>
Binds _key combo_ to execute the sway command _command_ when pressed. You Binds _key combo_ to execute the sway command _command_ when pressed. You
may use XKB key names here (*xev*(1) is a good tool for discovering these). may use XKB key names here (*xev*(1) is a good tool for discovering these).
With the flag _--release_, the command is executed when the key combo is With the flag _--release_, the command is executed when the key combo is
released. Unless the flag _--locked_ is set, the command will not be run released. Unless the flag _--locked_ is set, the command will not be run
when a screen locking program is active. when a screen locking program is active. If _input-device_ is given, the
binding will only be executed for that input device and will be executed
instead of any binding that is generic to all devices.
Example: Example:
# Execute firefox when alt, shift, and f are pressed together # Execute firefox when alt, shift, and f are pressed together
bindsym Mod1+Shift+f exec firefox bindsym Mod1+Shift+f exec firefox
*bindcode* [--release|--locked] <code> <command> is also available for *bindcode* [--release|--locked] [--input-device=<device>] <code> <command>
binding with key codes instead of key names. is also available for binding with key codes instead of key names.
*client.<class>* <border> <background> <text> <indicator> <child\_border> *client.<class>* <border> <background> <text> <indicator> <child\_border>
Configures the color of window borders and title bars. All 5 colors are Configures the color of window borders and title bars. All 5 colors are