Merge branch 'new-message-format' into work

http-comm
Ondřej Hruška 7 years ago
commit 7bca46384e
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 4
      user/apars_csi.c
  2. 6
      user/cgi_main.c
  3. 105
      user/cgi_sockets.c
  4. 2
      user/cgi_sockets.h
  5. 42
      user/cgi_term_cfg.c
  6. 3
      user/routes.h
  7. 426
      user/screen.c
  8. 70
      user/screen.h

@ -757,11 +757,11 @@ do_csi_set_private_option(CSI_Data *opts)
} }
else if (n == 800) { // ESPTerm: Toggle display of buttons else if (n == 800) { // ESPTerm: Toggle display of buttons
termconf_live.show_buttons = yn; termconf_live.show_buttons = yn;
screen_notifyChange(CHANGE_CONTENT); // this info is included in the screen preamble screen_notifyChange(TOPIC_CHANGE_SCREEN_OPTS); // this info is included in the screen preamble
} }
else if (n == 801) { // ESPTerm: Toggle display of config links else if (n == 801) { // ESPTerm: Toggle display of config links
termconf_live.show_config_links = yn; termconf_live.show_config_links = yn;
screen_notifyChange(CHANGE_CONTENT); // this info is included in the screen preamble screen_notifyChange(TOPIC_CHANGE_SCREEN_OPTS); // this info is included in the screen preamble
} }
else { else {
ansi_noimpl("?OPTION %d", n); ansi_noimpl("?OPTION %d", n);

@ -25,11 +25,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplScreen(HttpdConnData *connData, char *token
char buff[150]; char buff[150];
if (streq(token, "labels_seq")) { if (streq(token, "theme")) {
screenSerializeLabelsToBuffer(buff, 150);
tplSend(connData, buff, -1);
}
else if (streq(token, "theme")) {
sprintf(buff, "%d", termconf->theme); sprintf(buff, "%d", termconf->theme);
tplSend(connData, buff, -1); tplSend(connData, buff, -1);
} }

@ -16,6 +16,8 @@
// Must be less than httpd sendbuf // Must be less than httpd sendbuf
#define SOCK_BUF_LEN 2000 #define SOCK_BUF_LEN 2000
volatile ScreenNotifyTopics pendingBroadcastTopics = 0;
// flags for screen update timeouts // flags for screen update timeouts
volatile bool notify_available = true; volatile bool notify_available = true;
volatile bool notify_cooldown = false; volatile bool notify_cooldown = false;
@ -24,12 +26,11 @@ volatile bool notify_cooldown = false;
* and we have to tell it we're ready again */ * and we have to tell it we're ready again */
volatile bool browser_wants_xon = false; volatile bool browser_wants_xon = false;
static ETSTimer notifyContentTim; static ETSTimer updateNotifyTim;
static ETSTimer notifyLabelsTim;
static ETSTimer notifyCooldownTim; static ETSTimer notifyCooldownTim;
static ETSTimer heartbeatTim; static ETSTimer heartbeatTim;
volatile int active_clients = 0; volatile int term_active_clients = 0;
// we're trying to do a kind of mutex here, without the actual primitives // we're trying to do a kind of mutex here, without the actual primitives
// this might glitch, very rarely. // this might glitch, very rarely.
@ -52,25 +53,21 @@ notifyCooldownTimCb(void *arg)
* @param arg * @param arg
*/ */
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
notifyContentTimCb(void *arg) updateNotify_do(Websock *ws, ScreenNotifyTopics topics)
{ {
Websock *ws = arg;
void *data = NULL; void *data = NULL;
int max_bl, total_bl;
char sock_buff[SOCK_BUF_LEN]; char sock_buff[SOCK_BUF_LEN];
cgiWebsockMeasureBacklog(URL_WS_UPDATE, &max_bl, &total_bl);
if (!notify_available || notify_cooldown || (max_bl > 2048)) { // do not send if we have anything significant backlogged
// postpone a little
TIMER_START(&notifyContentTim, notifyContentTimCb, 4, 0);
inp_dbg("postpone notify content");
return;
}
notify_available = false; notify_available = false;
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
httpd_cgi_state cont = screenSerializeToBuffer(sock_buff, SOCK_BUF_LEN, &data); if (! ws) {
// broadcast
topics = pendingBroadcastTopics;
pendingBroadcastTopics = 0;
}
httpd_cgi_state cont = screenSerializeToBuffer(sock_buff, SOCK_BUF_LEN, topics, &data);
int flg = 0; int flg = 0;
if (cont == HTTPD_CGI_MORE) flg |= WEBSOCK_FLAG_MORE; if (cont == HTTPD_CGI_MORE) flg |= WEBSOCK_FLAG_MORE;
if (i > 0) flg |= WEBSOCK_FLAG_CONT; if (i > 0) flg |= WEBSOCK_FLAG_CONT;
@ -86,56 +83,42 @@ notifyContentTimCb(void *arg)
} }
// cleanup // cleanup
screenSerializeToBuffer(NULL, SOCK_BUF_LEN, &data); screenSerializeToBuffer(NULL, 0, 0, &data);
notify_cooldown = true;
notify_available = true; notify_available = true;
TIMER_START(&notifyCooldownTim, notifyCooldownTimCb, termconf->display_cooldown_ms, 0);
} }
/** /**
* Tell browsers about the new text labels * Tell browser we have new content
* @param arg * @param arg
*/ */
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
notifyLabelsTimCb(void *arg) updateNotifyCb(void *arg)
{ {
Websock *ws = arg; int max_bl, total_bl;
char sock_buff[SOCK_BUF_LEN]; cgiWebsockMeasureBacklog(URL_WS_UPDATE, &max_bl, &total_bl);
if (!notify_available || notify_cooldown) { if (!notify_available || notify_cooldown || (max_bl > 2048)) { // do not send if we have anything significant backlogged
// postpone a little // postpone a little
TIMER_START(&notifyLabelsTim, notifyLabelsTimCb, 7, 0); TIMER_START(&updateNotifyTim, updateNotifyCb, 4, 0);
inp_dbg("postpone notify labels"); inp_dbg("postpone notify content");
return; return;
} }
notify_available = false;
screenSerializeLabelsToBuffer(sock_buff, SOCK_BUF_LEN);
if (ws) { updateNotify_do(arg, 0);
cgiWebsocketSend(ws, sock_buff, (int) strlen(sock_buff), 0);
} else {
cgiWebsockBroadcast(URL_WS_UPDATE, sock_buff, (int) strlen(sock_buff), 0);
resetHeartbeatTimer();
}
notify_cooldown = true; notify_cooldown = true;
notify_available = true;
TIMER_START(&notifyCooldownTim, notifyCooldownTimCb, termconf->display_cooldown_ms, 0); TIMER_START(&notifyCooldownTim, notifyCooldownTimCb, termconf->display_cooldown_ms, 0);
} }
/** Beep */ /** Beep */
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
send_beep(void) send_beep(void)
{ {
if (active_clients == 0) return; if (term_active_clients == 0) return;
screen_notifyChange(TOPIC_BELL); // XXX has latency, maybe better to send directly
// here's some potential for a race error with the other broadcast functions :C
cgiWebsockBroadcast(URL_WS_UPDATE, "B", 1, 0);
resetHeartbeatTimer();
} }
@ -143,10 +126,10 @@ send_beep(void)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
notify_growl(char *msg) notify_growl(char *msg)
{ {
if (active_clients == 0) return; if (term_active_clients == 0) return;
// TODO via timer... // here's some potential for a race error with the other broadcast functions
// here's some potential for a race error with the other broadcast functions :C // - we assume app won't send notifications in the middle of updating content
cgiWebsockBroadcast(URL_WS_UPDATE, msg, (int) strlen(msg), 0); cgiWebsockBroadcast(URL_WS_UPDATE, msg, (int) strlen(msg), 0);
resetHeartbeatTimer(); resetHeartbeatTimer();
} }
@ -157,24 +140,19 @@ notify_growl(char *msg)
* This is a callback for the Screen module, * This is a callback for the Screen module,
* called after each visible screen modification. * called after each visible screen modification.
*/ */
void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyChangeTopic topic) void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyTopics topics)
{ {
if (active_clients == 0) return; if (term_active_clients == 0) return;
// this is probably not needed here - ensure timeout is not 0 // this is probably not needed here - ensure timeout is not 0
if (termconf->display_tout_ms == 0) if (termconf->display_tout_ms == 0) {
termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS; termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS;
}
// NOTE: the timers are restarted if already running pendingBroadcastTopics |= topics;
if (topic == CHANGE_LABELS) { // NOTE: the timer is restarted if already running
// separate timer from content change timer, to avoid losing that update TIMER_START(&updateNotifyTim, updateNotifyCb, termconf->display_tout_ms, 0); // note - this adds latency to beep
TIMER_START(&notifyLabelsTim, notifyLabelsTimCb, termconf->display_tout_ms+2, 0); // this delay is useful when both are fired at once on screen reset
}
else if (topic == CHANGE_CONTENT) {
// throttle delay
TIMER_START(&notifyContentTim, notifyContentTimCb, termconf->display_tout_ms, 0);
}
} }
/** /**
@ -296,8 +274,7 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int
case 'i': case 'i':
// requests initial load // requests initial load
inp_dbg("Client requests initial load"); inp_dbg("Client requests initial load");
notifyContentTimCb(ws); // delay?? updateNotify_do(ws, TOPIC_INITIAL);
notifyLabelsTimCb(ws);
break; break;
case 'm': case 'm':
@ -322,7 +299,7 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int
/** Send a heartbeat msg */ /** Send a heartbeat msg */
static void ICACHE_FLASH_ATTR heartbeatTimCb(void *unused) static void ICACHE_FLASH_ATTR heartbeatTimCb(void *unused)
{ {
if (active_clients > 0) { if (term_active_clients > 0) {
if (notify_available) { if (notify_available) {
inp_dbg("."); inp_dbg(".");
@ -348,9 +325,9 @@ static void ICACHE_FLASH_ATTR resetHeartbeatTimer(void)
static void ICACHE_FLASH_ATTR closeSockCb(Websock *ws) static void ICACHE_FLASH_ATTR closeSockCb(Websock *ws)
{ {
active_clients--; term_active_clients--;
if (active_clients <= 0) { if (term_active_clients <= 0) {
active_clients = 0; term_active_clients = 0;
if (mouse_tracking.focus_tracking) { if (mouse_tracking.focus_tracking) {
UART_SendAsync("\x1b[O", 3); UART_SendAsync("\x1b[O", 3);
@ -368,7 +345,7 @@ void ICACHE_FLASH_ATTR updateSockConnect(Websock *ws)
ws->recvCb = updateSockRx; ws->recvCb = updateSockRx;
ws->closeCb = closeSockCb; ws->closeCb = closeSockCb;
if (active_clients == 0) { if (term_active_clients == 0) {
if (mouse_tracking.focus_tracking) { if (mouse_tracking.focus_tracking) {
UART_SendAsync("\x1b[I", 3); UART_SendAsync("\x1b[I", 3);
} }
@ -376,7 +353,7 @@ void ICACHE_FLASH_ATTR updateSockConnect(Websock *ws)
resetHeartbeatTimer(); resetHeartbeatTimer();
} }
active_clients++; term_active_clients++;
} }
ETSTimer xonTim; ETSTimer xonTim;

@ -15,6 +15,8 @@ void send_beep(void);
/** open pop-up notification */ /** open pop-up notification */
void notify_growl(char *msg); void notify_growl(char *msg);
extern volatile int term_active_clients;
// defined in the makefile // defined in the makefile
#if DEBUG_INPUT #if DEBUG_INPUT
#define inp_warn warn #define inp_warn warn

@ -40,7 +40,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
char buff[50]; char buff[50];
char redir_url_buf[100]; char redir_url_buf[100];
int32 n, w, h; int32 n, w, h;
bool notify_screen_content = 0, notify_screen_labels = 0; ScreenNotifyTopics topics = 0;
bool shall_clear_screen = false; bool shall_clear_screen = false;
bool shall_init_uart = false; bool shall_init_uart = false;
@ -94,7 +94,8 @@ cgiTermCfgSetParams(HttpdConnData *connData)
if (termconf->width != w || termconf->height != h) { if (termconf->width != w || termconf->height != h) {
termconf->width = w; termconf->width = w;
termconf->height = h; termconf->height = h;
shall_clear_screen = true; // this causes a notify shall_clear_screen = true;
topics |= TOPIC_CHANGE_SCREEN_OPTS | TOPIC_CHANGE_CONTENT_ALL;
} }
} while (0); } while (0);
} }
@ -112,6 +113,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
if (termconf->default_bg != n) { if (termconf->default_bg != n) {
termconf->default_bg = n; // this is current not sent through socket, no use to notify termconf->default_bg = n; // this is current not sent through socket, no use to notify
topics |= TOPIC_CHANGE_SCREEN_OPTS;
} }
} }
@ -128,6 +130,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
if (termconf->default_fg != n) { if (termconf->default_fg != n) {
termconf->default_fg = n; // this is current not sent through socket, no use to notify termconf->default_fg = n; // this is current not sent through socket, no use to notify
topics |= TOPIC_CHANGE_SCREEN_OPTS;
} }
} }
@ -168,40 +171,49 @@ cgiTermCfgSetParams(HttpdConnData *connData)
cgi_dbg("FN alt mode: %s", buff); cgi_dbg("FN alt mode: %s", buff);
n = atoi(buff); n = atoi(buff);
termconf->fn_alt_mode = (bool)n; termconf->fn_alt_mode = (bool)n;
notify_screen_content = true; topics |= TOPIC_CHANGE_SCREEN_OPTS;
} }
if (GET_ARG("want_all_fn")) { if (GET_ARG("want_all_fn")) {
cgi_dbg("AllFN mode: %s", buff); cgi_dbg("AllFN mode: %s", buff);
n = atoi(buff); n = atoi(buff);
termconf->want_all_fn = (bool)n; termconf->want_all_fn = (bool)n;
topics |= TOPIC_CHANGE_SCREEN_OPTS;
} }
if (GET_ARG("crlf_mode")) { if (GET_ARG("crlf_mode")) {
cgi_dbg("CRLF mode: %s", buff); cgi_dbg("CRLF mode: %s", buff);
n = atoi(buff); n = atoi(buff);
termconf->crlf_mode = (bool)n; termconf->crlf_mode = (bool)n;
notify_screen_content = true; topics |= TOPIC_CHANGE_SCREEN_OPTS;
} }
if (GET_ARG("show_buttons")) { if (GET_ARG("show_buttons")) {
cgi_dbg("Show buttons: %s", buff); cgi_dbg("Show buttons: %s", buff);
n = atoi(buff); n = atoi(buff);
termconf->show_buttons = (bool)n; termconf->show_buttons = (bool)n;
notify_screen_content = true; topics |= TOPIC_CHANGE_SCREEN_OPTS;
} }
if (GET_ARG("show_config_links")) { if (GET_ARG("show_config_links")) {
cgi_dbg("Show config links: %s", buff); cgi_dbg("Show config links: %s", buff);
n = atoi(buff); n = atoi(buff);
termconf->show_config_links = (bool)n; termconf->show_config_links = (bool)n;
notify_screen_content = true; topics |= TOPIC_CHANGE_SCREEN_OPTS;
} }
if (GET_ARG("loopback")) { if (GET_ARG("loopback")) {
cgi_dbg("Loopback: %s", buff); cgi_dbg("Loopback: %s", buff);
n = atoi(buff); n = atoi(buff);
termconf->loopback = (bool)n; termconf->loopback = (bool)n;
topics |= TOPIC_CHANGE_SCREEN_OPTS;
}
if (GET_ARG("debugbar")) {
cgi_dbg("Debugbar: %s", buff);
n = atoi(buff);
termconf->debugbar = (bool)n;
topics |= TOPIC_CHANGE_SCREEN_OPTS;
} }
if (GET_ARG("theme")) { if (GET_ARG("theme")) {
@ -213,6 +225,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
} else { } else {
cgi_warn("Bad theme num: %s", buff); cgi_warn("Bad theme num: %s", buff);
redir_url += sprintf(redir_url, "theme,"); redir_url += sprintf(redir_url, "theme,");
topics |= TOPIC_CHANGE_SCREEN_OPTS;
} }
} }
@ -221,7 +234,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
n = atoi(buff); n = atoi(buff);
if (n >= 0 && n <= 6 && n != 1) { if (n >= 0 && n <= 6 && n != 1) {
termconf->cursor_shape = (enum CursorShape) n; termconf->cursor_shape = (enum CursorShape) n;
notify_screen_content = true; topics |= TOPIC_CHANGE_SCREEN_OPTS;
} else { } else {
cgi_warn("Bad cursor_shape num: %s", buff); cgi_warn("Bad cursor_shape num: %s", buff);
redir_url += sprintf(redir_url, "cursor_shape,"); redir_url += sprintf(redir_url, "cursor_shape,");
@ -231,7 +244,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
if (GET_ARG("term_title")) { if (GET_ARG("term_title")) {
cgi_dbg("Terminal title default text: \"%s\"", buff); cgi_dbg("Terminal title default text: \"%s\"", buff);
strncpy_safe(termconf->title, buff, 64); // ATTN those must match the values in strncpy_safe(termconf->title, buff, 64); // ATTN those must match the values in
notify_screen_labels = true; topics |= TOPIC_CHANGE_TITLE;
} }
for (int btn_i = 1; btn_i <= TERM_BTN_COUNT; btn_i++) { for (int btn_i = 1; btn_i <= TERM_BTN_COUNT; btn_i++) {
@ -239,7 +252,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
if (GET_ARG(buff)) { if (GET_ARG(buff)) {
cgi_dbg("Button%d default text: \"%s\"", btn_i, buff); cgi_dbg("Button%d default text: \"%s\"", btn_i, buff);
strncpy_safe(termconf->btn[btn_i-1], buff, TERM_BTN_LEN); strncpy_safe(termconf->btn[btn_i-1], buff, TERM_BTN_LEN);
notify_screen_labels = true; topics |= TOPIC_CHANGE_BUTTONS;
} }
sprintf(buff, "bm%d", btn_i); sprintf(buff, "bm%d", btn_i);
@ -368,13 +381,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
serialInit(); serialInit();
} }
if (notify_screen_content) { if (topics) screen_notifyChange(topics);
screen_notifyChange(CHANGE_CONTENT);
}
if (notify_screen_labels) {
screen_notifyChange(CHANGE_LABELS);
}
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied."); httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
} else { } else {
@ -440,6 +447,9 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg)
else if (streq(token, "loopback")) { else if (streq(token, "loopback")) {
sprintf(buff, "%d", (int)termconf->loopback); sprintf(buff, "%d", (int)termconf->loopback);
} }
else if (streq(token, "debugbar")) {
sprintf(buff, "%d", (int)termconf->debugbar);
}
else if (streq(token, "theme")) { else if (streq(token, "theme")) {
sprintf(buff, "%d", termconf->theme); sprintf(buff, "%d", termconf->theme);
} }

@ -6,7 +6,4 @@
extern const HttpdBuiltInUrl routes[]; extern const HttpdBuiltInUrl routes[];
/** Broadcast screen state to sockets */
void screen_notifyChange();
#endif //ROUTES_H #endif //ROUTES_H

@ -5,10 +5,9 @@
#include "sgr.h" #include "sgr.h"
#include "ascii.h" #include "ascii.h"
#include "apars_logging.h" #include "apars_logging.h"
#include "jstring.h"
#include "character_sets.h" #include "character_sets.h"
#include "utf8.h" #include "utf8.h"
#include "uart_buffer.h" #include "cgi_sockets.h"
TerminalConfigBundle * const termconf = &persist.current.termconf; TerminalConfigBundle * const termconf = &persist.current.termconf;
TerminalConfigBundle termconf_live; TerminalConfigBundle termconf_live;
@ -74,7 +73,6 @@ typedef struct {
bool hanging; //!< xenl state - cursor half-wrapped bool hanging; //!< xenl state - cursor half-wrapped
/* SGR */ /* SGR */
bool inverse; //!< not in attrs bc it's applied immediately when writing the cell
bool conceal; //!< similar to inverse, causes all to be replaced by SP bool conceal; //!< similar to inverse, causes all to be replaced by SP
u16 attrs; u16 attrs;
Color fg; //!< Foreground color for writing Color fg; //!< Foreground color for writing
@ -139,14 +137,19 @@ static struct {
* (from nested calls) * (from nested calls)
*/ */
static volatile int notifyLock = 0; static volatile int notifyLock = 0;
static volatile ScreenNotifyTopics lockTopics = 0;
#define NOTIFY_LOCK() do { \ #define NOTIFY_LOCK() do { \
notifyLock++; \ notifyLock++; \
} while(0) } while(0)
#define NOTIFY_DONE() do { \ #define NOTIFY_DONE(updateTopics) do { \
lockTopics |= (updateTopics); \
if (notifyLock > 0) notifyLock--; \ if (notifyLock > 0) notifyLock--; \
if (notifyLock == 0) screen_notifyChange(CHANGE_CONTENT); \ if (notifyLock == 0) { \
screen_notifyChange(lockTopics); \
lockTopics = 0;\
} \
} while(0) } while(0)
/** Clear the hanging attribute if the cursor is no longer >= W */ /** Clear the hanging attribute if the cursor is no longer >= W */
@ -185,6 +188,7 @@ terminal_restore_defaults(void)
termconf->cursor_shape = SCR_DEF_CURSOR_SHAPE; termconf->cursor_shape = SCR_DEF_CURSOR_SHAPE;
termconf->crlf_mode = SCR_DEF_CRLF; termconf->crlf_mode = SCR_DEF_CRLF;
termconf->want_all_fn = SCR_DEF_ALLFN; termconf->want_all_fn = SCR_DEF_ALLFN;
termconf->debugbar = SCR_DEF_DEBUGBAR;
} }
/** /**
@ -203,12 +207,12 @@ terminal_apply_settings_noclear(void)
{ {
bool changed = false; bool changed = false;
// // Migrate to v1 // Migrate to v1
// if (termconf->config_version < 1) { if (termconf->config_version < 1) {
// persist_dbg("termconf: Updating to version %d", 1); persist_dbg("termconf: Updating to version %d", 1);
// termconf->display_cooldown_ms = SCR_DEF_DISPLAY_COOLDOWN_MS; termconf->debugbar = SCR_DEF_DEBUGBAR;
// changed = 1; changed = 1;
// } }
termconf->config_version = TERMCONF_VERSION; termconf->config_version = TERMCONF_VERSION;
@ -257,6 +261,8 @@ screen_init(void)
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
cursor_reset(void) cursor_reset(void)
{ {
NOTIFY_LOCK();
cursor.x = 0; cursor.x = 0;
cursor.y = 0; cursor.y = 0;
cursor.hanging = false; cursor.hanging = false;
@ -266,6 +272,8 @@ cursor_reset(void)
cursor.charset1 = CS_0_DEC_SUPPLEMENTAL; cursor.charset1 = CS_0_DEC_SUPPLEMENTAL;
screen_reset_sgr(); screen_reset_sgr();
NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
} }
/** /**
@ -285,7 +293,7 @@ screen_reset_on_resize(void)
// size is left unchanged // size is left unchanged
screen_clear(CLEAR_ALL); // also clears utf cache screen_clear(CLEAR_ALL); // also clears utf cache
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_INTERNAL);
} }
/** /**
@ -294,15 +302,20 @@ screen_reset_on_resize(void)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_reset_sgr(void) screen_reset_sgr(void)
{ {
NOTIFY_LOCK();
cursor.fg = 0; cursor.fg = 0;
cursor.bg = 0; cursor.bg = 0;
cursor.attrs = 0; cursor.attrs = 0;
cursor.conceal = false; cursor.conceal = false;
NOTIFY_DONE(TOPIC_INTERNAL);
} }
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
screen_reset_do(bool size, bool labels) screen_reset_do(bool size, bool labels)
{ {
ScreenNotifyTopics topics = TOPIC_CHANGE_SCREEN_OPTS | TOPIC_CHANGE_CURSOR | TOPIC_CHANGE_CONTENT_ALL;
NOTIFY_LOCK(); NOTIFY_LOCK();
// DECopts // DECopts
@ -350,7 +363,7 @@ screen_reset_do(bool size, bool labels)
termconf_live.show_buttons = termconf->show_buttons; termconf_live.show_buttons = termconf->show_buttons;
termconf_live.show_config_links = termconf->show_config_links; termconf_live.show_config_links = termconf->show_config_links;
screen_notifyChange(CHANGE_LABELS); topics |= TOPIC_CHANGE_TITLE | TOPIC_CHANGE_BUTTONS;
} }
// initial values in the save buffer in case of receiving restore without storing first // initial values in the save buffer in case of receiving restore without storing first
@ -368,7 +381,7 @@ screen_reset_do(bool size, bool labels)
opt_backup.show_buttons = termconf_live.show_buttons; opt_backup.show_buttons = termconf_live.show_buttons;
opt_backup.show_config_links = termconf_live.show_config_links; opt_backup.show_config_links = termconf_live.show_config_links;
NOTIFY_DONE(); NOTIFY_DONE(topics);
} }
/** /**
@ -395,8 +408,11 @@ screen_swap_state(bool alternate)
return; // nothing to do return; // nothing to do
} }
NOTIFY_LOCK();
if (alternate) { if (alternate) {
ansi_dbg("Swap to alternate"); ansi_dbg("Swap to alternate");
NOTIFY_LOCK();
// store old state // store old state
memcpy(state_backup.title, termconf_live.title, TERM_TITLE_LEN); memcpy(state_backup.title, termconf_live.title, TERM_TITLE_LEN);
memcpy(state_backup.btn, termconf_live.btn, sizeof(termconf_live.btn)); memcpy(state_backup.btn, termconf_live.btn, sizeof(termconf_live.btn));
@ -411,7 +427,6 @@ screen_swap_state(bool alternate)
} }
else { else {
ansi_dbg("Unswap from alternate"); ansi_dbg("Unswap from alternate");
NOTIFY_LOCK();
memcpy(termconf_live.title, state_backup.title, TERM_TITLE_LEN); memcpy(termconf_live.title, state_backup.title, TERM_TITLE_LEN);
memcpy(termconf_live.btn, state_backup.btn, sizeof(termconf_live.btn)); memcpy(termconf_live.btn, state_backup.btn, sizeof(termconf_live.btn));
memcpy(termconf_live.btn_msg, state_backup.btn_msg, sizeof(termconf_live.btn_msg)); memcpy(termconf_live.btn_msg, state_backup.btn_msg, sizeof(termconf_live.btn_msg));
@ -421,11 +436,10 @@ screen_swap_state(bool alternate)
// this may clear the screen as a side effect if size changed // this may clear the screen as a side effect if size changed
screen_resize(state_backup.height, state_backup.width); screen_resize(state_backup.height, state_backup.width);
// TODO restore screen content (if this is ever possible) // TODO restore screen content (if this is ever possible)
NOTIFY_DONE();
screen_notifyChange(CHANGE_LABELS);
} }
state_backup.alternate_active = alternate; state_backup.alternate_active = alternate;
NOTIFY_DONE(TOPIC_INITIAL);
} }
//endregion //endregion
@ -435,19 +449,25 @@ screen_swap_state(bool alternate)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_clear_all_tabs(void) screen_clear_all_tabs(void)
{ {
NOTIFY_LOCK();
memset(scr.tab_stops, 0, sizeof(scr.tab_stops)); memset(scr.tab_stops, 0, sizeof(scr.tab_stops));
NOTIFY_DONE(TOPIC_INTERNAL);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_tab(void) screen_set_tab(void)
{ {
NOTIFY_LOCK();
scr.tab_stops[cursor.x/32] |= (1<<(cursor.x%32)); scr.tab_stops[cursor.x/32] |= (1<<(cursor.x%32));
NOTIFY_DONE(TOPIC_INTERNAL);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_clear_tab(void) screen_clear_tab(void)
{ {
NOTIFY_LOCK();
scr.tab_stops[cursor.x/32] &= ~(1<<(cursor.x%32)); scr.tab_stops[cursor.x/32] &= ~(1<<(cursor.x%32));
NOTIFY_DONE(TOPIC_INTERNAL);
} }
/** /**
@ -529,7 +549,7 @@ screen_tab_forward(int count)
cursor.x = W - 1; cursor.x = W - 1;
} }
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -544,7 +564,7 @@ screen_tab_reverse(int count)
cursor.x = 0; cursor.x = 0;
} }
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
} }
//endregion //endregion
@ -733,7 +753,7 @@ screen_clear(ClearMode mode)
clear_range_utf(0, (cursor.y * W) + cursor.x); clear_range_utf(0, (cursor.y * W) + cursor.x);
break; break;
} }
NOTIFY_DONE(); NOTIFY_DONE(mode == CLEAR_ALL ? TOPIC_CHANGE_CONTENT_ALL : TOPIC_CHANGE_CONTENT_PART);
} }
/** /**
@ -756,7 +776,7 @@ screen_clear_line(ClearMode mode)
clear_range_utf(cursor.y * W, cursor.y * W + cursor.x); clear_range_utf(cursor.y * W, cursor.y * W + cursor.x);
break; break;
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -768,7 +788,7 @@ screen_clear_in_line(unsigned int count)
} else { } else {
clear_range_utf(cursor.y * W + cursor.x, cursor.y * W + cursor.x + count - 1); clear_range_utf(cursor.y * W + cursor.x, cursor.y * W + cursor.x + count - 1);
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -796,7 +816,7 @@ screen_insert_lines(unsigned int lines)
copy_row(i, cursor.y); copy_row(i, cursor.y);
} }
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -821,7 +841,7 @@ screen_delete_lines(unsigned int lines)
clear_range_noutf((movedBlockEnd+1)*W, (BTM+1)*W-1); clear_range_noutf((movedBlockEnd+1)*W, (BTM+1)*W-1);
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -844,7 +864,7 @@ screen_insert_characters(unsigned int count)
} }
clear_range_utf(cursor.y * W + cursor.x, cursor.y * W + targetStart - 1); clear_range_utf(cursor.y * W + cursor.x, cursor.y * W + targetStart - 1);
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -870,7 +890,7 @@ screen_delete_characters(unsigned int count)
screen_clear_line(CLEAR_FROM_CURSOR); screen_clear_line(CLEAR_FROM_CURSOR);
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
} }
//endregion //endregion
@ -891,7 +911,7 @@ screen_fill_with_E(void)
for (unsigned int i = 0; i <= W*H-1; i++) { for (unsigned int i = 0; i <= W*H-1; i++) {
memcpy(&screen[i], &sample, sizeof(Cell)); memcpy(&screen[i], &sample, sizeof(Cell));
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_ALL);
} }
/** /**
@ -920,14 +940,15 @@ screen_resize(int rows, int cols)
W = cols; W = cols;
H = rows; H = rows;
screen_reset_on_resize(); screen_reset_on_resize();
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS|TOPIC_CHANGE_CONTENT_ALL|TOPIC_CHANGE_CURSOR);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_title(const char *title) screen_set_title(const char *title)
{ {
NOTIFY_LOCK();
strncpy(termconf_live.title, title, TERM_TITLE_LEN); strncpy(termconf_live.title, title, TERM_TITLE_LEN);
screen_notifyChange(CHANGE_LABELS); NOTIFY_DONE(TOPIC_CHANGE_TITLE);
} }
/** /**
@ -938,8 +959,9 @@ screen_set_title(const char *title)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_button_text(int num, const char *text) screen_set_button_text(int num, const char *text)
{ {
NOTIFY_LOCK();
strncpy(termconf_live.btn[num-1], text, TERM_BTN_LEN); strncpy(termconf_live.btn[num-1], text, TERM_BTN_LEN);
screen_notifyChange(CHANGE_LABELS); NOTIFY_DONE(TOPIC_CHANGE_BUTTONS);
} }
/** /**
@ -970,7 +992,7 @@ screen_scroll_up(unsigned int lines)
clear_range_noutf(y * W, (BTM + 1) * W - 1); clear_range_noutf(y * W, (BTM + 1) * W - 1);
done: done:
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
} }
/** /**
@ -1000,13 +1022,14 @@ screen_scroll_down(unsigned int lines)
clear_range_noutf(TOP * W, TOP * W + lines * W - 1); clear_range_noutf(TOP * W, TOP * W + lines * W - 1);
done: done:
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
} }
/** Set scrolling region */ /** Set scrolling region */
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_scrolling_region(int from, int to) screen_set_scrolling_region(int from, int to)
{ {
NOTIFY_LOCK();
if (from <= 0 && to <= 0) { if (from <= 0 && to <= 0) {
scr.vm0 = 0; scr.vm0 = 0;
scr.vm1 = H-1; scr.vm1 = H-1;
@ -1020,6 +1043,7 @@ screen_set_scrolling_region(int from, int to)
// Always move cursor home (may be translated due to DECOM) // Always move cursor home (may be translated due to DECOM)
screen_cursor_set(0, 0); screen_cursor_set(0, 0);
NOTIFY_DONE(TOPIC_INTERNAL);
} }
//endregion //endregion
@ -1033,7 +1057,7 @@ screen_cursor_shape(enum CursorShape shape)
NOTIFY_LOCK(); NOTIFY_LOCK();
if (shape == CURSOR_DEFAULT) shape = termconf->cursor_shape; if (shape == CURSOR_DEFAULT) shape = termconf->cursor_shape;
termconf_live.cursor_shape = shape; termconf_live.cursor_shape = shape;
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS);
} }
/** set cursor blink option */ /** set cursor blink option */
@ -1050,7 +1074,7 @@ screen_cursor_blink(bool blink)
if (termconf_live.cursor_shape == CURSOR_BAR_BL) termconf_live.cursor_shape = CURSOR_BAR; if (termconf_live.cursor_shape == CURSOR_BAR_BL) termconf_live.cursor_shape = CURSOR_BAR;
if (termconf_live.cursor_shape == CURSOR_UNDERLINE_BL) termconf_live.cursor_shape = CURSOR_UNDERLINE; if (termconf_live.cursor_shape == CURSOR_UNDERLINE_BL) termconf_live.cursor_shape = CURSOR_UNDERLINE;
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS);
} }
/** /**
@ -1062,7 +1086,7 @@ screen_cursor_set(int y, int x)
NOTIFY_LOCK(); NOTIFY_LOCK();
screen_cursor_set_x(x); screen_cursor_set_x(x);
screen_cursor_set_y(y); screen_cursor_set_y(y);
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
} }
/** /**
@ -1093,7 +1117,7 @@ screen_cursor_set_x(int x)
// hanging happens when the cursor is virtually at col=81, which // hanging happens when the cursor is virtually at col=81, which
// cannot be set using the cursor-set commands. // cannot be set using the cursor-set commands.
cursor.hanging = false; cursor.hanging = false;
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
} }
/** /**
@ -1112,7 +1136,7 @@ screen_cursor_set_y(int y)
if (y < 0) y = 0; if (y < 0) y = 0;
} }
cursor.y = y; cursor.y = y;
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
} }
/** /**
@ -1195,7 +1219,7 @@ screen_cursor_move(int dy, int dx, bool scroll)
} }
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CURSOR | (scroll*TOPIC_CHANGE_CONTENT_PART));
} }
/** /**
@ -1232,21 +1256,23 @@ screen_cursor_restore(bool withAttrs)
} }
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_back_index(int count) screen_back_index(int count)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
ScreenNotifyTopics topics = TOPIC_CHANGE_CURSOR;
int new_x = cursor.x - count; int new_x = cursor.x - count;
if (new_x >= 0) { if (new_x >= 0) {
cursor.x = new_x; cursor.x = new_x;
} else { } else {
cursor.x = 0; cursor.x = 0;
screen_insert_characters(-new_x); screen_insert_characters(-new_x);
topics |= TOPIC_CHANGE_CONTENT_PART;
} }
NOTIFY_DONE(); NOTIFY_DONE(topics);
} }
//endregion //endregion
@ -1261,7 +1287,7 @@ screen_set_cursor_visible(bool visible)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
scr.cursor_visible = visible; scr.cursor_visible = visible;
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS);
} }
/** /**
@ -1270,7 +1296,9 @@ screen_set_cursor_visible(bool visible)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_wrap_enable(bool enable) screen_wrap_enable(bool enable)
{ {
NOTIFY_LOCK();
cursor.auto_wrap = enable; cursor.auto_wrap = enable;
NOTIFY_DONE(TOPIC_INTERNAL);
} }
/** /**
@ -1279,7 +1307,9 @@ screen_wrap_enable(bool enable)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_reverse_wrap_enable(bool enable) screen_reverse_wrap_enable(bool enable)
{ {
NOTIFY_LOCK();
cursor.reverse_wrap = enable; cursor.reverse_wrap = enable;
NOTIFY_DONE(TOPIC_INTERNAL);
} }
/** /**
@ -1288,8 +1318,10 @@ screen_reverse_wrap_enable(bool enable)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_fg(Color color) screen_set_fg(Color color)
{ {
NOTIFY_LOCK();
cursor.fg = color; cursor.fg = color;
cursor.attrs |= ATTR_FG; cursor.attrs |= ATTR_FG;
NOTIFY_DONE(TOPIC_INTERNAL);
} }
/** /**
@ -1298,8 +1330,10 @@ screen_set_fg(Color color)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_bg(Color color) screen_set_bg(Color color)
{ {
NOTIFY_LOCK();
cursor.bg = color; cursor.bg = color;
cursor.attrs |= ATTR_BG; cursor.attrs |= ATTR_BG;
NOTIFY_DONE(TOPIC_INTERNAL);
} }
/** /**
@ -1308,8 +1342,10 @@ screen_set_bg(Color color)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_default_fg(void) screen_set_default_fg(void)
{ {
NOTIFY_LOCK();
cursor.fg = 0; cursor.fg = 0;
cursor.attrs &= ~ATTR_FG; cursor.attrs &= ~ATTR_FG;
NOTIFY_DONE(TOPIC_INTERNAL);
} }
/** /**
@ -1318,45 +1354,57 @@ screen_set_default_fg(void)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_default_bg(void) screen_set_default_bg(void)
{ {
NOTIFY_LOCK();
cursor.bg = 0; cursor.bg = 0;
cursor.attrs &= ~ATTR_BG; cursor.attrs &= ~ATTR_BG;
NOTIFY_DONE(TOPIC_INTERNAL);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_sgr(CellAttrs attrs, bool ena) screen_set_sgr(CellAttrs attrs, bool ena)
{ {
NOTIFY_LOCK();
if (ena) { if (ena) {
cursor.attrs |= attrs; cursor.attrs |= attrs;
} }
else { else {
cursor.attrs &= ~attrs; cursor.attrs &= ~attrs;
} }
NOTIFY_DONE(TOPIC_INTERNAL);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_sgr_conceal(bool ena) screen_set_sgr_conceal(bool ena)
{ {
NOTIFY_LOCK();
cursor.conceal = ena; cursor.conceal = ena;
NOTIFY_DONE(TOPIC_INTERNAL);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_charset_n(int Gx) screen_set_charset_n(int Gx)
{ {
NOTIFY_LOCK();
if (Gx < 0 || Gx > 1) return; // bad n if (Gx < 0 || Gx > 1) return; // bad n
cursor.charsetN = Gx; cursor.charsetN = Gx;
NOTIFY_DONE(TOPIC_INTERNAL);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_charset(int Gx, char charset) screen_set_charset(int Gx, char charset)
{ {
NOTIFY_LOCK();
if (Gx == 0) cursor.charset0 = charset; if (Gx == 0) cursor.charset0 = charset;
else if (Gx == 1) cursor.charset1 = charset; else if (Gx == 1) cursor.charset1 = charset;
NOTIFY_DONE(TOPIC_INTERNAL);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_insert_mode(bool insert) screen_set_insert_mode(bool insert)
{ {
NOTIFY_LOCK();
scr.insert_mode = insert; scr.insert_mode = insert;
NOTIFY_DONE(TOPIC_INTERNAL);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -1364,7 +1412,7 @@ screen_set_numpad_alt_mode(bool alt_mode)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
scr.numpad_alt_mode = alt_mode; scr.numpad_alt_mode = alt_mode;
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -1372,7 +1420,7 @@ screen_set_cursors_alt_mode(bool alt_mode)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
scr.cursors_alt_mode = alt_mode; scr.cursors_alt_mode = alt_mode;
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -1380,7 +1428,7 @@ screen_set_reverse_video(bool reverse)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
scr.reverse_video = reverse; scr.reverse_video = reverse;
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -1388,7 +1436,7 @@ screen_set_bracketed_paste(bool ena)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
scr.bracketed_paste = ena; scr.bracketed_paste = ena;
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -1396,29 +1444,34 @@ screen_set_newline_mode(bool nlm)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
termconf_live.crlf_mode = nlm; termconf_live.crlf_mode = nlm;
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_origin_mode(bool region_origin) screen_set_origin_mode(bool region_origin)
{ {
NOTIFY_LOCK();
cursor.origin_mode = region_origin; cursor.origin_mode = region_origin;
screen_cursor_set(0, 0); screen_cursor_set(0, 0);
NOTIFY_DONE(TOPIC_INTERNAL);
} }
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
do_save_private_opt(int n, bool save) do_save_private_opt(int n, bool save)
{ {
ScreenNotifyTopics topics = TOPIC_INTERNAL;
if (!save) NOTIFY_LOCK();
#define SAVE_RESTORE(sf, of) do { if (save) sf=(of); else of=(sf); } while(0) #define SAVE_RESTORE(sf, of) do { if (save) sf=(of); else of=(sf); } while(0)
switch (n) { switch (n) {
case 1: case 1:
SAVE_RESTORE(opt_backup.cursors_alt_mode, scr.cursors_alt_mode); SAVE_RESTORE(opt_backup.cursors_alt_mode, scr.cursors_alt_mode);
topics |= TOPIC_CHANGE_SCREEN_OPTS;
break; break;
case 5: case 5:
SAVE_RESTORE(opt_backup.reverse_video, scr.reverse_video); SAVE_RESTORE(opt_backup.reverse_video, scr.reverse_video);
break; break;
case 6: case 6:
SAVE_RESTORE(opt_backup.origin_mode, cursor.origin_mode); SAVE_RESTORE(opt_backup.origin_mode, cursor.origin_mode); // XXX maybe we should move cursor to 1,1 if it's restored to True
break; break;
case 7: case 7:
SAVE_RESTORE(opt_backup.auto_wrap, cursor.auto_wrap); SAVE_RESTORE(opt_backup.auto_wrap, cursor.auto_wrap);
@ -1429,14 +1482,17 @@ do_save_private_opt(int n, bool save)
case 1002: case 1002:
case 1003: case 1003:
SAVE_RESTORE(opt_backup.mouse_tracking, mouse_tracking.mode); SAVE_RESTORE(opt_backup.mouse_tracking, mouse_tracking.mode);
topics |= TOPIC_CHANGE_SCREEN_OPTS;
break; break;
case 1004: case 1004:
SAVE_RESTORE(opt_backup.focus_tracking, mouse_tracking.focus_tracking); SAVE_RESTORE(opt_backup.focus_tracking, mouse_tracking.focus_tracking);
topics |= TOPIC_CHANGE_SCREEN_OPTS;
break; break;
case 1005: case 1005:
case 1006: case 1006:
case 1015: case 1015:
SAVE_RESTORE(opt_backup.mouse_encoding, mouse_tracking.encoding); SAVE_RESTORE(opt_backup.mouse_encoding, mouse_tracking.encoding);
topics |= TOPIC_CHANGE_SCREEN_OPTS;
break; break;
case 12: // cursor blink case 12: // cursor blink
if (save) { if (save) {
@ -1444,9 +1500,11 @@ do_save_private_opt(int n, bool save)
} else { } else {
screen_cursor_blink(opt_backup.cursor_blink); screen_cursor_blink(opt_backup.cursor_blink);
} }
topics |= TOPIC_CHANGE_SCREEN_OPTS;
break; break;
case 25: case 25:
SAVE_RESTORE(opt_backup.cursor_visible, scr.cursor_visible); SAVE_RESTORE(opt_backup.cursor_visible, scr.cursor_visible);
topics |= TOPIC_CHANGE_SCREEN_OPTS;
break; break;
case 45: case 45:
SAVE_RESTORE(opt_backup.reverse_wrap, cursor.reverse_wrap); SAVE_RESTORE(opt_backup.reverse_wrap, cursor.reverse_wrap);
@ -1456,19 +1514,24 @@ do_save_private_opt(int n, bool save)
break; break;
case 800: case 800:
SAVE_RESTORE(opt_backup.show_buttons, termconf_live.show_buttons); SAVE_RESTORE(opt_backup.show_buttons, termconf_live.show_buttons);
topics |= TOPIC_CHANGE_SCREEN_OPTS;
break; break;
case 801: case 801:
SAVE_RESTORE(opt_backup.show_config_links, termconf_live.show_config_links); SAVE_RESTORE(opt_backup.show_config_links, termconf_live.show_config_links);
topics |= TOPIC_CHANGE_SCREEN_OPTS;
break; break;
default: default:
ansi_warn("Cannot store ?%d", n); ansi_warn("Cannot store ?%d", n);
} }
if (!save) NOTIFY_DONE(topics);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_save_private_opt(int n) screen_save_private_opt(int n)
{ {
NOTIFY_LOCK();
do_save_private_opt(n, true); do_save_private_opt(n, true);
NOTIFY_DONE(TOPIC_INTERNAL);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -1476,7 +1539,7 @@ screen_restore_private_opt(int n)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
do_save_private_opt(n, false); do_save_private_opt(n, false);
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_INTERNAL);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -1599,7 +1662,7 @@ screen_putchar(const char *ch)
strncpy(scr.last_char, result, 4); strncpy(scr.last_char, result, 4);
done: done:
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_WRITE);
} }
/** /**
@ -1621,7 +1684,7 @@ screen_repeat_last_character(int count)
putchar_graphic(scr.last_char); putchar_graphic(scr.last_char);
count--; count--;
} }
NOTIFY_DONE(); NOTIFY_DONE(TOPIC_WRITE);
} }
/** /**
@ -1674,29 +1737,12 @@ struct ScreenSerializeState {
UnicodeCacheRef lastSymbol; UnicodeCacheRef lastSymbol;
char lastChar[4]; char lastChar[4];
u8 lastCharLen; u8 lastCharLen;
int index; int index; // index in the screen buffer
ScreenNotifyTopics topics;
ScreenNotifyTopics last_topic;
ScreenNotifyTopics current_topic;
}; };
/**
* buffer should be at least 64+5*10+6 long (title + buttons + 6), ie. 120
* @param buffer
* @param buf_len
*/
void ICACHE_FLASH_ATTR
screenSerializeLabelsToBuffer(char *buffer, size_t buf_len)
{
(void)buf_len;
// let's just assume it's long enough - called with the huge websocket buffer
sprintf(buffer, "T%s\x01%s\x01%s\x01%s\x01%s\x01%s", // use 0x01 as separator
termconf_live.title,
termconf_live.btn[0],
termconf_live.btn[1],
termconf_live.btn[2],
termconf_live.btn[3],
termconf_live.btn[4]
);
}
/** /**
* Serialize the screen to a data buffer. May need multiple calls if the buffer is insufficient in size. * Serialize the screen to a data buffer. May need multiple calls if the buffer is insufficient in size.
* *
@ -1705,12 +1751,14 @@ screenSerializeLabelsToBuffer(char *buffer, size_t buf_len)
* *
* @param buffer - buffer array of limited size. If NULL, indicates this is the last call. * @param buffer - buffer array of limited size. If NULL, indicates this is the last call.
* @param buf_len - buffer array size * @param buf_len - buffer array size
* @param topics - what should be included in the message (ignored after the first call)
* @param data - opaque pointer to internal data structure for storing state between repeated calls * @param data - opaque pointer to internal data structure for storing state between repeated calls
* if NULL, indicates this is the first call. * if NULL, indicates this is the first call; the structure will be allocated.
* @return HTTPD_CGI_DONE or HTTPD_CGI_MORE. If more, repeat with the same DATA. *
* @return HTTPD_CGI_DONE or HTTPD_CGI_MORE. If more, repeat with the same `data` pointer.
*/ */
httpd_cgi_state ICACHE_FLASH_ATTR httpd_cgi_state ICACHE_FLASH_ATTR
screenSerializeToBuffer(char *buffer, size_t buf_len, void **data) screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, void **data)
{ {
struct ScreenSerializeState *ss = *data; struct ScreenSerializeState *ss = *data;
@ -1719,11 +1767,11 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
Cell *cell, *cell0; Cell *cell, *cell0; // temporary cell pointers for finding repetitions
u8 nbytes; u8 nbytes; // temporary variable for utf writing utilities
size_t remain = buf_len; size_t remain = buf_len; // remaining space in the output buffer
char *bb = buffer; char *bb = buffer; // write pointer
#define bufput_c(c) do { \ #define bufput_c(c) do { \
*bb = (char)(c); \ *bb = (char)(c); \
@ -1742,47 +1790,183 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
bufput_utf8((num)); \ bufput_utf8((num)); \
} while(0) } while(0)
// tags for screen serialization
#define SEQ_TAG_REPEAT '\x02'
#define SEQ_TAG_COLORS '\x03'
#define SEQ_TAG_ATTRS '\x04'
#define SEQ_TAG_FG '\x05'
#define SEQ_TAG_BG '\x06'
#define TOPICMARK_SCREEN_OPTS 'O'
#define TOPICMARK_TITLE 'T'
#define TOPICMARK_BUTTONS 'B'
#define TOPICMARK_DEBUG 'D'
#define TOPICMARK_BELL '!'
#define TOPICMARK_CURSOR 'C'
#define TOPICMARK_SCREEN 'S'
if (ss == NULL) { if (ss == NULL) {
// START!
*data = ss = malloc(sizeof(struct ScreenSerializeState)); *data = ss = malloc(sizeof(struct ScreenSerializeState));
if (topics == 0 || termconf_live.debugbar) {
topics |= TOPIC_INTERNAL;
}
ss->index = 0; ss->index = 0;
ss->lastBg = 0xFF;
ss->lastFg = 0xFF; ss->topics = topics;
ss->lastAttrs = 0xFFFF; ss->last_topic = 0; // to be filled
ss->lastCharLen = 0; ss->current_topic = 0; // to be filled
ss->lastSymbol = 0;
strncpy(ss->lastChar, " ", 4); strncpy(ss->lastChar, " ", 4);
bufput_c('S'); bufput_c('U'); // - stands for "update"
// H W X Y Attribs
bufput_utf8(H); bufput_utf8(topics);
bufput_utf8(W);
bufput_utf8(cursor.y);
bufput_utf8(cursor.x);
// 3B has 18 free bits
bufput_utf8(
(scr.cursor_visible << 0) |
(cursor.hanging << 1) |
(scr.cursors_alt_mode << 2) |
(scr.numpad_alt_mode << 3) |
(termconf_live.fn_alt_mode << 4) |
((mouse_tracking.mode>MTM_NONE) << 5) | // disables context menu
((mouse_tracking.mode>=MTM_NORMAL) << 6) | // disables selecting
(termconf_live.show_buttons << 7) |
(termconf_live.show_config_links << 8) |
((termconf_live.cursor_shape&0x07) << 9) | // 9,10,11 - cursor shape based on DECSCUSR
(termconf_live.crlf_mode << 12) |
(scr.bracketed_paste << 13) |
(scr.reverse_video << 14)
);
} }
#define SEQ_TAG_REPEAT '\x02' int begun_topic = 0;
#define SEQ_TAG_COLORS '\x03' int prev_topic = 0;
#define SEQ_TAG_ATTRS '\x04'
#define SEQ_TAG_FG '\x05' #define BEGIN_TOPIC(topic, size) \
#define SEQ_TAG_BG '\x06' if (ss->last_topic == prev_topic) { \
begun_topic = (topic); \
if (ss->topics & (topic)) { \
if (remain < (size)) return HTTPD_CGI_MORE;
#define END_TOPIC \
} \
ss->last_topic = begun_topic; \
ss->current_topic = 0; \
} \
prev_topic = begun_topic;
if (ss->current_topic == 0) {
BEGIN_TOPIC(TOPIC_CHANGE_SCREEN_OPTS, 32+1)
bufput_c(TOPICMARK_SCREEN_OPTS);
bufput_utf8(H);
bufput_utf8(W);
bufput_utf8(termconf_live.theme);
bufput_utf8(termconf_live.default_fg & 0x000FFF);
bufput_utf8((termconf_live.default_fg & 0xFFF000) >> 12);
bufput_utf8(termconf_live.default_bg & 0x000FFF);
bufput_utf8((termconf_live.default_bg & 0xFFF000) >> 12);
bufput_utf8(
(scr.cursor_visible << 0) |
(termconf_live.debugbar << 1) | // debugbar - this was previously "hanging"
(scr.cursors_alt_mode << 2) |
(scr.numpad_alt_mode << 3) |
(termconf_live.fn_alt_mode << 4) |
((mouse_tracking.mode > MTM_NONE) << 5) | // disables context menu
((mouse_tracking.mode >= MTM_NORMAL) << 6) | // disables selecting
(termconf_live.show_buttons << 7) |
(termconf_live.show_config_links << 8) |
((termconf_live.cursor_shape & 0x07) << 9) | // 9,10,11 - cursor shape based on DECSCUSR
(termconf_live.crlf_mode << 12) |
(scr.bracketed_paste << 13) |
(scr.reverse_video << 14)
);
END_TOPIC
BEGIN_TOPIC(TOPIC_CHANGE_TITLE, TERM_TITLE_LEN+4+1)
bufput_c(TOPICMARK_TITLE);
int len = (int) strlen(termconf_live.title);
memcpy(bb, termconf_live.title, len);
bb += len;
remain -= len;
bufput_c('\x01');
END_TOPIC
BEGIN_TOPIC(TOPIC_CHANGE_BUTTONS, (TERM_BTN_LEN+4)*TERM_BTN_COUNT+1+4)
bufput_c(TOPICMARK_BUTTONS);
bufput_utf8(TERM_BTN_COUNT);
for (int i = 0; i < TERM_BTN_COUNT; i++) {
int len = (int) strlen(termconf_live.btn[i]);
memcpy(bb, termconf_live.btn[i], len);
bb += len;
remain -= len;
bufput_c('\x01');
}
END_TOPIC
BEGIN_TOPIC(TOPIC_INTERNAL, 45)
bufput_c(TOPICMARK_DEBUG);
// General flags
bufput_utf8(
(scr.insert_mode << 0) |
(cursor.conceal << 1) |
(cursor.auto_wrap << 2) |
(cursor.reverse_wrap << 3) |
(cursor.origin_mode << 4) |
(cursor_saved << 5) |
(state_backup.alternate_active << 6)
);
bufput_utf8(cursor.attrs);
bufput_utf8(scr.vm0);
bufput_utf8(scr.vm1);
bufput_utf8(cursor.charsetN);
bufput_c(cursor.charset0);
bufput_c(cursor.charset1);
bufput_utf8(system_get_free_heap_size());
bufput_utf8(term_active_clients);
END_TOPIC
BEGIN_TOPIC(TOPIC_BELL, 1)
bufput_c(TOPICMARK_BELL);
END_TOPIC
BEGIN_TOPIC(TOPIC_CHANGE_CURSOR, 13)
bufput_c(TOPICMARK_CURSOR);
bufput_utf8(cursor.y);
bufput_utf8(cursor.x);
bufput_utf8(
(cursor.hanging << 0)
);
END_TOPIC
if (ss->last_topic == TOPIC_CHANGE_CURSOR) {
// now we can begin any of the two screen sequences
if (ss->topics & TOPIC_CHANGE_CONTENT_ALL) {
ss->current_topic = TOPIC_CHANGE_CONTENT_ALL;
}
if (ss->topics & TOPIC_CHANGE_CONTENT_PART) {
ss->current_topic = TOPIC_CHANGE_CONTENT_PART;
}
if (ss->current_topic == 0) {
// no screen mode - wrap it up
goto ser_done;
}
}
}
// screen contents
int i = ss->index; int i = ss->index;
if (i == 0) {
bufput_c(TOPICMARK_SCREEN); // desired update mode is in `ss->current_topic`
// TODO implement partial
bufput_utf8(0); // Y0
bufput_utf8(0); // X0
bufput_utf8(H); // height
bufput_utf8(W); // width
ss->index = 0;
ss->lastBg = 0xFF;
ss->lastFg = 0xFF;
ss->lastAttrs = 0xFFFF;
ss->lastCharLen = 0;
ss->lastSymbol = 0;
}
while(i < W*H && remain > 12) { while(i < W*H && remain > 12) {
cell = cell0 = &screen[i]; cell = cell0 = &screen[i];
@ -1848,20 +2032,22 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
} }
ss->index = i; ss->index = i;
if (i >= W*H-1) goto ser_done;
// MORE TO WRITE...
bufput_c('\0'); // terminate the string bufput_c('\0'); // terminate the string
return HTTPD_CGI_MORE;
ser_done:
bufput_c('\0'); // terminate the string
return HTTPD_CGI_DONE;
}
//endregion
#if 0 #if 0
printf("MSG: "); printf("MSG: ");
for (int j=0;j<bb-buffer;j++) { for (int j=0;j<bb-buffer;j++) {
printf("%02X ", buffer[j]); printf("%02X ", buffer[j]);
} }
printf("\n"); printf("\n");
#endif #endif
if (i < W*H-1) {
return HTTPD_CGI_MORE;
}
return HTTPD_CGI_DONE;
}
//endregion

