Add UI "heat" for status/messages/pings
Bring back the beeps! Allow pings from notices. Also factor out dequoting of part/quit messages.
This commit is contained in:
		
							parent
							
								
									a38738c938
								
							
						
					
					
						commit
						38fc42f03d
					
				
							
								
								
									
										10
									
								
								chat.c
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								chat.c
									
									
									
									
									
								
							@ -60,7 +60,7 @@ static union {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void spawn(char *const argv[]) {
 | 
					void spawn(char *const argv[]) {
 | 
				
			||||||
	if (fds.pipe.events) {
 | 
						if (fds.pipe.events) {
 | 
				
			||||||
		uiLog(TAG_STATUS, L"spawn: existing pipe");
 | 
							uiLog(TAG_STATUS, UI_WARM, L"spawn: existing pipe");
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -93,7 +93,7 @@ static void pipeRead(void) {
 | 
				
			|||||||
	if (len) {
 | 
						if (len) {
 | 
				
			||||||
		buf[len] = '\0';
 | 
							buf[len] = '\0';
 | 
				
			||||||
		len = strcspn(buf, "\n");
 | 
							len = strcspn(buf, "\n");
 | 
				
			||||||
		uiFmt(TAG_STATUS, "spawn: %.*s", (int)len, buf);
 | 
							uiFmt(TAG_STATUS, UI_WARM, "spawn: %.*s", (int)len, buf);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		close(fds.pipe.fd);
 | 
							close(fds.pipe.fd);
 | 
				
			||||||
		fds.pipe.events = 0;
 | 
							fds.pipe.events = 0;
 | 
				
			||||||
@ -124,9 +124,9 @@ static void sigchld(int sig) {
 | 
				
			|||||||
	pid_t pid = wait(&status);
 | 
						pid_t pid = wait(&status);
 | 
				
			||||||
	if (pid < 0) err(EX_OSERR, "wait");
 | 
						if (pid < 0) err(EX_OSERR, "wait");
 | 
				
			||||||
	if (WIFEXITED(status) && WEXITSTATUS(status)) {
 | 
						if (WIFEXITED(status) && WEXITSTATUS(status)) {
 | 
				
			||||||
		uiFmt(TAG_STATUS, "spawn: exit %d", WEXITSTATUS(status));
 | 
							uiFmt(TAG_STATUS, UI_WARM, "spawn: exit %d", WEXITSTATUS(status));
 | 
				
			||||||
	} else if (WIFSIGNALED(status)) {
 | 
						} else if (WIFSIGNALED(status)) {
 | 
				
			||||||
		uiFmt(TAG_STATUS, "spawn: signal %d", WTERMSIG(status));
 | 
							uiFmt(TAG_STATUS, UI_WARM, "spawn: signal %d", WTERMSIG(status));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -182,7 +182,7 @@ int main(int argc, char *argv[]) {
 | 
				
			|||||||
	inputTab();
 | 
						inputTab();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uiInit();
 | 
						uiInit();
 | 
				
			||||||
	uiLog(TAG_STATUS, L"Traveling...");
 | 
						uiLog(TAG_STATUS, UI_WARM, L"Traveling...");
 | 
				
			||||||
	uiDraw();
 | 
						uiDraw();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fds.irc.fd = ircConnect(host, port, pass, webirc);
 | 
						fds.irc.fd = ircConnect(host, port, pass, webirc);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										17
									
								
								chat.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								chat.h
									
									
									
									
									
								
							@ -91,12 +91,19 @@ void uiHide(void);
 | 
				
			|||||||
void uiExit(void);
 | 
					void uiExit(void);
 | 
				
			||||||
void uiDraw(void);
 | 
					void uiDraw(void);
 | 
				
			||||||
void uiRead(void);
 | 
					void uiRead(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void uiViewTag(struct Tag tag);
 | 
					void uiViewTag(struct Tag tag);
 | 
				
			||||||
void uiViewNum(int num);
 | 
					void uiViewNum(int num);
 | 
				
			||||||
void uiCloseTag(struct Tag tag);
 | 
					void uiCloseTag(struct Tag tag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum UIHeat {
 | 
				
			||||||
 | 
						UI_COLD,
 | 
				
			||||||
 | 
						UI_WARM,
 | 
				
			||||||
 | 
						UI_HOT,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
void uiTopic(struct Tag tag, const char *topic);
 | 
					void uiTopic(struct Tag tag, const char *topic);
 | 
				
			||||||
void uiLog(struct Tag tag, const wchar_t *line);
 | 
					void uiLog(struct Tag tag, enum UIHeat heat, const wchar_t *line);
 | 
				
			||||||
void uiFmt(struct Tag tag, const wchar_t *format, ...);
 | 
					void uiFmt(struct Tag tag, enum UIHeat heat, const wchar_t *format, ...);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum TermMode {
 | 
					enum TermMode {
 | 
				
			||||||
	TERM_FOCUS,
 | 
						TERM_FOCUS,
 | 
				
			||||||
@ -155,10 +162,10 @@ int vaswprintf(wchar_t **ret, const wchar_t *format, va_list ap);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// HACK: clang won't check wchar_t *format strings.
 | 
					// HACK: clang won't check wchar_t *format strings.
 | 
				
			||||||
#ifdef NDEBUG
 | 
					#ifdef NDEBUG
 | 
				
			||||||
#define uiFmt(tag, format, ...) uiFmt(tag, L##format, __VA_ARGS__)
 | 
					#define uiFmt(tag, heat, format, ...) uiFmt(tag, heat, L##format, __VA_ARGS__)
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
#define uiFmt(tag, format, ...) do { \
 | 
					#define uiFmt(tag, heat, format, ...) do { \
 | 
				
			||||||
	snprintf(NULL, 0, format, __VA_ARGS__); \
 | 
						snprintf(NULL, 0, format, __VA_ARGS__); \
 | 
				
			||||||
	uiFmt(tag, L##format, __VA_ARGS__); \
 | 
						uiFmt(tag, heat, L##format, __VA_ARGS__); \
 | 
				
			||||||
} while(0)
 | 
					} while(0)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										127
									
								
								handle.c
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								handle.c
									
									
									
									
									
								
							@ -93,8 +93,7 @@ static bool isSelf(const char *nick, const char *user) {
 | 
				
			|||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool isPing(const char *nick, const char *user, const char *mesg) {
 | 
					static bool isPing(const char *mesg) {
 | 
				
			||||||
	if (isSelf(nick, user)) return false;
 | 
					 | 
				
			||||||
	size_t len = strlen(self.nick);
 | 
						size_t len = strlen(self.nick);
 | 
				
			||||||
	const char *match = mesg;
 | 
						const char *match = mesg;
 | 
				
			||||||
	while (NULL != (match = strcasestr(match, self.nick))) {
 | 
						while (NULL != (match = strcasestr(match, self.nick))) {
 | 
				
			||||||
@ -108,6 +107,13 @@ static bool isPing(const char *nick, const char *user, const char *mesg) {
 | 
				
			|||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static char *dequote(char *mesg) {
 | 
				
			||||||
 | 
						if (mesg[0] == '"') mesg = &mesg[1];
 | 
				
			||||||
 | 
						size_t len = strlen(mesg);
 | 
				
			||||||
 | 
						if (mesg[len - 1] == '"') mesg[len - 1] = '\0';
 | 
				
			||||||
 | 
						return mesg;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef void (*Handler)(char *prefix, char *params);
 | 
					typedef void (*Handler)(char *prefix, char *params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void handlePing(char *prefix, char *params) {
 | 
					static void handlePing(char *prefix, char *params) {
 | 
				
			||||||
@ -118,39 +124,46 @@ static void handlePing(char *prefix, char *params) {
 | 
				
			|||||||
static void handleReplyErroneousNickname(char *prefix, char *params) {
 | 
					static void handleReplyErroneousNickname(char *prefix, char *params) {
 | 
				
			||||||
	char *mesg;
 | 
						char *mesg;
 | 
				
			||||||
	shift(prefix, NULL, NULL, NULL, params, 3, 0, NULL, NULL, &mesg);
 | 
						shift(prefix, NULL, NULL, NULL, params, 3, 0, NULL, NULL, &mesg);
 | 
				
			||||||
	uiLog(TAG_STATUS, L"You can't use that name here");
 | 
						// FIXME: Better formatting.
 | 
				
			||||||
	uiFmt(TAG_STATUS, "Sheriff says, \"%s\"", mesg);
 | 
						uiLog(TAG_STATUS, UI_HOT, L"You can't use that name here");
 | 
				
			||||||
	uiLog(TAG_STATUS, L"Type /nick <name> to choose a new one");
 | 
						uiFmt(TAG_STATUS, UI_HOT, "Sheriff says, \"%s\"", mesg);
 | 
				
			||||||
 | 
						uiLog(TAG_STATUS, UI_HOT, L"Type /nick <name> to choose a new one");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void handleReplyWelcome(char *prefix, char *params) {
 | 
					static void handleReplyWelcome(char *prefix, char *params) {
 | 
				
			||||||
	char *nick;
 | 
						char *nick;
 | 
				
			||||||
	shift(prefix, NULL, NULL, NULL, params, 1, 0, &nick);
 | 
						shift(prefix, NULL, NULL, NULL, params, 1, 0, &nick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (strcmp(nick, self.nick)) selfNick(nick);
 | 
						if (strcmp(nick, self.nick)) selfNick(nick);
 | 
				
			||||||
	tabTouch(TAG_STATUS, self.nick);
 | 
					 | 
				
			||||||
	if (self.join) ircFmt("JOIN %s\r\n", self.join);
 | 
						if (self.join) ircFmt("JOIN %s\r\n", self.join);
 | 
				
			||||||
	uiLog(TAG_STATUS, L"You have arrived");
 | 
						tabTouch(TAG_STATUS, self.nick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uiLog(TAG_STATUS, UI_WARM, L"You have arrived");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void handleReplyMOTD(char *prefix, char *params) {
 | 
					static void handleReplyMOTD(char *prefix, char *params) {
 | 
				
			||||||
	char *mesg;
 | 
						char *mesg;
 | 
				
			||||||
	shift(prefix, NULL, NULL, NULL, params, 2, 0, NULL, &mesg);
 | 
						shift(prefix, NULL, NULL, NULL, params, 2, 0, NULL, &mesg);
 | 
				
			||||||
	if (mesg[0] == '-' && mesg[1] == ' ') mesg = &mesg[2];
 | 
						if (mesg[0] == '-' && mesg[1] == ' ') mesg = &mesg[2];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	urlScan(TAG_STATUS, mesg);
 | 
						urlScan(TAG_STATUS, mesg);
 | 
				
			||||||
	uiFmt(TAG_STATUS, "%s", mesg);
 | 
						uiFmt(TAG_STATUS, UI_COLD, "%s", mesg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void handleJoin(char *prefix, char *params) {
 | 
					static void handleJoin(char *prefix, char *params) {
 | 
				
			||||||
	char *nick, *user, *chan;
 | 
						char *nick, *user, *chan;
 | 
				
			||||||
	shift(prefix, &nick, &user, NULL, params, 1, 0, &chan);
 | 
						shift(prefix, &nick, &user, NULL, params, 1, 0, &chan);
 | 
				
			||||||
	struct Tag tag = tagFor(chan);
 | 
						struct Tag tag = tagFor(chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (isSelf(nick, user)) {
 | 
						if (isSelf(nick, user)) {
 | 
				
			||||||
		tabTouch(TAG_NONE, chan);
 | 
							tabTouch(TAG_NONE, chan);
 | 
				
			||||||
		uiViewTag(tag);
 | 
							uiViewTag(tag);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tabTouch(tag, nick);
 | 
						tabTouch(tag, nick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uiFmt(
 | 
						uiFmt(
 | 
				
			||||||
		tag, "\3%d%s\3 arrives in \3%d%s\3",
 | 
							tag, UI_COLD,
 | 
				
			||||||
 | 
							"\3%d%s\3 arrives in \3%d%s\3",
 | 
				
			||||||
		color(user), nick, color(chan), chan
 | 
							color(user), nick, color(chan), chan
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -159,16 +172,24 @@ static void handlePart(char *prefix, char *params) {
 | 
				
			|||||||
	char *nick, *user, *chan, *mesg;
 | 
						char *nick, *user, *chan, *mesg;
 | 
				
			||||||
	shift(prefix, &nick, &user, NULL, params, 1, 1, &chan, &mesg);
 | 
						shift(prefix, &nick, &user, NULL, params, 1, 1, &chan, &mesg);
 | 
				
			||||||
	struct Tag tag = tagFor(chan);
 | 
						struct Tag tag = tagFor(chan);
 | 
				
			||||||
	(void)(isSelf(nick, user) ? tabClear(tag) : tabRemove(tag, nick));
 | 
					
 | 
				
			||||||
 | 
						if (isSelf(nick, user)) {
 | 
				
			||||||
 | 
							tabClear(tag);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							tabRemove(tag, nick);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (mesg) {
 | 
						if (mesg) {
 | 
				
			||||||
		urlScan(tag, mesg);
 | 
							urlScan(tag, mesg);
 | 
				
			||||||
		uiFmt(
 | 
							uiFmt(
 | 
				
			||||||
			tag, "\3%d%s\3 leaves \3%d%s\3, \"%s\"",
 | 
								tag, UI_COLD,
 | 
				
			||||||
 | 
								"\3%d%s\3 leaves \3%d%s\3, \"%s\"",
 | 
				
			||||||
			color(user), nick, color(chan), chan, mesg
 | 
								color(user), nick, color(chan), chan, mesg
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		uiFmt(
 | 
							uiFmt(
 | 
				
			||||||
			tag, "\3%d%s\3 leaves \3%d%s\3",
 | 
								tag, UI_COLD,
 | 
				
			||||||
 | 
								"\3%d%s\3 leaves \3%d%s\3",
 | 
				
			||||||
			color(user), nick, color(chan), chan
 | 
								color(user), nick, color(chan), chan
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -178,16 +199,26 @@ static void handleKick(char *prefix, char *params) {
 | 
				
			|||||||
	char *nick, *user, *chan, *kick, *mesg;
 | 
						char *nick, *user, *chan, *kick, *mesg;
 | 
				
			||||||
	shift(prefix, &nick, &user, NULL, params, 2, 1, &chan, &kick, &mesg);
 | 
						shift(prefix, &nick, &user, NULL, params, 2, 1, &chan, &kick, &mesg);
 | 
				
			||||||
	struct Tag tag = tagFor(chan);
 | 
						struct Tag tag = tagFor(chan);
 | 
				
			||||||
	(void)(isSelf(nick, user) ? tabClear(tag) : tabRemove(tag, nick));
 | 
						bool kicked = !strcmp(kick, self.nick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (kicked) {
 | 
				
			||||||
 | 
							tabClear(tag);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							tabRemove(tag, kick);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (mesg) {
 | 
						if (mesg) {
 | 
				
			||||||
		urlScan(tag, mesg);
 | 
							urlScan(tag, mesg);
 | 
				
			||||||
		uiFmt(
 | 
							uiFmt(
 | 
				
			||||||
			tag, "\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3, \"%s\"",
 | 
								tag, (kicked ? UI_HOT : UI_COLD),
 | 
				
			||||||
			color(user), nick, color(kick), kick, color(chan), chan, mesg
 | 
								"\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3, \"%s\"",
 | 
				
			||||||
 | 
								color(user), nick, color(kick), kick, color(chan), chan,
 | 
				
			||||||
 | 
								dequote(mesg)
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		uiFmt(
 | 
							uiFmt(
 | 
				
			||||||
			tag, "\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3",
 | 
								tag, (kicked ? UI_HOT : UI_COLD),
 | 
				
			||||||
 | 
								"\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3",
 | 
				
			||||||
			color(user), nick, color(kick), kick, color(chan), chan
 | 
								color(user), nick, color(kick), kick, color(chan), chan
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -196,18 +227,20 @@ static void handleKick(char *prefix, char *params) {
 | 
				
			|||||||
static void handleQuit(char *prefix, char *params) {
 | 
					static void handleQuit(char *prefix, char *params) {
 | 
				
			||||||
	char *nick, *user, *mesg;
 | 
						char *nick, *user, *mesg;
 | 
				
			||||||
	shift(prefix, &nick, &user, NULL, params, 0, 1, &mesg);
 | 
						shift(prefix, &nick, &user, NULL, params, 0, 1, &mesg);
 | 
				
			||||||
	char *quot = (mesg && mesg[0] == '"') ? "" : "\"";
 | 
					
 | 
				
			||||||
	struct Tag tag;
 | 
						struct Tag tag;
 | 
				
			||||||
	while (TAG_NONE.id != (tag = tabTag(nick)).id) {
 | 
						while (TAG_NONE.id != (tag = tabTag(nick)).id) {
 | 
				
			||||||
		tabRemove(tag, nick);
 | 
							tabRemove(tag, nick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (mesg) {
 | 
							if (mesg) {
 | 
				
			||||||
			urlScan(tag, mesg);
 | 
								urlScan(tag, mesg);
 | 
				
			||||||
			uiFmt(
 | 
								uiFmt(
 | 
				
			||||||
				tag, "\3%d%s\3 leaves, %s%s%s",
 | 
									tag, UI_COLD,
 | 
				
			||||||
				color(user), nick, quot, mesg, quot
 | 
									"\3%d%s\3 leaves, \"%s\"",
 | 
				
			||||||
 | 
									color(user), nick, dequote(mesg)
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			uiFmt(tag, "\3%d%s\3 leaves", color(user), nick);
 | 
								uiFmt(tag, UI_COLD, "\3%d%s\3 leaves", color(user), nick);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -216,10 +249,12 @@ static void handleReplyTopic(char *prefix, char *params) {
 | 
				
			|||||||
	char *chan, *topic;
 | 
						char *chan, *topic;
 | 
				
			||||||
	shift(prefix, NULL, NULL, NULL, params, 3, 0, NULL, &chan, &topic);
 | 
						shift(prefix, NULL, NULL, NULL, params, 3, 0, NULL, &chan, &topic);
 | 
				
			||||||
	struct Tag tag = tagFor(chan);
 | 
						struct Tag tag = tagFor(chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	urlScan(tag, topic);
 | 
						urlScan(tag, topic);
 | 
				
			||||||
	uiTopic(tag, topic);
 | 
						uiTopic(tag, topic);
 | 
				
			||||||
	uiFmt(
 | 
						uiFmt(
 | 
				
			||||||
		tag, "The sign in \3%d%s\3 reads, \"%s\"",
 | 
							tag, UI_COLD,
 | 
				
			||||||
 | 
							"The sign in \3%d%s\3 reads, \"%s\"",
 | 
				
			||||||
		color(chan), chan, topic
 | 
							color(chan), chan, topic
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -228,11 +263,14 @@ static void handleTopic(char *prefix, char *params) {
 | 
				
			|||||||
	char *nick, *user, *chan, *topic;
 | 
						char *nick, *user, *chan, *topic;
 | 
				
			||||||
	shift(prefix, &nick, &user, NULL, params, 2, 0, &chan, &topic);
 | 
						shift(prefix, &nick, &user, NULL, params, 2, 0, &chan, &topic);
 | 
				
			||||||
	struct Tag tag = tagFor(chan);
 | 
						struct Tag tag = tagFor(chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!isSelf(nick, user)) tabTouch(tag, nick);
 | 
						if (!isSelf(nick, user)) tabTouch(tag, nick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	urlScan(tag, topic);
 | 
						urlScan(tag, topic);
 | 
				
			||||||
	uiTopic(tag, topic);
 | 
						uiTopic(tag, topic);
 | 
				
			||||||
	uiFmt(
 | 
						uiFmt(
 | 
				
			||||||
		tag, "\3%d%s\3 places a new sign in \3%d%s\3, \"%s\"",
 | 
							tag, UI_COLD,
 | 
				
			||||||
 | 
							"\3%d%s\3 places a new sign in \3%d%s\3, \"%s\"",
 | 
				
			||||||
		color(user), nick, color(chan), chan, topic
 | 
							color(user), nick, color(chan), chan, topic
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -256,7 +294,10 @@ static void handleReplyWho(char *prefix, char *params) {
 | 
				
			|||||||
		params, 6, 0, NULL, &chan, &user, NULL, NULL, &nick
 | 
							params, 6, 0, NULL, &chan, &user, NULL, NULL, &nick
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
	struct Tag tag = tagFor(chan);
 | 
						struct Tag tag = tagFor(chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// FIXME: Don't clobber order if they already exist.
 | 
				
			||||||
	tabTouch(tag, nick);
 | 
						tabTouch(tag, nick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t cap = sizeof(who.buf) - who.len;
 | 
						size_t cap = sizeof(who.buf) - who.len;
 | 
				
			||||||
	int len = snprintf(
 | 
						int len = snprintf(
 | 
				
			||||||
		&who.buf[who.len], cap,
 | 
							&who.buf[who.len], cap,
 | 
				
			||||||
@ -270,8 +311,10 @@ static void handleReplyEndOfWho(char *prefix, char *params) {
 | 
				
			|||||||
	char *chan;
 | 
						char *chan;
 | 
				
			||||||
	shift(prefix, NULL, NULL, NULL, params, 2, 0, NULL, &chan);
 | 
						shift(prefix, NULL, NULL, NULL, params, 2, 0, NULL, &chan);
 | 
				
			||||||
	struct Tag tag = tagFor(chan);
 | 
						struct Tag tag = tagFor(chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uiFmt(
 | 
						uiFmt(
 | 
				
			||||||
		tag, "In \3%d%s\3 are %s",
 | 
							tag, UI_COLD,
 | 
				
			||||||
 | 
							"In \3%d%s\3 are %s",
 | 
				
			||||||
		color(chan), chan, who.buf
 | 
							color(chan), chan, who.buf
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
	who.len = 0;
 | 
						who.len = 0;
 | 
				
			||||||
@ -280,12 +323,16 @@ static void handleReplyEndOfWho(char *prefix, char *params) {
 | 
				
			|||||||
static void handleNick(char *prefix, char *params) {
 | 
					static void handleNick(char *prefix, char *params) {
 | 
				
			||||||
	char *prev, *user, *next;
 | 
						char *prev, *user, *next;
 | 
				
			||||||
	shift(prefix, &prev, &user, NULL, params, 1, 0, &next);
 | 
						shift(prefix, &prev, &user, NULL, params, 1, 0, &next);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (isSelf(prev, user)) selfNick(next);
 | 
						if (isSelf(prev, user)) selfNick(next);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct Tag tag;
 | 
						struct Tag tag;
 | 
				
			||||||
	while (TAG_NONE.id != (tag = tabTag(prev)).id) {
 | 
						while (TAG_NONE.id != (tag = tabTag(prev)).id) {
 | 
				
			||||||
		tabReplace(tag, prev, next);
 | 
							tabReplace(tag, prev, next);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		uiFmt(
 | 
							uiFmt(
 | 
				
			||||||
			tag, "\3%d%s\3 is now known as \3%d%s\3",
 | 
								tag, UI_COLD,
 | 
				
			||||||
 | 
								"\3%d%s\3 is now known as \3%d%s\3",
 | 
				
			||||||
			color(user), prev, color(user), next
 | 
								color(user), prev, color(user), next
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -296,11 +343,15 @@ static void handleCTCP(struct Tag tag, char *nick, char *user, char *mesg) {
 | 
				
			|||||||
	char *ctcp = strsep(&mesg, " ");
 | 
						char *ctcp = strsep(&mesg, " ");
 | 
				
			||||||
	char *params = strsep(&mesg, "\1");
 | 
						char *params = strsep(&mesg, "\1");
 | 
				
			||||||
	if (strcmp(ctcp, "ACTION")) return;
 | 
						if (strcmp(ctcp, "ACTION")) return;
 | 
				
			||||||
	if (!isSelf(nick, user)) tabTouch(tag, nick);
 | 
					
 | 
				
			||||||
 | 
						bool self = isSelf(nick, user);
 | 
				
			||||||
 | 
						if (!self) tabTouch(tag, nick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	urlScan(tag, params);
 | 
						urlScan(tag, params);
 | 
				
			||||||
	bool ping = isPing(nick, user, params);
 | 
						bool ping = !self && isPing(params);
 | 
				
			||||||
	uiFmt(
 | 
						uiFmt(
 | 
				
			||||||
		tag, "%c\3%d* %s\17 %s",
 | 
							tag, (ping ? UI_HOT : UI_WARM),
 | 
				
			||||||
 | 
							"%c\3%d* %s\17 %s",
 | 
				
			||||||
		ping["\17\26"], color(user), nick, params
 | 
							ping["\17\26"], color(user), nick, params
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -313,12 +364,15 @@ static void handlePrivmsg(char *prefix, char *params) {
 | 
				
			|||||||
		handleCTCP(tag, nick, user, mesg);
 | 
							handleCTCP(tag, nick, user, mesg);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (!isSelf(nick, user)) tabTouch(tag, nick);
 | 
					
 | 
				
			||||||
	urlScan(tag, mesg);
 | 
					 | 
				
			||||||
	bool self = isSelf(nick, user);
 | 
						bool self = isSelf(nick, user);
 | 
				
			||||||
	bool ping = isPing(nick, user, mesg);
 | 
						if (!self) tabTouch(tag, nick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						urlScan(tag, mesg);
 | 
				
			||||||
 | 
						bool ping = !self && isPing(mesg);
 | 
				
			||||||
	uiFmt(
 | 
						uiFmt(
 | 
				
			||||||
		tag, "%c\3%d%c%s%c\17 %s",
 | 
							tag, (ping ? UI_HOT : UI_WARM),
 | 
				
			||||||
 | 
							"%c\3%d%c%s%c\17 %s",
 | 
				
			||||||
		ping["\17\26"], color(user), self["<("], nick, self[">)"], mesg
 | 
							ping["\17\26"], color(user), self["<("], nick, self[">)"], mesg
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -328,11 +382,16 @@ static void handleNotice(char *prefix, char *params) {
 | 
				
			|||||||
	shift(prefix, &nick, &user, NULL, params, 2, 0, &chan, &mesg);
 | 
						shift(prefix, &nick, &user, NULL, params, 2, 0, &chan, &mesg);
 | 
				
			||||||
	struct Tag tag = TAG_STATUS;
 | 
						struct Tag tag = TAG_STATUS;
 | 
				
			||||||
	if (user) tag = (strcmp(chan, self.nick) ? tagFor(chan) : tagFor(nick));
 | 
						if (user) tag = (strcmp(chan, self.nick) ? tagFor(chan) : tagFor(nick));
 | 
				
			||||||
	if (!isSelf(nick, user)) tabTouch(tag, nick);
 | 
					
 | 
				
			||||||
 | 
						bool self = isSelf(nick, user);
 | 
				
			||||||
 | 
						if (!self) tabTouch(tag, nick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	urlScan(tag, mesg);
 | 
						urlScan(tag, mesg);
 | 
				
			||||||
 | 
						bool ping = !self && isPing(mesg);
 | 
				
			||||||
	uiFmt(
 | 
						uiFmt(
 | 
				
			||||||
		tag, "\3%d-%s-\3 %s",
 | 
							tag, (ping ? UI_HOT : UI_WARM),
 | 
				
			||||||
		color(user), nick, mesg
 | 
							"%c\3%d-%s-\17 %s",
 | 
				
			||||||
 | 
							ping["\17\26"], color(user), nick, mesg
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								input.c
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								input.c
									
									
									
									
									
								
							@ -40,7 +40,7 @@ static void privmsg(struct Tag tag, bool action, const char *mesg) {
 | 
				
			|||||||
static char *param(const char *command, char **params, const char *name) {
 | 
					static char *param(const char *command, char **params, const char *name) {
 | 
				
			||||||
	char *param = strsep(params, " ");
 | 
						char *param = strsep(params, " ");
 | 
				
			||||||
	if (param) return param;
 | 
						if (param) return param;
 | 
				
			||||||
	uiFmt(TAG_STATUS, "%s requires a %s", command, name);
 | 
						uiFmt(TAG_STATUS, UI_WARM, "%s requires a %s", command, name);
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -123,7 +123,7 @@ static void inputView(struct Tag tag, char *params) {
 | 
				
			|||||||
		if (tag.id != TAG_NONE.id) {
 | 
							if (tag.id != TAG_NONE.id) {
 | 
				
			||||||
			uiViewTag(tag);
 | 
								uiViewTag(tag);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			uiFmt(TAG_STATUS, "No view for %s", view);
 | 
								uiFmt(TAG_STATUS, UI_WARM, "No view for %s", view);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -178,7 +178,7 @@ void input(struct Tag tag, char *input) {
 | 
				
			|||||||
		COMMANDS[i].handler(tag, input);
 | 
							COMMANDS[i].handler(tag, input);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	uiFmt(TAG_STATUS, "%s isn't a recognized command", command);
 | 
						uiFmt(TAG_STATUS, UI_WARM, "%s isn't a recognized command", command);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void inputTab(void) {
 | 
					void inputTab(void) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										14
									
								
								irc.c
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								irc.c
									
									
									
									
									
								
							@ -106,7 +106,12 @@ void ircFmt(const char *format, ...) {
 | 
				
			|||||||
	int len = vasprintf(&buf, format, ap);
 | 
						int len = vasprintf(&buf, format, ap);
 | 
				
			||||||
	va_end(ap);
 | 
						va_end(ap);
 | 
				
			||||||
	if (!buf) err(EX_OSERR, "vasprintf");
 | 
						if (!buf) err(EX_OSERR, "vasprintf");
 | 
				
			||||||
	if (self.verbose) uiFmt(TAG_VERBOSE, "\3%d<<<\3 %.*s", IRC_WHITE, len - 2, buf);
 | 
						if (self.verbose) {
 | 
				
			||||||
 | 
							uiFmt(
 | 
				
			||||||
 | 
								TAG_VERBOSE, UI_COLD,
 | 
				
			||||||
 | 
								"\3%d<<<\3 %.*s", IRC_WHITE, len - 2, buf
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	ircWrite(buf, len);
 | 
						ircWrite(buf, len);
 | 
				
			||||||
	free(buf);
 | 
						free(buf);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -126,7 +131,12 @@ void ircRead(void) {
 | 
				
			|||||||
	char *crlf, *line = buf;
 | 
						char *crlf, *line = buf;
 | 
				
			||||||
	while ((crlf = strnstr(line, "\r\n", &buf[len] - line))) {
 | 
						while ((crlf = strnstr(line, "\r\n", &buf[len] - line))) {
 | 
				
			||||||
		crlf[0] = '\0';
 | 
							crlf[0] = '\0';
 | 
				
			||||||
		if (self.verbose) uiFmt(TAG_VERBOSE, "\3%d>>>\3 %s", IRC_GRAY, line);
 | 
							if (self.verbose) {
 | 
				
			||||||
 | 
								uiFmt(
 | 
				
			||||||
 | 
									TAG_VERBOSE, UI_COLD,
 | 
				
			||||||
 | 
									"\3%d>>>\3 %s", IRC_GRAY, line
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		handle(line);
 | 
							handle(line);
 | 
				
			||||||
		line = &crlf[2];
 | 
							line = &crlf[2];
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										38
									
								
								ui.c
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								ui.c
									
									
									
									
									
								
							@ -75,6 +75,8 @@ struct View {
 | 
				
			|||||||
	WINDOW *topic;
 | 
						WINDOW *topic;
 | 
				
			||||||
	WINDOW *log;
 | 
						WINDOW *log;
 | 
				
			||||||
	int scroll;
 | 
						int scroll;
 | 
				
			||||||
 | 
						int unread;
 | 
				
			||||||
 | 
						bool hot;
 | 
				
			||||||
	bool mark;
 | 
						bool mark;
 | 
				
			||||||
	struct View *prev;
 | 
						struct View *prev;
 | 
				
			||||||
	struct View *next;
 | 
						struct View *next;
 | 
				
			||||||
@ -145,6 +147,15 @@ static void viewClose(struct View *view) {
 | 
				
			|||||||
	free(view);
 | 
						free(view);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void viewMark(struct View *view) {
 | 
				
			||||||
 | 
						view->mark = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					static void viewUnmark(struct View *view) {
 | 
				
			||||||
 | 
						view->unread = 0;
 | 
				
			||||||
 | 
						view->hot = false;
 | 
				
			||||||
 | 
						view->mark = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct {
 | 
					static struct {
 | 
				
			||||||
	bool hide;
 | 
						bool hide;
 | 
				
			||||||
	struct View *view;
 | 
						struct View *view;
 | 
				
			||||||
@ -233,8 +244,8 @@ static void uiView(struct View *view) {
 | 
				
			|||||||
	termTitle(view->tag.name);
 | 
						termTitle(view->tag.name);
 | 
				
			||||||
	if (view->topic) touchwin(view->topic);
 | 
						if (view->topic) touchwin(view->topic);
 | 
				
			||||||
	touchwin(view->log);
 | 
						touchwin(view->log);
 | 
				
			||||||
	ui.view->mark = true;
 | 
						viewMark(ui.view);
 | 
				
			||||||
	view->mark = false;
 | 
						viewUnmark(view);
 | 
				
			||||||
	ui.view = view;
 | 
						ui.view = view;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -383,37 +394,40 @@ void uiTopic(struct Tag tag, const char *topic) {
 | 
				
			|||||||
	free(wcs);
 | 
						free(wcs);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void uiLog(struct Tag tag, const wchar_t *line) {
 | 
					void uiLog(struct Tag tag, enum UIHeat heat, const wchar_t *line) {
 | 
				
			||||||
	struct View *view = viewTag(tag);
 | 
						struct View *view = viewTag(tag);
 | 
				
			||||||
	waddch(view->log, '\n');
 | 
						waddch(view->log, '\n');
 | 
				
			||||||
	if (view->mark) {
 | 
						if (view->mark && heat > UI_COLD) {
 | 
				
			||||||
		waddch(view->log, '\n');
 | 
							if (!view->unread++) waddch(view->log, '\n');
 | 
				
			||||||
		view->mark = false;
 | 
							if (heat > UI_WARM) {
 | 
				
			||||||
 | 
								view->hot = true;
 | 
				
			||||||
 | 
								beep(); // TODO: Notification.
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	addIRC(view->log, line);
 | 
						addIRC(view->log, line);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void uiFmt(struct Tag tag, const wchar_t *format, ...) {
 | 
					void uiFmt(struct Tag tag, enum UIHeat heat, const wchar_t *format, ...) {
 | 
				
			||||||
	wchar_t *wcs;
 | 
						wchar_t *wcs;
 | 
				
			||||||
	va_list ap;
 | 
						va_list ap;
 | 
				
			||||||
	va_start(ap, format);
 | 
						va_start(ap, format);
 | 
				
			||||||
	vaswprintf(&wcs, format, ap);
 | 
						vaswprintf(&wcs, format, ap);
 | 
				
			||||||
	va_end(ap);
 | 
						va_end(ap);
 | 
				
			||||||
	if (!wcs) err(EX_OSERR, "vaswprintf");
 | 
						if (!wcs) err(EX_OSERR, "vaswprintf");
 | 
				
			||||||
	uiLog(tag, wcs);
 | 
						uiLog(tag, heat, wcs);
 | 
				
			||||||
	free(wcs);
 | 
						free(wcs);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void logScrollUp(int lines) {
 | 
					static void logScrollUp(int lines) {
 | 
				
			||||||
	int height = logHeight(ui.view);
 | 
						int height = logHeight(ui.view);
 | 
				
			||||||
	if (ui.view->scroll == height) return;
 | 
						if (ui.view->scroll == height) return;
 | 
				
			||||||
	if (ui.view->scroll == LOG_LINES) ui.view->mark = true;
 | 
						if (ui.view->scroll == LOG_LINES) viewMark(ui.view);
 | 
				
			||||||
	ui.view->scroll = MAX(ui.view->scroll - lines, height);
 | 
						ui.view->scroll = MAX(ui.view->scroll - lines, height);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
static void logScrollDown(int lines) {
 | 
					static void logScrollDown(int lines) {
 | 
				
			||||||
	if (ui.view->scroll == LOG_LINES) return;
 | 
						if (ui.view->scroll == LOG_LINES) return;
 | 
				
			||||||
	ui.view->scroll = MIN(ui.view->scroll + lines, LOG_LINES);
 | 
						ui.view->scroll = MIN(ui.view->scroll + lines, LOG_LINES);
 | 
				
			||||||
	if (ui.view->scroll == LOG_LINES) ui.view->mark = false;
 | 
						if (ui.view->scroll == LOG_LINES) viewUnmark(ui.view);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
static void logPageUp(void) {
 | 
					static void logPageUp(void) {
 | 
				
			||||||
	logScrollUp(logHeight(ui.view) / 2);
 | 
						logScrollUp(logHeight(ui.view) / 2);
 | 
				
			||||||
@ -426,8 +440,8 @@ static bool keyChar(wchar_t ch) {
 | 
				
			|||||||
	if (iswascii(ch)) {
 | 
						if (iswascii(ch)) {
 | 
				
			||||||
		enum TermEvent event = termEvent((char)ch);
 | 
							enum TermEvent event = termEvent((char)ch);
 | 
				
			||||||
		switch (event) {
 | 
							switch (event) {
 | 
				
			||||||
			break; case TERM_FOCUS_IN:  ui.view->mark = false;
 | 
								break; case TERM_FOCUS_IN:  viewUnmark(ui.view);
 | 
				
			||||||
			break; case TERM_FOCUS_OUT: ui.view->mark = true;
 | 
								break; case TERM_FOCUS_OUT: viewMark(ui.view);
 | 
				
			||||||
			break; default: {}
 | 
								break; default: {}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (event) return false;
 | 
							if (event) return false;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user