aboutsummaryrefslogtreecommitdiff
path: root/ui-terminal-curses.c
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2017-03-14 16:53:53 +0100
committerMarc André Tanner <mat@brain-dump.org>2017-03-14 19:04:21 +0100
commit9bcf2667e7e239873597b7ec2172206a9af18071 (patch)
tree7e9ccb42fa665ba01be65b93fc995fa76719aaf7 /ui-terminal-curses.c
parentbed289a96e1ed17e4b9fa4f9e22227fcf13cc818 (diff)
downloadvis-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.c286
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();
+}
+