@ -34,11 +34,6 @@
* *
*/ */
// Size designed for the terminal config structure
// Must be constant to avoid corrupting user config after upgrade
#define TERMCONF_SIZE 300
#define TERMCONF_VERSION 0
#define TERM_BTN_LEN 10 #define TERM_BTN_LEN 10
#define TERM_BTN_MSG_LEN 10 #define TERM_BTN_MSG_LEN 10
#define TERM_TITLE_LEN 64 #define TERM_TITLE_LEN 64
@ -70,10 +65,16 @@ enum CursorShape {
#define SCR_DEF_CURSOR_SHAPE CURSOR_BLOCK_BL #define SCR_DEF_CURSOR_SHAPE CURSOR_BLOCK_BL
#define SCR_DEF_CRLF 0 // enter sends CRLF #define SCR_DEF_CRLF 0 // enter sends CRLF
#define SCR_DEF_ALLFN 0 // capture F5 etc #define SCR_DEF_ALLFN 0 // capture F5 etc
#define SCR_DEF_DEBUGBAR 0
// --- Persistent Settings --- // --- Persistent Settings ---
#define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL) #define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL)
// Size designed for the terminal config structure
// Must be constant to avoid corrupting user config after upgrade
#define TERMCONF_SIZE 300
#define TERMCONF_VERSION 1
typedef struct { typedef struct {
u32 width; u32 width;
u32 height; u32 height;
@ -94,6 +95,7 @@ typedef struct {
enum CursorShape cursor_shape; enum CursorShape cursor_shape;
bool crlf_mode; bool crlf_mode;
bool want_all_fn; bool want_all_fn;
bool debugbar;
} TerminalConfigBundle; } TerminalConfigBundle;
// Live config // Live config
@ -152,9 +154,29 @@ typedef enum {
CS_1_DOS_437 = '1', CS_1_DOS_437 = '1',
} CHARSET; } CHARSET;
httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data); enum ScreenSerializeTopic {
TOPIC_CHANGE_SCREEN_OPTS = (1<<0),
TOPIC_CHANGE_CONTENT_ALL = (1<<1),
TOPIC_CHANGE_CONTENT_PART = (1<<2),
TOPIC_CHANGE_TITLE = (1<<3),
TOPIC_CHANGE_BUTTONS = (1<<4),
TOPIC_CHANGE_CURSOR = (1<<5),
TOPIC_INTERNAL = (1<<6), // debugging internal state
TOPIC_BELL = (1<<7), // beep
// combos
TOPIC_INITIAL =
TOPIC_CHANGE_SCREEN_OPTS |
TOPIC_CHANGE_CONTENT_ALL |
TOPIC_CHANGE_CURSOR |
TOPIC_CHANGE_TITLE |
TOPIC_CHANGE_BUTTONS,
TOPIC_WRITE = TOPIC_CHANGE_CURSOR | TOPIC_CHANGE_CONTENT_PART,
};
typedef u16 ScreenNotifyTopics;
void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len); httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, void **data);
// --- Clearing --- // --- Clearing ---
@ -231,18 +253,19 @@ void screen_set_origin_mode(bool region_origin);
typedef uint8_t Color; typedef uint8_t Color;
typedef uint16_t CellAttrs; typedef uint16_t CellAttrs;
// TODO sort by the expected frequency of being set - so when we switch to utf-8 encoding for data fields, it uses fewer bytes enum SgrAttrBits {
#define ATTR_BOLD (1<<0) //!< Bold font ATTR_FG = (1<<0), //!< 1 if not using default background color (ignore cell bg) - color extension bit
#define ATTR_FAINT (1<<1) //!< Faint foreground color (reduced alpha) ATTR_BG = (1<<1), //!< 1 if not using default foreground color (ignore cell fg) - color extension bit
#define ATTR_ITALIC (1<<2) //!< Italic font ATTR_BOLD = (1<<2), //!< Bold font
#define ATTR_UNDERLINE (1<<3) //!< Underline decoration ATTR_UNDERLINE = (1<<3), //!< Underline decoration
#define ATTR_BLINK (1<<4) //!< Blinking ATTR_INVERSE = (1<<4), //!< Invert colors - this is useful so we can clear then with SGR manipulation commands
#define ATTR_FRAKTUR (1<<5) //!< Fraktur font (unicode substitution) ATTR_BLINK = (1<<5), //!< Blinking
#define ATTR_STRIKE (1<<6) //!< Strike-through decoration ATTR_ITALIC = (1<<6), //!< Italic font
#define ATTR_OVERLINE (1<<7) //!< Over-line decoration ATTR_STRIKE = (1<<7), //!< Strike-through decoration
#define ATTR_FG (1<<8) //!< 1 if not using default background color (ignore cell bg) - color extension bit ATTR_OVERLINE = (1<<8), //!< Over-line decoration
#define ATTR_BG (1<<9) //!< 1 if not using default foreground color (ignore cell fg) - color extension bit ATTR_FAINT = (1<<9), //!< Faint foreground color (reduced alpha)
#define ATTR_INVERSE (1<<10) //!< Invert colors - this is useful so we can clear then with SGR manipulation commands ATTR_FRAKTUR = (1<<10), //!< Fraktur font (unicode substitution)
};
/** Set cursor foreground color */ /** Set cursor foreground color */
void screen_set_fg(Color color); void screen_set_fg(Color color);
@ -323,18 +346,11 @@ void screen_repeat_last_character(int count);
/** Report current SGR as num;num;... for DAC query */ /** Report current SGR as num;num;... for DAC query */
void screen_report_sgr(char *buffer); void screen_report_sgr(char *buffer);
// --- Notify ---
typedef enum {
CHANGE_CONTENT = 0,
CHANGE_LABELS = 1,
} ScreenNotifyChangeTopic;
/** /**
* Called when the screen content or settings change * Called when the screen content or settings change
* and the front-end should redraw / update. * and the front-end should redraw / update.
* @param topic - what kind of change this is (chooses what message to send) * @param topic - what kind of change this is (chooses what message to send)
*/ */
extern void screen_notifyChange(ScreenNotifyChangeTopic topic); extern void screen_notifyChange(ScreenNotifyTopics topics);
#endif // SCREEN_H #endif // SCREEN_H

Loading…
Cancel
Save