diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2017-03-14 16:53:53 +0100 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2017-03-14 19:04:21 +0100 |
| commit | 9bcf2667e7e239873597b7ec2172206a9af18071 (patch) | |
| tree | 7e9ccb42fa665ba01be65b93fc995fa76719aaf7 /ui-terminal-curses.c | |
| parent | bed289a96e1ed17e4b9fa4f9e22227fcf13cc818 (diff) | |
| download | vis-9bcf2667e7e239873597b7ec2172206a9af18071.tar.gz vis-9bcf2667e7e239873597b7ec2172206a9af18071.tar.xz | |
Restructure display code
Use pull instead of push based model for display code. Previously view.c
was calling into the ui frontend code, with the new scheme this switches
around: the necessary data is fetched by the ui as necessary.
The UI independent display code is moved out of view.c/ui-curses.c into
vis.c. The cell styles are now directly embedded into the Cell struct.
New UI styles are introduced for:
- status bar (focused / non-focused)
- info message
- window separator
- EOF symbol
You will have to update your color themes.
The terminal output code is further abstracted into a generic ui-terminal.c
part which keeps track of the whole in-memory cell matrix and #includes
ui-terminal-curses.c for the actual terminal output. This architecture
currently assumes that there are no overlapping windows. It will also
allow non-curses based terminal user interfaces.
Diffstat (limited to 'ui-terminal-curses.c')
| -rw-r--r-- | ui-terminal-curses.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/ui-terminal-curses.c b/ui-terminal-curses.c new file mode 100644 index 0000000..5f0319b --- /dev/null +++ b/ui-terminal-curses.c @@ -0,0 +1,286 @@ +/* This file is included from ui-terminal.c */ +#include <stdio.h> +#include <curses.h> + +#define ui_term_backend_init ui_curses_init +#define ui_term_backend_blit ui_curses_blit +#define ui_term_backend_clear ui_curses_clear +#define ui_term_backend_colors ui_curses_colors +#define ui_term_backend_resize ui_curses_resize +#define ui_term_backend_restore ui_curses_restore +#define ui_term_backend_save ui_curses_save +#define ui_term_backend_new ui_curses_new +#define ui_term_backend_resume ui_curses_resume +#define ui_term_backend_suspend ui_curses_suspend +#define ui_term_backend_free ui_curses_suspend + +#define CELL_COLOR_BLACK COLOR_BLACK +#define CELL_COLOR_RED COLOR_RED +#define CELL_COLOR_GREEN COLOR_GREEN +#define CELL_COLOR_YELLOW COLOR_YELLOW +#define CELL_COLOR_BLUE COLOR_BLUE +#define CELL_COLOR_MAGENTA COLOR_MAGENTA +#define CELL_COLOR_CYAN COLOR_CYAN +#define CELL_COLOR_WHITE COLOR_WHITE +#define CELL_COLOR_DEFAULT (-1) + +#ifndef A_ITALIC +#define A_ITALIC A_NORMAL +#endif +#define CELL_ATTR_NORMAL A_NORMAL +#define CELL_ATTR_UNDERLINE A_UNDERLINE +#define CELL_ATTR_REVERSE A_REVERSE +#define CELL_ATTR_BLINK A_BLINK +#define CELL_ATTR_BOLD A_BOLD +#define CELL_ATTR_ITALIC A_ITALIC + +#ifdef NCURSES_VERSION +# ifndef NCURSES_EXT_COLORS +# define NCURSES_EXT_COLORS 0 +# endif +# if !NCURSES_EXT_COLORS +# define MAX_COLOR_PAIRS 256 +# endif +#endif +#ifndef MAX_COLOR_PAIRS +# define MAX_COLOR_PAIRS COLOR_PAIRS +#endif + +#define MAX_COLOR_CLOBBER 240 + +static short color_clobber_idx = 0; +static uint32_t clobbering_colors[MAX_COLOR_CLOBBER]; +static int change_colors = -1; + +/* Calculate r,g,b components of one of the standard upper 240 colors */ +static void get_6cube_rgb(unsigned int n, int *r, int *g, int *b) +{ + if (n < 16) { + return; + } else if (n < 232) { + n -= 16; + *r = (n / 36) ? (n / 36) * 40 + 55 : 0; + *g = ((n / 6) % 6) ? ((n / 6) % 6) * 40 + 55 : 0; + *b = (n % 6) ? (n % 6) * 40 + 55 : 0; + } else if (n < 256) { + n -= 232; + *r = n * 10 + 8; + *g = n * 10 + 8; + *b = n * 10 + 8; + } +} + +/* Reset color palette to default values using OSC 104 */ +static void undo_palette(void) +{ + fputs("\033]104;\a", stderr); + fflush(stderr); +} + +/* Work out the nearest color from the 256 color set, or perhaps exactly. */ +static CellColor color_rgb(UiTerm *ui, uint8_t r, uint8_t g, uint8_t b) +{ + if (change_colors == -1) + change_colors = ui->vis->change_colors && can_change_color() && COLORS >= 256; + if (change_colors) { + uint32_t hexrep = ((r << 16) | (g << 8) | b) + 1; + for (short i = 0; i < MAX_COLOR_CLOBBER; ++i) { + if (clobbering_colors[i] == hexrep) + return i + 16; + else if (!clobbering_colors[i]) + break; + } + + short i = color_clobber_idx; + clobbering_colors[i] = hexrep; + init_color(i + 16, (r * 1000) / 0xff, (g * 1000) / 0xff, + (b * 1000) / 0xff); + + /* in the unlikely case a user requests this many colors, reuse old slots */ + if (++color_clobber_idx >= MAX_COLOR_CLOBBER) + color_clobber_idx = 0; + + return i + 16; + } + + static const unsigned char color_256_to_16[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4, + 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, + 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12, + 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, + 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1, + 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12, + 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5, + 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3, + 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, + 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, + 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10, + 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, + 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, + 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8, + 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15 + }; + + int i = 0; + if ((!r || (r - 55) % 40 == 0) && + (!g || (g - 55) % 40 == 0) && + (!b || (b - 55) % 40 == 0)) { + i = 16; + i += r ? ((r - 55) / 40) * 36 : 0; + i += g ? ((g - 55) / 40) * 6 : 0; + i += g ? ((b - 55) / 40) : 0; + } else if (r == g && g == b && (r - 8) % 10 == 0 && r < 239) { + i = 232 + ((r - 8) / 10); + } else { + unsigned lowest = UINT_MAX; + for (int j = 16; j < 256; ++j) { + int jr = 0, jg = 0, jb = 0; + get_6cube_rgb(j, &jr, &jg, &jb); + int dr = jr - r; + int dg = jg - g; + int db = jb - b; + unsigned int distance = dr * dr + dg * dg + db * db; + if (distance < lowest) { + lowest = distance; + i = j; + } + } + } + + if (COLORS <= 16) + return color_256_to_16[i]; + return i; +} + +static CellColor color_terminal(UiTerm *ui, uint8_t index) { + return index; +} + +static inline unsigned int color_pair_hash(short fg, short bg) { + if (fg == -1) + fg = COLORS; + if (bg == -1) + bg = COLORS + 1; + return fg * (COLORS + 2) + bg; +} + +static short color_pair_get(short fg, short bg) { + static bool has_default_colors; + static short *color2palette, default_fg, default_bg; + static short color_pairs_max, color_pair_current; + + if (!color2palette) { + pair_content(0, &default_fg, &default_bg); + if (default_fg == -1) + default_fg = CELL_COLOR_WHITE; + if (default_bg == -1) + default_bg = CELL_COLOR_BLACK; + has_default_colors = (use_default_colors() == OK); + color_pairs_max = MIN(COLOR_PAIRS, MAX_COLOR_PAIRS); + if (COLORS) + color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(short)); + } + + if (fg >= COLORS) + fg = default_fg; + if (bg >= COLORS) + bg = default_bg; + + if (!has_default_colors) { + if (fg == -1) + fg = default_fg; + if (bg == -1) + bg = default_bg; + } + + if (!color2palette || (fg == -1 && bg == -1)) + return 0; + + unsigned int index = color_pair_hash(fg, bg); + if (color2palette[index] == 0) { + short oldfg, oldbg; + if (++color_pair_current >= color_pairs_max) + color_pair_current = 1; + pair_content(color_pair_current, &oldfg, &oldbg); + unsigned int old_index = color_pair_hash(oldfg, oldbg); + if (init_pair(color_pair_current, fg, bg) == OK) { + color2palette[old_index] = 0; + color2palette[index] = color_pair_current; + } + } + + return color2palette[index]; +} + +static inline attr_t style_to_attr(CellStyle *style) { + return style->attr | COLOR_PAIR(color_pair_get(style->fg, style->bg)); +} + +static void ui_curses_blit(UiTerm *tui) { + int w = tui->width, h = tui->height; + Cell *cell = tui->cells; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + attrset(style_to_attr(&cell->style)); + mvaddstr(y, x, cell->data); + cell++; + } + } + wnoutrefresh(stdscr); + doupdate(); +} + +static void ui_curses_clear(UiTerm *tui) { + clear(); +} + +static void ui_curses_resize(UiTerm *tui, int width, int height) { + resizeterm(height, width); + wresize(stdscr, height, width); +} + +static void ui_curses_save(UiTerm *tui) { + curs_set(1); + reset_shell_mode(); +} + +static void ui_curses_restore(UiTerm *tui) { + reset_prog_mode(); + wclear(stdscr); + curs_set(0); +} + +static int ui_curses_colors(Ui *ui) { + return COLORS; +} + +static bool ui_curses_init(UiTerm *tui, const char *term) { + if (!newterm(term, stderr, stdin)) { + snprintf(tui->info, sizeof(tui->info), "Warning: unknown term `%s'", term); + if (!newterm(strstr(term, "-256color") ? "xterm-256color" : "xterm", stderr, stdin)) + return false; + } + start_color(); + use_default_colors(); + raw(); + noecho(); + nonl(); + keypad(stdscr, TRUE); + meta(stdscr, TRUE); + curs_set(0); + return true; +} + +static UiTerm *ui_curses_new(void) { + return calloc(1, sizeof(UiTerm)); +} + +static void ui_curses_resume(UiTerm *term) { } + +static void ui_curses_suspend(UiTerm *term) { + if (change_colors == 1) + undo_palette(); + endwin(); +} + |
