diff --git a/CHANGELOG.md b/CHANGELOG.md index e89d9d30..b8b7aba5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,8 @@ * **tweak.render-timer** option to `footrc`. * Modifier support in mouse bindings (https://codeberg.org/dnkl/foot/issues/77). +* Click count support in mouse bindings, i.e double- and triple-click + (https://codeberg.org/dnkl/foot/issues/78). ### Deprecated diff --git a/config.c b/config.c index 4aae6c32..8e51b608 100644 --- a/config.c +++ b/config.c @@ -97,45 +97,43 @@ static const char *const search_binding_action_map[] = { static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT, "search binding action map size mismatch"); -#define LOG_AND_NOTIFY_ERR(fmt, ...) \ - LOG_ERR(fmt, ## __VA_ARGS__); \ - { \ - char *text = xasprintf(fmt, ## __VA_ARGS__); \ - struct user_notification notif = { \ - .kind = USER_NOTIFICATION_ERROR, \ - .text = text, \ - }; \ - tll_push_back(conf->notifications, notif); \ - } +#define LOG_AND_NOTIFY_ERR(fmt, ...) \ + do { \ + LOG_ERR(fmt, ## __VA_ARGS__); \ + char *text = xasprintf(fmt, ## __VA_ARGS__); \ + struct user_notification notif = { \ + .kind = USER_NOTIFICATION_ERROR, \ + .text = text, \ + }; \ + tll_push_back(conf->notifications, notif); \ + } while (0) #define LOG_AND_NOTIFY_WARN(fmt, ...) \ - LOG_WARN(fmt, ## __VA_ARGS__); \ - { \ + do { \ + LOG_WARN(fmt, ## __VA_ARGS__); \ char *text = xasprintf(fmt, ## __VA_ARGS__); \ struct user_notification notif = { \ .kind = USER_NOTIFICATION_WARNING, \ .text = text, \ }; \ tll_push_back(conf->notifications, notif); \ - } + } while (0) #define LOG_AND_NOTIFY_ERRNO(fmt, ...) \ - { \ + do { \ int _errno = errno; \ LOG_ERRNO(fmt, ## __VA_ARGS__); \ - { \ - int len = snprintf(NULL, 0, fmt, ## __VA_ARGS__); \ - int errno_len = snprintf(NULL, 0, ": %s", strerror(_errno)); \ - char *text = xmalloc(len + errno_len + 1); \ - snprintf(text, len + errno_len + 1, fmt, ## __VA_ARGS__); \ - snprintf(&text[len], errno_len + 1, ": %s", strerror(_errno)); \ - struct user_notification notif = { \ - .kind = USER_NOTIFICATION_ERROR, \ - .text = text, \ - }; \ - tll_push_back(conf->notifications, notif); \ - } \ - } + int len = snprintf(NULL, 0, fmt, ## __VA_ARGS__); \ + int errno_len = snprintf(NULL, 0, ": %s", strerror(_errno)); \ + char *text = xmalloc(len + errno_len + 1); \ + snprintf(text, len + errno_len + 1, fmt, ## __VA_ARGS__); \ + snprintf(&text[len], errno_len + 1, ": %s", strerror(_errno)); \ + struct user_notification notif = { \ + .kind = USER_NOTIFICATION_ERROR, \ + .text = text, \ + }; \ + tll_push_back(conf->notifications, notif); \ + } while(0) static char * get_shell(void) @@ -1009,17 +1007,41 @@ parse_mouse_combos(struct config *conf, const char *combos, key_combo_list_t *ke combo = strtok_r(NULL, " ", &tok_ctx)) { struct config_key_modifiers modifiers = {}; - const char *key = strrchr(combo, '+'); + char *key = strrchr(combo, '+'); if (key == NULL) { /* No modifiers */ key = combo; } else { + *key = '\0'; if (!parse_modifiers(conf, combo, key - combo, &modifiers, path, lineno)) goto err; key++; /* Skip past the '+' */ } + size_t count = 0; + { + char *_count = strrchr(key, '-'); + if (_count != NULL) { + *_count = '\0'; + _count++; + + errno = 0; + char *end; + unsigned long value = strtoul(_count, &end, 10); + if (_count[0] == '\0' || *end != '\0' || errno != 0) { + if (errno != 0) + LOG_AND_NOTIFY_ERRNO( + "%s:%d: %s: invalid click count", path, lineno, _count); + else + LOG_AND_NOTIFY_ERR( + "%s:%d: %s: invalid click count", path, lineno, _count); + goto err; + } + count = value; + } + } + static const struct { const char *name; int code; @@ -1052,7 +1074,7 @@ parse_mouse_combos(struct config *conf, const char *combos, key_combo_list_t *ke .modifiers = modifiers, .m = { .button = button, - .count = 1, + .count = count, }, }; tll_push_back(*key_combos, new); diff --git a/doc/footrc.5.scd b/doc/footrc.5.scd index 12fb2ded..5f39f72d 100644 --- a/doc/footrc.5.scd +++ b/doc/footrc.5.scd @@ -364,9 +364,18 @@ This section lets you override the default mouse bindings. The general format is _action=combo1...comboN_. That is, each action may have one or more key combinations, space separated. Each -combination is on the form _mod1+mod2+BTN\__. The names of the -modifiers and the key *must* be valid XKB key names. You can find the -button names using *libinput debug-events*. +combination is on the form _mod1+mod2+BTN\__[-COUNT]. The names +of the modifiers and the key *must* be valid XKB key names. You can +find the button names using *libinput debug-events*. + +The trailing *COUNT* is optional and specifies the click count +required to trigger the binding. The default is *COUNT* is omitted is +_1_. + +Example: *primary-paste=Control+BTN_LEFT-2* + +Trigger _primary-paste_ when *ctrl* is pressed, and the left mouse +button is double clicked. A button can only be mapped to *one* action. Lets say you want to bind *BTN\_MIDDLE* to *fullscreen*. Since *BTN\_MIDDLE* is the default