Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c55202b42
|
||
|
|
65977bfbae
|
||
|
|
74d52a0c61
|
||
|
|
0fae510207
|
||
|
|
1a8d3cb332
|
||
|
|
ca5a7e3de9
|
||
|
|
fa5489ccfc
|
||
|
|
502babf7b0
|
||
|
|
47f6cd38cd
|
||
|
|
0f10d84a59
|
||
|
|
cb71586138
|
||
|
|
58bd27a082
|
||
|
|
7eb1bdeec6
|
||
|
|
674e89c8c3
|
||
|
|
62a0925586
|
||
|
|
4f4ea95c06
|
||
|
|
2ede271c65
|
||
|
|
692b2c4d11
|
||
|
|
948bcb62ff
|
||
|
|
7607b639b0 | ||
|
|
2e8ec41115 | ||
|
|
80e8e4f7da | ||
|
|
357a9d43e3 | ||
|
|
a464a73a0e | ||
|
|
38b3ce2dc8 | ||
|
|
37de3a095d | ||
|
|
82961568b8 | ||
|
|
3849e161e5 | ||
|
|
43241c2679 | ||
|
|
3bfd51061e | ||
|
|
781cf8825b | ||
|
|
079b522540 | ||
|
|
471aa95c34 | ||
|
|
ac56b1dc61 | ||
|
|
82fd9e5ad1 | ||
|
|
92a454a2cf | ||
|
|
0012c92c4a | ||
|
|
0e8af5712b | ||
|
|
184c772b57 | ||
|
|
62888d4473 |
@@ -12,6 +12,7 @@ html_compressed/
|
||||
ldscript_memspecific.ld
|
||||
eagle.app.sym
|
||||
node_modules
|
||||
esphttpdconfig.mk
|
||||
|
||||
# Garbage added by CLion
|
||||
.idea/
|
||||
|
||||
@@ -153,6 +153,7 @@ include_directories(esp_iot_sdk_v1.5.2/include)
|
||||
add_definitions(
|
||||
-D__ets__
|
||||
-DICACHE_FLASH
|
||||
-DDEBUG_LOGBUF_SIZE=2048
|
||||
-DUSE_OPTIMIZE_PRINTF=1
|
||||
-DHTTPD_MAX_CONNECTIONS=5
|
||||
-DHTTPD_STACKSIZE=1000
|
||||
@@ -160,9 +161,12 @@ add_definitions(
|
||||
-DICACHE_RODATA_ATTR=
|
||||
-DFLAG_GZIP=2
|
||||
-DADMIN_PASSWORD="asdf"
|
||||
-DGIT_HASH_BACKEND="asdf"
|
||||
-DGIT_HASH_FRONTEND="asdf"
|
||||
-DGIT_HASH="blabla"
|
||||
-DDEBUG_HEAP=1
|
||||
-DDEBUG_MALLOC=1
|
||||
-D__TIMEZONE__="UTC"
|
||||
-DESPFS_HEATSHRINK)
|
||||
|
||||
add_executable(ESPTerm ${SOURCE_FILES})
|
||||
|
||||
@@ -69,7 +69,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 += -DADMIN_PASSWORD=$(ADMIN_PASSWORD)
|
||||
CFLAGS += -D__TIMEZONE__='"$(shell date +%Z)"'
|
||||
|
||||
ifdef GLOBAL_CFLAGS
|
||||
CFLAGS += $(GLOBAL_CFLAGS)
|
||||
|
||||
@@ -30,6 +30,26 @@ emulator backend that runs on the ESP8266. The web version will be updated to ma
|
||||
after each minor release (and sometimes in between for testing; the version currently being show-cased
|
||||
can be read on the About page of the demo).
|
||||
|
||||
### Browser support
|
||||
|
||||
Use the above linked online demo to verify compatibility with your browser.
|
||||
|
||||
To our knowledge, ESPTerm **works with**:
|
||||
- Google Chrome (desktop, mobile)
|
||||
- Firefox (desktop, mobile not tested)
|
||||
- Safari (desktop, mobile)
|
||||
- Chromium
|
||||
- Opera
|
||||
- Brave
|
||||
- Konqueror
|
||||
|
||||
It **does not work with**:
|
||||
- Microsoft Edge - runs, but is extremely laggy
|
||||
- Internet Explorer (any version) - crashes, missing JS features
|
||||
- Opera Mini - crashes, missing JS and CSS features
|
||||
- Blackberry browser - not tested, but unlikely
|
||||
- Old Android Browser (before 4.4?) - not tested, likely missing JS features
|
||||
|
||||
## Main features
|
||||
|
||||
- **Almost complete VT102 emulation** with some extras from Xterm, eg.
|
||||
@@ -47,6 +67,16 @@ can be read on the About page of the demo).
|
||||
- **User-friendly comprehensive WiFi configuration** (Demo: [WiFi][demo-wifi], [network][demo-network] config)
|
||||
- Static IP, DHCP, channel selection, power
|
||||
- SSID search utility for finding your existing network
|
||||
|
||||
## Bugs? Ideas?
|
||||
|
||||
To ask any questions or discuss new features you'd like to see added, it's best to use
|
||||
the **[ESPTerm Dev mailing list](https://groups.google.com/forum/#!forum/espterm-dev)**
|
||||
|
||||
Subscribe to the mailing list to also receive new release announcements.
|
||||
|
||||
If you found a bug (that happens alot!), please submit it to our [bug tracker](https://github.com/espterm/espterm-firmware/issues).
|
||||
We also use it to track planned ideas. If you don't want to create a GitHub account for that, just send it to the mailing list.
|
||||
|
||||
## Running ESPTerm
|
||||
|
||||
|
||||
@@ -3,3 +3,6 @@
|
||||
echo "-- Building parser from Ragel source --"
|
||||
|
||||
ragel -L -G0 user/ansi_parser.rl -o user/ansi_parser.c
|
||||
|
||||
sed -i "s/static const char _ansi_actions\[\]/static const char _ansi_actions\[\] ESP_CONST_DATA/" user/ansi_parser.c
|
||||
sed -i "s/static const char _ansi_eof_actions\[\]/static const char _ansi_eof_actions\[\] ESP_CONST_DATA/" user/ansi_parser.c
|
||||
|
||||
+2
-4
@@ -38,15 +38,12 @@ OUTPUT_TYPE = combined
|
||||
# SPI flash size, in K
|
||||
ESP_SPI_FLASH_SIZE_K = 1024
|
||||
|
||||
# Admin password, used to store settings to flash as defaults
|
||||
ADMIN_PASSWORD = "19738426"
|
||||
|
||||
GLOBAL_CFLAGS = \
|
||||
-DDEBUG_ROUTER=0 \
|
||||
-DDEBUG_CAPTDNS=0 \
|
||||
-DDEBUG_HTTP=0 \
|
||||
-DDEBUG_ESPFS=0 \
|
||||
-DDEBUG_PERSIST=0 \
|
||||
-DDEBUG_PERSIST=1 \
|
||||
-DDEBUG_UTFCACHE=0 \
|
||||
-DDEBUG_CGI=0 \
|
||||
-DDEBUG_WIFI=0 \
|
||||
@@ -59,5 +56,6 @@ GLOBAL_CFLAGS = \
|
||||
-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
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# --------------- 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
|
||||
+1
-1
Submodule front-end updated: 53a6ab4a84...f5dd70a6f3
+2
-2
@@ -9,7 +9,7 @@
|
||||
/* Ragel constants block */
|
||||
|
||||
/* #line 12 "user/ansi_parser.c" */
|
||||
static const char _ansi_actions[] = {
|
||||
static const char _ansi_actions[] ESP_CONST_DATA = {
|
||||
0, 1, 0, 1, 1, 1, 2, 1,
|
||||
3, 1, 4, 1, 5, 1, 6, 1,
|
||||
7, 1, 8, 1, 9, 1, 10, 1,
|
||||
@@ -17,7 +17,7 @@ static const char _ansi_actions[] = {
|
||||
15
|
||||
};
|
||||
|
||||
static const char _ansi_eof_actions[] = {
|
||||
static const char _ansi_eof_actions[] ESP_CONST_DATA = {
|
||||
0, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
+17
-21
@@ -551,31 +551,27 @@ do_csi_sgr(CSI_Data *opts)
|
||||
else if (n >= SGR_FG_BRT_START && n <= SGR_FG_BRT_END) screen_set_fg((Color) ((n - SGR_FG_BRT_START) + 8)); // AIX bright fg
|
||||
else if (n >= SGR_BG_BRT_START && n <= SGR_BG_BRT_END) screen_set_bg((Color) ((n - SGR_BG_BRT_START) + 8)); // AIX bright bg
|
||||
// reset color
|
||||
else if (n == SGR_FG_DEFAULT) screen_set_fg(termconf_live.default_fg); // default fg
|
||||
else if (n == SGR_BG_DEFAULT) screen_set_bg(termconf_live.default_bg); // default bg
|
||||
else if (n == SGR_FG_DEFAULT) screen_set_default_fg();
|
||||
else if (n == SGR_BG_DEFAULT) screen_set_default_bg();
|
||||
// 256 colors
|
||||
else if (n == SGR_FG_256 || n == SGR_BG_256) {
|
||||
if (i < count-2) {
|
||||
if (opts->n[i + 1] == 5) {
|
||||
u8 color = (u8) opts->n[i + 2];
|
||||
bool fg = n == SGR_FG_256;
|
||||
if (fg) {
|
||||
screen_set_fg(color);
|
||||
} else {
|
||||
screen_set_bg(color);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ansi_warn("SGR syntax err");
|
||||
apars_show_context();
|
||||
break; // abandon further
|
||||
}
|
||||
i += 2;
|
||||
} else {
|
||||
if (i >= count-2) {
|
||||
ansi_warn("SGR syntax err");
|
||||
apars_show_context();
|
||||
break; // abandon further
|
||||
}
|
||||
|
||||
if (opts->n[i + 1] != 5) {
|
||||
ansi_warn("SGR syntax err");
|
||||
apars_show_context();
|
||||
break; // abandon further
|
||||
}
|
||||
|
||||
u8 color = (u8) opts->n[i + 2];
|
||||
bool fg = n == SGR_FG_256;
|
||||
if (fg) screen_set_fg(color);
|
||||
else screen_set_bg(color);
|
||||
i += 2;
|
||||
}
|
||||
// -- set attr --
|
||||
else if (n == SGR_BOLD) screen_set_sgr(ATTR_BOLD, 1);
|
||||
@@ -585,7 +581,7 @@ do_csi_sgr(CSI_Data *opts)
|
||||
else if (n == SGR_BLINK || n == SGR_BLINK_FAST) screen_set_sgr(ATTR_BLINK, 1); // 6 - rapid blink, not supported
|
||||
else if (n == SGR_STRIKE) screen_set_sgr(ATTR_STRIKE, 1);
|
||||
else if (n == SGR_FRAKTUR) screen_set_sgr(ATTR_FRAKTUR, 1);
|
||||
else if (n == SGR_INVERSE) screen_set_sgr_inverse(1);
|
||||
else if (n == SGR_INVERSE) screen_set_sgr(ATTR_INVERSE, 1);
|
||||
else if (n == SGR_CONCEAL) screen_set_sgr_conceal(1);
|
||||
else if (n == SGR_OVERLINE) screen_set_sgr(ATTR_OVERLINE, 1);
|
||||
// -- clear attr --
|
||||
@@ -595,7 +591,7 @@ do_csi_sgr(CSI_Data *opts)
|
||||
else if (n == SGR_NO_UNDERLINE) screen_set_sgr(ATTR_UNDERLINE, 0);
|
||||
else if (n == SGR_NO_BLINK) screen_set_sgr(ATTR_BLINK, 0);
|
||||
else if (n == SGR_NO_STRIKE) screen_set_sgr(ATTR_STRIKE, 0);
|
||||
else if (n == SGR_NO_INVERSE) screen_set_sgr_inverse(0);
|
||||
else if (n == SGR_NO_INVERSE) screen_set_sgr(ATTR_INVERSE, 0);
|
||||
else if (n == SGR_NO_CONCEAL) screen_set_sgr_conceal(0);
|
||||
else if (n == SGR_NO_OVERLINE) screen_set_sgr(ATTR_OVERLINE, 0);
|
||||
else {
|
||||
|
||||
+11
-2
@@ -37,6 +37,14 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplScreen(HttpdConnData *connData, char *token
|
||||
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);
|
||||
tplSend(connData, buff, -1);
|
||||
}
|
||||
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
@@ -54,13 +62,14 @@ tplAbout(HttpdConnData *connData, char *token, void **arg)
|
||||
tplSend(connData, __DATE__, -1);
|
||||
}
|
||||
else if (streq(token, "time")) {
|
||||
tplSend(connData, __TIME__, -1);
|
||||
tplSend(connData, __TIME__" "__TIMEZONE__, -1);
|
||||
}
|
||||
else if (streq(token, "vers_httpd")) {
|
||||
tplSend(connData, httpdGetVersion(), -1);
|
||||
}
|
||||
else if (streq(token, "vers_sdk")) {
|
||||
tplSend(connData, STR(ESP_SDK_VERSION), -1);
|
||||
//tplSend(connData, STR(ESP_SDK_VERSION), -1);
|
||||
tplSend(connData, system_get_sdk_version(), -1);
|
||||
}
|
||||
else if (streq(token, "hash_backend")) {
|
||||
tplSend(connData, GIT_HASH_BACKEND, -1);
|
||||
|
||||
+15
-1
@@ -41,6 +41,11 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
WiFiConfigBundle *wificonf_backup = malloc(sizeof(WiFiConfigBundle));
|
||||
WiFiConfChangeFlags *wcf_backup = malloc(sizeof(WiFiConfChangeFlags));
|
||||
memcpy(wificonf_backup, wificonf, sizeof(WiFiConfigBundle));
|
||||
memcpy(wcf_backup, &wifi_change_flags, sizeof(WiFiConfChangeFlags));
|
||||
|
||||
// ---- AP DHCP server lease time ----
|
||||
|
||||
if (GET_ARG("ap_dhcp_time")) {
|
||||
@@ -176,6 +181,8 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
|
||||
}
|
||||
}
|
||||
|
||||
(void) redir_url;
|
||||
|
||||
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
|
||||
// All was OK
|
||||
cgi_info("Set network params - success, applying in 1000 ms");
|
||||
@@ -189,12 +196,19 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
|
||||
os_timer_setfn(&timer, applyNetSettingsLaterCb, NULL);
|
||||
os_timer_arm(&timer, 1000, false);
|
||||
|
||||
httpdRedirect(connData, SET_REDIR_SUC);
|
||||
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
|
||||
} else {
|
||||
cgi_warn("Some WiFi settings did not validate, asking for correction");
|
||||
|
||||
memcpy(wificonf, wificonf_backup, sizeof(WiFiConfigBundle));
|
||||
memcpy(&wifi_change_flags, wcf_backup, sizeof(WiFiConfChangeFlags));
|
||||
|
||||
// Some errors, appended to the URL as ?err=
|
||||
httpdRedirect(connData, redir_url_buf);
|
||||
}
|
||||
|
||||
free(wificonf_backup);
|
||||
free(wcf_backup);
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
|
||||
+5
-7
@@ -8,14 +8,12 @@ Cgi/template routines for configuring non-wifi settings
|
||||
#include "helpers.h"
|
||||
#include "cgi_logging.h"
|
||||
|
||||
#define SET_REDIR_SUC "/cfg/admin"
|
||||
#define SET_REDIR_SUC "/cfg/system"
|
||||
|
||||
static bool ICACHE_FLASH_ATTR
|
||||
verify_admin_pw(const char *pw)
|
||||
{
|
||||
// This is not really for security, but to prevent someone who
|
||||
// shouldn't touch those settings from fucking it up.
|
||||
return streq(pw, STR(ADMIN_PASSWORD)); // the PW comes from the makefile
|
||||
return streq(pw, persist.admin.pw);
|
||||
}
|
||||
|
||||
httpd_cgi_state ICACHE_FLASH_ATTR
|
||||
@@ -36,7 +34,7 @@ cgiPersistWriteDefaults(HttpdConnData *connData)
|
||||
|
||||
persist_set_as_default();
|
||||
|
||||
httpdRedirect(connData, SET_REDIR_SUC);
|
||||
httpdRedirect(connData, SET_REDIR_SUC "?msg=Default%20settings%20updated.");
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
// if pw failed, show the same error as if it's wrong
|
||||
@@ -57,7 +55,7 @@ cgiPersistRestoreDefaults(HttpdConnData *connData)
|
||||
|
||||
persist_restore_default();
|
||||
|
||||
httpdRedirect(connData, SET_REDIR_SUC);
|
||||
httpdRedirect(connData, SET_REDIR_SUC "?msg=All%20settings%20restored%20to%20saved%20defaults.");
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
@@ -73,6 +71,6 @@ cgiPersistRestoreHard(HttpdConnData *connData)
|
||||
// Defaults are not changed.
|
||||
persist_load_hard_default();
|
||||
|
||||
httpdRedirect(connData, SET_REDIR_SUC);
|
||||
httpdRedirect(connData, SET_REDIR_SUC "?msg=All%20settings%20restored%20to%20factory%20defaults.");
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
+89
-23
@@ -8,7 +8,6 @@
|
||||
#include "ansi_parser.h"
|
||||
#include "jstring.h"
|
||||
#include "uart_driver.h"
|
||||
#include "cgi_logging.h"
|
||||
|
||||
// Heartbeat interval in ms
|
||||
#define HB_TIME 1000
|
||||
@@ -17,9 +16,12 @@
|
||||
// Must be less than httpd sendbuf
|
||||
#define SOCK_BUF_LEN 2000
|
||||
|
||||
// flags for screen update timeouts
|
||||
volatile bool notify_available = true;
|
||||
volatile bool notify_cooldown = false;
|
||||
|
||||
/** True if we sent XOFF to browser to stop uploading,
|
||||
* and we have to tell it we're ready again */
|
||||
volatile bool browser_wants_xon = false;
|
||||
|
||||
static ETSTimer notifyContentTim;
|
||||
@@ -27,10 +29,14 @@ static ETSTimer notifyLabelsTim;
|
||||
static ETSTimer notifyCooldownTim;
|
||||
static ETSTimer heartbeatTim;
|
||||
|
||||
volatile int active_clients = 0;
|
||||
|
||||
// we're trying to do a kind of mutex here, without the actual primitives
|
||||
// this might glitch, very rarely.
|
||||
// it's recommended to put some delay between setting labels and updating the screen.
|
||||
|
||||
static void resetHeartbeatTimer(void);
|
||||
|
||||
/**
|
||||
* Cooldown delay is over
|
||||
* @param arg
|
||||
@@ -57,7 +63,8 @@ notifyContentTimCb(void *arg)
|
||||
|
||||
if (!notify_available || notify_cooldown || (max_bl > 2048)) { // do not send if we have anything significant backlogged
|
||||
// postpone a little
|
||||
TIMER_START(¬ifyContentTim, notifyContentTimCb, 5, 0);
|
||||
TIMER_START(¬ifyContentTim, notifyContentTimCb, 4, 0);
|
||||
inp_dbg("postpone notify content");
|
||||
return;
|
||||
}
|
||||
notify_available = false;
|
||||
@@ -71,6 +78,7 @@ notifyContentTimCb(void *arg)
|
||||
cgiWebsocketSend(ws, sock_buff, (int) strlen(sock_buff), flg);
|
||||
} else {
|
||||
cgiWebsockBroadcast(URL_WS_UPDATE, sock_buff, (int) strlen(sock_buff), flg);
|
||||
resetHeartbeatTimer();
|
||||
}
|
||||
if (cont == HTTPD_CGI_DONE) break;
|
||||
|
||||
@@ -93,17 +101,25 @@ notifyContentTimCb(void *arg)
|
||||
static void ICACHE_FLASH_ATTR
|
||||
notifyLabelsTimCb(void *arg)
|
||||
{
|
||||
Websock *ws = arg;
|
||||
char sock_buff[SOCK_BUF_LEN];
|
||||
|
||||
if (!notify_available || notify_cooldown) {
|
||||
// postpone a little
|
||||
TIMER_START(¬ifyLabelsTim, notifyLabelsTimCb, 1, 0);
|
||||
TIMER_START(¬ifyLabelsTim, notifyLabelsTimCb, 7, 0);
|
||||
inp_dbg("postpone notify labels");
|
||||
return;
|
||||
}
|
||||
notify_available = false;
|
||||
|
||||
screenSerializeLabelsToBuffer(sock_buff, SOCK_BUF_LEN);
|
||||
cgiWebsockBroadcast(URL_WS_UPDATE, sock_buff, (int) strlen(sock_buff), 0);
|
||||
|
||||
if (ws) {
|
||||
cgiWebsocketSend(ws, sock_buff, (int) strlen(sock_buff), 0);
|
||||
} else {
|
||||
cgiWebsockBroadcast(URL_WS_UPDATE, sock_buff, (int) strlen(sock_buff), 0);
|
||||
resetHeartbeatTimer();
|
||||
}
|
||||
|
||||
notify_cooldown = true;
|
||||
notify_available = true;
|
||||
@@ -115,8 +131,11 @@ notifyLabelsTimCb(void *arg)
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -124,9 +143,12 @@ send_beep(void)
|
||||
void ICACHE_FLASH_ATTR
|
||||
notify_growl(char *msg)
|
||||
{
|
||||
if (active_clients == 0) return;
|
||||
|
||||
// TODO via timer...
|
||||
// here's some potential for a race error with the other broadcast functions :C
|
||||
cgiWebsockBroadcast(URL_WS_UPDATE, msg, (int) strlen(msg), 0);
|
||||
resetHeartbeatTimer();
|
||||
}
|
||||
|
||||
|
||||
@@ -137,15 +159,17 @@ notify_growl(char *msg)
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyChangeTopic topic)
|
||||
{
|
||||
// this is not the most ideal/cleanest implementation
|
||||
// PRs are welcome for a nicer update "queue" solution
|
||||
if (termconf->display_tout_ms == 0) termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS;
|
||||
if (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;
|
||||
|
||||
// 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, 0);
|
||||
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
|
||||
@@ -161,7 +185,7 @@ void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyChangeTopic topic)
|
||||
* @param button - which button, 1-based. 0=none
|
||||
* @param mods - modifier keys bitmap: meta|alt|shift|ctrl
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mods)
|
||||
static void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mods)
|
||||
{
|
||||
// one-based
|
||||
x++;
|
||||
@@ -230,12 +254,12 @@ void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mo
|
||||
}
|
||||
|
||||
/** Socket received a message */
|
||||
void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
|
||||
static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
|
||||
{
|
||||
// Add terminator if missing (seems to randomly happen)
|
||||
data[len] = 0;
|
||||
|
||||
ws_dbg("Sock RX str: %s, len %d", data, len);
|
||||
inp_dbg("Sock RX str: %s, len %d", data, len);
|
||||
|
||||
int y, x, m, b;
|
||||
u8 btnNum;
|
||||
@@ -271,8 +295,9 @@ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
|
||||
|
||||
case 'i':
|
||||
// requests initial load
|
||||
ws_dbg("Client requests initial load");
|
||||
notifyContentTimCb(ws);
|
||||
inp_dbg("Client requests initial load");
|
||||
notifyContentTimCb(ws); // delay??
|
||||
notifyLabelsTimCb(ws);
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
@@ -290,39 +315,80 @@ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
|
||||
break;
|
||||
|
||||
default:
|
||||
ws_warn("Bad command.");
|
||||
inp_warn("Bad command.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Send a heartbeat msg */
|
||||
void ICACHE_FLASH_ATTR heartbeatTimCb(void *unused)
|
||||
static void ICACHE_FLASH_ATTR heartbeatTimCb(void *unused)
|
||||
{
|
||||
if (notify_available) {
|
||||
// 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);
|
||||
if (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);
|
||||
|
||||
// schedule next tick
|
||||
TIMER_START(&heartbeatTim, heartbeatTimCb, HB_TIME, 0);
|
||||
} else {
|
||||
// postpone...
|
||||
TIMER_START(&heartbeatTim, heartbeatTimCb, 10, 0);
|
||||
inp_dbg("postpone heartbeat");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR resetHeartbeatTimer(void)
|
||||
{
|
||||
TIMER_START(&heartbeatTim, heartbeatTimCb, HB_TIME, 0);
|
||||
}
|
||||
|
||||
|
||||
static void ICACHE_FLASH_ATTR closeSockCb(Websock *ws)
|
||||
{
|
||||
active_clients--;
|
||||
if (active_clients <= 0) {
|
||||
active_clients = 0;
|
||||
|
||||
if (mouse_tracking.focus_tracking) {
|
||||
UART_SendAsync("\x1b[O", 3);
|
||||
}
|
||||
|
||||
// stop the timer
|
||||
os_timer_disarm(&heartbeatTim);
|
||||
}
|
||||
}
|
||||
|
||||
/** Socket connected for updates */
|
||||
void ICACHE_FLASH_ATTR updateSockConnect(Websock *ws)
|
||||
{
|
||||
ws_info("Socket connected to "URL_WS_UPDATE);
|
||||
inp_info("Socket connected to "URL_WS_UPDATE);
|
||||
ws->recvCb = updateSockRx;
|
||||
ws->closeCb = closeSockCb;
|
||||
|
||||
TIMER_START(&heartbeatTim, heartbeatTimCb, HB_TIME, 1);
|
||||
if (active_clients == 0) {
|
||||
if (mouse_tracking.focus_tracking) {
|
||||
UART_SendAsync("\x1b[I", 3);
|
||||
}
|
||||
|
||||
resetHeartbeatTimer();
|
||||
}
|
||||
|
||||
active_clients++;
|
||||
}
|
||||
|
||||
ETSTimer xonTim;
|
||||
|
||||
void ICACHE_FLASH_ATTR notify_empty_txbuf_cb(void *unused)
|
||||
static void ICACHE_FLASH_ATTR notify_empty_txbuf_cb(void *unused)
|
||||
{
|
||||
UART_WriteChar(UART1, '+', 100);
|
||||
cgiWebsockBroadcast(URL_WS_UPDATE, "+", 1, 0);
|
||||
resetHeartbeatTimer();
|
||||
browser_wants_xon = false;
|
||||
}
|
||||
|
||||
|
||||
void notify_empty_txbuf(void)
|
||||
{
|
||||
if (browser_wants_xon) {
|
||||
|
||||
+6
-6
@@ -17,13 +17,13 @@ void notify_growl(char *msg);
|
||||
|
||||
// defined in the makefile
|
||||
#if DEBUG_INPUT
|
||||
#define ws_warn warn
|
||||
#define ws_dbg dbg
|
||||
#define ws_info info
|
||||
#define inp_warn warn
|
||||
#define inp_dbg dbg
|
||||
#define inp_info info
|
||||
#else
|
||||
#define ws_warn(...)
|
||||
#define ws_dbg(...)
|
||||
#define ws_info(...)
|
||||
#define inp_warn(...)
|
||||
#define inp_dbg(...)
|
||||
#define inp_info(...)
|
||||
#endif
|
||||
|
||||
#endif //CGI_SOCKETS_H
|
||||
|
||||
+130
-53
@@ -6,7 +6,6 @@
|
||||
#include "cgi_system.h"
|
||||
#include "persist.h"
|
||||
#include "syscfg.h"
|
||||
#include "uart_driver.h"
|
||||
#include "ansi_parser.h"
|
||||
#include "cgi_logging.h"
|
||||
|
||||
@@ -78,7 +77,8 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiPing(HttpdConnData *connData)
|
||||
httpd_cgi_state ICACHE_FLASH_ATTR
|
||||
cgiSystemCfgSetParams(HttpdConnData *connData)
|
||||
{
|
||||
char buff[50];
|
||||
char buff[65];
|
||||
char buff2[65];
|
||||
char redir_url_buf[100];
|
||||
|
||||
char *redir_url = redir_url_buf;
|
||||
@@ -90,53 +90,113 @@ cgiSystemCfgSetParams(HttpdConnData *connData)
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
if (GET_ARG("uart_baud")) {
|
||||
cgi_dbg("Baud rate: %s", buff);
|
||||
int baud = atoi(buff);
|
||||
if (baud == BIT_RATE_300 ||
|
||||
baud == BIT_RATE_600 ||
|
||||
baud == BIT_RATE_1200 ||
|
||||
baud == BIT_RATE_2400 ||
|
||||
baud == BIT_RATE_4800 ||
|
||||
baud == BIT_RATE_9600 ||
|
||||
baud == BIT_RATE_19200 ||
|
||||
baud == BIT_RATE_38400 ||
|
||||
baud == BIT_RATE_57600 ||
|
||||
baud == BIT_RATE_74880 ||
|
||||
baud == BIT_RATE_115200 ||
|
||||
baud == BIT_RATE_230400 ||
|
||||
baud == BIT_RATE_460800 ||
|
||||
baud == BIT_RATE_921600 ||
|
||||
baud == BIT_RATE_1843200 ||
|
||||
baud == BIT_RATE_3686400) {
|
||||
sysconf->uart_baudrate = (u32) baud;
|
||||
} else {
|
||||
cgi_warn("Bad baud rate %s", buff);
|
||||
redir_url += sprintf(redir_url, "uart_baud,");
|
||||
AdminConfigBlock *admin_backup = malloc(sizeof(AdminConfigBlock));
|
||||
SystemConfigBundle *sysconf_backup = malloc(sizeof(SystemConfigBundle));
|
||||
memcpy(admin_backup, &persist.admin, sizeof(AdminConfigBlock));
|
||||
memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle));
|
||||
|
||||
do {
|
||||
if (!GET_ARG("pw")) {
|
||||
break; // if no PW in GET, not trying to configure anything protected
|
||||
}
|
||||
|
||||
if (!streq(buff, persist.admin.pw)) {
|
||||
warn("Bad admin pw!");
|
||||
redir_url += sprintf(redir_url, "pw,");
|
||||
break;
|
||||
}
|
||||
|
||||
// authenticated OK
|
||||
if (GET_ARG("pwlock")) {
|
||||
cgi_dbg("pwlock: %s", buff);
|
||||
int pwlock = atoi(buff);
|
||||
if (pwlock < 0 || pwlock >= PWLOCK_MAX) {
|
||||
cgi_warn("Bad pwlock %s", buff);
|
||||
redir_url += sprintf(redir_url, "pwlock,");
|
||||
break;
|
||||
}
|
||||
|
||||
sysconf->pwlock = (enum pwlock) pwlock;
|
||||
}
|
||||
|
||||
if (GET_ARG("access_pw")) {
|
||||
cgi_dbg("access_pw: %s", buff);
|
||||
|
||||
if (strlen(buff)) {
|
||||
strcpy(buff2, buff);
|
||||
if (!GET_ARG("access_pw2")) {
|
||||
cgi_warn("Missing repeated access_pw %s", buff);
|
||||
redir_url += sprintf(redir_url, "access_pw2,");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!streq(buff, buff2)) {
|
||||
cgi_warn("Bad repeated access_pw %s", buff);
|
||||
redir_url += sprintf(redir_url, "access_pw2,");
|
||||
break;
|
||||
}
|
||||
|
||||
if (strlen(buff) >= 64) {
|
||||
cgi_warn("Too long access_pw %s", buff);
|
||||
redir_url += sprintf(redir_url, "access_pw,");
|
||||
break;
|
||||
}
|
||||
|
||||
cgi_dbg("Changing access PW!");
|
||||
strncpy(sysconf->access_pw, buff, 64);
|
||||
}
|
||||
}
|
||||
|
||||
if (GET_ARG("access_name")) {
|
||||
cgi_dbg("access_name: %s", buff);
|
||||
|
||||
if (!strlen(buff) || strlen(buff) >= 32) {
|
||||
cgi_warn("Too long access_name %s", buff);
|
||||
redir_url += sprintf(redir_url, "access_name,");
|
||||
break;
|
||||
}
|
||||
|
||||
strncpy(sysconf->access_name, buff, 32);
|
||||
}
|
||||
|
||||
if (GET_ARG("admin_pw")) {
|
||||
cgi_dbg("admin_pw: %s", buff);
|
||||
|
||||
if (strlen(buff)) {
|
||||
strcpy(buff2, buff);
|
||||
if (!GET_ARG("admin_pw2")) {
|
||||
cgi_warn("Missing repeated admin_pw %s", buff);
|
||||
redir_url += sprintf(redir_url, "admin_pw2,");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!streq(buff, buff2)) {
|
||||
cgi_warn("Bad repeated admin_pw %s", buff);
|
||||
redir_url += sprintf(redir_url, "admin_pw2,");
|
||||
break;
|
||||
}
|
||||
|
||||
if (strlen(buff) >= 64) {
|
||||
cgi_warn("Too long admin_pw %s", buff);
|
||||
redir_url += sprintf(redir_url, "admin_pw,");
|
||||
break;
|
||||
}
|
||||
|
||||
cgi_dbg("Changing admin PW!");
|
||||
strncpy(persist.admin.pw, buff, 64);
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (GET_ARG("overclock")) {
|
||||
cgi_dbg("overclock = %s", buff);
|
||||
int enable = atoi(buff);
|
||||
if (sysconf->overclock != enable) {
|
||||
sysconf->overclock = (bool)enable;
|
||||
}
|
||||
}
|
||||
|
||||
if (GET_ARG("uart_parity")) {
|
||||
cgi_dbg("Parity: %s", buff);
|
||||
int parity = atoi(buff);
|
||||
if (parity >= 0 && parity <= 2) {
|
||||
sysconf->uart_parity = (UartParityMode) parity;
|
||||
} else {
|
||||
cgi_warn("Bad parity %s", buff);
|
||||
redir_url += sprintf(redir_url, "uart_parity,");
|
||||
}
|
||||
}
|
||||
|
||||
if (GET_ARG("uart_stopbits")) {
|
||||
cgi_dbg("Stop bits: %s", buff);
|
||||
int stopbits = atoi(buff);
|
||||
if (stopbits >= 1 && stopbits <= 3) {
|
||||
sysconf->uart_stopbits = (UartStopBitsNum) stopbits;
|
||||
} else {
|
||||
cgi_warn("Bad stopbits %s", buff);
|
||||
redir_url += sprintf(redir_url, "uart_stopbits,");
|
||||
}
|
||||
}
|
||||
(void)redir_url;
|
||||
|
||||
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
|
||||
// All was OK
|
||||
@@ -145,12 +205,20 @@ cgiSystemCfgSetParams(HttpdConnData *connData)
|
||||
sysconf_apply_settings();
|
||||
persist_store();
|
||||
|
||||
httpdRedirect(connData, SET_REDIR_SUC);
|
||||
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
|
||||
} else {
|
||||
cgi_warn("Some settings did not validate, asking for correction");
|
||||
|
||||
// revert any possible changes
|
||||
memcpy(&persist.admin, admin_backup, sizeof(AdminConfigBlock));
|
||||
memcpy(sysconf, sysconf_backup, sizeof(SystemConfigBundle));
|
||||
|
||||
// Some errors, appended to the URL as ?err=
|
||||
httpdRedirect(connData, redir_url_buf);
|
||||
}
|
||||
|
||||
free(admin_backup);
|
||||
free(sysconf_backup);
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
@@ -168,14 +236,23 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg)
|
||||
|
||||
strcpy(buff, ""); // fallback
|
||||
|
||||
if (streq(token, "uart_baud")) {
|
||||
sprintf(buff, "%d", sysconf->uart_baudrate);
|
||||
if (streq(token, "pwlock")) {
|
||||
sprintf(buff, "%d", sysconf->pwlock);
|
||||
}
|
||||
else if (streq(token, "uart_parity")) {
|
||||
sprintf(buff, "%d", sysconf->uart_parity);
|
||||
else if (streq(token, "access_name")) {
|
||||
sprintf(buff, "%s", sysconf->access_name);
|
||||
}
|
||||
else if (streq(token, "uart_stopbits")) {
|
||||
sprintf(buff, "%d", sysconf->uart_stopbits);
|
||||
else if (streq(token, "def_access_name")) {
|
||||
sprintf(buff, "%s", DEF_ACCESS_NAME);
|
||||
}
|
||||
else if (streq(token, "def_access_pw")) {
|
||||
sprintf(buff, "%s", DEF_ACCESS_PW);
|
||||
}
|
||||
else if (streq(token, "def_admin_pw")) {
|
||||
sprintf(buff, "%s", DEFAULT_ADMIN_PW);
|
||||
}
|
||||
else if (streq(token, "overclock")) {
|
||||
sprintf(buff, "%d", sysconf->overclock);
|
||||
}
|
||||
|
||||
tplSend(connData, buff, -1);
|
||||
|
||||
+162
-47
@@ -9,10 +9,28 @@ Cgi/template routines for configuring non-wifi settings
|
||||
#include "screen.h"
|
||||
#include "helpers.h"
|
||||
#include "cgi_logging.h"
|
||||
#include "uart_driver.h"
|
||||
#include "serial.h"
|
||||
|
||||
#define SET_REDIR_SUC "/cfg/term"
|
||||
#define SET_REDIR_ERR SET_REDIR_SUC"?err="
|
||||
|
||||
/** convert hex number to int */
|
||||
static ICACHE_FLASH_ATTR u32
|
||||
decodehex(const char *buf) {
|
||||
u32 n = 0;
|
||||
char c;
|
||||
while ((c = *buf++) != 0) {
|
||||
if (c >= '0' && c <= '9') c -= '0';
|
||||
else if (c >= 'a' && c <= 'f') c -= 'a'-10;
|
||||
else if (c >= 'A' && c <= 'F') c -= 'A'-10;
|
||||
else c = 0;
|
||||
n *= 16;
|
||||
n += c;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Universal CGI endpoint to set Terminal params.
|
||||
*/
|
||||
@@ -25,11 +43,17 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
bool notify_screen_content = 0, notify_screen_labels = 0;
|
||||
|
||||
bool shall_clear_screen = false;
|
||||
bool shall_init_uart = false;
|
||||
|
||||
char *redir_url = redir_url_buf;
|
||||
redir_url += sprintf(redir_url, SET_REDIR_ERR);
|
||||
// we'll test if anything was printed by looking for \0 in failed_keys_buf
|
||||
|
||||
SystemConfigBundle *sysconf_backup = malloc(sizeof(SystemConfigBundle));
|
||||
TerminalConfigBundle *termconf_backup = malloc(sizeof(TerminalConfigBundle));
|
||||
memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle));
|
||||
memcpy(termconf_backup, termconf, sizeof(TerminalConfigBundle));
|
||||
|
||||
if (connData->conn == NULL) {
|
||||
//Connection aborted. Clean up.
|
||||
return HTTPD_CGI_DONE;
|
||||
@@ -37,63 +61,73 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
|
||||
// width and height must always go together so we can do max size validation
|
||||
if (GET_ARG("term_width")) {
|
||||
cgi_dbg("Default screen width: %s", buff);
|
||||
w = atoi(buff);
|
||||
if (w > 1) {
|
||||
if (GET_ARG("term_height")) {
|
||||
cgi_dbg("Default screen height: %s", buff);
|
||||
h = atoi(buff);
|
||||
if (h > 1) {
|
||||
if (w * h <= MAX_SCREEN_SIZE) {
|
||||
if (termconf->width != w || termconf->height != h) {
|
||||
termconf->width = w;
|
||||
termconf->height = h;
|
||||
shall_clear_screen = true; // this causes a notify
|
||||
}
|
||||
} else {
|
||||
cgi_warn("Bad dimensions: %d x %d (total %d)", w, h, w*h);
|
||||
redir_url += sprintf(redir_url, "term_width,term_height,");
|
||||
}
|
||||
} else {
|
||||
cgi_warn("Bad height: \"%s\"", buff);
|
||||
redir_url += sprintf(redir_url, "term_width,");
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
cgi_dbg("Default screen width: %s", buff);
|
||||
w = atoi(buff);
|
||||
if (w < 1) {
|
||||
cgi_warn("Bad width: \"%s\"", buff);
|
||||
redir_url += sprintf(redir_url, "term_width,");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!GET_ARG("term_height")) {
|
||||
cgi_warn("Missing height arg!");
|
||||
// this wont happen normally when the form is used
|
||||
redir_url += sprintf(redir_url, "term_width,term_height,");
|
||||
redir_url += sprintf(redir_url, "term_height,");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cgi_warn("Bad width: \"%s\"", buff);
|
||||
redir_url += sprintf(redir_url, "term_width,");
|
||||
}
|
||||
|
||||
cgi_dbg("Default screen height: %s", buff);
|
||||
h = atoi(buff);
|
||||
if (h < 1) {
|
||||
cgi_warn("Bad height: \"%s\"", buff);
|
||||
redir_url += sprintf(redir_url, "term_height,");
|
||||
break;
|
||||
}
|
||||
|
||||
if (w * h > MAX_SCREEN_SIZE) {
|
||||
cgi_warn("Bad dimensions: %d x %d (total %d)", w, h, w * h);
|
||||
redir_url += sprintf(redir_url, "term_width,term_height,");
|
||||
break;
|
||||
}
|
||||
|
||||
if (termconf->width != w || termconf->height != h) {
|
||||
termconf->width = w;
|
||||
termconf->height = h;
|
||||
shall_clear_screen = true; // this causes a notify
|
||||
}
|
||||
} while (0);
|
||||
}
|
||||
|
||||
if (GET_ARG("default_bg")) {
|
||||
cgi_dbg("Screen default BG: %s", buff);
|
||||
n = atoi(buff);
|
||||
if (n >= 0 && n < 16) {
|
||||
if (termconf->default_bg != n) {
|
||||
termconf->default_bg = (u8) n;
|
||||
shall_clear_screen = true;
|
||||
}
|
||||
|
||||
if (buff[0] == '#') {
|
||||
// decode hex
|
||||
n = decodehex(buff+1);
|
||||
n += 256;
|
||||
} else {
|
||||
cgi_warn("Bad color %s", buff);
|
||||
redir_url += sprintf(redir_url, "default_bg,");
|
||||
n = atoi(buff);
|
||||
}
|
||||
|
||||
if (termconf->default_bg != n) {
|
||||
termconf->default_bg = n; // this is current not sent through socket, no use to notify
|
||||
}
|
||||
}
|
||||
|
||||
if (GET_ARG("default_fg")) {
|
||||
cgi_dbg("Screen default FG: %s", buff);
|
||||
n = atoi(buff);
|
||||
if (n >= 0 && n < 16) {
|
||||
if (termconf->default_fg != n) {
|
||||
termconf->default_fg = (u8) n;
|
||||
shall_clear_screen = true;
|
||||
}
|
||||
|
||||
if (buff[0] == '#') {
|
||||
// decode hex
|
||||
n = decodehex(buff+1);
|
||||
n += 256;
|
||||
} else {
|
||||
cgi_warn("Bad color %s", buff);
|
||||
redir_url += sprintf(redir_url, "default_fg,");
|
||||
n = atoi(buff);
|
||||
}
|
||||
|
||||
if (termconf->default_fg != n) {
|
||||
termconf->default_fg = n; // this is current not sent through socket, no use to notify
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +207,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
if (GET_ARG("theme")) {
|
||||
cgi_dbg("Screen color theme: %s", buff);
|
||||
n = atoi(buff);
|
||||
if (n >= 0 && n <= 5) { // ALWAYS ADJUST WHEN ADDING NEW THEME!
|
||||
if (n >= 0) {
|
||||
termconf->theme = (u8) n;
|
||||
// this can't be notified, page must reload.
|
||||
} else {
|
||||
@@ -265,6 +299,59 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
}
|
||||
}
|
||||
|
||||
if (GET_ARG("uart_baud")) {
|
||||
cgi_dbg("Baud rate: %s", buff);
|
||||
int baud = atoi(buff);
|
||||
if (baud == BIT_RATE_300 ||
|
||||
baud == BIT_RATE_600 ||
|
||||
baud == BIT_RATE_1200 ||
|
||||
baud == BIT_RATE_2400 ||
|
||||
baud == BIT_RATE_4800 ||
|
||||
baud == BIT_RATE_9600 ||
|
||||
baud == BIT_RATE_19200 ||
|
||||
baud == BIT_RATE_38400 ||
|
||||
baud == BIT_RATE_57600 ||
|
||||
baud == BIT_RATE_74880 ||
|
||||
baud == BIT_RATE_115200 ||
|
||||
baud == BIT_RATE_230400 ||
|
||||
baud == BIT_RATE_460800 ||
|
||||
baud == BIT_RATE_921600 ||
|
||||
baud == BIT_RATE_1843200 ||
|
||||
baud == BIT_RATE_3686400) {
|
||||
sysconf->uart_baudrate = (u32) baud;
|
||||
shall_init_uart = true;
|
||||
} else {
|
||||
cgi_warn("Bad baud rate %s", buff);
|
||||
redir_url += sprintf(redir_url, "uart_baud,");
|
||||
}
|
||||
}
|
||||
|
||||
if (GET_ARG("uart_parity")) {
|
||||
cgi_dbg("Parity: %s", buff);
|
||||
int parity = atoi(buff);
|
||||
if (parity >= 0 && parity <= 2) {
|
||||
sysconf->uart_parity = (UartParityMode) parity;
|
||||
shall_init_uart = true;
|
||||
} else {
|
||||
cgi_warn("Bad parity %s", buff);
|
||||
redir_url += sprintf(redir_url, "uart_parity,");
|
||||
}
|
||||
}
|
||||
|
||||
if (GET_ARG("uart_stopbits")) {
|
||||
cgi_dbg("Stop bits: %s", buff);
|
||||
int stopbits = atoi(buff);
|
||||
if (stopbits >= 1 && stopbits <= 3) {
|
||||
sysconf->uart_stopbits = (UartStopBitsNum) stopbits;
|
||||
shall_init_uart = true;
|
||||
} else {
|
||||
cgi_warn("Bad stopbits %s", buff);
|
||||
redir_url += sprintf(redir_url, "uart_stopbits,");
|
||||
}
|
||||
}
|
||||
|
||||
(void)redir_url;
|
||||
|
||||
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
|
||||
// All was OK
|
||||
info("Set term params - success, saving...");
|
||||
@@ -277,6 +364,10 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
terminal_apply_settings_noclear();
|
||||
}
|
||||
|
||||
if (shall_init_uart) {
|
||||
serialInit();
|
||||
}
|
||||
|
||||
if (notify_screen_content) {
|
||||
screen_notifyChange(CHANGE_CONTENT);
|
||||
}
|
||||
@@ -285,12 +376,19 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
screen_notifyChange(CHANGE_LABELS);
|
||||
}
|
||||
|
||||
httpdRedirect(connData, SET_REDIR_SUC);
|
||||
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
|
||||
} else {
|
||||
cgi_warn("Some settings did not validate, asking for correction");
|
||||
|
||||
memcpy(sysconf, sysconf_backup, sizeof(SystemConfigBundle));
|
||||
memcpy(termconf, termconf_backup, sizeof(TerminalConfigBundle));
|
||||
|
||||
// Some errors, appended to the URL as ?err=
|
||||
httpdRedirect(connData, redir_url_buf);
|
||||
}
|
||||
|
||||
free(sysconf_backup);
|
||||
free(termconf_backup);
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
@@ -346,10 +444,18 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg)
|
||||
sprintf(buff, "%d", termconf->theme);
|
||||
}
|
||||
else if (streq(token, "default_bg")) {
|
||||
sprintf(buff, "%d", termconf->default_bg);
|
||||
if (termconf->default_bg < 256) {
|
||||
sprintf(buff, "%d", termconf->default_bg);
|
||||
} else {
|
||||
sprintf(buff, "#%06X", termconf->default_bg - 256);
|
||||
}
|
||||
}
|
||||
else if (streq(token, "default_fg")) {
|
||||
sprintf(buff, "%d", termconf->default_fg);
|
||||
if (termconf->default_fg < 256) {
|
||||
sprintf(buff, "%d", termconf->default_fg);
|
||||
} else {
|
||||
sprintf(buff, "#%06X", termconf->default_fg - 256);
|
||||
}
|
||||
}
|
||||
else if (streq(token, "cursor_shape")) {
|
||||
sprintf(buff, "%d", termconf->cursor_shape);
|
||||
@@ -357,6 +463,15 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg)
|
||||
else if (streq(token, "term_title")) {
|
||||
strncpy_safe(buff, termconf->title, BUFLEN);
|
||||
}
|
||||
else if (streq(token, "uart_baud")) {
|
||||
sprintf(buff, "%d", sysconf->uart_baudrate);
|
||||
}
|
||||
else if (streq(token, "uart_parity")) {
|
||||
sprintf(buff, "%d", sysconf->uart_parity);
|
||||
}
|
||||
else if (streq(token, "uart_stopbits")) {
|
||||
sprintf(buff, "%d", sysconf->uart_stopbits);
|
||||
}
|
||||
else {
|
||||
for (int btn_i = 1; btn_i <= TERM_BTN_COUNT; btn_i++) {
|
||||
sprintf(buff2, "btn%d", btn_i);
|
||||
|
||||
+15
-1
@@ -355,6 +355,11 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
WiFiConfigBundle *wificonf_backup = malloc(sizeof(WiFiConfigBundle));
|
||||
WiFiConfChangeFlags *wcf_backup = malloc(sizeof(WiFiConfChangeFlags));
|
||||
memcpy(wificonf_backup, wificonf, sizeof(WiFiConfigBundle));
|
||||
memcpy(wcf_backup, &wifi_change_flags, sizeof(WiFiConfChangeFlags));
|
||||
|
||||
bool sta_turned_on = false;
|
||||
bool sta_ssid_pw_changed = false;
|
||||
|
||||
@@ -502,6 +507,8 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
|
||||
}
|
||||
}
|
||||
|
||||
(void)redir_url;
|
||||
|
||||
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
|
||||
// All was OK
|
||||
cgi_info("Set WiFi params - success, applying in 2000 ms");
|
||||
@@ -528,13 +535,20 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
|
||||
httpdRedirect(connData, "/cfg/wifi/connecting");
|
||||
}
|
||||
else {
|
||||
httpdRedirect(connData, SET_REDIR_SUC);
|
||||
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
|
||||
}
|
||||
} else {
|
||||
cgi_warn("Some WiFi settings did not validate, asking for correction");
|
||||
|
||||
memcpy(wificonf, wificonf_backup, sizeof(WiFiConfigBundle));
|
||||
memcpy(&wifi_change_flags, wcf_backup, sizeof(WiFiConfChangeFlags));
|
||||
|
||||
// Some errors, appended to the URL as ?err=
|
||||
httpdRedirect(connData, redir_url_buf);
|
||||
}
|
||||
|
||||
free(wificonf_backup);
|
||||
free(wcf_backup);
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
|
||||
+25
-7
@@ -97,19 +97,27 @@ compute_checksum(AppConfigBundle *bundle)
|
||||
return calculateCRC32((uint8_t *) bundle, sizeof(AppConfigBundle) - 4);
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR
|
||||
set_admin_block_defaults(void)
|
||||
{
|
||||
persist_info("[Persist] Initing admin config block");
|
||||
strcpy(persist.admin.pw, DEFAULT_ADMIN_PW);
|
||||
persist.admin.version = ADMINCONF_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load, verify and apply persistent config
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
persist_load(void)
|
||||
{
|
||||
persist_info("[Persist] Loading stored settings from FLASH...");
|
||||
persist_info("[Persist] Loading settings from FLASH...");
|
||||
|
||||
persist_dbg("AppConfigBundle memory map:");
|
||||
persist_dbg("> WiFiConfigBundle at %4d (error %2d)", wconf_at, wconf_at - 0);
|
||||
persist_dbg("> SystemConfigBundle at %4d (error %2d)", sconf_at, sconf_at - WIFICONF_SIZE);
|
||||
persist_dbg("> TerminalConfigBundle at %4d (error %2d)", tconf_at, tconf_at - WIFICONF_SIZE - SYSCONF_SIZE);
|
||||
persist_dbg("> Checksum at %4d (error %2d)", cksum_at, cksum_at - (APPCONF_SIZE - 4));
|
||||
persist_dbg("Persist memory map:");
|
||||
persist_dbg("> wifi at %4d (error %2d)", wconf_at, wconf_at - 0);
|
||||
persist_dbg("> sys at %4d (error %2d)", sconf_at, sconf_at - WIFICONF_SIZE);
|
||||
persist_dbg("> term at %4d (error %2d)", tconf_at, tconf_at - WIFICONF_SIZE - SYSCONF_SIZE);
|
||||
persist_dbg("> cksum at %4d (error %2d)", cksum_at, cksum_at - (APPCONF_SIZE - 4));
|
||||
persist_dbg("> Total size = %d bytes (error %d)", sizeof(AppConfigBundle), APPCONF_SIZE - sizeof(AppConfigBundle));
|
||||
|
||||
bool hard_reset = false;
|
||||
@@ -120,7 +128,8 @@ persist_load(void)
|
||||
// Verify checksums
|
||||
if (hard_reset ||
|
||||
(compute_checksum(&persist.defaults) != persist.defaults.checksum) ||
|
||||
(compute_checksum(&persist.current) != persist.current.checksum)) {
|
||||
(compute_checksum(&persist.current) != persist.current.checksum) ||
|
||||
(persist.admin.version != 0 && (calculateCRC32((uint8_t *) &persist.admin, sizeof(AdminConfigBlock) - 4) != persist.admin.checksum))) {
|
||||
error("[Persist] Checksum verification: FAILED");
|
||||
hard_reset = true;
|
||||
} else {
|
||||
@@ -135,10 +144,18 @@ persist_load(void)
|
||||
|
||||
// write them also as defaults
|
||||
memcpy(&persist.defaults, &persist.current, sizeof(AppConfigBundle));
|
||||
|
||||
// reset admin pw
|
||||
set_admin_block_defaults();
|
||||
persist_store();
|
||||
|
||||
// this also stores them to flash and applies to modules
|
||||
} else {
|
||||
if (persist.admin.version == 0) {
|
||||
set_admin_block_defaults();
|
||||
persist_store();
|
||||
}
|
||||
|
||||
apply_live_settings();
|
||||
}
|
||||
|
||||
@@ -153,6 +170,7 @@ persist_store(void)
|
||||
// Update checksums before write
|
||||
persist.current.checksum = compute_checksum(&persist.current);
|
||||
persist.defaults.checksum = compute_checksum(&persist.defaults);
|
||||
persist.admin.checksum = calculateCRC32((uint8_t *) &persist.admin, sizeof(AdminConfigBlock) - 4);
|
||||
|
||||
if (!system_param_save_with_protect(PERSIST_SECTOR_ID, &persist, sizeof(PersistBlock))) {
|
||||
error("[Persist] Store to flash failed!");
|
||||
|
||||
+16
-3
@@ -14,11 +14,13 @@
|
||||
#include "screen.h"
|
||||
#include "syscfg.h"
|
||||
|
||||
#define DEFAULT_ADMIN_PW "adminpw"
|
||||
|
||||
// Changing this could be used to force-erase the config area
|
||||
// after a firmware upgrade
|
||||
#define CHECKSUM_SALT 3
|
||||
#define CHECKSUM_SALT 6
|
||||
|
||||
#define APPCONF_SIZE 2048
|
||||
#define APPCONF_SIZE 1900
|
||||
|
||||
/** Struct for current or default settings */
|
||||
typedef struct { // the entire block should be 1024 bytes long (for compatibility across upgrades)
|
||||
@@ -41,7 +43,7 @@ typedef struct { // the entire block should be 1024 bytes long (for compatibilit
|
||||
// it grew to a different memory area.
|
||||
uint8_t _filler_end[
|
||||
APPCONF_SIZE
|
||||
- sizeof(uint32_t) // checksum
|
||||
- 4 // checksum
|
||||
- WIFICONF_SIZE
|
||||
- SYSCONF_SIZE
|
||||
- TERMCONF_SIZE
|
||||
@@ -50,10 +52,21 @@ typedef struct { // the entire block should be 1024 bytes long (for compatibilit
|
||||
uint32_t checksum; // computed before write and tested on load. If it doesn't match, values are reset to hard defaults.
|
||||
} AppConfigBundle;
|
||||
|
||||
#define ADMINCONF_VERSION 1
|
||||
#define ADMINCONF_SIZE 256
|
||||
|
||||
typedef struct {
|
||||
u8 version;
|
||||
char pw[64];
|
||||
uint8_t _filler[ADMINCONF_SIZE-64-4];
|
||||
uint32_t checksum;
|
||||
} AdminConfigBlock;
|
||||
|
||||
/** This is the entire data block stored in FLASH */
|
||||
typedef struct {
|
||||
AppConfigBundle defaults; // defaults are stored here
|
||||
AppConfigBundle current; // active settings adjusted by the user
|
||||
AdminConfigBlock admin;
|
||||
} PersistBlock;
|
||||
|
||||
// Persist holds the data currently loaded from the flash
|
||||
|
||||
+72
-22
@@ -12,12 +12,79 @@
|
||||
#include "cgi_network.h"
|
||||
#include "cgi_term_cfg.h"
|
||||
#include "cgi_persist.h"
|
||||
#include "syscfg.h"
|
||||
#include "persist.h"
|
||||
|
||||
#define WIFI_PROTECT 0
|
||||
#define WIFI_AUTH_NAME "wifi"
|
||||
#define WIFI_AUTH_PASS "nicitel"
|
||||
/**
|
||||
* Password for WiFi config
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen)
|
||||
{
|
||||
if (no == 0) {
|
||||
os_strcpy(user, sysconf->access_name);
|
||||
os_strcpy(pass, sysconf->access_pw);
|
||||
return 1;
|
||||
}
|
||||
if (no == 1) {
|
||||
os_strcpy(user, "admin");
|
||||
os_strcpy(pass, persist.admin.pw);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen);
|
||||
httpd_cgi_state ICACHE_FLASH_ATTR cgiOptionalPwLock(HttpdConnData *connData)
|
||||
{
|
||||
bool protect = false;
|
||||
|
||||
http_dbg("Route, %s, pwlock=%d", connData->url, sysconf->pwlock);
|
||||
|
||||
switch (sysconf->pwlock) {
|
||||
case PWLOCK_ALL:
|
||||
protect = true;
|
||||
break;
|
||||
|
||||
case PWLOCK_SETTINGS_NOTERM:
|
||||
protect = strstarts(connData->url, "/cfg") && !strstarts(connData->url, "/cfg/term");
|
||||
break;
|
||||
|
||||
case PWLOCK_SETTINGS_ALL:
|
||||
protect = strstarts(connData->url, "/cfg");
|
||||
break;
|
||||
|
||||
case PWLOCK_MENUS:
|
||||
protect = strstarts(connData->url, "/cfg") || strstarts(connData->url, "/about") || strstarts(connData->url, "/help");
|
||||
break;
|
||||
|
||||
default:
|
||||
case PWLOCK_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
// pages outside the normal scope
|
||||
|
||||
if (sysconf->pwlock > PWLOCK_NONE) {
|
||||
if (strstarts(connData->url, "/system/reset")) protect = true;
|
||||
}
|
||||
|
||||
if (sysconf->pwlock > PWLOCK_SETTINGS_NOTERM) {
|
||||
if (strstarts(connData->url, "/system/cls")) protect = true;
|
||||
}
|
||||
|
||||
if (sysconf->access_pw[0] == 0) {
|
||||
http_dbg("Access PW is nil, no protection.");
|
||||
protect = false;
|
||||
}
|
||||
|
||||
if (protect) {
|
||||
http_dbg("Page is protected!");
|
||||
connData->cgiArg = wifiPassFn;
|
||||
return authBasic(connData);
|
||||
} else {
|
||||
http_dbg("Not protected");
|
||||
return HTTPD_CGI_NOTFOUND;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Application routes
|
||||
@@ -25,6 +92,7 @@ static int wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen,
|
||||
const HttpdBuiltInUrl routes[] ESP_CONST_DATA = {
|
||||
// redirect func for the captive portal
|
||||
ROUTE_CGI_ARG("*", cgiRedirectApClientToHostname, "esp-terminal.ap"),
|
||||
ROUTE_CGI("*", cgiOptionalPwLock),
|
||||
|
||||
// --- Web pages ---
|
||||
ROUTE_TPL_FILE("/", tplScreen, "/term.tpl"),
|
||||
@@ -39,10 +107,6 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = {
|
||||
ROUTE_CGI("/system/ping/?", cgiPing),
|
||||
ROUTE_CGI("/system/cls/?", cgiResetScreen),
|
||||
|
||||
// --- WiFi config --- (TODO make this conditional and configurable)
|
||||
#if WIFI_PROTECT
|
||||
ROUTE_AUTH("/wifi*", wifiPassFn),
|
||||
#endif
|
||||
ROUTE_REDIRECT("/cfg/?", "/cfg/wifi"),
|
||||
|
||||
ROUTE_TPL_FILE("/cfg/wifi/?", tplWlan, "/cfg_wifi.tpl"),
|
||||
@@ -67,17 +131,3 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = {
|
||||
ROUTE_END(),
|
||||
};
|
||||
|
||||
// --- Wifi password protection ---
|
||||
|
||||
/**
|
||||
* Password for WiFi config
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen)
|
||||
{
|
||||
if (no == 0) {
|
||||
os_strcpy(user, WIFI_AUTH_NAME);
|
||||
os_strcpy(pass, WIFI_AUTH_PASS);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
+360
-253
@@ -8,6 +8,7 @@
|
||||
#include "jstring.h"
|
||||
#include "character_sets.h"
|
||||
#include "utf8.h"
|
||||
#include "uart_buffer.h"
|
||||
|
||||
TerminalConfigBundle * const termconf = &persist.current.termconf;
|
||||
TerminalConfigBundle termconf_live;
|
||||
@@ -20,11 +21,6 @@ static void utf8_remap(char* out, char g, char charset);
|
||||
#define W termconf_live.width
|
||||
#define H termconf_live.height
|
||||
|
||||
/**
|
||||
* Highest permissible value of the color attribute
|
||||
*/
|
||||
#define COLOR_MAX 15
|
||||
|
||||
/**
|
||||
* Screen cell data type (16 bits)
|
||||
*/
|
||||
@@ -32,7 +28,7 @@ typedef struct __attribute__((packed)) {
|
||||
UnicodeCacheRef symbol : 8;
|
||||
Color fg;
|
||||
Color bg;
|
||||
u8 attrs;
|
||||
CellAttrs attrs;
|
||||
} Cell;
|
||||
|
||||
/**
|
||||
@@ -64,8 +60,8 @@ static struct {
|
||||
char last_char[4];
|
||||
} scr;
|
||||
|
||||
#define R0 scr.vm0
|
||||
#define R1 scr.vm1
|
||||
#define TOP scr.vm0
|
||||
#define BTM scr.vm1
|
||||
#define RH (scr.vm1 - scr.vm0 + 1)
|
||||
// horizontal edges - will be useful if horizontal margin is implemented
|
||||
//#define C0 0
|
||||
@@ -78,9 +74,9 @@ typedef struct {
|
||||
bool hanging; //!< xenl state - cursor half-wrapped
|
||||
|
||||
/* SGR */
|
||||
bool inverse; //!< not in attrs bc it's applied server-side (not sent to browser)
|
||||
bool conceal; //!< similar to inverse, causes all to be replaced by SP
|
||||
u8 attrs;
|
||||
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
|
||||
Color bg; //!< Background color for writing
|
||||
|
||||
@@ -158,7 +154,7 @@ static volatile int notifyLock = 0;
|
||||
if (cursor.hanging && cursor.x != W-1) cursor.hanging = false; \
|
||||
} while(false)
|
||||
|
||||
#define cursor_inside_region() (cursor.y >= R0 && cursor.y <= R1)
|
||||
#define cursor_inside_region() (cursor.y >= TOP && cursor.y <= BTM)
|
||||
|
||||
//region --- Settings ---
|
||||
|
||||
@@ -207,45 +203,12 @@ terminal_apply_settings_noclear(void)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
// Migrate to v1
|
||||
if (termconf->config_version < 1) {
|
||||
persist_dbg("termconf: Updating to version 1");
|
||||
termconf->display_cooldown_ms = SCR_DEF_DISPLAY_COOLDOWN_MS;
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
// Migrate to v2
|
||||
if (termconf->config_version < 2) {
|
||||
persist_dbg("termconf: Updating to version 2");
|
||||
termconf->loopback = 0;
|
||||
termconf->show_config_links = 1;
|
||||
termconf->show_buttons = 1;
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
// Migrate to v3
|
||||
if (termconf->config_version < 3) {
|
||||
persist_dbg("termconf: Updating to version 3");
|
||||
for(int i=1; i <= TERM_BTN_COUNT; i++) {
|
||||
sprintf(termconf->btn_msg[i-1], "%c", i);
|
||||
}
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
// Migrate to v4
|
||||
if (termconf->config_version < 4) {
|
||||
persist_dbg("termconf: Updating to version 4");
|
||||
termconf->cursor_shape = CURSOR_BLOCK_BL;
|
||||
termconf->crlf_mode = false;
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
// Migrate to v5
|
||||
if (termconf->config_version < 4) {
|
||||
persist_dbg("termconf: Updating to version 5");
|
||||
termconf->want_all_fn = 0;
|
||||
changed = 1;
|
||||
}
|
||||
// // 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;
|
||||
// }
|
||||
|
||||
termconf->config_version = TERMCONF_VERSION;
|
||||
|
||||
@@ -285,18 +248,7 @@ screen_init(void)
|
||||
{
|
||||
if(DEBUG_HEAP) dbg("Screen buffer size = %d bytes", sizeof(screen));
|
||||
|
||||
NOTIFY_LOCK();
|
||||
Cell sample;
|
||||
sample.symbol = ' ';
|
||||
sample.fg = termconf->default_fg;
|
||||
sample.bg = termconf->default_bg;
|
||||
sample.attrs = 0;
|
||||
for (unsigned int i = 0; i < MAX_SCREEN_SIZE; i++) {
|
||||
memcpy(&screen[i], &sample, sizeof(Cell));
|
||||
}
|
||||
|
||||
screen_reset();
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,16 +277,13 @@ screen_reset_on_resize(void)
|
||||
ansi_dbg("Screen partial reset due to resize");
|
||||
NOTIFY_LOCK();
|
||||
|
||||
cursor.x = 0;
|
||||
cursor.y = 0;
|
||||
cursor.hanging = false;
|
||||
cursor_reset();
|
||||
|
||||
scr.vm0 = 0;
|
||||
scr.vm1 = H-1;
|
||||
|
||||
// size is left unchanged
|
||||
screen_clear(CLEAR_ALL);
|
||||
unicode_cache_clear();
|
||||
screen_clear(CLEAR_ALL); // also clears utf cache
|
||||
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
@@ -345,25 +294,17 @@ screen_reset_on_resize(void)
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_reset_sgr(void)
|
||||
{
|
||||
cursor.fg = termconf->default_fg;
|
||||
cursor.bg = termconf->default_bg;
|
||||
cursor.fg = 0;
|
||||
cursor.bg = 0;
|
||||
cursor.attrs = 0;
|
||||
cursor.inverse = false;
|
||||
cursor.conceal = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the screen - called by ESC c
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_reset(void)
|
||||
static void ICACHE_FLASH_ATTR
|
||||
screen_reset_do(bool size, bool labels)
|
||||
{
|
||||
ansi_dbg("Screen reset.");
|
||||
NOTIFY_LOCK();
|
||||
|
||||
cursor_reset();
|
||||
unicode_cache_clear();
|
||||
|
||||
// DECopts
|
||||
scr.cursor_visible = true;
|
||||
scr.insert_mode = false;
|
||||
@@ -377,23 +318,41 @@ screen_reset(void)
|
||||
termconf_live.crlf_mode = termconf->crlf_mode;
|
||||
scr.reverse_video = false;
|
||||
|
||||
scr.vm0 = 0;
|
||||
scr.vm1 = H-1;
|
||||
|
||||
state_backup.alternate_active = false;
|
||||
|
||||
mouse_tracking.encoding = MTE_SIMPLE;
|
||||
mouse_tracking.focus_tracking = false;
|
||||
mouse_tracking.mode = MTM_NONE;
|
||||
|
||||
// size is left unchanged
|
||||
screen_clear(CLEAR_ALL);
|
||||
if (size) {
|
||||
W = termconf->width;
|
||||
H = termconf->height;
|
||||
}
|
||||
|
||||
scr.vm0 = 0;
|
||||
scr.vm1 = H - 1;
|
||||
cursor_reset();
|
||||
screen_clear(CLEAR_ALL); // also clears utf cache
|
||||
|
||||
// Set initial tabstops
|
||||
for (int i = 0; i < TABSTOP_WORDS; i++) {
|
||||
scr.tab_stops[i] = 0x80808080;
|
||||
}
|
||||
|
||||
if (labels) {
|
||||
strcpy(termconf_live.title, termconf->title);
|
||||
|
||||
for (int i = 1; i <= TERM_BTN_COUNT; i++) {
|
||||
strcpy(termconf_live.btn[i], termconf->btn[i]);
|
||||
strcpy(termconf_live.btn_msg[i], termconf->btn_msg[i]);
|
||||
}
|
||||
|
||||
termconf_live.show_buttons = termconf->show_buttons;
|
||||
termconf_live.show_config_links = termconf->show_config_links;
|
||||
|
||||
screen_notifyChange(CHANGE_LABELS);
|
||||
}
|
||||
|
||||
// initial values in the save buffer in case of receiving restore without storing first
|
||||
opt_backup.cursors_alt_mode = scr.cursors_alt_mode;
|
||||
opt_backup.reverse_video = scr.reverse_video;
|
||||
@@ -412,6 +371,17 @@ screen_reset(void)
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the screen - called by ESC c
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_reset(void)
|
||||
{
|
||||
ansi_dbg("Screen reset.");
|
||||
screen_reset_do(true, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Swap screen buffer / state
|
||||
* this is CSI ? 47/1047/1049 h/l
|
||||
@@ -583,27 +553,163 @@ screen_tab_reverse(int count)
|
||||
|
||||
/**
|
||||
* Clear range, inclusive
|
||||
*
|
||||
* @param from - starting absolute position
|
||||
* @param to - ending absolute position
|
||||
* @param clear_utf - release any encountered utf8
|
||||
*/
|
||||
static void ICACHE_FLASH_ATTR
|
||||
clear_range(unsigned int from, unsigned int to)
|
||||
clear_range_do(unsigned int from, unsigned int to, bool clear_utf)
|
||||
{
|
||||
if (to >= W*H) to = W*H-1;
|
||||
Color fg = (cursor.inverse) ? cursor.bg : cursor.fg;
|
||||
Color bg = (cursor.inverse) ? cursor.fg : cursor.bg;
|
||||
|
||||
Cell sample;
|
||||
sample.symbol = ' ';
|
||||
sample.fg = fg;
|
||||
sample.bg = bg;
|
||||
sample.attrs = 0;
|
||||
sample.fg = cursor.fg;
|
||||
sample.bg = cursor.bg;
|
||||
// we discard all attributes except color-set flags
|
||||
sample.attrs = (CellAttrs) (cursor.attrs & (ATTR_FG | ATTR_BG));
|
||||
|
||||
for (unsigned int i = from; i <= to; i++) {
|
||||
UnicodeCacheRef symbol = screen[i].symbol;
|
||||
if (IS_UNICODE_CACHE_REF(symbol)) unicode_cache_remove(symbol);
|
||||
if (clear_utf) {
|
||||
UnicodeCacheRef symbol = screen[i].symbol;
|
||||
if (IS_UNICODE_CACHE_REF(symbol)) unicode_cache_remove(symbol);
|
||||
}
|
||||
memcpy(&screen[i], &sample, sizeof(Cell));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear range, inclusive, freeing any encountered UTF8 from the cache
|
||||
*
|
||||
* @param from - starting absolute position
|
||||
* @param to - ending absolute position
|
||||
*/
|
||||
static inline void ICACHE_FLASH_ATTR
|
||||
clear_range_utf(unsigned int from, unsigned int to)
|
||||
{
|
||||
clear_range_do(from, to, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear range, inclusive. Do not release utf characters
|
||||
*
|
||||
* @param from - starting absolute position
|
||||
* @param to - ending absolute position
|
||||
*/
|
||||
static inline void ICACHE_FLASH_ATTR
|
||||
clear_range_noutf(unsigned int from, unsigned int to)
|
||||
{
|
||||
clear_range_do(from, to, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a utf8 reference character in a cell
|
||||
*
|
||||
* @param row
|
||||
* @param col
|
||||
*/
|
||||
static inline void ICACHE_FLASH_ATTR
|
||||
utf_free_cell(int row, int col)
|
||||
{
|
||||
//dbg("free cell (row %d) %d", row, col);
|
||||
UnicodeCacheRef symbol = screen[row * W + col].symbol;
|
||||
if (IS_UNICODE_CACHE_REF(symbol))
|
||||
unicode_cache_remove(symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Back-up utf8 reference in a cell (i.e. increment the counter,
|
||||
* so 1 subsequent free has no effect)
|
||||
*
|
||||
* @param row
|
||||
* @param col
|
||||
*/
|
||||
static inline void ICACHE_FLASH_ATTR
|
||||
utf_backup_cell(int row, int col)
|
||||
{
|
||||
//dbg("backup cell (row %d) %d", row, col);
|
||||
UnicodeCacheRef symbol = screen[row * W + col].symbol;
|
||||
if (IS_UNICODE_CACHE_REF(symbol))
|
||||
unicode_cache_inc(symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a cell within a row
|
||||
* @param row - row to work on
|
||||
* @param dest_col - destination column
|
||||
* @param src_col - source column
|
||||
*/
|
||||
static inline void ICACHE_FLASH_ATTR
|
||||
copy_cell(int row, int dest_col, int src_col)
|
||||
{
|
||||
//dbg("copy cell (row %d) %d -> %d", row, src_col, dest_col);
|
||||
memcpy(screen+row*W+dest_col, screen+row*W+src_col, sizeof(Cell));
|
||||
}
|
||||
|
||||
/**
|
||||
* Free all utf8 on a row
|
||||
*
|
||||
* @param row
|
||||
*/
|
||||
static inline void ICACHE_FLASH_ATTR
|
||||
utf_free_row(int row)
|
||||
{
|
||||
//dbg("free row %d", row);
|
||||
for (int col = 0; col < W; col++) {
|
||||
utf_free_cell(row, col);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Back-up all utf8 refs on a row
|
||||
*
|
||||
* @param row
|
||||
*/
|
||||
static inline void ICACHE_FLASH_ATTR
|
||||
utf_backup_row(int row)
|
||||
{
|
||||
//dbg("backup row %d", row);
|
||||
for (int col = 0; col < W; col++) {
|
||||
utf_backup_cell(row, col);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a row
|
||||
*
|
||||
* @param dest - destination row number (0-based)
|
||||
* @param src - source row number (0-based)
|
||||
*/
|
||||
static inline void ICACHE_FLASH_ATTR
|
||||
copy_row(int dest, int src)
|
||||
{
|
||||
//dbg("copy row %d -> %d", src, dest);
|
||||
memcpy(screen + dest * W, screen + src * W, sizeof(Cell) * W);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a row, do nothing with the utf8 cache
|
||||
*
|
||||
* @param row
|
||||
*/
|
||||
static inline void ICACHE_FLASH_ATTR
|
||||
clear_row_noutf(int row)
|
||||
{
|
||||
clear_range_noutf(row * W, (row + 1) * W - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a row, freeing any utf8 refs
|
||||
*
|
||||
* @param row
|
||||
*/
|
||||
static inline void ICACHE_FLASH_ATTR
|
||||
clear_row_utf(int row)
|
||||
{
|
||||
clear_range_utf(row * W, (row + 1) * W - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear screen area
|
||||
*/
|
||||
@@ -614,15 +720,17 @@ screen_clear(ClearMode mode)
|
||||
NOTIFY_LOCK();
|
||||
switch (mode) {
|
||||
case CLEAR_ALL:
|
||||
clear_range(0, W * H - 1);
|
||||
unicode_cache_clear();
|
||||
clear_range_noutf(0, W * H - 1);
|
||||
scr.last_char[0] = 0;
|
||||
break;
|
||||
|
||||
case CLEAR_FROM_CURSOR:
|
||||
clear_range((cursor.y * W) + cursor.x, W * H - 1);
|
||||
clear_range_utf((cursor.y * W) + cursor.x, W * H - 1);
|
||||
break;
|
||||
|
||||
case CLEAR_TO_CURSOR:
|
||||
clear_range(0, (cursor.y * W) + cursor.x);
|
||||
clear_range_utf(0, (cursor.y * W) + cursor.x);
|
||||
break;
|
||||
}
|
||||
NOTIFY_DONE();
|
||||
@@ -637,15 +745,15 @@ screen_clear_line(ClearMode mode)
|
||||
NOTIFY_LOCK();
|
||||
switch (mode) {
|
||||
case CLEAR_ALL:
|
||||
clear_range(cursor.y * W, (cursor.y + 1) * W - 1);
|
||||
clear_row_utf(cursor.y);
|
||||
break;
|
||||
|
||||
case CLEAR_FROM_CURSOR:
|
||||
clear_range(cursor.y * W + cursor.x, (cursor.y + 1) * W - 1);
|
||||
clear_range_utf(cursor.y * W + cursor.x, (cursor.y + 1) * W - 1);
|
||||
break;
|
||||
|
||||
case CLEAR_TO_CURSOR:
|
||||
clear_range(cursor.y * W, cursor.y * W + cursor.x);
|
||||
clear_range_utf(cursor.y * W, cursor.y * W + cursor.x);
|
||||
break;
|
||||
}
|
||||
NOTIFY_DONE();
|
||||
@@ -654,14 +762,13 @@ screen_clear_line(ClearMode mode)
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_clear_in_line(unsigned int count)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
if (cursor.x + count >= W) {
|
||||
screen_clear_line(CLEAR_FROM_CURSOR);
|
||||
} else {
|
||||
clear_range_utf(cursor.y * W + cursor.x, cursor.y * W + cursor.x + count - 1);
|
||||
}
|
||||
else {
|
||||
NOTIFY_LOCK();
|
||||
clear_range(cursor.y * W + cursor.x, cursor.y * W + cursor.x + count - 1);
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
@@ -670,24 +777,24 @@ screen_insert_lines(unsigned int lines)
|
||||
if (!cursor_inside_region()) return; // can't insert if not inside region
|
||||
NOTIFY_LOCK();
|
||||
|
||||
// FIXME remove cleared & overwritten cells from unicode cache!
|
||||
|
||||
// shift the following lines
|
||||
int targetStart = cursor.y + lines;
|
||||
if (targetStart > R1) {
|
||||
targetStart = R1-1;
|
||||
if (targetStart > BTM) {
|
||||
clear_range_utf(cursor.y*W, (BTM+1)*W-1);
|
||||
} else {
|
||||
// do the moving
|
||||
for (int i = R1; i >= targetStart; i--) {
|
||||
memcpy(screen+i*W, screen+(i-lines)*W, sizeof(Cell)*W);
|
||||
for (int i = BTM; i >= targetStart; i--) {
|
||||
utf_free_row(i); // release old characters
|
||||
copy_row(i, i - lines);
|
||||
if (i != targetStart) utf_backup_row(i);
|
||||
}
|
||||
}
|
||||
|
||||
// clear the first line
|
||||
screen_clear_line(CLEAR_ALL);
|
||||
// copy it to the rest of the cleared region
|
||||
for (int i = cursor.y+1; i < targetStart; i++) {
|
||||
memcpy(screen+i*W, screen+cursor.y*W, sizeof(Cell)*W);
|
||||
// clear the first line
|
||||
clear_row_noutf(cursor.y);
|
||||
// copy it to the rest of the cleared region
|
||||
for (int i = cursor.y+1; i < targetStart; i++) {
|
||||
copy_row(i, cursor.y);
|
||||
}
|
||||
}
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
@@ -698,21 +805,22 @@ screen_delete_lines(unsigned int lines)
|
||||
if (!cursor_inside_region()) return; // can't delete if not inside region
|
||||
NOTIFY_LOCK();
|
||||
|
||||
// FIXME remove cleared & overwritten cells from unicode cache!
|
||||
|
||||
// shift lines up
|
||||
int targetEnd = R1 - lines - 1;
|
||||
if (targetEnd <= cursor.y) {
|
||||
targetEnd = cursor.y;
|
||||
int movedBlockEnd = BTM - lines ;
|
||||
if (movedBlockEnd <= cursor.y) {
|
||||
// clear the entire rest of the screen
|
||||
movedBlockEnd = cursor.y;
|
||||
clear_range_utf(movedBlockEnd*W, (BTM+1)*W-1);
|
||||
} else {
|
||||
// do the moving
|
||||
for (int i = cursor.y; i <= targetEnd; i++) {
|
||||
memcpy(screen+i*W, screen+(i+lines)*W, sizeof(Cell)*W);
|
||||
// move some lines up, clear the rest
|
||||
for (int i = cursor.y; i <= movedBlockEnd; i++) {
|
||||
utf_free_row(i);
|
||||
copy_row(i, i+lines);
|
||||
if (i != movedBlockEnd) utf_backup_row(i);
|
||||
}
|
||||
clear_range_noutf((movedBlockEnd+1)*W, (BTM+1)*W-1);
|
||||
}
|
||||
|
||||
// clear the rest
|
||||
clear_range(targetEnd*W, W*R1);
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
@@ -721,19 +829,21 @@ screen_insert_characters(unsigned int count)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
|
||||
// FIXME remove cleared & overwritten cells from unicode cache!
|
||||
// shove rest of the line to the right
|
||||
|
||||
int targetStart = cursor.x + count;
|
||||
if (targetStart >= W) {
|
||||
targetStart = W-1;
|
||||
// all rest of line was cleared
|
||||
clear_range_utf(cursor.y * W + cursor.x, (cursor.y + 1) * W - 1);
|
||||
} else {
|
||||
// do the moving
|
||||
for (int i = W-1; i >= targetStart; i--) {
|
||||
memcpy(screen+cursor.y*W+i, screen+cursor.y*W+(i-count), sizeof(Cell));
|
||||
utf_free_cell(cursor.y, i);
|
||||
copy_cell(cursor.y, i, i - count);
|
||||
utf_backup_cell(cursor.y, i);
|
||||
}
|
||||
clear_range_utf(cursor.y * W + cursor.x, cursor.y * W + targetStart - 1);
|
||||
}
|
||||
|
||||
clear_range(cursor.y*W+cursor.x, cursor.y*W+targetStart-1);
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
@@ -742,21 +852,24 @@ screen_delete_characters(unsigned int count)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
|
||||
// FIXME remove cleared & overwritten cells from unicode cache!
|
||||
int targetEnd = W - count;
|
||||
if (targetEnd > cursor.x) {
|
||||
// do the moving
|
||||
for (int i = cursor.x; i <= targetEnd; i++) {
|
||||
memcpy(screen+cursor.y*W+i, screen+cursor.y*W+(i+count), sizeof(Cell));
|
||||
}
|
||||
}
|
||||
// pull rest of the line to the left
|
||||
|
||||
if (targetEnd <= cursor.x) {
|
||||
int movedBlockEnd = W - count;
|
||||
if (movedBlockEnd > cursor.x) {
|
||||
// partial line delete / move
|
||||
|
||||
for (int i = cursor.x; i <= movedBlockEnd; i++) {
|
||||
utf_free_cell(cursor.y, i);
|
||||
copy_cell(cursor.y, i, i + count);
|
||||
utf_backup_cell(cursor.y, i);
|
||||
}
|
||||
// clear original positions of the moved characters
|
||||
clear_range_noutf(cursor.y * W + (W - count), (cursor.y + 1) * W - 1);
|
||||
} else {
|
||||
// all rest was cleared
|
||||
screen_clear_line(CLEAR_FROM_CURSOR);
|
||||
}
|
||||
else {
|
||||
clear_range(cursor.y * W + (W - count), (cursor.y + 1) * W - 1);
|
||||
}
|
||||
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
//endregion
|
||||
@@ -767,12 +880,12 @@ void ICACHE_FLASH_ATTR
|
||||
screen_fill_with_E(void)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
screen_reset(); // based on observation from xterm
|
||||
screen_reset_do(false, false); // based on observation from xterm
|
||||
|
||||
Cell sample;
|
||||
sample.symbol = 'E';
|
||||
sample.fg = termconf->default_fg;
|
||||
sample.bg = termconf->default_bg;
|
||||
sample.fg = 0;
|
||||
sample.bg = 0;
|
||||
sample.attrs = 0;
|
||||
|
||||
for (unsigned int i = 0; i <= W*H-1; i++) {
|
||||
@@ -837,7 +950,8 @@ screen_scroll_up(unsigned int lines)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
if (lines >= RH) {
|
||||
clear_range(R0*W, (R1+1)*W-1);
|
||||
// clear entire region
|
||||
clear_range_utf(TOP * W, (BTM + 1) * W - 1);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -846,16 +960,16 @@ screen_scroll_up(unsigned int lines)
|
||||
goto done;
|
||||
}
|
||||
|
||||
// FIXME remove cleared & overwritten cells from unicode cache!
|
||||
|
||||
int y;
|
||||
for (y = R0; y <= R1 - lines; y++) {
|
||||
memcpy(screen + y * W, screen + (y + lines) * W, W * sizeof(Cell));
|
||||
for (y = TOP; y <= BTM - lines; y++) {
|
||||
utf_free_row(y);
|
||||
copy_row(y, y+lines);
|
||||
if (y < BTM - lines) utf_backup_row(y);
|
||||
}
|
||||
|
||||
clear_range(y * W, (R1+1)*W-1);
|
||||
clear_range_noutf(y * W, (BTM + 1) * W - 1);
|
||||
|
||||
done:
|
||||
done:
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
@@ -867,24 +981,25 @@ screen_scroll_down(unsigned int lines)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
if (lines >= RH) {
|
||||
clear_range(R0*W, (R1+1)*W-1);
|
||||
// clear entire region
|
||||
clear_range_utf(TOP * W, (BTM + 1) * W - 1);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// FIXME remove cleared & overwritten cells from unicode cache!
|
||||
|
||||
// bad cmd
|
||||
if (lines == 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
int y;
|
||||
for (y = R1; y >= R0+lines; y--) {
|
||||
memcpy(screen + y * W, screen + (y - lines) * W, W * sizeof(Cell));
|
||||
for (y = BTM; y >= TOP+lines; y--) {
|
||||
utf_free_row(y);
|
||||
copy_row(y, y-lines);
|
||||
if (y > TOP + lines) utf_backup_row(y);
|
||||
}
|
||||
|
||||
clear_range(R0*W, R0*W+ lines * W-1);
|
||||
done:
|
||||
clear_range_noutf(TOP * W, TOP * W + lines * W - 1);
|
||||
done:
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
@@ -960,7 +1075,7 @@ screen_cursor_get(int *y, int *x)
|
||||
*y = cursor.y;
|
||||
|
||||
if (cursor.origin_mode) {
|
||||
*y -= R0;
|
||||
*y -= TOP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -989,9 +1104,9 @@ screen_cursor_set_y(int y)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
if (cursor.origin_mode) {
|
||||
y += R0;
|
||||
if (y > R1) y = R1;
|
||||
if (y < R0) y = R0;
|
||||
y += TOP;
|
||||
if (y > BTM) y = BTM;
|
||||
if (y < TOP) y = TOP;
|
||||
} else {
|
||||
if (y > H-1) y = H-1;
|
||||
if (y < 0) y = 0;
|
||||
@@ -1012,7 +1127,7 @@ screen_cursor_move(int dy, int dx, bool scroll)
|
||||
clear_invalid_hanging();
|
||||
|
||||
if (cursor.hanging && dx < 0) {
|
||||
dx += 1; // consume one step on the removal of "xenl"
|
||||
//dx += 1; // consume one step on the removal of "xenl"
|
||||
cursor.hanging = false;
|
||||
}
|
||||
|
||||
@@ -1040,7 +1155,7 @@ screen_cursor_move(int dy, int dx, bool scroll)
|
||||
}
|
||||
else {
|
||||
// end of screen, end of line (wrap around)
|
||||
cursor.y = R1;
|
||||
cursor.y = BTM;
|
||||
cursor.x = W - 1;
|
||||
}
|
||||
}
|
||||
@@ -1050,10 +1165,10 @@ screen_cursor_move(int dy, int dx, bool scroll)
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor.y < R0) {
|
||||
if (cursor.y < TOP) {
|
||||
if (was_inside) {
|
||||
move = -(cursor.y - R0);
|
||||
cursor.y = R0;
|
||||
move = -(cursor.y - TOP);
|
||||
cursor.y = TOP;
|
||||
if (scroll) screen_scroll_down((unsigned int) move);
|
||||
}
|
||||
else {
|
||||
@@ -1065,10 +1180,10 @@ screen_cursor_move(int dy, int dx, bool scroll)
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor.y > R1) {
|
||||
if (cursor.y > BTM) {
|
||||
if (was_inside) {
|
||||
move = cursor.y - R1;
|
||||
cursor.y = R1;
|
||||
move = cursor.y - BTM;
|
||||
cursor.y = BTM;
|
||||
if (scroll) screen_scroll_up((unsigned int) move);
|
||||
}
|
||||
else {
|
||||
@@ -1174,6 +1289,7 @@ void ICACHE_FLASH_ATTR
|
||||
screen_set_fg(Color color)
|
||||
{
|
||||
cursor.fg = color;
|
||||
cursor.attrs |= ATTR_FG;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1183,10 +1299,31 @@ void ICACHE_FLASH_ATTR
|
||||
screen_set_bg(Color color)
|
||||
{
|
||||
cursor.bg = color;
|
||||
cursor.attrs |= ATTR_BG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cursor foreground color to default
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_set_default_fg(void)
|
||||
{
|
||||
cursor.fg = 0;
|
||||
cursor.attrs &= ~ATTR_FG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cursor background color to default
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_set_default_bg(void)
|
||||
{
|
||||
cursor.bg = 0;
|
||||
cursor.attrs &= ~ATTR_BG;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_set_sgr(u8 attrs, bool ena)
|
||||
screen_set_sgr(CellAttrs attrs, bool ena)
|
||||
{
|
||||
if (ena) {
|
||||
cursor.attrs |= attrs;
|
||||
@@ -1196,12 +1333,6 @@ screen_set_sgr(u8 attrs, bool ena)
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_set_sgr_inverse(bool ena)
|
||||
{
|
||||
cursor.inverse = ena;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_set_sgr_conceal(bool ena)
|
||||
{
|
||||
@@ -1359,9 +1490,11 @@ screen_report_sgr(char *buffer)
|
||||
if (cursor.attrs & ATTR_BLINK) buffer += sprintf(buffer, ";%d", SGR_BLINK);
|
||||
if (cursor.attrs & ATTR_FRAKTUR) buffer += sprintf(buffer, ";%d", SGR_FRAKTUR);
|
||||
if (cursor.attrs & ATTR_STRIKE) buffer += sprintf(buffer, ";%d", SGR_STRIKE);
|
||||
if (cursor.inverse) buffer += sprintf(buffer, ";%d", SGR_INVERSE);
|
||||
if (cursor.fg != termconf->default_fg) buffer += sprintf(buffer, ";%d", ((cursor.fg > 7) ? SGR_FG_BRT_START : SGR_FG_START) + (cursor.fg&7));
|
||||
if (cursor.bg != termconf->default_bg) buffer += sprintf(buffer, ";%d", ((cursor.bg > 7) ? SGR_BG_BRT_START : SGR_BG_START) + (cursor.bg&7));
|
||||
if (cursor.attrs & ATTR_INVERSE) buffer += sprintf(buffer, ";%d", SGR_INVERSE);
|
||||
if (cursor.attrs & ATTR_FG)
|
||||
buffer += sprintf(buffer, ";%d", ((cursor.fg > 7) ? SGR_FG_BRT_START : SGR_FG_START) + (cursor.fg&7));
|
||||
if (cursor.attrs & ATTR_BG)
|
||||
buffer += sprintf(buffer, ";%d", ((cursor.bg > 7) ? SGR_BG_BRT_START : SGR_BG_START) + (cursor.bg&7));
|
||||
(void)buffer;
|
||||
}
|
||||
|
||||
@@ -1381,10 +1514,10 @@ putchar_graphic(const char *ch)
|
||||
cursor.x = 0;
|
||||
cursor.y++;
|
||||
// Y wrap
|
||||
if (cursor.y > R1) {
|
||||
if (cursor.y > BTM) {
|
||||
// Scroll up, so we have space for writing
|
||||
screen_scroll_up(1);
|
||||
cursor.y = R1;
|
||||
cursor.y = BTM;
|
||||
}
|
||||
|
||||
cursor.hanging = false;
|
||||
@@ -1403,14 +1536,8 @@ putchar_graphic(const char *ch)
|
||||
}
|
||||
unicode_cache_remove(c->symbol);
|
||||
c->symbol = unicode_cache_add((const u8 *)ch);
|
||||
|
||||
if (cursor.inverse) {
|
||||
c->fg = cursor.bg;
|
||||
c->bg = cursor.fg;
|
||||
} else {
|
||||
c->fg = cursor.fg;
|
||||
c->bg = cursor.bg;
|
||||
}
|
||||
c->fg = cursor.fg;
|
||||
c->bg = cursor.bg;
|
||||
c->attrs = cursor.attrs;
|
||||
|
||||
cursor.x++;
|
||||
@@ -1543,7 +1670,7 @@ utf8_remap(char *out, char g, char charset)
|
||||
struct ScreenSerializeState {
|
||||
Color lastFg;
|
||||
Color lastBg;
|
||||
bool lastAttrs;
|
||||
CellAttrs lastAttrs;
|
||||
UnicodeCacheRef lastSymbol;
|
||||
char lastChar[4];
|
||||
u8 lastCharLen;
|
||||
@@ -1593,58 +1720,46 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
|
||||
}
|
||||
|
||||
Cell *cell, *cell0;
|
||||
WordB2 w1;
|
||||
WordB3 lw1;
|
||||
|
||||
u8 nbytes;
|
||||
size_t remain = buf_len;
|
||||
char *bb = buffer;
|
||||
|
||||
#define bufput_c(c) do { \
|
||||
*bb = (char)c; bb++; \
|
||||
*bb = (char)(c); \
|
||||
bb++; \
|
||||
remain--; \
|
||||
} while(0)
|
||||
|
||||
#define bufput_2B(n) do { \
|
||||
encode2B((u16) n, &w1); \
|
||||
bufput_c(w1.lsb); \
|
||||
bufput_c(w1.msb); \
|
||||
} while(0)
|
||||
#define bufput_utf8(num) do { \
|
||||
nbytes = utf8_encode(bb, (num)+1); \
|
||||
bb += nbytes; \
|
||||
remain -= nbytes; \
|
||||
} while(0)
|
||||
|
||||
#define bufput_3B(n) do { \
|
||||
encode3B((u32) n, &lw1); \
|
||||
bufput_c(lw1.lsb); \
|
||||
bufput_c(lw1.msb); \
|
||||
bufput_c(lw1.xsb); \
|
||||
} while(0)
|
||||
|
||||
#define bufput_t2B(t, n) do { \
|
||||
bufput_c(t); \
|
||||
bufput_2B(n); \
|
||||
} while(0)
|
||||
|
||||
#define bufput_t3B(t, n) do { \
|
||||
bufput_c(t); \
|
||||
bufput_3B(n); \
|
||||
} while(0)
|
||||
#define bufput_t_utf8(t, num) do { \
|
||||
bufput_c((t)); \
|
||||
bufput_utf8((num)); \
|
||||
} while(0)
|
||||
|
||||
if (ss == NULL) {
|
||||
*data = ss = malloc(sizeof(struct ScreenSerializeState));
|
||||
ss->index = 0;
|
||||
ss->lastBg = 0;
|
||||
ss->lastFg = 0;
|
||||
ss->lastAttrs = 0;
|
||||
ss->lastBg = 0xFF;
|
||||
ss->lastFg = 0xFF;
|
||||
ss->lastAttrs = 0xFFFF;
|
||||
ss->lastCharLen = 0;
|
||||
ss->lastSymbol = 32;
|
||||
ss->lastSymbol = 0;
|
||||
strncpy(ss->lastChar, " ", 4);
|
||||
|
||||
bufput_c('S');
|
||||
// H W X Y Attribs
|
||||
bufput_2B(H);
|
||||
bufput_2B(W);
|
||||
bufput_2B(cursor.y);
|
||||
bufput_2B(cursor.x);
|
||||
bufput_utf8(H);
|
||||
bufput_utf8(W);
|
||||
bufput_utf8(cursor.y);
|
||||
bufput_utf8(cursor.x);
|
||||
// 3B has 18 free bits
|
||||
bufput_3B(
|
||||
bufput_utf8(
|
||||
(scr.cursor_visible << 0) |
|
||||
(cursor.hanging << 1) |
|
||||
(scr.cursors_alt_mode << 2) |
|
||||
@@ -1656,7 +1771,8 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
|
||||
(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.bracketed_paste << 13) |
|
||||
(scr.reverse_video << 14)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1683,35 +1799,29 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
|
||||
}
|
||||
|
||||
if (repCnt == 0) {
|
||||
// No repeat
|
||||
bool changeAttrs = cell0->attrs != ss->lastAttrs || i==0;
|
||||
// No repeat - first occurrence
|
||||
bool changeAttrs = cell0->attrs != ss->lastAttrs;
|
||||
bool changeFg = cell0->fg != ss->lastFg;
|
||||
bool changeBg = cell0->bg != ss->lastBg;
|
||||
bool changeColors = (changeFg && changeBg) || i==0;
|
||||
bool changeColors = changeFg && changeBg;
|
||||
Color fg, bg;
|
||||
|
||||
// Reverse fg and bg if we're in global reverse mode
|
||||
if (! scr.reverse_video) {
|
||||
fg = cell0->fg;
|
||||
bg = cell0->bg;
|
||||
}
|
||||
else {
|
||||
fg = cell0->bg;
|
||||
bg = cell0->fg;
|
||||
}
|
||||
fg = cell0->fg;
|
||||
bg = cell0->bg;
|
||||
|
||||
if (changeColors) {
|
||||
bufput_t3B(SEQ_TAG_COLORS, bg<<8 | fg);
|
||||
bufput_t_utf8(SEQ_TAG_COLORS, bg<<8 | fg);
|
||||
}
|
||||
else if (changeFg) {
|
||||
bufput_t2B(SEQ_TAG_FG, fg);
|
||||
bufput_t_utf8(SEQ_TAG_FG, fg);
|
||||
}
|
||||
else if (changeBg) {
|
||||
bufput_t2B(SEQ_TAG_BG, bg);
|
||||
bufput_t_utf8(SEQ_TAG_BG, bg);
|
||||
}
|
||||
|
||||
if (changeAttrs) {
|
||||
bufput_t2B(SEQ_TAG_ATTRS, cell0->attrs);
|
||||
bufput_t_utf8(SEQ_TAG_ATTRS, cell0->attrs);
|
||||
}
|
||||
|
||||
// copy the symbol, until first 0 or reached 4 bytes
|
||||
@@ -1733,24 +1843,21 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
|
||||
i++;
|
||||
} else {
|
||||
// last character was repeated repCnt times
|
||||
int savings = ss->lastCharLen*repCnt;
|
||||
if (savings > 3) {
|
||||
// Repeat count
|
||||
bufput_t2B(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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
bufput_t_utf8(SEQ_TAG_REPEAT, repCnt);
|
||||
}
|
||||
}
|
||||
|
||||
ss->index = i;
|
||||
bufput_c('\0'); // terminate the string
|
||||
|
||||
#if 0
|
||||
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;
|
||||
}
|
||||
|
||||
+25
-18
@@ -37,7 +37,7 @@
|
||||
// Size designed for the terminal config structure
|
||||
// Must be constant to avoid corrupting user config after upgrade
|
||||
#define TERMCONF_SIZE 300
|
||||
#define TERMCONF_VERSION 5
|
||||
#define TERMCONF_VERSION 0
|
||||
|
||||
#define TERM_BTN_LEN 10
|
||||
#define TERM_BTN_MSG_LEN 10
|
||||
@@ -65,11 +65,11 @@ enum CursorShape {
|
||||
CURSOR_BAR = 6,
|
||||
};
|
||||
|
||||
#define SCR_DEF_SHOW_BUTTONS 0
|
||||
#define SCR_DEF_SHOW_BUTTONS 1
|
||||
#define SCR_DEF_SHOW_MENU 1
|
||||
#define SCR_DEF_CURSOR_SHAPE CURSOR_BLOCK_BL
|
||||
#define SCR_DEF_CRLF 0
|
||||
#define SCR_DEF_ALLFN 0
|
||||
#define SCR_DEF_CRLF 0 // enter sends CRLF
|
||||
#define SCR_DEF_ALLFN 0 // capture F5 etc
|
||||
|
||||
// --- Persistent Settings ---
|
||||
#define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL)
|
||||
@@ -77,8 +77,8 @@ enum CursorShape {
|
||||
typedef struct {
|
||||
u32 width;
|
||||
u32 height;
|
||||
u8 default_bg; // should be the Color typedef, but this way the size is more explicit
|
||||
u8 default_fg;
|
||||
u32 default_bg; // 00-FFh - ANSI colors, (00:00:00-FF:FF:FF)+256 - True Color
|
||||
u32 default_fg;
|
||||
char title[TERM_TITLE_LEN];
|
||||
char btn[TERM_BTN_COUNT][TERM_BTN_LEN];
|
||||
u8 theme;
|
||||
@@ -228,25 +228,32 @@ void screen_set_origin_mode(bool region_origin);
|
||||
|
||||
// --- Graphic rendition setting ---
|
||||
|
||||
typedef uint8_t Color; // 0-16
|
||||
typedef uint8_t Color;
|
||||
typedef uint16_t CellAttrs;
|
||||
|
||||
#define ATTR_BOLD (1<<0)
|
||||
#define ATTR_FAINT (1<<1)
|
||||
#define ATTR_ITALIC (1<<2)
|
||||
#define ATTR_UNDERLINE (1<<3)
|
||||
#define ATTR_BLINK (1<<4)
|
||||
#define ATTR_FRAKTUR (1<<5)
|
||||
#define ATTR_STRIKE (1<<6)
|
||||
#define ATTR_OVERLINE (1<<7)
|
||||
// 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
|
||||
|
||||
/** Set cursor foreground color */
|
||||
void screen_set_fg(Color color);
|
||||
/** Set cursor background coloor */
|
||||
void screen_set_bg(Color color);
|
||||
/** Set cursor foreground color to default */
|
||||
void screen_set_default_fg(void);
|
||||
/** Set cursor background color to default */
|
||||
void screen_set_default_bg(void);
|
||||
/** Enable/disable attrs by bitmask */
|
||||
void screen_set_sgr(u8 attrs, bool ena);
|
||||
/** Set the inverse attribute */
|
||||
void screen_set_sgr_inverse(bool ena);
|
||||
void screen_set_sgr(CellAttrs attrs, bool ena);
|
||||
/** Conceal style */
|
||||
void screen_set_sgr_conceal(bool ena);
|
||||
/** Reset cursor attribs */
|
||||
|
||||
+4
-5
@@ -4,8 +4,7 @@
|
||||
#include "ansi_parser.h"
|
||||
#include "syscfg.h"
|
||||
|
||||
#define LOGBUF_SIZE 512
|
||||
static char logbuf[LOGBUF_SIZE];
|
||||
static char logbuf[DEBUG_LOGBUF_SIZE];
|
||||
static u32 lb_nw = 1;
|
||||
static u32 lb_ls = 0;
|
||||
static ETSTimer flushLogTimer;
|
||||
@@ -14,7 +13,7 @@ static void buf_putc(char c)
|
||||
{
|
||||
if (lb_ls != lb_nw) {
|
||||
logbuf[lb_nw++] = c;
|
||||
if (lb_nw >= LOGBUF_SIZE) lb_nw = 0;
|
||||
if (lb_nw >= DEBUG_LOGBUF_SIZE) lb_nw = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +24,11 @@ buf_pop(void *unused)
|
||||
u32 old_ls;
|
||||
while (quantity > 0) {
|
||||
// stop when done
|
||||
if ((lb_ls == lb_nw-1) || (lb_ls == LOGBUF_SIZE-1 && lb_nw == 0)) break;
|
||||
if ((lb_ls == lb_nw-1) || (lb_ls == DEBUG_LOGBUF_SIZE-1 && lb_nw == 0)) break;
|
||||
|
||||
old_ls = lb_ls;
|
||||
lb_ls++;
|
||||
if (lb_ls >= LOGBUF_SIZE) lb_ls = 0;
|
||||
if (lb_ls >= DEBUG_LOGBUF_SIZE) lb_ls = 0;
|
||||
|
||||
if (OK == UART_WriteCharCRLF(UART1, logbuf[lb_ls], 5)) {
|
||||
quantity--;
|
||||
|
||||
+21
-1
@@ -12,9 +12,23 @@ SystemConfigBundle * const sysconf = &persist.current.sysconf;
|
||||
void ICACHE_FLASH_ATTR
|
||||
sysconf_apply_settings(void)
|
||||
{
|
||||
// !!! Update to current version !!!
|
||||
bool changed = false;
|
||||
if (sysconf->config_version < 1) {
|
||||
dbg("Upgrading syscfg to v 1");
|
||||
sysconf->overclock = false;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
sysconf->config_version = SYSCONF_VERSION;
|
||||
|
||||
if (changed) {
|
||||
persist_store();
|
||||
}
|
||||
|
||||
// uart settings live here, but the CGI handler + form has been moved to the Terminal config page
|
||||
serialInit();
|
||||
|
||||
system_update_cpu_freq((uint8) (sysconf->overclock ? 160 : 80));
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
@@ -23,4 +37,10 @@ sysconf_restore_defaults(void)
|
||||
sysconf->uart_parity = PARITY_NONE;
|
||||
sysconf->uart_baudrate = BIT_RATE_115200;
|
||||
sysconf->uart_stopbits = ONE_STOP_BIT;
|
||||
sysconf->config_version = SYSCONF_VERSION;
|
||||
sysconf->access_pw[0] = 0;
|
||||
sysconf->pwlock = PWLOCK_NONE;
|
||||
strcpy(sysconf->access_pw, DEF_ACCESS_PW);
|
||||
strcpy(sysconf->access_name, DEF_ACCESS_NAME);
|
||||
sysconf->overclock = false;
|
||||
}
|
||||
|
||||
+18
-2
@@ -9,14 +9,30 @@
|
||||
|
||||
// Size designed for the wifi config structure
|
||||
// Must be constant to avoid corrupting user config after upgrade
|
||||
#define SYSCONF_SIZE 200
|
||||
#define SYSCONF_VERSION 0
|
||||
#define SYSCONF_SIZE 300
|
||||
#define SYSCONF_VERSION 1
|
||||
|
||||
#define DEF_ACCESS_PW "1234"
|
||||
#define DEF_ACCESS_NAME "espterm"
|
||||
|
||||
enum pwlock {
|
||||
PWLOCK_NONE = 0,
|
||||
PWLOCK_SETTINGS_NOTERM = 1,
|
||||
PWLOCK_SETTINGS_ALL = 2,
|
||||
PWLOCK_MENUS = 3,
|
||||
PWLOCK_ALL = 4,
|
||||
PWLOCK_MAX = 5,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
u32 uart_baudrate;
|
||||
u8 uart_parity;
|
||||
u8 uart_stopbits;
|
||||
u8 config_version;
|
||||
enum pwlock pwlock : 8; // page access lock
|
||||
char access_pw[64]; // access password
|
||||
char access_name[32]; // access name
|
||||
bool overclock;
|
||||
} SystemConfigBundle;
|
||||
|
||||
extern SystemConfigBundle * const sysconf;
|
||||
|
||||
+22
-12
@@ -9,7 +9,7 @@
|
||||
#include <uart_register.h>
|
||||
|
||||
#define UART_TX_BUFFER_SIZE 512 //Ring buffer length of tx buffer
|
||||
#define UART_RX_BUFFER_SIZE 512 //Ring buffer length of rx buffer
|
||||
#define UART_RX_BUFFER_SIZE 600 //Ring buffer length of rx buffer
|
||||
|
||||
struct UartBuffer {
|
||||
uint32 UartBuffSize;
|
||||
@@ -158,24 +158,34 @@ 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) {
|
||||
UART_WriteChar(UART1, '%', 100);
|
||||
}
|
||||
else {
|
||||
// try to read at least the bit we can
|
||||
fifo_len = (uint8) (pRxBuffer->Space - 1);
|
||||
UART_WriteChar(UART1, '%', 1);
|
||||
// discard contents of the FIFO - would loop forever
|
||||
buf_idx = 0;
|
||||
while (buf_idx < fifo_len) {
|
||||
buf_idx++;
|
||||
fifo_data = (uint8) (READ_PERI_REG(UART_FIFO(UART0)) & 0xFF);
|
||||
*(pRxBuffer->pInPos++) = fifo_data;
|
||||
if (pRxBuffer->pInPos == (pRxBuffer->pUartBuff + pRxBuffer->UartBuffSize)) {
|
||||
pRxBuffer->pInPos = pRxBuffer->pUartBuff;
|
||||
}
|
||||
(void)fifo_data; // pretend we use it
|
||||
}
|
||||
pRxBuffer->Space -= fifo_len;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pRxBuffer->Space >= UART_FIFO_LEN) {
|
||||
uart_rx_intr_enable(UART0);
|
||||
buf_idx = 0;
|
||||
while (buf_idx < fifo_len) {
|
||||
buf_idx++;
|
||||
fifo_data = (uint8) (READ_PERI_REG(UART_FIFO(UART0)) & 0xFF);
|
||||
*(pRxBuffer->pInPos++) = fifo_data;
|
||||
if (pRxBuffer->pInPos == (pRxBuffer->pUartBuff + pRxBuffer->UartBuffSize)) {
|
||||
pRxBuffer->pInPos = pRxBuffer->pUartBuff;
|
||||
}
|
||||
}
|
||||
pRxBuffer->Space -= fifo_len;
|
||||
//
|
||||
// this is called by the processing routine, no need here
|
||||
// if (pRxBuffer->Space >= UART_FIFO_LEN) {
|
||||
// uart_rx_intr_enable(UART0);
|
||||
// }
|
||||
}
|
||||
|
||||
u16 ICACHE_FLASH_ATTR UART_AsyncTxGetEmptySpace(void)
|
||||
@@ -209,7 +219,7 @@ UART_SendAsync(const char *pdata, int16_t data_len)
|
||||
UART_WriteToAsyncBuffer(pTxBuffer, pdata, real_len);
|
||||
}
|
||||
else {
|
||||
UART_WriteChar(UART1, '^', 100);
|
||||
UART_WriteChar(UART1, '^', 1);
|
||||
}
|
||||
// }
|
||||
|
||||
|
||||
+1
-1
@@ -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 4
|
||||
#define FIFO_FULL_THRES 32
|
||||
|
||||
#define uart_recvTaskPrio 1
|
||||
#define uart_recvTaskQueueLen 25
|
||||
|
||||
+1
-3
@@ -79,7 +79,6 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg)
|
||||
|
||||
// Deferred init
|
||||
static void user_start(void *unused);
|
||||
static void user_start2(void *unused);
|
||||
|
||||
static ETSTimer userStartTimer;
|
||||
static ETSTimer prHeapTimer;
|
||||
@@ -99,7 +98,7 @@ 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__);
|
||||
banner_info("Version "FIRMWARE_VERSION", built " __DATE__ " at " __TIME__ " " __TIMEZONE__);
|
||||
printf("\r\n");
|
||||
|
||||
ioInit();
|
||||
@@ -121,7 +120,6 @@ void ICACHE_FLASH_ATTR user_init(void)
|
||||
TIMER_START(&userStartTimer, user_start, 10, 0);
|
||||
}
|
||||
|
||||
|
||||
static void ICACHE_FLASH_ATTR user_start(void *unused)
|
||||
{
|
||||
// Load and apply stored settings, or defaults if stored settings are invalid
|
||||
|
||||
+29
-4
@@ -20,7 +20,8 @@ static UnicodeCacheSlot cache[UNICODE_CACHE_SIZE];
|
||||
* @return
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
unicode_cache_clear(void) {
|
||||
unicode_cache_clear(void)
|
||||
{
|
||||
utfc_dbg("utf8 cache clear!");
|
||||
for (int slot = 0; slot < UNICODE_CACHE_SIZE; slot++) {
|
||||
cache[slot].count=0;
|
||||
@@ -35,7 +36,8 @@ unicode_cache_clear(void) {
|
||||
* @return the obtained look-up reference
|
||||
*/
|
||||
UnicodeCacheRef ICACHE_FLASH_ATTR
|
||||
unicode_cache_add(const u8 *bytes) {
|
||||
unicode_cache_add(const u8 *bytes)
|
||||
{
|
||||
if (bytes[0] < 32) {
|
||||
utfc_warn("utf8 cache bad char '%c'", bytes[0]);
|
||||
return '?';
|
||||
@@ -69,6 +71,27 @@ unicode_cache_add(const u8 *bytes) {
|
||||
return ID_TO_REF(slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment a reference
|
||||
*
|
||||
* @param ref - reference
|
||||
* @return success
|
||||
*/
|
||||
bool ICACHE_FLASH_ATTR
|
||||
unicode_cache_inc(UnicodeCacheRef ref)
|
||||
{
|
||||
if (!IS_UNICODE_CACHE_REF(ref)) return true; // ASCII
|
||||
|
||||
int slot = REF_TO_ID(ref);
|
||||
if (cache[slot].count == 0) {
|
||||
utfc_warn("utf8 cache inc-after-free ref @ %d", ref);
|
||||
return false;
|
||||
}
|
||||
cache[slot].count++;
|
||||
utfc_dbg("utf8 cache inc '%.4s' @ %d, %d uses", cache[slot].bytes, slot, cache[slot].count);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a code point in the cache by reference. Do not change the use counter.
|
||||
*
|
||||
@@ -77,7 +100,8 @@ unicode_cache_add(const u8 *bytes) {
|
||||
* @return true if the look-up succeeded
|
||||
*/
|
||||
bool ICACHE_FLASH_ATTR
|
||||
unicode_cache_retrieve(UnicodeCacheRef ref, u8 *target) {
|
||||
unicode_cache_retrieve(UnicodeCacheRef ref, u8 *target)
|
||||
{
|
||||
if (!IS_UNICODE_CACHE_REF(ref)) {
|
||||
// ASCII, bypass
|
||||
target[0] = ref;
|
||||
@@ -107,7 +131,8 @@ unicode_cache_retrieve(UnicodeCacheRef ref, u8 *target) {
|
||||
* @return true if the code point was found in the cache
|
||||
*/
|
||||
bool ICACHE_FLASH_ATTR
|
||||
unicode_cache_remove(UnicodeCacheRef ref) {
|
||||
unicode_cache_remove(UnicodeCacheRef ref)
|
||||
{
|
||||
if (!IS_UNICODE_CACHE_REF(ref)) return true; // ASCII, bypass
|
||||
|
||||
u8 slot = REF_TO_ID(ref);
|
||||
|
||||
+9
-1
@@ -8,7 +8,7 @@
|
||||
#include <c_types.h>
|
||||
|
||||
// 160 is maximum possible
|
||||
#define UNICODE_CACHE_SIZE 100
|
||||
#define UNICODE_CACHE_SIZE 160
|
||||
|
||||
typedef u8 UnicodeCacheRef;
|
||||
#define IS_UNICODE_CACHE_REF(c) ((c) < 32 || (c) >= 127)
|
||||
@@ -28,6 +28,14 @@ void unicode_cache_clear(void);
|
||||
*/
|
||||
UnicodeCacheRef unicode_cache_add(const u8 *bytes);
|
||||
|
||||
/**
|
||||
* Increment a reference
|
||||
*
|
||||
* @param ref - reference
|
||||
* @return success
|
||||
*/
|
||||
bool unicode_cache_inc(UnicodeCacheRef ref);
|
||||
|
||||
/**
|
||||
* Look up a code point in the cache by reference. Do not change the use counter.
|
||||
*
|
||||
|
||||
+3
-3
@@ -5,9 +5,9 @@
|
||||
#ifndef ESP_VT100_FIRMWARE_VERSION_H
|
||||
#define ESP_VT100_FIRMWARE_VERSION_H
|
||||
|
||||
#define FW_V_MAJOR 1
|
||||
#define FW_V_MINOR 1
|
||||
#define FW_V_PATCH 1
|
||||
#define FW_V_MAJOR 2
|
||||
#define FW_V_MINOR 0
|
||||
#define FW_V_PATCH 0
|
||||
|
||||
#define FIRMWARE_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH)
|
||||
#define FIRMWARE_VERSION_NUM (FW_V_MAJOR*1000 + FW_V_MINOR*10 + FW_V_PATCH) // this is used in ID queries
|
||||
|
||||
@@ -187,6 +187,10 @@ wifimgr_apply_settings(void)
|
||||
configure_ap();
|
||||
}
|
||||
|
||||
// tpw seems to be common - but info is scarce
|
||||
// at any rate seems to do no harm to have it here
|
||||
system_phy_set_max_tpw(wificonf->tpw);
|
||||
|
||||
wifi_change_flags.ap = false;
|
||||
wifi_change_flags.sta = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user