commit 710f69ddfe3b6d1b02eb607f41bc773c05b7e77c
parent 383a2025bf01bef30433aef6715c0b4be9e362b9
Author: Bastien Dejean <nihilhill@gmail.com>
Date: Sat, 22 Jun 2013 19:06:01 +0200
Handle *nested* sequences
Diffstat:
5 files changed, 154 insertions(+), 74 deletions(-)
diff --git a/README.md b/README.md
@@ -48,7 +48,7 @@ If `:` is added at the beginning of the keysym, the captured event will be repla
Mouse hotkeys can be defined by using one of the following special keysym names: `button1`, `button2`, `button3`, …, `button24`.
-The hotkey and the command may contain a sequence of the form `{STRING_1,…,STRING_N}`.
+The hotkey and the command may contain sequences of the form `{STRING_1,…,STRING_N}`.
In addition, the sequences can contain ranges of the form `A-Z` where *A* and *Z* are alphanumeric characters.
@@ -98,6 +98,8 @@ If no configuration file is specified via the `-c` option, the following is used
super + o ; {e,w,m}
{gvim,firefox,thunderbird}
+ super + alt + control + {h,j,k,l} ; {0-9}
+ bspc fence_ratio {left,down,up,right} 0.{0-9}
## Installation
diff --git a/parse.c b/parse.c
@@ -2415,36 +2415,17 @@ void process_hotkey(char *hotkey_string, char *command_string)
{
char hotkey[MAXLEN] = {0};
char command[MAXLEN] = {0};
- char hotkey_sequence[MAXLEN] = {0};
- char hotkey_prefix[MAXLEN] = {0};
- char hotkey_suffix[MAXLEN] = {0};
- char command_sequence[MAXLEN] = {0};
- char command_prefix[MAXLEN] = {0};
- char command_suffix[MAXLEN] = {0};
- char hk_item[MAXLEN] = {0};
- char cm_item[MAXLEN] = {0};
- bool hk_loop = extract_sequence(hotkey_string, hotkey_prefix, hotkey_sequence, hotkey_suffix);
- bool cm_loop = extract_sequence(command_string, command_prefix, command_sequence, command_suffix);
- char unfolded_hotkey[MAXLEN], unfolded_command[MAXLEN];
- char *hk_ptr, *cm_ptr;
- char hk_a = 1, hk_z = 0, cm_a = 1, cm_z = 0;
- hk_ptr = gettok(hk_item, hotkey_sequence, SEQ_SEP);
- cm_ptr = gettok(cm_item, command_sequence, SEQ_SEP);
+ chunk_t *hk_chunks = extract_chunks(hotkey_string);
+ chunk_t *cm_chunks = extract_chunks(command_string);
+ if (hk_chunks == NULL)
+ strncpy(hotkey, hotkey_string, sizeof(hotkey));
+ if (cm_chunks == NULL)
+ strncpy(command, command_string, sizeof(command));
+ render_next(hk_chunks, hotkey);
+ render_next(cm_chunks, command);
- while ((!hk_loop || hk_item[0] != '\0' || hk_a <= hk_z) && (!cm_loop || cm_item[0] != '\0' || cm_a <= cm_z)) {
-#define PREPROC(itm, ra, rz, prefix, suffix, unf) \
- if (ra > rz && strlen(itm) == 3 && sscanf(itm, "%c-%c", &ra, &rz) == 2 && ra >= rz) \
- ra = 1, rz = 0; \
- if (ra <= rz) \
- snprintf(unf, sizeof(unf), "%s%c%s", prefix, ra, suffix); \
- else \
- snprintf(unf, sizeof(unf), "%s%s%s", prefix, itm, suffix);
- PREPROC(hk_item, hk_a, hk_z, hotkey_prefix, hotkey_suffix, unfolded_hotkey)
- PREPROC(cm_item, cm_a, cm_z, command_prefix, command_suffix, unfolded_command)
-#undef PREPROC
+ while ((hk_chunks == NULL || hotkey[0] != '\0') && (cm_chunks == NULL || command[0] != '\0')) {
- strncpy(hotkey, hk_loop ? unfolded_hotkey : hotkey_string, sizeof(hotkey));
- strncpy(command, cm_loop ? unfolded_command : command_string, sizeof(command));
PRINTF("%s: %s\n", hotkey, command);
chain_t *chain = make_chain();
if (parse_chain(hotkey, chain)) {
@@ -2454,21 +2435,19 @@ void process_hotkey(char *hotkey_string, char *command_string)
free(chain);
}
- if (!hk_loop && !cm_loop)
+ if (hk_chunks == NULL && cm_chunks == NULL)
break;
-#define POSTPROC(itm, ra, rz, ptr) \
- if (ra >= rz) \
- ptr = gettok(itm, ptr, SEQ_SEP), ra = 1, rz = 0; \
- else \
- ra++;
- POSTPROC(hk_item, hk_a, hk_z, hk_ptr)
- POSTPROC(cm_item, cm_a, cm_z, cm_ptr)
-#undef POSTPROC
+ render_next(hk_chunks, hotkey);
+ render_next(cm_chunks, command);
}
+ if (hk_chunks != NULL)
+ destroy_chunks(hk_chunks);
+ if (cm_chunks != NULL)
+ destroy_chunks(cm_chunks);
}
-char *gettok(char *dst, char *src, char c)
+char *get_token(char *dst, char *src, char *sep)
{
size_t len = strlen(src);
unsigned int i = 0, j = 0;
@@ -2480,10 +2459,15 @@ char *gettok(char *dst, char *src, char c)
inhibit = false;
} else if (src[i] == MAGIC_INHIBIT) {
inhibit = true;
- if (src[i+1] != MAGIC_INHIBIT && src[i+1] != c)
+ if (src[i+1] != MAGIC_INHIBIT && strchr(sep, src[i+1]) == NULL)
dst[j++] = src[i];
- } else if (src[i] == c) {
- found = true;
+ } else if (strchr(sep, src[i]) != NULL) {
+ if (j > 0)
+ found = true;
+ do {
+ i++;
+ } while (i < len && strchr(sep, src[i]) != NULL);
+ i--;
} else {
dst[j++] = src[i];
}
@@ -2493,70 +2477,158 @@ char *gettok(char *dst, char *src, char c)
return src + i;
}
-bool extract_sequence(char *s, char *prefix, char *sequence, char *suffix)
+void render_next(chunk_t *chunks, char *dest)
+{
+ if (chunks == NULL)
+ return;
+ int i = 0;
+ bool incr = false;
+ for (chunk_t *c = chunks; c != NULL; c = c->next) {
+ if (c->sequence) {
+ if (!incr) {
+ if (c->range_cur < c->range_max) {
+ c->range_cur++;
+ incr = true;
+ } else {
+ c->range_cur = 1, c->range_max = 0;
+ }
+ }
+ if (c->advance == NULL) {
+ incr = true;
+ c->advance = get_token(c->item, c->text, SEQ_SEP);
+ } else if (!incr && c->range_cur > c->range_max) {
+ if (c->advance[0] == '\0') {
+ c->advance = get_token(c->item, c->text, SEQ_SEP);
+ } else {
+ c->advance = get_token(c->item, c->advance, SEQ_SEP);
+ incr = true;
+ }
+ }
+ if (c->range_cur > c->range_max && strlen(c->item) == 3)
+ sscanf(c->item, "%c-%c", &c->range_cur, &c->range_max);
+ if (c->range_cur <= c->range_max) {
+ dest[i++] = c->range_cur;
+ } else {
+ strcpy(dest + i, c->item);
+ i += strlen(c->item);
+ }
+ } else {
+ strcpy(dest + i, c->text);
+ i += strlen(c->text);
+ }
+ }
+ dest[i] = '\0';
+ if (!incr)
+ dest[0] = '\0';
+}
+
+chunk_t *extract_chunks(char *s)
{
- char *cur = prefix;
size_t len = strlen(s);
unsigned int i = 0, j = 0;
bool inhibit = false;
+ int num_seq = 0;
+ chunk_t *c = make_chunk();
+ chunk_t *head = c;
while (i < len) {
if (inhibit) {
- cur[j++] = s[i];
+ c->text[j++] = s[i];
inhibit = false;
} else if (s[i] == MAGIC_INHIBIT) {
inhibit = true;
- if ((s[i+1] != MAGIC_INHIBIT || cur == sequence)
+ if ((s[i+1] != MAGIC_INHIBIT || c->sequence)
&& s[i+1] != SEQ_BEGIN
&& s[i+1] != SEQ_END)
- cur[j++] = s[i];
+ c->text[j++] = s[i];
} else if (s[i] == SEQ_BEGIN) {
- cur[j] = '\0';
- j = 0;
- cur = sequence;
+ if (j > 0) {
+ c->text[j] = '\0';
+ j = 0;
+ chunk_t *next = make_chunk();
+ c->next = next;
+ c = next;
+ }
+ c->sequence = true;
} else if (s[i] == SEQ_END) {
- cur[j] = '\0';
- j = 0;
- cur = suffix;
+ if (c->sequence)
+ num_seq++;
+ if (j > 0) {
+ c->text[j] = '\0';
+ j = 0;
+ chunk_t *next = make_chunk();
+ c->next = next;
+ c = next;
+ }
+ c->sequence = false;
} else {
- cur[j++] = s[i];
+ c->text[j++] = s[i];
}
i++;
- /* printf("i %u j %u\n", i, j); */
}
- cur[j] = '\0';
- return (cur == suffix);
+ c->text[j] = '\0';
+ if (num_seq == 0) {
+ destroy_chunks(head);
+ return NULL;
+ } else {
+ return head;
+ }
+}
+
+chunk_t *make_chunk(void)
+{
+ chunk_t *c = malloc(sizeof(chunk_t));
+ c->sequence = false;
+ c->advance = NULL;
+ c->next = NULL;
+ c->range_cur = 1;
+ c->range_max = 0;
+ return c;
+}
+
+void destroy_chunks(chunk_t *chunk)
+{
+ chunk_t *c = chunk;
+ while (c != NULL) {
+ chunk_t *next = c->next;
+ free(c);
+ c = next;
+ }
}
bool parse_chain(char *string, chain_t *chain)
{
+ char chord[MAXLEN] = {0};
+ char name[MAXLEN] = {0};
xcb_keysym_t keysym = XCB_NO_SYMBOL;
xcb_button_t button = XCB_NONE;
uint16_t modfield = 0;
uint8_t event_type = XCB_KEY_PRESS;
bool replay_event = false;
- char *outerptr;
- char *innerptr;
- for (char *s = strtok_r(string, LNK_SEP, &outerptr); s != NULL; s = strtok_r(NULL, LNK_SEP, &outerptr)) {
- for (char *name = strtok_r(s, SYM_SEP, &innerptr); name != NULL; name = strtok_r(NULL, SYM_SEP, &innerptr)) {
+ char *outer_advance;
+ char *inner_advance;
+ for (outer_advance = get_token(chord, string, LNK_SEP); chord[0] != '\0'; outer_advance = get_token(chord, outer_advance, LNK_SEP)) {
+ for (inner_advance = get_token(name, chord, SYM_SEP); name[0] != '\0'; inner_advance = get_token(name, inner_advance, SYM_SEP)) {
+ int offset = 0;
if (name[0] == RELEASE_PREFIX) {
event_type = XCB_KEY_RELEASE;
- name++;
+ offset++;
} else if (name[0] == MOTION_PREFIX) {
event_type = XCB_MOTION_NOTIFY;
- name++;
+ offset++;
} else if (name[0] == REPLAY_PREFIX) {
replay_event = true;
- name++;
+ offset++;
}
- if (!parse_modifier(name, &modfield) && !parse_keysym(name, &keysym) && !parse_button(name, &button)) {
- warn("Unknown name: '%s'.\n", name);
+ char *nm = name + offset;
+ if (!parse_modifier(nm, &modfield) && !parse_keysym(nm, &keysym) && !parse_button(nm, &button)) {
+ warn("Unknown name: '%s'.\n", nm);
return false;
}
}
if (button != XCB_NONE)
event_type = key_to_button(event_type);
- chord_t *chord = make_chord(keysym, button, modfield, event_type, replay_event);
- add_chord(chain, chord);
+ chord_t *c = make_chord(keysym, button, modfield, event_type, replay_event);
+ add_chord(chain, c);
keysym = XCB_NO_SYMBOL;
button = XCB_NONE;
modfield = 0;
diff --git a/parse.h b/parse.h
@@ -13,13 +13,16 @@
#define SYM_SEP "+ "
#define SEQ_BEGIN '{'
#define SEQ_END '}'
-#define SEQ_SEP ','
+#define SEQ_SEP ","
typedef struct chunk_t chunk_t;
struct chunk_t {
char text[MAXLEN];
+ char item[MAXLEN];
char *advance;
bool sequence;
+ char range_cur;
+ char range_max;
chunk_t *next;
};
@@ -29,8 +32,11 @@ xcb_keysym_t Alt_L, Alt_R, Super_L, Super_R, Hyper_L, Hyper_R,
void load_config(char *);
void parse_event(xcb_generic_event_t *, uint8_t, xcb_keysym_t *, xcb_button_t *, uint16_t *);
void process_hotkey(char *, char *);
-char *gettok(char *, char *, char);
-bool extract_sequence(char *, char *, char *, char *);
+void render_next(chunk_t *, char *);
+chunk_t *extract_chunks(char *);
+char *get_token(char *, char *, char *);
+chunk_t *make_chunk(void);
+void destroy_chunks(chunk_t *);
bool parse_chain(char *, chain_t *);
bool parse_keysym(char *, xcb_keysym_t *);
bool parse_button(char *, xcb_button_t *);
diff --git a/sxhkd.1 b/sxhkd.1
@@ -75,7 +75,7 @@ is added at the beginning of the keysym, the captured event will be replayed for
Mouse hotkeys can be defined by using one of the following special keysym names:
.BR button1 ", " button2 ", " button3 ", ..., " button24 .
.PP
-The hotkey and the command may contain a sequence of the form
+The hotkey and the command may contain sequences of the form
.IR {STRING_1,...,STRING_N} .
.PP
In addition, the sequences can contain ranges of the form
diff --git a/sxhkd.c b/sxhkd.c
@@ -88,14 +88,14 @@ void key_button_event(xcb_generic_event_t *evt, uint8_t event_type)
if (replay_event)
xcb_allow_events(dpy, XCB_ALLOW_REPLAY_POINTER, XCB_CURRENT_TIME);
else
- xcb_allow_events(dpy, XCB_ALLOW_ASYNC_POINTER, XCB_CURRENT_TIME);
+ xcb_allow_events(dpy, XCB_ALLOW_SYNC_POINTER, XCB_CURRENT_TIME);
break;
case XCB_KEY_PRESS:
case XCB_KEY_RELEASE:
if (replay_event)
xcb_allow_events(dpy, XCB_ALLOW_REPLAY_KEYBOARD, XCB_CURRENT_TIME);
else
- xcb_allow_events(dpy, XCB_ALLOW_ASYNC_KEYBOARD, XCB_CURRENT_TIME);
+ xcb_allow_events(dpy, XCB_ALLOW_SYNC_KEYBOARD, XCB_CURRENT_TIME);
break;
}
xcb_flush(dpy);