diff options
| author | June McEnroe | 2022-02-19 20:20:19 -0500 | 
|---|---|---|
| committer | June McEnroe | 2022-02-19 20:20:19 -0500 | 
| commit | 073cebec7a5a07ab2b829e40ce47ec3b1d774bd9 (patch) | |
| tree | ad20f89bb88ca5bd3499c2628b03a00d67985a4c | |
| parent | 3359a5d69b0fe3c08812f7db83e27958ffec820f (diff) | |
Factor out input handling to input.c
| -rw-r--r-- | Makefile | 5 | ||||
| -rw-r--r-- | README.7 | 4 | ||||
| -rw-r--r-- | chat.c | 9 | ||||
| -rw-r--r-- | chat.h | 14 | ||||
| -rw-r--r-- | handle.c | 2 | ||||
| -rw-r--r-- | input.c | 390 | ||||
| -rw-r--r-- | ui.c | 369 | ||||
| -rw-r--r-- | window.c | 6 | 
8 files changed, 424 insertions, 375 deletions
@@ -20,6 +20,7 @@ OBJS += config.o  OBJS += edit.o  OBJS += filter.o  OBJS += handle.o +OBJS += input.o  OBJS += irc.o  OBJS += log.o  OBJS += ui.o @@ -36,7 +37,9 @@ all: catgirl  catgirl: ${OBJS}  	${CC} ${LDFLAGS} ${OBJS} ${LDLIBS} -o $@ -${OBJS} ${TESTS}: chat.h edit.h +${OBJS}: chat.h + +edit.o edit.t input.o: edit.h  check: ${TESTS} @@ -183,10 +183,12 @@ IRC connection and parsing  curses interface  .It Pa window.c  window management +.It Pa input.c +input handling  .It Pa handle.c  IRC message handling  .It Pa command.c -input command handling +command handling  .It Pa buffer.c  line wrapping  .It Pa edit.c @@ -375,7 +375,7 @@ int main(int argc, char *argv[]) {  	ircConfig(insecure, trust, cert, priv); -	uiInitEarly(); +	uiInit();  	sig_t cursesWinch = signal(SIGWINCH, signalHandler);  	if (save) {  		uiLoad(save); @@ -407,7 +407,8 @@ int main(int argc, char *argv[]) {  	ircFormat("NICK :%s\r\n", nick);  	ircFormat("USER %s 0 * :%s\r\n", user, real); -	uiInitLate(); +	// Avoid disabling VINTR until main loop. +	inputInit();  	signal(SIGHUP, signalHandler);  	signal(SIGINT, signalHandler);  	signal(SIGALRM, signalHandler); @@ -436,7 +437,7 @@ int main(int argc, char *argv[]) {  		int nfds = poll(fds, (pipes ? ARRAY_LEN(fds) : 2), -1);  		if (nfds < 0 && errno != EINTR) err(EX_IOERR, "poll");  		if (nfds > 0) { -			if (fds[0].revents) uiRead(); +			if (fds[0].revents) inputRead();  			if (fds[1].revents) ircRecv();  			if (fds[2].revents) utilRead();  			if (fds[3].revents) execRead(); @@ -488,7 +489,7 @@ int main(int argc, char *argv[]) {  			cursesWinch(SIGWINCH);  			// doupdate(3) needs to be called for KEY_RESIZE to be picked up.  			uiDraw(); -			uiRead(); +			inputRead();  		}  		uiDraw(); @@ -311,17 +311,16 @@ enum {  extern char uiTitle[TitleCap];  extern struct _win_st *uiStatus;  extern struct _win_st *uiMain; +extern struct _win_st *uiInput; +extern bool uiSpoilerReveal;  extern struct Util uiNotifyUtil; -void uiInitEarly(void); -void uiInitLate(void); +void uiInit(void);  uint uiAttr(struct Style style);  short uiPair(struct Style style); -void uiUpdate(void);  void uiShow(void);  void uiHide(void); -void uiWait(void);  void uiDraw(void); -void uiRead(void); +void uiResize(void);  void uiWrite(uint id, enum Heat heat, const time_t *time, const char *str);  void uiFormat(  	uint id, enum Heat heat, const time_t *time, const char *format, ... @@ -329,6 +328,11 @@ void uiFormat(  void uiLoad(const char *name);  int uiSave(void); +void inputInit(void); +void inputWait(void); +void inputUpdate(void); +void inputRead(void); +  enum Scroll {  	ScrollOne,  	ScrollPage, @@ -425,7 +425,7 @@ static void handleNick(struct Message *msg) {  	require(msg, true, 1);  	if (!strcmp(msg->nick, self.nick)) {  		set(&self.nick, msg->params[0]); -		uiRead(); // Update prompt. +		inputUpdate();  	}  	for (uint id; (id = completeID(msg->nick));) {  		if (!strcmp(idNames[id], msg->nick)) { @@ -0,0 +1,390 @@ +/* Copyright (C) 2020  June McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <https://www.gnu.org/licenses/>. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this Program, or any covered work, by linking or + * combining it with OpenSSL (or a modified version of that library), + * containing parts covered by the terms of the OpenSSL License and the + * original SSLeay license, the licensors of this Program grant you + * additional permission to convey the resulting work. Corresponding + * Source for a non-source form of such a combination shall include the + * source code for the parts of OpenSSL used as well as that of the + * covered work. + */ + +#define _XOPEN_SOURCE_EXTENDED + +#include <curses.h> +#include <err.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <sysexits.h> +#include <termios.h> + +#include "chat.h" +#include "edit.h" + +#define ENUM_KEY \ +	X(KeyCtrlLeft, "\33[1;5D", NULL) \ +	X(KeyCtrlRight, "\33[1;5C", NULL) \ +	X(KeyMeta0, "\0330", "\33)") \ +	X(KeyMeta1, "\0331", "\33!") \ +	X(KeyMeta2, "\0332", "\33@") \ +	X(KeyMeta3, "\0333", "\33#") \ +	X(KeyMeta4, "\0334", "\33$") \ +	X(KeyMeta5, "\0335", "\33%") \ +	X(KeyMeta6, "\0336", "\33^") \ +	X(KeyMeta7, "\0337", "\33&") \ +	X(KeyMeta8, "\0338", "\33*") \ +	X(KeyMeta9, "\0339", "\33(") \ +	X(KeyMetaA, "\33a", NULL) \ +	X(KeyMetaB, "\33b", NULL) \ +	X(KeyMetaD, "\33d", NULL) \ +	X(KeyMetaF, "\33f", NULL) \ +	X(KeyMetaL, "\33l", NULL) \ +	X(KeyMetaM, "\33m", NULL) \ +	X(KeyMetaN, "\33n", NULL) \ +	X(KeyMetaP, "\33p", NULL) \ +	X(KeyMetaQ, "\33q", NULL) \ +	X(KeyMetaS, "\33s", NULL) \ +	X(KeyMetaT, "\33t", NULL) \ +	X(KeyMetaU, "\33u", NULL) \ +	X(KeyMetaV, "\33v", NULL) \ +	X(KeyMetaEnter, "\33\r", "\33\n") \ +	X(KeyMetaGt, "\33>", "\33.") \ +	X(KeyMetaLt, "\33<", "\33,") \ +	X(KeyMetaEqual, "\33=", NULL) \ +	X(KeyMetaMinus, "\33-", "\33_") \ +	X(KeyMetaPlus, "\33+", NULL) \ +	X(KeyMetaSlash, "\33/", "\33?") \ +	X(KeyFocusIn, "\33[I", NULL) \ +	X(KeyFocusOut, "\33[O", NULL) \ +	X(KeyPasteOn, "\33[200~", NULL) \ +	X(KeyPasteOff, "\33[201~", NULL) \ +	X(KeyPasteManual, "\32p", "\32\20") + +enum { +	KeyMax = KEY_MAX, +#define X(id, seq, alt) id, +	ENUM_KEY +#undef X +}; + +static struct Edit edit; + +void inputInit(void) { +	struct termios term; +	int error = tcgetattr(STDOUT_FILENO, &term); +	if (error) err(EX_OSERR, "tcgetattr"); + +	// Gain use of C-q, C-s, C-c, C-z, C-y, C-v, C-o. +	term.c_iflag &= ~IXON; +	term.c_cc[VINTR] = _POSIX_VDISABLE; +	term.c_cc[VSUSP] = _POSIX_VDISABLE; +#ifdef VDSUSP +	term.c_cc[VDSUSP] = _POSIX_VDISABLE; +#endif +	term.c_cc[VLNEXT] = _POSIX_VDISABLE; +	term.c_cc[VDISCARD] = _POSIX_VDISABLE; + +	error = tcsetattr(STDOUT_FILENO, TCSANOW, &term); +	if (error) err(EX_OSERR, "tcsetattr"); + +	def_prog_mode(); + +#define X(id, seq, alt) define_key(seq, id); if (alt) define_key(alt, id); +	ENUM_KEY +#undef X + +	keypad(uiInput, true); +	nodelay(uiInput, true); +} + +static void inputAdd(struct Style reset, struct Style *style, const char *str) { +	while (*str) { +		const char *code = str; +		size_t len = styleParse(style, &str); +		wattr_set(uiInput, A_BOLD | A_REVERSE, 0, NULL); +		switch (*code) { +			break; case B: waddch(uiInput, 'B'); +			break; case C: waddch(uiInput, 'C'); +			break; case O: waddch(uiInput, 'O'); +			break; case R: waddch(uiInput, 'R'); +			break; case I: waddch(uiInput, 'I'); +			break; case U: waddch(uiInput, 'U'); +			break; case '\n': waddch(uiInput, 'N'); +		} +		if (str - code > 1) waddnstr(uiInput, &code[1], str - &code[1]); +		if (str[0] == '\n') { +			*style = reset; +			str++; +			len--; +		} +		size_t nl = strcspn(str, "\n"); +		if (nl < len) len = nl; +		wattr_set(uiInput, uiAttr(*style), uiPair(*style), NULL); +		waddnstr(uiInput, str, len); +		str += len; +	} +} + +static char *inputStop( +	struct Style reset, struct Style *style, +	const char *str, char *stop +) { +	char ch = *stop; +	*stop = '\0'; +	inputAdd(reset, style, str); +	*stop = ch; +	return stop; +} + +void inputUpdate(void) { +	uint id = windowID(); +	char *buf = editString(&edit); + +	const char *prefix = ""; +	const char *prompt = self.nick; +	const char *suffix = ""; +	const char *skip = buf; +	struct Style stylePrompt = { .fg = self.color, .bg = Default }; +	struct Style styleInput = StyleDefault; + +	size_t split = commandWillSplit(id, buf); +	const char *privmsg = commandIsPrivmsg(id, buf); +	const char *notice = commandIsNotice(id, buf); +	const char *action = commandIsAction(id, buf); +	if (privmsg) { +		prefix = "<"; suffix = "> "; +		skip = privmsg; +	} else if (notice) { +		prefix = "-"; suffix = "- "; +		styleInput.fg = LightGray; +		skip = notice; +	} else if (action) { +		prefix = "* "; suffix = " "; +		stylePrompt.attr |= Italic; +		styleInput.attr |= Italic; +		skip = action; +	} else if (id == Debug && buf[0] != '/') { +		prompt = "<< "; +		stylePrompt.fg = Gray; +	} else { +		prompt = ""; +	} +	if (skip > &buf[edit.mbs.pos]) { +		prefix = prompt = suffix = ""; +		skip = buf; +	} + +	int y, x; +	wmove(uiInput, 0, 0); +	if (windowTimeEnable() && id != Network) { +		whline(uiInput, ' ', windowTime.width); +		wmove(uiInput, 0, windowTime.width); +	} +	wattr_set(uiInput, uiAttr(stylePrompt), uiPair(stylePrompt), NULL); +	waddstr(uiInput, prefix); +	waddstr(uiInput, prompt); +	waddstr(uiInput, suffix); +	getyx(uiInput, y, x); + +	int pos; +	struct Style style = styleInput; +	inputStop(styleInput, &style, skip, &buf[edit.mbs.pos]); +	getyx(uiInput, y, pos); +	wmove(uiInput, y, x); + +	style = styleInput; +	const char *ptr = skip; +	if (split) { +		ptr = inputStop(styleInput, &style, ptr, &buf[split]); +		style = styleInput; +		style.bg = Red; +	} +	inputAdd(styleInput, &style, ptr); +	wclrtoeol(uiInput); +	wmove(uiInput, y, pos); +} + +static void inputEnter(void) { +	command(windowID(), editString(&edit)); +	editFn(&edit, EditClear); +} + +static void keyCode(int code) { +	switch (code) { +		break; case KEY_RESIZE:  uiResize(); +		break; case KeyFocusIn:  windowUnmark(); +		break; case KeyFocusOut: windowMark(); + +		break; case KeyMetaEnter: editInsert(&edit, L'\n'); +		break; case KeyMetaEqual: windowToggleMute(); +		break; case KeyMetaMinus: windowToggleThresh(-1); +		break; case KeyMetaPlus:  windowToggleThresh(+1); +		break; case KeyMetaSlash: windowSwap(); + +		break; case KeyMetaGt: windowScroll(ScrollAll, -1); +		break; case KeyMetaLt: windowScroll(ScrollAll, +1); + +		break; case KeyMeta0 ... KeyMeta9: windowShow(code - KeyMeta0); +		break; case KeyMetaA: windowAuto(); +		break; case KeyMetaB: editFn(&edit, EditPrevWord); +		break; case KeyMetaD: editFn(&edit, EditDeleteNextWord); +		break; case KeyMetaF: editFn(&edit, EditNextWord); +		break; case KeyMetaL: windowBare(); +		break; case KeyMetaM: uiWrite(windowID(), Warm, NULL, ""); +		break; case KeyMetaN: windowScroll(ScrollHot, +1); +		break; case KeyMetaP: windowScroll(ScrollHot, -1); +		break; case KeyMetaQ: editFn(&edit, EditCollapse); +		break; case KeyMetaS: uiSpoilerReveal ^= true; windowUpdate(); +		break; case KeyMetaT: windowToggleTime(); +		break; case KeyMetaU: windowScroll(ScrollUnread, 0); +		break; case KeyMetaV: windowScroll(ScrollPage, +1); + +		break; case KeyCtrlLeft: editFn(&edit, EditPrevWord); +		break; case KeyCtrlRight: editFn(&edit, EditNextWord); + +		break; case KEY_BACKSPACE: editFn(&edit, EditDeletePrev); +		break; case KEY_DC: editFn(&edit, EditDeleteNext); +		break; case KEY_DOWN: windowScroll(ScrollOne, -1); +		break; case KEY_END: editFn(&edit, EditTail); +		break; case KEY_ENTER: inputEnter(); +		break; case KEY_HOME: editFn(&edit, EditHead); +		break; case KEY_LEFT: editFn(&edit, EditPrev); +		break; case KEY_NPAGE: windowScroll(ScrollPage, -1); +		break; case KEY_PPAGE: windowScroll(ScrollPage, +1); +		break; case KEY_RIGHT: editFn(&edit, EditNext); +		break; case KEY_SEND: windowScroll(ScrollAll, -1); +		break; case KEY_SHOME: windowScroll(ScrollAll, +1); +		break; case KEY_UP: windowScroll(ScrollOne, +1); +	} +} + +static void keyCtrl(wchar_t ch) { +	switch (ch ^ L'@') { +		break; case L'?': editFn(&edit, EditDeletePrev); +		break; case L'A': editFn(&edit, EditHead); +		break; case L'B': editFn(&edit, EditPrev); +		break; case L'C': raise(SIGINT); +		break; case L'D': editFn(&edit, EditDeleteNext); +		break; case L'E': editFn(&edit, EditTail); +		break; case L'F': editFn(&edit, EditNext); +		break; case L'H': editFn(&edit, EditDeletePrev); +		break; case L'J': inputEnter(); +		break; case L'K': editFn(&edit, EditDeleteTail); +		break; case L'L': clearok(curscr, true); +		break; case L'N': windowShow(windowNum() + 1); +		break; case L'P': windowShow(windowNum() - 1); +		break; case L'R': windowSearch(editString(&edit), -1); +		break; case L'S': windowSearch(editString(&edit), +1); +		break; case L'T': editFn(&edit, EditTranspose); +		break; case L'U': editFn(&edit, EditDeleteHead); +		break; case L'V': windowScroll(ScrollPage, -1); +		break; case L'W': editFn(&edit, EditDeletePrevWord); +		break; case L'Y': editFn(&edit, EditPaste); +	} +} + +static void keyStyle(wchar_t ch) { +	if (iswcntrl(ch)) ch = towlower(ch ^ L'@'); +	char buf[8] = {0}; +	enum Color color = Default; +	switch (ch) { +		break; case L'A': color = Gray; +		break; case L'B': color = Blue; +		break; case L'C': color = Cyan; +		break; case L'G': color = Green; +		break; case L'K': color = Black; +		break; case L'M': color = Magenta; +		break; case L'N': color = Brown; +		break; case L'O': color = Orange; +		break; case L'P': color = Pink; +		break; case L'R': color = Red; +		break; case L'W': color = White; +		break; case L'Y': color = Yellow; +		break; case L'b': buf[0] = B; +		break; case L'c': buf[0] = C; +		break; case L'i': buf[0] = I; +		break; case L'o': buf[0] = O; +		break; case L'r': buf[0] = R; +		break; case L's': { +			snprintf(buf, sizeof(buf), "%c%02d,%02d", C, Black, Black); +		} +		break; case L'u': buf[0] = U; +	} +	if (color != Default) { +		snprintf(buf, sizeof(buf), "%c%02d", C, color); +	} +	for (char *ch = buf; *ch; ++ch) { +		editInsert(&edit, *ch); +	} +} + +static bool waiting; + +void inputWait(void) { +	waiting = true; +} + +void inputRead(void) { +	if (isendwin()) { +		if (waiting) { +			uiShow(); +			flushinp(); +			waiting = false; +		} else { +			return; +		} +	} + +	wint_t ch; +	static bool paste, style, literal; +	for (int ret; ERR != (ret = wget_wch(uiInput, &ch));) { +		bool spr = uiSpoilerReveal; +		if (ret == KEY_CODE_YES && ch == KeyPasteOn) { +			paste = true; +		} else if (ret == KEY_CODE_YES && ch == KeyPasteOff) { +			paste = false; +		} else if (ret == KEY_CODE_YES && ch == KeyPasteManual) { +			paste ^= true; +		} else if (paste || literal) { +			editInsert(&edit, ch); +		} else if (ret == KEY_CODE_YES) { +			keyCode(ch); +		} else if (ch == (L'Z' ^ L'@')) { +			style = true; +			continue; +		} else if (style && ch == (L'V' ^ L'@')) { +			literal = true; +			continue; +		} else if (style) { +			keyStyle(ch); +		} else if (iswcntrl(ch)) { +			keyCtrl(ch); +		} else { +			editInsert(&edit, ch); +		} +		style = false; +		literal = false; +		if (spr) { +			uiSpoilerReveal = false; +			windowUpdate(); +		} +	} +	inputUpdate(); +} @@ -54,7 +54,6 @@  #endif  #include "chat.h" -#include "edit.h"  // Annoying stuff from <term.h>:  #undef lines @@ -66,7 +65,7 @@  WINDOW *uiStatus;  WINDOW *uiMain; -static WINDOW *input; +WINDOW *uiInput;  static short colorPairs; @@ -101,52 +100,6 @@ static short colorPair(short fg, short bg) {  	return colorPairs++;  } -#define ENUM_KEY \ -	X(KeyCtrlLeft, "\33[1;5D", NULL) \ -	X(KeyCtrlRight, "\33[1;5C", NULL) \ -	X(KeyMeta0, "\0330", "\33)") \ -	X(KeyMeta1, "\0331", "\33!") \ -	X(KeyMeta2, "\0332", "\33@") \ -	X(KeyMeta3, "\0333", "\33#") \ -	X(KeyMeta4, "\0334", "\33$") \ -	X(KeyMeta5, "\0335", "\33%") \ -	X(KeyMeta6, "\0336", "\33^") \ -	X(KeyMeta7, "\0337", "\33&") \ -	X(KeyMeta8, "\0338", "\33*") \ -	X(KeyMeta9, "\0339", "\33(") \ -	X(KeyMetaA, "\33a", NULL) \ -	X(KeyMetaB, "\33b", NULL) \ -	X(KeyMetaD, "\33d", NULL) \ -	X(KeyMetaF, "\33f", NULL) \ -	X(KeyMetaL, "\33l", NULL) \ -	X(KeyMetaM, "\33m", NULL) \ -	X(KeyMetaN, "\33n", NULL) \ -	X(KeyMetaP, "\33p", NULL) \ -	X(KeyMetaQ, "\33q", NULL) \ -	X(KeyMetaS, "\33s", NULL) \ -	X(KeyMetaT, "\33t", NULL) \ -	X(KeyMetaU, "\33u", NULL) \ -	X(KeyMetaV, "\33v", NULL) \ -	X(KeyMetaEnter, "\33\r", "\33\n") \ -	X(KeyMetaGt, "\33>", "\33.") \ -	X(KeyMetaLt, "\33<", "\33,") \ -	X(KeyMetaEqual, "\33=", NULL) \ -	X(KeyMetaMinus, "\33-", "\33_") \ -	X(KeyMetaPlus, "\33+", NULL) \ -	X(KeyMetaSlash, "\33/", "\33?") \ -	X(KeyFocusIn, "\33[I", NULL) \ -	X(KeyFocusOut, "\33[O", NULL) \ -	X(KeyPasteOn, "\33[200~", NULL) \ -	X(KeyPasteOff, "\33[201~", NULL) \ -	X(KeyPasteManual, "\32p", "\32\20") - -enum { -	KeyMax = KEY_MAX, -#define X(id, seq, alt) id, -	ENUM_KEY -#undef X -}; -  // XXX: Assuming terminals will be fine with these even if they're unsupported,  // since they're "private" modes.  static const char *FocusMode[2] = { "\33[?1004l", "\33[?1004h" }; @@ -158,7 +111,7 @@ static void errExit(void) {  	reset_shell_mode();  } -void uiInitEarly(void) { +void uiInit(void) {  	initscr();  	cbreak();  	noecho(); @@ -177,49 +130,20 @@ void uiInitEarly(void) {  		from_status_line = "\7";  	} -#define X(id, seq, alt) define_key(seq, id); if (alt) define_key(alt, id); -	ENUM_KEY -#undef X -  	uiStatus = newwin(StatusLines, COLS, 0, 0);  	if (!uiStatus) err(EX_OSERR, "newwin");  	uiMain = newwin(MAIN_LINES, COLS, StatusLines, 0);  	if (!uiMain) err(EX_OSERR, "newwin"); -	input = newpad(InputLines, InputCols); -	if (!input) err(EX_OSERR, "newpad"); -	keypad(input, true); -	nodelay(input, true); +	uiInput = newpad(InputLines, InputCols); +	if (!uiInput) err(EX_OSERR, "newpad");  	windowInit();  	uiShow();  } -// Avoid disabling VINTR until main loop. -void uiInitLate(void) { -	struct termios term; -	int error = tcgetattr(STDOUT_FILENO, &term); -	if (error) err(EX_OSERR, "tcgetattr"); - -	// Gain use of C-q, C-s, C-c, C-z, C-y, C-v, C-o. -	term.c_iflag &= ~IXON; -	term.c_cc[VINTR] = _POSIX_VDISABLE; -	term.c_cc[VSUSP] = _POSIX_VDISABLE; -#ifdef VDSUSP -	term.c_cc[VDSUSP] = _POSIX_VDISABLE; -#endif -	term.c_cc[VLNEXT] = _POSIX_VDISABLE; -	term.c_cc[VDISCARD] = _POSIX_VDISABLE; - -	error = tcsetattr(STDOUT_FILENO, TCSANOW, &term); -	if (error) err(EX_OSERR, "tcsetattr"); - -	def_prog_mode(); -} -  static bool hidden = true; -static bool waiting;  char uiTitle[TitleCap];  static char prevTitle[TitleCap]; @@ -229,9 +153,9 @@ void uiDraw(void) {  	wnoutrefresh(uiStatus);  	wnoutrefresh(uiMain);  	int y, x; -	getyx(input, y, x); +	getyx(uiInput, y, x);  	pnoutrefresh( -		input, +		uiInput,  		0, (x + 1 > RIGHT ? x + 1 - RIGHT : 0),  		LINES - InputLines, 0,  		BOTTOM, RIGHT @@ -284,10 +208,10 @@ uint uiAttr(struct Style style) {  	return attr | colorAttr(Colors[style.fg]);  } -static bool spoilerReveal; +bool uiSpoilerReveal;  short uiPair(struct Style style) { -	if (spoilerReveal && style.fg == style.bg) { +	if (uiSpoilerReveal && style.fg == style.bg) {  		return colorPair(Colors[Default], Colors[style.bg]);  	}  	return colorPair(Colors[style.fg], Colors[style.bg]); @@ -312,10 +236,6 @@ void uiHide(void) {  	endwin();  } -void uiWait(void) { -	waiting = true; -} -  struct Util uiNotifyUtil;  static void notify(uint id, const char *str) {  	if (self.restricted) return; @@ -361,283 +281,12 @@ void uiFormat(  	uiWrite(id, heat, time, buf);  } -static void resize(void) { +void uiResize(void) {  	wclear(uiMain);  	wresize(uiMain, MAIN_LINES, COLS);  	windowResize();  } -static void inputAdd(struct Style reset, struct Style *style, const char *str) { -	while (*str) { -		const char *code = str; -		size_t len = styleParse(style, &str); -		wattr_set(input, A_BOLD | A_REVERSE, 0, NULL); -		switch (*code) { -			break; case B: waddch(input, 'B'); -			break; case C: waddch(input, 'C'); -			break; case O: waddch(input, 'O'); -			break; case R: waddch(input, 'R'); -			break; case I: waddch(input, 'I'); -			break; case U: waddch(input, 'U'); -			break; case '\n': waddch(input, 'N'); -		} -		if (str - code > 1) waddnstr(input, &code[1], str - &code[1]); -		if (str[0] == '\n') { -			*style = reset; -			str++; -			len--; -		} -		size_t nl = strcspn(str, "\n"); -		if (nl < len) len = nl; -		wattr_set(input, uiAttr(*style), uiPair(*style), NULL); -		waddnstr(input, str, len); -		str += len; -	} -} - -static char *inputStop( -	struct Style reset, struct Style *style, -	const char *str, char *stop -) { -	char ch = *stop; -	*stop = '\0'; -	inputAdd(reset, style, str); -	*stop = ch; -	return stop; -} - -static struct Edit edit; - -void uiUpdate(void) { -	char *buf = editString(&edit); -	uint id = windowID(); - -	const char *prefix = ""; -	const char *prompt = self.nick; -	const char *suffix = ""; -	const char *skip = buf; -	struct Style stylePrompt = { .fg = self.color, .bg = Default }; -	struct Style styleInput = StyleDefault; - -	size_t split = commandWillSplit(id, buf); -	const char *privmsg = commandIsPrivmsg(id, buf); -	const char *notice = commandIsNotice(id, buf); -	const char *action = commandIsAction(id, buf); -	if (privmsg) { -		prefix = "<"; suffix = "> "; -		skip = privmsg; -	} else if (notice) { -		prefix = "-"; suffix = "- "; -		styleInput.fg = LightGray; -		skip = notice; -	} else if (action) { -		prefix = "* "; suffix = " "; -		stylePrompt.attr |= Italic; -		styleInput.attr |= Italic; -		skip = action; -	} else if (id == Debug && buf[0] != '/') { -		prompt = "<< "; -		stylePrompt.fg = Gray; -	} else { -		prompt = ""; -	} -	if (skip > &buf[edit.mbs.pos]) { -		prefix = prompt = suffix = ""; -		skip = buf; -	} - -	int y, x; -	wmove(input, 0, 0); -	if (windowTimeEnable() && id != Network) { -		whline(input, ' ', windowTime.width); -		wmove(input, 0, windowTime.width); -	} -	wattr_set(input, uiAttr(stylePrompt), uiPair(stylePrompt), NULL); -	waddstr(input, prefix); -	waddstr(input, prompt); -	waddstr(input, suffix); -	getyx(input, y, x); - -	int pos; -	struct Style style = styleInput; -	inputStop(styleInput, &style, skip, &buf[edit.mbs.pos]); -	getyx(input, y, pos); -	wmove(input, y, x); - -	style = styleInput; -	const char *ptr = skip; -	if (split) { -		ptr = inputStop(styleInput, &style, ptr, &buf[split]); -		style = styleInput; -		style.bg = Red; -	} -	inputAdd(styleInput, &style, ptr); -	wclrtoeol(input); -	wmove(input, y, pos); -} - -static void inputEnter(void) { -	command(windowID(), editString(&edit)); -	editFn(&edit, EditClear); -} - -static void keyCode(int code) { -	switch (code) { -		break; case KEY_RESIZE:  resize(); -		break; case KeyFocusIn:  windowUnmark(); -		break; case KeyFocusOut: windowMark(); - -		break; case KeyMetaEnter: editInsert(&edit, L'\n'); -		break; case KeyMetaEqual: windowToggleMute(); -		break; case KeyMetaMinus: windowToggleThresh(-1); -		break; case KeyMetaPlus:  windowToggleThresh(+1); -		break; case KeyMetaSlash: windowSwap(); - -		break; case KeyMetaGt: windowScroll(ScrollAll, -1); -		break; case KeyMetaLt: windowScroll(ScrollAll, +1); - -		break; case KeyMeta0 ... KeyMeta9: windowShow(code - KeyMeta0); -		break; case KeyMetaA: windowAuto(); -		break; case KeyMetaB: editFn(&edit, EditPrevWord); -		break; case KeyMetaD: editFn(&edit, EditDeleteNextWord); -		break; case KeyMetaF: editFn(&edit, EditNextWord); -		break; case KeyMetaL: windowBare(); -		break; case KeyMetaM: uiWrite(windowID(), Warm, NULL, ""); -		break; case KeyMetaN: windowScroll(ScrollHot, +1); -		break; case KeyMetaP: windowScroll(ScrollHot, -1); -		break; case KeyMetaQ: editFn(&edit, EditCollapse); -		break; case KeyMetaS: spoilerReveal ^= true; windowUpdate(); -		break; case KeyMetaT: windowToggleTime(); -		break; case KeyMetaU: windowScroll(ScrollUnread, 0); -		break; case KeyMetaV: windowScroll(ScrollPage, +1); - -		break; case KeyCtrlLeft: editFn(&edit, EditPrevWord); -		break; case KeyCtrlRight: editFn(&edit, EditNextWord); - -		break; case KEY_BACKSPACE: editFn(&edit, EditDeletePrev); -		break; case KEY_DC: editFn(&edit, EditDeleteNext); -		break; case KEY_DOWN: windowScroll(ScrollOne, -1); -		break; case KEY_END: editFn(&edit, EditTail); -		break; case KEY_ENTER: inputEnter(); -		break; case KEY_HOME: editFn(&edit, EditHead); -		break; case KEY_LEFT: editFn(&edit, EditPrev); -		break; case KEY_NPAGE: windowScroll(ScrollPage, -1); -		break; case KEY_PPAGE: windowScroll(ScrollPage, +1); -		break; case KEY_RIGHT: editFn(&edit, EditNext); -		break; case KEY_SEND: windowScroll(ScrollAll, -1); -		break; case KEY_SHOME: windowScroll(ScrollAll, +1); -		break; case KEY_UP: windowScroll(ScrollOne, +1); -	} -} - -static void keyCtrl(wchar_t ch) { -	switch (ch ^ L'@') { -		break; case L'?': editFn(&edit, EditDeletePrev); -		break; case L'A': editFn(&edit, EditHead); -		break; case L'B': editFn(&edit, EditPrev); -		break; case L'C': raise(SIGINT); -		break; case L'D': editFn(&edit, EditDeleteNext); -		break; case L'E': editFn(&edit, EditTail); -		break; case L'F': editFn(&edit, EditNext); -		break; case L'H': editFn(&edit, EditDeletePrev); -		break; case L'J': inputEnter(); -		break; case L'K': editFn(&edit, EditDeleteTail); -		break; case L'L': clearok(curscr, true); -		break; case L'N': windowShow(windowNum() + 1); -		break; case L'P': windowShow(windowNum() - 1); -		break; case L'R': windowSearch(editString(&edit), -1); -		break; case L'S': windowSearch(editString(&edit), +1); -		break; case L'T': editFn(&edit, EditTranspose); -		break; case L'U': editFn(&edit, EditDeleteHead); -		break; case L'V': windowScroll(ScrollPage, -1); -		break; case L'W': editFn(&edit, EditDeletePrevWord); -		break; case L'Y': editFn(&edit, EditPaste); -	} -} - -static void keyStyle(wchar_t ch) { -	if (iswcntrl(ch)) ch = towlower(ch ^ L'@'); -	char buf[8] = {0}; -	enum Color color = Default; -	switch (ch) { -		break; case L'A': color = Gray; -		break; case L'B': color = Blue; -		break; case L'C': color = Cyan; -		break; case L'G': color = Green; -		break; case L'K': color = Black; -		break; case L'M': color = Magenta; -		break; case L'N': color = Brown; -		break; case L'O': color = Orange; -		break; case L'P': color = Pink; -		break; case L'R': color = Red; -		break; case L'W': color = White; -		break; case L'Y': color = Yellow; -		break; case L'b': buf[0] = B; -		break; case L'c': buf[0] = C; -		break; case L'i': buf[0] = I; -		break; case L'o': buf[0] = O; -		break; case L'r': buf[0] = R; -		break; case L's': { -			snprintf(buf, sizeof(buf), "%c%02d,%02d", C, Black, Black); -		} -		break; case L'u': buf[0] = U; -	} -	if (color != Default) { -		snprintf(buf, sizeof(buf), "%c%02d", C, color); -	} -	for (char *ch = buf; *ch; ++ch) { -		editInsert(&edit, *ch); -	} -} - -void uiRead(void) { -	if (hidden) { -		if (waiting) { -			uiShow(); -			flushinp(); -			waiting = false; -		} else { -			return; -		} -	} - -	wint_t ch; -	static bool paste, style, literal; -	for (int ret; ERR != (ret = wget_wch(input, &ch));) { -		bool spr = spoilerReveal; -		if (ret == KEY_CODE_YES && ch == KeyPasteOn) { -			paste = true; -		} else if (ret == KEY_CODE_YES && ch == KeyPasteOff) { -			paste = false; -		} else if (ret == KEY_CODE_YES && ch == KeyPasteManual) { -			paste ^= true; -		} else if (paste || literal) { -			editInsert(&edit, ch); -		} else if (ret == KEY_CODE_YES) { -			keyCode(ch); -		} else if (ch == (L'Z' ^ L'@')) { -			style = true; -			continue; -		} else if (style && ch == (L'V' ^ L'@')) { -			literal = true; -			continue; -		} else if (style) { -			keyStyle(ch); -		} else if (iswcntrl(ch)) { -			keyCtrl(ch); -		} else { -			editInsert(&edit, ch); -		} -		style = false; -		literal = false; -		if (spr) { -			spoilerReveal = false; -			windowUpdate(); -		} -	} -	uiUpdate(); -} -  static FILE *saveFile;  static const uint64_t Signatures[] = { @@ -273,7 +273,7 @@ void windowUpdate(void) {  void windowBare(void) {  	uiHide(); -	uiWait(); +	inputWait();  	const struct Window *window = windows[show];  	const struct Line *line = bufferHard(window->buffer, windowBottom(window)); @@ -426,7 +426,7 @@ void windowShow(uint num) {  	user = num;  	unmark(windows[show]);  	mainUpdate(); -	uiUpdate(); +	inputUpdate();  }  void windowAuto(void) { @@ -515,7 +515,7 @@ void windowToggleTime(void) {  	windows[show]->time ^= true;  	reflow(windows[show]);  	windowUpdate(); -	uiUpdate(); +	inputUpdate();  }  void windowToggleThresh(int n) {  | 
