1  /*
2   * UAE - The Un*x Amiga Emulator
3   *
4   * ncurses frontend for a text-based user interface.
5   *
6   * Copyright 1996 Bernd Schmidt
7   * If you find the routines in this file useful, you may use them in your
8   * programs without restrictions. Essentially, it's in the public domain.
9   *
10   */
11 
12 
13 #include "sysconfig.h"
14 #include "sysdeps.h"
15 
16 #ifdef HAVE_NCURSES_H
17 #include <ncurses.h>
18 #else
19 #include <curses.h>
20 #endif
21 #include <ctype.h>
22 
23 #include "options.h"
24 #include "uae.h"
25 #include "tui.h"
26 
27 #ifdef DONT_HAVE_ATTR_T
28 typedef int attr_t;
29 #endif
30 
31 static WINDOW *currwin;
32 
33 static WINDOW *winstack[10]; /* more than enough */
34 static int winnr = 0;
35 
tui_setup(void)36 void tui_setup(void)
37 {
38     int i;
39 
40     for (i = 0; i < 10; i++)
41 	winstack[i] = NULL;
42     /* From the ncurses manpage... */
43     initscr (); start_color (); cbreak(); noecho(); nonl (); intrflush (stdscr, FALSE); keypad (stdscr, TRUE);
44     currwin = stdscr;
45     if (has_colors ()) {
46 	init_pair (1, COLOR_WHITE, COLOR_BLUE);
47 	init_pair (2, COLOR_BLACK, COLOR_WHITE);
48 	wattron (currwin, COLOR_PAIR (1) | A_BOLD);
49 	wbkgd (currwin, ' ' | COLOR_PAIR (1));
50     }
51 
52     winstack[0] = stdscr;
53     winnr = 1;
54 }
55 
tui_lines(void)56 int tui_lines (void)
57 {
58     return LINES;
59 }
60 
tui_cols(void)61 int tui_cols (void)
62 {
63     return COLS;
64 }
65 
tui_shutdown(void)66 void tui_shutdown (void)
67 {
68     endwin ();
69 }
70 
tui_refresh(void)71 void tui_refresh (void)
72 {
73     int w;
74     for (w = 0; w < winnr; w++) {
75 	touchwin (winstack[w]);
76 	wnoutrefresh (winstack[w]);
77     }
78     doupdate ();
79 }
80 
tui_puts(const char * s)81 void tui_puts (const char *s)
82 {
83     waddstr (currwin, s);
84 }
85 
tui_cursoff(void)86 void tui_cursoff(void)
87 {
88 }
89 
tui_curson(void)90 void tui_curson (void)
91 {
92 }
93 
tui_putc(char c)94 void tui_putc(char c)
95 {
96     waddch (currwin, c);
97 }
98 
tui_cr(void)99 void tui_cr (void)
100 {
101     waddch (currwin, '\r');
102 }
103 
tui_getc(void)104 char tui_getc(void)
105 {
106     return getch ();
107 }
108 
tui_gotoxy(int x,int y)109 void tui_gotoxy (int x, int y)
110 {
111     x--; y--;
112     wmove (currwin, y, x);
113 }
114 
tui_selwin(int w)115 void tui_selwin (int w)
116 {
117     currwin = winstack[w];
118 }
119 
tui_clrwin(int w)120 void tui_clrwin (int w)
121 {
122     werase (winstack[w]);
123 }
124 
tui_drawbox(int w)125 void tui_drawbox(int w)
126 {
127     wborder (winstack[w], 0, 0, 0, 0, 0, 0, 0, 0);
128 }
129 
tui_hline(int x1,int y1,int x2)130 void tui_hline (int x1, int y1, int x2)
131 {
132     wmove (currwin, y1-1, x1-1);
133     whline (currwin, 0, x2-x1+1);
134 }
135 
tui_dlog(int x1,int y1,int x2,int y2)136 int tui_dlog(int x1, int y1, int x2, int y2)
137 {
138     x1--; y1--;
139     winstack[winnr] = newwin (y2 - y1, x2 - x1, y1, x1);
140     return winnr++;
141 }
142 
tui_dlogdie(int w)143 void tui_dlogdie (int w)
144 {
145     if (currwin == winstack[w])
146 	currwin = stdscr;
147     delwin (winstack[w]);
148     winstack[w] = NULL;
149     while (winstack[winnr-1] == NULL)
150 	winnr--;
151 
152     for (w = 0; w < winnr; w++)
153 	redrawwin (winstack[w]), wrefresh (winstack[w]);
154 }
155 
tui_gets(char * buf,int x,int y,int n)156 int tui_gets (char *buf, int x, int y, int n)
157 {
158     int i = 0;
159     for (;;) {
160 	int c, j;
161 
162 	buf[i] = 0;
163 	wmove (currwin, y, x);
164 	for (j = 0; j < i; j++)
165 	    waddch (currwin, buf[j]);
166 	for (; j < n; j++)
167 	    waddch (currwin, ' ');
168 
169 	wmove (currwin, y, x + i);
170 	wrefresh (currwin);
171 
172 	c = getch ();
173 
174 	wmove (currwin, y, x + i);
175 	if (c == 13)
176 	    return 1;
177 	else if (c == 27)
178 	    return 0;
179 	else if (i > 0 && c == KEY_BACKSPACE)
180 	    i--;
181 	else if (i + 1 < n && !iscntrl (c))
182 	    buf[i++] = c;
183     }
184 }
185 
tui_wgets(char * buf,const char * title,int n)186 int tui_wgets (char *buf, const char *title, int n)
187 {
188     int l = strlen (title);
189     int ww = (l > n ? l : n) + 2;
190     int w = tui_dlog((tui_cols ()-ww)/2, tui_lines ()/2-1, (tui_cols ()+ww)/2, tui_lines ()/2+1);
191     int result;
192 
193     tui_selwin (w); tui_drawbox(w);
194     wmove (currwin, 0, (ww-l)/2);
195     waddstr (currwin, title);
196     result = tui_gets (buf, 1, 1, n);
197     tui_dlogdie (w);
198     return result;
199 }
200 
tui_menubrowse(struct bstring * menu,int xoff,int yoff,int selected,int height)201 int tui_menubrowse (struct bstring *menu, int xoff, int yoff, int selected, int height)
202 {
203     int count = 0, maxsel = 0, maxw = 0;
204     int i, j, w, s, yp, oldyp;
205     chtype moresave[6][2];
206     int xpos, ypos;
207 
208     const char *mtitle = NULL;
209 
210     for (i = 0; menu[i].val != -3; i++) {
211 	int tmp;
212 	if (menu[i].val == -4) {
213 	    if (maxsel < selected)
214 		selected--;
215 	    continue;
216 	}
217 	if (menu[i].val != 0) {
218 	    count++;
219 	    if (menu[i].val != -2)
220 		maxsel++;
221 	} else
222 	    mtitle = menu[i].data;
223 	if ((tmp = strlen (menu[i].data)) > maxw)
224 	    maxw = tmp;
225     }
226     if (height > count)
227 	height = count;
228     maxw += 3;
229     if (strlen (mtitle ? mtitle : "") + 8 > maxw)
230 	maxw = strlen (mtitle ? mtitle : "") + 8;
231     if (xoff > 0)
232 	xpos = xoff;
233     else
234 	xpos = tui_cols () + xoff - maxw - 1;
235     if (yoff > 0)
236 	ypos = yoff;
237     else
238 	ypos = tui_lines () + yoff - height - 2;
239     w = tui_dlog(xpos, ypos, xpos+maxw, ypos+height+1);
240     tui_selwin (w);
241     tui_drawbox(w);
242     if (mtitle != NULL) {
243 	mvwaddstr (currwin, 0, 1, mtitle);
244     }
245     for (i = 0; i < 6; i++) {
246 	moresave[i][0] = mvwinch (currwin, 0, maxw-6+i);
247 	moresave[i][1] = mvwinch (currwin, height+1, maxw-6+i);
248     }
249     s = yp = 0; oldyp = -1;
250     for (;;) {
251 	int c;
252 	int s2;
253 
254 	while (selected < yp)
255 	    yp--;
256 	while (selected >= yp + height)
257 	    yp++;
258 	if (yp == 0)
259 	    for (i = 0; i < 6; i++)
260 		mvwaddch (currwin, 0, maxw-6+i, moresave[i][0]);
261 	else
262 	    mvwaddstr (currwin, 0, maxw-6, "(more)");
263 	if (yp + height == count)
264 	    for (i = 0; i < 6; i++)
265 		mvwaddch (currwin, height+1, maxw-6+i, moresave[i][1]);
266 	else
267 	    mvwaddstr (currwin, height+1, maxw-6, "(more)");
268 	for (i = s2 = j = 0; i < count; i++, j++) {
269 	    int k, x;
270 	    attr_t a = s2 == selected ? A_STANDOUT : 0;
271 	    while (menu[j].val == 0 || menu[j].val == -4)
272 		j++;
273 	    if (i >= yp && i < yp+height) {
274 		mvwaddch (currwin, 1+i-yp, 1, ' ' | a);
275 		for (k = x = 0; menu[j].data[k]; k++, x++) {
276 		    int a2 = 0;
277 		    c = menu[j].data[k];
278 		    if (c == '_')
279 			c = menu[j].data[++k], a2 = A_UNDERLINE;
280 		    mvwaddch (currwin, 1+i-yp, 2+x, c | a | a2);
281 		}
282 		for (; x < maxw-2; x++) {
283 		    mvwaddch (currwin, 1+i-yp, 2+x, ' ' | a);
284 		}
285 	    }
286 	    if (menu[j].val != -2)
287 		s2++;
288 	}
289 
290 	tui_refresh ();
291 	c = getch ();
292 	if (c == 27) {
293 	    tui_dlogdie (w);
294 	    return -1;
295 	} else if (c == KEY_ENTER || c == 13 || c == ' ') {
296 	    tui_dlogdie (w);
297 	    for (i = s2 = j = 0; s2 <= selected; j++) {
298 		if (menu[j].val == -4) {
299 		    i++; j++; continue;
300 		}
301 		while (menu[j].val == 0)
302 		    j++;
303 		if (s2 == selected)
304 		    return i;
305 		if (menu[j].val != -2)
306 		    s2++, i++;
307 	    }
308 	    abort();
309 	} else switch (c) {
310 	 case KEY_UP:
311 	    if (selected > 0)
312 		selected--;
313 	    break;
314 	 case KEY_DOWN:
315 	    if (selected + 1 < count)
316 		selected++;
317 	    break;
318 	 case KEY_PPAGE:
319 	    if (selected > height)
320 		selected -= height;
321 	    else
322 		selected = 0;
323 	    break;
324 	 case KEY_NPAGE:
325 	    if (selected + height < count)
326 		selected += height;
327 	    else
328 		selected = count-1;
329 	    break;
330 	 default:
331 	    for (j = i = 0; menu[i].val != -3; i++)
332 		if (menu[i].val == toupper (c)) {
333 		    tui_dlogdie (w);
334 		    return j;
335 		} else if (menu[i].val == -1 || menu[i].val == -4 || menu[i].val > 0) {
336 		    j++;
337 		}
338 
339 	    break;
340 	}
341     }
342     return -1; /* Can't get here */
343 }
344 
tui_errorbox(const char * err)345 void tui_errorbox(const char *err)
346 {
347     const char *hak = "Hit any key";
348     int n = strlen (hak);
349     int l = strlen (err);
350     int ww = (l > n ? l : n) + 2;
351     int w = tui_dlog((tui_cols ()-ww)/2, tui_lines ()/2-1, (tui_cols ()+ww)/2, tui_lines ()/2+1);
352     tui_selwin (w); tui_drawbox(w);
353 
354     wmove (currwin, 0, (ww-6)/2);
355     waddstr (currwin, "Error!");
356     wmove (currwin, 1, (ww-l)/2);
357     waddstr (currwin, err);
358     wmove (currwin, 2, (ww-n)/2);
359     waddstr (currwin, hak);
360 
361     wrefresh (currwin);
362     for (;;) {
363 	int c = getch ();
364 	if (c == 13)
365 	    break;
366     }
367     tui_dlogdie (w);
368 }
369 
370 static char *pattern;
371 static int maxlen;
372 
put_filename(char * s,int x,int y,attr_t a)373 static void put_filename (char *s, int x, int y, attr_t a)
374 {
375     char buf[256];
376     int i;
377 
378     tui_gotoxy (x,y);
379     if (strcmp (s, ".") == 0)
380 	strcpy (buf, "(none)");
381     else
382 	strcpy (buf, s);
383     buf[maxlen] = 0;
384     for (i = 0; i < strlen (buf); i++)
385 	waddch (currwin, buf[i] | a);
386     for (; i < maxlen; i++)
387 	waddch (currwin, ' ' | a);
388 }
389 
390 static char fsbuf[256];
391 
selectfn(const struct dirent * de)392 static int selectfn (const struct dirent *de)
393 {
394     int l1, l2;
395 
396 /*    l1 = strlen (pattern + 1);*/
397     l2 = strlen (de->d_name);
398 
399     if (l2 >= tui_cols ()-10) /* Restrict length of filenames so we won't mess up the display */
400 	return 0;
401 
402     /* No pattern matching for now. But we don't show hidden files. */
403     if (strcmp (de->d_name, ".") != 0 && strcmp (de->d_name, "..") != 0
404 	&& de->d_name[0] == '.')
405 	return 0;
406     if (l2 > maxlen)
407 	maxlen = l2;
408     return 1;
409 }
410 
my_alphasort(const void * a,const void * b)411 static int my_alphasort (const void *a, const void *b)
412 {
413     return strcmp ((*(struct dirent **) a)->d_name,
414 		   (*(struct dirent **) b)->d_name);
415 }
416 
tui_filereq(char * s,char * oldfile,const char * title)417 char *tui_filereq(char *s, char *oldfile, const char *title)
418 {
419     char cwd[256];
420     char *retval = fsbuf;
421     char *tmp;
422     int fin = 0;
423     chtype moresave[6][2];
424 
425     /* Save wd */
426     if (getcwd (cwd, 256) == NULL)
427 	return NULL;
428 
429     /* Change into directory of old file */
430     strcpy (fsbuf, oldfile);
431     tmp = strrchr (fsbuf, '/');
432     if (tmp != NULL) {
433 	*tmp = 0;
434 	if (strlen (fsbuf) > 0)
435 	    chdir (fsbuf);
436     }
437 
438     pattern = s;
439     if (s[0] != '*')
440 	write_log ("Can't handle wildcard %s\n", s);
441     if (s[1] != 0 && strchr (s+1, '*') != NULL)
442 	write_log ("Can't handle wildcard %s\n", s);
443     for (;!fin;) {
444 	struct dirent **names;
445 	int i, w, n, l, yp, oldyp, s;
446 
447 	maxlen = 0;
448 	n = scandir (".", &names, selectfn, my_alphasort);
449 
450 	if (n <= 0)
451 	    return NULL;
452 	if (title != NULL && strlen (title) + 6 > maxlen)
453 	    maxlen = strlen (title) + 6;
454 	l = n;
455 	if (l > 15)
456 	    l = 15;
457 	yp = s = 0; oldyp = -1;
458 	w = tui_dlog (tui_cols () - maxlen - 8, 5, tui_cols () - 5, 5 + l + 1);
459 	tui_selwin (w); tui_drawbox (w);
460 	if (title)
461 	    mvwaddstr (currwin, 0, 2, title);
462 	for (i = 0; i < 6; i++) {
463 	    moresave[i][0] = mvwinch (currwin, 0, maxlen-3+i);
464 	    moresave[i][1] = mvwinch (currwin, l+1, maxlen-3+i);
465 	}
466 	for (;;) {
467 	    int c;
468 	    char tmp[256];
469 	    while (s < yp)
470 		yp--;
471 	    while (s >= yp + l)
472 		yp++;
473 	    if (oldyp != yp) {
474 		oldyp = yp;
475 		for (i = 0; i < l; i++) {
476 		    put_filename (names[i + yp]->d_name, 3, 2 + i, 0);
477 		}
478 	    }
479 	    put_filename (names[s]->d_name, 3, 2 + s - yp, A_STANDOUT);
480 
481 	    if (yp == 0)
482 		for (i = 0; i < 6; i++)
483 		    mvwaddch (currwin, 0, maxlen-3+i, moresave[i][0]);
484 	    else
485 		mvwaddstr (currwin, 0, maxlen-3, "(more)");
486 	    if (yp + l == n)
487 		for (i = 0; i < 6; i++)
488 		    mvwaddch (currwin, l+1, maxlen-3+i, moresave[i][1]);
489 	    else
490 		mvwaddstr (currwin, l+1, maxlen-3, "(more)");
491 
492 	    tui_refresh ();
493 	    c = getch ();
494 	    put_filename (names[s]->d_name, 3, 2 + s - yp, 0);
495 	    if (c == 27) {
496 		retval = NULL; fin = 1;
497 		break;
498 	    } else if (c == KEY_ENTER || c == 13 || c == ' ') {
499 		int err;
500 
501 		if (strcmp (names[s]->d_name, ".") == 0) {
502 		    fin = 1;
503 		    strcpy (fsbuf, "");
504 		    break;
505 		}
506 		err = chdir (names[s]->d_name);
507 
508 		if (err == 0)
509 		    break;
510 		else if (errno == ENOTDIR) {
511 		    fin = 1;
512 		    if (getcwd (fsbuf, 256) == NULL)
513 			retval = NULL;
514 		    if (strlen (fsbuf) + strlen (names[s]->d_name) + 2 >= 256)
515 			retval = NULL;
516 		    else {
517 			strcat(fsbuf, "/");
518 			strcat(fsbuf, names[s]->d_name);
519 		    }
520 		    break;
521 		} /* else what? */
522 	    }
523 	    switch (c) {
524 	     case KEY_UP:
525 		if (s > 0)
526 		    s--;
527 		break;
528 	     case KEY_DOWN:
529 		if (s + 1 < n)
530 		    s++;
531 		break;
532 	     case KEY_PPAGE:
533 		if (s > l)
534 		    s -= l;
535 		else
536 		    s = 0;
537 		break;
538 	     case KEY_NPAGE:
539 		if (s + l < n)
540 		    s += l;
541 		else
542 		    s = n - 1;
543 		break;
544 	     default:
545 		i = 0;
546 		if (names[s]->d_name[0] == c)
547 		    i = s+1;
548 		for (; i < n*2; i++) {
549 		    int j = i;
550 		    if (i >= n)
551 			j -= n;
552 		    if (names[j]->d_name[0] == c) {
553 			s = j;
554 			break;
555 		    }
556 		}
557 	    }
558 	}
559 #if 0
560 	/* @@@ is this right? */
561 	for (i = 0; i < n; i++)
562 	    free (names[i]);
563 	free (names);
564 #endif
565 	tui_dlogdie (w);
566     }
567     chdir (cwd);
568     return retval;
569 }
570 
tui_backup_optionsfile(void)571 int tui_backup_optionsfile (void)
572 {
573     char tmp[257];
574     strcpy (tmp, optionsfile);
575     strcat (tmp, "~");
576     return rename (optionsfile, tmp);
577 }
578