about summary refs log tree commit diff stats
path: root/termbox
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2017-05-13 12:42:17 -0700
committerKartik K. Agaram <vc@akkartik.com>2017-05-13 12:42:17 -0700
commit0c0d1ea5cdb96a98e7eb62edbd1acb534ae12940 (patch)
tree7b64a6e98fb16d2bf02c5003acc2c14d7d1d6043 /termbox
parent8195ed4ee94f490d377b91caa0d79f21dd3e86ed (diff)
downloadmu-0c0d1ea5cdb96a98e7eb62edbd1acb534ae12940.tar.gz
3854
Revert commits 3824, 3850 and 3852. We'll redo them more carefully.
Diffstat (limited to 'termbox')
-rw-r--r--termbox/termbox.c156
-rw-r--r--termbox/termbox.h36
2 files changed, 180 insertions, 12 deletions
diff --git a/termbox/termbox.c b/termbox/termbox.c
index 6a22f109..36c9496a 100644
--- a/termbox/termbox.c
+++ b/termbox/termbox.c
@@ -23,10 +23,20 @@ extern int wcwidth (wchar_t);
 #include "output.inl"
 #include "input.inl"
 
+struct cellbuf {
+  int width;
+  int height;
+  struct tb_cell *cells;
+};
+
+#define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)]
+#define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1)
 #define LAST_COORD_INIT -1
 
 static struct termios orig_tios;
 
+static struct cellbuf back_buffer;
+static struct cellbuf front_buffer;
 static struct bytebuffer output_buffer;
 static struct bytebuffer input_buffer;
 
@@ -47,6 +57,11 @@ static uint16_t foreground = TB_WHITE;
 static void write_cursor(int x, int y);
 static void write_sgr(uint16_t fg, uint16_t bg);
 
+static void cellbuf_init(struct cellbuf *buf, int width, int height);
+static void cellbuf_resize(struct cellbuf *buf, int width, int height);
+static void cellbuf_clear(struct cellbuf *buf);
+static void cellbuf_free(struct cellbuf *buf);
+
 static void update_size(void);
 static void update_term_size(void);
 static void send_attr(uint16_t fg, uint16_t bg);
@@ -104,9 +119,14 @@ int tb_init(void)
   bytebuffer_puts(&output_buffer, funcs[T_ENTER_KEYPAD]);
   bytebuffer_puts(&output_buffer, funcs[T_ENTER_MOUSE]);
   bytebuffer_puts(&output_buffer, funcs[T_ENTER_BRACKETED_PASTE]);
-  bytebuffer_flush(&output_buffer, inout);
+  send_clear();
 
   update_term_size();
+  cellbuf_init(&back_buffer, termw, termh);
+  cellbuf_init(&front_buffer, termw, termh);
+  cellbuf_clear(&back_buffer);
+  cellbuf_clear(&front_buffer);
+
   return 0;
 }
 
@@ -126,6 +146,8 @@ void tb_shutdown(void)
   close(winch_fds[0]);
   close(winch_fds[1]);
 
+  cellbuf_free(&back_buffer);
+  cellbuf_free(&front_buffer);
   bytebuffer_free(&output_buffer);
   bytebuffer_free(&input_buffer);
   termw = termh = -1;
@@ -136,21 +158,88 @@ int tb_is_active(void)
   return termw != -1;
 }
 
+static void tb_repaint(bool force) {
+  int x,y,w,i;
+  struct tb_cell *back, *front;
+
+  assert(termw != -1);
+
+  /* invalidate cursor position */
+  lastx = LAST_COORD_INIT;
+  lasty = LAST_COORD_INIT;
+
+  if (buffer_size_change_request) {
+    update_size();
+    buffer_size_change_request = 0;
+  }
+
+  for (y = 0; y < front_buffer.height; ++y) {
+    for (x = 0; x < front_buffer.width; ) {
+      back = &CELL(&back_buffer, x, y);
+      front = &CELL(&front_buffer, x, y);
+      w = wcwidth(back->ch);
+      if (w < 1) w = 1;
+      if (!force && memcmp(back, front, sizeof(struct tb_cell)) == 0) {
+        x += w;
+        continue;
+      }
+      memcpy(front, back, sizeof(struct tb_cell));
+      send_attr(back->fg, back->bg);
+      if (w > 1 && x >= front_buffer.width - (w - 1)) {
+        // Not enough room for wide ch, so send spaces
+        for (i = x; i < front_buffer.width; ++i) {
+          send_char(i, y, ' ');
+        }
+      } else {
+        send_char(x, y, back->ch);
+        for (i = 1; i < w; ++i) {
+          front = &CELL(&front_buffer, x + i, y);
+          front->ch = 0;
+          front->fg = back->fg;
+          front->bg = back->bg;
+        }
+      }
+      x += w;
+    }
+  }
+  if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
+    write_cursor(cursor_x, cursor_y);
+  bytebuffer_flush(&output_buffer, inout);
+}
+
+void tb_present(void)
+{
+  tb_repaint(false);
+}
+
+void tb_sync(void)
+{
+  tb_repaint(true);
+}
+
 void tb_set_cursor(int cx, int cy)
 {
   assert(termw != -1);
   cursor_x = cx;
   cursor_y = cy;
-  write_cursor(cursor_x, cursor_y);
-  bytebuffer_flush(&output_buffer, inout);
+  if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
+    write_cursor(cursor_x, cursor_y);
 }
 
 void tb_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg)
 {
   assert(termw != -1);
-  send_attr(fg, bg);
-  send_char(x, y, ch);
-  bytebuffer_flush(&output_buffer, inout);
+  if ((unsigned)x >= (unsigned)back_buffer.width)
+    return;
+  if ((unsigned)y >= (unsigned)back_buffer.height)
+    return;
+  struct tb_cell c = {ch, fg, bg};
+  CELL(&back_buffer, x, y) = c;
+}
+
+struct tb_cell *tb_cell_buffer()
+{
+  return back_buffer.cells;
 }
 
 int tb_poll_event(struct tb_event *event)
