new message format

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

@ -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);

@ -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);
}

@ -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(&notifyContentTim, 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(&notifyCooldownTim, 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(&notifyLabelsTim, 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(&notifyCooldownTim, 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(&notifyLabelsTim, notifyLabelsTimCb, termconf->display_tout_ms+2, 0); // this delay is useful when both are fired at once on screen reset
}
else if (topic == CHANGE_CONTENT) {
// throttle delay
TIMER_START(&notifyContentTim, notifyContentTimCb, termconf->display_tout_ms, 0);
}
// 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;

@ -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

@ -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);
}

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

@ -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<bb-buffer;j++) {
printf("%02X ", buffer[j]);
}
printf("\n");
#endif
if (i < W*H-1) {
return HTTPD_CGI_MORE;
}
return HTTPD_CGI_DONE;
}
//endregion

@ -34,11 +34,6 @@
*
*/
// Size designed for the terminal config structure
// Must be constant to avoid corrupting user config after upgrade
#define TERMCONF_SIZE 300
#define TERMCONF_VERSION 0
#define TERM_BTN_LEN 10
#define TERM_BTN_MSG_LEN 10
#define TERM_TITLE_LEN 64
@ -70,10 +65,16 @@ enum CursorShape {
#define SCR_DEF_CURSOR_SHAPE CURSOR_BLOCK_BL
#define SCR_DEF_CRLF 0 // enter sends CRLF
#define SCR_DEF_ALLFN 0 // capture F5 etc
#define SCR_DEF_DEBUGBAR 0
// --- Persistent Settings ---
#define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL)
// Size designed for the terminal config structure
// Must be constant to avoid corrupting user config after upgrade
#define TERMCONF_SIZE 300
#define TERMCONF_VERSION 1
typedef struct {
u32 width;
u32 height;
@ -94,6 +95,7 @@ typedef struct {
enum CursorShape cursor_shape;
bool crlf_mode;
bool want_all_fn;
bool debugbar;
} TerminalConfigBundle;
// Live config
@ -152,9 +154,29 @@ typedef enum {
CS_1_DOS_437 = '1',
} CHARSET;
httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data);
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
// combos
TOPIC_INITIAL =
TOPIC_CHANGE_SCREEN_OPTS |
TOPIC_CHANGE_CONTENT_ALL |
TOPIC_CHANGE_CURSOR |
TOPIC_CHANGE_TITLE |
TOPIC_CHANGE_BUTTONS,
TOPIC_WRITE = TOPIC_CHANGE_CURSOR | TOPIC_CHANGE_CONTENT_PART,
};
typedef u16 ScreenNotifyTopics;
void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len);
httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, void **data);
// --- Clearing ---
@ -323,18 +345,11 @@ void screen_repeat_last_character(int count);
/** Report current SGR as num;num;... for DAC query */
void screen_report_sgr(char *buffer);
// --- Notify ---
typedef enum {
CHANGE_CONTENT = 0,
CHANGE_LABELS = 1,
} ScreenNotifyChangeTopic;
/**
* Called when the screen content or settings change
* and the front-end should redraw / update.
* @param topic - what kind of change this is (chooses what message to send)
*/
extern void screen_notifyChange(ScreenNotifyChangeTopic topic);
extern void screen_notifyChange(ScreenNotifyTopics topics);
#endif // SCREEN_H

Loading…
Cancel
Save