UI: Add pazz0's dungeon-crawler-like interface - sacc - sacc(omys), simple console gopher client HTML git clone git://bitreich.org/sacc/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/sacc/ DIR Log DIR Files DIR Refs DIR Tags DIR LICENSE --- DIR commit d16bb53db353da7b817b18809adf57b57c4f973e DIR parent 0fce134e1baa2ae47aeb0ee7c091e6c25651bcea HTML Author: Quentin Rameau <quinq@fifth.space> Date: Thu, 28 Mar 2024 12:13:51 +0100 UI: Add pazz0's dungeon-crawler-like interface This definitely looks fun and interesting Diffstat: A ui_rogue.c | 938 +++++++++++++++++++++++++++++++ 1 file changed, 938 insertions(+), 0 deletions(-) --- DIR diff --git a/ui_rogue.c b/ui_rogue.c @@ -0,0 +1,938 @@ +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <term.h> +#include <termios.h> +#include <unistd.h> +#include <sys/types.h> + +#include "common.h" +#include "config.h" + +#define C(c) #c +#define S(c) C(c) + +/* ncurses doesn't define those in term.h, where they're used */ +#ifndef OK +#define OK (0) +#endif +#ifndef ERR +#define ERR (-1) +#endif + +static struct termios tsave; +static struct termios tsacc; +static Item *curentry; +static int termset = ERR; +static char bufout[256]; +static char bufout2[256]; + +void drawscreen(void); + +uint32_t +fnv1a(int n,...) +{ + int i; + char *s; + va_list l; + uint32_t h; + + h = 0x811c9dc5; + + va_start(l, n); + for (i = 0; i < n; i++) { + for (s = va_arg(l, char*); *s; s++) { + h ^= *s; + h *= 0x01000193; + } + } + va_end(l); + + return h; +} + +uint32_t +xorshift(uint32_t *s) +{ + *s ^= *s << 13; + *s ^= *s >> 17; + *s ^= *s << 5; + return *s; +} + +struct cell { + char c; + size_t nitems; + Item **items; +}; + +#define MAPHEIGHT (25) +#define MAPWIDTH (80) +struct cell map[MAPHEIGHT][MAPWIDTH]; + +struct room { + struct room *p; + void *d; + size_t x, y; + size_t w, h; +}; + +struct rect { + struct rect *next, *next2; + struct room *room; + size_t x1, y1; + size_t x2, y2; + size_t d; +}; + +struct rect * +randomneighbor(struct rect *x, struct rect *rs, uint32_t *prng) +{ + struct rect *r, *result; + size_t n; + + n = 0; + result = NULL; + for (r = rs; r; r = r->next) { + if (r->y2 < x->y1 || r->y1 > x->y2 || r->x2 < x->x1 || r->x1 > x->x2) + continue; + if ((r->y2 == x->y1 || r->y1 == x->y2) && (r->x2 == x->x1 || r->x1 == x->x2)) + continue; + n++; + if (xorshift(prng) / (1. + UINT32_MAX) < 1. / n) + result = r; + } + + return result; +} + +#define ROOM_HEIGHT_MIN 3 +#define ROOM_WIDTH_MIN 5 +#define ROOM_MARGIN_MIN 1 +#define CELL_HEIGHT_MIN (ROOM_HEIGHT_MIN + ROOM_MARGIN_MIN + 3) +#define CELL_WIDTH_MIN (ROOM_WIDTH_MIN + ROOM_MARGIN_MIN + 3) +size_t +generaterooms_gnarf(uint32_t prng, struct room *rs, size_t l) +{ + struct rect *queuehead, *queuetail; + struct rect *r, *t; + struct rect *rects, *walk; + size_t w, h, i, j, rl, n; + int vertical; + struct room *room; + + r = malloc(sizeof(*r)); + r->x1 = r->y1 = ROOM_MARGIN_MIN; + r->x2 = MAPWIDTH; + r->y2 = MAPHEIGHT; + r->d = 0; + + queuetail = r; + queuetail->next = NULL; + queuehead = r; + + rects = NULL; + rl = 0; + + while (queuehead) { + r = queuehead; + if (queuetail == queuehead) + queuetail = NULL; + queuehead = queuehead->next; + + if (r->x2 - r->x1 >= CELL_WIDTH_MIN * 2 && r->y2 - r->y1 >= CELL_HEIGHT_MIN * 2) { + vertical = xorshift(&prng) & 1; + } else if (r->x2 - r->x1 >= CELL_WIDTH_MIN * 2) { + vertical = 0; + } else if (r->y2 - r->y1 >= CELL_HEIGHT_MIN * 2) { + vertical = 1; + } else { + r->next = rects; + rects = r; + rl++; + continue; + } + + if (vertical) { + w = r->x2 - r->x1; + h = CELL_HEIGHT_MIN + xorshift(&prng) % (1 + r->y2 - r->y1 - CELL_HEIGHT_MIN * 2); + } else { + w = CELL_WIDTH_MIN + xorshift(&prng) % (1 + r->x2 - r->x1 - CELL_WIDTH_MIN * 2); + h = r->y2 - r->y1; + } + + t = malloc(sizeof(*t)); + t->x1 = r->x1; + t->y1 = r->y1; + t->x2 = r->x1 + w; + t->y2 = r->y1 + h; + t->d = r->d + 1; + t->next = NULL; + t->room = NULL; + + if (!queuetail) { + queuehead = t; + queuetail = t; + } else { + queuetail->next = t; + queuetail = t; + } + + t = malloc(sizeof(*t)); + if (vertical) { + t->x1 = r->x1; + t->y1 = r->y1 + h; + } else { + t->x1 = r->x1 + w; + t->y1 = r->y1; + } + t->x2 = r->x2; + t->y2 = r->y2; + t->d = r->d + 1; + t->next = NULL; + t->room = NULL; + + queuetail->next = t; + queuetail = t; + + free(r); + } + + if (l > rl) + l = rl; + + for (r = rects; r; r = r->next) { + if (MAPHEIGHT / 2 >= r->y1 && MAPHEIGHT / 2 < r->y2 && + MAPWIDTH / 2 >= r->x1 && MAPWIDTH / 2 < r->x2) + break; + } + + i = 0; + rs[i].w = ROOM_WIDTH_MIN + xorshift(&prng) % (1 + r->x2 - r->x1 - ROOM_MARGIN_MIN - ROOM_WIDTH_MIN); + rs[i].h = ROOM_HEIGHT_MIN + xorshift(&prng) % (1 + r->y2 - r->y1 - ROOM_MARGIN_MIN - ROOM_HEIGHT_MIN); + rs[i].x = r->x1 + xorshift(&prng) % (1 + r->x2 - r->x1 - ROOM_MARGIN_MIN - rs[i].w); + rs[i].y = r->y1 + xorshift(&prng) % (1 + r->y2 - r->y1 - ROOM_MARGIN_MIN - rs[i].h); + rs[i].p = NULL; + r->room = &rs[i]; + + walk = r; + walk->next2 = NULL; + + i++; + for (; i < l;) { + t = randomneighbor(r, rects, &prng); + if (!t || t->room) { + n = 0; + for (t = walk; t; t = t->next2) { + n++; + if (xorshift(&prng) / (1. + UINT32_MAX) < 1. / n) + r = t; + + } + continue; + } + rs[i].w = ROOM_WIDTH_MIN + xorshift(&prng) % (1 + t->x2 - t->x1 - ROOM_MARGIN_MIN - ROOM_WIDTH_MIN); + rs[i].h = ROOM_HEIGHT_MIN + xorshift(&prng) % (1 + t->y2 - t->y1 - ROOM_MARGIN_MIN - ROOM_HEIGHT_MIN); + rs[i].x = t->x1 + xorshift(&prng) % (1 + t->x2 - t->x1 - ROOM_MARGIN_MIN - rs[i].w); + rs[i].y = t->y1 + xorshift(&prng) % (1 + t->y2 - t->y1 - ROOM_MARGIN_MIN - rs[i].h); + rs[i].p = r->room; + t->room = &rs[i]; + i++; + r = t; + r->next2 = walk; + walk = r; + } + + for (r = rects; r;) { + t = r->next; + free(r); + r = t; + } + + return l; +} + +size_t +distance(size_t x1, size_t y1, size_t x2, size_t y2) +{ + size_t d; + + if (y1 < y2) + d = y2 - y1; + else + d = y1 - y2; + if (x1 < x2) + d += x2 - x1; + else + d += x1 - x2; + + return d; +} + +void +nearestpoints(struct room *a, struct room *b, size_t *ax, size_t *ay, size_t *bx, size_t *by) +{ + if (a->y >= b->y && a->y < b->y + b->h) { + *ay = *by = a->y; + } else if (b->y >= a->y && b->y < a->y + a->h) { + *ay = *by = b->y; + } else if (a->y >= b->y) { + *ay = a->y; + *by = b->y + b->h - 1; + } else if (b->y >= a->y) { + *ay = a->y + a->h - 1; + *by = b->y; + } + + if (a->x >= b->x && a->x < b->x + b->w) { + *ax = *bx = a->x; + } else if (b->x >= a->x && b->x < a->x + a->w) { + *ax = *bx = b->x; + } else if (a->x >= b->x) { + *ax = a->x; + *bx = b->x + b->w - 1; + } else if (b->x >= a->x) { + *ax = a->x + a->w - 1; + *bx = b->x; + } +} + +void +connectrooms(struct room *a, struct room *b) +{ + size_t i, j; + ssize_t ii; + size_t x1, y1; + size_t x2, y2; + + nearestpoints(a, b, &x1, &y1, &x2, &y2); + + if (y1 > y2) { + ii = -1; + } else if (y2 > y1) { + ii = 1; + } else { + ii = 0; + } + +/* +printf("%lu\t%lu\t%d\n", y1, y2, ii); +*/ + for (i = y1; i != y2; i += ii) + map[i][x1].c = '.'; + + if (x1 > x2) { + ii = -1; + } else if (x2 > x1) { + ii = 1; + } else { + ii = 0; + } + + for (i = x1; i != x2; i += ii) + map[y2][i].c = '.'; +} + +void +rendermap(void) +{ + size_t i, j; + + for (i = 0; i < MAPHEIGHT; i++) { + for (j = 0; j < MAPWIDTH; j++) + putchar(map[i][j].c); + putchar('\n'); + } +} + +size_t +placeitems_hash(Item *item, size_t *assocs, size_t k) +{ + Dir *dir; + Item *citem; + size_t i; + + dir = item->dat; + for (i = 0; i < dir->nitems; i++) { + citem = &dir->items[i]; + /* TODO Somewhere else */ + if (!citem->host || !citem->port || !citem->selector) + continue; + assocs[i] = fnv1a(6, item->host, item->port, item->selector, citem->host, citem->port, citem->selector) % k; + } + + return k; +} + +#define POSITIONS_LENGTH 4 +enum { + Portal, + StaircaseDown, + Bookshelf, + Back +}; + +size_t px, py; + +void +generatemap(Item *item, int new) +{ + Dir *dir; + Item *citem; + size_t i, j, k, l, ir; + size_t x, y; + ssize_t n, m; + size_t *cassocs; + struct room *rooms, *r; + struct { + unsigned char x, y; + } positions[POSITIONS_LENGTH]; + uint32_t prng; + char buffer[3]; + + for (i = 0; i < MAPHEIGHT; i++) { + for (j = 0; j < MAPWIDTH; j++) { + map[i][j].c = '#'; + free(map[i][j].items); + map[i][j].items = NULL; + map[i][j].nitems = 0; + } + } + + dir = item->dat; + for (j = l = 0; j < dir->nitems; j++) { + if (dir->items[j].type == '0' || + dir->items[j].type == '1') + l++; + } + + k = 1 + l / 10; + rooms = calloc(k, sizeof(*rooms)); + if (!rooms) + return; + k = generaterooms_gnarf(fnv1a(3, item->host, item->port, item->selector), rooms, k); + + cassocs = calloc(dir->nitems, sizeof(*cassocs)); + if (!cassocs) + goto cleanup; + + k = placeitems_hash(item, cassocs, k); + + /* Insert rooms */ + for (i = 0; i < k; i++) { + for (y = rooms[i].y; y < rooms[i].y + rooms[i].h; y++) { + for (x = rooms[i].x; x < rooms[i].x + rooms[i].w; x++) + map[y][x].c = '.'; + } + } + + /* Insert connections */ + for (i = 0; i < k; i++) { + if (rooms[i].p) + connectrooms(&rooms[i], rooms[i].p); + } + + /* + Insert items + The placement of items affects the initial placement of the player, because they could have gone back to this map, so they should appear at the elevator/portal/stair they used. + */ + ir = fnv1a(4, item->host, item->port, item->selector, "initial_room") % k; + + for (i = 0; i < k; i++) { + snprintf(buffer, sizeof(buffer), "%d", i); + prng = fnv1a(4, item->host, item->port, item->selector, buffer); + for (j = 0, n = 0, m = rooms[i].h * rooms[i].w; j < m; j++) { + if ((m - j) * (xorshift(&prng) / (double)UINT32_MAX) < POSITIONS_LENGTH - n) { + positions[n].x = rooms[i].x + j % rooms[i].w; + positions[n].y = rooms[i].y + j / rooms[i].w; + n++; + } + if (n == POSITIONS_LENGTH) + break; + } + for (j = 0; j < dir->nitems; j++) { + if (cassocs[j] != i) + continue; + + citem = &dir->items[j]; + switch (citem->type) { + case '0': + x = positions[Bookshelf].x; + y = positions[Bookshelf].y; + if (map[y][x].nitems) + map[y][x].c = 'E'; + else + map[y][x].c = '?'; + break; + case '1': + if (strcmp(citem->host, item->host) || strcmp(citem->port, item->port)) { + x = positions[Portal].x; + y = positions[Portal].y; + if (map[y][x].nitems) + map[y][x].c = 'O'; + else + map[y][x].c = '0'; + } else { + x = positions[StaircaseDown].x; + y = positions[StaircaseDown].y; + if (map[y][x].nitems) + map[y][x].c = 'L'; + else + map[y][x].c = '>'; + } + break; + default: + continue; + } + map[y][x].nitems++; + map[y][x].items = realloc(map[y][x].items, map[y][x].nitems * sizeof(*map[y][x].items)); + map[y][x].items[map[y][x].nitems-1] = citem; + + if (new && j == dir->curline && citem->raw) { + px = x; + py = y; + } + } + + if (i == ir && item->entry != item) { + y = positions[Back].y; + x = positions[Back].x; + if (strcmp(item->entry->host, item->host) || strcmp(item->entry->port, item->port)) + map[y][x].c = '0'; + else + map[y][x].c = '<'; + map[y][x].nitems++; + map[y][x].items = realloc(map[y][x].items, map[y][x].nitems * sizeof(*map[y][x].items)); + map[y][x].items[map[y][x].nitems-1] = item->entry; + } + + if (i == ir && new && !dir->items[dir->curline].raw) { + px = positions[Back].x; + py = positions[Back].y; + } + } + free(cassocs); + +cleanup: + free(rooms); +} + +void +uisetup(void) +{ + tcgetattr(0, &tsave); + tsacc = tsave; + tsacc.c_lflag &= ~(ECHO|ICANON); + tsacc.c_cc[VMIN] = 1; + tsacc.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &tsacc); + + if (termset != OK) + /* setupterm call exits on error */ + termset = setupterm(NULL, 1, NULL); + putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + fflush(stdout); +} + +void +uicleanup(void) +{ + tcsetattr(0, TCSANOW, &tsave); + + if (termset != OK) + return; + + putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + fflush(stdout); +} + +char * +uiprompt(char *fmt, ...) +{ + va_list ap; + char *input = NULL; + size_t n; + ssize_t r; + + putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0)); + putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + va_start(ap, fmt); + vsnprintf(bufout, sizeof(bufout), fmt, ap); + va_end(ap); + + n = mbsprint(bufout, columns); + + putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + putp(tparm(cursor_address, lines-1, n, 0, 0, 0, 0, 0, 0, 0)); + + tsacc.c_lflag |= (ECHO|ICANON); + tcsetattr(0, TCSANOW, &tsacc); + fflush(stdout); + + n = 0; + r = getline(&input, &n, stdin); + + tsacc.c_lflag &= ~(ECHO|ICANON); + tcsetattr(0, TCSANOW, &tsacc); + putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + fflush(stdout); + + if (r == -1) { + clearerr(stdin); + clear(&input); + } else if (input[r - 1] == '\n') { + input[--r] = '\0'; + } + + return input; +} + +void +displaybar(char *s) { + size_t n; + + putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + putp(tparm(cursor_address, lines-2, 0, 0, 0, 0, 0, 0, 0, 0)); + putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + n = mbsprint(s, columns); + for (n = columns - n; n; n--) + putchar(' '); + + putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + fflush(stdout); +} + +void +vdisplayinfoline(char *fmt, va_list ap) +{ + putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0)); + + vsnprintf(bufout, sizeof(bufout), fmt, ap); + + mbsprint(bufout, columns); + + putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + fflush(stdout); +} + +void +uistatus(char *fmt, ...) +{ + va_list ap; + size_t n; + + putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0)); + + va_start(ap, fmt); + n = vsnprintf(bufout, sizeof(bufout), fmt, ap); + va_end(ap); + + if (n < sizeof(bufout)-1) { + snprintf(bufout+n, sizeof(bufout)-n, + " [Press a key to continue \xe2\x98\x83]"); + } + + mbsprint(bufout, columns); + + putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + fflush(stdout); + + getchar(); +} + +void +displayinfoline(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vdisplayinfoline(fmt, ap); + va_end(ap); +} + +Item * +showmenu(char *title, Item **item, size_t l) +{ + size_t i; + + putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + printf("%s\n", title); + putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + for (i = 0; i < l; i++) + printf("%lu\t%s\n", i, item[i]->username); + + if (!scanf("%lu", &i) || i >= l) + return NULL; + fflush(stdout); + + return item[i]; +} + +Item * +prompt(char *text, Item *item) +{ + displayinfoline(text, item->username); + getchar(); + return item; +} + +Item * +interact(Item *item) +{ + Item *selection; + + selection = NULL; + + switch (map[py][px].c) { + case '?': + case '0': + case '>': + case '<': + selection = map[py][px].items[0]; + break; + case 'E': + selection = showmenu("Bookshelf", map[py][px].items, map[py][px].nitems); + break; + case 'O': + selection = showmenu("Portal machine", map[py][px].items, map[py][px].nitems); + break; + case 'L': + selection = showmenu("Elevator", map[py][px].items, map[py][px].nitems); + break; + } + + drawscreen(); + + if (selection) { + switch (map[py][px].c) { + case '?': + case 'E': + displayinfoline("A loading bar?! In a book?!"); + break; + case 'O': + case '0': + displayinfoline("You are getting transported through time and space."); + break; + case 'L': + displayinfoline("You hear elevator music..."); + break; + case '<': + case '>': + displayinfoline("Too many stairs..."); + break; + } + } + + return selection; +} + +void +describe(size_t x, size_t y, int verbose) +{ + switch (map[y][x].c) { + case 'E': + displayinfoline("A bookshelf."); + break; + case 'O': + displayinfoline("A portal machine."); + break; + case 'L': + displayinfoline("An elevator."); + break; + case '?': + case '>': + case '<': + case '0': + if (*map[y][x].items[0]->username) { + displayinfoline("'%s'.", map[y][x].items[0]->username); + } else { + itemuri(map[y][x].items[0], bufout2, sizeof(bufout2)); + displayinfoline("'%s'.", bufout2); + } + break; + default: + if (verbose) { + switch (map[y][x].c) { + case '.': + displayinfoline("Floor."); + break; + case '#': + displayinfoline("Wall."); + break; + } + } else { + displayinfoline(""); + } + break; + } +} + +void +move(ssize_t dx, ssize_t dy) +{ + size_t x, y; + + /* allow wraparound of the world for the lulz, even if it's not happening */ + y = (MAPHEIGHT + py + dy) % MAPHEIGHT; + x = (MAPWIDTH + px + dx) % MAPWIDTH; + + if (map[y][x].c == '#') + return; + + putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); + putchar(map[py][px].c); + + py = y; + px = x; + putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); + putchar('@'); + putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); + + describe(x, y, 0); + putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); +} + +void +drawscreen(void) +{ + Dir *dir; + + putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + rendermap(); + + if (curentry->entry != curentry && (dir = curentry->entry->dat)) { + displaybar(dir->items[dir->curline].username); + } else { + itemuri(curentry, bufout, sizeof(bufout)); + displaybar(bufout); + } + + move(0, 0); + +} + +void +uidisplay(Item *entry) +{ + if (!entry || entry->type != '1') + return; + + generatemap(entry, curentry != entry); + + curentry = entry; + drawscreen(); +} + +void +lookmode(void) +{ + size_t x, y; + + x = px; + y = py; + + for (;;) { + switch (getchar()) { + case 0x1B: + case 'q': + putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0)); + return; + case 'h': + x = (MAPWIDTH + x - 1) % MAPWIDTH; + break; + case 'j': + y = (y + 1) % MAPHEIGHT; + break; + case 'k': + y = (MAPHEIGHT + y - 1) % MAPHEIGHT; + break; + case 'l': + x = (x + 1) % MAPWIDTH; + break; + } + putp(tparm(cursor_address, y, x, 0, 0, 0, 0, 0, 0, 0)); + describe(x, y, 1); + } +} + +Item * +uiselectitem(Item *entry) +{ + Dir *dir; + Item *e; + size_t i; + + if (!entry || !(dir = entry->dat)) + return NULL; + + for (;;) { + switch (getchar()) { + case 'h': + move(-1, 0); + break; + case 'j': + move(0, 1); + break; + case 'k': + move(0, -1); + break; + case 'l': + move(1, 0); + break; + case 'L': + lookmode(); + break; + case ' ': + /* Portals, stairs, bookshelfs */ + if (e = interact(entry)) { + if (e->type == '1') { + for (i = 0; i < dir->nitems; i++) { + if (e == &dir->items[i]) { + dir->curline = i; + break; + } + } + } + return e; + } + break; + case 'q': + return NULL; + } + } +} + +void +uisigwinch(int signal) +{ + Dir *dir; + + if (termset == OK) + del_curterm(cur_term); + termset = setupterm(NULL, 1, NULL); + + if (!curentry || !(dir = curentry->dat)) + return; + + uidisplay(curentry); +}