implemented scrolling region and origin mode

pull/111/merge
Ondřej Hruška 7 years ago
parent 2c7c496862
commit dd68c112b0
  1. 2
      user/apars_csi.c
  2. 7
      user/cgi_sockets.c
  3. 133
      user/screen.c

@ -276,7 +276,7 @@ apars_handle_csi(char leadchar, const int *params, int count, char keychar)
case 'r': case 'r':
if (leadchar == NUL && (count == 2 || count == 0)) { if (leadchar == NUL && (count == 2 || count == 0)) {
screen_set_scrolling_region(n1, n2); screen_set_scrolling_region(n1-1, n2-1);
} }
else if (leadchar == '?') { else if (leadchar == '?') {
// Restore private attributes (CSI ? Pm h/l) // Restore private attributes (CSI ? Pm h/l)

@ -6,6 +6,7 @@
#include "uart_driver.h" #include "uart_driver.h"
#include "screen.h" #include "screen.h"
#include "uart_buffer.h" #include "uart_buffer.h"
#include "ansi_parser.h"
#define SOCK_BUF_LEN 1024 #define SOCK_BUF_LEN 1024
static char sock_buff[SOCK_BUF_LEN]; static char sock_buff[SOCK_BUF_LEN];
@ -128,6 +129,10 @@ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
if (strstarts(data, "STR:")) { if (strstarts(data, "STR:")) {
// pass string verbatim // pass string verbatim
UART_SendAsync(data+4, -1); UART_SendAsync(data+4, -1);
// debug loopback
// for(int i=4;i<strlen(data); i++) {
// ansi_parser(data[i]);
// }
} }
else if (strstarts(data, "BTN:")) { else if (strstarts(data, "BTN:")) {
// send button as low ASCII value 1-9 // send button as low ASCII value 1-9
@ -167,7 +172,7 @@ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
// Send as 1-based to user // Send as 1-based to user
sprintf(buf, "\033[%d;%dM", y+1, x+1); sprintf(buf, "\033[%d;%dM", y+1, x+1);
UART_WriteString(UART0, buf, UART_TIMEOUT_US); UART_SendAsync(buf, -1);
} }
else { else {
ws_warn("Bad command."); ws_warn("Bad command.");

@ -48,13 +48,21 @@ static struct {
bool numpad_alt_mode; //!< Application Mode - affects how user input of control keys is sent bool numpad_alt_mode; //!< Application Mode - affects how user input of control keys is sent
bool cursors_alt_mode; //!< Application mode for cursor keys bool cursors_alt_mode; //!< Application mode for cursor keys
bool newline_mode; bool newline_mode; // LNM - CR automatically sends LF
bool reverse; bool reverse;
// Vertical margin bounds (inclusive start/end of scrolling region)
int vm0;
int vm1;
char charset0; char charset0;
char charset1; char charset1;
} scr; } scr;
#define R0 scr.vm0
#define R1 scr.vm1
#define RH (scr.vm1 - scr.vm0 + 1)
typedef struct { typedef struct {
int x; //!< X coordinate int x; //!< X coordinate
int y; //!< Y coordinate int y; //!< Y coordinate
@ -69,8 +77,10 @@ typedef struct {
// Other attribs // Other attribs
int charsetN; int charsetN;
bool wraparound; //!< Wrapping when EOL bool wraparound; //!< Wrapping when EOL
bool origin_mode; // DECOM - absolute positioning is relative to vertical margins
bool selective_erase; // TODO implement
// Not saved/restored // Not saved/restored FIXME those should not be saved, but are (a bug?)
bool insert_mode; //!< Insert mode (move rest of the line to the right) bool insert_mode; //!< Insert mode (move rest of the line to the right)
bool visible; //!< Visible (not attribute, DEC special) bool visible; //!< Visible (not attribute, DEC special)
} CursorTypeDef; } CursorTypeDef;
@ -105,6 +115,8 @@ static volatile int notifyLock = 0;
if (cursor.hanging && cursor.x != W-1) cursor.hanging = false; \ if (cursor.hanging && cursor.x != W-1) cursor.hanging = false; \
} while(false) } while(false)
#define cursor_inside_region() (cursor.y >= R0 && cursor.y <= R1)
//region --- Settings --- //region --- Settings ---
/** /**
@ -198,6 +210,8 @@ cursor_reset(void)
cursor.hanging = false; cursor.hanging = false;
cursor.visible = true; cursor.visible = true;
cursor.insert_mode = false; cursor.insert_mode = false;
cursor.selective_erase = false;
cursor.origin_mode = false;
cursor.charsetN = 0; cursor.charsetN = 0;
cursor.wraparound = true; cursor.wraparound = true;
@ -235,6 +249,9 @@ screen_reset(void)
scr.charset0 = 'B'; scr.charset0 = 'B';
scr.charset1 = '0'; scr.charset1 = '0';
scr.vm0 = 0;
scr.vm1 = H-1;
// size is left unchanged // size is left unchanged
screen_clear(CLEAR_ALL); screen_clear(CLEAR_ALL);
@ -385,7 +402,7 @@ screen_tab_reverse(int count)
/** /**
* Clear range, inclusive * Clear range, inclusive
*/ */
static inline void static void ICACHE_FLASH_ATTR
clear_range(unsigned int from, unsigned int to) clear_range(unsigned int from, unsigned int to)
{ {
if (to >= W*H) to = W*H-1; if (to >= W*H) to = W*H-1;
@ -412,6 +429,7 @@ clear_range(unsigned int from, unsigned int to)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_clear(ClearMode mode) screen_clear(ClearMode mode)
{ {
// Those ignore margins and DECOM
NOTIFY_LOCK(); NOTIFY_LOCK();
switch (mode) { switch (mode) {
case CLEAR_ALL: case CLEAR_ALL:
@ -467,14 +485,15 @@ screen_clear_in_line(unsigned int count)
void screen_insert_lines(unsigned int lines) void screen_insert_lines(unsigned int lines)
{ {
if (!cursor_inside_region()) return; // can't insert if not inside region
NOTIFY_LOCK(); NOTIFY_LOCK();
// shift the following lines // shift the following lines
int targetStart = cursor.y + lines; int targetStart = cursor.y + lines;
if (targetStart >= H) { if (targetStart > R1) {
targetStart = H; targetStart = R1-1;
} else { } else {
// do the moving // do the moving
for (int i = H-1; i >= targetStart; i--) { for (int i = R1; i >= targetStart; i--) {
memcpy(screen+i*W, screen+(i-lines)*W, sizeof(Cell)*W); memcpy(screen+i*W, screen+(i-lines)*W, sizeof(Cell)*W);
} }
} }
@ -490,7 +509,9 @@ void screen_insert_lines(unsigned int lines)
void screen_delete_lines(unsigned int lines) void screen_delete_lines(unsigned int lines)
{ {
if (!cursor_inside_region()) return; // can't delete if not inside region
NOTIFY_LOCK(); NOTIFY_LOCK();
// shift lines up // shift lines up
int targetEnd = H - 1 - lines; int targetEnd = H - 1 - lines;
if (targetEnd <= cursor.y) { if (targetEnd <= cursor.y) {
@ -503,7 +524,7 @@ void screen_delete_lines(unsigned int lines)
} }
// clear the rest // clear the rest
clear_range(targetEnd*W, W*H-1); clear_range(targetEnd*W, W*R1);
NOTIFY_DONE(); NOTIFY_DONE();
} }
@ -551,6 +572,8 @@ void ICACHE_FLASH_ATTR
screen_fill_with_E(void) screen_fill_with_E(void)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
screen_reset(); // based on observation from xterm
Cell sample; Cell sample;
sample.c[0] = 'E'; sample.c[0] = 'E';
sample.c[1] = 0; sample.c[1] = 0;
@ -608,8 +631,8 @@ void ICACHE_FLASH_ATTR
screen_scroll_up(unsigned int lines) screen_scroll_up(unsigned int lines)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
if (lines >= H - 1) { if (lines >= RH) {
screen_clear(CLEAR_ALL); clear_range(R0*W, (R1+1)*H-1);
goto done; goto done;
} }
@ -619,11 +642,11 @@ screen_scroll_up(unsigned int lines)
} }
int y; int y;
for (y = 0; y < H - lines; y++) { for (y = R0; y <= R1 - lines; y++) {
memcpy(screen + y * W, screen + (y + lines) * W, W * sizeof(Cell)); memcpy(screen + y * W, screen + (y + lines) * W, W * sizeof(Cell));
} }
clear_range(y * W, W * H - 1); clear_range(R0 * W+y * W, (R1+1)*W-1);
done: done:
NOTIFY_DONE(); NOTIFY_DONE();
@ -636,8 +659,8 @@ void ICACHE_FLASH_ATTR
screen_scroll_down(unsigned int lines) screen_scroll_down(unsigned int lines)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
if (lines >= H - 1) { if (lines >= RH) {
screen_clear(CLEAR_ALL); clear_range(R0*W, (R1+1)*H-1);
goto done; goto done;
} }
@ -647,11 +670,11 @@ screen_scroll_down(unsigned int lines)
} }
int y; int y;
for (y = H-1; y >= lines; y--) { for (y = R1; y >= R0+lines; y--) {
memcpy(screen + y * W, screen + (y - lines) * W, W * sizeof(Cell)); memcpy(screen + y * W, screen + (y - lines) * W, W * sizeof(Cell));
} }
clear_range(0, lines * W-1); clear_range(R0*W, R0*W+ lines * W-1);
done: done:
NOTIFY_DONE(); NOTIFY_DONE();
} }
@ -660,14 +683,18 @@ screen_scroll_down(unsigned int lines)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_scrolling_region(int from, int to) screen_set_scrolling_region(int from, int to)
{ {
// TODO implement (also add to scr)
if (from == 0 && to == 0) { if (from == 0 && to == 0) {
// whole screen scr.vm0 = 0;
} else if (from > 1 && from < H-1 && to > from) { scr.vm1 = H-1;
// set range } else if (from >= 0 && from < H-1 && to > from) {
scr.vm0 = from;
scr.vm1 = to;
} else { } else {
// Bad range, do nothing // Bad range, do nothing
} }
// Always move cursor home (may be translated due to DECOM)
screen_cursor_set(0, 0);
} }
//endregion //endregion
@ -681,8 +708,18 @@ void ICACHE_FLASH_ATTR
screen_cursor_set(int y, int x) screen_cursor_set(int y, int x)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
if (cursor.origin_mode) {
y += R0;
if (y > R1) y = R1;
if (y < R0) y = R0;
}
else {
if (y > H-1) y = H-1;
if (y < 0) y = 0;
}
if (x >= W) x = W - 1; if (x >= W) x = W - 1;
if (y >= H) y = H - 1; if (x < 0) x = 0;
cursor.x = x; cursor.x = x;
cursor.y = y; cursor.y = y;
clear_invalid_hanging(); clear_invalid_hanging();
@ -719,7 +756,14 @@ void ICACHE_FLASH_ATTR
screen_cursor_set_y(int y) screen_cursor_set_y(int y)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
if (y >= H) y = H - 1; if (cursor.origin_mode) {
y += R0;
if (y > R1) y = R1;
if (y < R0) y = R0;
} else {
if (y > H-1) y = H-1;
if (y < 0) y = 0;
}
cursor.y = y; cursor.y = y;
NOTIFY_DONE(); NOTIFY_DONE();
} }
@ -740,21 +784,41 @@ screen_cursor_move(int dy, int dx, bool scroll)
cursor.hanging = false; cursor.hanging = false;
} }
bool was_inside = cursor_inside_region();
cursor.x += dx; cursor.x += dx;
cursor.y += dy; cursor.y += dy;
if (cursor.x >= (int)W) cursor.x = W - 1; if (cursor.x >= (int)W) cursor.x = W - 1;
if (cursor.x < (int)0) cursor.x = 0; if (cursor.x < (int)0) cursor.x = 0;
if (cursor.y < 0) { if (cursor.y < R0) {
move = -cursor.y; if (was_inside) {
cursor.y = 0; move = -(cursor.y - R0);
if (scroll) screen_scroll_down((unsigned int)move); cursor.y = R0;
if (scroll) screen_scroll_down((unsigned int) move);
}
else {
// outside the region, just validate that we're not going offscreen
// scrolling is not possible in this case
if (cursor.y < 0) {
cursor.y = 0;
}
}
} }
if (cursor.y >= H) { if (cursor.y > R1) {
move = cursor.y - (H - 1); if (was_inside) {
cursor.y = H - 1; move = cursor.y - R1;
if (scroll) screen_scroll_up((unsigned int)move); cursor.y = R1;
if (scroll) screen_scroll_up((unsigned int) move);
}
else {
// outside the region, just validate that we're not going offscreen
// scrolling is not possible in this case
if (cursor.y >= H) {
cursor.y = H-1;
}
}
} }
NOTIFY_DONE(); NOTIFY_DONE();
@ -850,7 +914,7 @@ screen_set_sgr_inverse(bool ena)
} }
/** /**
* Check if coords are in range * Check if coords are in range - used for verifying mouse clicks
* *
* @param y * @param y
* @param x * @param x
@ -915,7 +979,8 @@ screen_set_newline_mode(bool nlm)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_origin_mode(bool region_origin) screen_set_origin_mode(bool region_origin)
{ {
// TODO implement (also add to scr) cursor.origin_mode = region_origin;
screen_cursor_set(0, 0);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -989,10 +1054,10 @@ screen_putchar(const char *ch)
cursor.x = 0; cursor.x = 0;
cursor.y++; cursor.y++;
// Y wrap // Y wrap
if (cursor.y > H - 1) { if (cursor.y > R1) {
// Scroll up, so we have space for writing // Scroll up, so we have space for writing
screen_scroll_up(1); screen_scroll_up(1);
cursor.y = H - 1; cursor.y = R1;
} }
cursor.hanging = false; cursor.hanging = false;
@ -1039,7 +1104,7 @@ screen_putchar(const char *ch)
*/ */
static const u16 codepage_0[] = static const u16 codepage_0[] =
{// Unicode ASCII SYM {// Unicode ASCII SYM
0x25c6, // 96 ` ◆ 0x2666, // 96 ` ♦
0x2592, // 97 a ▒ 0x2592, // 97 a ▒
0x2409, // 98 b HT 0x2409, // 98 b HT
0x240c, // 99 c FF 0x240c, // 99 c FF

Loading…
Cancel
Save