Browse Source

Reorganize UI code for the umpteenth time

It's actually in a good state now, I think.
Causal Agent 3 months ago
parent
commit
e20c64d5d7
Signed by: Curtis McEnroe <june@causal.agency> GPG Key ID: CEA2F97ADCFCD77C
6 changed files with 296 additions and 315 deletions
  1. 3
    0
      chat.c
  2. 5
    5
      chat.h
  3. 3
    4
      handle.c
  4. 5
    13
      input.c
  5. 1
    2
      irc.c
  6. 279
    291
      ui.c

+ 3
- 0
chat.c View File

@@ -17,6 +17,7 @@
17 17
 #define _WITH_GETLINE
18 18
 
19 19
 #include <err.h>
20
+#include <locale.h>
20 21
 #include <stdio.h>
21 22
 #include <stdlib.h>
22 23
 #include <string.h>
@@ -49,6 +50,8 @@ static char *prompt(const char *prompt) {
49 50
 }
50 51
 
51 52
 int main(int argc, char *argv[]) {
53
+	setlocale(LC_CTYPE, "");
54
+
52 55
 	int opt;
53 56
 	while (0 < (opt = getopt(argc, argv, "NW:a:h:j:l:n:p:r:u:vw:"))) {
54 57
 		switch (opt) {

+ 5
- 5
chat.h View File

@@ -116,13 +116,13 @@ void ircQuit(const char *mesg);
116 116
 void uiInit(void);
117 117
 void uiShow(void);
118 118
 void uiHide(void);
119
-void uiExit(void);
120 119
 void uiDraw(void);
121 120
 void uiRead(void);
122
-void uiPrompt(void);
121
+void uiExit(int status);
123 122
 
124
-void uiWindowTag(struct Tag tag);
125
-void uiWindowNum(int num);
123
+void uiPrompt(bool nickChanged);
124
+void uiShowTag(struct Tag tag);
125
+void uiShowNum(int num);
126 126
 void uiCloseTag(struct Tag tag);
127 127
 
128 128
 enum UIHeat {
@@ -130,7 +130,7 @@ enum UIHeat {
130 130
 	UIWarm,
131 131
 	UIHot,
132 132
 };
133
-void uiLog(struct Tag tag, enum UIHeat heat, const wchar_t *line);
133
+void uiLog(struct Tag tag, enum UIHeat heat, const wchar_t *str);
134 134
 void uiFmt(struct Tag tag, enum UIHeat heat, const wchar_t *format, ...);
135 135
 
136 136
 enum TermMode {

+ 3
- 4
handle.c View File

@@ -98,8 +98,7 @@ static void handleError(char *prefix, char *params) {
98 98
 	char *mesg;
99 99
 	parse(prefix, NULL, NULL, NULL, params, 1, 0, &mesg);
100 100
 	if (self.quit) {
101
-		uiExit();
102
-		exit(EX_OK);
101
+		uiExit(EX_OK);
103 102
 	} else {
104 103
 		errx(EX_PROTOCOL, "%s", mesg);
105 104
 	}
@@ -219,7 +218,7 @@ static void handleJoin(char *prefix, char *params) {
219 218
 
220 219
 	if (!strcmp(nick, self.nick)) {
221 220
 		tabTouch(TagNone, chan);
222
-		uiWindowTag(tag);
221
+		uiShowTag(tag);
223 222
 		logReplay(tag);
224 223
 	}
225 224
 	tabTouch(tag, nick);
@@ -404,7 +403,7 @@ static void handleNick(char *prefix, char *params) {
404 403
 		free(self.nick);
405 404
 		self.nick = strdup(next);
406 405
 		if (!self.nick) err(EX_OSERR, "strdup");
407
-		uiPrompt();
406
+		uiPrompt(true);
408 407
 	}
409 408
 
410 409
 	struct Tag tag;

+ 5
- 13
input.c View File

@@ -80,11 +80,7 @@ static void inputJoin(struct Tag tag, char *params) {
80 80
 }
81 81
 
82 82
 static void inputPart(struct Tag tag, char *params) {
83
-	if (params) {
84
-		ircFmt("PART %s :%s\r\n", tag.name, params);
85
-	} else {
86
-		ircFmt("PART %s :Goodbye\r\n", tag.name);
87
-	}
83
+	ircFmt("PART %s :%s\r\n", tag.name, params ? params : "Goodbye");
88 84
 }
89 85
 
90 86
 static void inputQuery(struct Tag tag, char *params) {
@@ -92,7 +88,7 @@ static void inputQuery(struct Tag tag, char *params) {
92 88
 	char *nick = param(tag, "/query", &params, "nick");
93 89
 	if (!nick) return;
94 90
 	tabTouch(TagNone, nick);
95
-	uiWindowTag(tagFor(nick));
91
+	uiShowTag(tagFor(nick));
96 92
 	logReplay(tagFor(nick));
97 93
 }
98 94
 
@@ -118,11 +114,7 @@ static void inputTopic(struct Tag tag, char *params) {
118 114
 
119 115
 static void inputQuit(struct Tag tag, char *params) {
120 116
 	(void)tag;
121
-	if (params) {
122
-		ircQuit(params);
123
-	} else {
124
-		ircQuit("Goodbye");
125
-	}
117
+	ircQuit(params ? params : "Goodbye");
126 118
 }
127 119
 
128 120
 static void inputURL(struct Tag tag, char *params) {
@@ -145,11 +137,11 @@ static void inputWindow(struct Tag tag, char *params) {
145 137
 	if (!name) return;
146 138
 	int num = strtol(name, &name, 0);
147 139
 	if (!name[0]) {
148
-		uiWindowNum(num);
140
+		uiShowNum(num);
149 141
 	} else {
150 142
 		struct Tag tag = tagFind(name);
151 143
 		if (tag.id != TagNone.id) {
152
-			uiWindowTag(tag);
144
+			uiShowTag(tag);
153 145
 		} else {
154 146
 			uiFmt(tag, UIHot, "No window for %s", name);
155 147
 		}

+ 1
- 2
irc.c View File

@@ -133,8 +133,7 @@ retry:
133 133
 	if (read < 0) errx(EX_IOERR, "tls_read: %s", tls_error(client));
134 134
 	if (!read) {
135 135
 		if (!self.quit) errx(EX_PROTOCOL, "unexpected eof");
136
-		uiExit();
137
-		exit(EX_OK);
136
+		uiExit(EX_OK);
138 137
 	}
139 138
 	len += read;
140 139
 

+ 279
- 291
ui.c View File

@@ -1,4 +1,4 @@
1
-/* Copyright (C) 2018  Curtis McEnroe <june@causal.agency>
1
+/* Copyright (C) 2018, 2019  C. McEnroe <june@causal.agency>
2 2
  *
3 3
  * This program is free software: you can redistribute it and/or modify
4 4
  * it under the terms of the GNU Affero General Public License as published by
@@ -18,7 +18,6 @@
18 18
 
19 19
 #include <curses.h>
20 20
 #include <err.h>
21
-#include <locale.h>
22 21
 #include <stdarg.h>
23 22
 #include <stdbool.h>
24 23
 #include <stdlib.h>
@@ -28,119 +27,215 @@
28 27
 #include <wctype.h>
29 28
 
30 29
 #ifndef A_ITALIC
31
-#define A_ITALIC A_NORMAL
30
+#define A_ITALIC A_UNDERLINE
32 31
 #endif
33 32
 
34 33
 #include "chat.h"
35 34
 #undef uiFmt
36 35
 
36
+#define CTRL(ch) ((ch) & 037)
37
+enum { Esc = L'\33', Del = L'\177' };
38
+
39
+static const int LogLines = 512;
40
+
41
+static int lastLine(void) {
42
+	return LINES - 1;
43
+}
44
+static int lastCol(void) {
45
+	return COLS - 1;
46
+}
47
+static int logHeight(void) {
48
+	return LINES - 2;
49
+}
50
+
51
+struct Window {
52
+	struct Tag tag;
53
+	WINDOW *log;
54
+	bool hot;
55
+	bool mark;
56
+	int scroll;
57
+	uint unread;
58
+	struct Window *prev;
59
+	struct Window *next;
60
+};
61
+
62
+static struct {
63
+	struct Window *active;
64
+	struct Window *head;
65
+	struct Window *tail;
66
+	struct Window *tag[TagsLen];
67
+} windows;
68
+
69
+static void windowAppend(struct Window *win) {
70
+	if (windows.tail) windows.tail->next = win;
71
+	win->prev = windows.tail;
72
+	win->next = NULL;
73
+	windows.tail = win;
74
+	if (!windows.head) windows.head = win;
75
+	windows.tag[win->tag.id] = win;
76
+}
77
+
78
+static void windowRemove(struct Window *win) {
79
+	windows.tag[win->tag.id] = NULL;
80
+	if (win->prev) win->prev->next = win->next;
81
+	if (win->next) win->next->prev = win->prev;
82
+	if (windows.head == win) windows.head = win->next;
83
+	if (windows.tail == win) windows.tail = win->prev;
84
+}
85
+
86
+static struct Window *windowFor(struct Tag tag) {
87
+	struct Window *win = windows.tag[tag.id];
88
+	if (win) return win;
89
+
90
+	win = calloc(1, sizeof(*win));
91
+	if (!win) err(EX_OSERR, "calloc");
92
+
93
+	win->tag = tag;
94
+	win->mark = true;
95
+	win->scroll = LogLines;
96
+
97
+	win->log = newpad(LogLines, COLS);
98
+	wsetscrreg(win->log, 0, LogLines - 1);
99
+	scrollok(win->log, true);
100
+	wmove(win->log, LogLines - 1, 0);
101
+
102
+	windowAppend(win);
103
+	return win;
104
+}
105
+
106
+static void windowResize(struct Window *win) {
107
+	wresize(win->log, LogLines, COLS);
108
+	wmove(win->log, LogLines - 1, lastCol());
109
+}
110
+
111
+static void windowMark(struct Window *win) {
112
+	win->mark = true;
113
+}
114
+static void windowUnmark(struct Window *win) {
115
+	win->mark = false;
116
+	win->unread = 0;
117
+	win->hot = false;
118
+}
119
+
120
+static void windowShow(struct Window *win) {
121
+	if (windows.active) windowMark(windows.active);
122
+	if (win) {
123
+		touchwin(win->log);
124
+		windowUnmark(win);
125
+	}
126
+	windows.active = win;
127
+}
128
+
129
+static void windowClose(struct Window *win) {
130
+	if (windows.active == win) windowShow(win->next ? win->next : win->prev);
131
+	windowRemove(win);
132
+	delwin(win->log);
133
+	free(win);
134
+}
135
+
136
+static void windowScroll(struct Window *win, int lines) {
137
+	if (lines < 0) {
138
+		if (win->scroll == logHeight()) return;
139
+		if (win->scroll == LogLines) windowMark(win);
140
+		win->scroll = MAX(win->scroll + lines, logHeight());
141
+	} else {
142
+		if (win->scroll == LogLines) return;
143
+		win->scroll = MIN(win->scroll + lines, LogLines);
144
+		if (win->scroll == LogLines) windowUnmark(win);
145
+	}
146
+}
147
+
37 148
 static void colorInit(void) {
38 149
 	start_color();
39 150
 	use_default_colors();
40
-	if (COLORS >= 16) {
41
-		for (short pair = 0; pair < 0x100; ++pair) {
42
-			if (pair < 0x10) {
151
+	if (COLORS < 16) {
152
+		for (short pair = 0; pair < 0100; ++pair) {
153
+			if (pair < 010) {
43 154
 				init_pair(1 + pair, pair, -1);
44 155
 			} else {
45
-				init_pair(1 + pair, pair & 0x0F, (pair & 0xF0) >> 4);
156
+				init_pair(1 + pair, pair & 007, (pair & 070) >> 3);
46 157
 			}
47 158
 		}
48 159
 	} else {
49
-		for (short pair = 0; pair < 0100; ++pair) {
50
-			if (pair < 010) {
160
+		for (short pair = 0; pair < 0x100; ++pair) {
161
+			if (pair < 0x10) {
51 162
 				init_pair(1 + pair, pair, -1);
52 163
 			} else {
53
-				init_pair(1 + pair, pair & 007, (pair & 070) >> 3);
164
+				init_pair(1 + pair, pair & 0x0F, (pair & 0xF0) >> 4);
54 165
 			}
55 166
 		}
56 167
 	}
57 168
 }
58 169
 
59
-static attr_t attr8(short pair) {
60
-	if (COLORS >= 16 || pair < 0) return A_NORMAL;
61
-	return (pair & 0x08) ? A_BOLD : A_NORMAL;
170
+static attr_t colorAttr(short color) {
171
+	if (color < 0) return A_NORMAL;
172
+	if (COLORS < 16 && (color & 0x08)) return A_BOLD;
173
+	return A_NORMAL;
62 174
 }
63
-static short pair8(short pair) {
64
-	if (COLORS >= 16 || pair < 0) return pair;
65
-	return (pair & 0x70) >> 1 | (pair & 0x07);
175
+static short colorPair(short color) {
176
+	if (color < 0) return 0;
177
+	if (COLORS < 16) return 1 + ((color & 0x70) >> 1 | (color & 0x07));
178
+	return 1 + color;
66 179
 }
67 180
 
68
-struct Window {
69
-	struct Tag tag;
70
-	WINDOW *log;
71
-	int scroll;
72
-	bool hot, mark;
73
-	uint unread;
74
-	struct Window *prev;
75
-	struct Window *next;
76
-};
77
-
78 181
 static struct {
79 182
 	bool hide;
80 183
 	WINDOW *status;
81 184
 	WINDOW *input;
82
-	struct Window *window;
83 185
 } ui;
84 186
 
85
-void uiShow(void) {
86
-	ui.hide = false;
87
-	termMode(TermFocus, true);
88
-	uiDraw();
89
-}
90
-
91
-void uiHide(void) {
92
-	ui.hide = true;
93
-	termMode(TermFocus, false);
94
-	endwin();
95
-}
96
-
97 187
 void uiInit(void) {
98
-	setlocale(LC_CTYPE, "");
99 188
 	initscr();
100 189
 	cbreak();
101 190
 	noecho();
102
-
103
-	colorInit();
104 191
 	termInit();
105
-
192
+	colorInit();
106 193
 	ui.status = newwin(1, COLS, 0, 0);
107 194
 	ui.input = newpad(1, 512);
108 195
 	keypad(ui.input, true);
109 196
 	nodelay(ui.input, true);
110
-
111
-	uiWindowTag(TagStatus);
112 197
 	uiShow();
113 198
 }
114 199
 
115
-void uiExit(void) {
116
-	uiHide();
117
-	printf(
118
-		"This program is AGPLv3 Free Software!\n"
119
-		"The source is available at <" SOURCE_URL ">.\n"
120
-	);
200
+static void uiResize(void) {
201
+	wresize(ui.status, 1, COLS);
202
+	for (struct Window *win = windows.head; win; win = win->next) {
203
+		windowResize(win);
204
+	}
121 205
 }
122 206
 
123
-static int lastLine(void) {
124
-	return LINES - 1;
207
+void uiShow(void) {
208
+	ui.hide = false;
209
+	termMode(TermFocus, true);
210
+	uiDraw();
125 211
 }
126
-static int lastCol(void) {
127
-	return COLS - 1;
212
+void uiHide(void) {
213
+	ui.hide = true;
214
+	termMode(TermFocus, false);
215
+	endwin();
128 216
 }
129
-static int logHeight(void) {
130
-	return LINES - 2;
217
+
218
+void uiExit(int status) {
219
+	uiHide();
220
+	printf(
221
+		"This program is AGPLv3 Free Software!\n"
222
+		"Code is available from <" SOURCE_URL ">.\n"
223
+	);
224
+	exit(status);
131 225
 }
132 226
 
133 227
 static int _;
134
-
135 228
 void uiDraw(void) {
136 229
 	if (ui.hide) return;
137 230
 	wnoutrefresh(ui.status);
138
-	pnoutrefresh(
139
-		ui.window->log,
140
-		ui.window->scroll - logHeight(), 0,
141
-		1, 0,
142
-		lastLine() - 1, lastCol()
143
-	);
231
+	if (windows.active) {
232
+		pnoutrefresh(
233
+			windows.active->log,
234
+			windows.active->scroll - logHeight(), 0,
235
+			1, 0,
236
+			lastLine() - 1, lastCol()
237
+		);
238
+	}
144 239
 	int x;
145 240
 	getyx(ui.input, _, x);
146 241
 	pnoutrefresh(
@@ -178,11 +273,11 @@ static void addFormat(WINDOW *win, const struct Format *format) {
178 273
 	if (format->underline) attr |= A_UNDERLINE;
179 274
 	if (format->reverse)   attr |= A_REVERSE;
180 275
 
181
-	short pair = -1;
182
-	if (format->fg != IRCDefault) pair = Colors[format->fg];
183
-	if (format->bg != IRCDefault) pair |= Colors[format->bg] << 4;
276
+	short color = -1;
277
+	if (format->fg != IRCDefault) color = Colors[format->fg];
278
+	if (format->bg != IRCDefault) color |= Colors[format->bg] << 4;
184 279
 
185
-	wattr_set(win, attr | attr8(pair), 1 + pair8(pair), NULL);
280
+	wattr_set(win, attr | colorAttr(color), colorPair(color), NULL);
186 281
 	waddnwstr(win, format->str, format->len);
187 282
 }
188 283
 
@@ -196,9 +291,9 @@ static int printWidth(const wchar_t *str, size_t len) {
196 291
 
197 292
 static int addWrap(WINDOW *win, const wchar_t *str) {
198 293
 	int lines = 0;
199
-
200 294
 	struct Format format = { .str = str };
201 295
 	formatReset(&format);
296
+
202 297
 	while (formatParse(&format, NULL)) {
203 298
 		size_t word = 1 + wcscspn(&format.str[1], L" ");
204 299
 		if (word < format.len) format.len = word;
@@ -214,24 +309,15 @@ static int addWrap(WINDOW *win, const wchar_t *str) {
214 309
 			waddch(win, '\n');
215 310
 			lines++;
216 311
 		}
217
-
218 312
 		addFormat(win, &format);
219 313
 	}
220 314
 	return lines;
221 315
 }
222 316
 
223
-static struct {
224
-	struct Window *head;
225
-	struct Window *tail;
226
-	struct Window *tag[TagsLen];
227
-} windows;
228
-
229
-static void uiTitle(const struct Window *win) {
317
+static void title(const struct Window *win) {
230 318
 	int unread;
231 319
 	char *str;
232
-	int len = asprintf(
233
-		&str, "%s%n (%u)", win->tag.name, &unread, win->unread
234
-	);
320
+	int len = asprintf(&str, "%s%n (%u)", win->tag.name, &unread, win->unread);
235 321
 	if (len < 0) err(EX_OSERR, "asprintf");
236 322
 	if (!win->unread) str[unread] = '\0';
237 323
 	termTitle(str);
@@ -242,13 +328,13 @@ static void uiStatus(void) {
242 328
 	wmove(ui.status, 0, 0);
243 329
 	int num = 0;
244 330
 	for (const struct Window *win = windows.head; win; win = win->next, ++num) {
245
-		if (!win->unread && ui.window != win) continue;
246
-		if (ui.window == win) uiTitle(win);
331
+		if (!win->unread && windows.active != win) continue;
332
+		if (windows.active == win) title(win);
247 333
 		int unread;
248 334
 		wchar_t *str;
249 335
 		int len = aswprintf(
250 336
 			&str, L"%c %d %s %n(\3%02d%u\3) ",
251
-			(ui.window == win ? IRCReverse : IRCReset),
337
+			(windows.active == win ? IRCReverse : IRCReset),
252 338
 			num, win->tag.name,
253 339
 			&unread, (win->hot ? IRCYellow : IRCDefault), win->unread
254 340
 		);
@@ -257,139 +343,62 @@ static void uiStatus(void) {
257 343
 		addWrap(ui.status, str);
258 344
 		free(str);
259 345
 	}
260
-	// TODO: Put window's topic in the rest of the status area.
346
+	// TODO: Put active window's topic in the rest of the status area.
261 347
 	wclrtoeol(ui.status);
262 348
 }
263 349
 
264
-static void windowAppend(struct Window *win) {
265
-	if (windows.tail) windows.tail->next = win;
266
-	win->prev = windows.tail;
267
-	win->next = NULL;
268
-	windows.tail = win;
269
-	if (!windows.head) windows.head = win;
270
-	windows.tag[win->tag.id] = win;
271
-}
272
-
273
-static void windowRemove(struct Window *win) {
274
-	if (win->prev) win->prev->next = win->next;
275
-	if (win->next) win->next->prev = win->prev;
276
-	if (windows.head == win) windows.head = win->next;
277
-	if (windows.tail == win) windows.tail = win->prev;
278
-	windows.tag[win->tag.id] = NULL;
279
-}
280
-
281
-static const int LogLines = 512;
282
-
283
-static struct Window *windowTag(struct Tag tag) {
284
-	struct Window *win = windows.tag[tag.id];
285
-	if (win) return win;
286
-
287
-	win = calloc(1, sizeof(*win));
288
-	if (!win) err(EX_OSERR, "calloc");
289
-
290
-	win->tag = tag;
291
-	win->mark = true;
292
-	win->scroll = LogLines;
293
-	win->log = newpad(LogLines, COLS);
294
-	wsetscrreg(win->log, 0, LogLines - 1);
295
-	scrollok(win->log, true);
296
-	wmove(win->log, LogLines - 1, 0);
297
-
298
-	windowAppend(win);
299
-	return win;
300
-}
301
-
302
-static void windowClose(struct Window *win) {
303
-	windowRemove(win);
304
-	delwin(win->log);
305
-	free(win);
306
-}
307
-
308
-static void uiResize(void) {
309
-	wresize(ui.status, 1, COLS);
310
-	for (struct Window *win = windows.head; win; win = win->next) {
311
-		wresize(win->log, LogLines, COLS);
312
-		wmove(win->log, LogLines - 1, lastCol());
313
-	}
314
-}
315
-
316
-static void windowUnmark(struct Window *win) {
317
-	win->mark = false;
318
-	win->unread = 0;
319
-	win->hot = false;
320
-	uiStatus();
321
-}
322
-
323
-static void uiWindow(struct Window *win) {
324
-	touchwin(win->log);
325
-	if (ui.window) ui.window->mark = true;
326
-	windowUnmark(win);
327
-	ui.window = win;
350
+void uiShowTag(struct Tag tag) {
351
+	windowShow(windowFor(tag));
328 352
 	uiStatus();
329
-	uiPrompt();
353
+	uiPrompt(false);
330 354
 }
331 355
 
332
-void uiWindowTag(struct Tag tag) {
333
-	uiWindow(windowTag(tag));
334
-}
335
-
336
-void uiWindowNum(int num) {
356
+void uiShowNum(int num) {
357
+	struct Window *win = NULL;
337 358
 	if (num < 0) {
338
-		for (struct Window *win = windows.tail; win; win = win->prev) {
339
-			if (++num) continue;
340
-			uiWindow(win);
341
-			break;
359
+		for (win = windows.tail; win; win = win->prev) {
360
+			if (!++num) break;
342 361
 		}
343 362
 	} else {
344
-		for (struct Window *win = windows.head; win; win = win->next) {
345
-			if (num--) continue;
346
-			uiWindow(win);
347
-			break;
363
+		for (win = windows.head; win; win = win->next) {
364
+			if (!num--) break;
348 365
 		}
349 366
 	}
367
+	if (win) windowShow(win);
368
+	uiStatus();
369
+	uiPrompt(false);
350 370
 }
351 371
 
352 372
 void uiCloseTag(struct Tag tag) {
353
-	struct Window *win = windowTag(tag);
354
-	if (ui.window == win) {
355
-		if (win->next) {
356
-			uiWindow(win->next);
357
-		} else if (win->prev) {
358
-			uiWindow(win->prev);
359
-		} else {
360
-			return;
361
-		}
362
-	}
363
-	windowClose(win);
373
+	windowClose(windowFor(tag));
374
+	uiStatus();
375
+	uiPrompt(false);
364 376
 }
365 377
 
366
-static void notify(struct Tag tag, const wchar_t *line) {
378
+static void notify(struct Tag tag, const wchar_t *str) {
367 379
 	beep();
368 380
 	if (!self.notify) return;
369 381
 
382
+	size_t len = 0;
370 383
 	char buf[256];
371
-	size_t cap = sizeof(buf);
372
-
373
-	struct Format format = { .str = line };
384
+	struct Format format = { .str = str };
374 385
 	formatReset(&format);
375 386
 	while (formatParse(&format, NULL)) {
376
-		int len = snprintf(
377
-			&buf[sizeof(buf) - cap], cap,
387
+		int n = snprintf(
388
+			&buf[len], sizeof(buf) - len,
378 389
 			"%.*ls", (int)format.len, format.str
379 390
 		);
380
-		if (len < 0) err(EX_OSERR, "snprintf");
381
-		if ((size_t)len >= cap) break;
382
-		cap -= len;
391
+		if (n < 0) err(EX_OSERR, "snprintf");
392
+		len += n;
393
+		if (len >= sizeof(buf)) break;
383 394
 	}
384
-
385 395
 	eventPipe((const char *[]) { "notify-send", tag.name, buf, NULL });
386 396
 }
387 397
 
388
-void uiLog(struct Tag tag, enum UIHeat heat, const wchar_t *line) {
389
-	struct Window *win = windowTag(tag);
398
+void uiLog(struct Tag tag, enum UIHeat heat, const wchar_t *str) {
399
+	struct Window *win = windowFor(tag);
390 400
 	int lines = 1;
391 401
 	waddch(win->log, '\n');
392
-
393 402
 	if (win->mark && heat > UICold) {
394 403
 		if (!win->unread++) {
395 404
 			lines++;
@@ -397,12 +406,11 @@ void uiLog(struct Tag tag, enum UIHeat heat, const wchar_t *line) {
397 406
 		}
398 407
 		if (heat > UIWarm) {
399 408
 			win->hot = true;
400
-			notify(tag, line);
409
+			notify(tag, str);
401 410
 		}
402 411
 		uiStatus();
403 412
 	}
404
-
405
-	lines += addWrap(win->log, line);
413
+	lines += addWrap(win->log, str);
406 414
 	if (win->scroll != LogLines) win->scroll -= lines;
407 415
 }
408 416
 
@@ -417,105 +425,99 @@ void uiFmt(struct Tag tag, enum UIHeat heat, const wchar_t *format, ...) {
417 425
 	free(str);
418 426
 }
419 427
 
420
-static void scrollUp(int lines) {
421
-	if (ui.window->scroll == logHeight()) return;
422
-	if (ui.window->scroll == LogLines) ui.window->mark = true;
423
-	ui.window->scroll = MAX(ui.window->scroll - lines, logHeight());
424
-}
425
-static void scrollDown(int lines) {
426
-	if (ui.window->scroll == LogLines) return;
427
-	ui.window->scroll = MIN(ui.window->scroll + lines, LogLines);
428
-	if (ui.window->scroll == LogLines) windowUnmark(ui.window);
429
-}
430
-
431
-static void keyCode(wchar_t ch) {
432
-	switch (ch) {
433
-		break; case KEY_RESIZE:    uiResize();
434
-		break; case KEY_SLEFT:     scrollUp(1);
435
-		break; case KEY_SRIGHT:    scrollDown(1);
436
-		break; case KEY_PPAGE:     scrollUp(logHeight() / 2);
437
-		break; case KEY_NPAGE:     scrollDown(logHeight() / 2);
438
-		break; case KEY_LEFT:      edit(ui.window->tag, EditLeft, 0);
439
-		break; case KEY_RIGHT:     edit(ui.window->tag, EditRight, 0);
440
-		break; case KEY_HOME:      edit(ui.window->tag, EditHome, 0);
441
-		break; case KEY_END:       edit(ui.window->tag, EditEnd, 0);
442
-		break; case KEY_DC:        edit(ui.window->tag, EditDelete, 0);
443
-		break; case KEY_BACKSPACE: edit(ui.window->tag, EditBackspace, 0);
444
-		break; case KEY_ENTER:     edit(ui.window->tag, EditEnter, 0);
428
+static void keyCode(wchar_t code) {
429
+	if (code == KEY_RESIZE) uiResize();
430
+	struct Window *win = windows.active;
431
+	if (!win) return;
432
+	switch (code) {
433
+		break; case KEY_SLEFT:     windowScroll(win, -1);
434
+		break; case KEY_SRIGHT:    windowScroll(win, +1);
435
+		break; case KEY_PPAGE:     windowScroll(win, -logHeight() / 2);
436
+		break; case KEY_NPAGE:     windowScroll(win, +logHeight() / 2);
437
+		break; case KEY_LEFT:      edit(win->tag, EditLeft, 0);
438
+		break; case KEY_RIGHT:     edit(win->tag, EditRight, 0);
439
+		break; case KEY_HOME:      edit(win->tag, EditHome, 0);
440
+		break; case KEY_END:       edit(win->tag, EditEnd, 0);
441
+		break; case KEY_DC:        edit(win->tag, EditDelete, 0);
442
+		break; case KEY_BACKSPACE: edit(win->tag, EditBackspace, 0);
443
+		break; case KEY_ENTER:     edit(win->tag, EditEnter, 0);
445 444
 	}
446 445
 }
447 446
 
448
-#define CTRL(ch) ((ch) ^ 0100)
449
-
450 447
 static void keyChar(wchar_t ch) {
448
+	struct Window *win = windows.active;
451 449
 	if (ch < 0200) {
452 450
 		enum TermEvent event = termEvent((char)ch);
453 451
 		switch (event) {
454
-			break; case TermFocusIn:  windowUnmark(ui.window);
455
-			break; case TermFocusOut: ui.window->mark = true;
452
+			break; case TermFocusIn:  if (win) windowUnmark(win);
453
+			break; case TermFocusOut: if (win) windowMark(win);
456 454
 			break; default: {}
457 455
 		}
458 456
 		if (event) return;
459 457
 	}
458
+	if (ch == Del) ch = L'\b';
460 459
 
461 460
 	static bool meta;
462
-	if (ch == L'\33') {
461
+	if (ch == Esc) {
463 462
 		meta = true;
464 463
 		return;
465 464
 	}
466
-
467
-	if (ch == L'\177') ch = L'\b';
468
-
469 465
 	if (meta) {
470 466
 		meta = false;
467
+		if (ch >= L'0' && ch <= L'9') uiShowNum(ch - L'0');
468
+		if (!win) return;
471 469
 		switch (ch) {
472
-			break; case L'b':  edit(ui.window->tag, EditBackWord, 0);
473
-			break; case L'f':  edit(ui.window->tag, EditForeWord, 0);
474
-			break; case L'\b': edit(ui.window->tag, EditKillBackWord, 0);
475
-			break; case L'd':  edit(ui.window->tag, EditKillForeWord, 0);
476
-			break; case L'm':  uiLog(ui.window->tag, UICold, L"");
477
-			break; default: {
478
-				if (ch >= L'0' && ch <= L'9') uiWindowNum(ch - L'0');
479
-			}
470
+			break; case L'b':  edit(win->tag, EditBackWord, 0);
471
+			break; case L'f':  edit(win->tag, EditForeWord, 0);
472
+			break; case L'\b': edit(win->tag, EditKillBackWord, 0);
473
+			break; case L'd':  edit(win->tag, EditKillForeWord, 0);
474
+			break; case L'm':  uiLog(win->tag, UICold, L"");
480 475
 		}
481 476
 		return;
482 477
 	}
483 478
 
479
+	if (ch == CTRL(L'L')) clearok(curscr, true);
480
+	if (!win) return;
484 481
 	switch (ch) {
485
-		break; case CTRL(L'L'): clearok(curscr, true);
486
-
487
-		break; case CTRL(L'A'): edit(ui.window->tag, EditHome, 0);
488
-		break; case CTRL(L'B'): edit(ui.window->tag, EditLeft, 0);
489
-		break; case CTRL(L'D'): edit(ui.window->tag, EditDelete, 0);
490
-		break; case CTRL(L'E'): edit(ui.window->tag, EditEnd, 0);
491
-		break; case CTRL(L'F'): edit(ui.window->tag, EditRight, 0);
492
-		break; case CTRL(L'K'): edit(ui.window->tag, EditKillLine, 0);
493
-		break; case CTRL(L'W'): edit(ui.window->tag, EditKillBackWord, 0);
494
-
495
-		break; case CTRL(L'C'): edit(ui.window->tag, EditInsert, IRCColor);
496
-		break; case CTRL(L'N'): edit(ui.window->tag, EditInsert, IRCReset);
497
-		break; case CTRL(L'O'): edit(ui.window->tag, EditInsert, IRCBold);
498
-		break; case CTRL(L'R'): edit(ui.window->tag, EditInsert, IRCColor);
499
-		break; case CTRL(L'T'): edit(ui.window->tag, EditInsert, IRCItalic);
500
-		break; case CTRL(L'U'): edit(ui.window->tag, EditInsert, IRCUnderline);
501
-		break; case CTRL(L'V'): edit(ui.window->tag, EditInsert, IRCReverse);
502
-
503
-		break; case L'\b': edit(ui.window->tag, EditBackspace, 0);
504
-		break; case L'\t': edit(ui.window->tag, EditComplete, 0);
505
-		break; case L'\n': edit(ui.window->tag, EditEnter, 0);
506
-
507
-		break; default: {
508
-			if (iswprint(ch)) edit(ui.window->tag, EditInsert, ch);
509
-		}
482
+		break; case CTRL(L'A'): edit(win->tag, EditHome, 0);
483
+		break; case CTRL(L'B'): edit(win->tag, EditLeft, 0);
484
+		break; case CTRL(L'D'): edit(win->tag, EditDelete, 0);
485
+		break; case CTRL(L'E'): edit(win->tag, EditEnd, 0);
486
+		break; case CTRL(L'F'): edit(win->tag, EditRight, 0);
487
+		break; case CTRL(L'K'): edit(win->tag, EditKillLine, 0);
488
+		break; case CTRL(L'W'): edit(win->tag, EditKillBackWord, 0);
489
+
490
+		break; case CTRL(L'C'): edit(win->tag, EditInsert, IRCColor);
491
+		break; case CTRL(L'N'): edit(win->tag, EditInsert, IRCReset);
492
+		break; case CTRL(L'O'): edit(win->tag, EditInsert, IRCBold);
493
+		break; case CTRL(L'R'): edit(win->tag, EditInsert, IRCColor);
494
+		break; case CTRL(L'T'): edit(win->tag, EditInsert, IRCItalic);
495
+		break; case CTRL(L'U'): edit(win->tag, EditInsert, IRCUnderline);
496
+		break; case CTRL(L'V'): edit(win->tag, EditInsert, IRCReverse);
497
+
498
+		break; case L'\b': edit(win->tag, EditBackspace, 0);
499
+		break; case L'\t': edit(win->tag, EditComplete, 0);
500
+		break; case L'\n': edit(win->tag, EditEnter, 0);
501
+
502
+		break; default: if (iswprint(ch)) edit(win->tag, EditInsert, ch);
510 503
 	}
511 504
 }
512 505
 
506
+void uiRead(void) {
507
+	if (ui.hide) uiShow();
508
+	int ret;
509
+	wint_t ch;
510
+	while (ERR != (ret = wget_wch(ui.input, &ch))) {
511
+		(ret == KEY_CODE_YES ? keyCode(ch) : keyChar(ch));
512
+	}
513
+	uiPrompt(false);
514
+}
515
+
513 516
 static bool isAction(struct Tag tag, const wchar_t *input) {
514 517
 	if (tag.id == TagStatus.id || tag.id == TagRaw.id) return false;
515 518
 	return !wcsncasecmp(input, L"/me ", 4);
516 519
 }
517 520
 
518
-// FIXME: This duplicates logic from input.c for wcs.
519 521
 static bool isCommand(struct Tag tag, const wchar_t *input) {
520 522
 	if (tag.id == TagStatus.id || tag.id == TagRaw.id) return true;
521 523
 	if (input[0] != L'/') return false;
@@ -524,29 +526,30 @@ static bool isCommand(struct Tag tag, const wchar_t *input) {
524 526
 	return !extra || (space && extra > space);
525 527
 }
526 528
 
527
-void uiPrompt(void) {
528
-	const wchar_t *input = editHead();
529
-
530
-	// TODO: Avoid reformatting these on every read.
531
-	wchar_t *prompt = NULL;
532
-	int len = 0;
533
-	if (isAction(ui.window->tag, input) && editTail() >= &input[4]) {
534
-		input = &input[4];
535
-		len = aswprintf(
536
-			&prompt, L"\3%d* %s\3 ",
537
-			formatColor(self.user), self.nick
538
-		);
539
-	} else if (!isCommand(ui.window->tag, input)) {
540
-		len = aswprintf(
541
-			&prompt, L"\3%d<%s>\3 ",
542
-			formatColor(self.user), self.nick
543
-		);
529
+void uiPrompt(bool nickChanged) {
530
+	static wchar_t *promptMesg;
531
+	static wchar_t *promptAction;
532
+	if (nickChanged || !promptMesg || !promptAction) {
533
+		free(promptMesg);
534
+		free(promptAction);
535
+		enum IRCColor color = formatColor(self.user);
536
+		int len = aswprintf(&promptMesg, L"\3%d<%s>\3 ", color, self.nick);
537
+		if (len < 0) err(EX_OSERR, "aswprintf");
538
+		len = aswprintf(&promptAction, L"\3%d* %s\3 ", color, self.nick);
539
+		if (len < 0) err(EX_OSERR, "aswprintf");
544 540
 	}
545
-	if (len < 0) err(EX_OSERR, "aswprintf");
541
+
542
+	const wchar_t *input = editHead();
546 543
 
547 544
 	wmove(ui.input, 0, 0);
548
-	if (prompt) addWrap(ui.input, prompt);
549
-	free(prompt);
545
+	if (windows.active) {
546
+		if (isAction(windows.active->tag, input) && editTail() >= &input[4]) {
547
+			input = &input[4];
548
+			addWrap(ui.input, promptAction);
549
+		} else if (!isCommand(windows.active->tag, input)) {
550
+			addWrap(ui.input, promptMesg);
551
+		}
552
+	}
550 553
 
551 554
 	int x = 0;
552 555
 	struct Format format = { .str = input };
@@ -555,21 +558,6 @@ void uiPrompt(void) {
555 558
 		if (format.split) getyx(ui.input, _, x);
556 559
 		addFormat(ui.input, &format);
557 560
 	}
558
-
559 561
 	wclrtoeol(ui.input);
560 562
 	wmove(ui.input, 0, x);
561 563
 }
562
-
563
-void uiRead(void) {
564
-	if (ui.hide) uiShow();
565
-	int ret;
566
-	wint_t ch;
567
-	while (ERR != (ret = wget_wch(ui.input, &ch))) {
568
-		if (ret == KEY_CODE_YES) {
569
-			keyCode(ch);
570
-		} else {
571
-			keyChar(ch);
572
-		}
573
-	}
574
-	uiPrompt();
575
-}

Loading…
Cancel
Save