From ee1a18f050a5458ade460720091e20ce6b335011 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Thu, 18 May 2017 09:44:37 -0700 Subject: [PATCH] 3860 - stop buffering the screen in termbox To achieve this we have to switch to a model of the screen in termbox that is closer to the underlying terminal. Before: a screen is a grid of characters writing out of bounds does nothing After: a screen is a scrolling raster of characters writing out of bounds wraps to next line and scrolls if necessary To move to the new model, it was essential that I migrate my fake screen at the same time to mimic it. This is why the first attempt (commit 3824) failed (commit 3858). This is also why this commit can't be split into smaller pieces. The fake screen now 'scrolls' by rotating screen lines from top to bottom. There's still no notion of a scrollback buffer. The newer model is richer; it permits repl-like apps that upstream termbox can't do easily. It also permits us to simply use `printf` or `cout` to write to the screen, and everything mostly works as you would expect. Exceptions: a) '\n' won't do what you expect. You need to explicitly print both '\n' and '\r'. b) backspace won't do what you expect. It only moves the cursor back, without erasing the previous character. It does not wrap. Both behaviors exactly mimic my existing terminal's emulation of vt100. The catch: it's easy to accidentally scroll in apps. Out-of-bounds prints didn't matter before, but they're bugs now. To help track them down, use the `save-top-idx`, `assert-no-scroll` pair of helpers. An important trick is to wrap the cursor before rather after printing a character. Otherwise we end up scrolling every time we print to the bottom-right character. This means that the cursor position can be invalid at the start of a print, and we need to handle that. In the process we also lose the ability to hide and show the screen. We have to show the prints happening. Seems apt for a "white-box" platform like Mu. --- 080display.cc | 74 ++---- 081print.mu | 304 ++++++++++++++++++------- 100trace_browser.cc | 10 - edit/001-editor.mu | 2 - edit/002-typing.mu | 5 + edit/004-programming-environment.mu | 8 +- edit/005-sandbox.mu | 4 - edit/006-sandbox-copy.mu | 2 - edit/007-sandbox-delete.mu | 2 - edit/008-sandbox-edit.mu | 2 - edit/009-sandbox-test.mu | 2 - edit/010-sandbox-trace.mu | 2 - sandbox/001-editor.mu | 2 - sandbox/002-typing.mu | 5 + sandbox/004-programming-environment.mu | 8 +- sandbox/005-sandbox.mu | 4 - sandbox/006-sandbox-copy.mu | 2 - sandbox/007-sandbox-delete.mu | 2 - sandbox/008-sandbox-edit.mu | 2 - sandbox/009-sandbox-test.mu | 2 - sandbox/010-sandbox-trace.mu | 2 - termbox/termbox.c | 187 ++------------- termbox/termbox.h | 27 +-- termbox/x.cc | 12 +- 24 files changed, 277 insertions(+), 395 deletions(-) diff --git a/080display.cc b/080display.cc index 26b179268..b639c2a7b 100644 --- a/080display.cc +++ b/080display.cc @@ -7,7 +7,6 @@ :(before "End Globals") int Display_row = 0; int Display_column = 0; -bool Autodisplay = true; :(before "End Includes") #define CHECK_SCREEN \ @@ -38,6 +37,7 @@ case OPEN_CONSOLE: { :(before "End Primitive Recipe Implementations") case OPEN_CONSOLE: { tb_init(); + std::setvbuf(stdout, NULL, _IONBF, 0); // disable buffering in cout Display_row = Display_column = 0; int width = tb_width(); int height = tb_height(); @@ -128,29 +128,24 @@ case PRINT_CHARACTER_TO_DISPLAY: { if (bg_color == 0) bg_color = TB_BLACK; } tb_change_cell(Display_column, Display_row, c, color, bg_color); - if (c == '\n' || c == '\r') { - if (Display_row < height-1) { - Display_column = 0; - ++Display_row; - tb_set_cursor(Display_column, Display_row); - if (Autodisplay) tb_present(); - } - break; + // track row and column, mimicking what happens on screen + if (c == '\n') { + if (Display_row < height-1) ++Display_row; // otherwise we scroll and Display_row remains unchanged } - if (c == '\b') { - if (Display_column > 0) { - tb_change_cell(Display_column-1, Display_row, ' ', color, bg_color); - --Display_column; - tb_set_cursor(Display_column, Display_row); - if (Autodisplay) tb_present(); - } - break; + else if (c == '\r') { + Display_column = 0; } - if (Display_column < width-1) { + else if (c == '\b') { + if (Display_column > 0) --Display_column; + } + else { ++Display_column; - tb_set_cursor(Display_column, Display_row); + if (Display_column >= width) { + Display_column = 0; + if (Display_row < height-1) ++Display_row; + } } - if (Autodisplay) tb_present(); + tb_set_cursor(Display_column, Display_row); break; } @@ -197,7 +192,6 @@ case MOVE_CURSOR_ON_DISPLAY: { Display_row = ingredients.at(0).at(0); Display_column = ingredients.at(1).at(0); tb_set_cursor(Display_column, Display_row); - if (Autodisplay) tb_present(); break; } @@ -217,7 +211,6 @@ case MOVE_CURSOR_DOWN_ON_DISPLAY: { if (Display_row < height-1) { ++Display_row; tb_set_cursor(Display_column, Display_row); - if (Autodisplay) tb_present(); } break; } @@ -236,7 +229,6 @@ case MOVE_CURSOR_UP_ON_DISPLAY: { if (Display_row > 0) { --Display_row; tb_set_cursor(Display_column, Display_row); - if (Autodisplay) tb_present(); } break; } @@ -257,7 +249,6 @@ case MOVE_CURSOR_RIGHT_ON_DISPLAY: { if (Display_column < width-1) { ++Display_column; tb_set_cursor(Display_column, Display_row); - if (Autodisplay) tb_present(); } break; } @@ -276,7 +267,6 @@ case MOVE_CURSOR_LEFT_ON_DISPLAY: { if (Display_column > 0) { --Display_column; tb_set_cursor(Display_column, Display_row); - if (Autodisplay) tb_present(); } break; } @@ -292,7 +282,6 @@ void move_cursor_to_start_of_next_line_on_display() { else Display_row = 0; Display_column = 0; tb_set_cursor(Display_column, Display_row); - if (Autodisplay) tb_present(); } :(before "End Primitive Recipe Declarations") @@ -327,37 +316,6 @@ case DISPLAY_HEIGHT: { break; } -:(before "End Primitive Recipe Declarations") -HIDE_DISPLAY, -:(before "End Primitive Recipe Numbers") -put(Recipe_ordinal, "hide-display", HIDE_DISPLAY); -:(before "End Primitive Recipe Checks") -case HIDE_DISPLAY: { - break; -} -:(before "End Primitive Recipe Implementations") -case HIDE_DISPLAY: { - CHECK_SCREEN; - Autodisplay = false; - break; -} - -:(before "End Primitive Recipe Declarations") -SHOW_DISPLAY, -:(before "End Primitive Recipe Numbers") -put(Recipe_ordinal, "show-display", SHOW_DISPLAY); -:(before "End Primitive Recipe Checks") -case SHOW_DISPLAY: { - break; -} -:(before "End Primitive Recipe Implementations") -case SHOW_DISPLAY: { - CHECK_SCREEN; - Autodisplay = true; - tb_present(); - break; -} - //:: Keyboard/mouse management :(before "End Primitive Recipe Declarations") @@ -481,7 +439,6 @@ case CLEAR_LINE_ON_DISPLAY: { tb_change_cell(x, Display_row, ' ', TB_WHITE, TB_BLACK); } tb_set_cursor(Display_column, Display_row); - if (Autodisplay) tb_present(); break; } @@ -507,6 +464,5 @@ case CLEAR_DISPLAY_FROM: { tb_change_cell(column, row, ' ', TB_WHITE, TB_BLACK); } } - if (Autodisplay) tb_present(); break; } diff --git a/081print.mu b/081print.mu index b778948e2..2c2e09ae4 100644 --- a/081print.mu +++ b/081print.mu @@ -6,7 +6,9 @@ container screen [ num-columns:num cursor-row:num cursor-column:num - data:&:@:screen-cell + data:&:@:screen-cell # capacity num-rows*num-columns + top-idx:num # index inside data that corresponds to top-left of screen + # modified on scroll, wrapping around to the top of data ] container screen-cell [ @@ -18,9 +20,13 @@ def new-fake-screen w:num, h:num -> result:&:screen [ local-scope load-ingredients result <- new screen:type + non-zero-width?:bool <- greater-than w, 0 + assert non-zero-width?, [screen can't have zero width] + non-zero-height?:bool <- greater-than h, 0 + assert non-zero-height?, [screen can't have zero height] bufsize:num <- multiply w, h data:&:@:screen-cell <- new screen-cell:type, bufsize - *result <- merge h/num-rows, w/num-columns, 0/cursor-row, 0/cursor-column, data + *result <- merge h/num-rows, w/num-columns, 0/cursor-row, 0/cursor-column, data, 0/top-idx result <- clear-screen result ] @@ -48,6 +54,7 @@ def clear-screen screen:&:screen -> screen:&:screen [ # reset cursor *screen <- put *screen, cursor-row:offset, 0 *screen <- put *screen, cursor-column:offset, 0 + *screen <- put *screen, top-idx:offset, 0 ] def fake-screen-is-empty? screen:&:screen -> result:bool [ @@ -97,71 +104,152 @@ def print screen:&:screen, c:char -> screen:&:screen [ # (handle special cases exactly like in the real screen) width:num <- get *screen, num-columns:offset height:num <- get *screen, num-rows:offset - # if cursor is out of bounds, silently exit + capacity:num <- multiply width, height row:num <- get *screen, cursor-row:offset - row <- round row - legal?:bool <- greater-or-equal row, 0 - return-unless legal? - legal? <- lesser-than row, height - return-unless legal? column:num <- get *screen, cursor-column:offset + buf:&:@:screen-cell <- get *screen, data:offset + # some potentially slow sanity checks for preconditions { + # eliminate fractions from column and row + row <- round row column <- round column - legal? <- greater-or-equal column, 0 - return-unless legal? - legal? <- lesser-than column, width - return-unless legal? + # if cursor is past left margin (error), reset to left margin + { + too-far-left?:bool <- lesser-than column, 0 + break-unless too-far-left? + column <- copy 0 + *screen <- put *screen, cursor-column:offset, column + } + # if cursor is at right margin, wrap + { + at-right?:bool <- equal column, width + break-unless at-right? + column <- copy 0 + *screen <- put *screen, cursor-column:offset, column + row <- add row, 1 + *screen <- put *screen, cursor-row:offset, row + } + # if cursor is past right margin (error), reset to right margin + { + too-far-right?:bool <- greater-than column, width + break-unless too-far-right? + column <- subtract width, 1 + *screen <- put *screen, cursor-row:offset, row + } + # if row is above top margin (error), reset to top margin + { + too-far-up?:bool <- lesser-than row, 0 + break-unless too-far-up? + row <- copy 0 + *screen <- put *screen, cursor-row:offset, row + } + # if row is at bottom margin, scroll + { + at-bottom?:bool <- equal row, height + break-unless at-bottom? + scroll-fake-screen screen + row <- subtract height, 1 + *screen <- put *screen, cursor-row:offset, row + } + # if row is below bottom margin (error), reset to bottom margin + { + too-far-down?:bool <- greater-than row, height + break-unless too-far-down? + row <- subtract height, 1 + *screen <- put *screen, cursor-row:offset, row + } + # } #? $print [print-character (], row, [, ], column, [): ], c, 10/newline # special-case: newline { newline?:bool <- equal c, 10/newline break-unless newline? - { - # unless cursor is already at bottom - bottom:num <- subtract height, 1 - at-bottom?:bool <- greater-or-equal row, bottom - break-if at-bottom? - # move it to the next row - column <- copy 0 - *screen <- put *screen, cursor-column:offset, column - row <- add row, 1 - *screen <- put *screen, cursor-row:offset, row - } + cursor-down-on-fake-screen screen # doesn't modify column + return + } + # special-case: linefeed + { + linefeed?:bool <- equal c, 13/linefeed + break-unless linefeed? + *screen <- put *screen, cursor-column:offset, 0 return } - # save character in fake screen - index:num <- multiply row, width - index <- add index, column - buf:&:@:screen-cell <- get *screen, data:offset - len:num <- length *buf # special-case: backspace + # moves cursor left but does not erase { - backspace?:bool <- equal c, 8 + backspace?:bool <- equal c, 8/backspace break-unless backspace? { - # unless cursor is already at left margin - at-left?:bool <- lesser-or-equal column, 0 - break-if at-left? - # clear previous location + break-unless column column <- subtract column, 1 *screen <- put *screen, cursor-column:offset, column - index <- subtract index, 1 - cursor:screen-cell <- merge 32/space, 7/white - *buf <- put-index *buf, index, cursor } return } + # save character in fake screen + top-idx:num <- get *screen, top-idx:offset + index:num <- data-index row, column, width, height, top-idx cursor:screen-cell <- merge c, color *buf <- put-index *buf, index, cursor - # increment column unless it's already all the way to the right + # move cursor to next character + # (but don't bother making it valid; we'll do that before the next print) + column <- add column, 1 + *screen <- put *screen, cursor-column:offset, column +] + +def cursor-down-on-fake-screen screen:&:screen -> screen:&:screen [ + local-scope + load-ingredients + row:num <- get *screen, cursor-row:offset + height:num <- get *screen, num-rows:offset + bottom:num <- subtract height, 1 + at-bottom?:bool <- greater-or-equal row, bottom { - right:num <- subtract width, 1 - at-right?:bool <- greater-or-equal column, right - break-if at-right? - column <- add column, 1 - *screen <- put *screen, cursor-column:offset, column + break-if at-bottom? + row <- add row, 1 + *screen <- put *screen, cursor-row:offset, row + } + { + break-unless at-bottom? + scroll-fake-screen screen # does not modify row } ] +def scroll-fake-screen screen:&:screen -> screen:&:screen [ + local-scope + load-ingredients + width:num <- get *screen, num-columns:offset + height:num <- get *screen, num-rows:offset + buf:&:@:screen-cell <- get *screen, data:offset + # clear top line and 'rotate' it to the bottom + top-idx:num <- get *screen, top-idx:offset # 0 <= top-idx < len(buf) + next-top-idx:num <- add top-idx, width # 0 <= next-top-idx <= len(buf) + empty-cell:screen-cell <- merge 0, 0 + { + done?:bool <- greater-or-equal top-idx, next-top-idx + break-if done? + put-index *buf, top-idx, empty-cell + top-idx <- add top-idx, 1 + # no modulo; top-idx is always a multiple of width, + # so it can never wrap around inside this loop + loop + } + # top-idx now same as next-top-idx; wrap around if necessary + capacity:num <- multiply width, height + _, top-idx <- divide-with-remainder, top-idx, capacity + *screen <- put *screen, top-idx:offset, top-idx +] + +# translate from screen (row, column) coordinates to an index into data +# while accounting for scrolling (sliding top-idx) +def data-index row:num, column:num, width:num, height:num, top-idx:num -> result:num [ + local-scope + load-ingredients + result <- multiply width, row + result <- add result, column, top-idx + capacity:num <- multiply width, height + _, result <- divide-with-remainder result, capacity +] + scenario print-character-at-top-left [ local-scope fake-screen:&:screen <- new-fake-screen 3/width, 2/height @@ -232,7 +320,7 @@ scenario print-backspace-character [ memory-should-contain [ 10 <- 0 # cursor column 11 <- 6 # width*height - 12 <- 32 # space, not 'a' + 12 <- 97 # still 'a' 13 <- 7 # white # rest of screen is empty 14 <- 0 @@ -255,7 +343,7 @@ scenario print-extra-backspace-character [ memory-should-contain [ 1 <- 0 # cursor column 3 <- 6 # width*height - 4 <- 32 # space, not 'a' + 4 <- 97 # still 'a' 5 <- 7 # white # rest of screen is empty 6 <- 0 @@ -271,22 +359,26 @@ scenario print-character-at-right-margin [ b:char <- copy 98/b fake-screen <- print fake-screen, b run [ - # cursor now at right margin + # cursor now at next row c:char <- copy 99/c fake-screen <- print fake-screen, c - 10:num/raw <- get *fake-screen, cursor-column:offset + 10:num/raw <- get *fake-screen, cursor-row:offset + 11:num/raw <- get *fake-screen, cursor-column:offset cell:&:@:screen-cell <- get *fake-screen, data:offset - 11:@:screen-cell/raw <- copy *cell + 12:@:screen-cell/raw <- copy *cell ] memory-should-contain [ - 10 <- 1 # cursor column - 11 <- 4 # width*height - 12 <- 97 # 'a' - 13 <- 7 # white - 14 <- 99 # 'c' over 'b' - 15 <- 7 # white - # rest of screen is empty - 16 <- 0 + 10 <- 1 # cursor row + 11 <- 1 # cursor column + 12 <- 4 # width*height + 13 <- 97 # 'a' + 14 <- 7 # white + 15 <- 98 # 'b' + 16 <- 7 # white + 17 <- 99 # 'c' + 18 <- 7 # white + 19 <- 0 # ' ' + 20 <- 7 # white ] ] @@ -305,7 +397,7 @@ scenario print-newline-character [ ] memory-should-contain [ 10 <- 1 # cursor row - 11 <- 0 # cursor column + 11 <- 1 # cursor column 12 <- 6 # width*height 13 <- 97 # 'a' 14 <- 7 # white @@ -336,39 +428,81 @@ scenario print-newline-at-bottom-line [ scenario print-character-at-bottom-right [ local-scope fake-screen:&:screen <- new-fake-screen 2/width, 2/height - newline:char <- copy 10/newline - fake-screen <- print fake-screen, newline a:char <- copy 97/a fake-screen <- print fake-screen, a b:char <- copy 98/b fake-screen <- print fake-screen, b c:char <- copy 99/c fake-screen <- print fake-screen, c - fake-screen <- print fake-screen, newline run [ # cursor now at bottom right d:char <- copy 100/d fake-screen <- print fake-screen, d 10:num/raw <- get *fake-screen, cursor-row:offset 11:num/raw <- get *fake-screen, cursor-column:offset + 12:num/raw <- get *fake-screen, top-idx:offset cell:&:@:screen-cell <- get *fake-screen, data:offset 20:@:screen-cell/raw <- copy *cell ] + # cursor column overflows the screen but is not wrapped yet memory-should-contain [ 10 <- 1 # cursor row - 11 <- 1 # cursor column - 20 <- 4 # width*height - 21 <- 0 # unused + 11 <- 2 # cursor column -- outside screen + 12 <- 0 # top-idx -- not yet scrolled + 20 <- 4 # screen size (width*height) + 21 <- 97 # 'a' 22 <- 7 # white - 23 <- 0 # unused + 23 <- 98 # 'b' 24 <- 7 # white - 25 <- 97 # 'a' + 25 <- 99 # 'c' 26 <- 7 # white - 27 <- 100 # 'd' over 'b' and 'c' and newline + 27 <- 100 # 'd' 28 <- 7 # white - # rest of screen is empty - 29 <- 0 ] + run [ + e:char <- copy 101/e + print fake-screen, e + 10:num/raw <- get *fake-screen, cursor-row:offset + 11:num/raw <- get *fake-screen, cursor-column:offset + 12:num/raw <- get *fake-screen, top-idx:offset + cell:&:@:screen-cell <- get *fake-screen, data:offset + 20:@:screen-cell/raw <- copy *cell + ] + memory-should-contain [ + # text scrolls by 1, we lose the top line + 10 <- 1 # cursor row + 11 <- 1 # cursor column -- wrapped + 12 <- 2 # top-idx -- scrolled + 20 <- 4 # screen size (width*height) + # screen now checked in rotated order + 25 <- 99 # 'c' + 26 <- 7 # white + 27 <- 100 # 'd' + 28 <- 7 # white + # screen wraps; bottom line is cleared of old contents + 21 <- 101 # 'e' + 22 <- 7 # white + 23 <- 0 # unused + 24 <- 0 # no color + ] +] + +# even though our screen supports scrolling, some apps may want to avoid +# scrolling +# these helpers help check for scrolling at development time +def save-top-idx screen:&:screen -> result:num [ + local-scope + load-ingredients + return-unless screen, 0 # check is only for fake screens + result <- get *screen, top-idx:offset +] +def assert-no-scroll screen:&:screen, old-top-idx:num [ + local-scope + load-ingredients + return-unless screen + new-top-idx:num <- get *screen, top-idx:offset + no-scroll?:bool <- equal old-top-idx, new-top-idx + assert no-scroll?, [render should never use screen's scrolling capabilities] ] def clear-line screen:&:screen -> screen:&:screen [ @@ -398,10 +532,14 @@ def clear-line screen:&:screen -> screen:&:screen [ *screen <- put *screen, cursor-column:offset, original-column ] +# only for non-scrolling apps def clear-line-until screen:&:screen, right:num/inclusive -> screen:&:screen [ local-scope load-ingredients - _, column:num <- cursor-position screen + row:num, column:num <- cursor-position screen + height:num <- screen-height screen + past-bottom?:bool <- greater-or-equal row, height + return-if past-bottom? space:char <- copy 32/space bg-color:num, bg-color-found?:bool <- next-ingredient { @@ -597,22 +735,6 @@ def screen-height screen:&:screen -> height:num [ height <- display-height ] -def hide-screen screen:&:screen -> screen:&:screen [ - local-scope - load-ingredients - return-if screen # fake screen; do nothing - # real screen - hide-display -] - -def show-screen screen:&:screen -> screen:&:screen [ - local-scope - load-ingredients - return-if screen # fake screen; do nothing - # real screen - show-display -] - def print screen:&:screen, s:text -> screen:&:screen [ local-scope load-ingredients @@ -640,24 +762,32 @@ def print screen:&:screen, s:text -> screen:&:screen [ } ] -scenario print-text-stops-at-right-margin [ +scenario print-text-wraps-past-right-margin [ local-scope fake-screen:&:screen <- new-fake-screen 3/width, 2/height run [ fake-screen <- print fake-screen, [abcd] + 5:num/raw <- get *fake-screen, cursor-row:offset + 6:num/raw <- get *fake-screen, cursor-column:offset + 7:num/raw <- get *fake-screen, top-idx:offset cell:&:@:screen-cell <- get *fake-screen, data:offset 10:@:screen-cell/raw <- copy *cell ] memory-should-contain [ + 5 <- 1 # cursor-row + 6 <- 1 # cursor-column + 7 <- 0 # top-idx 10 <- 6 # width*height 11 <- 97 # 'a' 12 <- 7 # white 13 <- 98 # 'b' 14 <- 7 # white - 15 <- 100 # 'd' overwrites 'c' + 15 <- 99 # 'c' 16 <- 7 # white + 17 <- 100 # 'd' + 18 <- 7 # white # rest of screen is empty - 17 <- 0 + 19 <- 0 ] ] diff --git a/100trace_browser.cc b/100trace_browser.cc index 42a4d848a..ffa66b41c 100644 --- a/100trace_browser.cc +++ b/100trace_browser.cc @@ -276,7 +276,6 @@ bool start_search_editor(search_direction dir) { tb_change_cell(col, bottom_screen_line, '/', TB_WHITE, TB_BLACK); ++col; tb_set_cursor(col, bottom_screen_line); - tb_present(); string pattern; while (true) { int key = read_key(); @@ -294,25 +293,21 @@ bool start_search_editor(search_direction dir) { if (col > /*slash*/1) { --col; tb_set_cursor(col, bottom_screen_line); - tb_present(); } } else if (key == TB_KEY_ARROW_RIGHT) { if (col-/*slash*/1 < SIZE(pattern)) { ++col; tb_set_cursor(col, bottom_screen_line); - tb_present(); } } else if (key == TB_KEY_HOME || key == TB_KEY_CTRL_A) { col = /*skip slash*/1; tb_set_cursor(col, bottom_screen_line); - tb_present(); } else if (key == TB_KEY_END || key == TB_KEY_CTRL_E) { col = SIZE(pattern)+/*skip slash*/1; tb_set_cursor(col, bottom_screen_line); - tb_present(); } else if (key == TB_KEY_BACKSPACE || key == TB_KEY_BACKSPACE2) { if (col > /*slash*/1) { @@ -330,7 +325,6 @@ bool start_search_editor(search_direction dir) { tb_change_cell(SIZE(pattern)+/*skip slash*/1, bottom_screen_line, ' ', TB_WHITE, TB_BLACK); } tb_set_cursor(col, bottom_screen_line); - tb_present(); } } else if (key == TB_KEY_CTRL_K) { @@ -339,7 +333,6 @@ bool start_search_editor(search_direction dir) { for (int x = col; x < old_pattern_size+/*slash*/1; ++x) tb_change_cell(x, bottom_screen_line, ' ', TB_WHITE, TB_BLACK); tb_set_cursor(col, bottom_screen_line); - tb_present(); } else if (key == TB_KEY_CTRL_U) { int old_pattern_size = SIZE(pattern); @@ -349,7 +342,6 @@ bool start_search_editor(search_direction dir) { for (int x = SIZE(pattern)+/*slash*/1; x < old_pattern_size+/*skip slash*/1; ++x) tb_change_cell(x, bottom_screen_line, ' ', TB_WHITE, TB_BLACK); tb_set_cursor(/*start of pattern skipping slash*/1, bottom_screen_line); - tb_present(); } else if (key < 128) { // ascii only // update pattern @@ -360,7 +352,6 @@ bool start_search_editor(search_direction dir) { tb_change_cell(x, bottom_screen_line, pattern.at(x-/*slash*/1), TB_WHITE, TB_BLACK); ++col; tb_set_cursor(col, bottom_screen_line); - tb_present(); } } } @@ -444,7 +435,6 @@ void render() { render_line(screen_row, "~", /*cursor_line?*/false); // move cursor back to display row at the end tb_set_cursor(0, Display_row); - tb_present(); } int lines_hidden(int screen_row) { diff --git a/edit/001-editor.mu b/edit/001-editor.mu index d81278aeb..a6dde85bd 100644 --- a/edit/001-editor.mu +++ b/edit/001-editor.mu @@ -6,10 +6,8 @@ def main text:text [ local-scope load-ingredients open-console - hide-screen 0/screen e:&:editor <- new-editor text, 0/left, 5/right render 0/screen, e - show-screen 0/screen wait-for-event 0/console close-console ] diff --git a/edit/002-typing.mu b/edit/002-typing.mu index 8c48f1477..166934296 100644 --- a/edit/002-typing.mu +++ b/edit/002-typing.mu @@ -264,6 +264,7 @@ def insert-at-cursor editor:&:editor, c:char, screen:&:screen -> go-render?:bool def editor-render screen:&:screen, editor:&:editor -> screen:&:screen, editor:&:editor [ local-scope load-ingredients + old-top-idx:num <- save-top-idx screen left:num <- get *editor, left:offset right:num <- get *editor, right:offset row:num, column:num <- render screen, editor @@ -272,6 +273,7 @@ def editor-render screen:&:screen, editor:&:editor -> screen:&:screen, editor:&: draw-horizontal screen, row, left, right, 9480/horizontal-dotted row <- add row, 1 clear-screen-from screen, row, left, left, right + assert-no-scroll screen, old-top-idx ] scenario editor-handles-empty-event-queue [ @@ -1057,6 +1059,9 @@ after [ def draw-horizontal screen:&:screen, row:num, x:num, right:num -> screen:&:screen [ local-scope load-ingredients + height:num <- screen-height screen + past-bottom?:bool <- greater-or-equal row, height + return-if past-bottom? style:char, style-found?:bool <- next-ingredient { break-if style-found? diff --git a/edit/004-programming-environment.mu b/edit/004-programming-environment.mu index 0d4e907da..606cdfa2e 100644 --- a/edit/004-programming-environment.mu +++ b/edit/004-programming-environment.mu @@ -96,7 +96,6 @@ def event-loop screen:&:screen, console:&:console, env:&:environment, resources: } # if it's not global and not a touch event, send to appropriate editor { - hide-screen screen sandbox-in-focus?:bool <- get *env, sandbox-in-focus?:offset { break-if sandbox-in-focus? @@ -117,7 +116,6 @@ def event-loop screen:&:screen, console:&:console, env:&:environment, resources: screen <- render-all screen, env, render } screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env - show-screen screen } loop } @@ -396,7 +394,7 @@ def render-all screen:&:screen, env:&:environment, {render-editor: (recipe (addr local-scope load-ingredients trace 10, [app], [render all] - hide-screen screen + old-top-idx:num <- save-top-idx screen # top menu trace 11, [app], [render top menu] width:num <- screen-width screen @@ -414,14 +412,14 @@ def render-all screen:&:screen, env:&:environment, {render-editor: (recipe (addr # screen <- render-recipes screen, env, render-editor screen <- render-sandbox-side screen, env, render-editor - + # no early returns permitted # recipes:&:editor <- get *env, recipes:offset current-sandbox:&:editor <- get *env, current-sandbox:offset sandbox-in-focus?:bool <- get *env, sandbox-in-focus?:offset screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env # - show-screen screen + assert-no-scroll screen, old-top-idx ] def render-recipes screen:&:screen, env:&:environment, {render-editor: (recipe (address screen) (address editor) -> number number (address screen) (address editor))} -> screen:&:screen, env:&:environment [ diff --git a/edit/005-sandbox.mu b/edit/005-sandbox.mu index 5f08554dd..2e16e05b4 100644 --- a/edit/005-sandbox.mu +++ b/edit/005-sandbox.mu @@ -971,10 +971,8 @@ after [ render-from <- add render-from, 1 *env <- put *env, render-from:offset, render-from } - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env - show-screen screen loop +next-event } ] @@ -1003,10 +1001,8 @@ after [ break-if at-beginning? render-from <- subtract render-from, 1 *env <- put *env, render-from:offset, render-from - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env - show-screen screen loop +next-event } ] diff --git a/edit/006-sandbox-copy.mu b/edit/006-sandbox-copy.mu index 9df5e6254..d3f82e88d 100644 --- a/edit/006-sandbox-copy.mu +++ b/edit/006-sandbox-copy.mu @@ -128,10 +128,8 @@ after [ break-unless copy? copy?, env <- try-copy-sandbox click-row, env break-unless copy? - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env - show-screen screen loop +next-event } ] diff --git a/edit/007-sandbox-delete.mu b/edit/007-sandbox-delete.mu index 4fa3c37df..5167b0388 100644 --- a/edit/007-sandbox-delete.mu +++ b/edit/007-sandbox-delete.mu @@ -72,10 +72,8 @@ after [ break-unless delete? delete?, env <- try-delete-sandbox click-row, env break-unless delete? - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env - show-screen screen loop +next-event } ] diff --git a/edit/008-sandbox-edit.mu b/edit/008-sandbox-edit.mu index dd5c1bb91..2d591ad63 100644 --- a/edit/008-sandbox-edit.mu +++ b/edit/008-sandbox-edit.mu @@ -111,10 +111,8 @@ after [ break-unless edit? edit?, env <- try-edit-sandbox click-row, env break-unless edit? - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env - show-screen screen loop +next-event } ] diff --git a/edit/009-sandbox-test.mu b/edit/009-sandbox-test.mu index 023015ede..badd795b9 100644 --- a/edit/009-sandbox-test.mu +++ b/edit/009-sandbox-test.mu @@ -130,10 +130,8 @@ after [ # toggle its expected-response, and save session sandbox <- toggle-expected-response sandbox save-sandboxes env, resources - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env - show-screen screen loop +next-event } ] diff --git a/edit/010-sandbox-trace.mu b/edit/010-sandbox-trace.mu index 66a321d6c..8088577ae 100644 --- a/edit/010-sandbox-trace.mu +++ b/edit/010-sandbox-trace.mu @@ -200,10 +200,8 @@ after [ x:bool <- get *sandbox, display-trace?:offset x <- not x *sandbox <- put *sandbox, display-trace?:offset, x - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env - show-screen screen loop +next-event } ] diff --git a/sandbox/001-editor.mu b/sandbox/001-editor.mu index d81278aeb..a6dde85bd 100644 --- a/sandbox/001-editor.mu +++ b/sandbox/001-editor.mu @@ -6,10 +6,8 @@ def main text:text [ local-scope load-ingredients open-console - hide-screen 0/screen e:&:editor <- new-editor text, 0/left, 5/right render 0/screen, e - show-screen 0/screen wait-for-event 0/console close-console ] diff --git a/sandbox/002-typing.mu b/sandbox/002-typing.mu index 8c48f1477..166934296 100644 --- a/sandbox/002-typing.mu +++ b/sandbox/002-typing.mu @@ -264,6 +264,7 @@ def insert-at-cursor editor:&:editor, c:char, screen:&:screen -> go-render?:bool def editor-render screen:&:screen, editor:&:editor -> screen:&:screen, editor:&:editor [ local-scope load-ingredients + old-top-idx:num <- save-top-idx screen left:num <- get *editor, left:offset right:num <- get *editor, right:offset row:num, column:num <- render screen, editor @@ -272,6 +273,7 @@ def editor-render screen:&:screen, editor:&:editor -> screen:&:screen, editor:&: draw-horizontal screen, row, left, right, 9480/horizontal-dotted row <- add row, 1 clear-screen-from screen, row, left, left, right + assert-no-scroll screen, old-top-idx ] scenario editor-handles-empty-event-queue [ @@ -1057,6 +1059,9 @@ after [ def draw-horizontal screen:&:screen, row:num, x:num, right:num -> screen:&:screen [ local-scope load-ingredients + height:num <- screen-height screen + past-bottom?:bool <- greater-or-equal row, height + return-if past-bottom? style:char, style-found?:bool <- next-ingredient { break-if style-found? diff --git a/sandbox/004-programming-environment.mu b/sandbox/004-programming-environment.mu index 0f1280e35..53f9f8469 100644 --- a/sandbox/004-programming-environment.mu +++ b/sandbox/004-programming-environment.mu @@ -79,7 +79,6 @@ def event-loop screen:&:screen, console:&:console, env:&:environment, resources: } # not global and not a touch event { - hide-screen screen render?:bool <- handle-keyboard-event screen, current-sandbox, e:event break-unless render? # try to batch up rendering if there are more events queued up @@ -93,7 +92,6 @@ def event-loop screen:&:screen, console:&:console, env:&:environment, resources: } +finish-event screen <- update-cursor screen, current-sandbox, env - show-screen screen } loop } @@ -199,7 +197,7 @@ def render-all screen:&:screen, env:&:environment, {render-editor: (recipe (addr local-scope load-ingredients trace 10, [app], [render all] - hide-screen screen + old-top-idx:num <- save-top-idx screen # top menu trace 11, [app], [render top menu] width:num <- screen-width screen @@ -211,12 +209,12 @@ def render-all screen:&:screen, env:&:environment, {render-editor: (recipe (addr print screen, [ run (F4) ], 255/white, 161/reddish # screen <- render-sandbox-side screen, env, render-editor - + # no early returns permitted # current-sandbox:&:editor <- get *env, current-sandbox:offset screen <- update-cursor screen, current-sandbox, env # - show-screen screen + assert-no-scroll screen, old-top-idx ] # replaced in a later layer diff --git a/sandbox/005-sandbox.mu b/sandbox/005-sandbox.mu index e95007972..a7df7a542 100644 --- a/sandbox/005-sandbox.mu +++ b/sandbox/005-sandbox.mu @@ -793,9 +793,7 @@ after [ render-from <- add render-from, 1 *env <- put *env, render-from:offset, render-from } - hide-screen screen screen <- render-sandbox-side screen, env, render - show-screen screen jump +finish-event } ] @@ -822,9 +820,7 @@ after [ break-if at-beginning? render-from <- subtract render-from, 1 *env <- put *env, render-from:offset, render-from - hide-screen screen screen <- render-sandbox-side screen, env, render - show-screen screen jump +finish-event } ] diff --git a/sandbox/006-sandbox-copy.mu b/sandbox/006-sandbox-copy.mu index 995f4c7c1..4835f02ea 100644 --- a/sandbox/006-sandbox-copy.mu +++ b/sandbox/006-sandbox-copy.mu @@ -140,10 +140,8 @@ after [ break-unless copy? copy?, env <- try-copy-sandbox click-row, env break-unless copy? - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, current-sandbox, env - show-screen screen loop +next-event } ] diff --git a/sandbox/007-sandbox-delete.mu b/sandbox/007-sandbox-delete.mu index ddfbf6920..107c861c7 100644 --- a/sandbox/007-sandbox-delete.mu +++ b/sandbox/007-sandbox-delete.mu @@ -69,10 +69,8 @@ after [ break-unless delete? delete?, env <- try-delete-sandbox click-row, env break-unless delete? - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, current-sandbox, env - show-screen screen loop +next-event } ] diff --git a/sandbox/008-sandbox-edit.mu b/sandbox/008-sandbox-edit.mu index cb19ebc42..ec4fd578a 100644 --- a/sandbox/008-sandbox-edit.mu +++ b/sandbox/008-sandbox-edit.mu @@ -111,10 +111,8 @@ after [ break-unless edit? edit?, env <- try-edit-sandbox click-row, env break-unless edit? - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, current-sandbox, env - show-screen screen loop +next-event } ] diff --git a/sandbox/009-sandbox-test.mu b/sandbox/009-sandbox-test.mu index d7b8ed62a..1c24bcb8b 100644 --- a/sandbox/009-sandbox-test.mu +++ b/sandbox/009-sandbox-test.mu @@ -132,10 +132,8 @@ after [ # toggle its expected-response, and save session sandbox <- toggle-expected-response sandbox save-sandboxes env, resources - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, current-sandbox, env - show-screen screen loop +next-event } ] diff --git a/sandbox/010-sandbox-trace.mu b/sandbox/010-sandbox-trace.mu index f81d4151e..27f2915ac 100644 --- a/sandbox/010-sandbox-trace.mu +++ b/sandbox/010-sandbox-trace.mu @@ -190,10 +190,8 @@ after [ x:bool <- get *sandbox, display-trace?:offset x <- not x *sandbox <- put *sandbox, display-trace?:offset, x - hide-screen screen screen <- render-sandbox-side screen, env, render screen <- update-cursor screen, current-sandbox, env - show-screen screen loop +next-event } ] diff --git a/termbox/termbox.c b/termbox/termbox.c index 1ae275ce7..0ee4b283d 100644 --- a/termbox/termbox.c +++ b/termbox/termbox.c @@ -23,19 +23,10 @@ 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 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; @@ -45,22 +36,12 @@ static int termh = -1; static int inout; static int winch_fds[2]; -static int lastx = LAST_COORD_INIT; -static int lasty = LAST_COORD_INIT; static int cursor_x = 0; static int cursor_y = 0; static uint16_t background = TB_BLACK; 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); @@ -121,11 +102,6 @@ int tb_init(void) 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; } @@ -145,8 +121,6 @@ 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; @@ -157,76 +131,12 @@ int tb_is_active(void) return termw != -1; } -void tb_present() { - 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 (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; - } - } - write_cursor(cursor_x, cursor_y); - bytebuffer_flush(&output_buffer, inout); -} - -void tb_set_cursor(int cx, int cy) -{ - assert(termw != -1); - cursor_x = cx; - cursor_y = cy; - 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); - 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; + send_attr(fg, bg); + send_char(x, y, ch); + bytebuffer_flush(&output_buffer, inout); } int tb_poll_event(struct tb_event *event) @@ -263,7 +173,7 @@ void tb_clear(void) update_size(); buffer_size_change_request = 0; } - cellbuf_clear(&back_buffer); + send_clear(); } void tb_set_clear_attributes(uint16_t fg, uint16_t bg) @@ -293,73 +203,14 @@ static int convertnum(uint32_t num, char* buf) { #define WRITE_LITERAL(X) bytebuffer_append(&output_buffer, (X), sizeof(X)-1) #define WRITE_INT(X) bytebuffer_append(&output_buffer, buf, convertnum((X), buf)) -static void write_cursor(int x, int y) { +void tb_set_cursor(int x, int y) { char buf[32]; WRITE_LITERAL("\033["); WRITE_INT(y+1); WRITE_LITERAL(";"); WRITE_INT(x+1); WRITE_LITERAL("H"); -} - -static void write_sgr(uint16_t fg, uint16_t bg) { - char buf[32]; - WRITE_LITERAL("\033[38;5;"); - WRITE_INT(fg); - WRITE_LITERAL("m"); - WRITE_LITERAL("\033[48;5;"); - WRITE_INT(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); + bytebuffer_flush(&output_buffer, inout); } static void get_term_size(int *w, int *h) @@ -402,7 +253,14 @@ static void send_attr(uint16_t fg, uint16_t bg) bytebuffer_puts(&output_buffer, funcs[T_UNDERLINE]); if ((fg & TB_REVERSE) || (bg & TB_REVERSE)) bytebuffer_puts(&output_buffer, funcs[T_REVERSE]); - write_sgr(fgcol, bgcol); + char buf[32]; + WRITE_LITERAL("\033[38;5;"); + WRITE_INT(fgcol); + WRITE_LITERAL("m"); + WRITE_LITERAL("\033[48;5;"); + WRITE_INT(bgcol); + WRITE_LITERAL("m"); + bytebuffer_flush(&output_buffer, inout); lastfg = fg; lastbg = bg; } @@ -413,9 +271,7 @@ static void send_char(int x, int y, uint32_t c) char buf[7]; int bw = tb_utf8_unicode_to_char(buf, c); buf[bw] = '\0'; - if (x-1 != lastx || y != lasty) - write_cursor(x, y); - lastx = x; lasty = y; + tb_set_cursor(x, y); if(!c) buf[0] = ' '; // replace 0 with whitespace bytebuffer_puts(&output_buffer, buf); } @@ -432,16 +288,8 @@ static void send_clear(void) { send_attr(foreground, background); bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]); - write_cursor(cursor_x, cursor_y); + tb_set_cursor(cursor_x, cursor_y); bytebuffer_flush(&output_buffer, inout); - - /* we need to invalidate cursor position too and these two vars are - * used only for simple cursor positioning optimization, cursor - * actually may be in the correct place, but we simply discard - * optimization once and it gives us simple solution for the case when - * cursor moved */ - lastx = LAST_COORD_INIT; - lasty = LAST_COORD_INIT; } static void sigwinch_handler(int xxx) @@ -455,9 +303,6 @@ 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 64c1c4ebb..973061422 100644 --- a/termbox/termbox.h +++ b/termbox/termbox.h @@ -8,19 +8,12 @@ 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 in tb_cell.fg and tb_cell.bg. */ +/* Names for some foreground/background colors. */ #define TB_BLACK 232 #define TB_WHITE 255 -/* Colors in tb_cell can be combined using bitwise-OR with multiple - * of the following attributes. */ +/* Some attributes of screen cells that can be combined with colors using + * bitwise-OR. */ #define TB_BOLD 0x0100 #define TB_UNDERLINE 0x0200 #define TB_REVERSE 0x0400 @@ -44,17 +37,6 @@ int tb_is_active(void); int tb_width(void); int tb_height(void); -/* 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); - -/* 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); @@ -63,8 +45,7 @@ void tb_set_clear_attributes(uint16_t fg, uint16_t bg); /* Move the cursor. Upper-left character is (0, 0). */ void tb_set_cursor(int cx, int cy); -/* Modify a specific cell of the screen. Don't forget to call tb_present() to - * commit your changes. */ +/* Modify a specific cell of the screen. */ void tb_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg); /*** 2. Controlling keyboard events. */ diff --git a/termbox/x.cc b/termbox/x.cc index f6b04693c..1b715746c 100644 --- a/termbox/x.cc +++ b/termbox/x.cc @@ -1,12 +1,18 @@ #include +using std::cout; #include"termbox.h" int main() { tb_init(); + std::setvbuf(stdout, NULL, _IONBF, 0); + cout << tb_width() << ' ' << tb_height(); tb_event x; - tb_poll_event(&x); - std::cout << "a\nb\r\nc\r\n"; - tb_poll_event(&x); + for (int col = 0; col <= tb_width(); ++col) { + tb_set_cursor(col, 1); + tb_poll_event(&x); + cout << "a"; + tb_poll_event(&x); + } tb_shutdown(); return 0; }