@@ -187,7 +276,7 @@ void tb_clear(void)
     update_size();
     buffer_size_change_request = 0;
   }
-  send_clear();
+  cellbuf_clear(&back_buffer);
 }
 
 void tb_set_clear_attributes(uint16_t fg, uint16_t bg)
@@ -236,6 +325,56 @@ static void write_sgr(uint16_t fg, uint16_t bg) {
   WRITE_LITERAL("m");
 }
 
+static void cellbuf_init(struct cellbuf *buf, int width, int height)
+{
+  buf->cells = (struct tb_cell*)malloc(sizeof(struct tb_cell) * width * height);
+  assert(buf->cells);
+  buf->width = width;
+  buf->height = height;
+}
+
+static void cellbuf_resize(struct cellbuf *buf, int width, int height)
+{
+  if (buf->width == width && buf->height == height)
+    return;
+
+  int oldw = buf->width;
+  int oldh = buf->height;
+  struct tb_cell *oldcells = buf->cells;
+
+  cellbuf_init(buf, width, height);
+  cellbuf_clear(buf);
+
+  int minw = (width < oldw) ? width : oldw;
+  int minh = (height < oldh) ? height : oldh;
+  int i;
+
+  for (i = 0; i < minh; ++i) {
+    struct tb_cell *csrc = oldcells + (i * oldw);
+    struct tb_cell *cdst = buf->cells + (i * width);
+    memcpy(cdst, csrc, sizeof(struct tb_cell) * minw);
+  }
+
+  free(oldcells);
+}
+
+static void cellbuf_clear(struct cellbuf *buf)
+{
+  int i;
+  int ncells = buf->width * buf->height;
+
+  for (i = 0; i < ncells; ++i) {
+    buf->cells[i].ch = ' ';
+    buf->cells[i].fg = foreground;
+    buf->cells[i].bg = background;
+  }
+}
+
+static void cellbuf_free(struct cellbuf *buf)
+{
+  free(buf->cells);
+}
+
 static void get_term_size(int *w, int *h)
 {
   struct winsize sz;
@@ -329,6 +468,9 @@ static void sigwinch_handler(int xxx)
 static void update_size(void)
 {
   update_term_size();
+  cellbuf_resize(&back_buffer, termw, termh);
+  cellbuf_resize(&front_buffer, termw, termh);
+  cellbuf_clear(&front_buffer);
   send_clear();
 }
 
diff --git a/termbox/termbox.h b/termbox/termbox.h
index 97e3f524..c6cda6e1 100644
--- a/termbox/termbox.h
+++ b/termbox/termbox.h
@@ -9,12 +9,18 @@ extern "C" {
 /*** 1. Controlling the screen. */
 
 /* The screen is a 2D array of cells. */
+struct tb_cell {
+  uint32_t ch;  /* unicode character */
+  uint16_t fg;  /* foreground color (0-255) and attributes */
+  uint16_t bg;  /* background color (0-255) and attributes */
+};
 
-/* Names for some colors. */
+/* Names for some colors in tb_cell.fg and tb_cell.bg. */
 #define TB_BLACK 232
 #define TB_WHITE 255
 
-/* Some attributes of screen cells that can be combined with colors using bitwise-OR. */
+/* Colors in tb_cell can be combined using bitwise-OR with multiple
+ * of the following attributes. */
 #define TB_BOLD      0x0100
 #define TB_UNDERLINE 0x0200
 #define TB_REVERSE   0x0400
@@ -38,14 +44,34 @@ int tb_is_active(void);
 int tb_width(void);
 int tb_height(void);
 
-/* Clear the screen. */
+/* Update the screen with internal state. Most methods below modify just the
+ * internal state of the screen. Changes won't be visible until you call
+ * tb_present(). */
+void tb_present(void);
+
+/* Variant of tb_present() that always refreshes the entire screen. */
+void tb_sync(void);
+
+/* Returns a pointer to the internal screen state: a 1D array of cells in
+ * raster order. You'll need to call tb_width() and tb_height() for the
+ * array's dimensions. The array stays valid until tb_clear() or tb_present()
+ * are called. */
+struct tb_cell *tb_cell_buffer();
+
+/* Clear the internal screen state using either TB_DEFAULT or the
+ * color/attributes set by tb_set_clear_attributes(). */
 void tb_clear(void);
 void tb_set_clear_attributes(uint16_t fg, uint16_t bg);
 
-/* Move the cursor. Upper-left character is (0, 0). */
+/* Move the cursor. Upper-left character is (0, 0).
+ */
 void tb_set_cursor(int cx, int cy);
+/* To hide the cursor, call tb_set_cursor(TB_HIDE_CURSOR, TB_HIDE_CURSOR).
+ * Cursor starts out hidden. */
+#define TB_HIDE_CURSOR -1
 
-/* Modify a specific cell of the screen. */
+/* Modify a specific cell of the screen. Don't forget to call tb_present() to
+ * commit your changes. */
 void tb_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg);
 
 /*** 2. Controlling keyboard events. */