URI: 
       Add sacc hackathon. - brcon2024-hackathons - Bitreichcon 2024 Hackathons
  HTML git clone git://bitreich.org/brcon2024-hackathons git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/brcon2024-hackathons
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR Submodules
       ---
   DIR commit 14f1a3a9baed42894753c5378cd2b3eb7ead1d4d
   DIR parent 82a5bb00c21621db20f28c368d204017c6d0ec82
  HTML Author: Christoph Lohmann <20h@r-36.net>
       Date:   Fri,  2 Aug 2024 15:40:33 +0200
       
       Add sacc hackathon.
       
       Diffstat:
         A sacc/README.md                      |      11 +++++++++++
         A sacc/ui_rogue.c                     |     938 +++++++++++++++++++++++++++++++
         A sacc/ui_rogue.tar.gz                |       0 
       
       3 files changed, 949 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/sacc/README.md b/sacc/README.md
       @@ -0,0 +1,11 @@
       +# Sacc Hackathon
       +
       +## Rouge Interface
       +
       +pazz0 created this wonderful rouge ui for sacc(1).
       +
       +Let us optimize it for release!
       +
       +Make it part of mainline sacc?
       +
       +
   DIR diff --git a/sacc/ui_rogue.c b/sacc/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);
       +}
   DIR diff --git a/sacc/ui_rogue.tar.gz b/sacc/ui_rogue.tar.gz
       Binary files differ.