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 == (size_t)getmaxy(logwin)) //cast is fine, getmaxy only returns ERR if WINDOW is NULL
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 struct field *f = game_at(g, (struct point){.y = i, .x = j});
243 if(f->has_monster)
244 waddch(mapwin, f->m.symbol);
245 else
246 waddch(mapwin, f->u->ground.symbol);
247 }
248 }
249
250 wrefresh(mapwin);
251}
252
253bool messagebox(const char *str, size_t y, size_t x)
254{
255 int yorig = y, xorig = x;
256 struct winsize w;
257 assert(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1);
258
259 const char *returnstr = "Press return to continue";
260 x = MAX(strlen(returnstr), x)+2;
261 y += 4;
262
263 if(y > w.ws_row || x > w.ws_col)
264 return false;
265
266 int ystart = w.ws_row/2-y/2;
267 int xstart = w.ws_col/2-x/2;
268
269 WINDOW *msgbox = newwin(y, x, ystart, xstart);
270 PANEL *msgboxpanel = new_panel(msgbox);
271 box(msgbox, '|', '-');
272 WINDOW *msgwin = newwin(y-2, x-2, ystart+1, xstart+1);
273 PANEL *msgwinpanel = new_panel(msgwin);
274 waddstr(msgwin, str);
275 waddstr(msgwin, "\n\n");
276 waddstr(msgwin, returnstr);
277 update_panels();
278 doupdate();
279 int c;
280 while((c = getch()) != '\n' && c != KEY_RESIZE);
281
282 del_panel(msgwinpanel);
283 del_panel(msgboxpanel);
284
285 delwin(msgbox);
286 delwin(msgwin);
287
288 update_panels();
289 doupdate();
290
291 if(c == KEY_RESIZE)
292 {
293 while(!setup());
294 return messagebox(str, yorig, xorig);
295 }
296 printlog(); //incase we resized
297 return true;
298
299}
300
301void itemselect(struct monster *m, itemselectfn f)
302{
303 WINDOW *menu = newwin(0, 0, 0, 0);
304 PANEL *menupanel = new_panel(menu);
305
306 size_t i = 0; //we put the index before the label so we keep it between redraws
307
308redraw:;
309 struct winsize w;
310 assert(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1);
311
312 for(size_t i = 0; i < m->items.size; i++)
313 {
314 struct item *it = &darray_item(m->items, i);
315 if(it->type != NOITEM) {
316 if(it->type == EQUIP) {
317 mvwprintw(menu, i, 0, "%.*s (%c)", w.ws_col, it->name, it->u.e.isequipped ? '*' : ' ');
318 } else {
319 mvwprintw(menu, i, 0, "%.*s", w.ws_col, it->name);
320 }
321 } else {
322 mvwaddstr(menu, i, 0, "EMPTY");
323 }
324 }
325
326 mvwchgat(menu, i, 0, -1, A_REVERSE, 0, NULL);
327 update_panels();
328 doupdate();
329
330 int c;
331 while((c = getch()) != 'i' && c != 'q')
332 {
333 switch(c)
334 {
335 case ' ':
336 if(m->items.size != 0 && f(m, i))
337 i = 0;
338 werase(menu);
339 goto redraw; //items might have changed, so we redraw the list
340 break;
341 case 's':
342 if(i != (m->items.size == 0 ? 0 :m->items.size-1)) {
343 wchgat(menu , -1, A_NORMAL, 0, NULL);
344 mvwchgat(menu, ++i, 0, -1, A_REVERSE, 0, NULL);
345 }
346 break;
347 case 'w':
348 if(i != 0) {
349 wchgat(menu, -1, A_NORMAL, 0, NULL);
350 mvwchgat(menu, --i, 0, -1, A_REVERSE, 0, NULL);
351 }
352 break;
353 case KEY_RESIZE:
354 werase(menu);
355 goto redraw;
356 break;
357 }
358 update_panels();
359 doupdate();
360 }
361
362
363 del_panel(menupanel);
364 delwin(menu);
365 update_panels();
366 doupdate();
367}
368
369
370
371