mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Simplify next key matching and fix problems with meta and Unicode keys.
This commit is contained in:
		
							
								
								
									
										175
									
								
								tty-keys.c
									
									
									
									
									
								
							
							
						
						
									
										175
									
								
								tty-keys.c
									
									
									
									
									
								
							| @@ -33,14 +33,17 @@ | |||||||
|  * into a ternary tree. |  * into a ternary tree. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| void		tty_keys_add1(struct tty_key **, const char *, key_code); | static void	tty_keys_add1(struct tty_key **, const char *, key_code); | ||||||
| void		tty_keys_add(struct tty *, const char *, key_code); | static void	tty_keys_add(struct tty *, const char *, key_code); | ||||||
| void		tty_keys_free1(struct tty_key *); | static void	tty_keys_free1(struct tty_key *); | ||||||
| struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t, | static struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t, | ||||||
| 		    size_t *); | 		    size_t *); | ||||||
| struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); | static struct tty_key *tty_keys_find(struct tty *, const char *, size_t, | ||||||
| void		tty_keys_callback(int, short, void *); | 		    size_t *); | ||||||
| int		tty_keys_mouse(struct tty *, const char *, size_t, size_t *); | static int	tty_keys_next1(struct tty *, const char *, size_t, key_code *, | ||||||
|  | 		    size_t *); | ||||||
|  | static void	tty_keys_callback(int, short, void *); | ||||||
|  | static int	tty_keys_mouse(struct tty *, const char *, size_t, size_t *); | ||||||
|  |  | ||||||
| /* Default raw keys. */ | /* Default raw keys. */ | ||||||
| struct tty_default_key_raw { | struct tty_default_key_raw { | ||||||
| @@ -316,7 +319,7 @@ const struct tty_default_key_code tty_default_code_keys[] = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Add key to tree. */ | /* Add key to tree. */ | ||||||
| void | static void | ||||||
| tty_keys_add(struct tty *tty, const char *s, key_code key) | tty_keys_add(struct tty *tty, const char *s, key_code key) | ||||||
| { | { | ||||||
| 	struct tty_key	*tk; | 	struct tty_key	*tk; | ||||||
| @@ -334,7 +337,7 @@ tty_keys_add(struct tty *tty, const char *s, key_code key) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Add next node to the tree. */ | /* Add next node to the tree. */ | ||||||
| void | static void | ||||||
| tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) | tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) | ||||||
| { | { | ||||||
| 	struct tty_key	*tk; | 	struct tty_key	*tk; | ||||||
| @@ -409,7 +412,7 @@ tty_keys_free(struct tty *tty) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Free a single key. */ | /* Free a single key. */ | ||||||
| void | static void | ||||||
| tty_keys_free1(struct tty_key *tk) | tty_keys_free1(struct tty_key *tk) | ||||||
| { | { | ||||||
| 	if (tk->next != NULL) | 	if (tk->next != NULL) | ||||||
| @@ -422,7 +425,7 @@ tty_keys_free1(struct tty_key *tk) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Lookup a key in the tree. */ | /* Lookup a key in the tree. */ | ||||||
| struct tty_key * | static struct tty_key * | ||||||
| tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size) | tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size) | ||||||
| { | { | ||||||
| 	*size = 0; | 	*size = 0; | ||||||
| @@ -430,7 +433,7 @@ tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Find the next node. */ | /* Find the next node. */ | ||||||
| struct tty_key * | static struct tty_key * | ||||||
| tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) | tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) | ||||||
| { | { | ||||||
| 	/* If the node is NULL, this is the end of the tree. No match. */ | 	/* If the node is NULL, this is the end of the tree. No match. */ | ||||||
| @@ -460,6 +463,56 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) | |||||||
| 	return (tty_keys_find1(tk, buf, len, size)); | 	return (tty_keys_find1(tk, buf, len, size)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Look up part of the next key. */ | ||||||
|  | static int | ||||||
|  | tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, | ||||||
|  |     size_t *size) | ||||||
|  | { | ||||||
|  | 	struct tty_key		*tk, *tk1; | ||||||
|  | 	struct utf8_data	 ud; | ||||||
|  | 	enum utf8_state		 more; | ||||||
|  | 	u_int			 i; | ||||||
|  | 	wchar_t			 wc; | ||||||
|  |  | ||||||
|  | 	log_debug("next key is %zu (%.*s)", len, (int)len, buf); | ||||||
|  |  | ||||||
|  | 	/* Empty buffer is a partial key. */ | ||||||
|  | 	if (len == 0) | ||||||
|  | 		return (1); | ||||||
|  |  | ||||||
|  | 	/* Is this a known key? */ | ||||||
|  | 	tk = tty_keys_find(tty, buf, len, size); | ||||||
|  | 	if (tk != NULL) { | ||||||
|  | 		tk1 = tk; | ||||||
|  | 		do | ||||||
|  | 			log_debug("keys in list: %#llx", tk->key); | ||||||
|  | 		while ((tk1 = tk1->next) != NULL); | ||||||
|  | 		*key = tk->key; | ||||||
|  | 		return (tk->next != NULL); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Is this valid UTF-8? */ | ||||||
|  | 	more = utf8_open(&ud, (u_char)*buf); | ||||||
|  | 	if (more == UTF8_MORE) { | ||||||
|  | 		*size = ud.size; | ||||||
|  | 		if (len < ud.size) | ||||||
|  | 			return (1); | ||||||
|  | 		for (i = 1; i < ud.size; i++) | ||||||
|  | 			more = utf8_append(&ud, (u_char)buf[i]); | ||||||
|  | 		if (more != UTF8_DONE) | ||||||
|  | 			return (0); | ||||||
|  |  | ||||||
|  | 		if (utf8_combine(&ud, &wc) != UTF8_DONE) | ||||||
|  | 			return (0); | ||||||
|  | 		*key = wc; | ||||||
|  |  | ||||||
|  | 		log_debug("UTF-8 key %.*s %#llx", (int)ud.size, buf, *key); | ||||||
|  | 		return (0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return (-1); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Process at least one key in the buffer and invoke tty->key_callback. Return |  * Process at least one key in the buffer and invoke tty->key_callback. Return | ||||||
|  * 0 if there are no further keys, or 1 if there could be more in the buffer. |  * 0 if there are no further keys, or 1 if there could be more in the buffer. | ||||||
| @@ -467,17 +520,12 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) | |||||||
| key_code | key_code | ||||||
| tty_keys_next(struct tty *tty) | tty_keys_next(struct tty *tty) | ||||||
| { | { | ||||||
| 	struct tty_key		*tk; |  | ||||||
| 	struct timeval	 tv; | 	struct timeval	 tv; | ||||||
| 	const char	*buf; | 	const char	*buf; | ||||||
| 	size_t		 len, size; | 	size_t		 len, size; | ||||||
| 	cc_t		 bspace; | 	cc_t		 bspace; | ||||||
| 	int		 delay, expired = 0; | 	int		 delay, expired = 0; | ||||||
| 	key_code	 key; | 	key_code	 key; | ||||||
| 	struct utf8_data	 ud; |  | ||||||
| 	enum utf8_state		 more; |  | ||||||
| 	u_int			 i; |  | ||||||
| 	wchar_t			 wc; |  | ||||||
|  |  | ||||||
| 	/* Get key buffer. */ | 	/* Get key buffer. */ | ||||||
| 	buf = EVBUFFER_DATA(tty->event->input); | 	buf = EVBUFFER_DATA(tty->event->input); | ||||||
| @@ -485,7 +533,7 @@ tty_keys_next(struct tty *tty) | |||||||
|  |  | ||||||
| 	if (len == 0) | 	if (len == 0) | ||||||
| 		return (0); | 		return (0); | ||||||
| 	log_debug("keys are %zu (%.*s)", len, (int) len, buf); | 	log_debug("keys are %zu (%.*s)", len, (int)len, buf); | ||||||
|  |  | ||||||
| 	/* Is this a mouse key press? */ | 	/* Is this a mouse key press? */ | ||||||
| 	switch (tty_keys_mouse(tty, buf, len, &size)) { | 	switch (tty_keys_mouse(tty, buf, len, &size)) { | ||||||
| @@ -501,68 +549,65 @@ tty_keys_next(struct tty *tty) | |||||||
| 		goto partial_key; | 		goto partial_key; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* Look for matching key string and return if found. */ | first_key: | ||||||
| 	tk = tty_keys_find(tty, buf, len, &size); | 	/* If escape is at the start, try without it. */ | ||||||
| 	if (tk != NULL) { | 	if (*buf == '\033') { | ||||||
| 		if (tk->next != NULL) | 		switch (tty_keys_next1 (tty, buf + 1, len - 1, &key, &size)) { | ||||||
| 			goto partial_key; | 		case 0:		/* found */ | ||||||
| 		key = tk->key; | 			if (key != KEYC_UNKNOWN) | ||||||
|  | 				key |= KEYC_ESCAPE; | ||||||
|  | 			size++; /* include escape */ | ||||||
| 			goto complete_key; | 			goto complete_key; | ||||||
|  | 		case -1:	/* not found */ | ||||||
|  | 			break; | ||||||
|  | 		case 1: | ||||||
|  | 			if (expired) | ||||||
|  | 				goto complete_key; | ||||||
|  | 			goto partial_key; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* Try to parse a key with an xterm-style modifier. */ | 	/* Try with the escape. */ | ||||||
|  | 	switch (tty_keys_next1 (tty, buf, len, &key, &size)) { | ||||||
|  | 	case 0:		/* found */ | ||||||
|  | 		goto complete_key; | ||||||
|  | 	case -1:	/* not found */ | ||||||
|  | 		break; | ||||||
|  | 	case 1: | ||||||
|  | 		if (expired) | ||||||
|  | 			goto complete_key; | ||||||
|  | 		goto partial_key; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Is this an an xterm(1) key? */ | ||||||
| 	switch (xterm_keys_find(buf, len, &size, &key)) { | 	switch (xterm_keys_find(buf, len, &size, &key)) { | ||||||
| 	case 0:		/* found */ | 	case 0:		/* found */ | ||||||
| 		goto complete_key; | 		goto complete_key; | ||||||
| 	case -1:	/* not found */ | 	case -1:	/* not found */ | ||||||
| 		break; | 		break; | ||||||
| 	case 1: | 	case 1: | ||||||
|  | 		if (expired) | ||||||
|  | 			break; | ||||||
| 		goto partial_key; | 		goto partial_key; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| first_key: | 	/* | ||||||
| 	/* Is this a meta key? */ | 	 * If this starts with escape and is at least two keys, it must be | ||||||
| 	if (len >= 2 && buf[0] == '\033') { | 	 * complete even if the timer has not expired, because otherwise | ||||||
| 		if (buf[1] != '\033') { | 	 * tty_keys_next1 would have found a partial key. If just an escape | ||||||
| 			key = buf[1] | KEYC_ESCAPE; | 	 * alone, it needs to wait for the timer first. | ||||||
|  | 	 */ | ||||||
|  | 	if (*buf == '\033') { | ||||||
|  | 		if (len >= 2) { | ||||||
|  | 			key = (u_char)buf[1] | KEYC_ESCAPE; | ||||||
| 			size = 2; | 			size = 2; | ||||||
| 			goto complete_key; | 			goto complete_key; | ||||||
| 		} | 		} | ||||||
|  | 		if (!expired) | ||||||
| 		tk = tty_keys_find(tty, buf + 1, len - 1, &size); |  | ||||||
| 		if (tk != NULL && (!expired || tk->next == NULL)) { |  | ||||||
| 			size++;	/* include escape */ |  | ||||||
| 			if (tk->next != NULL) |  | ||||||
| 				goto partial_key; |  | ||||||
| 			key = tk->key; |  | ||||||
| 			if (key != KEYC_UNKNOWN) |  | ||||||
| 				key |= KEYC_ESCAPE; |  | ||||||
| 			goto complete_key; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Is this valid UTF-8? */ |  | ||||||
| 	if ((more = utf8_open(&ud, (u_char)*buf) == UTF8_MORE)) { |  | ||||||
| 		size = ud.size; |  | ||||||
| 		if (len < size) { |  | ||||||
| 			if (expired) |  | ||||||
| 				goto discard_key; |  | ||||||
| 			goto partial_key; | 			goto partial_key; | ||||||
| 	} | 	} | ||||||
| 		for (i = 1; i < size; i++) |  | ||||||
| 			more = utf8_append(&ud, (u_char)buf[i]); |  | ||||||
| 		if (more != UTF8_DONE) |  | ||||||
| 			goto discard_key; |  | ||||||
|  |  | ||||||
| 		if (utf8_combine(&ud, &wc) != UTF8_DONE) | 	/* No longer key found, use the first character. */ | ||||||
| 			goto discard_key; |  | ||||||
| 		key = wc; |  | ||||||
|  |  | ||||||
| 		log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); |  | ||||||
| 		goto complete_key; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* No key found, take first. */ |  | ||||||
| 	key = (u_char)*buf; | 	key = (u_char)*buf; | ||||||
| 	size = 1; | 	size = 1; | ||||||
|  |  | ||||||
| @@ -578,7 +623,7 @@ first_key: | |||||||
| 	goto complete_key; | 	goto complete_key; | ||||||
|  |  | ||||||
| partial_key: | partial_key: | ||||||
| 	log_debug("partial key %.*s", (int) len, buf); | 	log_debug("partial key %.*s", (int)len, buf); | ||||||
|  |  | ||||||
| 	/* If timer is going, check for expiration. */ | 	/* If timer is going, check for expiration. */ | ||||||
| 	if (tty->flags & TTY_TIMER) { | 	if (tty->flags & TTY_TIMER) { | ||||||
| @@ -640,7 +685,7 @@ discard_key: | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Key timer callback. */ | /* Key timer callback. */ | ||||||
| void | static void | ||||||
| tty_keys_callback(__unused int fd, __unused short events, void *data) | tty_keys_callback(__unused int fd, __unused short events, void *data) | ||||||
| { | { | ||||||
| 	struct tty	*tty = data; | 	struct tty	*tty = data; | ||||||
| @@ -655,7 +700,7 @@ tty_keys_callback(__unused int fd, __unused short events, void *data) | |||||||
|  * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial |  * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial | ||||||
|  * (probably a mouse sequence but need more data). |  * (probably a mouse sequence but need more data). | ||||||
|  */ |  */ | ||||||
| int | static int | ||||||
| tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) | tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) | ||||||
| { | { | ||||||
| 	struct mouse_event	*m = &tty->mouse; | 	struct mouse_event	*m = &tty->mouse; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 nicm
					nicm