juegos

Juegos de terminal de Pancho: Catan (TUI, GUI, web, servidor, PicoCalc), ajedrez, calculadora y minijuegos
git clone https://git.lu3dhn.xyz/juegos.git
Log | Files | Refs

tuikit.c (9997B)


      1 /* tuikit.c - piezas de la TUI comunes a los juegos. */
      2 #include <locale.h>
      3 #include <poll.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 #include "tuikit.h"
      7 
      8 /* ------------------------------------------------------------------ colores */
      9 
     10 short tk_xterm(Color c)
     11 {
     12     static int16_t cache[65536];
     13     static bool ready;
     14     if (!ready) { for (int i = 0; i < 65536; i++) cache[i] = -1; ready = true; }
     15     if (cache[c] >= 0) return cache[c];
     16     int r = (c >> 11) << 3, g = ((c >> 5) & 63) << 2, b = (c & 31) << 3;
     17     static const int LV[6] = { 0, 95, 135, 175, 215, 255 };
     18     int ri = 0, gi = 0, bi = 0;
     19     for (int i = 1; i < 6; i++) {
     20         if (abs(LV[i] - r) < abs(LV[ri] - r)) ri = i;
     21         if (abs(LV[i] - g) < abs(LV[gi] - g)) gi = i;
     22         if (abs(LV[i] - b) < abs(LV[bi] - b)) bi = i;
     23     }
     24     int dc = (LV[ri] - r) * (LV[ri] - r) + (LV[gi] - g) * (LV[gi] - g) + (LV[bi] - b) * (LV[bi] - b);
     25     int gray = (r + g + b) / 3, gk = (gray - 8 + 5) / 10;
     26     if (gk < 0) gk = 0;
     27     if (gk > 23) gk = 23;
     28     int gv = 8 + gk * 10;
     29     int dg = (gv - r) * (gv - r) + (gv - g) * (gv - g) + (gv - b) * (gv - b);
     30     short idx = (short)(dg < dc ? 232 + gk : 16 + 36 * ri + 6 * gi + bi);
     31     cache[c] = idx;
     32     return idx;
     33 }
     34 
     35 void tk_att(short fg, short bg, attr_t a)
     36 {
     37     if (fg < 0) fg = TK_FG;
     38     if (bg < 0) bg = TK_BG;
     39     int p = alloc_pair(fg, bg);
     40     if (p < 0) p = 0;
     41     attr_set(a, 0, &p);
     42 }
     43 
     44 void tk_attc(Color fg, Color bg, attr_t a) { tk_att(tk_xterm(fg), tk_xterm(bg), a); }
     45 
     46 /* ------------------------------------------------------------------ texto */
     47 
     48 int tk_put(int y, int x, const char *s, int max)
     49 {
     50     int n = 0;
     51     const char *p = s;
     52     while (*p && n < max) {
     53         const char *q = p;
     54         int c = (unsigned char)*q++;
     55         if (c >= 0xC0) while ((*q & 0xC0) == 0x80) q++;
     56         n++;
     57         p = q;
     58     }
     59     mvaddnstr(y, x, s, (int)(p - s));
     60     return n;
     61 }
     62 
     63 int tk_ulen(const char *s)
     64 {
     65     int n = 0;
     66     for (; *s; s++) if (((unsigned char)*s & 0xC0) != 0x80) n++;
     67     return n;
     68 }
     69 
     70 void tk_fill(int y, int x, int w, const char *ch)
     71 {
     72     for (int i = 0; i < w; i++) mvaddstr(y, x + i, ch);
     73 }
     74 
     75 void tk_box(int y, int x, int h, int w, const char *title, Color accent)
     76 {
     77     tk_att(TK_DEF, 236, 0);
     78     for (int j = 0; j < h; j++) tk_fill(y + j, x, w, " ");
     79     tk_att(tk_xterm(accent), 236, A_BOLD);
     80     mvaddstr(y, x, "┌"); mvaddstr(y, x + w - 1, "┐");
     81     mvaddstr(y + h - 1, x, "└"); mvaddstr(y + h - 1, x + w - 1, "┘");
     82     for (int i = 1; i < w - 1; i++) { mvaddstr(y, x + i, "─"); mvaddstr(y + h - 1, x + i, "─"); }
     83     for (int j = 1; j < h - 1; j++) { mvaddstr(y + j, x, "│"); mvaddstr(y + j, x + w - 1, "│"); }
     84     if (title && *title) {
     85         tk_att(tk_xterm(accent), 236, A_BOLD);
     86         mvaddstr(y, x + 2, " ");
     87         int n = tk_put(y, x + 3, title, w - 6);
     88         mvaddstr(y, x + 3 + n, " ");
     89     }
     90 }
     91 
     92 void tk_section(int y, int x, int w, const char *title)
     93 {
     94     tk_att(244, TK_DEF, 0);
     95     tk_fill(y, x, w, "─");
     96     tk_att(tk_xterm(C_ACCENT), TK_DEF, A_BOLD);
     97     mvaddstr(y, x + 1, " ");
     98     int n = tk_put(y, x + 2, title, w - 4);
     99     mvaddstr(y, x + 2 + n, " ");
    100 }
    101 
    102 void tk_clear(void)
    103 {
    104     erase();
    105     tk_att(TK_DEF, TK_DEF, 0);
    106     for (int y = 0; y < LINES; y++) tk_fill(y, 0, COLS, " ");
    107 }
    108 
    109 /* ------------------------------------------------------------------ lienzo */
    110 
    111 static Color *pix;
    112 static int pix_w, pix_h;
    113 static Surface pix_surf;
    114 
    115 static void span_pix(Surface *s, int x, int y, int n, Color c)
    116 {
    117     (void)s;
    118     Color *p = pix + y * pix_w + x;
    119     while (n--) *p++ = c;
    120 }
    121 
    122 Surface *tk_pix_begin(int w, int h)
    123 {
    124     if (w * 2 * h > pix_w * pix_h || !pix) {
    125         free(pix);
    126         pix = malloc(sizeof(Color) * (size_t)w * (size_t)h * 2);
    127     }
    128     pix_w = w; pix_h = h * 2;
    129     surf_init(&pix_surf, pix_w, pix_h, span_pix, 0);
    130     return &pix_surf;
    131 }
    132 
    133 void tk_pix_blit(int y0, int x0)
    134 {
    135     for (int r = 0; r < pix_h / 2; r++)
    136         for (int col = 0; col < pix_w; col++) {
    137             Color top = pix[(2 * r) * pix_w + col], bot = pix[(2 * r + 1) * pix_w + col];
    138             tk_att(tk_xterm(top), tk_xterm(bot), 0);
    139             mvaddstr(y0 + r, x0 + col, top == bot ? " " : "▀");
    140         }
    141 }
    142 
    143 void tk_pix_text(int y0, int x0, int px, int py, const char *s, Color fg, Color bg, attr_t a)
    144 {
    145     int col = x0 + px, row = y0 + py / 2;
    146     if (px < 0 || py < 0 || px >= pix_w || py >= pix_h) return;
    147     tk_attc(fg, bg, a);
    148     mvaddstr(row, col, s);
    149 }
    150 
    151 /* ------------------------------------------------------------------ formularios */
    152 
    153 void (*tk_form_logo)(int cols);
    154 
    155 void tk_draw_form(const Form *form, HitList *hits)
    156 {
    157     hits->n = 0;
    158     tk_clear();
    159     int H = LINES, W = COLS;
    160     if (tk_form_logo) tk_form_logo(W);
    161     int rows = form->n + 6;
    162     int w = 60 < W - 2 ? 60 : W - 2, h = rows < H - 4 ? rows : H - 4;
    163     int x = (W - w) / 2, y = 3 + (H - 4 - h) / 2;
    164     tk_box(y, x, h, w, form->title, C_ACCENT);
    165     int line = y + 2;
    166     for (int i = 0; i < form->n && line < y + h - 2; i++) {
    167         const Field *fd = &form->f[i];
    168         if (fd->hidden) continue;
    169         bool sel = i == form->cur;
    170         char buf[160];
    171         switch (fd->type) {
    172         case F_LABEL:
    173             tk_att(246, 236, 0);
    174             tk_put(line, x + 3, fd->label, w - 6);
    175             break;
    176         case F_BUTTON:
    177             snprintf(buf, sizeof buf, "  %s  ", fd->label);
    178             if (sel) tk_att(tk_xterm(C_BLACK), tk_xterm(C_ACCENT), A_BOLD);
    179             else tk_att(fd->disabled ? 242 : 255, 238, 0);
    180             tk_put(line, x + 3, buf, w - 6);
    181             break;
    182         case F_CHOICE:
    183             tk_att(sel ? tk_xterm(C_ACCENT) : 250, 236, sel ? A_BOLD : 0);
    184             tk_put(line, x + 3, fd->label, 22);
    185             snprintf(buf, sizeof buf, "◀ %s ▶", fd->nchoices ? fd->choices[fd->value] : "");
    186             if (sel) tk_att(tk_xterm(C_BLACK), tk_xterm(C_ACCENT), A_BOLD);
    187             else tk_att(255, 238, 0);
    188             tk_put(line, x + 26, buf, w - 29);
    189             break;
    190         case F_TEXT:
    191             tk_att(sel ? tk_xterm(C_ACCENT) : 250, 236, sel ? A_BOLD : 0);
    192             tk_put(line, x + 3, fd->label, 22);
    193             tk_att(255, 240, 0);
    194             tk_fill(line, x + 26, w - 30, " ");
    195             {
    196                 char sh[64];
    197                 const char *t = form_shown(fd, sh, sizeof sh);
    198                 tk_put(line, x + 27, t, w - 32);
    199                 if (sel) { tk_att(tk_xterm(C_BLACK), tk_xterm(C_ACCENT), 0); mvaddstr(line, x + 27 + tk_ulen(t), " "); }
    200             }
    201             break;
    202         }
    203         hit_add(hits, x + 1, line, w - 2, 1, HIT_APP, i);
    204         line++;
    205     }
    206     if (form->msg[0]) {
    207         tk_att(tk_xterm(form->msg_err ? C_ERR : C_OK), 236, A_BOLD);
    208         tk_put(y + h - 2, x + 3, form->msg, w - 6);
    209     }
    210     tk_att(244, TK_DEF, 0);
    211     tk_put(H - 1, 1, g_lang ? "↑↓ move · ←→ change · Enter select · Esc back"
    212                             : "↑↓ mover · ←→ cambiar · Enter elegir · Esc volver", W - 2);
    213     refresh();
    214 }
    215 
    216 /* ------------------------------------------------------------------ input */
    217 
    218 static int translate(int ch)
    219 {
    220     switch (ch) {
    221     case KEY_UP: return K_UP;
    222     case KEY_DOWN: return K_DOWN;
    223     case KEY_LEFT: return K_LEFT;
    224     case KEY_RIGHT: return K_RIGHT;
    225     case KEY_ENTER: case '\n': case '\r': return K_OK;
    226     case 27: return K_BACK;
    227     case '\t': case KEY_BTAB: return K_TAB;
    228     case KEY_BACKSPACE: case 127: case 8: return K_BKSP;
    229     case KEY_DC: return K_DEL;
    230     case KEY_HOME: return K_HOME;
    231     case KEY_END: return K_END;
    232     case KEY_PPAGE: return K_PGUP;
    233     case KEY_NPAGE: return K_PGDN;
    234     }
    235     if (ch >= KEY_F(1) && ch <= KEY_F(10)) return K_F1 + ch - KEY_F(1);
    236     return ch;
    237 }
    238 
    239 static int read_one(InEvent *ev)
    240 {
    241     wint_t wc;
    242     int r = get_wch(&wc);
    243     if (r == ERR) return 0;
    244     if (r == KEY_CODE_YES) {
    245         if (wc == KEY_RESIZE) { ev->type = EV_RESIZE; return 1; }
    246         if (wc == KEY_MOUSE) {
    247             MEVENT me;
    248             if (getmouse(&me) != OK) return 0;
    249             if (me.bstate & (BUTTON1_PRESSED | BUTTON1_CLICKED)) {
    250                 ev->type = EV_CLICK; ev->x = me.x; ev->y = me.y;
    251                 return 1;
    252             }
    253 #ifdef BUTTON5_PRESSED
    254             if (me.bstate & (BUTTON4_PRESSED | BUTTON5_PRESSED)) {
    255                 ev->type = EV_WHEEL; ev->key = (me.bstate & BUTTON4_PRESSED) ? 1 : -1; ev->x = me.x; ev->y = me.y;
    256                 return 1;
    257             }
    258 #endif
    259             return 0;
    260         }
    261         /* las teclas de ncurses que no conocemos se descartan: sus codigos
    262          * se pisan con los K_* (KEY_HOME caia como K_TAB) */
    263         int k = translate((int)wc);
    264         if (k == (int)wc) return 0;
    265         ev->type = EV_KEY; ev->key = k;
    266         return 1;
    267     }
    268     ev->type = EV_KEY;
    269     ev->key = wc < 256 ? translate((int)wc) : '?';
    270     return 1;
    271 }
    272 
    273 int tk_wait(int timeout_ms, const int *fds, int nfds, InEvent *ev)
    274 {
    275     ev->type = EV_NONE;
    276     if (read_one(ev)) return 1;
    277     struct pollfd pf[16];
    278     pf[0].fd = 0; pf[0].events = POLLIN;
    279     int n = 1;
    280     for (int i = 0; i < nfds && n < 16; i++) { pf[n].fd = fds[i]; pf[n].events = POLLIN; n++; }
    281     int r = poll(pf, (nfds_t)n, timeout_ms);
    282     if (r > 0 && (pf[0].revents & POLLIN)) return read_one(ev);
    283     return 0;
    284 }
    285 
    286 /* ------------------------------------------------------------------ inicio */
    287 
    288 void tk_close(void)
    289 {
    290     endwin();
    291     free(pix);
    292     pix = 0;
    293     pix_w = pix_h = 0;
    294 }
    295 
    296 void tk_open(void)
    297 {
    298     setlocale(LC_ALL, "");
    299     initscr();
    300     cbreak();
    301     noecho();
    302     keypad(stdscr, TRUE);
    303     nodelay(stdscr, TRUE);
    304     curs_set(0);
    305     set_escdelay(25);
    306     start_color();
    307     use_default_colors();
    308     {
    309         int p = alloc_pair(TK_FG, TK_BG);
    310         if (p > 0 && p < 256) bkgd((chtype)COLOR_PAIR(p) | ' ');
    311     }
    312 #ifdef BUTTON5_PRESSED
    313     mousemask(BUTTON1_PRESSED | BUTTON1_CLICKED | BUTTON4_PRESSED | BUTTON5_PRESSED, 0);
    314 #else
    315     mousemask(BUTTON1_PRESSED | BUTTON1_CLICKED, 0);
    316 #endif
    317     mouseinterval(0);
    318 }