shithub: femtolisp

ref: 14c196a5da48284123d615418c80b6fbc87d7f46
dir: /terminal_posix.c/

View raw version
#include "flisp.h"
#include <sys/ioctl.h>
#include <termios.h>

static bool inraw = false;
static bool cursorvisible = true;
static bool atexitset = false;
static struct termios tios;

static void termreset(void);

static int
termsetraw(bool raw, bool showcursor)
{
	if(!atexitset){
		atexit(termreset);
		atexitset = true;
	}
	if(showcursor && !cursorvisible){
		write(STDOUT_FILENO, "\x1b[?25h", 6);
		cursorvisible = true;
	}else if(!showcursor && cursorvisible){
		write(STDOUT_FILENO, "\x1b[?25l", 6);
		cursorvisible = false;
	}

	if(raw && !inraw){
		if(tcgetattr(STDIN_FILENO, &tios) < 0)
			return -1;
		struct termios t;
		memcpy(&t, &tios, sizeof(t));
		t.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
		t.c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN);
		t.c_oflag &= ~(OPOST);
		t.c_cc[VMIN] = 0;
		if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) < 0)
			return -1;
		inraw = true;
	}else if(!raw && inraw){
		if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &tios) < 0)
			return -1;
		inraw = false;
	}
	return 0;
}

static void
termreset(void)
{
	termsetraw(false, true);
}

BUILTIN("terminal-enter-raw-mode", terminal_enter_raw_mode)
{
	USED(args);
	argcount(nargs, 0);
	return termsetraw(true, cursorvisible) == 0 ? FL(t) : FL(f);
}

BUILTIN("terminal-leave-raw-mode", terminal_leave_raw_mode)
{
	USED(args);
	argcount(nargs, 0);
	return termsetraw(false, cursorvisible) == 0 ? FL(t) : FL(f);
}

BUILTIN("terminal-show-cursor", terminal_show_cursor)
{
	USED(args);
	argcount(nargs, 0);
	return termsetraw(inraw, true) == 0 ? FL(t) : FL(f);
}

BUILTIN("terminal-hide-cursor", terminal_hide_cursor)
{
	USED(args);
	argcount(nargs, 0);
	return termsetraw(inraw, false) == 0 ? FL(t) : FL(f);
}

BUILTIN("terminal-get-size", terminal_get_size)
{
	USED(args);
	argcount(nargs, 0);
	struct winsize s;
	if(ioctl(STDIN_FILENO, TIOCGWINSZ, &s) < 0)
		return FL(f);
	value_t v = mk_cons(), tex, pix;
	car_(v) = tex = mk_cons();
	car_(tex) = fixnum(s.ws_col);
	cdr_(tex) = mk_cons(); car_(cdr_(tex)) = fixnum(s.ws_row); cdr_(cdr_(tex)) = FL(Nil);
	int x = s.ws_xpixel, y = s.ws_ypixel;
	bool wasraw = inraw;
	if((x == 0 || y == 0) && isatty(STDOUT_FILENO) && termsetraw(true, cursorvisible) == 0){
		// FIXME(sigrid): read everything out of stdin first
		write(STDOUT_FILENO, "\x1b[14t", 5);
		/* make sure not to hang if nothing is returned */
		struct timeval t;
		t.tv_sec = 0;
		t.tv_usec = 100;
		fd_set f;
		FD_ZERO(&f);
		FD_SET(STDIN_FILENO, &f);
		int r;
		while((r = select(STDIN_FILENO+1, &f, nil, nil, &t)) == EINTR);
		if(r > 0 && FD_ISSET(STDIN_FILENO, &f)){
			char buf[64];
			if((r = read(STDIN_FILENO, buf, sizeof(buf)-1)) >= 8){
				buf[r] = 0;
				if(buf[0] == 0x1b && buf[1] == '[' && buf[2] == '4' && buf[3] == ';'){ /* CSI 4 */
					x = atoi(buf+5);
					char *s = strchr(buf+5, ';');
					if(s != nil)
						y = atoi(s+1);
					while(strchr(buf, 't') == nil){
						if((r = read(STDIN_FILENO, buf, sizeof(buf)-1)) <= 0)
							break;
						buf[r] = 0;
					}
				}
			}
		}
		if(!wasraw)
			termsetraw(false, cursorvisible);
	}
	cdr_(v) = pix = mk_cons(); cdr_(pix) = FL(Nil);
	car_(pix) = mk_cons(); pix = car_(pix);
	car_(pix) = fixnum(x);
	cdr_(pix) = mk_cons(); car_(cdr_(pix)) = fixnum(y); cdr_(cdr_(pix)) = FL(Nil);
	return v;
}