tui.c
Raw
1#include <tui.h>
2#define NCURSES_WIDECHAR 1
3#include <curses.h>
4#include <sys/ioctl.h>
5#include <unistd.h>
6#include <time.h>
7#include <locale.h>
8#include <stdlib.h>
9#include <stdarg.h>
10#include <panel.h>
11#include <string.h>
12
13#include <helper.h>
14
15static PANEL *stdpanel = NULL;
16static WINDOW *logbox = NULL;
17static WINDOW *logwin = NULL;
18static WINDOW *charbox = NULL;
19static WINDOW *charwin = NULL;
20static WINDOW *mapbox = NULL;
21static WINDOW *mapwin = NULL;
22static bool initialized = false;
23
24static bool setup_log(struct winsize w)
25{
26 int ystart = 0;
27 int xstart = w.ws_col/2+1;
28 int ysize = w.ws_row;
29 int xsize = w.ws_col/2-1;
30 logbox = subwin(stdscr, ysize, xsize, ystart, xstart);
31 if(!logbox)
32 return false;
33 box(logbox,'|', '-');
34 wmove(logbox, 0,1);
35 wprintw(logbox, "Log");
36 logwin = subwin(logbox, ysize-2, xsize-2, ystart+1, xstart+1);
37 if(!logwin)
38 return false;
39// scrollok(logwin, true);
40 return true;
41}
42
43static bool setup_char(struct winsize w)
44{
45 int ystart = 0;
46 int xstart = 0;
47 int ysize = w.ws_row/3;
48 int xsize = w.ws_col/2;
49 charbox = subwin(stdscr, ysize, xsize, ystart, xstart);
50 if(!charbox)
51 return false;
52 box(charbox,'|', '-');
53 wmove(charbox, 0,1);
54 wprintw(charbox, "Character");
55 charwin = subwin(charbox, ysize-2, xsize-2, ystart+1, xstart+1);
56 if(!charwin)
57 return false;
58 return true;
59}
60
61static bool setup_map(struct winsize w)
62{
63 int ystart = w.ws_row/3+1;
64 int xstart = 0;
65 int ysize = w.ws_row-w.ws_row/3-1;
66 int xsize = w.ws_col/2;
67 mapbox = subwin(stdscr, ysize, xsize, ystart, xstart);
68 if(!mapbox)
69 return false;
70 box(mapbox,'|', '-');
71 wmove(mapbox, 0,1);
72 wprintw(mapbox, "Map");
73 mapwin = subwin(mapbox, ysize-2, xsize-2, ystart+1, xstart+1);
74 if(!mapwin)
75 return false;
76 return true;
77}
78
79void quit(void)
80{
81 endwin();
82}
83
84bool setup()
85{
86 if(initialized) {
87 erase();
88
89 if(logwin != NULL)
90 delwin(logwin);
91 if(logbox != NULL)
92 delwin(logbox);
93
94 if(charwin != NULL)
95 delwin(charwin);
96 if(charbox != NULL)
97 delwin(charbox);
98
99 if(mapwin != NULL)
100 delwin(mapwin);
101 if(mapbox != NULL)
102 delwin(mapbox);
103
104 } else {
105 atexit(quit);
106 setlocale(LC_ALL,"");
107 stdscr = initscr();
108 stdpanel = new_panel(stdscr);
109 assert(stdpanel != NULL);
110 noecho();
111 cbreak();
112 curs_set(0);
113 keypad(stdscr, true);
114 initialized = true;
115 }
116
117
118 struct winsize w;
119 assert(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1);
120 if(w.ws_col < 25 || w.ws_row < 10) {
121 endwin();
122 puts("Terminal too small");
123 abort();
124 }
125
126 if(!setup_char(w)) {
127 fprintf(stderr, "char\n");
128 return false;
129 }
130 if(!setup_log(w)) {
131 fprintf(stderr, "log\n");
132 return false;
133 }
134
135 if(!setup_map(w)) {
136 fprintf(stderr, "map\n");
137 return false;
138 }
139 update_panels();
140 doupdate();
141 return true;
142}
143
144size_t logindex = 0;
145size_t loglength;
146char **logs = NULL;
147
148void printlog() //TODO: fix logs if they go multiline
149{
150 werase(logwin);
151 wmove(logwin, 0, 0);
152 int ymax = getmaxy(logwin);
153 for(int i = 0; i < ymax && i+logindex < loglength; i++)
154 {
155 waddstr(logwin, logs[i+logindex]);
156 waddstr(logwin, "\n");
157 }
158 wrefresh(logwin);
159}
160
161void logstr(const char *format, ...)
162{
163 static size_t capacity;
164
165 if(logs == NULL) {
166 logs = xmalloc(8*sizeof(*logs));
167 capacity = 8;
168 loglength = 0;
169 }
170
171 if(capacity == loglength++) {
172 logs = xrealloc(logs, (capacity*=2) * sizeof(*logs));
173 }
174
175 va_list ap;
176 va_start(ap, format);
177 va_list ap2;
178 va_copy(ap2, ap);
179 int bufsz = vsnprintf(NULL, 0, format, ap);
180 va_end(ap);
181 struct tm mytime = *localtime(&(time_t){time(NULL)});
182 logs[loglength-1] = xmalloc(bufsz+11+2); //11 == size needed for the time header;
183 strftime(logs[loglength-1], 11, "%T> ", &mytime);
184 vsprintf(logs[loglength-1]+10, format, ap2);
185 va_end(ap2);
186
187 if(loglength - logindex == getmaxy(logwin))
188 logindex++;
189
190 printlog();
191}
192
193void charclear()
194{
195 werase(charwin);
196 wrefresh(charwin);
197}
198
199
200void charprint(const char* format, ...)
201{
202 va_list ap;
203 va_start(ap, format);
204
205 vw_printw(charwin, format, ap);
206 wprintw(charwin, "\n");
207 wrefresh(charwin);
208
209 va_end(ap);
210}
211
212
213void log_scroll(enum direction d)
214{
215 if(d == UP && logindex != 0)
216 logindex--;
217 else if(d == DOWN && logindex != loglength-1)
218 logindex++;
219
220 printlog();
221
222 wrefresh(logwin);
223}
224
225
226void draw_map(const struct game *g, struct point p)
227{
228 int x, y;
229 getmaxyx(mapwin, y, x);
230 size_t starty = MAX(0, p.y-y/2);
231 size_t endy = MIN(starty + y, g->ys);
232 size_t startx = MAX(0, p.x-x/2);
233 size_t endx = MIN(startx + x, g->xs);
234
235 werase(mapwin);
236 int line = 0;
237 for(size_t i = starty; i < endy; i++)
238 {
239 wmove(mapwin, line++, 0);
240 for(size_t j = startx; j < endx; j++)
241 {
242 waddch(mapwin, game_at(g, (struct point){.y = i, .x = j})->symbol);
243 }
244 }
245
246 wrefresh(mapwin);
247}
248
249bool messagebox(const char *str, int y, int x)
250{
251 int yorig = y, xorig = x;
252 struct winsize w;
253 assert(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1);
254
255 const char *returnstr = "Press return to continue";
256 x = MAX(strlen(returnstr), x)+2;
257 y += 4;
258
259 if(y > w.ws_row || x > w.ws_col)
260 return false;
261
262 int ystart = w.ws_row/2-y/2;
263 int xstart = w.ws_col/2-x/2;
264
265 WINDOW *msgbox = newwin(y, x, ystart, xstart);
266 PANEL *msgboxpanel = new_panel(msgbox);
267 box(msgbox, '|', '-');
268 WINDOW *msgwin = newwin(y-2, x-2, ystart+1, xstart+1);
269 PANEL *msgwinpanel = new_panel(msgwin);
270 waddstr(msgwin, str);
271 waddstr(msgwin, "\n\n");
272 waddstr(msgwin, returnstr);
273 update_panels();
274 doupdate();
275 int c;
276 while((c = getch()) != '\n' && c != KEY_RESIZE);
277
278 del_panel(msgwinpanel);
279 del_panel(msgboxpanel);
280
281 delwin(msgbox);
282 delwin(msgwin);
283
284 update_panels();
285 doupdate();
286
287 if(c == KEY_RESIZE)
288 {
289 while(!setup());
290 return messagebox(str, yorig, xorig);
291 }
292 printlog(); //incase we resized
293 return true;
294
295}
296
297void itemselect(struct monster *m, itemselectfn f)
298{
299 WINDOW *menu = newwin(0, 0, 0, 0);
300 PANEL *menupanel = new_panel(menu);
301
302 size_t i = 0; //we put the index before the label so we keep it between redraws
303
304redraw:;
305 struct winsize w;
306 assert(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1);
307
308 for(size_t i = 0; i < m->items.size; i++)
309 {
310 struct item *it = &darray_item(m->items, i);
311 if(it->type != NOITEM) {
312 if(it->type == EQUIP) {
313 mvwprintw(menu, i, 0, "%.*s (%c)", w.ws_col, it->name, it->u.e.isequipped ? '*' : ' ');
314 } else {
315 mvwprintw(menu, i, 0, "%.*s", w.ws_col, it->name);
316 }
317 } else {
318 mvwaddstr(menu, i, 0, "EMPTY");
319 }
320 }
321
322 mvwchgat(menu, i, 0, -1, A_REVERSE, 0, NULL);
323 update_panels();
324 doupdate();
325
326 int c;
327 while((c = getch()) != 'i' && c != 'q')
328 {
329 switch(c)
330 {
331 case ' ':
332 if(f(m, i))
333 i = 0;
334 werase(menu);
335 goto redraw; //items might have changed, so we redraw the list
336 break;
337 case 's':
338 if(i != (m->items.size == 0 ? 0 :m->items.size-1)) {
339 wchgat(menu , -1, A_NORMAL, 0, NULL);
340 mvwchgat(menu, ++i, 0, -1, A_REVERSE, 0, NULL);
341 }
342 break;
343 case 'w':
344 if(i != 0) {
345 wchgat(menu, -1, A_NORMAL, 0, NULL);
346 mvwchgat(menu, --i, 0, -1, A_REVERSE, 0, NULL);
347 }
348 break;
349 case KEY_RESIZE:
350 werase(menu);
351 goto redraw;
352 break;
353 }
354 update_panels();
355 doupdate();
356 }
357
358
359 del_panel(menupanel);
360 delwin(menu);
361 update_panels();
362 doupdate();
363}
364
365
366
367