aboutsummaryrefslogblamecommitdiffstats
path: root/libterminput_read.c
blob: 93245739b54aea0b2545eb9f093b215d99fe2624 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                                         
                   

 





                                                                                          
                 
                   

                           
                                           

                                                 


                                                                                      
                                                                                       

                                                                              



                                           
                                                                             
                                 
                                                                           


                                                                                
                                   








                                                                                  
                                           
                                 
                 

                                 





                                                                                                                  
                                                                                                      





                                               

      
                           
                                                                    
                                    









                                                                                            

                                                                

                                  




                                                       
                                         

                                                 
 











                                                              
                                  


                                                     
                            
                                                                                                      
                                  
                                                                                           
                                  
                                                                                                                    
                                      








                                                                                                                    

                                                        
                                                                                                                 
                                                
                                             




                                                                                                              
                                 

                                                  
                                                        







                                                                                  

                                         
                                                                                                                  
                                                

                                                                                                                  
                                                

                                                                                                                  
                                                
                                  

                                                 
                                                          


                                   





                                                                                                                     
                








                                                                        
                                                 

                                                            



                                                                           

                                                                  
                                                                   




                              

     
                       
                 
 
/* See LICENSE file for copyright and license details. */
#include "common.h"


int
libterminput_read(int fd, union libterminput_input *input, struct libterminput_state *ctx)
{
	struct input ret;
	size_t n, m;
	char *p;
	int r, i;
	ssize_t rd;

	if (!ctx->inited) {
		/* Initialise structures */
		ctx->inited = 1;
		memset(input, 0, sizeof(*input));
		/* The user should have set all bytes in `*ctx` to 0, and it doesn't
		 * contain any pointers, and in fact all initial values should be 0,
		 * so we do not need to modify `*ctx` except mark it as initialised */
	} else if (input->type == LIBTERMINPUT_KEYPRESS && input->keypress.times > 1) {
		/* For repeated input, unless counter is reset by user, report
		 * the same event again (and decrease the counter) */
		input->keypress.times -= 1;
		return 1;
	}

	/* If in a bracketed paste, use the reading function for that mode */
	if (ctx->bracketed_paste)
		return libterminput_read_bracketed_paste__(fd, input, ctx);

	/* If we are on an incomplete mouse tracking event, read more raw input,
	 * otherwise read one symbol */
	if (!ctx->mouse_tracking) {
		r = libterminput_read_symbol__(fd, &ret, ctx);
		if (r <= 0) {
			if (!r || ctx->blocked || !ctx->queued || errno != EAGAIN)
				return r;
			ctx->blocked = 1;
			ctx->queued = 0;
			input->type = LIBTERMINPUT_KEYPRESS;
			ctx->mods = 0;
			ctx->meta = 0;
			ctx->key[0] = '\0';
			return 1;
		}
		ctx->blocked = 0;
		ctx->queued = 0;
	} else {
		if (ctx->stored_tail > sizeof(ctx->stored) - (size_t)ctx->mouse_tracking) {
			memmove(ctx->stored, &ctx->stored[ctx->stored_tail], ctx->stored_head - ctx->stored_tail);
			ctx->stored_tail -= ctx->stored_head;
			ctx->stored_head = 0;
		}
		rd = read(fd, &ctx->stored[ctx->stored_head], sizeof(ctx->stored) - ctx->stored_head);
		if (rd <= 0)
			return (int)rd;
		ctx->stored_head += (size_t)rd;
		p = strchr(ctx->key, '\0');
		goto continue_incomplete;
	}

again:
	if (!*ret.symbol) {
		/* Incomplete input, specifically only Meta/ESC's */
		if (ctx->meta < 3) {
			/* Up to two Meta/ESC, wait until a third or something else is read,
			 * or if ESC on block, report as ESC or Meta+ESC */
			if (ctx->flags & LIBTERMINPUT_ESC_ON_BLOCK) {
				input->keypress.key = LIBTERMINPUT_ESC;
				input->keypress.times = 1;
				input->keypress.mods = ctx->mods;
				input->keypress.symbol[0] = '\0';
				if (ctx->meta > 1)
					input->keypress.mods |= LIBTERMINPUT_META;
				ctx->queued = 1;
				input->type = LIBTERMINPUT_NONE;
				return 1;
			}
			goto none;
		}
		/* Three ESC's */
		input->type = LIBTERMINPUT_KEYPRESS;
		input->keypress.key = LIBTERMINPUT_ESC;
		input->keypress.times = 3;
		input->keypress.mods = 0;
		input->keypress.symbol[0] = '\0';
		ctx->meta -= 3;

	} else if (*ctx->key) {
		/* Special keys */
		if (ret.mods) {
			/* Special key was aborted, restart */
			*ctx->key = '\0';
			goto again;
		}
		/* Add new input to sequence */
		n = strlen(ctx->key);
		m = strlen(ret.symbol);
		if (n + m >= sizeof(ctx->key)) {
			/* Abort if too long */
			goto none;
		}
		p = stpcpy(&ctx->key[n], ret.symbol);
		/* Check if sequence is complete */
	continue_incomplete:
		if (!isalpha(p[-1]) && p[-1] != '~' && p[-1] != '@' && p[-1] != '^' && p[-1] != '$') {
			goto none;
		} else if (ctx->key[0] == '[' && ctx->key[1] == '<' && p == &ctx->key[2]) {
			goto none;
		} else if (ctx->key[0] == '[' && ctx->key[1] == 'M' && (ctx->flags & LIBTERMINPUT_MACRO_ON_CSI_M)) {
			/* complete */
		} else if (ctx->key[0] == '[' && ctx->key[1] == 'M' && (ctx->flags & LIBTERMINPUT_MACRO_ON_BLOCK) &&
		                                                       ctx->stored_head - ctx->stored_tail == 0) {
			input->keypress.key = LIBTERMINPUT_MACRO;
			input->keypress.times = 1;
			input->keypress.mods = ctx->mods;
			input->keypress.symbol[0] = '\0';
			if (ctx->meta > 1)
				input->keypress.mods |= LIBTERMINPUT_META;
			ctx->queued = 1;
			input->type = LIBTERMINPUT_NONE;
			return 1;
		} else if (ctx->key[0] == '[' && ctx->key[1] == 'M' && (ctx->flags & LIBTERMINPUT_DECSET_1005)) {
			ctx->mouse_tracking = 1;
			n = ctx->stored_tail;
			for (i = 0; i < 3; i++) {
				r = libterminput_check_utf8_char__(&ctx->stored[n], ctx->stored_head - n, &m);
				if (r > 0) {
					n += m;
					continue;
				}
				if (!r)
					goto none;
				ctx->mouse_tracking = 0;
				input->type = LIBTERMINPUT_KEYPRESS;
				input->keypress.key = LIBTERMINPUT_MACRO;
				input->keypress.mods = ret.mods;
				input->keypress.times = 1;
				if (ctx->meta > 1)
					input->keypress.mods |= LIBTERMINPUT_META;
				ctx->meta = 0;
				ctx->key[0] = '\0';
				return 1;
			}
		} else if (ctx->key[0] == '[' && ctx->key[1] == 'M' && ctx->stored_head - ctx->stored_tail < 3U) {
			ctx->mouse_tracking = 3;
			goto none;
		} else if (ctx->key[0] == '[' && ctx->key[1] == 't' && ctx->stored_head - ctx->stored_tail < 2U) {
			ctx->mouse_tracking = 2;
			goto none;
		} else if (ctx->key[0] == '[' && ctx->key[1] == 'T' && ctx->stored_head - ctx->stored_tail < 6U) {
			ctx->mouse_tracking = 6;
			goto none;
		}
		/* Parse the complete sequence */
		libterminput_parse_sequence__(input, ctx);
		/* Reset */
		ctx->meta = 0;
		ctx->key[0] = '\0';

	} else if (ctx->meta && (!strcmp(ret.symbol, "[") || !strcmp(ret.symbol, "O") || !strcmp(ret.symbol, "?"))) {
		/* ESC [, ESC O, or ESC ? is used as the beginning of most special keys */
		stpcpy(ctx->key, ret.symbol);
		goto none;

	} else {
		/* Character input and single-byte special keys */
		input->type = LIBTERMINPUT_KEYPRESS;
		input->keypress.mods = ret.mods;
		input->keypress.times = 1;
		if (ctx->meta) {
			/* Transfer meta modifier from state to input */
			input->keypress.mods |= LIBTERMINPUT_META;
			ctx->meta = 0;
		}
		input->keypress.symbol[0] = '\0';
		switch (ret.symbol[1] ? 0 : ret.symbol[0]) {
		case 127:
		case '\b': input->keypress.key = LIBTERMINPUT_ERASE; break;
		case '\t': input->keypress.key = LIBTERMINPUT_TAB;   break;
		case '\n': input->keypress.key = LIBTERMINPUT_ENTER; break;
		case 033:  input->keypress.key = LIBTERMINPUT_ESC;   break;
		default:
			input->keypress.key = LIBTERMINPUT_SYMBOL;
			stpcpy(input->keypress.symbol, ret.symbol);
			break;
		}
	}

	return 1;

none:
	NOTHING(input);
	return 1;
}