aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands/gesture.c
blob: d4442cc35c514669098622702bcbbd1ad3adc466 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#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);
	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);
}