From 404ccaeb20f7786283966e1817eb29ae6bfa5552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 28 Sep 2017 20:58:45 +0200 Subject: [PATCH 01/35] new message format --- user/apars_csi.c | 4 +- user/cgi_main.c | 6 +- user/cgi_sockets.c | 105 +++++------ user/cgi_sockets.h | 2 + user/cgi_term_cfg.c | 42 +++-- user/routes.h | 3 - user/screen.c | 425 +++++++++++++++++++++++++++++++------------- user/screen.h | 45 +++-- 8 files changed, 405 insertions(+), 227 deletions(-) diff --git a/user/apars_csi.c b/user/apars_csi.c index ed18ac9..072d673 100644 --- a/user/apars_csi.c +++ b/user/apars_csi.c @@ -757,11 +757,11 @@ do_csi_set_private_option(CSI_Data *opts) } else if (n == 800) { // ESPTerm: Toggle display of buttons 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 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 { ansi_noimpl("?OPTION %d", n); diff --git a/user/cgi_main.c b/user/cgi_main.c index da13a41..fe33ab6 100644 --- a/user/cgi_main.c +++ b/user/cgi_main.c @@ -25,11 +25,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplScreen(HttpdConnData *connData, char *token char buff[150]; - if (streq(token, "labels_seq")) { - screenSerializeLabelsToBuffer(buff, 150); - tplSend(connData, buff, -1); - } - else if (streq(token, "theme")) { + if (streq(token, "theme")) { sprintf(buff, "%d", termconf->theme); tplSend(connData, buff, -1); } diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index 7f4fcfc..baddd07 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -16,6 +16,8 @@ // Must be less than httpd sendbuf #define SOCK_BUF_LEN 2000 +volatile ScreenNotifyTopics pendingBroadcastTopics = 0; + // flags for screen update timeouts volatile bool notify_available = true; volatile bool notify_cooldown = false; @@ -24,12 +26,11 @@ volatile bool notify_cooldown = false; * and we have to tell it we're ready again */ volatile bool browser_wants_xon = false; -static ETSTimer notifyContentTim; -static ETSTimer notifyLabelsTim; +static ETSTimer updateNotifyTim; static ETSTimer notifyCooldownTim; 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 // this might glitch, very rarely. @@ -52,25 +53,21 @@ notifyCooldownTimCb(void *arg) * @param arg */ static void ICACHE_FLASH_ATTR -notifyContentTimCb(void *arg) +updateNotify_do(Websock *ws, ScreenNotifyTopics topics) { - Websock *ws = arg; void *data = NULL; - int max_bl, total_bl; 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(¬ifyContentTim, notifyContentTimCb, 4, 0); - inp_dbg("postpone notify content"); - return; - } notify_available = false; 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; if (cont == HTTPD_CGI_MORE) flg |= WEBSOCK_FLAG_MORE; if (i > 0) flg |= WEBSOCK_FLAG_CONT; @@ -86,56 +83,42 @@ notifyContentTimCb(void *arg) } // cleanup - screenSerializeToBuffer(NULL, SOCK_BUF_LEN, &data); + screenSerializeToBuffer(NULL, 0, 0, &data); - notify_cooldown = true; notify_available = true; - - TIMER_START(¬ifyCooldownTim, notifyCooldownTimCb, termconf->display_cooldown_ms, 0); } /** - * Tell browsers about the new text labels + * Tell browser we have new content * @param arg */ static void ICACHE_FLASH_ATTR -notifyLabelsTimCb(void *arg) +updateNotifyCb(void *arg) { - Websock *ws = arg; - char sock_buff[SOCK_BUF_LEN]; + int max_bl, total_bl; + 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 - TIMER_START(¬ifyLabelsTim, notifyLabelsTimCb, 7, 0); - inp_dbg("postpone notify labels"); + TIMER_START(&updateNotifyTim, updateNotifyCb, 4, 0); + inp_dbg("postpone notify content"); return; } - notify_available = false; - - screenSerializeLabelsToBuffer(sock_buff, SOCK_BUF_LEN); - if (ws) { - cgiWebsocketSend(ws, sock_buff, (int) strlen(sock_buff), 0); - } else { - cgiWebsockBroadcast(URL_WS_UPDATE, sock_buff, (int) strlen(sock_buff), 0); - resetHeartbeatTimer(); - } + updateNotify_do(arg, 0); notify_cooldown = true; - notify_available = true; TIMER_START(¬ifyCooldownTim, notifyCooldownTimCb, termconf->display_cooldown_ms, 0); } + /** Beep */ void ICACHE_FLASH_ATTR send_beep(void) { - if (active_clients == 0) return; - - // here's some potential for a race error with the other broadcast functions :C - cgiWebsockBroadcast(URL_WS_UPDATE, "B", 1, 0); - resetHeartbeatTimer(); + if (term_active_clients == 0) return; + screen_notifyChange(TOPIC_BELL); // XXX has latency, maybe better to send directly } @@ -143,10 +126,10 @@ send_beep(void) void ICACHE_FLASH_ATTR 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 :C + // here's some potential for a race error with the other broadcast functions + // - we assume app won't send notifications in the middle of updating content cgiWebsockBroadcast(URL_WS_UPDATE, msg, (int) strlen(msg), 0); resetHeartbeatTimer(); } @@ -157,24 +140,17 @@ notify_growl(char *msg) * This is a callback for the Screen module, * 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 - if (termconf->display_tout_ms == 0) + if (termconf->display_tout_ms == 0) { termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS; - - // NOTE: the timers are restarted if already running - - if (topic == CHANGE_LABELS) { - // separate timer from content change timer, to avoid losing that update - TIMER_START(¬ifyLabelsTim, 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(¬ifyContentTim, notifyContentTimCb, termconf->display_tout_ms, 0); } + + // NOTE: the timer is restarted if already running + TIMER_START(&updateNotifyTim, updateNotifyCb, termconf->display_tout_ms, 0); // note - this adds latency to beep } /** @@ -296,8 +272,7 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int case 'i': // requests initial load inp_dbg("Client requests initial load"); - notifyContentTimCb(ws); // delay?? - notifyLabelsTimCb(ws); + updateNotify_do(ws, TOPIC_INITIAL); break; case 'm': @@ -322,7 +297,7 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int /** Send a heartbeat msg */ static void ICACHE_FLASH_ATTR heartbeatTimCb(void *unused) { - if (active_clients > 0) { + if (term_active_clients > 0) { if (notify_available) { inp_dbg("."); @@ -348,9 +323,9 @@ static void ICACHE_FLASH_ATTR resetHeartbeatTimer(void) static void ICACHE_FLASH_ATTR closeSockCb(Websock *ws) { - active_clients--; - if (active_clients <= 0) { - active_clients = 0; + term_active_clients--; + if (term_active_clients <= 0) { + term_active_clients = 0; if (mouse_tracking.focus_tracking) { UART_SendAsync("\x1b[O", 3); @@ -368,7 +343,7 @@ void ICACHE_FLASH_ATTR updateSockConnect(Websock *ws) ws->recvCb = updateSockRx; ws->closeCb = closeSockCb; - if (active_clients == 0) { + if (term_active_clients == 0) { if (mouse_tracking.focus_tracking) { UART_SendAsync("\x1b[I", 3); } @@ -376,7 +351,7 @@ void ICACHE_FLASH_ATTR updateSockConnect(Websock *ws) resetHeartbeatTimer(); } - active_clients++; + term_active_clients++; } ETSTimer xonTim; diff --git a/user/cgi_sockets.h b/user/cgi_sockets.h index 2010cfb..f608b3d 100644 --- a/user/cgi_sockets.h +++ b/user/cgi_sockets.h @@ -15,6 +15,8 @@ void send_beep(void); /** open pop-up notification */ void notify_growl(char *msg); +extern volatile int term_active_clients; + // defined in the makefile #if DEBUG_INPUT #define inp_warn warn diff --git a/user/cgi_term_cfg.c b/user/cgi_term_cfg.c index 775db58..85f810e 100644 --- a/user/cgi_term_cfg.c +++ b/user/cgi_term_cfg.c @@ -40,7 +40,7 @@ cgiTermCfgSetParams(HttpdConnData *connData) char buff[50]; char redir_url_buf[100]; int32 n, w, h; - bool notify_screen_content = 0, notify_screen_labels = 0; + ScreenNotifyTopics topics = 0; bool shall_clear_screen = false; bool shall_init_uart = false; @@ -94,7 +94,8 @@ cgiTermCfgSetParams(HttpdConnData *connData) if (termconf->width != w || termconf->height != h) { termconf->width = w; 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); } @@ -112,6 +113,7 @@ cgiTermCfgSetParams(HttpdConnData *connData) if (termconf->default_bg != n) { 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) { 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); n = atoi(buff); termconf->fn_alt_mode = (bool)n; - notify_screen_content = true; + topics |= TOPIC_CHANGE_SCREEN_OPTS; } if (GET_ARG("want_all_fn")) { cgi_dbg("AllFN mode: %s", buff); n = atoi(buff); termconf->want_all_fn = (bool)n; + topics |= TOPIC_CHANGE_SCREEN_OPTS; } if (GET_ARG("crlf_mode")) { cgi_dbg("CRLF mode: %s", buff); n = atoi(buff); termconf->crlf_mode = (bool)n; - notify_screen_content = true; + topics |= TOPIC_CHANGE_SCREEN_OPTS; } if (GET_ARG("show_buttons")) { cgi_dbg("Show buttons: %s", buff); n = atoi(buff); termconf->show_buttons = (bool)n; - notify_screen_content = true; + topics |= TOPIC_CHANGE_SCREEN_OPTS; } if (GET_ARG("show_config_links")) { cgi_dbg("Show config links: %s", buff); n = atoi(buff); termconf->show_config_links = (bool)n; - notify_screen_content = true; + topics |= TOPIC_CHANGE_SCREEN_OPTS; } if (GET_ARG("loopback")) { cgi_dbg("Loopback: %s", buff); n = atoi(buff); 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")) { @@ -213,6 +225,7 @@ cgiTermCfgSetParams(HttpdConnData *connData) } else { cgi_warn("Bad theme num: %s", buff); redir_url += sprintf(redir_url, "theme,"); + topics |= TOPIC_CHANGE_SCREEN_OPTS; } } @@ -221,7 +234,7 @@ cgiTermCfgSetParams(HttpdConnData *connData) n = atoi(buff); if (n >= 0 && n <= 6 && n != 1) { termconf->cursor_shape = (enum CursorShape) n; - notify_screen_content = true; + topics |= TOPIC_CHANGE_SCREEN_OPTS; } else { cgi_warn("Bad cursor_shape num: %s", buff); redir_url += sprintf(redir_url, "cursor_shape,"); @@ -231,7 +244,7 @@ cgiTermCfgSetParams(HttpdConnData *connData) if (GET_ARG("term_title")) { cgi_dbg("Terminal title default text: \"%s\"", buff); 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++) { @@ -239,7 +252,7 @@ cgiTermCfgSetParams(HttpdConnData *connData) if (GET_ARG(buff)) { cgi_dbg("Button%d default text: \"%s\"", btn_i, buff); 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); @@ -368,13 +381,7 @@ cgiTermCfgSetParams(HttpdConnData *connData) serialInit(); } - if (notify_screen_content) { - screen_notifyChange(CHANGE_CONTENT); - } - - if (notify_screen_labels) { - screen_notifyChange(CHANGE_LABELS); - } + if (topics) screen_notifyChange(topics); httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied."); } else { @@ -440,6 +447,9 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg) else if (streq(token, "loopback")) { sprintf(buff, "%d", (int)termconf->loopback); } + else if (streq(token, "debugbar")) { + sprintf(buff, "%d", (int)termconf->debugbar); + } else if (streq(token, "theme")) { sprintf(buff, "%d", termconf->theme); } diff --git a/user/routes.h b/user/routes.h index 2ba843f..5296b48 100644 --- a/user/routes.h +++ b/user/routes.h @@ -6,7 +6,4 @@ extern const HttpdBuiltInUrl routes[]; -/** Broadcast screen state to sockets */ -void screen_notifyChange(); - #endif //ROUTES_H diff --git a/user/screen.c b/user/screen.c index 3a7af28..e24aee5 100644 --- a/user/screen.c +++ b/user/screen.c @@ -5,10 +5,9 @@ #include "sgr.h" #include "ascii.h" #include "apars_logging.h" -#include "jstring.h" #include "character_sets.h" #include "utf8.h" -#include "uart_buffer.h" +#include "cgi_sockets.h" TerminalConfigBundle * const termconf = &persist.current.termconf; TerminalConfigBundle termconf_live; @@ -74,7 +73,6 @@ typedef struct { bool hanging; //!< xenl state - cursor half-wrapped /* 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 u16 attrs; Color fg; //!< Foreground color for writing @@ -139,14 +137,19 @@ static struct { * (from nested calls) */ static volatile int notifyLock = 0; +static volatile ScreenNotifyTopics lockTopics = 0; -#define NOTIFY_LOCK() do { \ +#define NOTIFY_LOCK() do { \ notifyLock++; \ } while(0) - -#define NOTIFY_DONE() do { \ + +#define NOTIFY_DONE(updateTopics) do { \ + lockTopics |= (updateTopics); \ if (notifyLock > 0) notifyLock--; \ - if (notifyLock == 0) screen_notifyChange(CHANGE_CONTENT); \ + if (notifyLock == 0) { \ + screen_notifyChange(lockTopics); \ + lockTopics = 0;\ + } \ } while(0) /** 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->crlf_mode = SCR_DEF_CRLF; termconf->want_all_fn = SCR_DEF_ALLFN; + termconf->debugbar = SCR_DEF_DEBUGBAR; } /** @@ -203,12 +207,12 @@ terminal_apply_settings_noclear(void) { bool changed = false; -// // Migrate to v1 -// if (termconf->config_version < 1) { -// persist_dbg("termconf: Updating to version %d", 1); -// termconf->display_cooldown_ms = SCR_DEF_DISPLAY_COOLDOWN_MS; -// changed = 1; -// } + // Migrate to v1 + if (termconf->config_version < 1) { + persist_dbg("termconf: Updating to version %d", 1); + termconf->debugbar = SCR_DEF_DEBUGBAR; + changed = 1; + } termconf->config_version = TERMCONF_VERSION; @@ -257,6 +261,8 @@ screen_init(void) static void ICACHE_FLASH_ATTR cursor_reset(void) { + NOTIFY_LOCK(); + cursor.x = 0; cursor.y = 0; cursor.hanging = false; @@ -266,6 +272,8 @@ cursor_reset(void) cursor.charset1 = CS_0_DEC_SUPPLEMENTAL; screen_reset_sgr(); + + NOTIFY_DONE(TOPIC_CHANGE_CURSOR); } /** @@ -285,7 +293,7 @@ screen_reset_on_resize(void) // size is left unchanged 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 screen_reset_sgr(void) { + NOTIFY_LOCK(); + cursor.fg = 0; cursor.bg = 0; cursor.attrs = 0; cursor.conceal = false; + + NOTIFY_DONE(TOPIC_INTERNAL); } static void ICACHE_FLASH_ATTR screen_reset_do(bool size, bool labels) { + ScreenNotifyTopics topics = TOPIC_CHANGE_SCREEN_OPTS | TOPIC_CHANGE_CURSOR | TOPIC_CHANGE_CONTENT_ALL; NOTIFY_LOCK(); // DECopts @@ -350,7 +363,7 @@ screen_reset_do(bool size, bool labels) termconf_live.show_buttons = termconf->show_buttons; 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 @@ -368,7 +381,7 @@ screen_reset_do(bool size, bool labels) opt_backup.show_buttons = termconf_live.show_buttons; 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 } + NOTIFY_LOCK(); + if (alternate) { ansi_dbg("Swap to alternate"); + NOTIFY_LOCK(); // store old state memcpy(state_backup.title, termconf_live.title, TERM_TITLE_LEN); memcpy(state_backup.btn, termconf_live.btn, sizeof(termconf_live.btn)); @@ -411,7 +427,6 @@ screen_swap_state(bool alternate) } else { ansi_dbg("Unswap from alternate"); - NOTIFY_LOCK(); 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_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 screen_resize(state_backup.height, state_backup.width); // TODO restore screen content (if this is ever possible) - NOTIFY_DONE(); - screen_notifyChange(CHANGE_LABELS); } state_backup.alternate_active = alternate; + NOTIFY_DONE(TOPIC_INITIAL); } //endregion @@ -435,19 +449,25 @@ screen_swap_state(bool alternate) void ICACHE_FLASH_ATTR screen_clear_all_tabs(void) { + NOTIFY_LOCK(); memset(scr.tab_stops, 0, sizeof(scr.tab_stops)); + NOTIFY_DONE(TOPIC_INTERNAL); } void ICACHE_FLASH_ATTR screen_set_tab(void) { + NOTIFY_LOCK(); scr.tab_stops[cursor.x/32] |= (1<<(cursor.x%32)); + NOTIFY_DONE(TOPIC_INTERNAL); } void ICACHE_FLASH_ATTR screen_clear_tab(void) { + NOTIFY_LOCK(); 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; } } - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_CURSOR); } void ICACHE_FLASH_ATTR @@ -544,7 +564,7 @@ screen_tab_reverse(int count) cursor.x = 0; } } - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_CURSOR); } //endregion @@ -733,7 +753,7 @@ screen_clear(ClearMode mode) clear_range_utf(0, (cursor.y * W) + cursor.x); 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); break; } - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } void ICACHE_FLASH_ATTR @@ -768,7 +788,7 @@ screen_clear_in_line(unsigned int count) } else { 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 @@ -796,7 +816,7 @@ screen_insert_lines(unsigned int lines) copy_row(i, cursor.y); } } - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } void ICACHE_FLASH_ATTR @@ -821,7 +841,7 @@ screen_delete_lines(unsigned int lines) clear_range_noutf((movedBlockEnd+1)*W, (BTM+1)*W-1); } - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } 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); } - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } void ICACHE_FLASH_ATTR @@ -870,7 +890,7 @@ screen_delete_characters(unsigned int count) screen_clear_line(CLEAR_FROM_CURSOR); } - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } //endregion @@ -891,7 +911,7 @@ screen_fill_with_E(void) for (unsigned int i = 0; i <= W*H-1; i++) { 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; H = rows; screen_reset_on_resize(); - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS|TOPIC_CHANGE_CONTENT_ALL|TOPIC_CHANGE_CURSOR); } void ICACHE_FLASH_ATTR screen_set_title(const char *title) { + NOTIFY_LOCK(); 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 screen_set_button_text(int num, const char *text) { + NOTIFY_LOCK(); 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); 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); done: - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } /** Set scrolling region */ void ICACHE_FLASH_ATTR screen_set_scrolling_region(int from, int to) { + NOTIFY_LOCK(); if (from <= 0 && to <= 0) { scr.vm0 = 0; 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) screen_cursor_set(0, 0); + NOTIFY_DONE(TOPIC_INTERNAL); } //endregion @@ -1033,7 +1057,7 @@ screen_cursor_shape(enum CursorShape shape) NOTIFY_LOCK(); if (shape == CURSOR_DEFAULT) shape = termconf->cursor_shape; termconf_live.cursor_shape = shape; - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS); } /** 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_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(); screen_cursor_set_x(x); 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 // cannot be set using the cursor-set commands. 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; } 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 screen_back_index(int count) { NOTIFY_LOCK(); + ScreenNotifyTopics topics = TOPIC_CHANGE_CURSOR; int new_x = cursor.x - count; if (new_x >= 0) { cursor.x = new_x; } else { cursor.x = 0; screen_insert_characters(-new_x); + topics |= TOPIC_CHANGE_CONTENT_PART; } - NOTIFY_DONE(); + NOTIFY_DONE(topics); } //endregion @@ -1261,7 +1287,7 @@ screen_set_cursor_visible(bool visible) { NOTIFY_LOCK(); 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 screen_wrap_enable(bool enable) { + NOTIFY_LOCK(); cursor.auto_wrap = enable; + NOTIFY_DONE(TOPIC_INTERNAL); } /** @@ -1279,7 +1307,9 @@ screen_wrap_enable(bool enable) void ICACHE_FLASH_ATTR screen_reverse_wrap_enable(bool enable) { + NOTIFY_LOCK(); cursor.reverse_wrap = enable; + NOTIFY_DONE(TOPIC_INTERNAL); } /** @@ -1288,8 +1318,10 @@ screen_reverse_wrap_enable(bool enable) void ICACHE_FLASH_ATTR screen_set_fg(Color color) { + NOTIFY_LOCK(); cursor.fg = color; cursor.attrs |= ATTR_FG; + NOTIFY_DONE(TOPIC_INTERNAL); } /** @@ -1298,8 +1330,10 @@ screen_set_fg(Color color) void ICACHE_FLASH_ATTR screen_set_bg(Color color) { + NOTIFY_LOCK(); cursor.bg = color; cursor.attrs |= ATTR_BG; + NOTIFY_DONE(TOPIC_INTERNAL); } /** @@ -1308,8 +1342,10 @@ screen_set_bg(Color color) void ICACHE_FLASH_ATTR screen_set_default_fg(void) { + NOTIFY_LOCK(); cursor.fg = 0; cursor.attrs &= ~ATTR_FG; + NOTIFY_DONE(TOPIC_INTERNAL); } /** @@ -1318,45 +1354,57 @@ screen_set_default_fg(void) void ICACHE_FLASH_ATTR screen_set_default_bg(void) { + NOTIFY_LOCK(); cursor.bg = 0; cursor.attrs &= ~ATTR_BG; + NOTIFY_DONE(TOPIC_INTERNAL); } void ICACHE_FLASH_ATTR screen_set_sgr(CellAttrs attrs, bool ena) { + NOTIFY_LOCK(); if (ena) { cursor.attrs |= attrs; } else { cursor.attrs &= ~attrs; } + NOTIFY_DONE(TOPIC_INTERNAL); } void ICACHE_FLASH_ATTR screen_set_sgr_conceal(bool ena) { + NOTIFY_LOCK(); cursor.conceal = ena; + NOTIFY_DONE(TOPIC_INTERNAL); } void ICACHE_FLASH_ATTR screen_set_charset_n(int Gx) { + NOTIFY_LOCK(); if (Gx < 0 || Gx > 1) return; // bad n cursor.charsetN = Gx; + NOTIFY_DONE(TOPIC_INTERNAL); } void ICACHE_FLASH_ATTR screen_set_charset(int Gx, char charset) { + NOTIFY_LOCK(); if (Gx == 0) cursor.charset0 = charset; else if (Gx == 1) cursor.charset1 = charset; + NOTIFY_DONE(TOPIC_INTERNAL); } void ICACHE_FLASH_ATTR screen_set_insert_mode(bool insert) { + NOTIFY_LOCK(); scr.insert_mode = insert; + NOTIFY_DONE(TOPIC_INTERNAL); } void ICACHE_FLASH_ATTR @@ -1364,7 +1412,7 @@ screen_set_numpad_alt_mode(bool alt_mode) { NOTIFY_LOCK(); scr.numpad_alt_mode = alt_mode; - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS); } void ICACHE_FLASH_ATTR @@ -1372,7 +1420,7 @@ screen_set_cursors_alt_mode(bool alt_mode) { NOTIFY_LOCK(); scr.cursors_alt_mode = alt_mode; - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS); } void ICACHE_FLASH_ATTR @@ -1380,7 +1428,7 @@ screen_set_reverse_video(bool reverse) { NOTIFY_LOCK(); scr.reverse_video = reverse; - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS); } void ICACHE_FLASH_ATTR @@ -1388,7 +1436,7 @@ screen_set_bracketed_paste(bool ena) { NOTIFY_LOCK(); scr.bracketed_paste = ena; - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS); } void ICACHE_FLASH_ATTR @@ -1396,29 +1444,34 @@ screen_set_newline_mode(bool nlm) { NOTIFY_LOCK(); termconf_live.crlf_mode = nlm; - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS); } void ICACHE_FLASH_ATTR screen_set_origin_mode(bool region_origin) { + NOTIFY_LOCK(); cursor.origin_mode = region_origin; screen_cursor_set(0, 0); + NOTIFY_DONE(TOPIC_INTERNAL); } static void ICACHE_FLASH_ATTR 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) switch (n) { case 1: SAVE_RESTORE(opt_backup.cursors_alt_mode, scr.cursors_alt_mode); + topics |= TOPIC_CHANGE_SCREEN_OPTS; break; case 5: SAVE_RESTORE(opt_backup.reverse_video, scr.reverse_video); break; 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; case 7: SAVE_RESTORE(opt_backup.auto_wrap, cursor.auto_wrap); @@ -1429,14 +1482,17 @@ do_save_private_opt(int n, bool save) case 1002: case 1003: SAVE_RESTORE(opt_backup.mouse_tracking, mouse_tracking.mode); + topics |= TOPIC_CHANGE_SCREEN_OPTS; break; case 1004: SAVE_RESTORE(opt_backup.focus_tracking, mouse_tracking.focus_tracking); + topics |= TOPIC_CHANGE_SCREEN_OPTS; break; case 1005: case 1006: case 1015: SAVE_RESTORE(opt_backup.mouse_encoding, mouse_tracking.encoding); + topics |= TOPIC_CHANGE_SCREEN_OPTS; break; case 12: // cursor blink if (save) { @@ -1444,9 +1500,11 @@ do_save_private_opt(int n, bool save) } else { screen_cursor_blink(opt_backup.cursor_blink); } + topics |= TOPIC_CHANGE_SCREEN_OPTS; break; case 25: SAVE_RESTORE(opt_backup.cursor_visible, scr.cursor_visible); + topics |= TOPIC_CHANGE_SCREEN_OPTS; break; case 45: SAVE_RESTORE(opt_backup.reverse_wrap, cursor.reverse_wrap); @@ -1456,19 +1514,24 @@ do_save_private_opt(int n, bool save) break; case 800: SAVE_RESTORE(opt_backup.show_buttons, termconf_live.show_buttons); + topics |= TOPIC_CHANGE_SCREEN_OPTS; break; case 801: SAVE_RESTORE(opt_backup.show_config_links, termconf_live.show_config_links); + topics |= TOPIC_CHANGE_SCREEN_OPTS; break; default: ansi_warn("Cannot store ?%d", n); } + if (!save) NOTIFY_DONE(topics); } void ICACHE_FLASH_ATTR screen_save_private_opt(int n) { + NOTIFY_LOCK(); do_save_private_opt(n, true); + NOTIFY_DONE(TOPIC_INTERNAL); } void ICACHE_FLASH_ATTR @@ -1476,7 +1539,7 @@ screen_restore_private_opt(int n) { NOTIFY_LOCK(); do_save_private_opt(n, false); - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_INTERNAL); } void ICACHE_FLASH_ATTR @@ -1599,7 +1662,7 @@ screen_putchar(const char *ch) strncpy(scr.last_char, result, 4); done: - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_WRITE); } /** @@ -1621,7 +1684,7 @@ screen_repeat_last_character(int count) putchar_graphic(scr.last_char); count--; } - NOTIFY_DONE(); + NOTIFY_DONE(TOPIC_WRITE); } /** @@ -1674,29 +1737,12 @@ struct ScreenSerializeState { UnicodeCacheRef lastSymbol; char lastChar[4]; 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. * @@ -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 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 - * if NULL, indicates this is the first call. - * @return HTTPD_CGI_DONE or HTTPD_CGI_MORE. If more, repeat with the same DATA. + * 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` pointer. */ 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; @@ -1719,11 +1767,11 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data) return HTTPD_CGI_DONE; } - Cell *cell, *cell0; + Cell *cell, *cell0; // temporary cell pointers for finding repetitions - u8 nbytes; - size_t remain = buf_len; - char *bb = buffer; + u8 nbytes; // temporary variable for utf writing utilities + size_t remain = buf_len; // remaining space in the output buffer + char *bb = buffer; // write pointer #define bufput_c(c) do { \ *bb = (char)(c); \ @@ -1742,47 +1790,180 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data) bufput_utf8((num)); \ } 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_ALL 'S' +#define TOPICMARK_SCREEN_PART 's' + if (ss == NULL) { + // START! + *data = ss = malloc(sizeof(struct ScreenSerializeState)); + + if (topics == 0 || termconf_live.debugbar) { + topics |= TOPIC_INTERNAL; + } + ss->index = 0; - ss->lastBg = 0xFF; - ss->lastFg = 0xFF; - ss->lastAttrs = 0xFFFF; - ss->lastCharLen = 0; - ss->lastSymbol = 0; + + ss->topics = topics; + ss->last_topic = 0; // to be filled + ss->current_topic = 0; // to be filled strncpy(ss->lastChar, " ", 4); - bufput_c('S'); - // H W X Y Attribs - bufput_utf8(H); - 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) - ); + dbg("NOTIFY -> %02X", topics); + + bufput_c('U'); // - stands for "update" + + bufput_utf8(topics & ~TOPIC_INTERNAL); } -#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' + int begun_topic = 0; + int prev_topic = 0; + +#define BEGIN_TOPIC(topic, size) \ + 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); + bufput_utf8(len); + memcpy(bb, termconf_live.title, len); + bb += len; + remain -= len; + 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]); + bufput_utf8(len); + memcpy(bb, termconf_live.btn[i], len); + bb += len; + remain -= len; + } + 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 - TODO implement partial update int i = ss->index; + if (i == 0) { + bufput_c(TOPICMARK_SCREEN_ALL); // desired update mode is in `ss->current_topic` + + ss->index = 0; + ss->lastBg = 0xFF; + ss->lastFg = 0xFF; + ss->lastAttrs = 0xFFFF; + ss->lastCharLen = 0; + ss->lastSymbol = 0; + } while(i < W*H && remain > 12) { cell = cell0 = &screen[i]; @@ -1848,20 +2029,22 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data) } ss->index = i; + if (i >= W*H-1) goto ser_done; + + // MORE TO WRITE... 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 - printf("MSG: "); +printf("MSG: "); for (int j=0;j Date: Thu, 28 Sep 2017 21:13:10 +0200 Subject: [PATCH 02/35] re-ordered cell attr bits for shorter serialize word length --- user/screen.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/user/screen.h b/user/screen.h index 9e0c271..2e4251b 100644 --- a/user/screen.h +++ b/user/screen.h @@ -253,18 +253,19 @@ void screen_set_origin_mode(bool region_origin); typedef uint8_t Color; 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 -#define ATTR_BOLD (1<<0) //!< Bold font -#define ATTR_FAINT (1<<1) //!< Faint foreground color (reduced alpha) -#define ATTR_ITALIC (1<<2) //!< Italic font -#define ATTR_UNDERLINE (1<<3) //!< Underline decoration -#define ATTR_BLINK (1<<4) //!< Blinking -#define ATTR_FRAKTUR (1<<5) //!< Fraktur font (unicode substitution) -#define ATTR_STRIKE (1<<6) //!< Strike-through decoration -#define ATTR_OVERLINE (1<<7) //!< Over-line decoration -#define ATTR_FG (1<<8) //!< 1 if not using default background color (ignore cell bg) - color extension bit -#define ATTR_BG (1<<9) //!< 1 if not using default foreground color (ignore cell fg) - color extension bit -#define ATTR_INVERSE (1<<10) //!< Invert colors - this is useful so we can clear then with SGR manipulation commands +enum SgrAttrBits { + ATTR_FG = (1<<0), //!< 1 if not using default background color (ignore cell bg) - color extension bit + ATTR_BG = (1<<1), //!< 1 if not using default foreground color (ignore cell fg) - color extension bit + ATTR_BOLD = (1<<2), //!< Bold font + ATTR_UNDERLINE = (1<<3), //!< Underline decoration + ATTR_INVERSE = (1<<4), //!< Invert colors - this is useful so we can clear then with SGR manipulation commands + ATTR_BLINK = (1<<5), //!< Blinking + ATTR_ITALIC = (1<<6), //!< Italic font + ATTR_STRIKE = (1<<7), //!< Strike-through decoration + ATTR_OVERLINE = (1<<8), //!< Over-line decoration + ATTR_FAINT = (1<<9), //!< Faint foreground color (reduced alpha) + ATTR_FRAKTUR = (1<<10), //!< Fraktur font (unicode substitution) +}; /** Set cursor foreground color */ void screen_set_fg(Color color); From da6c5bf5bef396cb2e7d30628d48ea00020ef886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 28 Sep 2017 21:50:51 +0200 Subject: [PATCH 03/35] re-arranged attr bits and changed string encoding for labels and title to use terminator rather than byte length --- user/screen.c | 19 ++++++++++++------- user/screen.h | 14 +++++++------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/user/screen.c b/user/screen.c index e24aee5..c4c5f70 100644 --- a/user/screen.c +++ b/user/screen.c @@ -1803,8 +1803,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, #define TOPICMARK_DEBUG 'D' #define TOPICMARK_BELL '!' #define TOPICMARK_CURSOR 'C' -#define TOPICMARK_SCREEN_ALL 'S' -#define TOPICMARK_SCREEN_PART 's' +#define TOPICMARK_SCREEN 'S' if (ss == NULL) { // START! @@ -1826,7 +1825,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_c('U'); // - stands for "update" - bufput_utf8(topics & ~TOPIC_INTERNAL); + bufput_utf8(topics); } int begun_topic = 0; @@ -1880,10 +1879,10 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_c(TOPICMARK_TITLE); int len = (int) strlen(termconf_live.title); - bufput_utf8(len); 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) @@ -1893,10 +1892,10 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, for (int i = 0; i < TERM_BTN_COUNT; i++) { int len = (int) strlen(termconf_live.btn[i]); - bufput_utf8(len); memcpy(bb, termconf_live.btn[i], len); bb += len; remain -= len; + bufput_c('\x01'); } END_TOPIC @@ -1952,10 +1951,16 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, } } - // screen contents - TODO implement partial update + // screen contents int i = ss->index; if (i == 0) { - bufput_c(TOPICMARK_SCREEN_ALL); // desired update mode is in `ss->current_topic` + 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; diff --git a/user/screen.h b/user/screen.h index 2e4251b..fc496d1 100644 --- a/user/screen.h +++ b/user/screen.h @@ -156,13 +156,13 @@ typedef enum { enum ScreenSerializeTopic { TOPIC_CHANGE_SCREEN_OPTS = (1<<0), - TOPIC_CHANGE_TITLE = (1<<1), - TOPIC_CHANGE_BUTTONS = (1<<2), - TOPIC_BELL = (1<<3), // beep - TOPIC_CHANGE_CURSOR = (1<<4), - TOPIC_CHANGE_CONTENT_ALL = (1<<5), - TOPIC_CHANGE_CONTENT_PART = (1<<6), - TOPIC_INTERNAL = (1<<7), // debugging internal state + 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 | From 03c7f4d0ad19e25a374c56de2b3c4c7fff4cf55d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 28 Sep 2017 22:09:30 +0200 Subject: [PATCH 04/35] removed bugs --- user/cgi_sockets.c | 2 ++ user/screen.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index baddd07..663dfac 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -149,6 +149,8 @@ void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyTopics topics) termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS; } + pendingBroadcastTopics |= topics; + // NOTE: the timer is restarted if already running TIMER_START(&updateNotifyTim, updateNotifyCb, termconf->display_tout_ms, 0); // note - this adds latency to beep } diff --git a/user/screen.c b/user/screen.c index c4c5f70..87a0245 100644 --- a/user/screen.c +++ b/user/screen.c @@ -1821,8 +1821,6 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, ss->current_topic = 0; // to be filled strncpy(ss->lastChar, " ", 4); - dbg("NOTIFY -> %02X", topics); - bufput_c('U'); // - stands for "update" bufput_utf8(topics); From 1614194d7679743f91062a62eddda7e4a6e5418f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 29 Sep 2017 20:49:52 +0200 Subject: [PATCH 05/35] Removed one NOTIFY_LOCK() that shouldn't have been there --- front-end | 2 +- libesphttpd | 2 +- user/cgi_sockets.c | 6 +++++- user/screen.c | 1 - 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/front-end b/front-end index f5dd70a..673358e 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit f5dd70a6f32ac36f0820badc170832f27242a09d +Subproject commit 673358e2ce64b65b28a4a94079dd83d56a51254a diff --git a/libesphttpd b/libesphttpd index 3479ab3..24f9a37 160000 --- a/libesphttpd +++ b/libesphttpd @@ -1 +1 @@ -Subproject commit 3479ab3efcb4581669370cde6a607f936ff5515a +Subproject commit 24f9a371eb5c0804dcc6657f99449ef07788140c diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index 663dfac..44accb2 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -98,10 +98,12 @@ updateNotifyCb(void *arg) int max_bl, total_bl; cgiWebsockMeasureBacklog(URL_WS_UPDATE, &max_bl, &total_bl); + inp_dbg("Notify broadcast +%02Xh?", pendingBroadcastTopics); + if (!notify_available || notify_cooldown || (max_bl > 2048)) { // do not send if we have anything significant backlogged // postpone a little TIMER_START(&updateNotifyTim, updateNotifyCb, 4, 0); - inp_dbg("postpone notify content"); + inp_dbg("postpone notify; avail? %d coold? %d maxbl? %d", notify_available, notify_cooldown, max_bl); return; } @@ -149,6 +151,8 @@ void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyTopics topics) termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS; } + inp_dbg("Notify +%02Xh", topics); + pendingBroadcastTopics |= topics; // NOTE: the timer is restarted if already running diff --git a/user/screen.c b/user/screen.c index 87a0245..cf6f5f0 100644 --- a/user/screen.c +++ b/user/screen.c @@ -412,7 +412,6 @@ screen_swap_state(bool alternate) if (alternate) { ansi_dbg("Swap to alternate"); - NOTIFY_LOCK(); // store old state memcpy(state_backup.title, termconf_live.title, TERM_TITLE_LEN); memcpy(state_backup.btn, termconf_live.btn, sizeof(termconf_live.btn)); From b70c65868b1067c736f78786af950a58219f0a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 29 Sep 2017 21:57:20 +0200 Subject: [PATCH 06/35] dump context and stop listening for a bit on rx of bad UTF --- user/ansi_parser.c | 49 ++++++++++++++++--------------- user/ansi_parser.h | 2 ++ user/ansi_parser.rl | 3 ++ user/apars_utf8.c | 71 +++++++++++++++++++++++++++++++++------------ user/user_main.c | 7 ++++- 5 files changed, 89 insertions(+), 43 deletions(-) diff --git a/user/ansi_parser.c b/user/ansi_parser.c index 7b01ded..d23cca6 100644 --- a/user/ansi_parser.c +++ b/user/ansi_parser.c @@ -44,6 +44,7 @@ static volatile bool inside_string = false; // public volatile u32 ansi_parser_char_cnt = 0; +volatile bool ansi_parser_inhibit = 0; void ICACHE_FLASH_ATTR ansi_parser_reset(void) { @@ -108,18 +109,20 @@ ansi_parser(char newchar) static char string_buffer[ANSI_STR_LEN]; static int str_ni; + if (ansi_parser_inhibit) return; + // This is used to detect timeout delay (time since last rx char) ansi_parser_char_cnt++; // Init Ragel on the first run if (cs == -1) { -/* #line 118 "user/ansi_parser.c" */ +/* #line 121 "user/ansi_parser.c" */ { cs = ansi_start; } -/* #line 92 "user/ansi_parser.rl" */ +/* #line 95 "user/ansi_parser.rl" */ #if DEBUG_ANSI memset(history, 0, sizeof(history)); @@ -199,7 +202,7 @@ ansi_parser(char newchar) // The parser -/* #line 203 "user/ansi_parser.c" */ +/* #line 206 "user/ansi_parser.c" */ { const char *_acts; unsigned int _nacts; @@ -389,7 +392,7 @@ execFuncs: while ( _nacts-- > 0 ) { switch ( *_acts++ ) { case 0: -/* #line 179 "user/ansi_parser.rl" */ +/* #line 182 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_show_context(); @@ -398,7 +401,7 @@ execFuncs: } break; case 1: -/* #line 188 "user/ansi_parser.rl" */ +/* #line 191 "user/ansi_parser.rl" */ { if ((*p) != 0) { apars_handle_plainchar((*p)); @@ -406,7 +409,7 @@ execFuncs: } break; case 2: -/* #line 196 "user/ansi_parser.rl" */ +/* #line 199 "user/ansi_parser.rl" */ { // Reset the CSI builder leadchar = NUL; @@ -423,13 +426,13 @@ execFuncs: } break; case 3: -/* #line 211 "user/ansi_parser.rl" */ +/* #line 214 "user/ansi_parser.rl" */ { leadchar = (*p); } break; case 4: -/* #line 215 "user/ansi_parser.rl" */ +/* #line 218 "user/ansi_parser.rl" */ { if (arg_cnt == 0) arg_cnt = 1; // x10 + digit @@ -439,7 +442,7 @@ execFuncs: } break; case 5: -/* #line 223 "user/ansi_parser.rl" */ +/* #line 226 "user/ansi_parser.rl" */ { if (arg_cnt == 0) arg_cnt = 1; // handle case when first arg is empty arg_cnt++; @@ -447,20 +450,20 @@ execFuncs: } break; case 6: -/* #line 229 "user/ansi_parser.rl" */ +/* #line 232 "user/ansi_parser.rl" */ { interchar = (*p); } break; case 7: -/* #line 233 "user/ansi_parser.rl" */ +/* #line 236 "user/ansi_parser.rl" */ { apars_handle_csi(leadchar, arg, arg_cnt, interchar, (*p)); {cs = 1;goto _again;} } break; case 8: -/* #line 245 "user/ansi_parser.rl" */ +/* #line 248 "user/ansi_parser.rl" */ { leadchar = (*p); str_ni = 0; @@ -470,13 +473,13 @@ execFuncs: } break; case 9: -/* #line 253 "user/ansi_parser.rl" */ +/* #line 256 "user/ansi_parser.rl" */ { string_buffer[str_ni++] = (*p); } break; case 10: -/* #line 257 "user/ansi_parser.rl" */ +/* #line 260 "user/ansi_parser.rl" */ { inside_string = false; string_buffer[str_ni++] = '\0'; @@ -485,41 +488,41 @@ execFuncs: } break; case 11: -/* #line 270 "user/ansi_parser.rl" */ +/* #line 273 "user/ansi_parser.rl" */ { apars_handle_hash_cmd((*p)); {cs = 1;goto _again;} } break; case 12: -/* #line 275 "user/ansi_parser.rl" */ +/* #line 278 "user/ansi_parser.rl" */ { apars_handle_short_cmd((*p)); {cs = 1;goto _again;} } break; case 13: -/* #line 280 "user/ansi_parser.rl" */ +/* #line 283 "user/ansi_parser.rl" */ { apars_handle_space_cmd((*p)); {cs = 1;goto _again;} } break; case 14: -/* #line 287 "user/ansi_parser.rl" */ +/* #line 290 "user/ansi_parser.rl" */ { leadchar = (*p); {cs = 10;goto _again;} } break; case 15: -/* #line 292 "user/ansi_parser.rl" */ +/* #line 295 "user/ansi_parser.rl" */ { apars_handle_chs_designate(leadchar, (*p)); {cs = 1;goto _again;} } break; -/* #line 523 "user/ansi_parser.c" */ +/* #line 526 "user/ansi_parser.c" */ } } goto _again; @@ -537,7 +540,7 @@ _again: while ( __nacts-- > 0 ) { switch ( *__acts++ ) { case 0: -/* #line 179 "user/ansi_parser.rl" */ +/* #line 182 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_show_context(); @@ -547,7 +550,7 @@ _again: goto _again;} } break; -/* #line 551 "user/ansi_parser.c" */ +/* #line 554 "user/ansi_parser.c" */ } } } @@ -555,6 +558,6 @@ goto _again;} _out: {} } -/* #line 315 "user/ansi_parser.rl" */ +/* #line 318 "user/ansi_parser.rl" */ } diff --git a/user/ansi_parser.h b/user/ansi_parser.h index 8e813bd..212aaa4 100644 --- a/user/ansi_parser.h +++ b/user/ansi_parser.h @@ -3,6 +3,8 @@ #include +extern volatile bool ansi_parser_inhibit; // discard all characters + void ansi_parser_reset(void); extern volatile u32 ansi_parser_char_cnt; diff --git a/user/ansi_parser.rl b/user/ansi_parser.rl index 2f74623..7566210 100644 --- a/user/ansi_parser.rl +++ b/user/ansi_parser.rl @@ -19,6 +19,7 @@ static volatile bool inside_string = false; // public volatile u32 ansi_parser_char_cnt = 0; +volatile bool ansi_parser_inhibit = 0; void ICACHE_FLASH_ATTR ansi_parser_reset(void) { @@ -83,6 +84,8 @@ ansi_parser(char newchar) static char string_buffer[ANSI_STR_LEN]; static int str_ni; + if (ansi_parser_inhibit) return; + // This is used to detect timeout delay (time since last rx char) ansi_parser_char_cnt++; diff --git a/user/apars_utf8.c b/user/apars_utf8.c index b3f502c..17b7387 100644 --- a/user/apars_utf8.c +++ b/user/apars_utf8.c @@ -8,11 +8,22 @@ #include "apars_utf8.h" #include "apars_logging.h" #include "screen.h" +#include "uart_driver.h" +#include "ansi_parser_callbacks.h" +#include "ansi_parser.h" -static char utf_collect[4]; -static int utf_i = 0; +static u8 bytes[4]; +static int utf_len = 0; static int utf_j = 0; +ETSTimer timerResumeRx; + +void ICACHE_FLASH_ATTR resumeRxCb(void *unused) +{ + ansi_dbg("Parser recover."); + ansi_parser_inhibit = false; +} + /** * Clear the buffer where we collect pieces of a code point. * This is used for parser reset. @@ -20,11 +31,22 @@ static int utf_j = 0; void ICACHE_FLASH_ATTR apars_reset_utf8buffer(void) { - utf_i = 0; + utf_len = 0; utf_j = 0; - memset(utf_collect, 0, 4); + memset(bytes, 0, 4); } +// Code Points First Byte Second Byte Third Byte Fourth Byte +// U+0000 - U+007F 00 - 7F +// U+0080 - U+07FF C2 - DF 80 - BF +// U+0800 - U+0FFF E0 *A0 - BF 80 - BF +// U+1000 - U+CFFF E1 - EC 80 - BF 80 - BF +// U+D000 - U+D7FF ED 80 - *9F 80 - BF +// U+E000 - U+FFFF EE - EF 80 - BF 80 - BF +// U+10000 - U+3FFFF F0 *90 - BF 80 - BF 80 - BF +// U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF +// U+100000 - U+10FFFF F4 80 - *8F 80 - BF 80 - BF + /** * Handle a received plain character * @param c - received character @@ -34,28 +56,28 @@ apars_handle_plainchar(char c) { // collecting unicode glyphs... if (c & 0x80) { - if (utf_i == 0) { + if (utf_len == 0) { // start - if (c == 192 || c == 193 || c >= 245) { - // forbidden codes (would be an overlong sequence) + if (c == 0xC0 || c == 0xC1 || c > 0xF4) { + // forbidden start codes goto fail; } if ((c & 0xE0) == 0xC0) { - utf_i = 2; + utf_len = 2; } else if ((c & 0xF0) == 0xE0) { - utf_i = 3; + utf_len = 3; } else if ((c & 0xF8) == 0xF0) { - utf_i = 4; + utf_len = 4; } else { // chars over 127 that don't start unicode sequences goto fail; } - utf_collect[0] = c; + bytes[0] = c; utf_j = 1; } else { @@ -63,22 +85,33 @@ apars_handle_plainchar(char c) goto fail; } else { - utf_collect[utf_j++] = c; - if (utf_j >= utf_i) { - screen_putchar(utf_collect); + bytes[utf_j++] = c; + if (utf_j >= utf_len) { + // check for bad sequences + if (bytes[0] == 0xF4 && bytes[1] > 0x8F) goto fail; + if (bytes[0] == 0xF0 && bytes[1] < 0x90) goto fail; + if (bytes[0] == 0xED && bytes[1] > 0x9F) goto fail; + if (bytes[0] == 0xE0 && bytes[1] < 0xA0) goto fail; + + screen_putchar((const char *) bytes); apars_reset_utf8buffer(); } } } } else { - utf_collect[0] = c; - utf_collect[1] = 0; // just to make sure it's closed... - screen_putchar(utf_collect); + bytes[0] = c; + bytes[1] = 0; // just to make sure it's closed... + screen_putchar((const char *) bytes); } return; - fail: - ansi_warn("Bad UTF-8: %0Xh", c); +fail: + ansi_parser_inhibit = true; + + ansi_warn("BAD UTF8!"); + apars_show_context(); apars_reset_utf8buffer(); + ansi_dbg("Temporarily inhibiting parser..."); + TIMER_START(&timerResumeRx, resumeRxCb, 1000, 0); } diff --git a/user/user_main.c b/user/user_main.c index 7b9a98f..fd735b5 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -28,6 +28,8 @@ #include "ansi_parser_callbacks.h" #include "wifimgr.h" #include "persist.h" +#include "ansi_parser.h" +#include "ascii.h" #ifdef ESPFS_POS CgiUploadFlashDef uploadParams={ @@ -86,6 +88,7 @@ static ETSTimer prHeapTimer; //Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. void ICACHE_FLASH_ATTR user_init(void) { + ansi_parser_inhibit = true; serialInitBase(); // Prevent WiFi starting and connecting by default @@ -128,9 +131,11 @@ static void ICACHE_FLASH_ATTR user_start(void *unused) captdnsInit(); httpdInit(routes, 80); + ansi_parser_inhibit = false; + // Print the CANCEL character to indicate the module has restarted // Critically important for client application if any kind of screen persistence / content re-use is needed - UART_WriteChar(UART0, 24, UART_TIMEOUT_US); // 0x18 - 24 - CAN + UART_WriteChar(UART0, CAN, UART_TIMEOUT_US); // 0x18 - 24 - CAN } // ---- unused funcs removed from sdk to save space --- From 1c23e79c59469f04d63a22f5f90cfbe1e94798c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 30 Sep 2017 01:18:03 +0200 Subject: [PATCH 07/35] partial update, may be a bit buggy in places --- user/cgi_sockets.c | 2 +- user/screen.c | 131 +++++++++++++++++++++++++++++++++++++++------ user/screen.h | 2 + 3 files changed, 117 insertions(+), 18 deletions(-) diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index 44accb2..4824615 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -278,7 +278,7 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int case 'i': // requests initial load inp_dbg("Client requests initial load"); - updateNotify_do(ws, TOPIC_INITIAL); + updateNotify_do(ws, TOPIC_INITIAL|TOPIC_FLAG_NOCLEAN); break; case 'm': diff --git a/user/screen.c b/user/screen.c index cf6f5f0..9b76c28 100644 --- a/user/screen.c +++ b/user/screen.c @@ -139,6 +139,24 @@ static struct { static volatile int notifyLock = 0; static volatile ScreenNotifyTopics lockTopics = 0; +static struct { + int x_min, y_min, x_max, y_max; +} scr_dirty; + +#define reset_screen_dirty() do { \ + scr_dirty.x_min = W; \ + scr_dirty.x_max = -1; \ + scr_dirty.y_min = H; \ + scr_dirty.y_max = -1; \ + } while(0) + +#define expand_dirty(y0, y1, x0, x1) do { \ + if ((y0) < scr_dirty.y_min) scr_dirty.y_min = (y0); \ + if ((x0) < scr_dirty.x_min) scr_dirty.x_min = (x0); \ + if ((y1) > scr_dirty.y_max) scr_dirty.y_max = (y1); \ + if ((x1) > scr_dirty.x_max) scr_dirty.x_max = (x1); \ + } while(0) + #define NOTIFY_LOCK() do { \ notifyLock++; \ } while(0) @@ -252,6 +270,7 @@ screen_init(void) { if(DEBUG_HEAP) dbg("Screen buffer size = %d bytes", sizeof(screen)); + reset_screen_dirty(); screen_reset(); } @@ -746,10 +765,12 @@ screen_clear(ClearMode mode) case CLEAR_FROM_CURSOR: clear_range_utf((cursor.y * W) + cursor.x, W * H - 1); + expand_dirty(cursor.y, H-1, 0, W-1); break; case CLEAR_TO_CURSOR: clear_range_utf(0, (cursor.y * W) + cursor.x); + expand_dirty(0, cursor.y, 0, W-1); break; } NOTIFY_DONE(mode == CLEAR_ALL ? TOPIC_CHANGE_CONTENT_ALL : TOPIC_CHANGE_CONTENT_PART); @@ -765,14 +786,17 @@ screen_clear_line(ClearMode mode) switch (mode) { case CLEAR_ALL: clear_row_utf(cursor.y); + expand_dirty(cursor.y, cursor.y, 0, W-1); break; case CLEAR_FROM_CURSOR: clear_range_utf(cursor.y * W + cursor.x, (cursor.y + 1) * W - 1); + expand_dirty(cursor.y, cursor.y, cursor.x, W-1); break; case CLEAR_TO_CURSOR: clear_range_utf(cursor.y * W, cursor.y * W + cursor.x); + expand_dirty(cursor.y, cursor.y, 0, cursor.x); break; } NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); @@ -786,6 +810,7 @@ screen_clear_in_line(unsigned int count) screen_clear_line(CLEAR_FROM_CURSOR); } else { clear_range_utf(cursor.y * W + cursor.x, cursor.y * W + cursor.x + count - 1); + expand_dirty(cursor.y, cursor.y, cursor.x, cursor.x + count - 1); } NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } @@ -815,6 +840,7 @@ screen_insert_lines(unsigned int lines) copy_row(i, cursor.y); } } + expand_dirty(cursor.y, BTM, 0, W - 1); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } @@ -840,6 +866,7 @@ screen_delete_lines(unsigned int lines) clear_range_noutf((movedBlockEnd+1)*W, (BTM+1)*W-1); } + expand_dirty(cursor.y, BTM, 0, W - 1); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } @@ -863,6 +890,7 @@ screen_insert_characters(unsigned int count) } clear_range_utf(cursor.y * W + cursor.x, cursor.y * W + targetStart - 1); } + expand_dirty(cursor.y, cursor.y, cursor.x, W - 1); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } @@ -889,6 +917,7 @@ screen_delete_characters(unsigned int count) screen_clear_line(CLEAR_FROM_CURSOR); } + expand_dirty(cursor.y, cursor.y, cursor.x, W - 1); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } //endregion @@ -991,6 +1020,7 @@ screen_scroll_up(unsigned int lines) clear_range_noutf(y * W, (BTM + 1) * W - 1); done: + expand_dirty(TOP, BTM, 0, W - 1); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } @@ -1021,6 +1051,7 @@ screen_scroll_down(unsigned int lines) clear_range_noutf(TOP * W, TOP * W + lines * W - 1); done: + expand_dirty(TOP, BTM, 0, W - 1); NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); } @@ -1146,6 +1177,7 @@ screen_cursor_move(int dy, int dx, bool scroll) { NOTIFY_LOCK(); int move; + bool scrolled = 0; clear_invalid_hanging(); @@ -1192,7 +1224,10 @@ screen_cursor_move(int dy, int dx, bool scroll) if (was_inside) { move = -(cursor.y - TOP); cursor.y = TOP; - if (scroll) screen_scroll_down((unsigned int) move); + if (scroll) { + screen_scroll_down((unsigned int) move); + scrolled = true; + } } else { // outside the region, just validate that we're not going offscreen @@ -1207,7 +1242,10 @@ screen_cursor_move(int dy, int dx, bool scroll) if (was_inside) { move = cursor.y - BTM; cursor.y = BTM; - if (scroll) screen_scroll_up((unsigned int) move); + if (scroll) { + screen_scroll_up((unsigned int) move); + scrolled = true; + } } else { // outside the region, just validate that we're not going offscreen @@ -1218,7 +1256,11 @@ screen_cursor_move(int dy, int dx, bool scroll) } } - NOTIFY_DONE(TOPIC_CHANGE_CURSOR | (scroll*TOPIC_CHANGE_CONTENT_PART)); + if (scrolled) { + expand_dirty(TOP, BTM, 0, W-1); + } + + NOTIFY_DONE(TOPIC_CHANGE_CURSOR | (scrolled*TOPIC_CHANGE_CONTENT_PART)); } /** @@ -1587,6 +1629,7 @@ putchar_graphic(const char *ch) } Cell *c = &screen[cursor.x + cursor.y * W]; + expand_dirty(cursor.y, cursor.y, cursor.x, cursor.x); // move the rest of the line if we're in Insert Mode if (cursor.x < W-1 && scr.insert_mode) screen_insert_characters(1); @@ -1740,6 +1783,10 @@ struct ScreenSerializeState { ScreenNotifyTopics topics; ScreenNotifyTopics last_topic; ScreenNotifyTopics current_topic; + bool partial; + int x_min, x_max, y_min, y_max; + int i_max; + int i_start; }; /** @@ -1813,7 +1860,46 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, topics |= TOPIC_INTERNAL; } - ss->index = 0; + if (topics & TOPIC_CHANGE_CONTENT_PART) { + // reset dirty extents + ss->partial = true; + + ss->x_min = scr_dirty.x_min; + ss->x_max = scr_dirty.x_max; + ss->y_min = scr_dirty.y_min; + ss->y_max = scr_dirty.y_max; + + if (ss->x_min > ss->x_max || ss->y_min > ss->y_max) { + dbg("Partial redraw, but bad bounds!"); + // use full redraw + topics ^= TOPIC_CHANGE_CONTENT_PART; + topics |= TOPIC_CHANGE_CONTENT_ALL; + } else { + // is OK + ss->i_max = ss->y_max * W + ss->x_max; + ss->index = W*ss->y_min + ss->x_min; + dbg("Partial! X %d..%d, Y %d..%d, i_max %d", ss->x_min, ss->x_max, ss->y_min, ss->y_min, ss->i_max); + } + } + + if (topics & TOPIC_CHANGE_CONTENT_ALL) { + // this is a no-clean request, do not purge + // it's also always a full-screen repaint + ss->partial = false; + ss->index = 0; + ss->i_max = W*H-1; + ss->x_min = 0; + ss->x_max = W-1; + ss->y_min = 0; + ss->y_max = H-1; + dbg("Full redraw!"); + } + + ss->i_start = ss->index; + + if ((topics & (TOPIC_CHANGE_CONTENT_ALL | TOPIC_CHANGE_CONTENT_PART)) && !(topics & TOPIC_FLAG_NOCLEAN)) { + reset_screen_dirty(); + } ss->topics = topics; ss->last_topic = 0; // to be filled @@ -1823,11 +1909,22 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_c('U'); // - stands for "update" bufput_utf8(topics); + + if (ss->partial) { + // advance to the first char we want to send + } } int begun_topic = 0; int prev_topic = 0; +#define INC_I() do { \ + i++; \ + if (ss->partial) {\ + if (i%W > ss->x_max) i += (W - ss->x_max + ss->x_min - 1);\ + } \ + } while (0) + #define BEGIN_TOPIC(topic, size) \ if (ss->last_topic == prev_topic) { \ begun_topic = (topic); \ @@ -1945,20 +2042,19 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, // no screen mode - wrap it up goto ser_done; } + + // start the screen section } } // screen contents int i = ss->index; - if (i == 0) { + if (i == ss->i_start) { 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 - + bufput_utf8(ss->y_min); // Y0 + bufput_utf8(ss->x_min); // X0 + bufput_utf8(ss->y_max - ss->y_min + 1); // height + bufput_utf8(ss->x_max - ss->x_min + 1); // width ss->index = 0; ss->lastBg = 0xFF; ss->lastFg = 0xFF; @@ -1966,19 +2062,20 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, ss->lastCharLen = 0; ss->lastSymbol = 0; } - while(i < W*H && remain > 12) { + while(i <= ss->i_max && remain > 12) { cell = cell0 = &screen[i]; // Count how many times same as previous int repCnt = 0; - while (i < W*H + while (i <= ss->i_max && cell->fg == ss->lastFg && cell->bg == ss->lastBg && cell->attrs == ss->lastAttrs && cell->symbol == ss->lastSymbol) { // Repeat repCnt++; - cell = &screen[++i]; + INC_I(); + cell = &screen[i]; // it can go outside the allocated memory here if we went over the top } if (repCnt == 0) { @@ -2023,7 +2120,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, ss->lastAttrs = cell0->attrs; ss->lastSymbol = cell0->symbol; - i++; + INC_I(); } else { // last character was repeated repCnt times bufput_t_utf8(SEQ_TAG_REPEAT, repCnt); @@ -2031,7 +2128,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, } ss->index = i; - if (i >= W*H-1) goto ser_done; + if (i >= ss->i_max) goto ser_done; // MORE TO WRITE... bufput_c('\0'); // terminate the string diff --git a/user/screen.h b/user/screen.h index fc496d1..da5a307 100644 --- a/user/screen.h +++ b/user/screen.h @@ -163,6 +163,8 @@ enum ScreenSerializeTopic { TOPIC_CHANGE_CURSOR = (1<<5), TOPIC_INTERNAL = (1<<6), // debugging internal state TOPIC_BELL = (1<<7), // beep + TOPIC_FLAG_NOCLEAN = (1<<15), // do not clean dirty extents + // combos TOPIC_INITIAL = TOPIC_CHANGE_SCREEN_OPTS | From 97552b30c344e903ac99a6194d2c6116f12b26e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 30 Sep 2017 02:40:24 +0200 Subject: [PATCH 08/35] fix some bugs + optimize stuff --- esphttpdconfig.mk | 3 +- libesphttpd | 2 +- user/screen.c | 75 ++++++++++++++++++++++++++++++++++------------- user/screen.h | 5 ++-- 4 files changed, 60 insertions(+), 25 deletions(-) diff --git a/esphttpdconfig.mk b/esphttpdconfig.mk index 617b057..e9eaebf 100644 --- a/esphttpdconfig.mk +++ b/esphttpdconfig.mk @@ -58,4 +58,5 @@ GLOBAL_CFLAGS = \ -DHTTPD_MAX_POST_LEN=512 \ -DDEBUG_LOGBUF_SIZE=1024 \ -mforce-l32 \ - -DUSE_OPTIMIZE_PRINTF=1 + -DUSE_OPTIMIZE_PRINTF=1 \ + -DHTTPD_CONN_TIMEOUT=3 diff --git a/libesphttpd b/libesphttpd index 24f9a37..5772d3c 160000 --- a/libesphttpd +++ b/libesphttpd @@ -1 +1 @@ -Subproject commit 24f9a371eb5c0804dcc6657f99449ef07788140c +Subproject commit 5772d3c9a99460dad79be730453982ecd35ed461 diff --git a/user/screen.c b/user/screen.c index 9b76c28..617456a 100644 --- a/user/screen.c +++ b/user/screen.c @@ -151,10 +151,11 @@ static struct { } while(0) #define expand_dirty(y0, y1, x0, x1) do { \ - if ((y0) < scr_dirty.y_min) scr_dirty.y_min = (y0); \ - if ((x0) < scr_dirty.x_min) scr_dirty.x_min = (x0); \ - if ((y1) > scr_dirty.y_max) scr_dirty.y_max = (y1); \ - if ((x1) > scr_dirty.x_max) scr_dirty.x_max = (x1); \ + seri_dbg("Expand: X: (%d..%d) -> %d..%d, Y: (%d..%d) -> %d..%d", scr_dirty.x_min, scr_dirty.x_max, x0, x1, scr_dirty.y_min, scr_dirty.y_max, y0, y1); \ + if ((int)(y0) < scr_dirty.y_min) scr_dirty.y_min = (y0); \ + if ((int)(x0) < scr_dirty.x_min) scr_dirty.x_min = (x0); \ + if ((int)(y1) > scr_dirty.y_max) scr_dirty.y_max = (y1); \ + if ((int)(x1) > scr_dirty.x_max) scr_dirty.x_max = (x1); \ } while(0) #define NOTIFY_LOCK() do { \ @@ -172,7 +173,10 @@ static struct { /** Clear the hanging attribute if the cursor is no longer >= W */ #define clear_invalid_hanging() do { \ - if (cursor.hanging && cursor.x != W-1) cursor.hanging = false; \ + if (cursor.hanging && cursor.x != W-1) { \ + cursor.hanging = false; \ + screen_notifyChange(TOPIC_CHANGE_CURSOR); \ + } \ } while(false) #define cursor_inside_region() (cursor.y >= TOP && cursor.y <= BTM) @@ -1611,6 +1615,9 @@ putchar_graphic(const char *ch) { static char buf[4]; + NOTIFY_LOCK(); + ScreenNotifyTopics topics = TOPIC_CHANGE_CURSOR; + if (cursor.hanging) { // perform the scheduled wrap if hanging // if auto-wrap = off, it overwrites the last char @@ -1629,22 +1636,33 @@ putchar_graphic(const char *ch) } Cell *c = &screen[cursor.x + cursor.y * W]; - expand_dirty(cursor.y, cursor.y, cursor.x, cursor.x); // move the rest of the line if we're in Insert Mode if (cursor.x < W-1 && scr.insert_mode) screen_insert_characters(1); - if (ch[1] == 0 && ch[0] <= 0x7f) { + char chs = (cursor.charsetN == 0) ? cursor.charset0 : cursor.charset1; + if (chs != 'B' && ch[1] == 0 && ch[0] <= 0x7f) { // we have len=1 and ASCII, can be re-mapped using a table - utf8_remap(buf, ch[0], (cursor.charsetN == 0) ? cursor.charset0 : cursor.charset1); + utf8_remap(buf, ch[0], chs); ch = buf; } + + UnicodeCacheRef oldSymbol = c->symbol; + Color oldFg = c->fg; + Color oldBg = c->bg; + CellAttrs oldAttrs = c->attrs; + unicode_cache_remove(c->symbol); c->symbol = unicode_cache_add((const u8 *)ch); c->fg = cursor.fg; c->bg = cursor.bg; c->attrs = cursor.attrs; + if (c->symbol != oldSymbol || c->fg != oldFg || c->bg != oldBg || c->attrs != oldAttrs) { + expand_dirty(cursor.y, cursor.y, cursor.x, cursor.x); + topics |= TOPIC_CHANGE_CONTENT_PART; + } + cursor.x++; // X wrap if (cursor.x >= W) { @@ -1652,6 +1670,7 @@ putchar_graphic(const char *ch) cursor.x = W - 1; } + NOTIFY_DONE(topics); return ch; } @@ -1661,8 +1680,6 @@ putchar_graphic(const char *ch) void ICACHE_FLASH_ATTR screen_putchar(const char *ch) { - NOTIFY_LOCK(); - // clear "hanging" flag if not possible clear_invalid_hanging(); @@ -1703,8 +1720,8 @@ screen_putchar(const char *ch) // not have to call the remap function repeatedly. strncpy(scr.last_char, result, 4); - done: - NOTIFY_DONE(TOPIC_WRITE); +done: + return; } /** @@ -1714,7 +1731,6 @@ screen_putchar(const char *ch) void ICACHE_FLASH_ATTR screen_repeat_last_character(int count) { - NOTIFY_LOCK(); if (scr.last_char[0]==0) { scr.last_char[0] = ' '; scr.last_char[1] = 0; @@ -1726,7 +1742,6 @@ screen_repeat_last_character(int count) putchar_graphic(scr.last_char); count--; } - NOTIFY_DONE(TOPIC_WRITE); } /** @@ -1842,6 +1857,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, #define SEQ_TAG_ATTRS '\x04' #define SEQ_TAG_FG '\x05' #define SEQ_TAG_BG '\x06' +#define SEQ_TAG_ATTRS_0 '\x07' #define TOPICMARK_SCREEN_OPTS 'O' #define TOPICMARK_TITLE 'T' @@ -1870,15 +1886,17 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, ss->y_max = scr_dirty.y_max; if (ss->x_min > ss->x_max || ss->y_min > ss->y_max) { - dbg("Partial redraw, but bad bounds!"); + seri_warn("Partial redraw, but bad bounds! X %d..%d, Y %d..%d", ss->x_min, ss->x_max, ss->y_min, ss->y_max); // use full redraw + reset_screen_dirty(); + topics ^= TOPIC_CHANGE_CONTENT_PART; topics |= TOPIC_CHANGE_CONTENT_ALL; } else { // is OK ss->i_max = ss->y_max * W + ss->x_max; ss->index = W*ss->y_min + ss->x_min; - dbg("Partial! X %d..%d, Y %d..%d, i_max %d", ss->x_min, ss->x_max, ss->y_min, ss->y_min, ss->i_max); + seri_dbg("Partial! X %d..%d, Y %d..%d, i_max %d", ss->x_min, ss->x_max, ss->y_min, ss->y_max, ss->i_max); } } @@ -1892,7 +1910,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, ss->x_max = W-1; ss->y_min = 0; ss->y_max = H-1; - dbg("Full redraw!"); + seri_dbg("Full redraw!"); } ss->i_start = ss->index; @@ -2081,8 +2099,8 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, if (repCnt == 0) { // No repeat - first occurrence bool changeAttrs = cell0->attrs != ss->lastAttrs; - bool changeFg = cell0->fg != ss->lastFg; - bool changeBg = cell0->bg != ss->lastBg; + bool changeFg = (cell0->fg != ss->lastFg) && (cell0->attrs & ATTR_FG); + bool changeBg = (cell0->bg != ss->lastBg) && (cell0->attrs & ATTR_BG); bool changeColors = changeFg && changeBg; Color fg, bg; @@ -2101,7 +2119,11 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, } if (changeAttrs) { - bufput_t_utf8(SEQ_TAG_ATTRS, cell0->attrs); + if (cell0->attrs) { + bufput_t_utf8(SEQ_TAG_ATTRS, cell0->attrs); + } else { + bufput_c(SEQ_TAG_ATTRS_0); + } } // copy the symbol, until first 0 or reached 4 bytes @@ -2123,7 +2145,18 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, INC_I(); } else { // last character was repeated repCnt times - bufput_t_utf8(SEQ_TAG_REPEAT, repCnt); + int savings = ss->lastCharLen*repCnt; + if (savings > 2) { + // Repeat count + bufput_t_utf8(SEQ_TAG_REPEAT, repCnt); + } else { + // repeat it manually + for(int k = 0; k < repCnt; k++) { + for (int j = 0; j < ss->lastCharLen; j++) { + bufput_c(ss->lastChar[j]); + } + } + } } } diff --git a/user/screen.h b/user/screen.h index da5a307..3f6540e 100644 --- a/user/screen.h +++ b/user/screen.h @@ -172,8 +172,6 @@ enum ScreenSerializeTopic { TOPIC_CHANGE_CURSOR | TOPIC_CHANGE_TITLE | TOPIC_CHANGE_BUTTONS, - - TOPIC_WRITE = TOPIC_CHANGE_CURSOR | TOPIC_CHANGE_CONTENT_PART, }; typedef u16 ScreenNotifyTopics; @@ -355,4 +353,7 @@ void screen_report_sgr(char *buffer); */ extern void screen_notifyChange(ScreenNotifyTopics topics); +#define seri_dbg(...) +#define seri_warn(...) warn(__VA_ARGS__) + #endif // SCREEN_H From 062b0e6188d6ab4ff8fbabc0dc1c09cbfbcaecc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 30 Sep 2017 13:52:28 +0200 Subject: [PATCH 09/35] fix messed up bg color encoding --- front-end | 2 +- user/screen.c | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/front-end b/front-end index 673358e..a5a157a 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 673358e2ce64b65b28a4a94079dd83d56a51254a +Subproject commit a5a157ad561879fdb801b81d37a3490d05fd89e3 diff --git a/user/screen.c b/user/screen.c index 617456a..8a6796a 100644 --- a/user/screen.c +++ b/user/screen.c @@ -1936,13 +1936,6 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, int begun_topic = 0; int prev_topic = 0; -#define INC_I() do { \ - i++; \ - if (ss->partial) {\ - if (i%W > ss->x_max) i += (W - ss->x_max + ss->x_min - 1);\ - } \ - } while (0) - #define BEGIN_TOPIC(topic, size) \ if (ss->last_topic == prev_topic) { \ begun_topic = (topic); \ @@ -1964,11 +1957,11 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, 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_fg & 0xFFF); + bufput_utf8((u32)(termconf_live.default_fg >> 16) & 0xFFFF); - bufput_utf8(termconf_live.default_bg & 0x000FFF); - bufput_utf8((termconf_live.default_bg & 0xFFF000) >> 12); + bufput_utf8(termconf_live.default_bg & 0xFFF); + bufput_utf8((u32)(termconf_live.default_bg >> 16) & 0xFFFF); bufput_utf8( (scr.cursor_visible << 0) | @@ -2065,6 +2058,14 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, } } +#define INC_I() do { \ + i++; \ + if (ss->partial) {\ + if (i%W == 0) i += (ss->x_min);\ + else if (i%W > ss->x_max) i += (W - ss->x_max + ss->x_min - 1);\ + } \ + } while (0) + // screen contents int i = ss->index; if (i == ss->i_start) { From b6c56486902ca4d7b7bc8f2724c2e2320595f63a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 30 Sep 2017 15:02:12 +0200 Subject: [PATCH 10/35] allow zero redraw delay --- front-end | 2 +- user/apars_utf8.c | 7 +++++-- user/cgi_sockets.c | 13 +++++++------ user/cgi_term_cfg.c | 2 +- user/screen.c | 22 +++++++++++----------- user/utf8.c | 7 ++++++- user/utf8.h | 3 ++- 7 files changed, 33 insertions(+), 23 deletions(-) diff --git a/front-end b/front-end index a5a157a..cee23ca 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit a5a157ad561879fdb801b81d37a3490d05fd89e3 +Subproject commit cee23ca951bd1c07b9c1d1d1334f18c0894cc028 diff --git a/user/apars_utf8.c b/user/apars_utf8.c index 17b7387..832cde8 100644 --- a/user/apars_utf8.c +++ b/user/apars_utf8.c @@ -87,12 +87,15 @@ apars_handle_plainchar(char c) else { bytes[utf_j++] = c; if (utf_j >= utf_len) { - // check for bad sequences + // check for bad sequences - overlong or some other problem if (bytes[0] == 0xF4 && bytes[1] > 0x8F) goto fail; if (bytes[0] == 0xF0 && bytes[1] < 0x90) goto fail; if (bytes[0] == 0xED && bytes[1] > 0x9F) goto fail; if (bytes[0] == 0xE0 && bytes[1] < 0xA0) goto fail; + // trap for surrogates - those break javascript + if (bytes[0] == 0xED && bytes[1] >= 0xA0 && bytes[1] <= 0xBF) goto fail; + screen_putchar((const char *) bytes); apars_reset_utf8buffer(); } @@ -113,5 +116,5 @@ fail: apars_show_context(); apars_reset_utf8buffer(); ansi_dbg("Temporarily inhibiting parser..."); - TIMER_START(&timerResumeRx, resumeRxCb, 1000, 0); + TIMER_START(&timerResumeRx, resumeRxCb, 500, 0); } diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index 4824615..c91bb2b 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -21,6 +21,7 @@ volatile ScreenNotifyTopics pendingBroadcastTopics = 0; // flags for screen update timeouts volatile bool notify_available = true; volatile bool notify_cooldown = false; +volatile bool notify_scheduled = false; /** True if we sent XOFF to browser to stop uploading, * and we have to tell it we're ready again */ @@ -109,6 +110,7 @@ updateNotifyCb(void *arg) updateNotify_do(arg, 0); + notify_scheduled = false; notify_cooldown = true; TIMER_START(¬ifyCooldownTim, notifyCooldownTimCb, termconf->display_cooldown_ms, 0); @@ -146,17 +148,16 @@ void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyTopics topics) { if (term_active_clients == 0) return; - // this is probably not needed here - ensure timeout is not 0 - if (termconf->display_tout_ms == 0) { - termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS; - } - inp_dbg("Notify +%02Xh", topics); pendingBroadcastTopics |= topics; + int time = termconf->display_tout_ms; + if (time == 0 && notify_scheduled) return; // do not reset the timer if already scheduled + + notify_scheduled = true; // NOTE: the timer is restarted if already running - TIMER_START(&updateNotifyTim, updateNotifyCb, termconf->display_tout_ms, 0); // note - this adds latency to beep + TIMER_START(&updateNotifyTim, updateNotifyCb, time, 0); // note - this adds latency to beep } /** diff --git a/user/cgi_term_cfg.c b/user/cgi_term_cfg.c index 85f810e..2f5275a 100644 --- a/user/cgi_term_cfg.c +++ b/user/cgi_term_cfg.c @@ -148,7 +148,7 @@ cgiTermCfgSetParams(HttpdConnData *connData) if (GET_ARG("display_tout_ms")) { cgi_dbg("Display update idle timeout: %s ms", buff); n = atoi(buff); - if (n > 0) { + if (n >= 0) { termconf->display_tout_ms = n; } else { cgi_warn("Bad update timeout %s", buff); diff --git a/user/screen.c b/user/screen.c index 8a6796a..eba8b88 100644 --- a/user/screen.c +++ b/user/screen.c @@ -239,12 +239,8 @@ terminal_apply_settings_noclear(void) termconf->config_version = TERMCONF_VERSION; // Validation... - if (termconf->display_tout_ms == 0) { - termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS; - changed = 1; - } if (termconf->display_cooldown_ms == 0) { - termconf->display_cooldown_ms = SCR_DEF_DISPLAY_COOLDOWN_MS; + termconf->display_cooldown_ms = 1; changed = 1; } @@ -1781,7 +1777,7 @@ utf8_remap(char *out, char g, char charset) break; } - utf8_encode(out, utf); + utf8_encode(out, utf, false); } //endregion @@ -1841,7 +1837,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, } while(0) #define bufput_utf8(num) do { \ - nbytes = utf8_encode(bb, (num)+1); \ + nbytes = utf8_encode(bb, (num)+1, true); \ bb += nbytes; \ remain -= nbytes; \ } while(0) @@ -1957,11 +1953,15 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_utf8(W); bufput_utf8(termconf_live.theme); - bufput_utf8(termconf_live.default_fg & 0xFFF); - bufput_utf8((u32)(termconf_live.default_fg >> 16) & 0xFFFF); + bufput_utf8(termconf_live.default_fg & 0xFFFF); + bufput_utf8((termconf_live.default_fg >> 16) & 0xFFFF); + + dbg("Fg %04X,%04X", termconf_live.default_fg & 0xFFFF, (termconf_live.default_fg >> 16) & 0xFFFF); + + bufput_utf8(termconf_live.default_bg & 0xFFFF); + bufput_utf8((termconf_live.default_bg >> 16) & 0xFFFF); - bufput_utf8(termconf_live.default_bg & 0xFFF); - bufput_utf8((u32)(termconf_live.default_bg >> 16) & 0xFFFF); + dbg("Bg %04X,%04X", termconf_live.default_bg & 0xFFFF, (termconf_live.default_bg >> 16) & 0xFFFF); bufput_utf8( (scr.cursor_visible << 0) | diff --git a/user/utf8.c b/user/utf8.c index 3d4c693..b615279 100644 --- a/user/utf8.c +++ b/user/utf8.c @@ -160,8 +160,13 @@ unicode_cache_remove(UnicodeCacheRef ref) * @return number of bytes on success, 0 on failure (also produces U+FFFD, which uses 3 bytes) */ int ICACHE_FLASH_ATTR -utf8_encode(char *out, uint32_t utf) +utf8_encode(char *out, uint32_t utf, bool surrogateFix) { + // Skip the surrogate block (wtf, unicode???) + if (surrogateFix && utf >= 0xD800) { + utf += 0x800; + } + if (utf <= 0x7F) { // Plain ASCII out[0] = (char) utf; diff --git a/user/utf8.h b/user/utf8.h index c24bf07..675545a 100644 --- a/user/utf8.h +++ b/user/utf8.h @@ -63,9 +63,10 @@ bool unicode_cache_remove(UnicodeCacheRef ref); * * @param out - output buffer (min 4 characters), will be 0-terminated if shorten than 4 * @param utf - code point 0-0x10FFFF + * @param surrogateFix - add 0x800 to 0xD800-0xDFFF to avoid invalid code points * @return number of bytes on success, 0 on failure (also produces U+FFFD, which uses 3 bytes) */ -int utf8_encode(char *out, uint32_t utf); +int utf8_encode(char *out, uint32_t utf, bool surrogateFix); #if DEBUG_UTFCACHE #define utfc_warn warn From f6c2b06d8a86b4c84a98d01353af7be2d71ed78c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 30 Sep 2017 15:10:52 +0200 Subject: [PATCH 11/35] notify about mouse attrs change --- user/apars_csi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/user/apars_csi.c b/user/apars_csi.c index 072d673..ca1ff26 100644 --- a/user/apars_csi.c +++ b/user/apars_csi.c @@ -709,6 +709,8 @@ do_csi_set_private_option(CSI_Data *opts) mouse_tracking.mode, mouse_tracking.encoding, mouse_tracking.focus_tracking); + + screen_notifyChange(TOPIC_CHANGE_SCREEN_OPTS); } else if (n == 12) { screen_cursor_blink(yn); From d2fe37403fa36aad0a4ed8ce7148807cf7fea5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 30 Sep 2017 17:48:42 +0200 Subject: [PATCH 12/35] Fix htop display bugs - wrong serialization --- user/screen.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/user/screen.c b/user/screen.c index eba8b88..e5a6881 100644 --- a/user/screen.c +++ b/user/screen.c @@ -608,6 +608,11 @@ clear_range_do(unsigned int from, unsigned int to, bool clear_utf) // we discard all attributes except color-set flags sample.attrs = (CellAttrs) (cursor.attrs & (ATTR_FG | ATTR_BG)); + // if no colors, always use 0,0 + if (0 == sample.attrs) { + sample.fg = sample.bg = 0; + } + for (unsigned int i = from; i <= to; i++) { if (clear_utf) { UnicodeCacheRef symbol = screen[i].symbol; @@ -1798,6 +1803,7 @@ struct ScreenSerializeState { int x_min, x_max, y_min, y_max; int i_max; int i_start; + bool first; }; /** @@ -2075,35 +2081,40 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_utf8(ss->y_max - ss->y_min + 1); // height bufput_utf8(ss->x_max - ss->x_min + 1); // width ss->index = 0; - ss->lastBg = 0xFF; - ss->lastFg = 0xFF; - ss->lastAttrs = 0xFFFF; + ss->lastBg = 0; + ss->lastFg = 0; + ss->lastAttrs = 0; ss->lastCharLen = 0; ss->lastSymbol = 0; + ss->first = 1; } while(i <= ss->i_max && remain > 12) { cell = cell0 = &screen[i]; - // Count how many times same as previous int repCnt = 0; - while (i <= ss->i_max - && cell->fg == ss->lastFg - && cell->bg == ss->lastBg - && cell->attrs == ss->lastAttrs - && cell->symbol == ss->lastSymbol) { - // Repeat - repCnt++; - INC_I(); - cell = &screen[i]; // it can go outside the allocated memory here if we went over the top + + if (!ss->first) { + // Count how many times same as previous + while (i <= ss->i_max + && cell->fg == ss->lastFg + && cell->bg == ss->lastBg + && cell->attrs == ss->lastAttrs + && cell->symbol == ss->lastSymbol) { + // Repeat + repCnt++; + INC_I(); + cell = &screen[i]; // it can go outside the allocated memory here if we went over the top + } } if (repCnt == 0) { // No repeat - first occurrence - bool changeAttrs = cell0->attrs != ss->lastAttrs; + bool changeAttrs = ss->first || (cell0->attrs != ss->lastAttrs); bool changeFg = (cell0->fg != ss->lastFg) && (cell0->attrs & ATTR_FG); bool changeBg = (cell0->bg != ss->lastBg) && (cell0->attrs & ATTR_BG); - bool changeColors = changeFg && changeBg; + bool changeColors = ss->first || (changeFg && changeBg); Color fg, bg; + ss->first = false; // Reverse fg and bg if we're in global reverse mode fg = cell0->fg; From 42d5f6bc89eb339ab8dfcc0ac41c697a59e4fc73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 30 Sep 2017 19:11:34 +0200 Subject: [PATCH 13/35] decopt 12 configurable --- user/cgi_term_cfg.c | 9 +++++++++ user/screen.c | 10 +++++++++- user/screen.h | 4 +++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/user/cgi_term_cfg.c b/user/cgi_term_cfg.c index 2f5275a..5f62b31 100644 --- a/user/cgi_term_cfg.c +++ b/user/cgi_term_cfg.c @@ -216,6 +216,12 @@ cgiTermCfgSetParams(HttpdConnData *connData) topics |= TOPIC_CHANGE_SCREEN_OPTS; } + if (GET_ARG("allow_decopt_12")) { + cgi_dbg("DECOPT 12: %s", buff); + n = atoi(buff); + termconf->allow_decopt_12 = (bool)n; + } + if (GET_ARG("theme")) { cgi_dbg("Screen color theme: %s", buff); n = atoi(buff); @@ -444,6 +450,9 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg) else if (streq(token, "show_config_links")) { sprintf(buff, "%d", (int)termconf->show_config_links); } + else if (streq(token, "allow_decopt_12")) { + sprintf(buff, "%d", (int)termconf->allow_decopt_12); + } else if (streq(token, "loopback")) { sprintf(buff, "%d", (int)termconf->loopback); } diff --git a/user/screen.c b/user/screen.c index e5a6881..7a3a0c2 100644 --- a/user/screen.c +++ b/user/screen.c @@ -211,6 +211,7 @@ terminal_restore_defaults(void) termconf->crlf_mode = SCR_DEF_CRLF; termconf->want_all_fn = SCR_DEF_ALLFN; termconf->debugbar = SCR_DEF_DEBUGBAR; + termconf->allow_decopt_12 = SCR_DEF_DECOPT12; } /** @@ -229,12 +230,17 @@ terminal_apply_settings_noclear(void) { bool changed = false; - // Migrate to v1 + // Migrate if (termconf->config_version < 1) { persist_dbg("termconf: Updating to version %d", 1); termconf->debugbar = SCR_DEF_DEBUGBAR; changed = 1; } + if (termconf->config_version < 2) { + persist_dbg("termconf: Updating to version %d", 1); + termconf->allow_decopt_12 = SCR_DEF_DECOPT12; + changed = 1; + } termconf->config_version = TERMCONF_VERSION; @@ -1099,6 +1105,8 @@ screen_cursor_shape(enum CursorShape shape) void ICACHE_FLASH_ATTR screen_cursor_blink(bool blink) { + if (!termconf->allow_decopt_12) return; + NOTIFY_LOCK(); if (blink) { if (termconf_live.cursor_shape == CURSOR_BLOCK) termconf_live.cursor_shape = CURSOR_BLOCK_BL; diff --git a/user/screen.h b/user/screen.h index 3f6540e..d69f711 100644 --- a/user/screen.h +++ b/user/screen.h @@ -66,6 +66,7 @@ enum CursorShape { #define SCR_DEF_CRLF 0 // enter sends CRLF #define SCR_DEF_ALLFN 0 // capture F5 etc #define SCR_DEF_DEBUGBAR 0 +#define SCR_DEF_DECOPT12 0 // --- Persistent Settings --- #define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL) @@ -73,7 +74,7 @@ enum CursorShape { // 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 +#define TERMCONF_VERSION 2 typedef struct { u32 width; @@ -96,6 +97,7 @@ typedef struct { bool crlf_mode; bool want_all_fn; bool debugbar; + bool allow_decopt_12; } TerminalConfigBundle; // Live config From 5150cc32eb2ddcd5eb8d87f72cc8369df9b9e2fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 30 Sep 2017 22:57:34 +0200 Subject: [PATCH 14/35] align serializer with front-end changes --- user/screen.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/user/screen.c b/user/screen.c index 7a3a0c2..a88fd93 100644 --- a/user/screen.c +++ b/user/screen.c @@ -1799,6 +1799,8 @@ utf8_remap(char *out, char g, char charset) struct ScreenSerializeState { Color lastFg; Color lastBg; + Color lastLiveFg; + Color lastLiveBg; CellAttrs lastAttrs; UnicodeCacheRef lastSymbol; char lastChar[4]; @@ -2091,6 +2093,8 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, ss->index = 0; ss->lastBg = 0; ss->lastFg = 0; + ss->lastLiveBg = 0; + ss->lastLiveFg = 0; ss->lastAttrs = 0; ss->lastCharLen = 0; ss->lastSymbol = 0; @@ -2118,8 +2122,8 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, if (repCnt == 0) { // No repeat - first occurrence bool changeAttrs = ss->first || (cell0->attrs != ss->lastAttrs); - bool changeFg = (cell0->fg != ss->lastFg) && (cell0->attrs & ATTR_FG); - bool changeBg = (cell0->bg != ss->lastBg) && (cell0->attrs & ATTR_BG); + bool changeFg = (cell0->fg != ss->lastLiveFg) && (cell0->attrs & ATTR_FG); + bool changeBg = (cell0->bg != ss->lastLiveBg) && (cell0->attrs & ATTR_BG); bool changeColors = ss->first || (changeFg && changeBg); Color fg, bg; ss->first = false; @@ -2159,6 +2163,8 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, ss->lastFg = cell0->fg; ss->lastBg = cell0->bg; + if (cell0->attrs & ATTR_FG) ss->lastLiveFg = cell0->fg; + if (cell0->attrs & ATTR_BG) ss->lastLiveBg = cell0->bg; ss->lastAttrs = cell0->attrs; ss->lastSymbol = cell0->symbol; From a6ee808a1d37a3646fa8090ae38f9272095b76ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 30 Sep 2017 22:59:46 +0200 Subject: [PATCH 15/35] add SKIP tag to defines --- user/screen.c | 1 + 1 file changed, 1 insertion(+) diff --git a/user/screen.c b/user/screen.c index a88fd93..7fc271b 100644 --- a/user/screen.c +++ b/user/screen.c @@ -1864,6 +1864,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, } while(0) // tags for screen serialization +#define SEQ_TAG_SKIP '\x01' #define SEQ_TAG_REPEAT '\x02' #define SEQ_TAG_COLORS '\x03' #define SEQ_TAG_ATTRS '\x04' From faffa687947e634765d2fd7c64b6ce0f27bbf799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 1 Oct 2017 03:23:04 +0200 Subject: [PATCH 16/35] use binary messages and fix bugs in utf sanitizer --- front-end | 2 +- user/ansi_parser.c | 56 +++++++++-------- user/ansi_parser.rl | 6 ++ user/apars_utf8.c | 150 ++++++++++++++++++++++++++++++++++++-------- user/cgi_sockets.c | 4 +- user/cgi_term_cfg.c | 10 +++ user/screen.c | 6 ++ user/screen.h | 4 +- 8 files changed, 184 insertions(+), 54 deletions(-) diff --git a/front-end b/front-end index cee23ca..55e2def 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit cee23ca951bd1c07b9c1d1d1334f18c0894cc028 +Subproject commit 55e2def6e3899a708a9afca099252bdf44919b7c diff --git a/user/ansi_parser.c b/user/ansi_parser.c index d23cca6..37d30cf 100644 --- a/user/ansi_parser.c +++ b/user/ansi_parser.c @@ -5,10 +5,11 @@ #include "ansi_parser_callbacks.h" #include "ascii.h" #include "apars_logging.h" +#include "screen.h" /* Ragel constants block */ -/* #line 12 "user/ansi_parser.c" */ +/* #line 13 "user/ansi_parser.c" */ static const char _ansi_actions[] ESP_CONST_DATA = { 0, 1, 0, 1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, @@ -32,7 +33,7 @@ static const int ansi_en_charsetcmd_body = 10; static const int ansi_en_main = 1; -/* #line 11 "user/ansi_parser.rl" */ +/* #line 12 "user/ansi_parser.rl" */ // Max nr of CSI parameters @@ -114,15 +115,20 @@ ansi_parser(char newchar) // This is used to detect timeout delay (time since last rx char) ansi_parser_char_cnt++; + if (termconf->ascii_debug) { + apars_handle_plainchar(newchar); + return; + } + // Init Ragel on the first run if (cs == -1) { -/* #line 121 "user/ansi_parser.c" */ +/* #line 127 "user/ansi_parser.c" */ { cs = ansi_start; } -/* #line 95 "user/ansi_parser.rl" */ +/* #line 101 "user/ansi_parser.rl" */ #if DEBUG_ANSI memset(history, 0, sizeof(history)); @@ -202,7 +208,7 @@ ansi_parser(char newchar) // The parser -/* #line 206 "user/ansi_parser.c" */ +/* #line 212 "user/ansi_parser.c" */ { const char *_acts; unsigned int _nacts; @@ -392,7 +398,7 @@ execFuncs: while ( _nacts-- > 0 ) { switch ( *_acts++ ) { case 0: -/* #line 182 "user/ansi_parser.rl" */ +/* #line 188 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_show_context(); @@ -401,7 +407,7 @@ execFuncs: } break; case 1: -/* #line 191 "user/ansi_parser.rl" */ +/* #line 197 "user/ansi_parser.rl" */ { if ((*p) != 0) { apars_handle_plainchar((*p)); @@ -409,7 +415,7 @@ execFuncs: } break; case 2: -/* #line 199 "user/ansi_parser.rl" */ +/* #line 205 "user/ansi_parser.rl" */ { // Reset the CSI builder leadchar = NUL; @@ -426,13 +432,13 @@ execFuncs: } break; case 3: -/* #line 214 "user/ansi_parser.rl" */ +/* #line 220 "user/ansi_parser.rl" */ { leadchar = (*p); } break; case 4: -/* #line 218 "user/ansi_parser.rl" */ +/* #line 224 "user/ansi_parser.rl" */ { if (arg_cnt == 0) arg_cnt = 1; // x10 + digit @@ -442,7 +448,7 @@ execFuncs: } break; case 5: -/* #line 226 "user/ansi_parser.rl" */ +/* #line 232 "user/ansi_parser.rl" */ { if (arg_cnt == 0) arg_cnt = 1; // handle case when first arg is empty arg_cnt++; @@ -450,20 +456,20 @@ execFuncs: } break; case 6: -/* #line 232 "user/ansi_parser.rl" */ +/* #line 238 "user/ansi_parser.rl" */ { interchar = (*p); } break; case 7: -/* #line 236 "user/ansi_parser.rl" */ +/* #line 242 "user/ansi_parser.rl" */ { apars_handle_csi(leadchar, arg, arg_cnt, interchar, (*p)); {cs = 1;goto _again;} } break; case 8: -/* #line 248 "user/ansi_parser.rl" */ +/* #line 254 "user/ansi_parser.rl" */ { leadchar = (*p); str_ni = 0; @@ -473,13 +479,13 @@ execFuncs: } break; case 9: -/* #line 256 "user/ansi_parser.rl" */ +/* #line 262 "user/ansi_parser.rl" */ { string_buffer[str_ni++] = (*p); } break; case 10: -/* #line 260 "user/ansi_parser.rl" */ +/* #line 266 "user/ansi_parser.rl" */ { inside_string = false; string_buffer[str_ni++] = '\0'; @@ -488,41 +494,41 @@ execFuncs: } break; case 11: -/* #line 273 "user/ansi_parser.rl" */ +/* #line 279 "user/ansi_parser.rl" */ { apars_handle_hash_cmd((*p)); {cs = 1;goto _again;} } break; case 12: -/* #line 278 "user/ansi_parser.rl" */ +/* #line 284 "user/ansi_parser.rl" */ { apars_handle_short_cmd((*p)); {cs = 1;goto _again;} } break; case 13: -/* #line 283 "user/ansi_parser.rl" */ +/* #line 289 "user/ansi_parser.rl" */ { apars_handle_space_cmd((*p)); {cs = 1;goto _again;} } break; case 14: -/* #line 290 "user/ansi_parser.rl" */ +/* #line 296 "user/ansi_parser.rl" */ { leadchar = (*p); {cs = 10;goto _again;} } break; case 15: -/* #line 295 "user/ansi_parser.rl" */ +/* #line 301 "user/ansi_parser.rl" */ { apars_handle_chs_designate(leadchar, (*p)); {cs = 1;goto _again;} } break; -/* #line 526 "user/ansi_parser.c" */ +/* #line 532 "user/ansi_parser.c" */ } } goto _again; @@ -540,7 +546,7 @@ _again: while ( __nacts-- > 0 ) { switch ( *__acts++ ) { case 0: -/* #line 182 "user/ansi_parser.rl" */ +/* #line 188 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_show_context(); @@ -550,7 +556,7 @@ _again: goto _again;} } break; -/* #line 554 "user/ansi_parser.c" */ +/* #line 560 "user/ansi_parser.c" */ } } } @@ -558,6 +564,6 @@ goto _again;} _out: {} } -/* #line 318 "user/ansi_parser.rl" */ +/* #line 324 "user/ansi_parser.rl" */ } diff --git a/user/ansi_parser.rl b/user/ansi_parser.rl index 7566210..c2042c7 100644 --- a/user/ansi_parser.rl +++ b/user/ansi_parser.rl @@ -3,6 +3,7 @@ #include "ansi_parser_callbacks.h" #include "ascii.h" #include "apars_logging.h" +#include "screen.h" /* Ragel constants block */ %%{ @@ -89,6 +90,11 @@ ansi_parser(char newchar) // This is used to detect timeout delay (time since last rx char) ansi_parser_char_cnt++; + if (termconf->ascii_debug) { + apars_handle_plainchar(newchar); + return; + } + // Init Ragel on the first run if (cs == -1) { %% write init; diff --git a/user/apars_utf8.c b/user/apars_utf8.c index 832cde8..110331a 100644 --- a/user/apars_utf8.c +++ b/user/apars_utf8.c @@ -11,19 +11,12 @@ #include "uart_driver.h" #include "ansi_parser_callbacks.h" #include "ansi_parser.h" +#include "ascii.h" static u8 bytes[4]; static int utf_len = 0; static int utf_j = 0; -ETSTimer timerResumeRx; - -void ICACHE_FLASH_ATTR resumeRxCb(void *unused) -{ - ansi_dbg("Parser recover."); - ansi_parser_inhibit = false; -} - /** * Clear the buffer where we collect pieces of a code point. * This is used for parser reset. @@ -47,6 +40,105 @@ apars_reset_utf8buffer(void) // U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF // U+100000 - U+10FFFF F4 80 - *8F 80 - BF 80 - BF +static void ICACHE_FLASH_ATTR screen_print_ascii(const char *str) +{ + char gly[2]; + gly[1] = 0; + for(int j = 0;str[j]!=0;j++) { + gly[0] = str[j]; + screen_putchar(gly); + } +} + +static void ICACHE_FLASH_ATTR hdump_spaces_eol(int needed) +{ + if (needed == 0) needed = 5; + int x, y; + screen_cursor_get(&y, &x); + if (x > termconf_live.width - needed) { + screen_clear_in_line(CLEAR_FROM_CURSOR); + screen_putchar("\n"); + screen_putchar("\r"); + } +} + + +static void ICACHE_FLASH_ATTR hdump_good(const char *ch) +{ + char buf[10]; + hdump_spaces_eol(6); + + screen_set_fg(7); + screen_set_bg(0); + if(ch[0]<32) { + screen_set_fg(7); + screen_set_bg(2); + switch (ch[0]) { + case NUL: screen_print_ascii("NUL"); break; + case SOH: screen_print_ascii("SOH"); break; + case STX: screen_print_ascii("STX"); break; + case ETX: screen_print_ascii("ETX"); break; + case EOT: screen_print_ascii("EOT"); break; + case ENQ: screen_print_ascii("ENQ"); break; + case ACK: screen_print_ascii("ACK"); break; + case BEL: screen_print_ascii("BEL"); break; + case BS: screen_print_ascii("BS"); break; + case TAB: screen_print_ascii("TAB"); break; + case LF: screen_print_ascii("LF"); break; + case VT: screen_print_ascii("VT"); break; + case FF: screen_print_ascii("FF"); break; + case CR: screen_print_ascii("CR"); break; + case SO: screen_print_ascii("SO"); break; + case SI: screen_print_ascii("SI"); break; + case DLE: screen_print_ascii("DLE"); break; + case DC1: screen_print_ascii("DC1"); break; + case DC2: screen_print_ascii("DC2"); break; + case DC3: screen_print_ascii("DC3"); break; + case DC4: screen_print_ascii("DC4"); break; + case NAK: screen_print_ascii("NAK"); break; + case SYN: screen_print_ascii("SYN"); break; + case ETB: screen_print_ascii("ETB"); break; + case CAN: screen_print_ascii("CAN"); break; + case EM: screen_print_ascii("EM"); break; + case SUB: screen_print_ascii("SUB"); break; + case ESC: screen_print_ascii("ESC"); break; + case FS: screen_print_ascii("FS"); break; + case GS: screen_print_ascii("GS"); break; + case RS: screen_print_ascii("RS"); break; + case US: screen_print_ascii("US"); break; + case SP: screen_print_ascii("SP"); break; + case DEL: screen_print_ascii("DEL"); break; + default: + sprintf(buf, "%02Xh", ch[0]); + screen_print_ascii(buf); + } + } else { + screen_putchar(ch); + } + + screen_set_default_bg(); + screen_set_default_fg(); + screen_print_ascii(" "); +} + +static void ICACHE_FLASH_ATTR hdump_bad(const char *ch, int len) +{ + char buf[10]; + hdump_spaces_eol(len*5); + + screen_set_fg(7); + screen_set_bg(1); + for (int i=0;i 0xF4) { + if (uc == 0xC0 || uc == 0xC1 || uc > 0xF4) { // forbidden start codes goto fail; } - if ((c & 0xE0) == 0xC0) { + if ((uc & 0xE0) == 0xC0) { utf_len = 2; } - else if ((c & 0xF0) == 0xE0) { + else if ((uc & 0xF0) == 0xE0) { utf_len = 3; } - else if ((c & 0xF8) == 0xF0) { + else if ((uc & 0xF8) == 0xF0) { utf_len = 4; } else { // chars over 127 that don't start unicode sequences goto fail; } - - bytes[0] = c; - utf_j = 1; } else { - if ((c & 0xC0) != 0x80) { + if ((uc & 0xC0) != 0x80) { + bytes[utf_j++] = uc; goto fail; } else { - bytes[utf_j++] = c; + bytes[utf_j++] = uc; if (utf_j >= utf_len) { // check for bad sequences - overlong or some other problem if (bytes[0] == 0xF4 && bytes[1] > 0x8F) goto fail; @@ -96,25 +190,31 @@ apars_handle_plainchar(char c) // trap for surrogates - those break javascript if (bytes[0] == 0xED && bytes[1] >= 0xA0 && bytes[1] <= 0xBF) goto fail; - screen_putchar((const char *) bytes); + if (termconf_live.ascii_debug) { + hdump_good((const char *) bytes); + } else { + screen_putchar((const char *) bytes); + } apars_reset_utf8buffer(); } } } } else { - bytes[0] = c; + bytes[0] = uc; bytes[1] = 0; // just to make sure it's closed... - screen_putchar((const char *) bytes); + if (termconf_live.ascii_debug) { + hdump_good((const char *) bytes); + } else { + screen_putchar((const char *) bytes); + } + apars_reset_utf8buffer(); } return; fail: - ansi_parser_inhibit = true; - + hdump_bad((const char *) bytes, utf_j); ansi_warn("BAD UTF8!"); apars_show_context(); apars_reset_utf8buffer(); - ansi_dbg("Temporarily inhibiting parser..."); - TIMER_START(&timerResumeRx, resumeRxCb, 500, 0); } diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index c91bb2b..9d6a087 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -69,7 +69,7 @@ updateNotify_do(Websock *ws, ScreenNotifyTopics topics) } httpd_cgi_state cont = screenSerializeToBuffer(sock_buff, SOCK_BUF_LEN, topics, &data); - int flg = 0; + int flg = WEBSOCK_FLAG_BIN; if (cont == HTTPD_CGI_MORE) flg |= WEBSOCK_FLAG_MORE; if (i > 0) flg |= WEBSOCK_FLAG_CONT; if (ws) { @@ -134,7 +134,7 @@ notify_growl(char *msg) // here's some potential for a race error with the other broadcast functions // - 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), WEBSOCK_FLAG_BIN); resetHeartbeatTimer(); } diff --git a/user/cgi_term_cfg.c b/user/cgi_term_cfg.c index 5f62b31..fe68015 100644 --- a/user/cgi_term_cfg.c +++ b/user/cgi_term_cfg.c @@ -222,6 +222,13 @@ cgiTermCfgSetParams(HttpdConnData *connData) termconf->allow_decopt_12 = (bool)n; } + if (GET_ARG("ascii_debug")) { + cgi_dbg("ascii_debug: %s", buff); + n = atoi(buff); + termconf->ascii_debug = (bool)n; + shall_clear_screen = true; + } + if (GET_ARG("theme")) { cgi_dbg("Screen color theme: %s", buff); n = atoi(buff); @@ -453,6 +460,9 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg) else if (streq(token, "allow_decopt_12")) { sprintf(buff, "%d", (int)termconf->allow_decopt_12); } + else if (streq(token, "ascii_debug")) { + sprintf(buff, "%d", (int)termconf->ascii_debug); + } else if (streq(token, "loopback")) { sprintf(buff, "%d", (int)termconf->loopback); } diff --git a/user/screen.c b/user/screen.c index 7fc271b..66a3e13 100644 --- a/user/screen.c +++ b/user/screen.c @@ -212,6 +212,7 @@ terminal_restore_defaults(void) termconf->want_all_fn = SCR_DEF_ALLFN; termconf->debugbar = SCR_DEF_DEBUGBAR; termconf->allow_decopt_12 = SCR_DEF_DECOPT12; + termconf->ascii_debug = SCR_DEF_ASCIIDEBUG; } /** @@ -241,6 +242,11 @@ terminal_apply_settings_noclear(void) termconf->allow_decopt_12 = SCR_DEF_DECOPT12; changed = 1; } + if (termconf->config_version < 3) { + persist_dbg("termconf: Updating to version %d", 1); + termconf->ascii_debug = SCR_DEF_ASCIIDEBUG; + changed = 1; + } termconf->config_version = TERMCONF_VERSION; diff --git a/user/screen.h b/user/screen.h index d69f711..8e4037b 100644 --- a/user/screen.h +++ b/user/screen.h @@ -67,6 +67,7 @@ enum CursorShape { #define SCR_DEF_ALLFN 0 // capture F5 etc #define SCR_DEF_DEBUGBAR 0 #define SCR_DEF_DECOPT12 0 +#define SCR_DEF_ASCIIDEBUG 0 // --- Persistent Settings --- #define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL) @@ -74,7 +75,7 @@ enum CursorShape { // Size designed for the terminal config structure // Must be constant to avoid corrupting user config after upgrade #define TERMCONF_SIZE 300 -#define TERMCONF_VERSION 2 +#define TERMCONF_VERSION 3 typedef struct { u32 width; @@ -98,6 +99,7 @@ typedef struct { bool want_all_fn; bool debugbar; bool allow_decopt_12; + bool ascii_debug; } TerminalConfigBundle; // Live config From 9321cd4ebb27f91a024bddee6f7e6103113e9551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 1 Oct 2017 03:35:31 +0200 Subject: [PATCH 17/35] made it survive idiots cat'ing image files etc --- user/apars_utf8.c | 10 +++++++--- user/serial.c | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/user/apars_utf8.c b/user/apars_utf8.c index 110331a..a8db968 100644 --- a/user/apars_utf8.c +++ b/user/apars_utf8.c @@ -213,8 +213,12 @@ apars_handle_plainchar(char c) return; fail: - hdump_bad((const char *) bytes, utf_j); - ansi_warn("BAD UTF8!"); - apars_show_context(); + if (termconf_live.ascii_debug) { + hdump_bad((const char *) bytes, utf_j); + } else { + screen_putchar("\xEF\xBF\xBD"); + } + //ansi_warn("BAD UTF8!"); + //apars_show_context(); apars_reset_utf8buffer(); } diff --git a/user/serial.c b/user/serial.c index bcbd54c..0a960cc 100644 --- a/user/serial.c +++ b/user/serial.c @@ -92,4 +92,5 @@ void ICACHE_FLASH_ATTR serialInit(void) void ICACHE_FLASH_ATTR UART_HandleRxByte(char c) { ansi_parser(c); + system_soft_wdt_feed(); // so we survive long torrents } From 253473daed669cdeda87bcd73dca11b4045c1e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 1 Oct 2017 03:35:52 +0200 Subject: [PATCH 18/35] version bump for next rel --- user/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user/version.h b/user/version.h index 6019923..4d20ae7 100644 --- a/user/version.h +++ b/user/version.h @@ -6,7 +6,7 @@ #define ESP_VT100_FIRMWARE_VERSION_H #define FW_V_MAJOR 2 -#define FW_V_MINOR 0 +#define FW_V_MINOR 1 #define FW_V_PATCH 0 #define FIRMWARE_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) From 0eeed9c7d9551c78c044752604f364d2cc8f6a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 1 Oct 2017 03:42:15 +0200 Subject: [PATCH 19/35] add %debugbar% to terminal page --- user/cgi_main.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/user/cgi_main.c b/user/cgi_main.c index fe33ab6..7e63ad6 100644 --- a/user/cgi_main.c +++ b/user/cgi_main.c @@ -25,20 +25,12 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplScreen(HttpdConnData *connData, char *token char buff[150]; - if (streq(token, "theme")) { - sprintf(buff, "%d", termconf->theme); - tplSend(connData, buff, -1); - } - else if (streq(token, "want_all_fn")) { + if (streq(token, "want_all_fn")) { sprintf(buff, "%d", termconf->want_all_fn); tplSend(connData, buff, -1); } - else if (streq(token, "default_fg")) { - sprintf(buff, "%d", termconf->default_fg); - tplSend(connData, buff, -1); - } - else if (streq(token, "default_bg")) { - sprintf(buff, "%d", termconf->default_bg); + else if (streq(token, "debugbar")) { + sprintf(buff, "%d", termconf->debugbar); tplSend(connData, buff, -1); } @@ -64,7 +56,6 @@ tplAbout(HttpdConnData *connData, char *token, void **arg) tplSend(connData, httpdGetVersion(), -1); } else if (streq(token, "vers_sdk")) { - //tplSend(connData, STR(ESP_SDK_VERSION), -1); tplSend(connData, system_get_sdk_version(), -1); } else if (streq(token, "hash_backend")) { From 7ffc947f1aab5f4fc7cb2d19ed29f96dd9b3be1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 1 Oct 2017 12:54:52 +0200 Subject: [PATCH 20/35] added client + testing command ESC#7 --- CMakeLists.txt | 42 ++++++++++++++++++++++---- Makefile | 6 +++- esphttpdconfig.mk | 62 --------------------------------------- esphttpdconfig.mk.example | 1 + front-end | 2 +- libesphttpd | 2 +- user/apars_short.c | 6 ++++ 7 files changed, 50 insertions(+), 71 deletions(-) delete mode 100644 esphttpdconfig.mk diff --git a/CMakeLists.txt b/CMakeLists.txt index 12dea35..9214dff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,8 @@ set(SOURCE_FILES libesphttpd/util/cgiwebsocket.c libesphttpd/util/cgiflash.c libesphttpd/util/captdns.c + libesphttpd/esphttpclient/httpclient.c + libesphttpd/include/httpclient.h esp_iot_sdk_v1.5.2/include/user_interface.h esp_iot_sdk_v1.5.2/include/upgrade.h @@ -85,9 +87,9 @@ set(SOURCE_FILES esp_iot_sdk_v1.5.2/include/json/jsontree.h esp_iot_sdk_v1.5.2/include/json/jsonparse.h esp_iot_sdk_v1.5.2/include/json/json.h - include/user_config.h include/ets_sys_extra.h + include/helpers.h user/io.c user/io.h user/cgi_wifi.c @@ -121,7 +123,6 @@ set(SOURCE_FILES user/wifimgr.h user/persist.c user/persist.h - include/helpers.h user/syscfg.c user/syscfg.h user/ascii.h @@ -139,9 +140,18 @@ set(SOURCE_FILES user/apars_osc.c user/apars_osc.h user/apars_dcs.c - user/apars_dcs.h user/uart_buffer.c user/uart_buffer.h user/jstring.c user/jstring.h user/character_sets.h user/utf8.h user/utf8.c user/cgi_logging.h) + user/apars_dcs.h + user/uart_buffer.c + user/uart_buffer.h + user/jstring.c + user/jstring.h + user/character_sets.h + user/utf8.h + user/utf8.c + user/cgi_logging.h) include_directories(include) +include_directories(libesphttpd/esphttpclient) include_directories(user) include_directories(libesphttpd/include) include_directories(libesphttpd/espfs) @@ -160,13 +170,33 @@ add_definitions( -DICACHE_FLASH_ATTR= -DICACHE_RODATA_ATTR= -DFLAG_GZIP=2 - -DADMIN_PASSWORD="asdf" + -DGIT_HASH_BACKEND="asdf" -DGIT_HASH_FRONTEND="asdf" -DGIT_HASH="blabla" + + -D__TIMEZONE__="UTC" + -DESPFS_HEATSHRINK + + -DDEBUG_ANSI=1 + -DDEBUG_ANSI_NOIMPL=1 + -DDEBUG_CAPTDNS=1 + -DDEBUG_CGI=0 + -DDEBUG_ESPFS=1 -DDEBUG_HEAP=1 + -DDEBUG_HTTP=1 + -DDEBUG_HTTPC=1 + -DDEBUG_INPUT=1 -DDEBUG_MALLOC=1 - -D__TIMEZONE__="UTC" - -DESPFS_HEATSHRINK) + -DDEBUG_PERSIST=1 + -DDEBUG_ROUTER=1 + -DDEBUG_UTFCACHE=1 + -DDEBUG_WIFI=1 + -DDEBUG_WS=1 + -DDEBUG_ROUTER=1 +) +# all the debug keys should be listed here ^ so clion thinks they are used +# and doesn't mess up the formatting and inspections + add_executable(ESPTerm ${SOURCE_FILES}) diff --git a/Makefile b/Makefile index a815b7a..b79afe8 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ APPGEN ?= $(SDK_BASE)/tools/gen_appbin.py TARGET = httpd # which modules (subdirectories) of the project to include in compiling -MODULES = user +MODULES = user esphttpclient EXTRA_INCDIR = include libesphttpd/include # libraries used in this project, mainly provided by the SDK @@ -194,6 +194,10 @@ endef web: $(Q) ./build_web.sh +updweb: + $(Q) cd front-end && git pull + $(Q) ./build_web.sh + parser: $(Q) ./build_parser.sh diff --git a/esphttpdconfig.mk b/esphttpdconfig.mk deleted file mode 100644 index e9eaebf..0000000 --- a/esphttpdconfig.mk +++ /dev/null @@ -1,62 +0,0 @@ -# --------------- esphttpd config options --------------- - -# If GZIP_COMPRESSION is set to "yes" then the static css, js, and html files will be compressed with gzip before added to the espfs image -# and will be served with gzip Content-Encoding header. -# This could speed up the downloading of these files, but might break compatibility with older web browsers not supporting gzip encoding -# because Accept-Encoding is simply ignored. Enable this option if you have large static files to serve (for e.g. JQuery, Twitter bootstrap) -# By default only js, css and html files are compressed. -# If you have text based static files with different extensions what you want to serve compressed then you will need to add the extension to the following places: -# - Add the extension to this Makefile at the webpages.espfs target to the find command -# - Add the extension to the gzippedFileTypes array in the user/httpd.c file -# -# Adding JPG or PNG files (and any other compressed formats) is not recommended, because GZIP compression does not works effectively on compressed files. - -#Static gzipping is disabled by default. -GZIP_COMPRESSION = yes - -# If COMPRESS_W_YUI is set to "yes" then the static css and js files will be compressed with yui-compressor -# This option works only when GZIP_COMPRESSION is set to "yes" -# http://yui.github.io/yuicompressor/ -#Disabled by default. -COMPRESS_W_YUI = no - -YUI-COMPRESSOR = /usr/bin/yui-compressor - -#If USE_HEATSHRINK is set to "yes" then the espfs files will be compressed with Heatshrink and decompressed -#on the fly while reading the file. Because the decompression is done in the esp8266, it does not require -#any support in the browser. -USE_HEATSHRINK = yes - -USE_OPENSDK = yes - -# this ugly trick is needed to allow a relative path -SDK_BASE=$(dir $(lastword $(MAKEFILE_LIST)))/esp_iot_sdk_v1.5.2/ - -# combined / separate / ota -OUTPUT_TYPE = combined - -# SPI flash size, in K -ESP_SPI_FLASH_SIZE_K = 1024 - -GLOBAL_CFLAGS = \ - -DDEBUG_ROUTER=0 \ - -DDEBUG_CAPTDNS=0 \ - -DDEBUG_HTTP=0 \ - -DDEBUG_ESPFS=0 \ - -DDEBUG_PERSIST=1 \ - -DDEBUG_UTFCACHE=0 \ - -DDEBUG_CGI=0 \ - -DDEBUG_WIFI=0 \ - -DDEBUG_WS=0 \ - -DDEBUG_ANSI=1 \ - -DDEBUG_ANSI_NOIMPL=1 \ - -DDEBUG_INPUT=0 \ - -DDEBUG_HEAP=1 \ - -DDEBUG_MALLOC=0 \ - -DHTTPD_MAX_BACKLOG_SIZE=8192 \ - -DHTTPD_MAX_HEAD_LEN=1024 \ - -DHTTPD_MAX_POST_LEN=512 \ - -DDEBUG_LOGBUF_SIZE=1024 \ - -mforce-l32 \ - -DUSE_OPTIMIZE_PRINTF=1 \ - -DHTTPD_CONN_TIMEOUT=3 diff --git a/esphttpdconfig.mk.example b/esphttpdconfig.mk.example index 617b057..714dce8 100644 --- a/esphttpdconfig.mk.example +++ b/esphttpdconfig.mk.example @@ -42,6 +42,7 @@ GLOBAL_CFLAGS = \ -DDEBUG_ROUTER=0 \ -DDEBUG_CAPTDNS=0 \ -DDEBUG_HTTP=0 \ + -DDEBUG_HTTPC=0 \ -DDEBUG_ESPFS=0 \ -DDEBUG_PERSIST=1 \ -DDEBUG_UTFCACHE=0 \ diff --git a/front-end b/front-end index 55e2def..29b8134 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 55e2def6e3899a708a9afca099252bdf44919b7c +Subproject commit 29b813457c7a185c41e050d358b001fadd2498e4 diff --git a/libesphttpd b/libesphttpd index 5772d3c..c8dda50 160000 --- a/libesphttpd +++ b/libesphttpd @@ -1 +1 @@ -Subproject commit 5772d3c9a99460dad79be730453982ecd35ed461 +Subproject commit c8dda50970d4c7c7ed2b9db4df7d433a39dde5b8 diff --git a/user/apars_short.c b/user/apars_short.c index 64ae3ab..06835b3 100644 --- a/user/apars_short.c +++ b/user/apars_short.c @@ -42,6 +42,7 @@ // #include +#include #include "apars_short.h" #include "apars_logging.h" #include "screen.h" @@ -103,6 +104,11 @@ void ICACHE_FLASH_ATTR apars_handle_hash_cmd(char c) screen_fill_with_E(); break; + // development codes - do not use! + case '7': + http_get("http://example.com", NULL); + break; + default: ansi_noimpl("ESC # %c", c); } From 55afcca955a77c67d085803dfffeb5f248ab6a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 1 Oct 2017 13:47:22 +0200 Subject: [PATCH 21/35] fix incorrect DCS command handling --- Makefile | 2 +- libesphttpd | 2 +- user/apars_dcs.c | 6 ++++-- user/apars_short.c | 2 +- user/screen.c | 12 ++++++++---- user/screen.h | 2 ++ 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index b79afe8..6470145 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ APPGEN ?= $(SDK_BASE)/tools/gen_appbin.py TARGET = httpd # which modules (subdirectories) of the project to include in compiling -MODULES = user esphttpclient +MODULES = user EXTRA_INCDIR = include libesphttpd/include # libraries used in this project, mainly provided by the SDK diff --git a/libesphttpd b/libesphttpd index c8dda50..7fce947 160000 --- a/libesphttpd +++ b/libesphttpd @@ -1 +1 @@ -Subproject commit c8dda50970d4c7c7ed2b9db4df7d433a39dde5b8 +Subproject commit 7fce9474395e208c83325ff6150fdd21ba16c9a4 diff --git a/user/apars_dcs.c b/user/apars_dcs.c index 5238582..7462d26 100644 --- a/user/apars_dcs.c +++ b/user/apars_dcs.c @@ -47,7 +47,9 @@ apars_handle_dcs(const char *buffer) } else if (buffer[2] == 'r') { // DECSTBM - Query scrolling region - sprintf(buf, "\033P1$r%d;%dr\033\\", 1, termconf_live.height); // 1-80 TODO real extent of scrolling region + int v0, v1; + screen_region_get(&v0, &v1); + sprintf(buf, "\033P1$r%d;%dr\033\\", v0+1, v1+1); apars_respond(buf); } else if (buffer[2] == 's') { @@ -64,7 +66,7 @@ apars_handle_dcs(const char *buffer) } else if (strneq(buffer+2, " q", 2)) { // DECSCUSR - Query cursor style - sprintf(buf, "\033P1$r%d q\033\\", 1); + sprintf(buf, "\033P1$r%d q\033\\", termconf_live.cursor_shape); /* Ps = 0 -> blinking block. Ps = 1 -> blinking block (default). diff --git a/user/apars_short.c b/user/apars_short.c index 06835b3..e00b2e2 100644 --- a/user/apars_short.c +++ b/user/apars_short.c @@ -106,7 +106,7 @@ void ICACHE_FLASH_ATTR apars_handle_hash_cmd(char c) // development codes - do not use! case '7': - http_get("http://example.com", NULL); + http_get("http://wtfismyip.com/text", NULL, http_callback_example); break; default: diff --git a/user/screen.c b/user/screen.c index 66a3e13..755aa44 100644 --- a/user/screen.c +++ b/user/screen.c @@ -1152,6 +1152,14 @@ screen_cursor_get(int *y, int *x) } } +/* Report scrolling region */ +void ICACHE_FLASH_ATTR +screen_region_get(int *pv0, int *pv1) +{ + *pv0 = TOP; + *pv1 = BTM; +} + /** * Set cursor X position */ @@ -1979,13 +1987,9 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_utf8(termconf_live.default_fg & 0xFFFF); bufput_utf8((termconf_live.default_fg >> 16) & 0xFFFF); - dbg("Fg %04X,%04X", termconf_live.default_fg & 0xFFFF, (termconf_live.default_fg >> 16) & 0xFFFF); - bufput_utf8(termconf_live.default_bg & 0xFFFF); bufput_utf8((termconf_live.default_bg >> 16) & 0xFFFF); - dbg("Bg %04X,%04X", termconf_live.default_bg & 0xFFFF, (termconf_live.default_bg >> 16) & 0xFFFF); - bufput_utf8( (scr.cursor_visible << 0) | (termconf_live.debugbar << 1) | // debugbar - this was previously "hanging" diff --git a/user/screen.h b/user/screen.h index 8e4037b..e38f776 100644 --- a/user/screen.h +++ b/user/screen.h @@ -249,6 +249,8 @@ void screen_wrap_enable(bool enable); void screen_reverse_wrap_enable(bool enable); /** Set scrolling region */ void screen_set_scrolling_region(int from, int to); +/* Report scrolling region */ +void screen_region_get(int *pv0, int *pv1); /** Enable or disable origin remap to top left of scrolling region */ void screen_set_origin_mode(bool region_origin); From 6665b5c183973a3f7c87bc8b0cf08c7a7511a392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 1 Oct 2017 14:09:39 +0200 Subject: [PATCH 22/35] CAN and SUB correctly abort current sequence, including string. Start of adding PM's --- user/ansi_parser.c | 55 ++++++++++++++++++------------------ user/ansi_parser.rl | 13 +++++---- user/ansi_parser_callbacks.c | 15 ++++++++++ user/apars_pm.c | 34 ++++++++++++++++++++++ user/apars_pm.h | 10 +++++++ user/apars_string.c | 2 ++ 6 files changed, 96 insertions(+), 33 deletions(-) create mode 100644 user/apars_pm.c create mode 100644 user/apars_pm.h diff --git a/user/ansi_parser.c b/user/ansi_parser.c index 37d30cf..49450d9 100644 --- a/user/ansi_parser.c +++ b/user/ansi_parser.c @@ -142,6 +142,13 @@ ansi_parser(char newchar) history[HISTORY_LEN-1] = newchar; #endif + // THose should work always, even inside a string + if (newchar == CAN || newchar == SUB) { + // Cancel the active sequence + cs = ansi_start; + return; + } + // Handle simple characters immediately (bypass parser) if (newchar < ' ' && !inside_string) { switch (newchar) { @@ -183,12 +190,6 @@ ansi_parser(char newchar) apars_handle_enq(); return; - // Cancel the active sequence - case CAN: - case SUB: - cs = ansi_start; - return; - default: // Discard all other control codes return; @@ -208,7 +209,7 @@ ansi_parser(char newchar) // The parser -/* #line 212 "user/ansi_parser.c" */ +/* #line 213 "user/ansi_parser.c" */ { const char *_acts; unsigned int _nacts; @@ -398,7 +399,7 @@ execFuncs: while ( _nacts-- > 0 ) { switch ( *_acts++ ) { case 0: -/* #line 188 "user/ansi_parser.rl" */ +/* #line 189 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_show_context(); @@ -407,7 +408,7 @@ execFuncs: } break; case 1: -/* #line 197 "user/ansi_parser.rl" */ +/* #line 198 "user/ansi_parser.rl" */ { if ((*p) != 0) { apars_handle_plainchar((*p)); @@ -415,7 +416,7 @@ execFuncs: } break; case 2: -/* #line 205 "user/ansi_parser.rl" */ +/* #line 206 "user/ansi_parser.rl" */ { // Reset the CSI builder leadchar = NUL; @@ -432,13 +433,13 @@ execFuncs: } break; case 3: -/* #line 220 "user/ansi_parser.rl" */ +/* #line 221 "user/ansi_parser.rl" */ { leadchar = (*p); } break; case 4: -/* #line 224 "user/ansi_parser.rl" */ +/* #line 225 "user/ansi_parser.rl" */ { if (arg_cnt == 0) arg_cnt = 1; // x10 + digit @@ -448,7 +449,7 @@ execFuncs: } break; case 5: -/* #line 232 "user/ansi_parser.rl" */ +/* #line 233 "user/ansi_parser.rl" */ { if (arg_cnt == 0) arg_cnt = 1; // handle case when first arg is empty arg_cnt++; @@ -456,20 +457,20 @@ execFuncs: } break; case 6: -/* #line 238 "user/ansi_parser.rl" */ +/* #line 239 "user/ansi_parser.rl" */ { interchar = (*p); } break; case 7: -/* #line 242 "user/ansi_parser.rl" */ +/* #line 243 "user/ansi_parser.rl" */ { apars_handle_csi(leadchar, arg, arg_cnt, interchar, (*p)); {cs = 1;goto _again;} } break; case 8: -/* #line 254 "user/ansi_parser.rl" */ +/* #line 255 "user/ansi_parser.rl" */ { leadchar = (*p); str_ni = 0; @@ -479,13 +480,13 @@ execFuncs: } break; case 9: -/* #line 262 "user/ansi_parser.rl" */ +/* #line 263 "user/ansi_parser.rl" */ { string_buffer[str_ni++] = (*p); } break; case 10: -/* #line 266 "user/ansi_parser.rl" */ +/* #line 267 "user/ansi_parser.rl" */ { inside_string = false; string_buffer[str_ni++] = '\0'; @@ -494,41 +495,41 @@ execFuncs: } break; case 11: -/* #line 279 "user/ansi_parser.rl" */ +/* #line 280 "user/ansi_parser.rl" */ { apars_handle_hash_cmd((*p)); {cs = 1;goto _again;} } break; case 12: -/* #line 284 "user/ansi_parser.rl" */ +/* #line 285 "user/ansi_parser.rl" */ { apars_handle_short_cmd((*p)); {cs = 1;goto _again;} } break; case 13: -/* #line 289 "user/ansi_parser.rl" */ +/* #line 290 "user/ansi_parser.rl" */ { apars_handle_space_cmd((*p)); {cs = 1;goto _again;} } break; case 14: -/* #line 296 "user/ansi_parser.rl" */ +/* #line 297 "user/ansi_parser.rl" */ { leadchar = (*p); {cs = 10;goto _again;} } break; case 15: -/* #line 301 "user/ansi_parser.rl" */ +/* #line 302 "user/ansi_parser.rl" */ { apars_handle_chs_designate(leadchar, (*p)); {cs = 1;goto _again;} } break; -/* #line 532 "user/ansi_parser.c" */ +/* #line 533 "user/ansi_parser.c" */ } } goto _again; @@ -546,7 +547,7 @@ _again: while ( __nacts-- > 0 ) { switch ( *__acts++ ) { case 0: -/* #line 188 "user/ansi_parser.rl" */ +/* #line 189 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_show_context(); @@ -556,7 +557,7 @@ _again: goto _again;} } break; -/* #line 560 "user/ansi_parser.c" */ +/* #line 561 "user/ansi_parser.c" */ } } } @@ -564,6 +565,6 @@ goto _again;} _out: {} } -/* #line 324 "user/ansi_parser.rl" */ +/* #line 325 "user/ansi_parser.rl" */ } diff --git a/user/ansi_parser.rl b/user/ansi_parser.rl index c2042c7..cd1ec26 100644 --- a/user/ansi_parser.rl +++ b/user/ansi_parser.rl @@ -111,6 +111,13 @@ ansi_parser(char newchar) history[HISTORY_LEN-1] = newchar; #endif + // THose should work always, even inside a string + if (newchar == CAN || newchar == SUB) { + // Cancel the active sequence + cs = ansi_start; + return; + } + // Handle simple characters immediately (bypass parser) if (newchar < ' ' && !inside_string) { switch (newchar) { @@ -152,12 +159,6 @@ ansi_parser(char newchar) apars_handle_enq(); return; - // Cancel the active sequence - case CAN: - case SUB: - cs = ansi_start; - return; - default: // Discard all other control codes return; diff --git a/user/ansi_parser_callbacks.c b/user/ansi_parser_callbacks.c index fa6173f..37c9914 100644 --- a/user/ansi_parser_callbacks.c +++ b/user/ansi_parser_callbacks.c @@ -12,6 +12,13 @@ #include "uart_buffer.h" #include "screen.h" +volatile bool enquiry_suppressed = false; +ETSTimer enqTimer; +void ICACHE_FLASH_ATTR enqTimerCb(void *unused) +{ + enquiry_suppressed = false; +} + /** * Send a response to UART0 * @param str @@ -37,8 +44,16 @@ apars_handle_bel(void) void ICACHE_FLASH_ATTR apars_handle_enq(void) { + if (enquiry_suppressed) return; + // version encased in SOS and ST apars_respond("\x1bXESPTerm " FIRMWARE_VERSION "\x1b\\"); + + // Throttle enquiry - this is a single-character-invoked response, + // so it tends to happen randomly when throwing garbage at the ESP. + // We don't want to fill the output buffer with dozens of enquiry responses + enquiry_suppressed = true; + TIMER_START(&enqTimer, enqTimerCb, 500, 0); } void ICACHE_FLASH_ATTR diff --git a/user/apars_pm.c b/user/apars_pm.c new file mode 100644 index 0000000..6defb03 --- /dev/null +++ b/user/apars_pm.c @@ -0,0 +1,34 @@ +// +// Created by MightyPork on 2017/08/20. +// +// Handle privacy messages +// PM Pt ST +// (PM = ESC ^) +// +// Those are used for device-to-device communication. +// They were not used for anything in the original VT100 and are not +// used by Xterm or any other common emulator, but they should be safely discarded. +// + +#include +#include "apars_pm.h" +#include "ansi_parser_callbacks.h" +#include "screen.h" +#include "apars_logging.h" + +/** + * Helper function to parse incoming DCS (Device Control String) + * @param buffer - the DCS body (after DCS and before ST) + */ +void ICACHE_FLASH_ATTR +apars_handle_pm(const char *buffer) +{ + size_t len = strlen(buffer); + if (false) { + // + } + else { + ansi_warn("Bad DCS: %s", buffer); + apars_show_context(); + } +} diff --git a/user/apars_pm.h b/user/apars_pm.h new file mode 100644 index 0000000..92f7b44 --- /dev/null +++ b/user/apars_pm.h @@ -0,0 +1,10 @@ +// +// Created by MightyPork on 2017/08/20. +// + +#ifndef ESP_VT100_FIRMWARE_APARS_PM_H +#define ESP_VT100_FIRMWARE_APARS_PM_H + +void apars_handle_pm(const char *buffer); + +#endif //ESP_VT100_FIRMWARE_APARS_PM_H diff --git a/user/apars_string.c b/user/apars_string.c index dbd2382..a4a8781 100644 --- a/user/apars_string.c +++ b/user/apars_string.c @@ -17,6 +17,7 @@ #include "apars_logging.h" #include "ansi_parser_callbacks.h" #include "screen.h" +#include "apars_pm.h" // ----- Generic String cmd - disambiguation ----- @@ -37,6 +38,7 @@ apars_handle_string_cmd(char leadchar, char *buffer) break; case '^': // PM - Privacy Message + apars_handle_pm(buffer); break; case '_': // APC - Application Program Command From dca02976a7a96e72f6c777fff61f42ea3306fe7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 1 Oct 2017 19:46:14 +0200 Subject: [PATCH 23/35] Fixed a horrible MegaBug and added HTTP message sending + D2D sending (no Rx yet) --- libesphttpd | 2 +- user/ansi_parser.c | 50 +++++----- user/ansi_parser.h | 3 + user/ansi_parser.rl | 4 - user/apars_dcs.c | 2 +- user/apars_dcs.h | 2 +- user/apars_pm.c | 20 ++-- user/apars_pm.h | 2 +- user/apars_short.c | 1 + user/apars_string.c | 2 +- user/cgi_d2d.c | 220 ++++++++++++++++++++++++++++++++++++++++++++ user/cgi_d2d.h | 14 +++ user/cgi_sockets.c | 4 +- user/serial.c | 1 - user/uart_buffer.c | 70 +++++++------- user/uart_buffer.h | 2 +- user/uart_handler.c | 6 +- user/version.h | 2 + 18 files changed, 320 insertions(+), 87 deletions(-) create mode 100644 user/cgi_d2d.c create mode 100644 user/cgi_d2d.h diff --git a/libesphttpd b/libesphttpd index 7fce947..a2fe09b 160000 --- a/libesphttpd +++ b/libesphttpd @@ -1 +1 @@ -Subproject commit 7fce9474395e208c83325ff6150fdd21ba16c9a4 +Subproject commit a2fe09bb4d610d08806afcc4cc008fc01c0dcee0 diff --git a/user/ansi_parser.c b/user/ansi_parser.c index 49450d9..a6bbe79 100644 --- a/user/ansi_parser.c +++ b/user/ansi_parser.c @@ -36,10 +36,6 @@ static const int ansi_en_main = 1; /* #line 12 "user/ansi_parser.rl" */ -// Max nr of CSI parameters -#define CSI_N_MAX 10 -#define ANSI_STR_LEN 64 - static volatile int cs = -1; static volatile bool inside_string = false; @@ -123,12 +119,12 @@ ansi_parser(char newchar) // Init Ragel on the first run if (cs == -1) { -/* #line 127 "user/ansi_parser.c" */ +/* #line 123 "user/ansi_parser.c" */ { cs = ansi_start; } -/* #line 101 "user/ansi_parser.rl" */ +/* #line 97 "user/ansi_parser.rl" */ #if DEBUG_ANSI memset(history, 0, sizeof(history)); @@ -209,7 +205,7 @@ ansi_parser(char newchar) // The parser -/* #line 213 "user/ansi_parser.c" */ +/* #line 209 "user/ansi_parser.c" */ { const char *_acts; unsigned int _nacts; @@ -399,7 +395,7 @@ execFuncs: while ( _nacts-- > 0 ) { switch ( *_acts++ ) { case 0: -/* #line 189 "user/ansi_parser.rl" */ +/* #line 185 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_show_context(); @@ -408,7 +404,7 @@ execFuncs: } break; case 1: -/* #line 198 "user/ansi_parser.rl" */ +/* #line 194 "user/ansi_parser.rl" */ { if ((*p) != 0) { apars_handle_plainchar((*p)); @@ -416,7 +412,7 @@ execFuncs: } break; case 2: -/* #line 206 "user/ansi_parser.rl" */ +/* #line 202 "user/ansi_parser.rl" */ { // Reset the CSI builder leadchar = NUL; @@ -433,13 +429,13 @@ execFuncs: } break; case 3: -/* #line 221 "user/ansi_parser.rl" */ +/* #line 217 "user/ansi_parser.rl" */ { leadchar = (*p); } break; case 4: -/* #line 225 "user/ansi_parser.rl" */ +/* #line 221 "user/ansi_parser.rl" */ { if (arg_cnt == 0) arg_cnt = 1; // x10 + digit @@ -449,7 +445,7 @@ execFuncs: } break; case 5: -/* #line 233 "user/ansi_parser.rl" */ +/* #line 229 "user/ansi_parser.rl" */ { if (arg_cnt == 0) arg_cnt = 1; // handle case when first arg is empty arg_cnt++; @@ -457,20 +453,20 @@ execFuncs: } break; case 6: -/* #line 239 "user/ansi_parser.rl" */ +/* #line 235 "user/ansi_parser.rl" */ { interchar = (*p); } break; case 7: -/* #line 243 "user/ansi_parser.rl" */ +/* #line 239 "user/ansi_parser.rl" */ { apars_handle_csi(leadchar, arg, arg_cnt, interchar, (*p)); {cs = 1;goto _again;} } break; case 8: -/* #line 255 "user/ansi_parser.rl" */ +/* #line 251 "user/ansi_parser.rl" */ { leadchar = (*p); str_ni = 0; @@ -480,13 +476,13 @@ execFuncs: } break; case 9: -/* #line 263 "user/ansi_parser.rl" */ +/* #line 259 "user/ansi_parser.rl" */ { string_buffer[str_ni++] = (*p); } break; case 10: -/* #line 267 "user/ansi_parser.rl" */ +/* #line 263 "user/ansi_parser.rl" */ { inside_string = false; string_buffer[str_ni++] = '\0'; @@ -495,41 +491,41 @@ execFuncs: } break; case 11: -/* #line 280 "user/ansi_parser.rl" */ +/* #line 276 "user/ansi_parser.rl" */ { apars_handle_hash_cmd((*p)); {cs = 1;goto _again;} } break; case 12: -/* #line 285 "user/ansi_parser.rl" */ +/* #line 281 "user/ansi_parser.rl" */ { apars_handle_short_cmd((*p)); {cs = 1;goto _again;} } break; case 13: -/* #line 290 "user/ansi_parser.rl" */ +/* #line 286 "user/ansi_parser.rl" */ { apars_handle_space_cmd((*p)); {cs = 1;goto _again;} } break; case 14: -/* #line 297 "user/ansi_parser.rl" */ +/* #line 293 "user/ansi_parser.rl" */ { leadchar = (*p); {cs = 10;goto _again;} } break; case 15: -/* #line 302 "user/ansi_parser.rl" */ +/* #line 298 "user/ansi_parser.rl" */ { apars_handle_chs_designate(leadchar, (*p)); {cs = 1;goto _again;} } break; -/* #line 533 "user/ansi_parser.c" */ +/* #line 529 "user/ansi_parser.c" */ } } goto _again; @@ -547,7 +543,7 @@ _again: while ( __nacts-- > 0 ) { switch ( *__acts++ ) { case 0: -/* #line 189 "user/ansi_parser.rl" */ +/* #line 185 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_show_context(); @@ -557,7 +553,7 @@ _again: goto _again;} } break; -/* #line 561 "user/ansi_parser.c" */ +/* #line 557 "user/ansi_parser.c" */ } } } @@ -565,6 +561,6 @@ goto _again;} _out: {} } -/* #line 325 "user/ansi_parser.rl" */ +/* #line 321 "user/ansi_parser.rl" */ } diff --git a/user/ansi_parser.h b/user/ansi_parser.h index 212aaa4..1aa9528 100644 --- a/user/ansi_parser.h +++ b/user/ansi_parser.h @@ -3,6 +3,9 @@ #include +#define CSI_N_MAX 12 +#define ANSI_STR_LEN 256 + extern volatile bool ansi_parser_inhibit; // discard all characters void ansi_parser_reset(void); diff --git a/user/ansi_parser.rl b/user/ansi_parser.rl index cd1ec26..67b3ef0 100644 --- a/user/ansi_parser.rl +++ b/user/ansi_parser.rl @@ -11,10 +11,6 @@ write data; }%% -// Max nr of CSI parameters -#define CSI_N_MAX 10 -#define ANSI_STR_LEN 64 - static volatile int cs = -1; static volatile bool inside_string = false; diff --git a/user/apars_dcs.c b/user/apars_dcs.c index 7462d26..0bf5129 100644 --- a/user/apars_dcs.c +++ b/user/apars_dcs.c @@ -30,7 +30,7 @@ * @param buffer - the DCS body (after DCS and before ST) */ void ICACHE_FLASH_ATTR -apars_handle_dcs(const char *buffer) +apars_handle_dcs(char *buffer) { char buf[64]; // just about big enough for full-house SGR size_t len = strlen(buffer); diff --git a/user/apars_dcs.h b/user/apars_dcs.h index f9d4ad7..012c6fc 100644 --- a/user/apars_dcs.h +++ b/user/apars_dcs.h @@ -5,6 +5,6 @@ #ifndef ESP_VT100_FIRMWARE_APARS_DCS_H #define ESP_VT100_FIRMWARE_APARS_DCS_H -void apars_handle_dcs(const char *buffer); +void apars_handle_dcs(char *buffer); #endif //ESP_VT100_FIRMWARE_APARS_DCS_H diff --git a/user/apars_pm.c b/user/apars_pm.c index 6defb03..c53ef13 100644 --- a/user/apars_pm.c +++ b/user/apars_pm.c @@ -11,24 +11,24 @@ // #include +#include #include "apars_pm.h" +#include "version.h" #include "ansi_parser_callbacks.h" #include "screen.h" #include "apars_logging.h" +#include "cgi_d2d.h" /** * Helper function to parse incoming DCS (Device Control String) - * @param buffer - the DCS body (after DCS and before ST) + * @param msg - the DCS body (after DCS and before ST) */ void ICACHE_FLASH_ATTR -apars_handle_pm(const char *buffer) +apars_handle_pm(char *msg) { - size_t len = strlen(buffer); - if (false) { - // - } - else { - ansi_warn("Bad DCS: %s", buffer); - apars_show_context(); - } + if (d2d_parse_command(msg)) return; + + return; +fail: + ansi_warn("D2D message error: %s", msg); } diff --git a/user/apars_pm.h b/user/apars_pm.h index 92f7b44..f1b241b 100644 --- a/user/apars_pm.h +++ b/user/apars_pm.h @@ -5,6 +5,6 @@ #ifndef ESP_VT100_FIRMWARE_APARS_PM_H #define ESP_VT100_FIRMWARE_APARS_PM_H -void apars_handle_pm(const char *buffer); +void apars_handle_pm(char *msg); #endif //ESP_VT100_FIRMWARE_APARS_PM_H diff --git a/user/apars_short.c b/user/apars_short.c index e00b2e2..7fb35c5 100644 --- a/user/apars_short.c +++ b/user/apars_short.c @@ -46,6 +46,7 @@ #include "apars_short.h" #include "apars_logging.h" #include "screen.h" +#include "ansi_parser_callbacks.h" // ----- Character Set --- diff --git a/user/apars_string.c b/user/apars_string.c index a4a8781..16bc94f 100644 --- a/user/apars_string.c +++ b/user/apars_string.c @@ -25,7 +25,7 @@ void ICACHE_FLASH_ATTR apars_handle_string_cmd(char leadchar, char *buffer) { switch (leadchar) { - case 'k': // ESC k TITLE ST (defined in GNU screen manpage) + case 'k': // ESC k TITLE ST (defined in GNU screen manpage, probably not standard) screen_set_title(buffer); break; diff --git a/user/cgi_d2d.c b/user/cgi_d2d.c new file mode 100644 index 0000000..cf18c6d --- /dev/null +++ b/user/cgi_d2d.c @@ -0,0 +1,220 @@ +// +// Created by MightyPork on 2017/10/01. +// + +#include +#include "cgi_d2d.h" +#include "apars_logging.h" +#include "version.h" +#include "ansi_parser_callbacks.h" +#include +#include + +#define D2D_TIMEOUT_MS 2000 + +#define D2D_HEADERS \ + "User-Agent: ESPTerm/"FIRMWARE_VERSION"\r\n" \ + "Content-Type: text/plain; charset=utf-8\r\n" \ + "Cache-Control: max-age=0\r\n" + +struct d2d_request_opts { + bool want_body; + bool want_head; + char *nonce; +}; + +static void ICACHE_FLASH_ATTR +requestCb(int http_status, + const char *response_headers, + const char *response_body, + size_t body_size, + void *userArg) +{ + if (userArg == NULL) return; + struct d2d_request_opts *opts = userArg; + + char buff100[100]; + int len = 0; + if (opts->want_head) len += strlen(response_headers); + if (opts->want_body) len += body_size + (opts->want_head*2); + char *bb = buff100; + bb += sprintf(bb, "\x1b^h;%d;", http_status); + const char *comma = ""; + + if (opts->want_head) { + bb += sprintf(bb, "%sH", comma); + comma = ","; + } + + if (opts->want_body) { + bb += sprintf(bb, "%sB", comma); + comma = ","; + } + + if (opts->nonce) { + bb += sprintf(bb, "%sN=%s", comma, opts->nonce); + comma = ","; + } + + if (opts->want_head || opts->want_body) { + bb += sprintf(bb, "%sL=%d", comma, len); + //comma = ","; + } + + // semicolon only if more data is to be sent + if (opts->want_head || opts->want_body) + sprintf(bb, ";"); + + apars_respond(buff100); + + dbg("Response %d, nonce %s", http_status, opts->nonce); + dbg("Headers %s", response_headers); + dbg("Body %s", response_body); + + // head and payload separated by \r\n\r\n (one \r\n is at the end of head - maybe) + if (opts->want_head) { + apars_respond(response_headers); + if(opts->want_body) apars_respond("\r\n"); + } + + if(opts->want_body) { + apars_respond(response_body); + } + + apars_respond("\a"); + + free(opts->nonce); + free(userArg); +} + +bool ICACHE_FLASH_ATTR +d2d_parse_command(char *msg) +{ + char buff40[40]; + char *p; + +#define FIND_NEXT(target, delim) do { \ + p = strchr(msg, (delim)); \ + if (p == NULL) return false; \ + *p = '\0'; \ + (target) = msg; \ + msg = p + 1; \ +} while(0) \ + + if (strstarts(msg, "M;")) { + // Send a esp-esp message + msg += 2; + const char *ip; + FIND_NEXT(ip, ';'); + const char *payload = msg; + + ansi_dbg("D2D Tx,dest=%s,msg=%s", ip, payload); + sprintf(buff40, "http://%s" D2D_MSG_ENDPOINT, ip); + + httpclient_args args; + httpclient_args_init(&args); + args.method = HTTPD_METHOD_POST; + args.body = payload; + args.headers = D2D_HEADERS; + args.timeout = D2D_TIMEOUT_MS; + args.url = buff40; // "escapes scope" warning - can ignore, strdup is used + http_request(&args, NULL); + return true; + } + else if (strstarts(msg, "H;")) { + // Send a esp-esp message + msg += 2; + const char *method = NULL; + const char *params = NULL; + const char *nonce = NULL; + const char *url = NULL; + const char *payload = NULL; + httpd_method methodNum = HTTPD_METHOD_GET; + + FIND_NEXT(method, ';'); + + if (streq(method, "GET")) methodNum = HTTPD_METHOD_GET; + else if (streq(method, "POST")) methodNum = HTTPD_METHOD_POST; + else if (streq(method, "OPTIONS")) methodNum = HTTPD_METHOD_OPTIONS; + else if (streq(method, "PUT")) methodNum = HTTPD_METHOD_PUT; + else if (streq(method, "DELETE")) methodNum = HTTPD_METHOD_DELETE; + else if (streq(method, "PATCH")) methodNum = HTTPD_METHOD_PATCH; + else if (streq(method, "HEAD")) methodNum = HTTPD_METHOD_HEAD; + else { + warn("BAD METHOD: %s, using GET", method); + } + + FIND_NEXT(params, ';'); + + dbg("Method %s", method); + dbg("Params %s", params); + + size_t max_len = HTTPCLIENT_DEF_MAX_LEN; + int timeout = HTTPCLIENT_DEF_TIMEOUT_MS; + bool want_body = 0; + bool want_head = 0; + bool no_resp = 0; + + do { + p = strchr(params, ','); + if (p != NULL) *p = '\0'; + const char *param = params; + if (params[0] == 0) break; // no params + + if(streq(param, "H")) want_head = 1; // Return head + else if(streq(param, "B")) want_body = 1; // Return body + else if(streq(param, "X")) no_resp = 1; // X - no response, no callback + else if(strstarts(param, "L=")) { // max length + max_len = atoi(param+2); + } else if(strstarts(param, "T=")) { // timeout + timeout = atoi(param+2); + } else if(strstarts(param, "N=")) { // Nonce + nonce = param+2; + } else { + warn("BAD PARAM: %s", param); + return false; + } + + dbg("- param %s", params); + + if (p == NULL) break; + params = p + 1; + } while(1); + + p = strchr(msg, '\n'); + if (p != NULL) *p = '\0'; + url = msg; + dbg("URL: %s", url); + + if (p != NULL) { + payload = p + 1; + dbg("Payload: %s", payload); + } else { + payload = NULL; + } + + httpclient_args args; + httpclient_args_init(&args); + args.method = methodNum; + args.body = payload; + args.headers = D2D_HEADERS; + args.timeout = timeout; + args.max_response_len = max_len; + args.url = url; + + if (!no_resp) { + struct d2d_request_opts *opts = malloc(sizeof(struct d2d_request_opts)); + opts->want_body = want_body; + opts->want_head = want_head; + opts->nonce = esp_strdup(nonce); + args.userData = opts; + } + + http_request(&args, no_resp ? NULL : requestCb); + + dbg("Done"); + return true; + } + + return false; +} diff --git a/user/cgi_d2d.h b/user/cgi_d2d.h new file mode 100644 index 0000000..7a22393 --- /dev/null +++ b/user/cgi_d2d.h @@ -0,0 +1,14 @@ +// +// Created by MightyPork on 2017/10/01. +// + +#ifndef ESPTERM_CGI_D2D_H +#define ESPTERM_CGI_D2D_H + +#include + +#define D2D_MSG_ENDPOINT "/api/v1/msg" + +bool d2d_parse_command(char *msg); + +#endif //ESPTERM_CGI_D2D_H diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index 9d6a087..7a78f95 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -260,7 +260,7 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int // TODO base this on the actual buffer empty space, not rx chunk size if ((UART_AsyncTxGetEmptySpace() < 256) && !browser_wants_xon) { - UART_WriteChar(UART1, '-', 100); + //UART_WriteChar(UART1, '-', 100); cgiWebsockBroadcast(URL_WS_UPDATE, "-", 1, 0); browser_wants_xon = true; @@ -365,7 +365,7 @@ ETSTimer xonTim; static void ICACHE_FLASH_ATTR notify_empty_txbuf_cb(void *unused) { - UART_WriteChar(UART1, '+', 100); + //UART_WriteChar(UART1, '+', 100); cgiWebsockBroadcast(URL_WS_UPDATE, "+", 1, 0); resetHeartbeatTimer(); browser_wants_xon = false; diff --git a/user/serial.c b/user/serial.c index 0a960cc..bcbd54c 100644 --- a/user/serial.c +++ b/user/serial.c @@ -92,5 +92,4 @@ void ICACHE_FLASH_ATTR serialInit(void) void ICACHE_FLASH_ATTR UART_HandleRxByte(char c) { ansi_parser(c); - system_soft_wdt_feed(); // so we survive long torrents } diff --git a/user/uart_buffer.c b/user/uart_buffer.c index 20bc3c6..93a770b 100644 --- a/user/uart_buffer.c +++ b/user/uart_buffer.c @@ -8,8 +8,11 @@ #include #include -#define UART_TX_BUFFER_SIZE 512 //Ring buffer length of tx buffer -#define UART_RX_BUFFER_SIZE 600 //Ring buffer length of rx buffer +//#define buf_dbg(format, ...) printf(format "\r\n", ##__VA_ARGS__) +#define buf_dbg(format, ...) (void)format + +#define UART_TX_BUFFER_SIZE 1024 //Ring buffer length of tx buffer +#define UART_RX_BUFFER_SIZE 1024 //Ring buffer length of rx buffer struct UartBuffer { uint32 UartBuffSize; @@ -55,6 +58,13 @@ UART_AsyncBufferInit(uint32 buf_size) } } +static void ICACHE_FLASH_ATTR +UART_AsyncBufferReset(struct UartBuffer *pBuff) +{ + pBuff->pInPos = pBuff->pUartBuff; + pBuff->pOutPos = pBuff->pUartBuff; + pBuff->Space = (uint16) pBuff->UartBuffSize; +} /** * Copy data onto Buffer @@ -67,23 +77,30 @@ UART_WriteToAsyncBuffer(struct UartBuffer *pCur, const char *pdata, uint16 data_ { if (data_len == 0) return; + buf_dbg("WTAB %d, space %d", data_len, pCur->Space); + uint16 tail_len = (uint16) (pCur->pUartBuff + pCur->UartBuffSize - pCur->pInPos); if (tail_len >= data_len) { //do not need to loop back the queue + buf_dbg("tail %d, no fold", tail_len); memcpy(pCur->pInPos, pdata, data_len); pCur->pInPos += (data_len); pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize); pCur->Space -= data_len; } else { + buf_dbg("tail only %d, folding", tail_len); memcpy(pCur->pInPos, pdata, tail_len); + buf_dbg("chunk 1, %d", tail_len); pCur->pInPos += (tail_len); pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize); pCur->Space -= tail_len; + buf_dbg("chunk 2, %d", data_len - tail_len); memcpy(pCur->pInPos, pdata + tail_len, data_len - tail_len); pCur->pInPos += (data_len - tail_len); pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize); pCur->Space -= (data_len - tail_len); } + buf_dbg("new space %d", pCur->Space); } /****************************************************************************** @@ -158,9 +175,8 @@ void UART_RxFifoCollect(void) uint8 fifo_data; fifo_len = (uint8) ((READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT); if (fifo_len >= pRxBuffer->Space) { - // try to read at least the bit we can fifo_len = (uint8) (pRxBuffer->Space - 1); - UART_WriteChar(UART1, '%', 1); + UART_WriteString(UART1, "\r\nRX BUF OVERRUN!!\r\n", 100); // discard contents of the FIFO - would loop forever buf_idx = 0; while (buf_idx < fifo_len) { @@ -199,29 +215,19 @@ u16 ICACHE_FLASH_ATTR UART_AsyncTxGetEmptySpace(void) * @param data_len - can be -1 for strlen */ void ICACHE_FLASH_ATTR -UART_SendAsync(const char *pdata, int16_t data_len) +UART_SendAsync(const char *pdata, int data_len) { - u16 real_len = (u16) data_len; - if (data_len <= 0) real_len = (u16) strlen(pdata); + size_t real_len = (data_len) <= 0 ? strlen(pdata) : (size_t) data_len; -// if (pTxBuffer == NULL) { -// printf("init tx buf\n\r"); -// pTxBuffer = UART_AsyncBufferInit(UART_TX_BUFFER_SIZE); -// if (pTxBuffer != NULL) { -// UART_WriteToAsyncBuffer(pTxBuffer, pdata, real_len); -// } -// else { -// printf("tx alloc fail\r\n"); -// } -// } -// else { - if (real_len <= pTxBuffer->Space) { - UART_WriteToAsyncBuffer(pTxBuffer, pdata, real_len); - } - else { - UART_WriteChar(UART1, '^', 1); - } -// } + buf_dbg("Send Async %d", real_len); + if (real_len <= pTxBuffer->Space) { + buf_dbg("accepted, space %d", pTxBuffer->Space); + UART_WriteToAsyncBuffer(pTxBuffer, pdata, (uint16) real_len); + } + else { + buf_dbg("FULL!"); + UART_WriteString(UART1, "\r\nTX BUF OVERRUN!!\r\n", 100); + } // Here we enable TX empty interrupt that will take care of sending the content SET_PERI_REG_MASK(UART_CONF1(UART0), (UART_TX_EMPTY_THRESH_VAL & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S); @@ -257,8 +263,8 @@ void UART_DispatchFromTxBuffer(uint8 uart_no) uint8 len_tmp; uint16 data_len; -// if (pTxBuffer) { - data_len = (uint8) (pTxBuffer->UartBuffSize - pTxBuffer->Space); + data_len = (uint16) (pTxBuffer->UartBuffSize - pTxBuffer->Space); + buf_dbg("rem %d",data_len); if (data_len > fifo_remain) { len_tmp = fifo_remain; UART_TxFifoEnq(pTxBuffer, len_tmp, uart_no); @@ -268,8 +274,9 @@ void UART_DispatchFromTxBuffer(uint8 uart_no) len_tmp = (uint8) data_len; UART_TxFifoEnq(pTxBuffer, len_tmp, uart_no); - // we get one more IT after fifo ends even if we have 0 more bytes - // for notify + // We get one more IT after fifo ends even if we have 0 more bytes, + // for notify. Otherwise we would say we have space while the FIFO + // was still running if (next_empty_it_only_for_notify) { notify_empty_txbuf(); next_empty_it_only_for_notify = 0; @@ -279,9 +286,4 @@ void UART_DispatchFromTxBuffer(uint8 uart_no) SET_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA); } } - -// } -// else { -// error("pTxBuff null \n\r"); -// } } diff --git a/user/uart_buffer.h b/user/uart_buffer.h index 6b9b362..244b4bf 100644 --- a/user/uart_buffer.h +++ b/user/uart_buffer.h @@ -14,7 +14,7 @@ void UART_AllocBuffers(void); uint16 UART_ReadAsync(char *pdata, uint16 data_len); // write to tx buffer -void UART_SendAsync(const char *pdata, int16_t data_len); +void UART_SendAsync(const char *pdata, int data_len); //move data from uart fifo to rx buffer void UART_RxFifoCollect(void); diff --git a/user/uart_handler.c b/user/uart_handler.c index 1f96d2f..2688379 100755 --- a/user/uart_handler.c +++ b/user/uart_handler.c @@ -25,7 +25,7 @@ static void uart_processTask(os_event_t *events); // Those heavily affect the byte loss ratio #define PROCESS_CHUNK_LEN 1 -#define FIFO_FULL_THRES 32 +#define RX_FIFO_FULL_THRES 16 #define uart_recvTaskPrio 1 #define uart_recvTaskQueueLen 25 @@ -78,8 +78,8 @@ void ICACHE_FLASH_ATTR UART_SetupAsyncReceiver(void) ETS_UART_INTR_ATTACH((void *)uart0_rx_intr_handler, &(UartDev.rcv_buff)); // the buf will be used as an arg // fifo threshold config (max: UART_RXFIFO_FULL_THRHD = 127) - uint32_t conf = ((FIFO_FULL_THRES & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S); - conf |= ((0x10 & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S); + uint32_t conf = ((RX_FIFO_FULL_THRES & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S); + conf |= ((0x05 & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S); // timeout config conf |= ((0x06 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S); // timeout threshold conf |= UART_RX_TOUT_EN; // enable timeout diff --git a/user/version.h b/user/version.h index 4d20ae7..29abf5a 100644 --- a/user/version.h +++ b/user/version.h @@ -5,6 +5,8 @@ #ifndef ESP_VT100_FIRMWARE_VERSION_H #define ESP_VT100_FIRMWARE_VERSION_H +#include "helpers.h" + #define FW_V_MAJOR 2 #define FW_V_MINOR 1 #define FW_V_PATCH 0 From 9a5bc0a2740902d21431f3b920406ceecb27843f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 1 Oct 2017 22:56:13 +0200 Subject: [PATCH 24/35] Add codename --- Makefile | 2 +- esphttpdconfig.mk.example | 1 + libesphttpd | 2 +- user/cgi_d2d.c | 35 +++++++++++++++++------------------ user/cgi_d2d.h | 10 ++++++++++ user/cgi_sockets.c | 10 ++++++++-- user/serial.c | 1 + user/uart_buffer.c | 25 +++++++++++++++---------- user/uart_buffer.h | 4 ++++ user/uart_handler.c | 4 ++-- user/user_main.c | 18 ++++++++++++++---- user/version.h | 3 ++- 12 files changed, 76 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index 6470145..c503bcf 100644 --- a/Makefile +++ b/Makefile @@ -216,7 +216,7 @@ libesphttpd/Makefile: $(Q) [[ -e "libesphttpd/Makefile" ]] || echo -e "\e[31mlibesphttpd submodule missing.\nIf build fails, run \"git submodule init\" and \"git submodule update\".\e[0m" libesphttpd: libesphttpd/Makefile - $(Q) make -C libesphttpd USE_OPENSDK=$(USE_OPENSDK) SERVERNAME_PREFIX="ESPTerm " -j4 + $(Q) make -C libesphttpd USE_OPENSDK=$(USE_OPENSDK) -j4 $(APP_AR): libesphttpd $(OBJ) $(vecho) "AR $@" diff --git a/esphttpdconfig.mk.example b/esphttpdconfig.mk.example index 714dce8..cb494b7 100644 --- a/esphttpdconfig.mk.example +++ b/esphttpdconfig.mk.example @@ -39,6 +39,7 @@ OUTPUT_TYPE = combined ESP_SPI_FLASH_SIZE_K = 1024 GLOBAL_CFLAGS = \ + -DDEBUG_D2D=0 \ -DDEBUG_ROUTER=0 \ -DDEBUG_CAPTDNS=0 \ -DDEBUG_HTTP=0 \ diff --git a/libesphttpd b/libesphttpd index a2fe09b..8f4db52 160000 --- a/libesphttpd +++ b/libesphttpd @@ -1 +1 @@ -Subproject commit a2fe09bb4d610d08806afcc4cc008fc01c0dcee0 +Subproject commit 8f4db520bce2ecdc147dd6625e05d8dda45c813a diff --git a/user/cgi_d2d.c b/user/cgi_d2d.c index cf18c6d..d5cbb34 100644 --- a/user/cgi_d2d.c +++ b/user/cgi_d2d.c @@ -13,7 +13,7 @@ #define D2D_TIMEOUT_MS 2000 #define D2D_HEADERS \ - "User-Agent: ESPTerm/"FIRMWARE_VERSION"\r\n" \ + "User-Agent: ESPTerm "FIRMWARE_VERSION"\r\n" \ "Content-Type: text/plain; charset=utf-8\r\n" \ "Cache-Control: max-age=0\r\n" @@ -62,14 +62,13 @@ requestCb(int http_status, } // semicolon only if more data is to be sent - if (opts->want_head || opts->want_body) - sprintf(bb, ";"); + if (opts->want_head || opts->want_body) sprintf(bb, ";"); apars_respond(buff100); - dbg("Response %d, nonce %s", http_status, opts->nonce); - dbg("Headers %s", response_headers); - dbg("Body %s", response_body); + d2d_dbg("Response %d, nonce \"%s\"", http_status, opts->nonce?opts->nonce:""); + d2d_dbg("Headers %s", response_headers); + d2d_dbg("Body %s", response_body); // head and payload separated by \r\n\r\n (one \r\n is at the end of head - maybe) if (opts->want_head) { @@ -108,7 +107,7 @@ d2d_parse_command(char *msg) FIND_NEXT(ip, ';'); const char *payload = msg; - ansi_dbg("D2D Tx,dest=%s,msg=%s", ip, payload); + d2d_dbg("D2D Tx,dest=%s,msg=%s", ip, payload); sprintf(buff40, "http://%s" D2D_MSG_ENDPOINT, ip); httpclient_args args; @@ -141,16 +140,16 @@ d2d_parse_command(char *msg) else if (streq(method, "PATCH")) methodNum = HTTPD_METHOD_PATCH; else if (streq(method, "HEAD")) methodNum = HTTPD_METHOD_HEAD; else { - warn("BAD METHOD: %s, using GET", method); + d2d_warn("BAD METHOD: %s, using GET", method); } FIND_NEXT(params, ';'); - dbg("Method %s", method); - dbg("Params %s", params); + d2d_dbg("Method %s", method); + d2d_dbg("Params %s", params); size_t max_len = HTTPCLIENT_DEF_MAX_LEN; - int timeout = HTTPCLIENT_DEF_TIMEOUT_MS; + uint timeout = HTTPCLIENT_DEF_TIMEOUT_MS; bool want_body = 0; bool want_head = 0; bool no_resp = 0; @@ -165,17 +164,17 @@ d2d_parse_command(char *msg) else if(streq(param, "B")) want_body = 1; // Return body else if(streq(param, "X")) no_resp = 1; // X - no response, no callback else if(strstarts(param, "L=")) { // max length - max_len = atoi(param+2); + max_len = (size_t) atoi(param + 2); } else if(strstarts(param, "T=")) { // timeout - timeout = atoi(param+2); + timeout = (uint) atoi(param + 2); } else if(strstarts(param, "N=")) { // Nonce nonce = param+2; } else { - warn("BAD PARAM: %s", param); + d2d_warn("BAD PARAM: %s", param); return false; } - dbg("- param %s", params); + d2d_dbg("- param %s", params); if (p == NULL) break; params = p + 1; @@ -184,11 +183,11 @@ d2d_parse_command(char *msg) p = strchr(msg, '\n'); if (p != NULL) *p = '\0'; url = msg; - dbg("URL: %s", url); + d2d_dbg("URL: %s", url); if (p != NULL) { payload = p + 1; - dbg("Payload: %s", payload); + d2d_dbg("Payload: %s", payload); } else { payload = NULL; } @@ -212,7 +211,7 @@ d2d_parse_command(char *msg) http_request(&args, no_resp ? NULL : requestCb); - dbg("Done"); + d2d_dbg("Done"); return true; } diff --git a/user/cgi_d2d.h b/user/cgi_d2d.h index 7a22393..70a95f2 100644 --- a/user/cgi_d2d.h +++ b/user/cgi_d2d.h @@ -7,6 +7,16 @@ #include +#if DEBUG_D2D +#define d2d_warn warn +#define d2d_dbg dbg +#define d2d_info info +#else +#define d2d_warn(fmt, ...) +#define d2d_dbg(fmt, ...) +#define d2d_info(fmt, ...) +#endif + #define D2D_MSG_ENDPOINT "/api/v1/msg" bool d2d_parse_command(char *msg); diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index 7a78f95..565afa5 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -69,7 +69,7 @@ updateNotify_do(Websock *ws, ScreenNotifyTopics topics) } httpd_cgi_state cont = screenSerializeToBuffer(sock_buff, SOCK_BUF_LEN, topics, &data); - int flg = WEBSOCK_FLAG_BIN; + int flg = 0; //WEBSOCK_FLAG_BIN if (cont == HTTPD_CGI_MORE) flg |= WEBSOCK_FLAG_MORE; if (i > 0) flg |= WEBSOCK_FLAG_CONT; if (ws) { @@ -304,13 +304,17 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int /** Send a heartbeat msg */ static void ICACHE_FLASH_ATTR heartbeatTimCb(void *unused) { + static u32 hbcnt=0; + if (term_active_clients > 0) { if (notify_available) { inp_dbg("."); // Heartbeat packet - indicate we're still connected // JS reloads the page if heartbeat is lost for a couple seconds - cgiWebsockBroadcast(URL_WS_UPDATE, ".", 1, 0); + char buf[10]; + sprintf(buf, ".%d", hbcnt++); + cgiWebsockBroadcast(URL_WS_UPDATE, buf, (int) strlen(buf), 0); // schedule next tick TIMER_START(&heartbeatTim, heartbeatTimCb, HB_TIME, 0); @@ -331,6 +335,7 @@ static void ICACHE_FLASH_ATTR resetHeartbeatTimer(void) static void ICACHE_FLASH_ATTR closeSockCb(Websock *ws) { term_active_clients--; + inp_dbg("Close socket CB, remain %d clients", term_active_clients); if (term_active_clients <= 0) { term_active_clients = 0; @@ -340,6 +345,7 @@ static void ICACHE_FLASH_ATTR closeSockCb(Websock *ws) // stop the timer os_timer_disarm(&heartbeatTim); + inp_dbg("Stop HB timer"); } } diff --git a/user/serial.c b/user/serial.c index bcbd54c..0a960cc 100644 --- a/user/serial.c +++ b/user/serial.c @@ -92,4 +92,5 @@ void ICACHE_FLASH_ATTR serialInit(void) void ICACHE_FLASH_ATTR UART_HandleRxByte(char c) { ansi_parser(c); + system_soft_wdt_feed(); // so we survive long torrents } diff --git a/user/uart_buffer.c b/user/uart_buffer.c index 93a770b..7c07d8f 100644 --- a/user/uart_buffer.c +++ b/user/uart_buffer.c @@ -11,9 +11,6 @@ //#define buf_dbg(format, ...) printf(format "\r\n", ##__VA_ARGS__) #define buf_dbg(format, ...) (void)format -#define UART_TX_BUFFER_SIZE 1024 //Ring buffer length of tx buffer -#define UART_RX_BUFFER_SIZE 1024 //Ring buffer length of rx buffer - struct UartBuffer { uint32 UartBuffSize; uint8 *pUartBuff; @@ -25,12 +22,15 @@ struct UartBuffer { static struct UartBuffer *pTxBuffer = NULL; static struct UartBuffer *pRxBuffer = NULL; -static struct UartBuffer *UART_AsyncBufferInit(uint32 buf_size); +static u8 rxArray[UART_RX_BUFFER_SIZE]; +static u8 txArray[UART_TX_BUFFER_SIZE]; + +static struct UartBuffer *UART_AsyncBufferInit(uint32 buf_size, u8 *buffer); void ICACHE_FLASH_ATTR UART_AllocBuffers(void) { - pTxBuffer = UART_AsyncBufferInit(UART_TX_BUFFER_SIZE); - pRxBuffer = UART_AsyncBufferInit(UART_RX_BUFFER_SIZE); + pTxBuffer = UART_AsyncBufferInit(UART_TX_BUFFER_SIZE, txArray); + pRxBuffer = UART_AsyncBufferInit(UART_RX_BUFFER_SIZE, rxArray); } /****************************************************************************** @@ -40,7 +40,7 @@ void ICACHE_FLASH_ATTR UART_AllocBuffers(void) * Returns : NONE *******************************************************************************/ static struct UartBuffer *ICACHE_FLASH_ATTR -UART_AsyncBufferInit(uint32 buf_size) +UART_AsyncBufferInit(uint32 buf_size, u8 *buffer) { uint32 heap_size = system_get_free_heap_size(); if (heap_size <= buf_size) { @@ -50,7 +50,7 @@ UART_AsyncBufferInit(uint32 buf_size) else { struct UartBuffer *pBuff = (struct UartBuffer *) malloc(sizeof(struct UartBuffer)); pBuff->UartBuffSize = buf_size; - pBuff->pUartBuff = (uint8 *) malloc(pBuff->UartBuffSize); + pBuff->pUartBuff = buffer != NULL ? buffer : (uint8 *) malloc(pBuff->UartBuffSize); pBuff->pInPos = pBuff->pUartBuff; pBuff->pOutPos = pBuff->pUartBuff; pBuff->Space = (uint16) pBuff->UartBuffSize; @@ -176,7 +176,7 @@ void UART_RxFifoCollect(void) fifo_len = (uint8) ((READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT); if (fifo_len >= pRxBuffer->Space) { fifo_len = (uint8) (pRxBuffer->Space - 1); - UART_WriteString(UART1, "\r\nRX BUF OVERRUN!!\r\n", 100); + UART_WriteChar(UART1, '#', 10); // discard contents of the FIFO - would loop forever buf_idx = 0; while (buf_idx < fifo_len) { @@ -209,6 +209,11 @@ u16 ICACHE_FLASH_ATTR UART_AsyncTxGetEmptySpace(void) return pTxBuffer->Space; } +u16 ICACHE_FLASH_ATTR UART_AsyncTxCount(void) +{ + return (u16) (pTxBuffer->UartBuffSize - pTxBuffer->Space); +} + /** * Schedule data to be sent * @param pdata @@ -226,7 +231,7 @@ UART_SendAsync(const char *pdata, int data_len) } else { buf_dbg("FULL!"); - UART_WriteString(UART1, "\r\nTX BUF OVERRUN!!\r\n", 100); + UART_WriteChar(UART1, '=', 10); } // Here we enable TX empty interrupt that will take care of sending the content diff --git a/user/uart_buffer.h b/user/uart_buffer.h index 244b4bf..3f91d39 100644 --- a/user/uart_buffer.h +++ b/user/uart_buffer.h @@ -7,6 +7,9 @@ #include +#define UART_TX_BUFFER_SIZE 1000 //Ring buffer length of tx buffer +#define UART_RX_BUFFER_SIZE 600 //Ring buffer length of rx buffer + // the init func void UART_AllocBuffers(void); @@ -22,6 +25,7 @@ void UART_RxFifoCollect(void); void UART_DispatchFromTxBuffer(uint8 uart_no); u16 UART_AsyncRxCount(void); +u16 UART_AsyncTxCount(void); u16 UART_AsyncTxGetEmptySpace(void); diff --git a/user/uart_handler.c b/user/uart_handler.c index 2688379..66a2f4a 100755 --- a/user/uart_handler.c +++ b/user/uart_handler.c @@ -24,8 +24,8 @@ static void uart_recvTask(os_event_t *events); static void uart_processTask(os_event_t *events); // Those heavily affect the byte loss ratio -#define PROCESS_CHUNK_LEN 1 -#define RX_FIFO_FULL_THRES 16 +#define PROCESS_CHUNK_LEN 10 +#define RX_FIFO_FULL_THRES 40 #define uart_recvTaskPrio 1 #define uart_recvTaskQueueLen 25 diff --git a/user/user_main.c b/user/user_main.c index fd735b5..b8b38f5 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -30,6 +30,7 @@ #include "persist.h" #include "ansi_parser.h" #include "ascii.h" +#include "uart_buffer.h" #ifdef ESPFS_POS CgiUploadFlashDef uploadParams={ @@ -51,6 +52,7 @@ CgiUploadFlashDef uploadParams={ #define INCLUDE_FLASH_FNS #endif +#define HEAP_TIMER_MS 1000 /** Periodically show heap usage */ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { @@ -60,18 +62,24 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) int heap = system_get_free_heap_size(); int diff = (heap-last); + int rxc = UART_AsyncRxCount(); + int txc = UART_AsyncTxCount(); + + int rxp = ((rxc*10000) / UART_RX_BUFFER_SIZE)/100; + int txp = ((txc*10000) / UART_TX_BUFFER_SIZE)/100; + const char *cc = "+"; if (diff<0) cc = ""; if (diff == 0) { if (cnt == 5) { // only every 5 secs if no change - dbg("FH: %d", heap); + dbg("Rx: %2d%c, Tx: %2d%c, Hp: %d", rxp, '%', txp, '%', heap); cnt = 0; } } else { // report change - dbg("FH: %d (%s%d)", heap, cc, diff); + dbg("Rx: %2d%c, Tx: %2d%c, Hp: %d (%s%d)", rxp, '%', txp, '%', heap, cc, diff); cnt = 0; } @@ -101,7 +109,8 @@ void ICACHE_FLASH_ATTR user_init(void) banner_info("Firmware (c) Ondrej Hruska, 2017"); banner_info(TERMINAL_GITHUB_REPO); banner_info(""); - banner_info("Version "FIRMWARE_VERSION", built " __DATE__ " at " __TIME__ " " __TIMEZONE__); + banner_info("Version "FIRMWARE_VERSION","); + banner_info("built " __DATE__ " at " __TIME__ " " __TIMEZONE__); printf("\r\n"); ioInit(); @@ -116,7 +125,7 @@ void ICACHE_FLASH_ATTR user_init(void) #if DEBUG_HEAP // Heap use timer & blink - TIMER_START(&prHeapTimer, prHeapTimerCb, 1000, 1); + TIMER_START(&prHeapTimer, prHeapTimerCb, HEAP_TIMER_MS, 1); #endif // do later (some functions do not work if called from user_init) @@ -130,6 +139,7 @@ static void ICACHE_FLASH_ATTR user_start(void *unused) captdnsInit(); httpdInit(routes, 80); + httpdSetName("ESPTerm " FIRMWARE_VERSION); ansi_parser_inhibit = false; diff --git a/user/version.h b/user/version.h index 29abf5a..ac47858 100644 --- a/user/version.h +++ b/user/version.h @@ -10,8 +10,9 @@ #define FW_V_MAJOR 2 #define FW_V_MINOR 1 #define FW_V_PATCH 0 +#define FW_CODENAME "Anthill" // 2.1.0 -#define FIRMWARE_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) +#define FIRMWARE_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) " \"" FW_CODENAME "\"" #define FIRMWARE_VERSION_NUM (FW_V_MAJOR*1000 + FW_V_MINOR*10 + FW_V_PATCH) // this is used in ID queries #define TERMINAL_GITHUB_REPO "https://github.com/espterm/espterm-firmware" #define TERMINAL_GITHUB_REPO_FRONT "https://github.com/espterm/espterm-front-end" From 791f6e716f496cb8991a7df414ffffa23a479c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 2 Oct 2017 01:40:26 +0200 Subject: [PATCH 25/35] finished basic d2d and external, adde flags etc --- esphttpdconfig.mk.example | 1 + front-end | 2 +- libesphttpd | 2 +- user/ansi_parser_callbacks.c | 4 +- user/api.h | 15 ++++ user/cgi_d2d.c | 137 ++++++++++++++++++++++++++++++----- user/cgi_d2d.h | 5 +- user/cgi_system.c | 4 +- user/routes.c | 22 ++++-- user/serial.c | 13 ++-- 10 files changed, 166 insertions(+), 39 deletions(-) create mode 100644 user/api.h diff --git a/esphttpdconfig.mk.example b/esphttpdconfig.mk.example index cb494b7..d95d005 100644 --- a/esphttpdconfig.mk.example +++ b/esphttpdconfig.mk.example @@ -39,6 +39,7 @@ OUTPUT_TYPE = combined ESP_SPI_FLASH_SIZE_K = 1024 GLOBAL_CFLAGS = \ + -DASYNC_LOG=1 \ -DDEBUG_D2D=0 \ -DDEBUG_ROUTER=0 \ -DDEBUG_CAPTDNS=0 \ diff --git a/front-end b/front-end index 29b8134..6f165da 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 29b813457c7a185c41e050d358b001fadd2498e4 +Subproject commit 6f165da9b6237fd88efc836a663a429ed8aadb6e diff --git a/libesphttpd b/libesphttpd index 8f4db52..3484209 160000 --- a/libesphttpd +++ b/libesphttpd @@ -1 +1 @@ -Subproject commit 8f4db520bce2ecdc147dd6625e05d8dda45c813a +Subproject commit 348420959b919d95412daeafe5014f4255854f80 diff --git a/user/ansi_parser_callbacks.c b/user/ansi_parser_callbacks.c index 37c9914..014c968 100644 --- a/user/ansi_parser_callbacks.c +++ b/user/ansi_parser_callbacks.c @@ -11,6 +11,7 @@ #include "version.h" #include "uart_buffer.h" #include "screen.h" +#include "uart_driver.h" volatile bool enquiry_suppressed = false; ETSTimer enqTimer; @@ -26,7 +27,8 @@ void ICACHE_FLASH_ATTR enqTimerCb(void *unused) void ICACHE_FLASH_ATTR apars_respond(const char *str) { - UART_SendAsync(str, -1); + UART_WriteString(UART0, str, UART_TIMEOUT_US); + //UART_SendAsync(str, -1); } /** diff --git a/user/api.h b/user/api.h new file mode 100644 index 0000000..7f16a8b --- /dev/null +++ b/user/api.h @@ -0,0 +1,15 @@ +// +// Created by MightyPork on 2017/10/01. +// + +#ifndef ESPTERM_API_H +#define ESPTERM_API_H + +// TODO use X-MACRO for access restrictions etc + +#define API_D2D_MSG "/api/v1/msg" +#define API_REBOOT "/api/v1/reboot" +#define API_PING "/api/v1/ping" +#define API_CLEAR "/api/v1/clear" + +#endif //ESPTERM_API_H diff --git a/user/cgi_d2d.c b/user/cgi_d2d.c index d5cbb34..931920d 100644 --- a/user/cgi_d2d.c +++ b/user/cgi_d2d.c @@ -4,39 +4,70 @@ #include #include "cgi_d2d.h" -#include "apars_logging.h" #include "version.h" #include "ansi_parser_callbacks.h" +#include "api.h" #include #include #define D2D_TIMEOUT_MS 2000 #define D2D_HEADERS \ - "User-Agent: ESPTerm "FIRMWARE_VERSION"\r\n" \ + "User-Agent: ESPTerm "FIRMWARE_VERSION" like curl wget HTTPie\r\n" \ "Content-Type: text/plain; charset=utf-8\r\n" \ - "Cache-Control: max-age=0\r\n" + "Accept-Encoding: identity\r\n" \ + "Accept-Charset: utf-8\r\n" \ + "Accept: text/*, application/json\r\n" \ + "Cache-Control: no-cache,private,max-age=0\r\n" struct d2d_request_opts { bool want_body; bool want_head; + size_t max_result_len; char *nonce; }; +volatile bool request_pending = false; + +static void ICACHE_FLASH_ATTR +requestNoopCb(int http_status, + char *response_headers, + char *response_body, + size_t body_size, + void *userArg) +{ + request_pending = false; + if (userArg != NULL) free(userArg); +} + static void ICACHE_FLASH_ATTR requestCb(int http_status, - const char *response_headers, - const char *response_body, + char *response_headers, + char *response_body, size_t body_size, void *userArg) { - if (userArg == NULL) return; + if (userArg == NULL) { + request_pending = false; + return; + } + struct d2d_request_opts *opts = userArg; + d2d_dbg("Rx url response, code %d, nonce \"%s\"", http_status, opts->nonce?opts->nonce:""); + + // ensure positive - would be hard to parse + if (http_status < 0) http_status = -http_status; + char buff100[100]; int len = 0; - if (opts->want_head) len += strlen(response_headers); + size_t headers_size = strlen(response_headers); + + if (opts->want_head) len += headers_size; if (opts->want_body) len += body_size + (opts->want_head*2); + if (opts->max_result_len > 0 && len > opts->max_result_len) + len = (int) opts->max_result_len; + char *bb = buff100; bb += sprintf(bb, "\x1b^h;%d;", http_status); const char *comma = ""; @@ -66,24 +97,35 @@ requestCb(int http_status, apars_respond(buff100); - d2d_dbg("Response %d, nonce \"%s\"", http_status, opts->nonce?opts->nonce:""); - d2d_dbg("Headers %s", response_headers); - d2d_dbg("Body %s", response_body); + //d2d_dbg("Headers (part) %100s", response_headers); + //d2d_dbg("Body (part) %100s", response_body); // head and payload separated by \r\n\r\n (one \r\n is at the end of head - maybe) if (opts->want_head) { + // truncate + if (headers_size > len) { + response_headers[len] = 0; + opts->want_body = false; // soz, it wouldn't fit + } apars_respond(response_headers); if(opts->want_body) apars_respond("\r\n"); } if(opts->want_body) { + // truncate + if (opts->want_head*(headers_size+2)+body_size > len) { + response_body[len - (opts->want_head*(headers_size+2))] = 0; + } + apars_respond(response_body); } apars_respond("\a"); free(opts->nonce); - free(userArg); + free(opts); + + request_pending = false; } bool ICACHE_FLASH_ATTR @@ -101,6 +143,8 @@ d2d_parse_command(char *msg) } while(0) \ if (strstarts(msg, "M;")) { + if (request_pending) return false; + // Send a esp-esp message msg += 2; const char *ip; @@ -108,7 +152,7 @@ d2d_parse_command(char *msg) const char *payload = msg; d2d_dbg("D2D Tx,dest=%s,msg=%s", ip, payload); - sprintf(buff40, "http://%s" D2D_MSG_ENDPOINT, ip); + sprintf(buff40, "http://%s" API_D2D_MSG, ip); httpclient_args args; httpclient_args_init(&args); @@ -117,10 +161,14 @@ d2d_parse_command(char *msg) args.headers = D2D_HEADERS; args.timeout = D2D_TIMEOUT_MS; args.url = buff40; // "escapes scope" warning - can ignore, strdup is used - http_request(&args, NULL); + + request_pending = true; + http_request(&args, requestNoopCb); return true; } else if (strstarts(msg, "H;")) { + if (request_pending) return false; + // Send a esp-esp message msg += 2; const char *method = NULL; @@ -128,7 +176,7 @@ d2d_parse_command(char *msg) const char *nonce = NULL; const char *url = NULL; const char *payload = NULL; - httpd_method methodNum = HTTPD_METHOD_GET; + httpd_method methodNum; FIND_NEXT(method, ';'); @@ -140,7 +188,8 @@ d2d_parse_command(char *msg) else if (streq(method, "PATCH")) methodNum = HTTPD_METHOD_PATCH; else if (streq(method, "HEAD")) methodNum = HTTPD_METHOD_HEAD; else { - d2d_warn("BAD METHOD: %s, using GET", method); + d2d_warn("BAD METHOD: %s", method); + return false; } FIND_NEXT(params, ';'); @@ -148,7 +197,8 @@ d2d_parse_command(char *msg) d2d_dbg("Method %s", method); d2d_dbg("Params %s", params); - size_t max_len = HTTPCLIENT_DEF_MAX_LEN; + size_t max_buf_len = HTTPCLIENT_DEF_MAX_LEN; + size_t max_result_len = 0; // 0 = no truncate uint timeout = HTTPCLIENT_DEF_TIMEOUT_MS; bool want_body = 0; bool want_head = 0; @@ -163,8 +213,10 @@ d2d_parse_command(char *msg) if(streq(param, "H")) want_head = 1; // Return head else if(streq(param, "B")) want_body = 1; // Return body else if(streq(param, "X")) no_resp = 1; // X - no response, no callback - else if(strstarts(param, "L=")) { // max length - max_len = (size_t) atoi(param + 2); + else if(strstarts(param, "l=")) { // max buffer length + max_buf_len = (size_t) atoi(param + 2); + } else if(strstarts(param, "L=")) { // max length + max_result_len = (size_t) atoi(param + 2); } else if(strstarts(param, "T=")) { // timeout timeout = (uint) atoi(param + 2); } else if(strstarts(param, "N=")) { // Nonce @@ -198,18 +250,20 @@ d2d_parse_command(char *msg) args.body = payload; args.headers = D2D_HEADERS; args.timeout = timeout; - args.max_response_len = max_len; + args.max_response_len = max_buf_len; args.url = url; if (!no_resp) { struct d2d_request_opts *opts = malloc(sizeof(struct d2d_request_opts)); opts->want_body = want_body; opts->want_head = want_head; + opts->max_result_len = max_result_len; opts->nonce = esp_strdup(nonce); args.userData = opts; } - http_request(&args, no_resp ? NULL : requestCb); + request_pending = true; + http_request(&args, no_resp ? requestNoopCb : requestCb); d2d_dbg("Done"); return true; @@ -217,3 +271,46 @@ d2d_parse_command(char *msg) return false; } + +httpd_cgi_state ICACHE_FLASH_ATTR cgiD2DMessage(HttpdConnData *connData) +{ + if (connData->conn==NULL) { + //Connection aborted. Clean up. + return HTTPD_CGI_DONE; + } + + size_t len = 0; + if (connData->post && connData->post->buff) + len = strlen(connData->post->buff); + else if (connData->getArgs) + len = strlen(connData->getArgs); + else + len = 0; + + u8 *ip = connData->remote_ip; + char buf[20]; + sprintf(buf, "\x1b^m;"IPSTR";L=%d;", ip[0], ip[1], ip[2], ip[3], (int)len); + apars_respond(buf); + + if (connData->post && connData->post->buff) + apars_respond(connData->post->buff); + else if (connData->getArgs) + apars_respond(connData->getArgs); + + apars_respond("\a"); + + d2d_dbg("D2D Rx src="IPSTR",len=%d", ip[0], ip[1], ip[2], ip[3],len); + + // Received a msg + + httdResponseOptions(connData, 0); + httdSetTransferMode(connData, HTTPD_TRANSFER_CLOSE); + + httpdStartResponse(connData, 200); + httpdHeader(connData, "Content-Type", "text/plain"); + httpdEndHeaders(connData); + + httpdSend(connData, "message received\r\n", -1); + + return HTTPD_CGI_DONE; +} diff --git a/user/cgi_d2d.h b/user/cgi_d2d.h index 70a95f2..4eb3a74 100644 --- a/user/cgi_d2d.h +++ b/user/cgi_d2d.h @@ -6,6 +6,7 @@ #define ESPTERM_CGI_D2D_H #include +#include #if DEBUG_D2D #define d2d_warn warn @@ -17,8 +18,8 @@ #define d2d_info(fmt, ...) #endif -#define D2D_MSG_ENDPOINT "/api/v1/msg" - bool d2d_parse_command(char *msg); +httpd_cgi_state cgiD2DMessage(HttpdConnData *connData); + #endif //ESPTERM_CGI_D2D_H diff --git a/user/cgi_system.c b/user/cgi_system.c index 8418c5d..d23d20b 100755 --- a/user/cgi_system.c +++ b/user/cgi_system.c @@ -50,7 +50,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiResetDevice(HttpdConnData *connData) os_timer_setfn(&tmr, tmrCb, NULL); os_timer_arm(&tmr, 100, false); - httpdSend(connData, "system reset\n", -1); + httpdSend(connData, "system reset\r\n", -1); return HTTPD_CGI_DONE; } @@ -66,7 +66,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiPing(HttpdConnData *connData) httpdHeader(connData, "Content-Type", "text/plain"); httpdEndHeaders(connData); - httpdSend(connData, "pong\n", -1); + httpdSend(connData, "pong\r\n", -1); return HTTPD_CGI_DONE; } diff --git a/user/routes.c b/user/routes.c index 9dfd8a5..4b6b502 100644 --- a/user/routes.c +++ b/user/routes.c @@ -14,6 +14,8 @@ #include "cgi_persist.h" #include "syscfg.h" #include "persist.h" +#include "api.h" +#include "cgi_d2d.h" /** * Password for WiFi config @@ -45,7 +47,8 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiOptionalPwLock(HttpdConnData *connData) break; case PWLOCK_SETTINGS_NOTERM: - protect = strstarts(connData->url, "/cfg") && !strstarts(connData->url, "/cfg/term"); + protect = strstarts(connData->url, "/cfg") && + !strstarts(connData->url, "/cfg/term"); break; case PWLOCK_SETTINGS_ALL: @@ -53,7 +56,9 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiOptionalPwLock(HttpdConnData *connData) break; case PWLOCK_MENUS: - protect = strstarts(connData->url, "/cfg") || strstarts(connData->url, "/about") || strstarts(connData->url, "/help"); + protect = strstarts(connData->url, "/cfg") || + strstarts(connData->url, "/about") || + strstarts(connData->url, "/help"); break; default: @@ -64,11 +69,11 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiOptionalPwLock(HttpdConnData *connData) // pages outside the normal scope if (sysconf->pwlock > PWLOCK_NONE) { - if (strstarts(connData->url, "/system/reset")) protect = true; + if (strstarts(connData->url, "/api/v1/reboot")) protect = true; } if (sysconf->pwlock > PWLOCK_SETTINGS_NOTERM) { - if (strstarts(connData->url, "/system/cls")) protect = true; + if (strstarts(connData->url, "/api/v1/clear")) protect = true; } if (sysconf->access_pw[0] == 0) { @@ -103,9 +108,12 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = { ROUTE_WS(URL_WS_UPDATE, updateSockConnect), // --- System control --- - ROUTE_CGI("/system/reset/?", cgiResetDevice), - ROUTE_CGI("/system/ping/?", cgiPing), - ROUTE_CGI("/system/cls/?", cgiResetScreen), + + // API endpoints + ROUTE_CGI(API_REBOOT"/?", cgiResetDevice), + ROUTE_CGI(API_PING"/?", cgiPing), + ROUTE_CGI(API_CLEAR"/?", cgiResetScreen), + ROUTE_CGI(API_D2D_MSG"/?", cgiD2DMessage), ROUTE_REDIRECT("/cfg/?", "/cfg/wifi"), diff --git a/user/serial.c b/user/serial.c index 0a960cc..9c9edce 100644 --- a/user/serial.c +++ b/user/serial.c @@ -40,10 +40,10 @@ buf_pop(void *unused) } } -//LOCAL void my_putc(char c) -//{ -// UART_WriteCharCRLF(UART1, (u8) c, 10); -//} +LOCAL void my_putc(char c) +{ + UART_WriteCharCRLF(UART1, (u8) c, 10); +} /** * Init the serial ports @@ -56,8 +56,11 @@ void ICACHE_FLASH_ATTR serialInitBase(void) UART_SetStopBits(UART1, ONE_STOP_BIT); UART_SetBaudrate(UART1, BIT_RATE_115200); UART_SetPrintPort(UART1); +#if ASYNC_LOG os_install_putc1(buf_putc); - //os_install_putc1(my_putc); +#else + os_install_putc1(my_putc); +#endif UART_SetupAsyncReceiver(); // 1 ms timer From a72e2bed63a3aa24369db23995a80173b2cc5bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 2 Oct 2017 03:18:43 +0200 Subject: [PATCH 26/35] makefile improvements --- .gitignore | 1 + Makefile | 15 +++++++++++++++ front-end | 2 +- release/.gitignore | 2 ++ 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 release/.gitignore diff --git a/.gitignore b/.gitignore index c013837..3334351 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ cmake-build-debug/ .sass-cache *.map +.gitignore diff --git a/Makefile b/Makefile index c503bcf..56be03c 100644 --- a/Makefile +++ b/Makefile @@ -210,6 +210,21 @@ espmac: all: checkdirs $(Q) make actual_all -j4 -B +release: + ESP_LANG=cs make web + make actual_all -j4 + cp firmware/0x00000.bin release/0x00000.bin + cp firmware/0x40000.bin release/0x40000-CS.bin + + ESP_LANG=en make web + make actual_all -j4 + cp firmware/0x40000.bin release/0x40000-EN.bin + + ESP_LANG=de make web + make actual_all -j4 + cp firmware/0x40000.bin release/0x40000-DE.bin + + actual_all: parser $(TARGET_OUT) $(FW_BASE) libesphttpd/Makefile: diff --git a/front-end b/front-end index 6f165da..69e5b77 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 6f165da9b6237fd88efc836a663a429ed8aadb6e +Subproject commit 69e5b774475fc78c6244d3e14ade7a677760a731 diff --git a/release/.gitignore b/release/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/release/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore From 935e05a56a0bf7c015ebcd624e5f9edc74dba4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 2 Oct 2017 19:51:58 +0200 Subject: [PATCH 27/35] Disable Rx pin pull-up, caused malfunction with cheap adapters --- user/uart_handler.c | 1 + 1 file changed, 1 insertion(+) diff --git a/user/uart_handler.c b/user/uart_handler.c index 66a2f4a..622565a 100755 --- a/user/uart_handler.c +++ b/user/uart_handler.c @@ -52,6 +52,7 @@ void ICACHE_FLASH_ATTR UART_Init(void) // U0RXD PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD); + PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0RXD_U); // U1TXD (GPIO2) PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); From a514601897af54e3efcd38b38801e28a1a9f8597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 2 Oct 2017 22:20:38 +0200 Subject: [PATCH 28/35] use async buffer for apars respond other than http --- user/ansi_parser_callbacks.c | 5 ++--- user/cgi_d2d.c | 27 ++++++++++++++++++--------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/user/ansi_parser_callbacks.c b/user/ansi_parser_callbacks.c index 014c968..027d3f5 100644 --- a/user/ansi_parser_callbacks.c +++ b/user/ansi_parser_callbacks.c @@ -11,7 +11,6 @@ #include "version.h" #include "uart_buffer.h" #include "screen.h" -#include "uart_driver.h" volatile bool enquiry_suppressed = false; ETSTimer enqTimer; @@ -27,8 +26,8 @@ void ICACHE_FLASH_ATTR enqTimerCb(void *unused) void ICACHE_FLASH_ATTR apars_respond(const char *str) { - UART_WriteString(UART0, str, UART_TIMEOUT_US); - //UART_SendAsync(str, -1); + // Using the Tx buffer causes issues with large data (eg. from http requests) + UART_SendAsync(str, -1); } /** diff --git a/user/cgi_d2d.c b/user/cgi_d2d.c index 931920d..6b8b086 100644 --- a/user/cgi_d2d.c +++ b/user/cgi_d2d.c @@ -7,6 +7,7 @@ #include "version.h" #include "ansi_parser_callbacks.h" #include "api.h" +#include "uart_driver.h" #include #include @@ -29,6 +30,14 @@ struct d2d_request_opts { volatile bool request_pending = false; +// NOTE! We bypass the async buffer here - used for user input and +// responses to queries {apars_respond()}. In rare situations this could +// lead to a race condition and mixing two different messages +static inline void ICACHE_FLASH_ATTR sendResponseToUART(const char *str) +{ + UART_WriteString(UART0, str, UART_TIMEOUT_US); +} + static void ICACHE_FLASH_ATTR requestNoopCb(int http_status, char *response_headers, @@ -95,7 +104,7 @@ requestCb(int http_status, // semicolon only if more data is to be sent if (opts->want_head || opts->want_body) sprintf(bb, ";"); - apars_respond(buff100); + sendResponseToUART(buff100); //d2d_dbg("Headers (part) %100s", response_headers); //d2d_dbg("Body (part) %100s", response_body); @@ -107,8 +116,8 @@ requestCb(int http_status, response_headers[len] = 0; opts->want_body = false; // soz, it wouldn't fit } - apars_respond(response_headers); - if(opts->want_body) apars_respond("\r\n"); + sendResponseToUART(response_headers); + if(opts->want_body) sendResponseToUART("\r\n"); } if(opts->want_body) { @@ -117,10 +126,10 @@ requestCb(int http_status, response_body[len - (opts->want_head*(headers_size+2))] = 0; } - apars_respond(response_body); + sendResponseToUART(response_body); } - apars_respond("\a"); + sendResponseToUART("\a"); free(opts->nonce); free(opts); @@ -290,14 +299,14 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiD2DMessage(HttpdConnData *connData) u8 *ip = connData->remote_ip; char buf[20]; sprintf(buf, "\x1b^m;"IPSTR";L=%d;", ip[0], ip[1], ip[2], ip[3], (int)len); - apars_respond(buf); + sendResponseToUART(buf); if (connData->post && connData->post->buff) - apars_respond(connData->post->buff); + sendResponseToUART(connData->post->buff); else if (connData->getArgs) - apars_respond(connData->getArgs); + sendResponseToUART(connData->getArgs); - apars_respond("\a"); + sendResponseToUART("\a"); d2d_dbg("D2D Rx src="IPSTR",len=%d", ip[0], ip[1], ip[2], ip[3],len); From 76272530eca27b8bfabe31f43255d5705b96cc7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 2 Oct 2017 22:55:56 +0200 Subject: [PATCH 29/35] improved ENQ response, now includes hashes and ID. Added ID to boot msg --- libesphttpd | 2 +- user/ansi_parser_callbacks.c | 8 +++++++- user/cgi_d2d.c | 2 +- user/cgi_main.c | 2 +- user/user_main.c | 26 +++++++++++++++++--------- user/version.h | 13 +++++++++++-- 6 files changed, 38 insertions(+), 15 deletions(-) diff --git a/libesphttpd b/libesphttpd index 3484209..e4ecf07 160000 --- a/libesphttpd +++ b/libesphttpd @@ -1 +1 @@ -Subproject commit 348420959b919d95412daeafe5014f4255854f80 +Subproject commit e4ecf0724e36c828be5222eddce58a6a5cd2386f diff --git a/user/ansi_parser_callbacks.c b/user/ansi_parser_callbacks.c index 027d3f5..5903a75 100644 --- a/user/ansi_parser_callbacks.c +++ b/user/ansi_parser_callbacks.c @@ -47,8 +47,14 @@ apars_handle_enq(void) { if (enquiry_suppressed) return; + u8 mac[6]; + wifi_get_macaddr(SOFTAP_IF, mac); + + char buf100[100]; + sprintf(buf100, "\x1bXESPTerm "VERSION_STRING" #"GIT_HASH_BACKEND"+"GIT_HASH_FRONTEND" id=%02X%02X%02X\x1b\\", mac[3], mac[4], mac[5]); + // version encased in SOS and ST - apars_respond("\x1bXESPTerm " FIRMWARE_VERSION "\x1b\\"); + apars_respond(buf100); // Throttle enquiry - this is a single-character-invoked response, // so it tends to happen randomly when throwing garbage at the ESP. diff --git a/user/cgi_d2d.c b/user/cgi_d2d.c index 6b8b086..522c50e 100644 --- a/user/cgi_d2d.c +++ b/user/cgi_d2d.c @@ -14,7 +14,7 @@ #define D2D_TIMEOUT_MS 2000 #define D2D_HEADERS \ - "User-Agent: ESPTerm "FIRMWARE_VERSION" like curl wget HTTPie\r\n" \ + "User-Agent: ESPTerm "VERSION_STRING" like curl wget HTTPie\r\n" \ "Content-Type: text/plain; charset=utf-8\r\n" \ "Accept-Encoding: identity\r\n" \ "Accept-Charset: utf-8\r\n" \ diff --git a/user/cgi_main.c b/user/cgi_main.c index 7e63ad6..ccde875 100644 --- a/user/cgi_main.c +++ b/user/cgi_main.c @@ -44,7 +44,7 @@ tplAbout(HttpdConnData *connData, char *token, void **arg) if (token == NULL) return HTTPD_CGI_DONE; if (streq(token, "vers_fw")) { - tplSend(connData, FIRMWARE_VERSION, -1); + tplSend(connData, VERSION_STRING, -1); } else if (streq(token, "date")) { tplSend(connData, __DATE__, -1); diff --git a/user/user_main.c b/user/user_main.c index b8b38f5..dc277d0 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -104,14 +104,22 @@ void ICACHE_FLASH_ATTR user_init(void) wifi_station_set_auto_connect(false); wifi_set_opmode(NULL_MODE); // saves to flash if changed - this might avoid the current spike on startup? - printf("\r\n"); - banner("====== ESPTerm ======"); - banner_info("Firmware (c) Ondrej Hruska, 2017"); - banner_info(TERMINAL_GITHUB_REPO); - banner_info(""); - banner_info("Version "FIRMWARE_VERSION","); - banner_info("built " __DATE__ " at " __TIME__ " " __TIMEZONE__); - printf("\r\n"); + u8 mac[6]; + wifi_get_macaddr(SOFTAP_IF, mac); + + banner_gap(); + banner("================ ESPTerm ================"); + banner_info(); + banner_info("Project by Ondrej Hruska, 2017"); + banner_info(); + banner_info(TERMINAL_GITHUB_REPO_NOPROTO); + banner_info(); + banner_info("Version "FW_VERSION", code name "FW_CODENAME_QUOTED); + banner_info(" back-end #"GIT_HASH_BACKEND" front-end #"GIT_HASH_FRONTEND); + banner_info(" built "__DATE__" at "__TIME__" "__TIMEZONE__); + banner_info(); + banner_info("Device ID: %02X%02X%02X", mac[3], mac[4], mac[5]); + banner_gap(); ioInit(); @@ -139,7 +147,7 @@ static void ICACHE_FLASH_ATTR user_start(void *unused) captdnsInit(); httpdInit(routes, 80); - httpdSetName("ESPTerm " FIRMWARE_VERSION); + httpdSetName("ESPTerm " VERSION_STRING); ansi_parser_inhibit = false; diff --git a/user/version.h b/user/version.h index ac47858..b089f3f 100644 --- a/user/version.h +++ b/user/version.h @@ -10,11 +10,20 @@ #define FW_V_MAJOR 2 #define FW_V_MINOR 1 #define FW_V_PATCH 0 +#define FW_V_SUFFIX "-beta" +//#define FW_V_SUFFIX "" #define FW_CODENAME "Anthill" // 2.1.0 +#define FW_CODENAME_QUOTED "\""FW_CODENAME"\"" + +#define FW_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) FW_V_SUFFIX + +#define VERSION_STRING FW_VERSION " " FW_CODENAME_QUOTED -#define FIRMWARE_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) " \"" FW_CODENAME "\"" #define FIRMWARE_VERSION_NUM (FW_V_MAJOR*1000 + FW_V_MINOR*10 + FW_V_PATCH) // this is used in ID queries -#define TERMINAL_GITHUB_REPO "https://github.com/espterm/espterm-firmware" + +#define TERMINAL_GITHUB_REPO_NOPROTO "github.com/espterm/espterm-firmware" +#define TERMINAL_GITHUB_REPO "https://"TERMINAL_GITHUB_REPO_NOPROTO + #define TERMINAL_GITHUB_REPO_FRONT "https://github.com/espterm/espterm-front-end" #endif //ESP_VT100_FIRMWARE_VERSION_H From d2edcd7fd56b7a6fd3f885d53f45d0b21553e456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 3 Oct 2017 00:59:21 +0200 Subject: [PATCH 30/35] added new release system --- CMakeLists.txt | 1 + Makefile | 20 +++++--------- front-end | 2 +- rel-tpl/README.txt | 21 +++++++++++++++ rel-tpl/flash.sh | 26 ++++++++++++++++++ release/.gitignore | 1 + ship.sh | 66 ++++++++++++++++++++++++++++++++++++++++++++++ user/user_main.c | 2 +- 8 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 rel-tpl/README.txt create mode 100644 rel-tpl/flash.sh create mode 100755 ship.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 9214dff..8e65181 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,7 @@ add_definitions( -DICACHE_FLASH_ATTR= -DICACHE_RODATA_ATTR= -DFLAG_GZIP=2 + -DESP_LANG="en" -DGIT_HASH_BACKEND="asdf" -DGIT_HASH_FRONTEND="asdf" diff --git a/Makefile b/Makefile index 56be03c..474bb3a 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,10 @@ LIBS = c gcc hal phy pp net80211 wpa main lwip crypto #Add in esphttpd lib LIBS += esphttpd +ifndef ESP_LANG +ESP_LANG = en +endif + # compiler flags using during compilation of source files -ggdb CFLAGS = -Os -std=gnu99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH \ @@ -69,7 +73,7 @@ CFLAGS = -Os -std=gnu99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inli CFLAGS += -DGIT_HASH_BACKEND='"$(shell git rev-parse --short HEAD)"' CFLAGS += -DGIT_HASH_FRONTEND='"$(shell cd front-end && git rev-parse --short HEAD)"' -CFLAGS += -D__TIMEZONE__='"$(shell date +%Z)"' +CFLAGS += -D__TIMEZONE__='"$(shell date +%Z)"' -DESP_LANG='"$(ESP_LANG)"' ifdef GLOBAL_CFLAGS CFLAGS += $(GLOBAL_CFLAGS) @@ -211,19 +215,7 @@ all: checkdirs $(Q) make actual_all -j4 -B release: - ESP_LANG=cs make web - make actual_all -j4 - cp firmware/0x00000.bin release/0x00000.bin - cp firmware/0x40000.bin release/0x40000-CS.bin - - ESP_LANG=en make web - make actual_all -j4 - cp firmware/0x40000.bin release/0x40000-EN.bin - - ESP_LANG=de make web - make actual_all -j4 - cp firmware/0x40000.bin release/0x40000-DE.bin - + $(Q) ./release.sh actual_all: parser $(TARGET_OUT) $(FW_BASE) diff --git a/front-end b/front-end index 69e5b77..2e64f0e 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 69e5b774475fc78c6244d3e14ade7a677760a731 +Subproject commit 2e64f0e20b2e82d9ada540639764e163467ea4be diff --git a/rel-tpl/README.txt b/rel-tpl/README.txt new file mode 100644 index 0000000..dc039ea --- /dev/null +++ b/rel-tpl/README.txt @@ -0,0 +1,21 @@ +This is a release archive of ESPTerm, +the VT100 terminal emulator for ESP8266. + +-------------------------------------------- +Version: %VERS% +Locale : %LANG% +Built : %DATETIME% +-------------------------------------------- + +Source repository: + https://github.com/espterm/espterm-firmware + +Report any bugs to our bug-tracker at + https://github.com/espterm/espterm-firmware/issues +or send them to out mailing list + espterm-dev@googlegroups.com + +On-line demo is available at + https://espterm.github.io/term.html + +[EOF] diff --git a/rel-tpl/flash.sh b/rel-tpl/flash.sh new file mode 100644 index 0000000..4a92a28 --- /dev/null +++ b/rel-tpl/flash.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# The parameters ESPTOOL, ESPPORT and ESPBAUD can be customized +# - export your preferred values in .bashrc + +echo -e "\e[32;1mFlashing ESPTerm %VERS% (%LANG%)\e[0m" + +if [ -z ${ESPTOOL} ]; then + ESPTOOL='esptool' + which ${ESPTOOL} &>/dev/null + if [ $? -ne 0 ]; then + ESPTOOL='esptool.py' + which ${ESPTOOL} &>/dev/null + if [ $? -ne 0 ]; then + echo -e '\e[31;1mesptool not found!\e[0m' + exit 1 + fi + fi +fi + +[ -z ESPPORT ] && ESPPORT=/dev/ttyUSB0 +[ -z ESPBAUD ] && ESPBAUD=460800 + +set -x +${ESPTOOL} --port ${ESPPORT} --baud ${ESPBAUD} \ + write_flash 0x00000 '%FILE0%' 0x40000 '%FILE4%' diff --git a/release/.gitignore b/release/.gitignore index d6b7ef3..3814986 100644 --- a/release/.gitignore +++ b/release/.gitignore @@ -1,2 +1,3 @@ * !.gitignore +!flash-tpl.sh diff --git a/ship.sh b/ship.sh new file mode 100755 index 0000000..36e0d12 --- /dev/null +++ b/ship.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +echo -n -e "\e[1;36m Version number (for file names): \e[0m" +read vers +echo + +if [ -z "$vers" ] + then + echo -e "\e[0;31m Aborted.\e[0m" + echo + exit +fi + +function buildlang() { + lang=$1 + + echo -e "\n\e[33;1m------ Building \"${lang}\" package ------\e[0m\n" + + ESP_LANG=${lang} make web + ESP_LANG=${lang} make actual_all -B -j4 + + cp firmware/0x00000.bin release/0x00000.bin + cp firmware/0x40000.bin release/0x40000.bin + cd release + + destdir="$vers-$lang" + file0=${vers}-0x00000-${lang}.bin + file4=${vers}-0x40000-${lang}.bin + [ -e ${destdir} ] && rm -r ${destdir} + mkdir ${destdir} + cp ../firmware/0x00000.bin ${destdir}/${file0} + cp ../firmware/0x40000.bin ${destdir}/${file4} + + flashsh=${destdir}/flash.sh + cp ../rel-tpl/flash.sh ${flashsh} + sed -i s/%FILE0%/${file0}/ ${flashsh} + sed -i s/%FILE4%/${file4}/ ${flashsh} + sed -i s/%VERS%/${vers}/ ${flashsh} + sed -i s/%LANG%/${lang}/ ${flashsh} + chmod +x ${flashsh} + + readmefil=${destdir}/README.txt + cp ../rel-tpl/README.txt ${readmefil} + sed -i s/%VERS%/${vers}/ ${readmefil} + sed -i s/%LANG%/${lang}/ ${readmefil} + dt=$(LC_TIME=en_US.UTF-8 date '+%c %Z') + sed -i "s#%DATETIME%#${dt}#" ${readmefil} + unix2dos ${readmefil} + + cd ${destdir} + sha256sum ${file0} ${file4} README.txt flash.sh > checksums.txt + cd .. + + targetfile=espterm-${vers}-${lang}.zip + [[ -e ${targetfile}.zip ]] && rm ${targetfile}.zip + pwd + zip -9 ${targetfile} ${destdir}/* + #rm -r ${destdir} + + rm 0x00000.bin 0x40000.bin + cd .. +} + +buildlang cs +buildlang en +buildlang de diff --git a/user/user_main.c b/user/user_main.c index dc277d0..62afe2e 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -114,7 +114,7 @@ void ICACHE_FLASH_ATTR user_init(void) banner_info(); banner_info(TERMINAL_GITHUB_REPO_NOPROTO); banner_info(); - banner_info("Version "FW_VERSION", code name "FW_CODENAME_QUOTED); + banner_info("Version "FW_VERSION" ("ESP_LANG"), code name "FW_CODENAME_QUOTED); banner_info(" back-end #"GIT_HASH_BACKEND" front-end #"GIT_HASH_FRONTEND); banner_info(" built "__DATE__" at "__TIME__" "__TIMEZONE__); banner_info(); From 2796709624bba557a07b64bb38e29a97adc7c33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 3 Oct 2017 01:55:46 +0200 Subject: [PATCH 31/35] version bump to beta2 --- front-end | 2 +- ship.sh | 1 + user/version.h | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/front-end b/front-end index 2e64f0e..2bd1673 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 2e64f0e20b2e82d9ada540639764e163467ea4be +Subproject commit 2bd167304c20f985a41f55cee55100283a6b7896 diff --git a/ship.sh b/ship.sh index 36e0d12..446a87d 100755 --- a/ship.sh +++ b/ship.sh @@ -16,6 +16,7 @@ function buildlang() { echo -e "\n\e[33;1m------ Building \"${lang}\" package ------\e[0m\n" + make clean ESP_LANG=${lang} make web ESP_LANG=${lang} make actual_all -B -j4 diff --git a/user/version.h b/user/version.h index b089f3f..85c0d0f 100644 --- a/user/version.h +++ b/user/version.h @@ -10,7 +10,7 @@ #define FW_V_MAJOR 2 #define FW_V_MINOR 1 #define FW_V_PATCH 0 -#define FW_V_SUFFIX "-beta" +#define FW_V_SUFFIX "-beta2" //#define FW_V_SUFFIX "" #define FW_CODENAME "Anthill" // 2.1.0 #define FW_CODENAME_QUOTED "\""FW_CODENAME"\"" From 6c1ab28e75962ef85eecb7925aed5d29fb4d8961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 3 Oct 2017 20:30:53 +0200 Subject: [PATCH 32/35] improved build script, automatic version fetch from source --- front-end | 2 +- get_version.c | 14 ++++++++++++++ ship.sh | 36 +++++++++++++++++++----------------- user/version.h | 5 +++-- 4 files changed, 37 insertions(+), 20 deletions(-) create mode 100644 get_version.c diff --git a/front-end b/front-end index 2bd1673..0114846 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 2bd167304c20f985a41f55cee55100283a6b7896 +Subproject commit 01148465b71b640892a50ec825205ae581d6dca7 diff --git a/get_version.c b/get_version.c new file mode 100644 index 0000000..0809451 --- /dev/null +++ b/get_version.c @@ -0,0 +1,14 @@ +// +// Created by MightyPork on 2017/10/03. +// +// helper for building release packages +// +// Run with `tcc -run` +// + +#include "user/version.h" +#include + +void main() { + printf(FW_VERSION); +} diff --git a/ship.sh b/ship.sh index 446a87d..6b419bf 100755 --- a/ship.sh +++ b/ship.sh @@ -1,16 +1,19 @@ #!/usr/bin/env bash -echo -n -e "\e[1;36m Version number (for file names): \e[0m" -read vers -echo - -if [ -z "$vers" ] - then - echo -e "\e[0;31m Aborted.\e[0m" - echo - exit +if [ -z "$1" ]; then + vers=$(tcc -run get_version.c) +else + vers=$1 fi +git pull + +echo -n -e "\e[1;36mBuilding packages for version $vers\e[0m" + +cd front-end +git pull +cd .. + function buildlang() { lang=$1 @@ -20,8 +23,6 @@ function buildlang() { ESP_LANG=${lang} make web ESP_LANG=${lang} make actual_all -B -j4 - cp firmware/0x00000.bin release/0x00000.bin - cp firmware/0x40000.bin release/0x40000.bin cd release destdir="$vers-$lang" @@ -56,12 +57,13 @@ function buildlang() { [[ -e ${targetfile}.zip ]] && rm ${targetfile}.zip pwd zip -9 ${targetfile} ${destdir}/* - #rm -r ${destdir} - - rm 0x00000.bin 0x40000.bin cd .. } -buildlang cs -buildlang en -buildlang de +if [ -z "$ESP_LANG" ]; then + buildlang cs + buildlang en + buildlang de +else + buildlang ${ESP_LANG} +fi diff --git a/user/version.h b/user/version.h index 85c0d0f..1bd6e06 100644 --- a/user/version.h +++ b/user/version.h @@ -5,12 +5,13 @@ #ifndef ESP_VT100_FIRMWARE_VERSION_H #define ESP_VT100_FIRMWARE_VERSION_H -#include "helpers.h" +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) #define FW_V_MAJOR 2 #define FW_V_MINOR 1 #define FW_V_PATCH 0 -#define FW_V_SUFFIX "-beta2" +#define FW_V_SUFFIX "-beta3" //#define FW_V_SUFFIX "" #define FW_CODENAME "Anthill" // 2.1.0 #define FW_CODENAME_QUOTED "\""FW_CODENAME"\"" From dc08512397a66136efdf7b1bc322983763180eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 3 Oct 2017 20:57:54 +0200 Subject: [PATCH 33/35] one timezone is probably enough in the release readme --- ship.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ship.sh b/ship.sh index 6b419bf..ae6e87f 100755 --- a/ship.sh +++ b/ship.sh @@ -45,7 +45,7 @@ function buildlang() { cp ../rel-tpl/README.txt ${readmefil} sed -i s/%VERS%/${vers}/ ${readmefil} sed -i s/%LANG%/${lang}/ ${readmefil} - dt=$(LC_TIME=en_US.UTF-8 date '+%c %Z') + dt=$(LC_TIME=en_US.UTF-8 date '+%c') sed -i "s#%DATETIME%#${dt}#" ${readmefil} unix2dos ${readmefil} From 022e882001f1c063320fd5737f16fdbff52d1abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 3 Oct 2017 22:16:07 +0200 Subject: [PATCH 34/35] added IP to answerback message --- user/ansi_parser_callbacks.c | 11 ++++++++++- user/cgi_wifi.c | 17 +---------------- user/wifimgr.c | 16 ++++++++++++++++ user/wifimgr.h | 2 ++ 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/user/ansi_parser_callbacks.c b/user/ansi_parser_callbacks.c index 5903a75..906a072 100644 --- a/user/ansi_parser_callbacks.c +++ b/user/ansi_parser_callbacks.c @@ -11,6 +11,7 @@ #include "version.h" #include "uart_buffer.h" #include "screen.h" +#include "wifimgr.h" volatile bool enquiry_suppressed = false; ETSTimer enqTimer; @@ -51,7 +52,15 @@ apars_handle_enq(void) wifi_get_macaddr(SOFTAP_IF, mac); char buf100[100]; - sprintf(buf100, "\x1bXESPTerm "VERSION_STRING" #"GIT_HASH_BACKEND"+"GIT_HASH_FRONTEND" id=%02X%02X%02X\x1b\\", mac[3], mac[4], mac[5]); + char *buf = buf100; + buf += sprintf(buf, "\x1bX"); + buf += sprintf(buf, "ESPTerm "VERSION_STRING" "); + buf += sprintf(buf, "#"GIT_HASH_BACKEND"+"GIT_HASH_FRONTEND" "); + buf += sprintf(buf, "id=%02X%02X%02X ", mac[3], mac[4], mac[5]); + int x = getStaIpAsString(buf); + if (x) buf += x; + else buf--; // remove the trailing space + buf += sprintf(buf, "\x1b\\"); // version encased in SOS and ST apars_respond(buf100); diff --git a/user/cgi_wifi.c b/user/cgi_wifi.c index 8f0f22f..7378977 100644 --- a/user/cgi_wifi.c +++ b/user/cgi_wifi.c @@ -617,22 +617,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, } } else if (streq(token, "sta_active_ip")) { - x = wifi_get_opmode(); - connectStatus = wifi_station_get_connect_status(); - - if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP || wificonf->opmode == SOFTAP_MODE) { - strcpy(buff, ""); - } - else { - struct ip_info info; - wifi_get_ip_info(STATION_IF, &info); - sprintf(buff, IPSTR, GOOD_IP2STR(info.ip.addr)); - -// sprintf(buff, "ip: "IPSTR", mask: "IPSTR", gw: "IPSTR, -// GOOD_IP2STR(info.ip.addr), -// GOOD_IP2STR(info.netmask.addr), -// GOOD_IP2STR(info.gw.addr)); - } + getStaIpAsString(buff); } tplSend(connData, buff, -1); diff --git a/user/wifimgr.c b/user/wifimgr.c index f1acc8e..2588365 100644 --- a/user/wifimgr.c +++ b/user/wifimgr.c @@ -8,6 +8,22 @@ WiFiConfigBundle * const wificonf = &persist.current.wificonf; WiFiConfChangeFlags wifi_change_flags; +int ICACHE_FLASH_ATTR getStaIpAsString(char *buffer) +{ + WIFI_MODE x = wifi_get_opmode(); + STATION_STATUS connectStatus = wifi_station_get_connect_status(); + + if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP || wificonf->opmode == SOFTAP_MODE) { + strcpy(buffer, ""); + return 0; + } + else { + struct ip_info info; + wifi_get_ip_info(STATION_IF, &info); + return sprintf(buffer, IPSTR, GOOD_IP2STR(info.ip.addr)); + } +} + /** * Restore defaults in the WiFi config block. * This is to be called if the WiFi config is corrupted on startup, diff --git a/user/wifimgr.h b/user/wifimgr.h index 33c49bb..146c3c8 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -60,6 +60,8 @@ void wifimgr_restore_defaults(void); void wifimgr_apply_settings(void); +int getStaIpAsString(char *buffer); + #if DEBUG_WIFI #define wifi_warn warn #define wifi_dbg dbg From 73c98eb2d1a584cdf2f84e55af7b36f11815a22b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 4 Oct 2017 00:10:54 +0200 Subject: [PATCH 35/35] some logging adjustments --- include/user_config.h | 1 - user/ansi_parser_callbacks.c | 2 ++ user/cgi_d2d.c | 13 ++++++------- user/user_main.c | 14 +++++++------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/user_config.h b/include/user_config.h index 8b13789..e69de29 100644 --- a/include/user_config.h +++ b/include/user_config.h @@ -1 +0,0 @@ - diff --git a/user/ansi_parser_callbacks.c b/user/ansi_parser_callbacks.c index 906a072..d17187f 100644 --- a/user/ansi_parser_callbacks.c +++ b/user/ansi_parser_callbacks.c @@ -62,6 +62,8 @@ apars_handle_enq(void) else buf--; // remove the trailing space buf += sprintf(buf, "\x1b\\"); + (void)buf; + // version encased in SOS and ST apars_respond(buf100); diff --git a/user/cgi_d2d.c b/user/cgi_d2d.c index 522c50e..1c28235 100644 --- a/user/cgi_d2d.c +++ b/user/cgi_d2d.c @@ -63,8 +63,6 @@ requestCb(int http_status, struct d2d_request_opts *opts = userArg; - d2d_dbg("Rx url response, code %d, nonce \"%s\"", http_status, opts->nonce?opts->nonce:""); - // ensure positive - would be hard to parse if (http_status < 0) http_status = -http_status; @@ -77,6 +75,8 @@ requestCb(int http_status, if (opts->max_result_len > 0 && len > opts->max_result_len) len = (int) opts->max_result_len; + d2d_info("Rx HTTP response, code %d, len %d, nonce \"%s\"", http_status, len, opts->nonce?opts->nonce:""); + char *bb = buff100; bb += sprintf(bb, "\x1b^h;%d;", http_status); const char *comma = ""; @@ -160,7 +160,7 @@ d2d_parse_command(char *msg) FIND_NEXT(ip, ';'); const char *payload = msg; - d2d_dbg("D2D Tx,dest=%s,msg=%s", ip, payload); + d2d_info("D2D Tx,dest=%s,msg=%s", ip, payload); sprintf(buff40, "http://%s" API_D2D_MSG, ip); httpclient_args args; @@ -203,9 +203,8 @@ d2d_parse_command(char *msg) FIND_NEXT(params, ';'); + d2d_info("HTTP request"); d2d_dbg("Method %s", method); - d2d_dbg("Params %s", params); - size_t max_buf_len = HTTPCLIENT_DEF_MAX_LEN; size_t max_result_len = 0; // 0 = no truncate uint timeout = HTTPCLIENT_DEF_TIMEOUT_MS; @@ -274,7 +273,7 @@ d2d_parse_command(char *msg) request_pending = true; http_request(&args, no_resp ? requestNoopCb : requestCb); - d2d_dbg("Done"); + d2d_dbg("Request sent."); return true; } @@ -308,7 +307,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiD2DMessage(HttpdConnData *connData) sendResponseToUART("\a"); - d2d_dbg("D2D Rx src="IPSTR",len=%d", ip[0], ip[1], ip[2], ip[3],len); + d2d_info("D2D Rx src="IPSTR",len=%d", ip[0], ip[1], ip[2], ip[3],len); // Received a msg diff --git a/user/user_main.c b/user/user_main.c index 62afe2e..db52518 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -74,12 +74,12 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) if (diff == 0) { if (cnt == 5) { // only every 5 secs if no change - dbg("Rx: %2d%c, Tx: %2d%c, Hp: %d", rxp, '%', txp, '%', heap); + dbg("Rx/Tx: %d/%d%c, Hp: %d", rxp, txp, '%', heap); cnt = 0; } } else { // report change - dbg("Rx: %2d%c, Tx: %2d%c, Hp: %d (%s%d)", rxp, '%', txp, '%', heap, cc, diff); + dbg("Rx/Tx: %d/%d%c, Hp: %d (%s%d)", rxp, txp, '%', heap, cc, diff); cnt = 0; } @@ -131,11 +131,6 @@ void ICACHE_FLASH_ATTR user_init(void) espFsInit((void *) (webpages_espfs_start)); #endif -#if DEBUG_HEAP - // Heap use timer & blink - TIMER_START(&prHeapTimer, prHeapTimerCb, HEAP_TIMER_MS, 1); -#endif - // do later (some functions do not work if called from user_init) TIMER_START(&userStartTimer, user_start, 10, 0); } @@ -154,6 +149,11 @@ static void ICACHE_FLASH_ATTR user_start(void *unused) // Print the CANCEL character to indicate the module has restarted // Critically important for client application if any kind of screen persistence / content re-use is needed UART_WriteChar(UART0, CAN, UART_TIMEOUT_US); // 0x18 - 24 - CAN + +#if DEBUG_HEAP + // Heap use timer & blink + TIMER_START(&prHeapTimer, prHeapTimerCb, HEAP_TIMER_MS, 1); +#endif } // ---- unused funcs removed from sdk to save space ---