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