#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "gesture.h" #include "log.h" #include "stringop.h" #include "sway/commands.h" void free_gesture_binding(struct sway_gesture_binding *binding) { if (!binding) { return; } free(binding->input); free(binding->command); free(binding); } /** * Returns true if the bindings have the same gesture type, direction, etc */ static bool binding_gesture_equal(struct sway_gesture_binding *binding_a, struct sway_gesture_binding *binding_b) { if (strcmp(binding_a->input, binding_b->input) != 0) { return false; } if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) { return false; } if ((binding_a->flags & BINDING_EXACT) != (binding_b->flags & BINDING_EXACT)) { return false; } return true; } /** * Add gesture binding to config */ static struct cmd_results *gesture_binding_add( struct sway_gesture_binding *binding, const char *gesturecombo, bool warn) { list_t *mode_bindings = config->current_mode->gesture_bindings; // overwrite the binding if it already exists bool overwritten = false; for (int i = 0; i < mode_bindings->length; ++i) { struct sway_gesture_binding *config_binding = mode_bindings->items[i]; if (binding_gesture_equal(binding, config_binding)) { sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", gesturecombo, binding->command, config_binding->command); if (warn) { config_add_swaynag_warning("Overwriting binding" "'%s' to `%s` from `%s`", gesturecombo, binding->command, config_binding->command); } free_gesture_binding(config_binding); mode_bindings->items[i] = binding; overwritten = true; } } if (!overwritten) { list_add(mode_bindings, binding); sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`", gesturecombo, binding->command); } return cmd_results_new(CMD_SUCCESS, NULL); } /** * Remove gesture binding from config */ static struct cmd_results *gesture_binding_remove( struct sway_gesture_binding *binding, const char *gesturecombo) { list_t *mode_bindings = config->current_mode->gesture_bindings; for (int i = 0; i < mode_bindings->length; ++i) { struct sway_gesture_binding *config_binding = mode_bindings->items[i]; if (binding_gesture_equal(binding, config_binding)) { free_gesture_binding(config_binding); free_gesture_binding(binding); list_del(mode_bindings, i); sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture", gesturecombo); return cmd_results_new(CMD_SUCCESS, NULL); } } free_gesture_binding(binding); return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`", gesturecombo); } /** * Parse and execute bindgesture or unbindgesture command. */ static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) { int minargs = 2; char *bindtype = "bindgesture"; if (unbind) { minargs--; bindtype = "unbindgesture"; } struct cmd_results *error = NULL; if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { return error; } struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding)); if (!binding) { return cmd_results_new(CMD_FAILURE, "Unable to allocate binding"); } binding->input = strdup("*"); bool warn = true; // Handle flags while (argc > 0) { if (strcmp("--exact", argv[0]) == 0) { binding->flags |= BINDING_EXACT; } else if (strcmp("--no-warn", argv[0]) == 0) { warn = false; } else if (strncmp("--input-device=", argv[0], strlen("--input-device=")) == 0) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); } else { break; } argv++; argc--; } if (argc < minargs) { free(binding); return cmd_results_new(CMD_FAILURE, "Invalid %s command (expected at least %d " "non-option arguments, got %d)", bindtype, minargs, argc); } char* errmsg = NULL; if ((errmsg = gesture_parse(argv[0], &binding->gesture))) { free(binding); struct cmd_results *final = cmd_results_new(CMD_FAILURE, "Invalid %s command (%s)", bindtype, errmsg); free(errmsg); return final; } if (unbind) { return gesture_binding_remove(binding, argv[0]); } binding->command = join_args(argv + 1, argc - 1); // Make sure that the gesture command is valid switch (binding->gesture.type) { case GESTURE_TYPE_WORKSPACE_SWIPE: if (gesture_workspace_swipe_command_parse(binding->command) == 0) { free(binding); return cmd_results_new(CMD_FAILURE, "Invalid %s command (%s). Either normal or inverted", bindtype, errmsg); } break; default: break; } return gesture_binding_add(binding, argv[0], warn); } struct cmd_results *cmd_bindgesture(int argc, char **argv) { return cmd_bind_or_unbind_gesture(argc, argv, false); } struct cmd_results *cmd_unbindgesture(int argc, char **argv) { return cmd_bind_or_unbind_gesture(argc, argv, true); }