Compare commits
51
Commits
2.1.0-beta3
...
work
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9030d5ce85
|
||
|
|
6971fe2036
|
||
|
|
9f97d5c9d1
|
||
|
|
5343d44ea8
|
||
|
|
76fbf5d65f
|
||
|
|
6b4013f779
|
||
|
|
0438f88c83
|
||
|
|
d0d7200e57
|
||
|
|
78aaa949a3
|
||
|
|
5dbb592946
|
||
|
|
d9aa0418fa
|
||
|
|
9008f2e1af
|
||
|
|
bc240e676b
|
||
|
|
3af928b39d
|
||
|
|
ead9120ae8
|
||
|
|
dca44963e8
|
||
|
|
3c12efe28f
|
||
|
|
e98588b317
|
||
|
|
0e6c297fe4
|
||
|
|
3e4181f18e
|
||
|
|
2f4cd08c60
|
||
|
|
ba2b73a287
|
||
|
|
4e285694d1
|
||
|
|
9700465927
|
||
|
|
1ab4101ac0
|
||
|
|
f0bc70553a
|
||
|
|
3eac0ee794
|
||
|
|
2c80b59d71
|
||
|
|
9146c9880a
|
||
|
|
cabd42f2b4
|
||
|
|
fbd4693035
|
||
|
|
c5670c09bb
|
||
|
|
4e74d1cd79
|
||
|
|
4c8360dbb5
|
||
|
|
c0adc253b4
|
||
|
|
be3e06aac1
|
||
|
|
992c927490
|
||
|
|
ef3b02e68c
|
||
|
|
1c556457c7
|
||
|
|
e49a8b0adf
|
||
|
|
81c3e426e7
|
||
|
|
c90de79a34 | ||
|
|
df404f5f63
|
||
|
|
00153656f1
|
||
|
|
aa920aee27
|
||
|
|
7b8c936559
|
||
|
|
17c18ed1c9
|
||
|
|
274e3b359b
|
||
|
|
73c98eb2d1
|
||
|
|
022e882001
|
||
|
|
dc08512397
|
+3
-2
@@ -16,8 +16,9 @@ esphttpdconfig.mk
|
|||||||
|
|
||||||
# Garbage added by CLion
|
# Garbage added by CLion
|
||||||
.idea/
|
.idea/
|
||||||
cmake-build-debug/
|
cmake-build-debug*/
|
||||||
.sass-cache
|
.sass-cache
|
||||||
|
|
||||||
*.map
|
*.map
|
||||||
.gitignore
|
!.gitignore
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -146,9 +146,12 @@ set(SOURCE_FILES
|
|||||||
user/jstring.c
|
user/jstring.c
|
||||||
user/jstring.h
|
user/jstring.h
|
||||||
user/character_sets.h
|
user/character_sets.h
|
||||||
|
user/ini_parser.h
|
||||||
|
user/ini_parser.c
|
||||||
|
user/ini_parser.rl
|
||||||
user/utf8.h
|
user/utf8.h
|
||||||
user/utf8.c
|
user/utf8.c
|
||||||
user/cgi_logging.h)
|
user/cgi_logging.h user/config_xmacros.h user/config_xmacros.c)
|
||||||
|
|
||||||
include_directories(include)
|
include_directories(include)
|
||||||
include_directories(libesphttpd/esphttpclient)
|
include_directories(libesphttpd/esphttpclient)
|
||||||
|
|||||||
@@ -5,21 +5,24 @@
|
|||||||
![Photo][photo-hw]<br>
|
![Photo][photo-hw]<br>
|
||||||
*Fig 1: Breadboard adapter developed for ESPTerm*
|
*Fig 1: Breadboard adapter developed for ESPTerm*
|
||||||
|
|
||||||
As of release 1.0, ESPTerm **passes most of VTTEST test cases** (from the main menu and some Xterm specific), making it
|
ESPTerm **passes most of VTTEST test cases**, making it functionally comparable to eg.
|
||||||
functionally comparable to eg. gnome-terminal, terminator, konsole, GtkTerm or PuTTY.
|
gnome-terminal, terminator, konsole, GtkTerm or PuTTY.
|
||||||
ESPTerm is **capable of running Midnight Commander** through agetty, **including full
|
ESPTerm is **capable of running Midnight Commander** through agetty, **including full
|
||||||
mouse support**, provided agetty is made to believe it's Xterm, which shows ESPTerm is sufficiently well
|
mouse support**, provided agetty is made to believe it's Xterm, which shows ESPTerm
|
||||||
implemented to work with ncurses.
|
is compatible with ncurses.
|
||||||
|
|
||||||
|
In addition to control sequences that manipulate the terminal or user input, there
|
||||||
|
is a set of simple **networking commands** for device-to-device message exchange and
|
||||||
|
interacting with remote servers.
|
||||||
|
|
||||||
To see what escape sequences are supported, check out this [annotated Xterm manual page][xterm-compare]
|
To see what escape sequences are supported, check out this [annotated Xterm manual page][xterm-compare]
|
||||||
which was used for reference, or the built-in help page ([online demo][demo-help])
|
which was used for reference, or the built-in help page ([online demo][demo-help])
|
||||||
|
|
||||||
The terminal screen can be accessed using any web browser, even on a phone or tablet.
|
The terminal screen can be accessed using any web browser, even on a phone or tablet.
|
||||||
It works with ESP-01, ESP-01S, ESP-12 and likely many other modules (I use an ESP-12
|
It works with ESP-01, ESP-01S, ESP-12 and likely many other modules.
|
||||||
on a LoLin NodeMCU board from eBay for development).
|
|
||||||
|
|
||||||
With ESPTerm, you can add remote access via WiFi to any embeded project, all you need is
|
With ESPTerm, you can add remote access via WiFi to any embeded project, all you need is
|
||||||
a serial port and some imagination!
|
UART and some imagination!
|
||||||
|
|
||||||
## Try it online
|
## Try it online
|
||||||
|
|
||||||
@@ -48,6 +51,8 @@ It **does not work with**:
|
|||||||
- Internet Explorer (any version) - crashes, missing JS features
|
- Internet Explorer (any version) - crashes, missing JS features
|
||||||
- Opera Mini - crashes, missing JS and CSS features
|
- Opera Mini - crashes, missing JS and CSS features
|
||||||
- Blackberry browser - not tested, but unlikely
|
- Blackberry browser - not tested, but unlikely
|
||||||
|
- Safari on old iOS versions - missing features
|
||||||
|
- WebOS browser
|
||||||
- Old Android Browser (before 4.4?) - not tested, likely missing JS features
|
- Old Android Browser (before 4.4?) - not tested, likely missing JS features
|
||||||
|
|
||||||
## Main features
|
## Main features
|
||||||
@@ -58,6 +63,10 @@ It **does not work with**:
|
|||||||
- Full UTF-8 support, alternate character sets
|
- Full UTF-8 support, alternate character sets
|
||||||
- Standard mouse tracking modes
|
- Standard mouse tracking modes
|
||||||
- You can dynamically set screen title, button labels...
|
- You can dynamically set screen title, button labels...
|
||||||
|
- **Networking commands**
|
||||||
|
- Command for sending a message to another ESPTerm
|
||||||
|
- API endpoint (`/api/v1/msg`) for receiving messages sent e.g. from a script on PC, web browser or CURL
|
||||||
|
- Command for requesting remote servers and getting back response headers and/or body
|
||||||
- **Web Terminal Interface**
|
- **Web Terminal Interface**
|
||||||
- Real-time screen update via WebSocket
|
- Real-time screen update via WebSocket
|
||||||
- Mouse and keyboard input, works also on mobile
|
- Mouse and keyboard input, works also on mobile
|
||||||
@@ -67,6 +76,15 @@ It **does not work with**:
|
|||||||
- **User-friendly comprehensive WiFi configuration** (Demo: [WiFi][demo-wifi], [network][demo-network] config)
|
- **User-friendly comprehensive WiFi configuration** (Demo: [WiFi][demo-wifi], [network][demo-network] config)
|
||||||
- Static IP, DHCP, channel selection, power
|
- Static IP, DHCP, channel selection, power
|
||||||
- SSID search utility for finding your existing network
|
- SSID search utility for finding your existing network
|
||||||
|
- **Basic security features**
|
||||||
|
- Possibility to password-lock parts of the web interface
|
||||||
|
- Admin password for some sensitive operations (can be changed!)
|
||||||
|
- Configurable AP password & Hidden attribute
|
||||||
|
- **Advanced settings storage**
|
||||||
|
- Stored in Flash
|
||||||
|
- Seamlesly updated and usually backwards compatible in minor releases
|
||||||
|
- Settings can be saved as defaults and later easily restored (ideal e.g. for classroom use, saving
|
||||||
|
good tested settings before giving the module to students)
|
||||||
|
|
||||||
## Bugs? Ideas?
|
## Bugs? Ideas?
|
||||||
|
|
||||||
@@ -108,36 +126,31 @@ It can happen that some changes to the WiFi or network config make the module in
|
|||||||
- To reset all settings to defaults, hold the button a couple seconds until the LED flashes rapidly, then release it.
|
- To reset all settings to defaults, hold the button a couple seconds until the LED flashes rapidly, then release it.
|
||||||
- You can cancel this wipe/reset operation (when triggered by accident) by pressing Reset or disconnecting the power supply.
|
- You can cancel this wipe/reset operation (when triggered by accident) by pressing Reset or disconnecting the power supply.
|
||||||
|
|
||||||
### Config files
|
### Config banks
|
||||||
|
|
||||||
ESPTerm has two config "files", one for defaults and one for the currently used settings. In the case of the terminal
|
ESPTerm has two config "files", one for defaults and one for the currently used settings. In the case of the terminal
|
||||||
config, there is also a third, temporary file for changes done via ESC commands.
|
config, there is also a third, temporary file for changes done via ESC commands.
|
||||||
|
|
||||||
When you get your settings *just right*, you can store them as defaults, which can then be at any time restored
|
When you get your settings *just right*, you can store them as defaults, which can then be at any time restored
|
||||||
by holding the BOOT (GPIO0) button. You can do this on the System Settings page. This asks for an "admin password",
|
by holding the BOOT (GPIO0) button. You can do this on the System Settings page. This asks for an "admin password",
|
||||||
which you can define when building the firmware in the `esphttpdconfig.mk` file.
|
which can (and should!) be changed. This password can't be easily recovered when forgotten.
|
||||||
|
|
||||||
The default password is `19738426`. This password can't presently be changed without re-flashing the firmware.
|
|
||||||
|
|
||||||
You can also restore everything (except the saved defaults) to "factory defaults", there is a button for this
|
You can also restore everything (except the saved defaults) to "factory defaults", there is a button for this
|
||||||
on the System Settings page. Those are the initial values in the config files.
|
on the System Settings page. Those are the initial values you would get after a clean install.
|
||||||
|
|
||||||
## Research resources
|
## Research resources
|
||||||
|
|
||||||
Developing ESPTerm wasn't an easy task, because the information is scattered across many places and the existing
|
Developing the terminal emulator was complicated by the information being scattered across many places and the existing
|
||||||
terminal emulators I originally used for reference (terminator, Konsole) are not implemented correctly in some details.
|
implementations I used for reference often got some details wrong or didn't implement certain features at all. Xterm proved to be by far the most complete implementation.
|
||||||
|
|
||||||
A great tool for checking my implementation has proven to be [VTTTEST][vttest] and Xterm as a reference
|
A great tool for checking my code has proven to be [VTTTEST][vttest]. ESPTerm passes most of the tests on the main page and some additional Xterm specific ones, like Mouse Tracking.
|
||||||
implementation that is probably the most complete emulator available, although it's cumbersome to use and its age
|
|
||||||
really shows in the looks.
|
|
||||||
|
|
||||||
I've comnpiled a list of those I found most helpful here: [VT100 emulation resources][resources]
|
Here is a list of useful [VT100 emulation resources][resources] I've collected for reference.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
ESPTerm's firmware is written in C and is based on SpriteTM's `libesphttpd` http server library forked to
|
ESPTerm's firmware is written in C and is based on SpriteTM's `libesphttpd` http server library forked to
|
||||||
[MightyPork/libesphttpd][httpdlib]. This fork includes various improvements
|
[MightyPork/libesphttpd][httpdlib]. This fork includes various improvements and changes required by the project.
|
||||||
and changes required by the project.
|
|
||||||
|
|
||||||
### Installation for development
|
### Installation for development
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
echo "-- Building parser from Ragel source --"
|
echo "-- Building parser from Ragel source --"
|
||||||
|
|
||||||
ragel -L -G0 user/ansi_parser.rl -o user/ansi_parser.c
|
ragel -L -G0 user/ansi_parser.rl -o user/ansi_parser.c
|
||||||
|
ragel -L -G0 user/ini_parser.rl -o user/ini_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_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
|
sed -i "s/static const char _ansi_eof_actions\[\]/static const char _ansi_eof_actions\[\] ESP_CONST_DATA/" user/ansi_parser.c
|
||||||
|
|
||||||
|
sed -i "s/static const char _ini_actions\[\]/static const char _ini_actions\[\] ESP_CONST_DATA/" user/ini_parser.c
|
||||||
|
sed -i "s/static const char _ini_eof_actions\[\]/static const char _ini_eof_actions\[\] ESP_CONST_DATA/" user/ini_parser.c
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ ESP_SPI_FLASH_SIZE_K = 1024
|
|||||||
|
|
||||||
GLOBAL_CFLAGS = \
|
GLOBAL_CFLAGS = \
|
||||||
-DASYNC_LOG=1 \
|
-DASYNC_LOG=1 \
|
||||||
|
-DDEBUG_INI=1 \
|
||||||
-DDEBUG_D2D=0 \
|
-DDEBUG_D2D=0 \
|
||||||
-DDEBUG_ROUTER=0 \
|
-DDEBUG_ROUTER=0 \
|
||||||
-DDEBUG_CAPTDNS=0 \
|
-DDEBUG_CAPTDNS=0 \
|
||||||
|
|||||||
+1
-1
Submodule front-end updated: 01148465b7...e25523de11
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
|
|||||||
+1
-1
Submodule libesphttpd updated: e4ecf0724e...d6651ca0d1
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
xtensa-lx106-elf-gcc -E -Iuser -Ilibesphttpd/include -Iesp_iot_sdk_v1.5.2/include -Iinclude $@
|
||||||
@@ -45,7 +45,7 @@ function buildlang() {
|
|||||||
cp ../rel-tpl/README.txt ${readmefil}
|
cp ../rel-tpl/README.txt ${readmefil}
|
||||||
sed -i s/%VERS%/${vers}/ ${readmefil}
|
sed -i s/%VERS%/${vers}/ ${readmefil}
|
||||||
sed -i s/%LANG%/${lang}/ ${readmefil}
|
sed -i s/%LANG%/${lang}/ ${readmefil}
|
||||||
dt=$(LC_TIME=en_US.UTF-8 date '+%c %Z')
|
dt=$(LC_TIME=en_US.UTF-8 date '+%c')
|
||||||
sed -i "s#%DATETIME%#${dt}#" ${readmefil}
|
sed -i "s#%DATETIME%#${dt}#" ${readmefil}
|
||||||
unix2dos ${readmefil}
|
unix2dos ${readmefil}
|
||||||
|
|
||||||
@@ -64,6 +64,7 @@ if [ -z "$ESP_LANG" ]; then
|
|||||||
buildlang cs
|
buildlang cs
|
||||||
buildlang en
|
buildlang en
|
||||||
buildlang de
|
buildlang de
|
||||||
|
buildlang hu
|
||||||
else
|
else
|
||||||
buildlang ${ESP_LANG}
|
buildlang ${ESP_LANG}
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "uart_buffer.h"
|
#include "uart_buffer.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
#include "wifimgr.h"
|
||||||
|
|
||||||
volatile bool enquiry_suppressed = false;
|
volatile bool enquiry_suppressed = false;
|
||||||
ETSTimer enqTimer;
|
ETSTimer enqTimer;
|
||||||
@@ -51,7 +52,17 @@ apars_handle_enq(void)
|
|||||||
wifi_get_macaddr(SOFTAP_IF, mac);
|
wifi_get_macaddr(SOFTAP_IF, mac);
|
||||||
|
|
||||||
char buf100[100];
|
char buf100[100];
|
||||||
sprintf(buf100, "\x1bXESPTerm "VERSION_STRING" #"GIT_HASH_BACKEND"+"GIT_HASH_FRONTEND" id=%02X%02X%02X\x1b\\", mac[3], mac[4], mac[5]);
|
char *buf = buf100;
|
||||||
|
buf += sprintf(buf, "\x1bX");
|
||||||
|
buf += sprintf(buf, "ESPTerm "VERSION_STRING" ");
|
||||||
|
buf += sprintf(buf, "#"GIT_HASH_BACKEND"+"GIT_HASH_FRONTEND" ");
|
||||||
|
buf += sprintf(buf, "id=%02X%02X%02X ", mac[3], mac[4], mac[5]);
|
||||||
|
int x = getStaIpAsString(buf);
|
||||||
|
if (x) buf += x;
|
||||||
|
else buf--; // remove the trailing space
|
||||||
|
buf += sprintf(buf, "\x1b\\");
|
||||||
|
|
||||||
|
(void)buf;
|
||||||
|
|
||||||
// version encased in SOS and ST
|
// version encased in SOS and ST
|
||||||
apars_respond(buf100);
|
apars_respond(buf100);
|
||||||
|
|||||||
+69
-3
@@ -16,6 +16,45 @@
|
|||||||
#include "ansi_parser.h"
|
#include "ansi_parser.h"
|
||||||
#include "cgi_sockets.h"
|
#include "cgi_sockets.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle ESPTerm-specific command
|
||||||
|
*
|
||||||
|
* @param n0 - top-level, 20-39
|
||||||
|
* @param n1 - sub-command
|
||||||
|
* @param buffer - buffer past the second command and its semicolon
|
||||||
|
*/
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
handle_espterm_osc(int n0, int n1, char *buffer)
|
||||||
|
{
|
||||||
|
if (n0 == 27) {
|
||||||
|
// Miscellaneous
|
||||||
|
if (n1 == 1) {
|
||||||
|
screen_set_backdrop(buffer);
|
||||||
|
}
|
||||||
|
else if (n1 == 2) {
|
||||||
|
screen_set_button_count(atoi(buffer));
|
||||||
|
}
|
||||||
|
else goto bad;
|
||||||
|
}
|
||||||
|
else if (n0 == 28) {
|
||||||
|
// Button label
|
||||||
|
screen_set_button_text(n1, buffer);
|
||||||
|
}
|
||||||
|
else if (n0 == 29) {
|
||||||
|
// Button message
|
||||||
|
screen_set_button_message(n1, buffer);
|
||||||
|
}
|
||||||
|
else if (n0 == 30) {
|
||||||
|
// Button color
|
||||||
|
screen_set_button_color(n1, buffer);
|
||||||
|
}
|
||||||
|
else goto bad;
|
||||||
|
return;
|
||||||
|
|
||||||
|
bad:
|
||||||
|
ansi_warn("No ESPTerm option at %d.%d", n0, n1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to parse incoming OSC (Operating System Control)
|
* Helper function to parse incoming OSC (Operating System Control)
|
||||||
* @param buffer - the OSC body (after OSC and before ST)
|
* @param buffer - the OSC body (after OSC and before ST)
|
||||||
@@ -23,8 +62,9 @@
|
|||||||
void ICACHE_FLASH_ATTR
|
void ICACHE_FLASH_ATTR
|
||||||
apars_handle_osc(char *buffer)
|
apars_handle_osc(char *buffer)
|
||||||
{
|
{
|
||||||
|
const char *origbuf = buffer;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
char c = 0;
|
char c;
|
||||||
while ((c = *buffer++) != 0) {
|
while ((c = *buffer++) != 0) {
|
||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
n = (n * 10 + (c - '0'));
|
n = (n * 10 + (c - '0'));
|
||||||
@@ -49,20 +89,46 @@ apars_handle_osc(char *buffer)
|
|||||||
buffer[0] = 'G';
|
buffer[0] = 'G';
|
||||||
notify_growl(buffer);
|
notify_growl(buffer);
|
||||||
}
|
}
|
||||||
|
else if (n >= 20 && n < 40) {
|
||||||
|
// New-style ESPTerm OSC
|
||||||
|
int n0 = n;
|
||||||
|
|
||||||
|
// Find sub-command
|
||||||
|
n = 0;
|
||||||
|
while ((c = *buffer++) != 0) {
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
n = (n * 10 + (c - '0'));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c == ';') {
|
||||||
|
handle_espterm_osc(n0, n, buffer);
|
||||||
|
} else {
|
||||||
|
ansi_warn("BAD OSC: %s", origbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (n == 70) {
|
||||||
|
// ESPTerm: backdrop
|
||||||
|
ansi_warn("OSC 70 is deprecated, use 27;1");
|
||||||
|
screen_set_backdrop(buffer);
|
||||||
|
}
|
||||||
else if (n >= 81 && n <= 85) {
|
else if (n >= 81 && n <= 85) {
|
||||||
// ESPTerm: action button label
|
// ESPTerm: action button label
|
||||||
|
ansi_warn("OSC 8x is deprecated, use 28;x");
|
||||||
screen_set_button_text(n - 80, buffer);
|
screen_set_button_text(n - 80, buffer);
|
||||||
}
|
}
|
||||||
else if (n >= 91 && n <= 95) {
|
else if (n >= 91 && n <= 95) {
|
||||||
// ESPTerm: action button message
|
// ESPTerm: action button message
|
||||||
strncpy(termconf_live.btn_msg[n - 91], buffer, TERM_BTN_MSG_LEN);
|
ansi_warn("OSC 9x is deprecated, use 29;x");
|
||||||
|
screen_set_button_message(n - 90, buffer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ansi_noimpl("OSC %d ; %s ST", n, buffer);
|
ansi_noimpl("OSC %d ; %s ST", n, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ansi_warn("BAD OSC: %s", buffer);
|
ansi_warn("BAD OSC: %s", origbuf);
|
||||||
apars_show_context();
|
apars_show_context();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-6
@@ -94,21 +94,33 @@ apars_handle_space_cmd(char c)
|
|||||||
void ICACHE_FLASH_ATTR apars_handle_hash_cmd(char c)
|
void ICACHE_FLASH_ATTR apars_handle_hash_cmd(char c)
|
||||||
{
|
{
|
||||||
switch(c) {
|
switch(c) {
|
||||||
|
case '1': // Double height, single width, top half (CUSTOM!)
|
||||||
|
screen_set_line_attr(1, 2, 1);
|
||||||
|
break;
|
||||||
|
case '2': // Double height, single width, bottom half (CUSTOM!)
|
||||||
|
screen_set_line_attr(1, 1, 2);
|
||||||
|
break;
|
||||||
case '3': // Double size, top half
|
case '3': // Double size, top half
|
||||||
case '4': // Single size, bottom half
|
screen_set_line_attr(2, 2, 1);
|
||||||
|
break;
|
||||||
|
case '4': // Double size, bottom half
|
||||||
|
screen_set_line_attr(2, 1, 2);
|
||||||
|
break;
|
||||||
case '5': // Single width, single height
|
case '5': // Single width, single height
|
||||||
|
screen_set_line_attr(1, 1, 1);
|
||||||
|
break;
|
||||||
case '6': // Double width
|
case '6': // Double width
|
||||||
ansi_noimpl("Double Size Line");
|
screen_set_line_attr(2, 1, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '8':
|
case '8':
|
||||||
screen_fill_with_E();
|
screen_fill_with_E();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// development codes - do not use!
|
// // development codes - do not use!
|
||||||
case '7':
|
// case '7':
|
||||||
http_get("http://wtfismyip.com/text", NULL, http_callback_example);
|
// http_get("http://wtfismyip.com/text", NULL, http_callback_example);
|
||||||
break;
|
// break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ansi_noimpl("ESC # %c", c);
|
ansi_noimpl("ESC # %c", c);
|
||||||
|
|||||||
+6
-7
@@ -63,8 +63,6 @@ requestCb(int http_status,
|
|||||||
|
|
||||||
struct d2d_request_opts *opts = userArg;
|
struct d2d_request_opts *opts = userArg;
|
||||||
|
|
||||||
d2d_dbg("Rx url response, code %d, nonce \"%s\"", http_status, opts->nonce?opts->nonce:"");
|
|
||||||
|
|
||||||
// ensure positive - would be hard to parse
|
// ensure positive - would be hard to parse
|
||||||
if (http_status < 0) http_status = -http_status;
|
if (http_status < 0) http_status = -http_status;
|
||||||
|
|
||||||
@@ -77,6 +75,8 @@ requestCb(int http_status,
|
|||||||
if (opts->max_result_len > 0 && len > opts->max_result_len)
|
if (opts->max_result_len > 0 && len > opts->max_result_len)
|
||||||
len = (int) opts->max_result_len;
|
len = (int) opts->max_result_len;
|
||||||
|
|
||||||
|
d2d_info("Rx HTTP response, code %d, len %d, nonce \"%s\"", http_status, len, opts->nonce?opts->nonce:"");
|
||||||
|
|
||||||
char *bb = buff100;
|
char *bb = buff100;
|
||||||
bb += sprintf(bb, "\x1b^h;%d;", http_status);
|
bb += sprintf(bb, "\x1b^h;%d;", http_status);
|
||||||
const char *comma = "";
|
const char *comma = "";
|
||||||
@@ -160,7 +160,7 @@ d2d_parse_command(char *msg)
|
|||||||
FIND_NEXT(ip, ';');
|
FIND_NEXT(ip, ';');
|
||||||
const char *payload = msg;
|
const char *payload = msg;
|
||||||
|
|
||||||
d2d_dbg("D2D Tx,dest=%s,msg=%s", ip, payload);
|
d2d_info("D2D Tx,dest=%s,msg=%s", ip, payload);
|
||||||
sprintf(buff40, "http://%s" API_D2D_MSG, ip);
|
sprintf(buff40, "http://%s" API_D2D_MSG, ip);
|
||||||
|
|
||||||
httpclient_args args;
|
httpclient_args args;
|
||||||
@@ -203,9 +203,8 @@ d2d_parse_command(char *msg)
|
|||||||
|
|
||||||
FIND_NEXT(params, ';');
|
FIND_NEXT(params, ';');
|
||||||
|
|
||||||
|
d2d_info("HTTP request");
|
||||||
d2d_dbg("Method %s", method);
|
d2d_dbg("Method %s", method);
|
||||||
d2d_dbg("Params %s", params);
|
|
||||||
|
|
||||||
size_t max_buf_len = HTTPCLIENT_DEF_MAX_LEN;
|
size_t max_buf_len = HTTPCLIENT_DEF_MAX_LEN;
|
||||||
size_t max_result_len = 0; // 0 = no truncate
|
size_t max_result_len = 0; // 0 = no truncate
|
||||||
uint timeout = HTTPCLIENT_DEF_TIMEOUT_MS;
|
uint timeout = HTTPCLIENT_DEF_TIMEOUT_MS;
|
||||||
@@ -274,7 +273,7 @@ d2d_parse_command(char *msg)
|
|||||||
request_pending = true;
|
request_pending = true;
|
||||||
http_request(&args, no_resp ? requestNoopCb : requestCb);
|
http_request(&args, no_resp ? requestNoopCb : requestCb);
|
||||||
|
|
||||||
d2d_dbg("Done");
|
d2d_dbg("Request sent.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +307,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiD2DMessage(HttpdConnData *connData)
|
|||||||
|
|
||||||
sendResponseToUART("\a");
|
sendResponseToUART("\a");
|
||||||
|
|
||||||
d2d_dbg("D2D Rx src="IPSTR",len=%d", ip[0], ip[1], ip[2], ip[3],len);
|
d2d_info("D2D Rx src="IPSTR",len=%d", ip[0], ip[1], ip[2], ip[3],len);
|
||||||
|
|
||||||
// Received a msg
|
// Received a msg
|
||||||
|
|
||||||
|
|||||||
+13
-161
@@ -9,6 +9,7 @@ configuring the network settings
|
|||||||
#include "persist.h"
|
#include "persist.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "cgi_logging.h"
|
#include "cgi_logging.h"
|
||||||
|
#include "config_xmacros.h"
|
||||||
|
|
||||||
#define SET_REDIR_SUC "/cfg/network"
|
#define SET_REDIR_SUC "/cfg/network"
|
||||||
#define SET_REDIR_ERR SET_REDIR_SUC"?err="
|
#define SET_REDIR_ERR SET_REDIR_SUC"?err="
|
||||||
@@ -48,138 +49,10 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
|
|||||||
|
|
||||||
// ---- AP DHCP server lease time ----
|
// ---- AP DHCP server lease time ----
|
||||||
|
|
||||||
if (GET_ARG("ap_dhcp_time")) {
|
#define XSTRUCT wificonf
|
||||||
cgi_dbg("Setting DHCP lease time to: %s min.", buff);
|
#define X XSET_CGI_FUNC
|
||||||
int min = atoi(buff);
|
XTABLE_WIFICONF
|
||||||
if (min >= 1 && min <= 2880) {
|
#undef X
|
||||||
if (wificonf->ap_dhcp_time != min) {
|
|
||||||
wificonf->ap_dhcp_time = (u16) min;
|
|
||||||
wifi_change_flags.ap = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("Lease time %s out of allowed range 1-2880.", buff);
|
|
||||||
redir_url += sprintf(redir_url, "ap_dhcp_time,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- AP DHCP start and end IP ----
|
|
||||||
|
|
||||||
if (GET_ARG("ap_dhcp_start")) {
|
|
||||||
cgi_dbg("Setting DHCP range start IP to: \"%s\"", buff);
|
|
||||||
u32 ip = ipaddr_addr(buff);
|
|
||||||
if (ip != 0) {
|
|
||||||
if (wificonf->ap_dhcp_range.start_ip.addr != ip) {
|
|
||||||
wificonf->ap_dhcp_range.start_ip.addr = ip;
|
|
||||||
wifi_change_flags.ap = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad IP: %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "ap_dhcp_start,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("ap_dhcp_end")) {
|
|
||||||
cgi_dbg("Setting DHCP range end IP to: \"%s\"", buff);
|
|
||||||
u32 ip = ipaddr_addr(buff);
|
|
||||||
if (ip != 0) {
|
|
||||||
if (wificonf->ap_dhcp_range.end_ip.addr != ip) {
|
|
||||||
wificonf->ap_dhcp_range.end_ip.addr = ip;
|
|
||||||
wifi_change_flags.ap = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad IP: %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "ap_dhcp_end,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- AP local address & config ----
|
|
||||||
|
|
||||||
if (GET_ARG("ap_addr_ip")) {
|
|
||||||
cgi_dbg("Setting AP local IP to: \"%s\"", buff);
|
|
||||||
u32 ip = ipaddr_addr(buff);
|
|
||||||
if (ip != 0) {
|
|
||||||
if (wificonf->ap_addr.ip.addr != ip) {
|
|
||||||
wificonf->ap_addr.ip.addr = ip;
|
|
||||||
wificonf->ap_addr.gw.addr = ip; // always the same, we're the router here
|
|
||||||
wifi_change_flags.ap = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad IP: %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "ap_addr_ip,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("ap_addr_mask")) {
|
|
||||||
cgi_dbg("Setting AP local IP netmask to: \"%s\"", buff);
|
|
||||||
u32 ip = ipaddr_addr(buff);
|
|
||||||
if (ip != 0) {
|
|
||||||
if (wificonf->ap_addr.netmask.addr != ip) {
|
|
||||||
// ideally this should be checked to match the IP.
|
|
||||||
// Let's hope users know what they're doing
|
|
||||||
wificonf->ap_addr.netmask.addr = ip;
|
|
||||||
wifi_change_flags.ap = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad IP mask: %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "ap_addr_mask,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Station enable/disable DHCP ----
|
|
||||||
|
|
||||||
// DHCP enable / disable (disable means static IP is enabled)
|
|
||||||
if (GET_ARG("sta_dhcp_enable")) {
|
|
||||||
cgi_dbg("DHCP enable = %s", buff);
|
|
||||||
int enable = atoi(buff);
|
|
||||||
if (wificonf->sta_dhcp_enable != enable) {
|
|
||||||
wificonf->sta_dhcp_enable = (bool)enable;
|
|
||||||
wifi_change_flags.sta = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Station IP config (Static IP) ----
|
|
||||||
|
|
||||||
if (GET_ARG("sta_addr_ip")) {
|
|
||||||
cgi_dbg("Setting Station mode static IP to: \"%s\"", buff);
|
|
||||||
u32 ip = ipaddr_addr(buff);
|
|
||||||
if (ip != 0) {
|
|
||||||
if (wificonf->sta_addr.ip.addr != ip) {
|
|
||||||
wificonf->sta_addr.ip.addr = ip;
|
|
||||||
wifi_change_flags.sta = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad IP: %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "sta_addr_ip,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("sta_addr_mask")) {
|
|
||||||
cgi_dbg("Setting Station mode static IP netmask to: \"%s\"", buff);
|
|
||||||
u32 ip = ipaddr_addr(buff);
|
|
||||||
if (ip != 0 && ip != 0xFFFFFFFFUL) {
|
|
||||||
if (wificonf->sta_addr.netmask.addr != ip) {
|
|
||||||
wificonf->sta_addr.netmask.addr = ip;
|
|
||||||
wifi_change_flags.sta = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad IP mask: %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "sta_addr_mask,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("sta_addr_gw")) {
|
|
||||||
cgi_dbg("Setting Station mode static IP default gateway to: \"%s\"", buff);
|
|
||||||
u32 ip = ipaddr_addr(buff);
|
|
||||||
if (ip != 0) {
|
|
||||||
if (wificonf->sta_addr.gw.addr != ip) {
|
|
||||||
wificonf->sta_addr.gw.addr = ip;
|
|
||||||
wifi_change_flags.sta = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad gw IP: %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "sta_addr_gw,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(void) redir_url;
|
(void) redir_url;
|
||||||
|
|
||||||
@@ -216,7 +89,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
|
|||||||
//Template code for the WLAN page.
|
//Template code for the WLAN page.
|
||||||
httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *token, void **arg)
|
httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *token, void **arg)
|
||||||
{
|
{
|
||||||
char buff[20];
|
char buff[64];
|
||||||
u8 mac[6];
|
u8 mac[6];
|
||||||
|
|
||||||
if (token == NULL) {
|
if (token == NULL) {
|
||||||
@@ -226,34 +99,13 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *toke
|
|||||||
|
|
||||||
strcpy(buff, ""); // fallback
|
strcpy(buff, ""); // fallback
|
||||||
|
|
||||||
if (streq(token, "ap_dhcp_time")) {
|
#define XSTRUCT wificonf
|
||||||
sprintf(buff, "%d", wificonf->ap_dhcp_time);
|
#define X XGET_CGI_FUNC
|
||||||
}
|
XTABLE_WIFICONF
|
||||||
else if (streq(token, "ap_dhcp_start")) {
|
#undef X
|
||||||
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr));
|
|
||||||
}
|
// non-config
|
||||||
else if (streq(token, "ap_dhcp_end")) {
|
if (streq(token, "sta_mac")) {
|
||||||
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_ip.addr));
|
|
||||||
}
|
|
||||||
else if (streq(token, "ap_addr_ip")) {
|
|
||||||
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.ip.addr));
|
|
||||||
}
|
|
||||||
else if (streq(token, "ap_addr_mask")) {
|
|
||||||
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.netmask.addr));
|
|
||||||
}
|
|
||||||
else if (streq(token, "sta_dhcp_enable")) {
|
|
||||||
sprintf(buff, "%d", wificonf->sta_dhcp_enable);
|
|
||||||
}
|
|
||||||
else if (streq(token, "sta_addr_ip")) {
|
|
||||||
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr));
|
|
||||||
}
|
|
||||||
else if (streq(token, "sta_addr_mask")) {
|
|
||||||
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr));
|
|
||||||
}
|
|
||||||
else if (streq(token, "sta_addr_gw")) {
|
|
||||||
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr));
|
|
||||||
}
|
|
||||||
else if (streq(token, "sta_mac")) {
|
|
||||||
wifi_get_macaddr(STATION_IF, mac);
|
wifi_get_macaddr(STATION_IF, mac);
|
||||||
sprintf(buff, MACSTR, MAC2STR(mac));
|
sprintf(buff, MACSTR, MAC2STR(mac));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,13 @@ Cgi/template routines for configuring non-wifi settings
|
|||||||
#include "persist.h"
|
#include "persist.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "cgi_logging.h"
|
#include "cgi_logging.h"
|
||||||
|
#include "version.h"
|
||||||
|
#include "screen.h"
|
||||||
|
#include "config_xmacros.h"
|
||||||
|
#include "ini_parser.h"
|
||||||
|
|
||||||
#define SET_REDIR_SUC "/cfg/system"
|
#define SET_REDIR_SUC "/cfg/system"
|
||||||
|
#define SET_REDIR_ERR SET_REDIR_SUC"?err="
|
||||||
|
|
||||||
static bool ICACHE_FLASH_ATTR
|
static bool ICACHE_FLASH_ATTR
|
||||||
verify_admin_pw(const char *pw)
|
verify_admin_pw(const char *pw)
|
||||||
@@ -74,3 +79,421 @@ cgiPersistRestoreHard(HttpdConnData *connData)
|
|||||||
httpdRedirect(connData, SET_REDIR_SUC "?msg=All%20settings%20restored%20to%20factory%20defaults.");
|
httpdRedirect(connData, SET_REDIR_SUC "?msg=All%20settings%20restored%20to%20factory%20defaults.");
|
||||||
return HTTPD_CGI_DONE;
|
return HTTPD_CGI_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------- Export --------------
|
||||||
|
|
||||||
|
#define httpdSend_orDie(conn, data, len) do { if (!httpdSend((conn), (data), (len))) return false; } while (0)
|
||||||
|
|
||||||
|
/* encode for double-quoted string */
|
||||||
|
int ICACHE_FLASH_ATTR httpdSend_dblquot(HttpdConnData *conn, const char *data, int len)
|
||||||
|
{
|
||||||
|
int start = 0, end = 0;
|
||||||
|
char c;
|
||||||
|
if (conn->conn==NULL) return 0;
|
||||||
|
if (len < 0) len = (int) strlen(data);
|
||||||
|
if (len==0) return 0;
|
||||||
|
|
||||||
|
for (end = 0; end < len; end++) {
|
||||||
|
c = data[end];
|
||||||
|
if (c == 0) {
|
||||||
|
// we found EOS
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '"' || c == '\'' || c == '\\' || c == '\n' || c == '\r' || c == '\x1b') {
|
||||||
|
if (start < end) httpdSend_orDie(conn, data + start, end - start);
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '"') httpdSend_orDie(conn, "\\\"", 2);
|
||||||
|
else if (c == '\'') httpdSend_orDie(conn, "\\'", 2);
|
||||||
|
else if (c == '\\') httpdSend_orDie(conn, "\\\\", 2);
|
||||||
|
else if (c == '\n') httpdSend_orDie(conn, "\\n", 2);
|
||||||
|
else if (c == '\r') httpdSend_orDie(conn, "\\r", 2);
|
||||||
|
else if (c == '\x1b') httpdSend_orDie(conn, "\\e", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < end) httpdSend_orDie(conn, data + start, end - start);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export settings to INI
|
||||||
|
*
|
||||||
|
* @param connData
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR
|
||||||
|
cgiPersistExport(HttpdConnData *connData)
|
||||||
|
{
|
||||||
|
char buff[256];
|
||||||
|
u8 mac[6];
|
||||||
|
|
||||||
|
int step = (int) connData->cgiData;
|
||||||
|
|
||||||
|
if (connData->conn == NULL) {
|
||||||
|
//Connection aborted. Clean up.
|
||||||
|
return HTTPD_CGI_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step == 0) {
|
||||||
|
wifi_get_macaddr(SOFTAP_IF, mac);
|
||||||
|
sprintf(buff, "attachment; filename=ESPTerm_%02X%02X%02X_%s.ini",
|
||||||
|
mac[3], mac[4], mac[5], FW_VERSION);
|
||||||
|
|
||||||
|
httpdStartResponse(connData, 200);
|
||||||
|
httpdHeader(connData, "Content-Disposition", buff);
|
||||||
|
httpdHeader(connData, "Content-Type", "text/plain");
|
||||||
|
httpdEndHeaders(connData);
|
||||||
|
|
||||||
|
sprintf(buff, "# == ESPTerm config export ==\r\n"
|
||||||
|
"# Device: %02X%02X%02X - %s\r\n"
|
||||||
|
"# Version: %s\r\n",
|
||||||
|
mac[3], mac[4], mac[5],
|
||||||
|
termconf->title,
|
||||||
|
FW_VERSION);
|
||||||
|
|
||||||
|
httpdSend(connData, buff, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not export SSID if unchanged - embeds unique ID that should
|
||||||
|
// not be overwritten in target.
|
||||||
|
|
||||||
|
char defSSID[20];
|
||||||
|
sprintf(defSSID, "TERM-%02X%02X%02X", mac[3], mac[4], mac[5]);
|
||||||
|
|
||||||
|
bool quoted;
|
||||||
|
#define X(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
|
||||||
|
do { if (allow) { \
|
||||||
|
xget(buff, deref XSTRUCT->name); \
|
||||||
|
if (streq(#name, "ap_ssid") && streq(buff, defSSID)) break; \
|
||||||
|
\
|
||||||
|
quoted = false; \
|
||||||
|
quoted |= streq(#type, "char"); \
|
||||||
|
quoted |= streq(#type, "uchar"); \
|
||||||
|
if (strstarts(#name, "bm")) quoted=false; \
|
||||||
|
\
|
||||||
|
httpdSend(connData, "\r\n"#name " = ", -1); \
|
||||||
|
if (quoted) { \
|
||||||
|
httpdSend(connData, "\"", 1); \
|
||||||
|
httpdSend_dblquot(connData, buff, -1); \
|
||||||
|
httpdSend(connData, "\"", 1); \
|
||||||
|
} else { \
|
||||||
|
httpdSend(connData, buff, -1); \
|
||||||
|
} \
|
||||||
|
} } while(0);
|
||||||
|
|
||||||
|
#define admin 1
|
||||||
|
#define tpl 1
|
||||||
|
if (step == 1) {
|
||||||
|
httpdSend(connData, "\r\n[system]", -1);
|
||||||
|
#define XSTRUCT sysconf
|
||||||
|
XTABLE_SYSCONF
|
||||||
|
#undef XSTRUCT
|
||||||
|
httpdSend(connData, "\r\n", -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step == 2) {
|
||||||
|
httpdSend(connData, "\r\n[wifi]", -1);
|
||||||
|
#define XSTRUCT wificonf
|
||||||
|
XTABLE_WIFICONF
|
||||||
|
#undef XSTRUCT
|
||||||
|
httpdSend(connData, "\r\n", -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step == 3) {
|
||||||
|
httpdSend(connData, "\r\n[terminal]", -1);
|
||||||
|
#define XSTRUCT termconf
|
||||||
|
XTABLE_TERMCONF
|
||||||
|
#undef XSTRUCT
|
||||||
|
httpdSend(connData, "\r\n", -1);
|
||||||
|
|
||||||
|
return HTTPD_CGI_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef X
|
||||||
|
connData->cgiData = (void *) (step + 1);
|
||||||
|
return HTTPD_CGI_MORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------- IMPORT --------------
|
||||||
|
|
||||||
|
struct IniUpload {
|
||||||
|
TerminalConfigBundle *term_backup;
|
||||||
|
WiFiConfigBundle *wifi_backup;
|
||||||
|
SystemConfigBundle *sys_backup;
|
||||||
|
bool term_ok;
|
||||||
|
bool wifi_ok;
|
||||||
|
bool sys_ok;
|
||||||
|
bool term_any;
|
||||||
|
bool wifi_any;
|
||||||
|
bool sys_any;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void ICACHE_FLASH_ATTR iniCb(const char *section, const char *key, const char *value, void *userData)
|
||||||
|
{
|
||||||
|
HttpdConnData *connData = (HttpdConnData *)userData;
|
||||||
|
struct IniUpload *state;
|
||||||
|
if (!connData || !connData->cgiData) {
|
||||||
|
error("userData or state is NULL!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state = connData->cgiData;
|
||||||
|
|
||||||
|
// cgi_dbg("%s.%s = %s", section, key, value);
|
||||||
|
|
||||||
|
/** used for INI */
|
||||||
|
#define X(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
|
||||||
|
if (streq(#name, key)) { \
|
||||||
|
found = true; \
|
||||||
|
type *_p = (type *) &XSTRUCT->name; \
|
||||||
|
enum xset_result res = xset(#name, _p, value, (const void*) (xsarg)); \
|
||||||
|
if (res == XSET_SET) { changed = true; xnotify; } \
|
||||||
|
else if (res == XSET_FAIL) { ok = false; } \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify flag, unused here
|
||||||
|
bool uart_changed = false;
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
bool ok = true;
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
if (streq(section, "terminal")) {
|
||||||
|
do {
|
||||||
|
#define XSTRUCT termconf
|
||||||
|
XTABLE_TERMCONF
|
||||||
|
#undef XSTRUCT
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
if (!ok) state->term_ok = false;
|
||||||
|
state->term_any |= changed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (streq(section, "wifi")) {
|
||||||
|
do {
|
||||||
|
#define XSTRUCT wificonf
|
||||||
|
XTABLE_WIFICONF
|
||||||
|
#undef XSTRUCT
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
if (!ok) state->wifi_ok = false;
|
||||||
|
state->wifi_any |= changed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (streq(section, "system")) {
|
||||||
|
do {
|
||||||
|
#define XSTRUCT sysconf
|
||||||
|
XTABLE_SYSCONF
|
||||||
|
#undef XSTRUCT
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
if (!ok) state->sys_ok = false;
|
||||||
|
state->sys_any |= changed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) cgi_warn("Unknown key %s.%s!", section, key);
|
||||||
|
|
||||||
|
#undef X
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ICACHE_FLASH_ATTR
|
||||||
|
freeIniUploadStruct(HttpdConnData *connData)
|
||||||
|
{
|
||||||
|
cgi_dbg("Free struct...");
|
||||||
|
struct IniUpload *state;
|
||||||
|
if (connData && connData->cgiData) {
|
||||||
|
state = connData->cgiData;
|
||||||
|
if (state->sys_backup != NULL) free(state->sys_backup);
|
||||||
|
if (state->wifi_backup != NULL) free(state->wifi_backup);
|
||||||
|
if (state->term_backup != NULL) free(state->term_backup);
|
||||||
|
free(state);
|
||||||
|
connData->cgiData = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static httpd_cgi_state ICACHE_FLASH_ATTR
|
||||||
|
postRecvHdl(HttpdConnData *connData, char *data, int len)
|
||||||
|
{
|
||||||
|
struct IniUpload *state = connData->cgiData;
|
||||||
|
if (!state) return HTTPD_CGI_DONE;
|
||||||
|
|
||||||
|
cgi_dbg("INI parse - postRecvHdl");
|
||||||
|
|
||||||
|
if (len>0) {
|
||||||
|
// Discard the boundary marker (there is only one)
|
||||||
|
char *bdr = connData->post->multipartBoundary;
|
||||||
|
char *foundat = strstr(data, bdr);
|
||||||
|
if (foundat != NULL) {
|
||||||
|
*foundat = '#'; // make it a comment
|
||||||
|
}
|
||||||
|
|
||||||
|
ini_parse(data, (size_t) len);
|
||||||
|
}
|
||||||
|
|
||||||
|
cgi_dbg("Closing parser");
|
||||||
|
ini_parse_end();
|
||||||
|
|
||||||
|
cgi_dbg("INI parse - end.");
|
||||||
|
|
||||||
|
// abort if bad screen size
|
||||||
|
bool tooLarge = (termconf->width*termconf->height > MAX_SCREEN_SIZE);
|
||||||
|
state->term_ok &= !tooLarge;
|
||||||
|
if (tooLarge) cgi_warn("Bad term screen size!");
|
||||||
|
|
||||||
|
bool suc = state->term_ok && state->wifi_ok && state->sys_ok;
|
||||||
|
bool any = state->term_any || state->wifi_any || state->sys_any;
|
||||||
|
|
||||||
|
cgi_dbg("Evaluating results...");
|
||||||
|
|
||||||
|
if (!state->term_ok) cgi_warn("Terminal settings rejected.");
|
||||||
|
if (!state->wifi_ok) cgi_warn("WiFi settings rejected.");
|
||||||
|
if (!state->sys_ok) cgi_warn("System settings rejected.");
|
||||||
|
|
||||||
|
if (!suc) {
|
||||||
|
cgi_warn("Some validation failed, reverting all!");
|
||||||
|
memcpy(termconf, state->term_backup, sizeof(TerminalConfigBundle));
|
||||||
|
memcpy(wificonf, state->wifi_backup, sizeof(WiFiConfigBundle));
|
||||||
|
memcpy(sysconf, state->sys_backup, sizeof(SystemConfigBundle));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (state->term_any) {
|
||||||
|
cgi_dbg("Applying terminal settings");
|
||||||
|
terminal_apply_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->sys_any) {
|
||||||
|
cgi_dbg("Applying system settings");
|
||||||
|
sysconf_apply_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->wifi_any) {
|
||||||
|
cgi_dbg("Applying WiFi settings (scheduling...)");
|
||||||
|
wifimgr_apply_settings_later(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any) {
|
||||||
|
cgi_dbg("Persisting results");
|
||||||
|
persist_store();
|
||||||
|
} else {
|
||||||
|
cgi_warn("Nothing written.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cgi_dbg("Redirect");
|
||||||
|
char buff[100];
|
||||||
|
char *b = buff;
|
||||||
|
if (suc) {
|
||||||
|
if (any) {
|
||||||
|
httpdRedirect(connData, SET_REDIR_SUC"?msg=Settings%20loaded%20and%20applied.");
|
||||||
|
} else {
|
||||||
|
httpdRedirect(connData, SET_REDIR_SUC"?msg=No%20settings%20changed.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b += sprintf(b, SET_REDIR_SUC"?errmsg=Errors%%20in:%%20");
|
||||||
|
bool comma = false;
|
||||||
|
if (!state->sys_ok) {
|
||||||
|
b += sprintf(b, "System%%20config");
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
if (!state->wifi_ok) {
|
||||||
|
if (comma) b += sprintf(b, ",%%20");
|
||||||
|
b += sprintf(b, "WiFi%%20config");
|
||||||
|
}
|
||||||
|
if (!state->term_ok) {
|
||||||
|
if (comma) b += sprintf(b, ",%%20");
|
||||||
|
b += sprintf(b, "Terminal%%20config");
|
||||||
|
}
|
||||||
|
httpdRedirect(connData, buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up.
|
||||||
|
freeIniUploadStruct(connData);
|
||||||
|
return HTTPD_CGI_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import settings from INI
|
||||||
|
*
|
||||||
|
* @param connData
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR
|
||||||
|
cgiPersistImport(HttpdConnData *connData)
|
||||||
|
{
|
||||||
|
struct IniUpload *state = NULL;
|
||||||
|
|
||||||
|
if (connData->conn == NULL) {
|
||||||
|
//Connection aborted. Clean up.
|
||||||
|
freeIniUploadStruct(connData);
|
||||||
|
return HTTPD_CGI_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *start = strstr(connData->post->buff, "\r\n\r\n");
|
||||||
|
if (start == NULL) {
|
||||||
|
error("Malformed attachment POST!");
|
||||||
|
|
||||||
|
httpdStartResponse(connData, 400);
|
||||||
|
httpdHeader(connData, "Content-Type", "text/plain");
|
||||||
|
httpdEndHeaders(connData);
|
||||||
|
httpdSend(connData, "Bad format.", -1);
|
||||||
|
return HTTPD_CGI_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cgi_info("Starting INI parser for uploaded file...");
|
||||||
|
|
||||||
|
state = malloc(sizeof(struct IniUpload));
|
||||||
|
if (!state) {
|
||||||
|
error("state struct alloc fail");
|
||||||
|
return HTTPD_CGI_DONE;
|
||||||
|
}
|
||||||
|
state->sys_backup = NULL;
|
||||||
|
state->wifi_backup = NULL;
|
||||||
|
state->term_backup = NULL;
|
||||||
|
connData->cgiData = state;
|
||||||
|
|
||||||
|
cgi_dbg("Allocating backup buffers");
|
||||||
|
state->sys_backup = malloc(sizeof(SystemConfigBundle));
|
||||||
|
state->wifi_backup = malloc(sizeof(WiFiConfigBundle));
|
||||||
|
state->term_backup = malloc(sizeof(TerminalConfigBundle));
|
||||||
|
|
||||||
|
cgi_dbg("Copying orig data");
|
||||||
|
memcpy(state->sys_backup, sysconf, sizeof(SystemConfigBundle));
|
||||||
|
memcpy(state->wifi_backup, wificonf, sizeof(WiFiConfigBundle));
|
||||||
|
memcpy(state->term_backup, termconf, sizeof(TerminalConfigBundle));
|
||||||
|
|
||||||
|
state->sys_ok = true;
|
||||||
|
state->wifi_ok = true;
|
||||||
|
state->term_ok = true;
|
||||||
|
|
||||||
|
state->sys_any = false;
|
||||||
|
state->wifi_any = false;
|
||||||
|
state->term_any = false;
|
||||||
|
|
||||||
|
cgi_dbg("Parser starts!");
|
||||||
|
ini_parse_begin(iniCb, connData);
|
||||||
|
|
||||||
|
size_t datalen = (size_t) connData->post->buffLen - (start - connData->post->buff);
|
||||||
|
ini_parse(start, datalen);
|
||||||
|
|
||||||
|
connData->recvHdl = postRecvHdl;
|
||||||
|
|
||||||
|
// special case for too short ini
|
||||||
|
int bytes_remain = connData->post->len;
|
||||||
|
bytes_remain -= connData->post->buffLen;
|
||||||
|
|
||||||
|
if (bytes_remain <= 0) {
|
||||||
|
return connData->recvHdl(connData, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// continues in recvHdl
|
||||||
|
return HTTPD_CGI_MORE;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,5 +6,7 @@
|
|||||||
httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData);
|
httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData);
|
||||||
httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData);
|
httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData);
|
||||||
httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData);
|
httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData);
|
||||||
|
httpd_cgi_state cgiPersistExport(HttpdConnData *connData);
|
||||||
|
httpd_cgi_state cgiPersistImport(HttpdConnData *connData);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+2
-2
@@ -271,8 +271,8 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int
|
|||||||
case 'b':
|
case 'b':
|
||||||
// action button press
|
// action button press
|
||||||
btnNum = (u8) (data[1]);
|
btnNum = (u8) (data[1]);
|
||||||
if (btnNum > 0 && btnNum < 10) {
|
if (btnNum > 0 && btnNum <= TERM_BTN_COUNT) {
|
||||||
UART_SendAsync(termconf_live.btn_msg[btnNum-1], -1);
|
UART_SendAsync(TERM_BM_N(&termconf_live, btnNum-1), -1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
+59
-83
@@ -95,108 +95,84 @@ cgiSystemCfgSetParams(HttpdConnData *connData)
|
|||||||
memcpy(admin_backup, &persist.admin, sizeof(AdminConfigBlock));
|
memcpy(admin_backup, &persist.admin, sizeof(AdminConfigBlock));
|
||||||
memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle));
|
memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle));
|
||||||
|
|
||||||
|
// flags for the template builder
|
||||||
|
bool uart_changed = false; //!< this is set in uart notify, for use in terminal settings (dummy here)
|
||||||
|
bool admin = false;
|
||||||
|
const bool tpl = false; // this optionally disables some fields
|
||||||
do {
|
do {
|
||||||
if (!GET_ARG("pw")) {
|
// Check admin PW
|
||||||
break; // if no PW in GET, not trying to configure anything protected
|
if (GET_ARG("pw")) {
|
||||||
}
|
if (!streq(buff, persist.admin.pw)) {
|
||||||
|
warn("Bad admin pw!");
|
||||||
if (!streq(buff, persist.admin.pw)) {
|
redir_url += sprintf(redir_url, "pw,");
|
||||||
warn("Bad admin pw!");
|
break; // Abort
|
||||||
redir_url += sprintf(redir_url, "pw,");
|
} else {
|
||||||
break;
|
admin = true;
|
||||||
}
|
|
||||||
|
|
||||||
// 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")) {
|
// Changing admin PW
|
||||||
cgi_dbg("access_pw: %s", buff);
|
if (admin && GET_ARG("admin_pw")) {
|
||||||
|
|
||||||
if (strlen(buff)) {
|
if (strlen(buff)) {
|
||||||
strcpy(buff2, buff);
|
cgi_dbg("admin_pw: %s", 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);
|
strcpy(buff2, buff);
|
||||||
if (!GET_ARG("admin_pw2")) {
|
if (!GET_ARG("admin_pw2")) {
|
||||||
cgi_warn("Missing repeated admin_pw %s", buff);
|
cgi_warn("Missing repeated admin_pw %s", buff);
|
||||||
redir_url += sprintf(redir_url, "admin_pw2,");
|
redir_url += sprintf(redir_url, "admin_pw2,");
|
||||||
break;
|
break; // Abort
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!streq(buff, buff2)) {
|
if (!streq(buff, buff2)) {
|
||||||
cgi_warn("Bad repeated admin_pw %s", buff);
|
cgi_warn("Bad repeated admin_pw %s", buff);
|
||||||
redir_url += sprintf(redir_url, "admin_pw2,");
|
redir_url += sprintf(redir_url, "admin_pw2,");
|
||||||
break;
|
break; // Abort
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(buff) >= 64) {
|
if (strlen(buff) >= 64) {
|
||||||
cgi_warn("Too long admin_pw %s", buff);
|
cgi_warn("Too long admin_pw %s", buff);
|
||||||
redir_url += sprintf(redir_url, "admin_pw,");
|
redir_url += sprintf(redir_url, "admin_pw,");
|
||||||
break;
|
break; // Abort
|
||||||
}
|
}
|
||||||
|
|
||||||
cgi_dbg("Changing admin PW!");
|
cgi_dbg("Changing admin PW!");
|
||||||
strncpy(persist.admin.pw, buff, 64);
|
strncpy(persist.admin.pw, buff, 64);
|
||||||
|
|
||||||
|
break; // this is the only field in this form
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject filled but unconfirmed access PW
|
||||||
|
if (admin && GET_ARG("access_pw")) {
|
||||||
|
if (strlen(buff)) {
|
||||||
|
cgi_dbg("access_pw: %s", 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; // Abort
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!streq(buff, buff2)) {
|
||||||
|
cgi_warn("Bad repeated access_pw %s", buff);
|
||||||
|
redir_url += sprintf(redir_url, "access_pw2,");
|
||||||
|
break; // Abort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settings in the system config block
|
||||||
|
#define XSTRUCT sysconf
|
||||||
|
#define X XSET_CGI_FUNC
|
||||||
|
XTABLE_SYSCONF
|
||||||
|
#undef X
|
||||||
|
#undef XSTRUCT
|
||||||
|
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
if (GET_ARG("overclock")) {
|
|
||||||
cgi_dbg("overclock = %s", buff);
|
|
||||||
int enable = atoi(buff);
|
|
||||||
if (sysconf->overclock != enable) {
|
|
||||||
sysconf->overclock = (bool)enable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)redir_url;
|
(void)redir_url;
|
||||||
|
(void)uart_changed; // unused
|
||||||
|
|
||||||
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
|
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
|
||||||
// All was OK
|
// All was OK
|
||||||
@@ -236,13 +212,16 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg)
|
|||||||
|
|
||||||
strcpy(buff, ""); // fallback
|
strcpy(buff, ""); // fallback
|
||||||
|
|
||||||
if (streq(token, "pwlock")) {
|
const bool admin = false;
|
||||||
sprintf(buff, "%d", sysconf->pwlock);
|
const bool tpl=true;
|
||||||
}
|
|
||||||
else if (streq(token, "access_name")) {
|
#define XSTRUCT sysconf
|
||||||
sprintf(buff, "%s", sysconf->access_name);
|
#define X XGET_CGI_FUNC
|
||||||
}
|
XTABLE_SYSCONF
|
||||||
else if (streq(token, "def_access_name")) {
|
#undef X
|
||||||
|
#undef XSTRUCT
|
||||||
|
|
||||||
|
if (streq(token, "def_access_name")) {
|
||||||
sprintf(buff, "%s", DEF_ACCESS_NAME);
|
sprintf(buff, "%s", DEF_ACCESS_NAME);
|
||||||
}
|
}
|
||||||
else if (streq(token, "def_access_pw")) {
|
else if (streq(token, "def_access_pw")) {
|
||||||
@@ -251,9 +230,6 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg)
|
|||||||
else if (streq(token, "def_admin_pw")) {
|
else if (streq(token, "def_admin_pw")) {
|
||||||
sprintf(buff, "%s", DEFAULT_ADMIN_PW);
|
sprintf(buff, "%s", DEFAULT_ADMIN_PW);
|
||||||
}
|
}
|
||||||
else if (streq(token, "overclock")) {
|
|
||||||
sprintf(buff, "%d", sysconf->overclock);
|
|
||||||
}
|
|
||||||
|
|
||||||
tplSend(connData, buff, -1);
|
tplSend(connData, buff, -1);
|
||||||
return HTTPD_CGI_DONE;
|
return HTTPD_CGI_DONE;
|
||||||
|
|||||||
+42
-438
@@ -15,22 +15,6 @@ Cgi/template routines for configuring non-wifi settings
|
|||||||
#define SET_REDIR_SUC "/cfg/term"
|
#define SET_REDIR_SUC "/cfg/term"
|
||||||
#define SET_REDIR_ERR SET_REDIR_SUC"?err="
|
#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.
|
* Universal CGI endpoint to set Terminal params.
|
||||||
*/
|
*/
|
||||||
@@ -39,15 +23,11 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
|||||||
{
|
{
|
||||||
char buff[50];
|
char buff[50];
|
||||||
char redir_url_buf[100];
|
char redir_url_buf[100];
|
||||||
int32 n, w, h;
|
|
||||||
ScreenNotifyTopics topics = 0;
|
ScreenNotifyTopics topics = 0;
|
||||||
|
|
||||||
bool shall_clear_screen = false;
|
|
||||||
bool shall_init_uart = false;
|
|
||||||
|
|
||||||
char *redir_url = redir_url_buf;
|
char *redir_url = redir_url_buf;
|
||||||
redir_url += sprintf(redir_url, SET_REDIR_ERR);
|
redir_url += sprintf(redir_url, SET_REDIR_ERR);
|
||||||
// we'll test if anything was printed by looking for \0 in failed_keys_buf
|
// we'll test if anything was printed by looking for \0 in redir_url
|
||||||
|
|
||||||
SystemConfigBundle *sysconf_backup = malloc(sizeof(SystemConfigBundle));
|
SystemConfigBundle *sysconf_backup = malloc(sizeof(SystemConfigBundle));
|
||||||
TerminalConfigBundle *termconf_backup = malloc(sizeof(TerminalConfigBundle));
|
TerminalConfigBundle *termconf_backup = malloc(sizeof(TerminalConfigBundle));
|
||||||
@@ -59,322 +39,29 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
|||||||
return HTTPD_CGI_DONE;
|
return HTTPD_CGI_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define XSTRUCT termconf
|
||||||
|
#define X XSET_CGI_FUNC
|
||||||
|
XTABLE_TERMCONF
|
||||||
|
#undef X
|
||||||
|
#undef XSTRUCT
|
||||||
|
|
||||||
// width and height must always go together so we can do max size validation
|
// width and height must always go together so we can do max size validation
|
||||||
if (GET_ARG("term_width")) {
|
u32 siz = termconf->width*termconf->height;
|
||||||
do {
|
if (siz == 0 || siz > MAX_SCREEN_SIZE) {
|
||||||
cgi_dbg("Default screen width: %s", buff);
|
cgi_warn("Bad dimensions: %d x %d (total %d)", termconf->width, termconf->height, termconf->width*termconf->height);
|
||||||
w = atoi(buff);
|
redir_url += sprintf(redir_url, "term_width,term_height,");
|
||||||
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_height,");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS | TOPIC_CHANGE_CONTENT_ALL;
|
|
||||||
}
|
|
||||||
} while (0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GET_ARG("default_bg")) {
|
// Settings from SysConf - UART
|
||||||
cgi_dbg("Screen default BG: %s", buff);
|
bool uart_changed = false;
|
||||||
|
// restrict what keys are allowed
|
||||||
if (buff[0] == '#') {
|
#define admin 0
|
||||||
// decode hex
|
#define tpl 0
|
||||||
n = decodehex(buff+1);
|
#define XSTRUCT sysconf
|
||||||
n += 256;
|
#define X XSET_CGI_FUNC
|
||||||
} else {
|
XTABLE_SYSCONF
|
||||||
n = atoi(buff);
|
#undef X
|
||||||
}
|
#undef XSTRUCT
|
||||||
|
|
||||||
if (termconf->default_bg != n) {
|
|
||||||
termconf->default_bg = n; // this is current not sent through socket, no use to notify
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("default_fg")) {
|
|
||||||
cgi_dbg("Screen default FG: %s", buff);
|
|
||||||
|
|
||||||
if (buff[0] == '#') {
|
|
||||||
// decode hex
|
|
||||||
n = decodehex(buff+1);
|
|
||||||
n += 256;
|
|
||||||
} else {
|
|
||||||
n = atoi(buff);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (termconf->default_fg != n) {
|
|
||||||
termconf->default_fg = n; // this is current not sent through socket, no use to notify
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("parser_tout_ms")) {
|
|
||||||
cgi_dbg("Parser timeout: %s ms", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
if (n >= 0) {
|
|
||||||
termconf->parser_tout_ms = n;
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad parser timeout %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "parser_tout_ms,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("display_tout_ms")) {
|
|
||||||
cgi_dbg("Display update idle timeout: %s ms", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
if (n >= 0) {
|
|
||||||
termconf->display_tout_ms = n;
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad update timeout %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "display_tout_ms,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("display_cooldown_ms")) {
|
|
||||||
cgi_dbg("Display update cooldown: %s ms", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
if (n > 0) {
|
|
||||||
termconf->display_cooldown_ms = n;
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad cooldown %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "display_cooldown_ms,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("fn_alt_mode")) {
|
|
||||||
cgi_dbg("FN alt mode: %s", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
termconf->fn_alt_mode = (bool)n;
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("want_all_fn")) {
|
|
||||||
cgi_dbg("AllFN mode: %s", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
termconf->want_all_fn = (bool)n;
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("crlf_mode")) {
|
|
||||||
cgi_dbg("CRLF mode: %s", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
termconf->crlf_mode = (bool)n;
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("show_buttons")) {
|
|
||||||
cgi_dbg("Show buttons: %s", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
termconf->show_buttons = (bool)n;
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("show_config_links")) {
|
|
||||||
cgi_dbg("Show config links: %s", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
termconf->show_config_links = (bool)n;
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("loopback")) {
|
|
||||||
cgi_dbg("Loopback: %s", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
termconf->loopback = (bool)n;
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("debugbar")) {
|
|
||||||
cgi_dbg("Debugbar: %s", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
termconf->debugbar = (bool)n;
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("allow_decopt_12")) {
|
|
||||||
cgi_dbg("DECOPT 12: %s", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
termconf->allow_decopt_12 = (bool)n;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("ascii_debug")) {
|
|
||||||
cgi_dbg("ascii_debug: %s", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
termconf->ascii_debug = (bool)n;
|
|
||||||
shall_clear_screen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("theme")) {
|
|
||||||
cgi_dbg("Screen color theme: %s", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
if (n >= 0) {
|
|
||||||
termconf->theme = (u8) n;
|
|
||||||
// this can't be notified, page must reload.
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad theme num: %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "theme,");
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("cursor_shape")) {
|
|
||||||
cgi_dbg("Cursor shape: %s", buff);
|
|
||||||
n = atoi(buff);
|
|
||||||
if (n >= 0 && n <= 6 && n != 1) {
|
|
||||||
termconf->cursor_shape = (enum CursorShape) n;
|
|
||||||
topics |= TOPIC_CHANGE_SCREEN_OPTS;
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad cursor_shape num: %s", buff);
|
|
||||||
redir_url += sprintf(redir_url, "cursor_shape,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("term_title")) {
|
|
||||||
cgi_dbg("Terminal title default text: \"%s\"", buff);
|
|
||||||
strncpy_safe(termconf->title, buff, 64); // ATTN those must match the values in
|
|
||||||
topics |= TOPIC_CHANGE_TITLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int btn_i = 1; btn_i <= TERM_BTN_COUNT; btn_i++) {
|
|
||||||
sprintf(buff, "btn%d", btn_i);
|
|
||||||
if (GET_ARG(buff)) {
|
|
||||||
cgi_dbg("Button%d default text: \"%s\"", btn_i, buff);
|
|
||||||
strncpy_safe(termconf->btn[btn_i-1], buff, TERM_BTN_LEN);
|
|
||||||
topics |= TOPIC_CHANGE_BUTTONS;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(buff, "bm%d", btn_i);
|
|
||||||
if (GET_ARG(buff)) {
|
|
||||||
cgi_dbg("Button%d message (ASCII): \"%s\"", btn_i, buff);
|
|
||||||
|
|
||||||
// parse: comma,space or semicolon separated decimal values of ASCII codes
|
|
||||||
char c;
|
|
||||||
char *cp = buff;
|
|
||||||
int char_i = 0;
|
|
||||||
int acu = 0;
|
|
||||||
bool lastsp = 1;
|
|
||||||
char buff_bm[TERM_BTN_MSG_LEN];
|
|
||||||
while ((c = *cp++) != 0) {
|
|
||||||
if (c == ' ' || c == ',' || c == ';') {
|
|
||||||
if(lastsp) continue;
|
|
||||||
|
|
||||||
if (acu==0 || acu>255) {
|
|
||||||
cgi_warn("Bad value! %d", acu);
|
|
||||||
redir_url += sprintf(redir_url, "bm%d,", btn_i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (char_i >= TERM_BTN_MSG_LEN-1) {
|
|
||||||
cgi_warn("Too long! %d", acu);
|
|
||||||
redir_url += sprintf(redir_url, "bm%d,", btn_i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
cgi_dbg("acu %d", acu);
|
|
||||||
buff_bm[char_i++] = (char)acu;
|
|
||||||
|
|
||||||
// prepare for next char
|
|
||||||
acu = 0;
|
|
||||||
lastsp = 1;
|
|
||||||
} else if (c>='0'&&c<='9') {
|
|
||||||
lastsp = 0;
|
|
||||||
acu *= 10;
|
|
||||||
acu += c - '0';
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad syntax!");
|
|
||||||
redir_url += sprintf(redir_url, "bm%d,", btn_i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lastsp && char_i == 0) {
|
|
||||||
cgi_warn("Required!");
|
|
||||||
redir_url += sprintf(redir_url, "bm%d,", btn_i);
|
|
||||||
}
|
|
||||||
if (!lastsp) {
|
|
||||||
buff_bm[char_i++] = (char)acu;
|
|
||||||
}
|
|
||||||
buff_bm[char_i] = 0;
|
|
||||||
cgi_dbg("%s, chari = %d", buff_bm, char_i);
|
|
||||||
|
|
||||||
strncpy(termconf->btn_msg[btn_i-1], buff_bm, TERM_BTN_MSG_LEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
(void)redir_url;
|
||||||
|
|
||||||
@@ -384,17 +71,23 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
|||||||
|
|
||||||
persist_store();
|
persist_store();
|
||||||
|
|
||||||
|
bool shall_clear_screen = false;
|
||||||
|
shall_clear_screen |= (termconf_backup->width != termconf->width);
|
||||||
|
shall_clear_screen |= (termconf_backup->height != termconf->height);
|
||||||
|
shall_clear_screen |= (termconf_backup->ascii_debug != termconf->ascii_debug);
|
||||||
|
|
||||||
if (shall_clear_screen) {
|
if (shall_clear_screen) {
|
||||||
terminal_apply_settings();
|
terminal_apply_settings();
|
||||||
} else {
|
} else {
|
||||||
terminal_apply_settings_noclear();
|
terminal_apply_settings_noclear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shall_init_uart) {
|
if (uart_changed) {
|
||||||
serialInit();
|
sysconf_apply_settings();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (topics) screen_notifyChange(topics);
|
// Trigger a full reload
|
||||||
|
screen_notifyChange(TOPIC_INITIAL);
|
||||||
|
|
||||||
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
|
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
|
||||||
} else {
|
} else {
|
||||||
@@ -416,9 +109,8 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
|||||||
httpd_cgi_state ICACHE_FLASH_ATTR
|
httpd_cgi_state ICACHE_FLASH_ATTR
|
||||||
tplTermCfg(HttpdConnData *connData, char *token, void **arg)
|
tplTermCfg(HttpdConnData *connData, char *token, void **arg)
|
||||||
{
|
{
|
||||||
#define BUFLEN TERM_TITLE_LEN
|
#define BUFLEN 100 // large enough for backdrop
|
||||||
char buff[BUFLEN];
|
char buff[BUFLEN];
|
||||||
char buff2[10];
|
|
||||||
|
|
||||||
if (token == NULL) {
|
if (token == NULL) {
|
||||||
// We're done
|
// We're done
|
||||||
@@ -427,106 +119,18 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg)
|
|||||||
|
|
||||||
strcpy(buff, ""); // fallback
|
strcpy(buff, ""); // fallback
|
||||||
|
|
||||||
if (streq(token, "term_width")) {
|
#define XSTRUCT termconf
|
||||||
sprintf(buff, "%d", termconf->width);
|
#define X XGET_CGI_FUNC
|
||||||
}
|
XTABLE_TERMCONF
|
||||||
else if (streq(token, "term_height")) {
|
#undef X
|
||||||
sprintf(buff, "%d", termconf->height);
|
#undef XSTRUCT
|
||||||
}
|
|
||||||
else if (streq(token, "parser_tout_ms")) {
|
|
||||||
sprintf(buff, "%d", termconf->parser_tout_ms);
|
|
||||||
}
|
|
||||||
else if (streq(token, "display_tout_ms")) {
|
|
||||||
sprintf(buff, "%d", termconf->display_tout_ms);
|
|
||||||
}
|
|
||||||
else if (streq(token, "display_cooldown_ms")) {
|
|
||||||
sprintf(buff, "%d", termconf->display_cooldown_ms);
|
|
||||||
}
|
|
||||||
else if (streq(token, "fn_alt_mode")) {
|
|
||||||
sprintf(buff, "%d", (int)termconf->fn_alt_mode);
|
|
||||||
}
|
|
||||||
else if (streq(token, "want_all_fn")) {
|
|
||||||
sprintf(buff, "%d", (int)termconf->want_all_fn);
|
|
||||||
}
|
|
||||||
else if (streq(token, "crlf_mode")) {
|
|
||||||
sprintf(buff, "%d", (int)termconf->crlf_mode);
|
|
||||||
}
|
|
||||||
else if (streq(token, "show_buttons")) {
|
|
||||||
sprintf(buff, "%d", (int)termconf->show_buttons);
|
|
||||||
}
|
|
||||||
else if (streq(token, "show_config_links")) {
|
|
||||||
sprintf(buff, "%d", (int)termconf->show_config_links);
|
|
||||||
}
|
|
||||||
else if (streq(token, "allow_decopt_12")) {
|
|
||||||
sprintf(buff, "%d", (int)termconf->allow_decopt_12);
|
|
||||||
}
|
|
||||||
else if (streq(token, "ascii_debug")) {
|
|
||||||
sprintf(buff, "%d", (int)termconf->ascii_debug);
|
|
||||||
}
|
|
||||||
else if (streq(token, "loopback")) {
|
|
||||||
sprintf(buff, "%d", (int)termconf->loopback);
|
|
||||||
}
|
|
||||||
else if (streq(token, "debugbar")) {
|
|
||||||
sprintf(buff, "%d", (int)termconf->debugbar);
|
|
||||||
}
|
|
||||||
else if (streq(token, "theme")) {
|
|
||||||
sprintf(buff, "%d", termconf->theme);
|
|
||||||
}
|
|
||||||
else if (streq(token, "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")) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
if (streq(token, buff2)) {
|
|
||||||
strncpy_safe(buff, termconf->btn[btn_i-1], BUFLEN);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(buff2, "bm%d", btn_i);
|
// for uart
|
||||||
if (streq(token, buff2)) {
|
#define XSTRUCT sysconf
|
||||||
char c;
|
#define X XGET_CGI_FUNC
|
||||||
char *bp = buff;
|
XTABLE_SYSCONF
|
||||||
char *cp = termconf->btn_msg[btn_i-1];
|
#undef X
|
||||||
int n = 0;
|
#undef XSTRUCT
|
||||||
while((c = *cp++) != 0) {
|
|
||||||
if(n>0) {
|
|
||||||
*bp = ',';
|
|
||||||
bp++;
|
|
||||||
}
|
|
||||||
bp += sprintf(bp, "%d", (int)c);
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tplSend(connData, buff, -1);
|
tplSend(connData, buff, -1);
|
||||||
return HTTPD_CGI_DONE;
|
return HTTPD_CGI_DONE;
|
||||||
|
|||||||
+15
-203
@@ -19,6 +19,7 @@ Cgi/template routines for the /wifi url.
|
|||||||
#include "wifimgr.h"
|
#include "wifimgr.h"
|
||||||
#include "persist.h"
|
#include "persist.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
#include "config_xmacros.h"
|
||||||
#include "cgi_logging.h"
|
#include "cgi_logging.h"
|
||||||
|
|
||||||
#define SET_REDIR_SUC "/cfg/wifi"
|
#define SET_REDIR_SUC "/cfg/wifi"
|
||||||
@@ -66,46 +67,6 @@ int ICACHE_FLASH_ATTR rssi2perc(int rssi)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert Auth type to string
|
|
||||||
*/
|
|
||||||
const ICACHE_FLASH_ATTR char *auth2str(AUTH_MODE auth)
|
|
||||||
{
|
|
||||||
switch (auth) {
|
|
||||||
case AUTH_OPEN:
|
|
||||||
return "Open";
|
|
||||||
case AUTH_WEP:
|
|
||||||
return "WEP";
|
|
||||||
case AUTH_WPA_PSK:
|
|
||||||
return "WPA";
|
|
||||||
case AUTH_WPA2_PSK:
|
|
||||||
return "WPA2";
|
|
||||||
case AUTH_WPA_WPA2_PSK:
|
|
||||||
return "WPA/WPA2";
|
|
||||||
default:
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert WiFi opmode to string
|
|
||||||
*/
|
|
||||||
const ICACHE_FLASH_ATTR char *opmode2str(WIFI_MODE opmode)
|
|
||||||
{
|
|
||||||
switch (opmode) {
|
|
||||||
case NULL_MODE:
|
|
||||||
return "Disabled";
|
|
||||||
case STATION_MODE:
|
|
||||||
return "Client";
|
|
||||||
case SOFTAP_MODE:
|
|
||||||
return "AP only";
|
|
||||||
case STATIONAP_MODE:
|
|
||||||
return "Client+AP";
|
|
||||||
default:
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback the code calls when a wlan ap scan is done. Basically stores the result in
|
* Callback the code calls when a wlan ap scan is done. Basically stores the result in
|
||||||
* the static cgiWifiAps struct.
|
* the static cgiWifiAps struct.
|
||||||
@@ -363,18 +324,14 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
|
|||||||
bool sta_turned_on = false;
|
bool sta_turned_on = false;
|
||||||
bool sta_ssid_pw_changed = false;
|
bool sta_ssid_pw_changed = false;
|
||||||
|
|
||||||
|
#define XSTRUCT wificonf
|
||||||
|
#define X XSET_CGI_FUNC
|
||||||
|
XTABLE_WIFICONF
|
||||||
|
#undef X
|
||||||
|
|
||||||
// ---- WiFi opmode ----
|
// ---- WiFi opmode ----
|
||||||
|
|
||||||
if (GET_ARG("opmode")) {
|
// those are helpers, not a real prop
|
||||||
cgi_dbg("Setting WiFi opmode to: %s", buff);
|
|
||||||
int mode = atoi(buff);
|
|
||||||
if (mode > NULL_MODE && mode < MAX_MODE) {
|
|
||||||
wificonf->opmode = (WIFI_MODE) mode;
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad opmode value \"%s\"", buff);
|
|
||||||
redir_url += sprintf(redir_url, "opmode,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_ARG("ap_enable")) {
|
if (GET_ARG("ap_enable")) {
|
||||||
cgi_dbg("Enable AP: %s", buff);
|
cgi_dbg("Enable AP: %s", buff);
|
||||||
@@ -399,114 +356,6 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- AP transmit power ----
|
|
||||||
|
|
||||||
if (GET_ARG("tpw")) {
|
|
||||||
cgi_dbg("Setting AP power to: %s", buff);
|
|
||||||
int tpw = atoi(buff);
|
|
||||||
if (tpw >= 0 && tpw <= 82) { // 0 actually isn't 0 but quite low. 82 is very strong
|
|
||||||
if (wificonf->tpw != tpw) {
|
|
||||||
wificonf->tpw = (u8) tpw;
|
|
||||||
wifi_change_flags.ap = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("tpw %s out of allowed range 0-82.", buff);
|
|
||||||
redir_url += sprintf(redir_url, "tpw,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- AP channel (applies in AP-only mode) ----
|
|
||||||
|
|
||||||
if (GET_ARG("ap_channel")) {
|
|
||||||
cgi_info("ap_channel = %s", buff);
|
|
||||||
int channel = atoi(buff);
|
|
||||||
if (channel > 0 && channel < 15) {
|
|
||||||
if (wificonf->ap_channel != channel) {
|
|
||||||
wificonf->ap_channel = (u8) channel;
|
|
||||||
wifi_change_flags.ap = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad channel value \"%s\", allowed 1-14", buff);
|
|
||||||
redir_url += sprintf(redir_url, "ap_channel,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- SSID name in AP mode ----
|
|
||||||
|
|
||||||
if (GET_ARG("ap_ssid")) {
|
|
||||||
// Replace all invalid ASCII with underscores
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 32; i++) {
|
|
||||||
char c = buff[i];
|
|
||||||
if (c == 0) break;
|
|
||||||
if (c < 32 || c >= 127) buff[i] = '_';
|
|
||||||
}
|
|
||||||
buff[i] = 0;
|
|
||||||
|
|
||||||
if (strlen(buff) > 0) {
|
|
||||||
if (!streq(wificonf->ap_ssid, buff)) {
|
|
||||||
cgi_info("Setting SSID to \"%s\"", buff);
|
|
||||||
strncpy_safe(wificonf->ap_ssid, buff, SSID_LEN);
|
|
||||||
wifi_change_flags.ap = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad SSID len.");
|
|
||||||
redir_url += sprintf(redir_url, "ap_ssid,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- AP password ----
|
|
||||||
|
|
||||||
if (GET_ARG("ap_password")) {
|
|
||||||
// Users are free to use any stupid shit in ther password,
|
|
||||||
// but it may lock them out.
|
|
||||||
if (strlen(buff) == 0 || (strlen(buff) >= 8 && strlen(buff) < PASSWORD_LEN-1)) {
|
|
||||||
if (!streq(wificonf->ap_password, buff)) {
|
|
||||||
cgi_info("Setting AP password to \"%s\"", buff);
|
|
||||||
strncpy_safe(wificonf->ap_password, buff, PASSWORD_LEN);
|
|
||||||
wifi_change_flags.ap = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cgi_warn("Bad password len.");
|
|
||||||
redir_url += sprintf(redir_url, "ap_password,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Hide AP network (do not announce) ----
|
|
||||||
|
|
||||||
if (GET_ARG("ap_hidden")) {
|
|
||||||
cgi_dbg("AP hidden = %s", buff);
|
|
||||||
int hidden = atoi(buff);
|
|
||||||
if (hidden != wificonf->ap_hidden) {
|
|
||||||
wificonf->ap_hidden = (hidden != 0);
|
|
||||||
wifi_change_flags.ap = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Station SSID (to connect to) ----
|
|
||||||
|
|
||||||
if (GET_ARG("sta_ssid")) {
|
|
||||||
if (!streq(wificonf->sta_ssid, buff)) {
|
|
||||||
// No verification needed, at worst it fails to connect
|
|
||||||
cgi_info("Setting station SSID to: \"%s\"", buff);
|
|
||||||
strncpy_safe(wificonf->sta_ssid, buff, SSID_LEN);
|
|
||||||
wifi_change_flags.sta = true;
|
|
||||||
sta_ssid_pw_changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Station password (empty for none is allowed) ----
|
|
||||||
|
|
||||||
if (GET_ARG("sta_password")) {
|
|
||||||
if (!streq(wificonf->sta_password, buff)) {
|
|
||||||
// No verification needed, at worst it fails to connect
|
|
||||||
cgi_info("Setting station password to: \"%s\"", buff);
|
|
||||||
strncpy_safe(wificonf->sta_password, buff, PASSWORD_LEN);
|
|
||||||
wifi_change_flags.sta = true;
|
|
||||||
sta_ssid_pw_changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)redir_url;
|
(void)redir_url;
|
||||||
|
|
||||||
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
|
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
|
||||||
@@ -552,7 +401,6 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
|
|||||||
return HTTPD_CGI_DONE;
|
return HTTPD_CGI_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Template code for the WLAN page.
|
//Template code for the WLAN page.
|
||||||
httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg)
|
httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg)
|
||||||
{
|
{
|
||||||
@@ -567,39 +415,18 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
|
|||||||
|
|
||||||
strcpy(buff, ""); // fallback
|
strcpy(buff, ""); // fallback
|
||||||
|
|
||||||
if (streq(token, "opmode_name")) {
|
#define XSTRUCT wificonf
|
||||||
strcpy(buff, opmode2str(wificonf->opmode));
|
#define X XGET_CGI_FUNC
|
||||||
}
|
XTABLE_WIFICONF
|
||||||
else if (streq(token, "opmode")) {
|
#undef X
|
||||||
sprintf(buff, "%d", wificonf->opmode);
|
|
||||||
}
|
// non-config
|
||||||
else if (streq(token, "sta_enable")) {
|
if (streq(token, "sta_enable")) {
|
||||||
sprintf(buff, "%d", (wificonf->opmode & STATION_MODE) != 0);
|
sprintf(buff, "%d", (wificonf->opmode & STATION_MODE) != 0);
|
||||||
}
|
}
|
||||||
else if (streq(token, "ap_enable")) {
|
else if (streq(token, "ap_enable")) {
|
||||||
sprintf(buff, "%d", (wificonf->opmode & SOFTAP_MODE) != 0);
|
sprintf(buff, "%d", (wificonf->opmode & SOFTAP_MODE) != 0);
|
||||||
}
|
}
|
||||||
else if (streq(token, "tpw")) {
|
|
||||||
sprintf(buff, "%d", wificonf->tpw);
|
|
||||||
}
|
|
||||||
else if (streq(token, "ap_channel")) {
|
|
||||||
sprintf(buff, "%d", wificonf->ap_channel);
|
|
||||||
}
|
|
||||||
else if (streq(token, "ap_ssid")) {
|
|
||||||
sprintf(buff, "%s", wificonf->ap_ssid);
|
|
||||||
}
|
|
||||||
else if (streq(token, "ap_password")) {
|
|
||||||
sprintf(buff, "%s", wificonf->ap_password);
|
|
||||||
}
|
|
||||||
else if (streq(token, "ap_hidden")) {
|
|
||||||
sprintf(buff, "%d", wificonf->ap_hidden);
|
|
||||||
}
|
|
||||||
else if (streq(token, "sta_ssid")) {
|
|
||||||
sprintf(buff, "%s", wificonf->sta_ssid);
|
|
||||||
}
|
|
||||||
else if (streq(token, "sta_password")) {
|
|
||||||
sprintf(buff, "%s", wificonf->sta_password);
|
|
||||||
}
|
|
||||||
else if (streq(token, "sta_rssi")) {
|
else if (streq(token, "sta_rssi")) {
|
||||||
sprintf(buff, "%d", wifi_station_get_rssi());
|
sprintf(buff, "%d", wifi_station_get_rssi());
|
||||||
}
|
}
|
||||||
@@ -617,22 +444,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (streq(token, "sta_active_ip")) {
|
else if (streq(token, "sta_active_ip")) {
|
||||||
x = wifi_get_opmode();
|
getStaIpAsString(buff);
|
||||||
connectStatus = wifi_station_get_connect_status();
|
|
||||||
|
|
||||||
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP || wificonf->opmode == SOFTAP_MODE) {
|
|
||||||
strcpy(buff, "");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
struct ip_info info;
|
|
||||||
wifi_get_ip_info(STATION_IF, &info);
|
|
||||||
sprintf(buff, IPSTR, GOOD_IP2STR(info.ip.addr));
|
|
||||||
|
|
||||||
// sprintf(buff, "ip: "IPSTR", mask: "IPSTR", gw: "IPSTR,
|
|
||||||
// GOOD_IP2STR(info.ip.addr),
|
|
||||||
// GOOD_IP2STR(info.netmask.addr),
|
|
||||||
// GOOD_IP2STR(info.gw.addr));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tplSend(connData, buff, -1);
|
tplSend(connData, buff, -1);
|
||||||
|
|||||||
+336
-127
@@ -19,143 +19,352 @@
|
|||||||
* Based on rxvt-unicode screen.C table.
|
* Based on rxvt-unicode screen.C table.
|
||||||
*/
|
*/
|
||||||
static const u16 codepage_0[] ESP_CONST_DATA =
|
static const u16 codepage_0[] ESP_CONST_DATA =
|
||||||
{// Unicode ASCII SYM
|
{ // Unicode ASCII SYM
|
||||||
// %%BEGIN:0%%
|
// %%BEGIN:0%%
|
||||||
0x2666, // 96 ` ♦
|
u'♦', // 0x2666 96 `
|
||||||
0x2592, // 97 a ▒
|
u'▒', // 0x2592 97 a
|
||||||
0x2409, // 98 b HT
|
u'␉', // 0x2409 98 b
|
||||||
0x240c, // 99 c FF
|
u'␌', // 0x240c 99 c FF
|
||||||
0x240d, // 100 d CR
|
u'␍', // 0x240d 100 d CR
|
||||||
0x240a, // 101 e LF
|
u'␊', // 0x240a 101 e LF
|
||||||
0x00b0, // 102 f °
|
u'°', // 0x00b0 102 f
|
||||||
0x00b1, // 103 g ±
|
u'±', // 0x00b1 103 g
|
||||||
0x2424, // 104 h NL
|
u'', // 0x2424 104 h NL
|
||||||
0x240b, // 105 i VT
|
u'␋', // 0x240b 105 i VT
|
||||||
0x2518, // 106 j ┘
|
u'┘', // 0x2518 106 j
|
||||||
0x2510, // 107 k ┐
|
u'┐', // 0x2510 107 k
|
||||||
0x250c, // 108 l ┌
|
u'┌', // 0x250c 108 l
|
||||||
0x2514, // 109 m └
|
u'└', // 0x2514 109 m
|
||||||
0x253c, // 110 n ┼
|
u'┼', // 0x253c 110 n
|
||||||
0x23ba, // 111 o ⎺
|
u'⎺', // 0x23ba 111 o
|
||||||
0x23bb, // 112 p ⎻
|
u'⎻', // 0x23bb 112 p
|
||||||
0x2500, // 113 q ─
|
u'─', // 0x2500 113 q
|
||||||
0x23bc, // 114 r ⎼
|
u'⎼', // 0x23bc 114 r
|
||||||
0x23bd, // 115 s ⎽
|
u'⎽', // 0x23bd 115 s
|
||||||
0x251c, // 116 t ├
|
u'├', // 0x251c 116 t
|
||||||
0x2524, // 117 u ┤
|
u'┤', // 0x2524 117 u
|
||||||
0x2534, // 118 v ┴
|
u'┴', // 0x2534 118 v
|
||||||
0x252c, // 119 w ┬
|
u'┬', // 0x252c 119 w
|
||||||
0x2502, // 120 x │
|
u'│', // 0x2502 120 x
|
||||||
0x2264, // 121 y ≤
|
u'≤', // 0x2264 121 y
|
||||||
0x2265, // 122 z ≥
|
u'≥', // 0x2265 122 z
|
||||||
0x03c0, // 123 { π
|
u'π', // 0x03c0 123 {
|
||||||
0x2260, // 124 | ≠
|
u'≠', // 0x2260 124 |
|
||||||
0x20a4, // 125 } £
|
u'£', // 0x20a4 125 }
|
||||||
0x00b7, // 126 ~ ·
|
u'·', // 0x00b7 126 ~
|
||||||
// %%END:0%%
|
// %%END:0%%
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CODEPAGE_1_BEGIN 33
|
#define CODEPAGE_1_BEGIN 33
|
||||||
#define CODEPAGE_1_END 126
|
#define CODEPAGE_1_END 126
|
||||||
|
|
||||||
|
// DOS, thin and double lines, arrows, angles, diagonals, thick border
|
||||||
static const u16 codepage_1[] ESP_CONST_DATA =
|
static const u16 codepage_1[] ESP_CONST_DATA =
|
||||||
{// Unicode ASCII SYM DOS
|
{// Unicode ASCII DOS
|
||||||
// %%BEGIN:1%%
|
// %%BEGIN:1%%
|
||||||
0x263A, // 33 ! ☺ (1) - low ASCII symbols from DOS, moved to +32
|
u'☺', // 0x263A, 33 ! (1) - low ASCII symbols from DOS, moved to +32
|
||||||
0x263B, // 34 " ☻ (2)
|
u'☻', // 0x263B, 34 " (2)
|
||||||
0x2665, // 35 # ♥ (3)
|
u'♥', // 0x2665, 35 # (3)
|
||||||
0x2666, // 36 $ ♦ (4)
|
u'♦', // 0x2666, 36 $ (4)
|
||||||
0x2663, // 37 % ♣ (5)
|
u'♣', // 0x2663, 37 % (5)
|
||||||
0x2660, // 38 & ♠ (6)
|
u'♠', // 0x2660, 38 & (6)
|
||||||
0x2022, // 39 ' • (7) - inverse dot and circle left out, can be done with SGR
|
u'•', // 0x2022, 39 ' (7) - inverse dot and circle left out, can be done with SGR
|
||||||
0x231B, // 40 ( ⌛ - hourglass (timer icon)
|
u'⌛', // 0x231B, 40 ( - hourglass (timer icon)
|
||||||
0x25CB, // 41 ) ○ (9)
|
u'○', // 0x25CB, 41 ) (9)
|
||||||
0x21AF, // 42 * ↯ - electricity (lightning monitor...)
|
u'↯', // 0x21AF, 42 * - electricity (lightning monitor...)
|
||||||
0x266A, // 43 + ♪ (13)
|
u'♪', // 0x266A, 43 + (13)
|
||||||
0x266B, // 44 , ♫ (14)
|
u'♫', // 0x266B, 44 , (14)
|
||||||
0x263C, // 45 - ☼ (15)
|
u'☼', // 0x263C, 45 - (15)
|
||||||
0x2302, // 46 . ⌂ (127)
|
u'⌂', // 0x2302, 46 . (127)
|
||||||
0x2622, // 47 / ☢ - radioactivity (geiger counter...)
|
u'☢', // 0x2622, 47 / - radioactivity (geiger counter...)
|
||||||
0x2591, // 48 0 ░ (176) - this block is kept aligned and ordered from DOS, moved -128
|
u'░', // 0x2591, 48 0 (176) - this block is kept aligned and ordered from DOS, moved -128
|
||||||
0x2592, // 49 1 ▒ (177)
|
u'▒', // 0x2592, 49 1 (177)
|
||||||
0x2593, // 50 2 ▓ (178)
|
u'▓', // 0x2593, 50 2 (178)
|
||||||
0x2502, // 51 3 │ (179)
|
u'│', // 0x2502, 51 3 (179)
|
||||||
0x2524, // 52 4 ┤ (180)
|
u'┤', // 0x2524, 52 4 (180)
|
||||||
0x2561, // 53 5 ╡ (181)
|
u'╡', // 0x2561, 53 5 (181)
|
||||||
0x2562, // 54 6 ╢ (182)
|
u'╢', // 0x2562, 54 6 (182)
|
||||||
0x2556, // 55 7 ╖ (183)
|
u'╖', // 0x2556, 55 7 (183)
|
||||||
0x2555, // 56 8 ╕ (184)
|
u'╕', // 0x2555, 56 8 (184)
|
||||||
0x2563, // 57 9 ╣ (185)
|
u'╣', // 0x2563, 57 9 (185)
|
||||||
0x2551, // 58 : ║ (186)
|
u'║', // 0x2551, 58 : (186)
|
||||||
0x2557, // 59 ; ╗ (187)
|
u'╗', // 0x2557, 59 ; (187)
|
||||||
0x255D, // 60 < ╝ (188)
|
u'╝', // 0x255D, 60 < (188)
|
||||||
0x255C, // 61 = ╜ (189)
|
u'╜', // 0x255C, 61 = (189)
|
||||||
0x255B, // 62 > ╛ (190)
|
u'╛', // 0x255B, 62 > (190)
|
||||||
0x2510, // 63 ? ┐ (191)
|
u'┐', // 0x2510, 63 ? (191)
|
||||||
0x2514, // 64 @ └ (192)
|
u'└', // 0x2514, 64 @ (192)
|
||||||
0x2534, // 65 A ┴ (193)
|
u'┴', // 0x2534, 65 A (193)
|
||||||
0x252C, // 66 B ┬ (194)
|
u'┬', // 0x252C, 66 B (194)
|
||||||
0x251C, // 67 C ├ (195)
|
u'├', // 0x251C, 67 C (195)
|
||||||
0x2500, // 68 D ─ (196)
|
u'─', // 0x2500, 68 D (196)
|
||||||
0x253C, // 69 E ┼ (197)
|
u'┼', // 0x253C, 69 E (197)
|
||||||
0x255E, // 70 F ╞ (198)
|
u'╞', // 0x255E, 70 F (198)
|
||||||
0x255F, // 71 G ╟ (199)
|
u'╟', // 0x255F, 71 G (199)
|
||||||
0x255A, // 72 H ╚ (200)
|
u'╚', // 0x255A, 72 H (200)
|
||||||
0x2554, // 73 I ╔ (201)
|
u'╔', // 0x2554, 73 I (201)
|
||||||
0x2569, // 74 J ╩ (202)
|
u'╩', // 0x2569, 74 J (202)
|
||||||
0x2566, // 75 K ╦ (203)
|
u'╦', // 0x2566, 75 K (203)
|
||||||
0x2560, // 76 L ╠ (204)
|
u'╠', // 0x2560, 76 L (204)
|
||||||
0x2550, // 77 M ═ (205)
|
u'═', // 0x2550, 77 M (205)
|
||||||
0x256C, // 78 N ╬ (206)
|
u'╬', // 0x256C, 78 N (206)
|
||||||
0x2567, // 79 O ╧ (207)
|
u'╧', // 0x2567, 79 O (207)
|
||||||
0x2568, // 80 P ╨ (208)
|
u'╨', // 0x2568, 80 P (208)
|
||||||
0x2564, // 81 Q ╤ (209)
|
u'╤', // 0x2564, 81 Q (209)
|
||||||
0x2565, // 82 R ╥ (210)
|
u'╥', // 0x2565, 82 R (210)
|
||||||
0x2559, // 83 S ╙ (211)
|
u'╙', // 0x2559, 83 S (211)
|
||||||
0x2558, // 84 T ╘ (212)
|
u'╘', // 0x2558, 84 T (212)
|
||||||
0x2552, // 85 U ╒ (213)
|
u'╒', // 0x2552, 85 U (213)
|
||||||
0x2553, // 86 V ╓ (214)
|
u'╓', // 0x2553, 86 V (214)
|
||||||
0x256B, // 87 W ╫ (215)
|
u'╫', // 0x256B, 87 W (215)
|
||||||
0x256A, // 88 X ╪ (216)
|
u'╪', // 0x256A, 88 X (216)
|
||||||
0x2518, // 89 Y ┘ (217)
|
u'┘', // 0x2518, 89 Y (217)
|
||||||
0x250C, // 90 Z ┌ (218)
|
u'┌', // 0x250C, 90 Z (218)
|
||||||
0x2588, // 91 [ █ (219)
|
u'█', // 0x2588, 91 [ (219)
|
||||||
0x2584, // 92 \ ▄ (220)
|
u'▄', // 0x2584, 92 \ (220)
|
||||||
0x258C, // 93 ] ▌ (221)
|
u'▌', // 0x258C, 93 ] (221)
|
||||||
0x2590, // 94 ^ ▐ (222)
|
u'▐', // 0x2590, 94 ^ (222)
|
||||||
0x2580, // 95 _ ▀ (223)
|
u'▀', // 0x2580, 95 _ (223)
|
||||||
0x2195, // 96 ` ↕ (18) - moved from low DOS ASCII
|
u'↕', // 0x2195, 96 ` (18) - moved from low DOS ASCII
|
||||||
0x2191, // 97 a ↑ (24)
|
u'↑', // 0x2191, 97 a (24)
|
||||||
0x2193, // 98 b ↓ (25)
|
u'↓', // 0x2193, 98 b (25)
|
||||||
0x2192, // 99 c → (26)
|
u'→', // 0x2192, 99 c (26)
|
||||||
0x2190, // 100 d ← (27)
|
u'←', // 0x2190, 100 d (27)
|
||||||
0x2194, // 101 e ↔ (29)
|
u'↔', // 0x2194, 101 e (29)
|
||||||
0x25B2, // 102 f ▲ (30)
|
u'▲', // 0x25B2, 102 f (30)
|
||||||
0x25BC, // 103 g ▼ (31)
|
u'▼', // 0x25BC, 103 g (31)
|
||||||
0x25BA, // 104 h ► (16)
|
u'►', // 0x25BA, 104 h (16)
|
||||||
0x25C4, // 105 i ◄ (17)
|
u'◄', // 0x25C4, 105 i (17)
|
||||||
0x25E2, // 106 j ◢ - added for slanted corners
|
u'◢', // 0x25E2, 106 j - added for slanted corners
|
||||||
0x25E3, // 107 k ◣
|
u'◣', // 0x25E3, 107 k
|
||||||
0x25E4, // 108 l ◤
|
u'◤', // 0x25E4, 108 l
|
||||||
0x25E5, // 109 m ◥
|
u'◥', // 0x25E5, 109 m
|
||||||
0x256D, // 110 n ╭ - rounded corners
|
u'╭', // 0x256D, 110 n - rounded corners
|
||||||
0x256E, // 111 o ╮
|
u'╮', // 0x256E, 111 o
|
||||||
0x256F, // 112 p ╯
|
u'╯', // 0x256F, 112 p
|
||||||
0x2570, // 113 q ╰
|
u'╰', // 0x2570, 113 q
|
||||||
0x0, // 114 r - free positions for future expansion
|
u'╱', // 0x0, 114 r - right up diagonal
|
||||||
0x0, // 115 s
|
u'╲', // 0x0, 115 s - right down diagonal
|
||||||
0x0, // 116 t
|
u'╳', // 0x0, 116 t
|
||||||
0x0, // 117 u
|
u'↺', // 0x0, 117 u
|
||||||
0x0, // 118 v
|
u'↻', // 0x0, 118 v
|
||||||
0x0, // 119 w
|
u'¶', // 0x0, 119 w
|
||||||
0x0, // 120 x
|
u'⏻', // 0x0, 120 x
|
||||||
0x0, // 121 y
|
0xE0B0, // powerline right triangle (filled), 121 y
|
||||||
0x0, // 122 z
|
0xE0B1, // powerline right triangle (hollow), 122 z
|
||||||
0x0, // 123 {
|
0xE0B2, // powerline left triangle (filled), 123 {
|
||||||
0x0, // 124 |
|
0xE0B3, // powerline left triangle (hollow), 124 | - reserved
|
||||||
0x2714, // 125 } ✔ - checkboxes or checklist items
|
u'✔', // 0x2714, 125 } - checkboxes or checklist items
|
||||||
0x2718, // 126 ~ ✘
|
u'✘', // 0x2718, 126 ~
|
||||||
// %%END:1%%
|
// %%END:1%%
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define CODEPAGE_2_BEGIN 33
|
||||||
|
#define CODEPAGE_2_END 126
|
||||||
|
|
||||||
|
// blocks, thick and split lines, line butts
|
||||||
|
static const u16 codepage_2[] ESP_CONST_DATA =
|
||||||
|
{// Unicode ASCII DOS
|
||||||
|
// %%BEGIN:2%%
|
||||||
|
u'▁', // 0x2581, 33 ! - those are ordered this way to allow easy calculating of the right code (for graphs)
|
||||||
|
u'▂', // 0x2582, 34 "
|
||||||
|
u'▃', // 0x2583, 35 #
|
||||||
|
u'▄', // 0x2584, 36 $
|
||||||
|
u'▅', // 0x2585, 37 %
|
||||||
|
u'▆', // 0x2586, 38 &
|
||||||
|
u'▇', // 0x2587, 39 ' - 7-eighths
|
||||||
|
u'█', // 0x2588, 40 ( - full block, shared by both sequences
|
||||||
|
u'▉', // 0x2589, 41 ) - those grow thinner, to re-use the full block
|
||||||
|
u'▊', // 0x258A, 42 *
|
||||||
|
u'▋', // 0x258B, 43 +
|
||||||
|
u'▌', // 0x258C, 44 ,
|
||||||
|
u'▍', // 0x258D, 45 -
|
||||||
|
u'▎', // 0x258E, 46 .
|
||||||
|
u'▏', // 0x258F, 47 /
|
||||||
|
u'▔', // 0x2594, 48 0 - complementary symbols
|
||||||
|
u'▕', // 0x2595, 49 1
|
||||||
|
u'▐', // 0x2590, 50 2
|
||||||
|
u'▀', // 0x2580, 51 3
|
||||||
|
u'▘', // 0x2598, 52 4 - top-left, top-right, bottom-right, bottom-left
|
||||||
|
u'▝', // 0x259D, 53 5
|
||||||
|
u'▗', // 0x2597, 54 6
|
||||||
|
u'▖', // 0x2596, 55 7
|
||||||
|
u'▟', // 0x259F, 56 8
|
||||||
|
u'▙', // 0x2599, 57 9
|
||||||
|
u'▛', // 0x259B, 58 :
|
||||||
|
u'▜', // 0x259C, 59 ;
|
||||||
|
u'▞', // 0x259E, 60 < - complementary diagonals
|
||||||
|
u'▚', // 0x259A, 61 =
|
||||||
|
u'━', // 0x, 62 > - here are thick and thin lines and their joins. it's really quite arbitrary, based on the unicode order, excluding single lines
|
||||||
|
u'┃', // 0x, 63 ?
|
||||||
|
u'┍', // 0x, 64 @
|
||||||
|
u'┎', // 0x, 65 A
|
||||||
|
u'┏', // 0x, 66 B
|
||||||
|
u'┑', // 0x, 67 C
|
||||||
|
u'┒', // 0x, 68 D
|
||||||
|
u'┓', // 0x, 69 E
|
||||||
|
u'┕', // 0x, 70 F
|
||||||
|
u'┖', // 0x, 71 G
|
||||||
|
u'┗', // 0x, 72 H
|
||||||
|
u'┙', // 0x, 73 I
|
||||||
|
u'┚', // 0x, 74 J
|
||||||
|
u'┛', // 0x, 75 K
|
||||||
|
u'┝', // 0x, 76 L
|
||||||
|
u'┞', // 0x, 77 M
|
||||||
|
u'┟', // 0x, 78 N
|
||||||
|
u'┠', // 0x, 79 O
|
||||||
|
u'┡', // 0x, 80 P
|
||||||
|
u'┢', // 0x, 81 Q
|
||||||
|
u'┣', // 0x, 82 R
|
||||||
|
u'┥', // 0x, 83 S
|
||||||
|
u'┦', // 0x, 84 T
|
||||||
|
u'┧', // 0x, 85 U
|
||||||
|
u'┨', // 0x, 86 V
|
||||||
|
u'┩', // 0x, 87 W
|
||||||
|
u'┪', // 0x, 88 X
|
||||||
|
u'┫', // 0x, 89 Y
|
||||||
|
u'┭', // 0x, 90 Z
|
||||||
|
u'┮', // 0x, 91 [
|
||||||
|
u'┯', // 0x, 92 \ .
|
||||||
|
u'┰', // 0x, 93 ]
|
||||||
|
u'┱', // 0x, 94 ^
|
||||||
|
u'┲', // 0x, 95 _
|
||||||
|
u'┳', // 0x, 96 `
|
||||||
|
u'┵', // 0x, 97 a
|
||||||
|
u'┶', // 0x, 98 b
|
||||||
|
u'┷', // 0x, 99 c
|
||||||
|
u'┸', // 0x, 100 d
|
||||||
|
u'┹', // 0x, 101 e
|
||||||
|
u'┺', // 0x, 102 f
|
||||||
|
u'┻', // 0x, 103 g
|
||||||
|
u'┽', // 0x, 104 h
|
||||||
|
u'┾', // 0x, 105 i
|
||||||
|
u'┿', // 0x, 106 j
|
||||||
|
u'╀', // 0x, 107 k
|
||||||
|
u'╁', // 0x, 108 l
|
||||||
|
u'╂', // 0x, 109 m
|
||||||
|
u'╃', // 0x, 110 n
|
||||||
|
u'╄', // 0x, 111 o
|
||||||
|
u'╅', // 0x, 112 p
|
||||||
|
u'╆', // 0x, 113 q
|
||||||
|
u'╇', // 0x, 114 r
|
||||||
|
u'╈', // 0x, 115 s
|
||||||
|
u'╉', // 0x, 116 t
|
||||||
|
u'╊', // 0x, 117 u
|
||||||
|
u'╋', // 0x, 118 v
|
||||||
|
u'╴', // 0x, 119 w - butts
|
||||||
|
u'╵', // 0x, 120 x
|
||||||
|
u'╶', // 0x, 121 y
|
||||||
|
u'╷', // 0x, 122 z
|
||||||
|
u'╸', // 0x, 123 {
|
||||||
|
u'╹', // 0x, 124 |
|
||||||
|
u'╺', // 0x, 125 }
|
||||||
|
u'╻', // 0x, 126 ~
|
||||||
|
// %%END:2%%
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CODEPAGE_3_BEGIN 33
|
||||||
|
#define CODEPAGE_3_END 48
|
||||||
|
|
||||||
|
// dashed lines, split straight lines
|
||||||
|
static const u16 codepage_3[] ESP_CONST_DATA =
|
||||||
|
{// Unicode ASCII DOS
|
||||||
|
// %%BEGIN:3%%
|
||||||
|
u'╌', // 0x, 33 !
|
||||||
|
u'┄', // 0x, 34 "
|
||||||
|
u'┈', // 0x, 35 #
|
||||||
|
u'╍', // 0x, 36 $
|
||||||
|
u'┅', // 0x, 37 %
|
||||||
|
u'┉', // 0x, 38 &
|
||||||
|
u'╎', // 0x, 39 '
|
||||||
|
u'┆', // 0x, 40 (
|
||||||
|
u'┊', // 0x, 41 )
|
||||||
|
u'╏', // 0x, 42 *
|
||||||
|
u'┇', // 0x, 43 +
|
||||||
|
u'┋', // 0x, 44 ,
|
||||||
|
u'╼', // 0x, 45 -
|
||||||
|
u'╽', // 0x, 46 .
|
||||||
|
u'╾', // 0x, 47 /
|
||||||
|
u'╿', // 0x, 48 0
|
||||||
|
// %%END:3%%
|
||||||
|
// u'\0', // 0x, 49 1
|
||||||
|
// u'\0', // 0x, 50 2
|
||||||
|
// u'\0', // 0x, 51 3
|
||||||
|
// u'\0', // 0x, 52 4
|
||||||
|
// u'\0', // 0x, 53 5
|
||||||
|
// u'\0', // 0x, 54 6
|
||||||
|
// u'\0', // 0x, 55 7
|
||||||
|
// u'\0', // 0x, 56 8
|
||||||
|
// u'\0', // 0x, 57 9
|
||||||
|
// u'\0', // 0x, 58 :
|
||||||
|
// u'\0', // 0x, 59 ;
|
||||||
|
// u'\0', // 0x, 60 <
|
||||||
|
// u'\0', // 0x, 61 =
|
||||||
|
// u'\0', // 0x, 62 >
|
||||||
|
// u'\0', // 0x, 63 ?
|
||||||
|
// u'\0', // 0x, 64 @
|
||||||
|
// u'\0', // 0x, 65 A
|
||||||
|
// u'\0', // 0x, 66 B
|
||||||
|
// u'\0', // 0x, 67 C
|
||||||
|
// u'\0', // 0x, 68 D
|
||||||
|
// u'\0', // 0x, 69 E
|
||||||
|
// u'\0', // 0x, 70 F
|
||||||
|
// u'\0', // 0x, 71 G
|
||||||
|
// u'\0', // 0x, 72 H
|
||||||
|
// u'\0', // 0x, 73 I
|
||||||
|
// u'\0', // 0x, 74 J
|
||||||
|
// u'\0', // 0x, 75 K
|
||||||
|
// u'\0', // 0x, 76 L
|
||||||
|
// u'\0', // 0x, 77 M
|
||||||
|
// u'\0', // 0x, 78 N
|
||||||
|
// u'\0', // 0x, 79 O
|
||||||
|
// u'\0', // 0x, 80 P
|
||||||
|
// u'\0', // 0x, 81 Q
|
||||||
|
// u'\0', // 0x, 82 R
|
||||||
|
// u'\0', // 0x, 83 S
|
||||||
|
// u'\0', // 0x, 84 T
|
||||||
|
// u'\0', // 0x, 85 U
|
||||||
|
// u'\0', // 0x, 86 V
|
||||||
|
// u'\0', // 0x, 87 W
|
||||||
|
// u'\0', // 0x, 88 X
|
||||||
|
// u'\0', // 0x, 89 Y
|
||||||
|
// u'\0', // 0x, 90 Z
|
||||||
|
// u'\0', // 0x, 91 [
|
||||||
|
// u'\0', // 0x, 92 \ .
|
||||||
|
// u'\0', // 0x, 93 ]
|
||||||
|
// u'\0', // 0x, 94 ^
|
||||||
|
// u'\0', // 0x, 95 _
|
||||||
|
// u'\0', // 0x, 96 `
|
||||||
|
// u'\0', // 0x, 97 a
|
||||||
|
// u'\0', // 0x, 98 b
|
||||||
|
// u'\0', // 0x, 99 c
|
||||||
|
// u'\0', // 0x, 100 d
|
||||||
|
// u'\0', // 0x, 101 e
|
||||||
|
// u'\0', // 0x, 102 f
|
||||||
|
// u'\0', // 0x, 103 g
|
||||||
|
// u'\0', // 0x, 104 h
|
||||||
|
// u'\0', // 0x, 105 i
|
||||||
|
// u'\0', // 0x, 106 j
|
||||||
|
// u'\0', // 0x, 107 k
|
||||||
|
// u'\0', // 0x, 108 l
|
||||||
|
// u'\0', // 0x, 109 m
|
||||||
|
// u'\0', // 0x, 110 n
|
||||||
|
// u'\0', // 0x, 111 o
|
||||||
|
// u'\0', // 0x, 112 p
|
||||||
|
// u'\0', // 0x, 113 q
|
||||||
|
// u'\0', // 0x, 114 r
|
||||||
|
// u'\0', // 0x, 115 s
|
||||||
|
// u'\0', // 0x, 116 t
|
||||||
|
// u'\0', // 0x, 117 u
|
||||||
|
// u'\0', // 0x, 118 v
|
||||||
|
// u'\0', // 0x, 119 w
|
||||||
|
// u'\0', // 0x, 120 x
|
||||||
|
// u'\0', // 0x, 121 y
|
||||||
|
// u'\0', // 0x, 122 z
|
||||||
|
// u'\0', // 0x, 123 {
|
||||||
|
// u'\0', // 0x, 124 |
|
||||||
|
// u'\0', // 0x, 125 }
|
||||||
|
// u'\0', // 0x, 126 ~
|
||||||
|
};
|
||||||
|
|
||||||
#endif //ESPTERM_CHARACTER_SETS_H_H
|
#endif //ESPTERM_CHARACTER_SETS_H_H
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/10/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "config_xmacros.h"
|
||||||
|
#include "cgi_logging.h"
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR xget_dec(char *buff, u32 value)
|
||||||
|
{
|
||||||
|
sprintf(buff, "%d", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR xget_bool(char *buff, bool value)
|
||||||
|
{
|
||||||
|
sprintf(buff, "%d", value?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR xget_ustring(char *buff, const u8 *value)
|
||||||
|
{
|
||||||
|
sprintf(buff, "%s", (const char *) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR xget_string(char *buff, const char *value)
|
||||||
|
{
|
||||||
|
sprintf(buff, "%s", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR xget_ip(char *buff, const struct ip_addr *value)
|
||||||
|
{
|
||||||
|
sprintf(buff, IPSTR, GOOD_IP2STR(value->addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------- XSET -------------
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_ip(const char *name, struct ip_addr *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
u32 ip = ipaddr_addr(buff);
|
||||||
|
if (ip != 0 && ip != 0xFFFFFFFFUL) {
|
||||||
|
if (field->addr != ip) {
|
||||||
|
field->addr = ip;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad IP: %s", buff);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_bool(const char *name, bool *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
bool enable = (atoi(buff) != 0);
|
||||||
|
|
||||||
|
if (*field != enable) {
|
||||||
|
*field = enable;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_u8(const char *name, u8 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
u32 val = (u32) atoi(buff);
|
||||||
|
|
||||||
|
if (val <= 255) {
|
||||||
|
if (*field != val) {
|
||||||
|
*field = (u8) val;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad value, max 255: %s", buff);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_u32(const char *name, u32 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
u32 val = (u32) atoi(buff);
|
||||||
|
|
||||||
|
if (*field != val) {
|
||||||
|
*field = (u32) val;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_u16(const char *name, u16 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
u16 val = (u16) atoi(buff);
|
||||||
|
|
||||||
|
if (*field != val) {
|
||||||
|
*field = (u16) val;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_string(const char *name, char *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
u32 maxlen = (u32) arg;
|
||||||
|
|
||||||
|
if (arg > 0 && (u32)strlen(buff) > maxlen) {
|
||||||
|
cgi_warn("String too long, max %d", maxlen);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!streq(field, buff)) {
|
||||||
|
strncpy_safe(field, buff, (u32)arg);
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_ustring(const char *name, uchar *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
u32 maxlen = (u32) arg;
|
||||||
|
|
||||||
|
if (arg > 0 && (u32)strlen(buff) > maxlen) {
|
||||||
|
cgi_warn("String too long, max %d", maxlen);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!streq(field, buff)) {
|
||||||
|
strncpy_safe(field, buff, (u32)arg);
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/10/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ESPTERM_CONFIG_XMACROS_H
|
||||||
|
#define ESPTERM_CONFIG_XMACROS_H
|
||||||
|
|
||||||
|
#include <esp8266.h>
|
||||||
|
#include <helpers.h>
|
||||||
|
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
|
||||||
|
#define XJOIN(a, b) a##b
|
||||||
|
|
||||||
|
/**Do nothing xnotify */
|
||||||
|
#define xnoop()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XGET interface
|
||||||
|
*
|
||||||
|
* @param buff - buffer where the value should be printed
|
||||||
|
* @param value - value to render to the buffer
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline bool xget_dummy(char *buff, u32 value)
|
||||||
|
{
|
||||||
|
sprintf(buff, "unused %d", value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xget_dec(char *buff, u32 value);
|
||||||
|
void xget_bool(char *buff, bool value);
|
||||||
|
void xget_ustring(char *buff, const u8 *value);
|
||||||
|
void xget_string(char *buff, const char *value);
|
||||||
|
void xget_ip(char *buff, const struct ip_addr *value);
|
||||||
|
void xget_dhcp(char *buff, const struct dhcps_lease *value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XSET interface
|
||||||
|
*
|
||||||
|
* @param name - field name (for debug)
|
||||||
|
* @param field - pointer to the target field
|
||||||
|
* @param buff - field with the value to be set
|
||||||
|
* @param arg - arbitrary argument, used to modify behavior
|
||||||
|
*
|
||||||
|
* @return xset_result
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum xset_result {
|
||||||
|
XSET_FAIL = 0,
|
||||||
|
XSET_SET = 1,
|
||||||
|
XSET_UNCHANGED = 2,
|
||||||
|
XSET_NONE = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dummy for unimplemented setters
|
||||||
|
static inline enum xset_result xset_dummy(const char *name, void *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result xset_ip(const char *name, struct ip_addr *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_bool(const char *name, bool *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_u8(const char *name, u8 *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_u32(const char *name, u32 *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_u16(const char *name, u16 *field, const char *buff, const void *arg);
|
||||||
|
|
||||||
|
// static string arrays are not &'d, so we don't get **
|
||||||
|
/** @param arg - max string length */
|
||||||
|
enum xset_result xset_string(const char *name, char *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_ustring(const char *name, u8 *field, const char *buff, const void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper template macro for CGI functions that load GET args to structs using XTABLE
|
||||||
|
*
|
||||||
|
* If 'name' is found in connData->getArgs, xset() is called.
|
||||||
|
* If the result is SET, xnotify() is fired. Else, 'name,' is appended to the redir_url buffer.
|
||||||
|
*/
|
||||||
|
#define XSET_CGI_FUNC(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
|
||||||
|
if ((allow) && GET_ARG(#name)) { \
|
||||||
|
type *_p = (type *) &XSTRUCT->name; \
|
||||||
|
enum xset_result res = xset(#name, _p, buff, (const void*) (xsarg)); \
|
||||||
|
if (res == XSET_SET) { xnotify; } \
|
||||||
|
else if (res == XSET_FAIL) { redir_url += sprintf(redir_url, #name","); } \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define XGET_CGI_FUNC(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
|
||||||
|
if ((allow) && streq(token, #name)) xget(buff, deref XSTRUCT->name);
|
||||||
|
|
||||||
|
#define XSTRUCT_FIELD(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
|
||||||
|
type name suffix;
|
||||||
|
|
||||||
|
#define XDUMP_FIELD(type, name, suffix, deref, xget, allow, xset, xsarg, xnotify) \
|
||||||
|
{ xget(buff, deref XSTRUCT->name); dbg(#name " = %s", buff); }
|
||||||
|
|
||||||
|
#endif //ESPTERM_CONFIG_XMACROS_H
|
||||||
@@ -0,0 +1,526 @@
|
|||||||
|
|
||||||
|
/* #line 1 "user/ini_parser.rl" */
|
||||||
|
|
||||||
|
/* Ragel constants block */
|
||||||
|
#include "ini_parser.h"
|
||||||
|
|
||||||
|
// Ragel setup
|
||||||
|
|
||||||
|
/* #line 10 "user/ini_parser.c" */
|
||||||
|
static const char _ini_actions[] ESP_CONST_DATA = {
|
||||||
|
0, 1, 1, 1, 2, 1, 3, 1,
|
||||||
|
4, 1, 5, 1, 6, 1, 7, 1,
|
||||||
|
8, 1, 9, 1, 10, 1, 11, 1,
|
||||||
|
13, 2, 0, 4, 2, 12, 4
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char _ini_eof_actions[] ESP_CONST_DATA = {
|
||||||
|
0, 23, 5, 5, 15, 15, 15, 15,
|
||||||
|
19, 19, 0, 0, 0, 0, 0, 0,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int ini_start = 1;
|
||||||
|
static const int ini_first_final = 12;
|
||||||
|
static const int ini_error = 0;
|
||||||
|
|
||||||
|
static const int ini_en_section = 2;
|
||||||
|
static const int ini_en_keyvalue = 4;
|
||||||
|
static const int ini_en_comment = 8;
|
||||||
|
static const int ini_en_discard2eol = 10;
|
||||||
|
static const int ini_en_main = 1;
|
||||||
|
|
||||||
|
|
||||||
|
/* #line 10 "user/ini_parser.rl" */
|
||||||
|
|
||||||
|
|
||||||
|
// Persistent state
|
||||||
|
static int8_t cs = -1; //!< Ragel's Current State variable
|
||||||
|
static uint32_t buff_i = 0; //!< Write pointer for the buffers
|
||||||
|
static char value_quote = 0; //!< Quote character of the currently collected value
|
||||||
|
static bool value_nextesc = false; //!< Next character is escaped, trated specially, and if quote, as literal quote character
|
||||||
|
static IniParserCallback keyCallback = NULL; //!< Currently assigned callback
|
||||||
|
static void *userdata = NULL; //!< Currently assigned user data for the callback
|
||||||
|
|
||||||
|
// Buffers
|
||||||
|
static char keybuf[INI_KEY_MAX];
|
||||||
|
static char secbuf[INI_KEY_MAX];
|
||||||
|
static char valbuf[INI_VALUE_MAX];
|
||||||
|
|
||||||
|
// See header for doxygen!
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parse_reset_partial(void)
|
||||||
|
{
|
||||||
|
buff_i = 0;
|
||||||
|
value_quote = 0;
|
||||||
|
value_nextesc = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parse_reset(void)
|
||||||
|
{
|
||||||
|
ini_parse_reset_partial();
|
||||||
|
keybuf[0] = secbuf[0] = valbuf[0] = 0;
|
||||||
|
|
||||||
|
/* #line 67 "user/ini_parser.c" */
|
||||||
|
{
|
||||||
|
cs = ini_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #line 41 "user/ini_parser.rl" */
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parser_error(const char* msg)
|
||||||
|
{
|
||||||
|
ini_error("Parser error: %s", msg);
|
||||||
|
ini_parse_reset_partial();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parse_begin(IniParserCallback callback, void *userData)
|
||||||
|
{
|
||||||
|
keyCallback = callback;
|
||||||
|
userdata = userData;
|
||||||
|
ini_parse_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
*ini_parse_end(void)
|
||||||
|
{
|
||||||
|
ini_parse("\n", 1);
|
||||||
|
if (keyCallback) {
|
||||||
|
keyCallback = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ud = userdata;
|
||||||
|
userdata = NULL;
|
||||||
|
return ud;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData)
|
||||||
|
{
|
||||||
|
ini_parse_begin(callback, userData);
|
||||||
|
ini_parse(text, len);
|
||||||
|
ini_parse_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ICACHE_FLASH_ATTR
|
||||||
|
rtrim_buf(char *buf, int32_t end)
|
||||||
|
{
|
||||||
|
if (end > 0) {
|
||||||
|
while ((uint8_t)buf[--end] < 33);
|
||||||
|
end++; // go past the last character
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[end] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parse(const char *newstr, size_t len)
|
||||||
|
{
|
||||||
|
int32_t i;
|
||||||
|
char c;
|
||||||
|
bool isnl;
|
||||||
|
bool isquot;
|
||||||
|
|
||||||
|
// Load new data to Ragel vars
|
||||||
|
const uint8_t *p;
|
||||||
|
const uint8_t *eof;
|
||||||
|
const uint8_t *pe;
|
||||||
|
|
||||||
|
if (len == 0) while(newstr[++len] != 0); // alternative to strlen
|
||||||
|
|
||||||
|
p = (const uint8_t *) newstr;
|
||||||
|
eof = NULL;
|
||||||
|
pe = (const uint8_t *) (newstr + len);
|
||||||
|
|
||||||
|
// Init Ragel on the first run
|
||||||
|
if (cs == -1) {
|
||||||
|
ini_parse_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The parser
|
||||||
|
|
||||||
|
/* #line 152 "user/ini_parser.c" */
|
||||||
|
{
|
||||||
|
const char *_acts;
|
||||||
|
unsigned int _nacts;
|
||||||
|
|
||||||
|
if ( p == pe )
|
||||||
|
goto _test_eof;
|
||||||
|
if ( cs == 0 )
|
||||||
|
goto _out;
|
||||||
|
_resume:
|
||||||
|
switch ( cs ) {
|
||||||
|
case 1:
|
||||||
|
switch( (*p) ) {
|
||||||
|
case 32u: goto tr1;
|
||||||
|
case 35u: goto tr3;
|
||||||
|
case 58u: goto tr0;
|
||||||
|
case 59u: goto tr3;
|
||||||
|
case 61u: goto tr0;
|
||||||
|
case 91u: goto tr4;
|
||||||
|
}
|
||||||
|
if ( (*p) < 9u ) {
|
||||||
|
if ( (*p) <= 8u )
|
||||||
|
goto tr0;
|
||||||
|
} else if ( (*p) > 13u ) {
|
||||||
|
if ( 14u <= (*p) && (*p) <= 31u )
|
||||||
|
goto tr0;
|
||||||
|
} else
|
||||||
|
goto tr1;
|
||||||
|
goto tr2;
|
||||||
|
case 0:
|
||||||
|
goto _out;
|
||||||
|
case 12:
|
||||||
|
goto tr0;
|
||||||
|
case 2:
|
||||||
|
switch( (*p) ) {
|
||||||
|
case 9u: goto tr6;
|
||||||
|
case 32u: goto tr6;
|
||||||
|
case 93u: goto tr5;
|
||||||
|
}
|
||||||
|
if ( (*p) <= 31u )
|
||||||
|
goto tr5;
|
||||||
|
goto tr7;
|
||||||
|
case 3:
|
||||||
|
if ( (*p) == 93u )
|
||||||
|
goto tr8;
|
||||||
|
if ( (*p) > 8u ) {
|
||||||
|
if ( 10u <= (*p) && (*p) <= 31u )
|
||||||
|
goto tr5;
|
||||||
|
} else
|
||||||
|
goto tr5;
|
||||||
|
goto tr7;
|
||||||
|
case 13:
|
||||||
|
goto tr5;
|
||||||
|
case 4:
|
||||||
|
switch( (*p) ) {
|
||||||
|
case 10u: goto tr10;
|
||||||
|
case 58u: goto tr11;
|
||||||
|
case 61u: goto tr11;
|
||||||
|
}
|
||||||
|
goto tr9;
|
||||||
|
case 5:
|
||||||
|
switch( (*p) ) {
|
||||||
|
case 9u: goto tr13;
|
||||||
|
case 10u: goto tr14;
|
||||||
|
case 13u: goto tr15;
|
||||||
|
case 32u: goto tr13;
|
||||||
|
}
|
||||||
|
goto tr12;
|
||||||
|
case 6:
|
||||||
|
switch( (*p) ) {
|
||||||
|
case 10u: goto tr14;
|
||||||
|
case 13u: goto tr15;
|
||||||
|
}
|
||||||
|
goto tr12;
|
||||||
|
case 14:
|
||||||
|
goto tr10;
|
||||||
|
case 7:
|
||||||
|
if ( (*p) == 10u )
|
||||||
|
goto tr14;
|
||||||
|
goto tr10;
|
||||||
|
case 8:
|
||||||
|
switch( (*p) ) {
|
||||||
|
case 10u: goto tr17;
|
||||||
|
case 13u: goto tr18;
|
||||||
|
}
|
||||||
|
goto tr16;
|
||||||
|
case 15:
|
||||||
|
goto tr19;
|
||||||
|
case 9:
|
||||||
|
if ( (*p) == 10u )
|
||||||
|
goto tr17;
|
||||||
|
goto tr19;
|
||||||
|
case 10:
|
||||||
|
switch( (*p) ) {
|
||||||
|
case 10u: goto tr21;
|
||||||
|
case 13u: goto tr22;
|
||||||
|
}
|
||||||
|
goto tr20;
|
||||||
|
case 16:
|
||||||
|
goto tr23;
|
||||||
|
case 11:
|
||||||
|
if ( (*p) == 10u )
|
||||||
|
goto tr21;
|
||||||
|
goto tr23;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr23: cs = 0; goto _again;
|
||||||
|
tr0: cs = 0; goto f0;
|
||||||
|
tr5: cs = 0; goto f4;
|
||||||
|
tr10: cs = 0; goto f7;
|
||||||
|
tr19: cs = 0; goto f11;
|
||||||
|
tr1: cs = 1; goto _again;
|
||||||
|
tr6: cs = 2; goto _again;
|
||||||
|
tr7: cs = 3; goto f5;
|
||||||
|
tr9: cs = 4; goto f8;
|
||||||
|
tr13: cs = 5; goto _again;
|
||||||
|
tr11: cs = 5; goto f9;
|
||||||
|
tr12: cs = 6; goto f10;
|
||||||
|
tr15: cs = 7; goto _again;
|
||||||
|
tr16: cs = 8; goto _again;
|
||||||
|
tr18: cs = 9; goto _again;
|
||||||
|
tr20: cs = 10; goto _again;
|
||||||
|
tr22: cs = 11; goto _again;
|
||||||
|
tr2: cs = 12; goto f1;
|
||||||
|
tr3: cs = 12; goto f2;
|
||||||
|
tr4: cs = 12; goto f3;
|
||||||
|
tr8: cs = 13; goto f6;
|
||||||
|
tr14: cs = 14; goto f10;
|
||||||
|
tr17: cs = 15; goto f12;
|
||||||
|
tr21: cs = 16; goto f13;
|
||||||
|
|
||||||
|
f5: _acts = _ini_actions + 1; goto execFuncs;
|
||||||
|
f6: _acts = _ini_actions + 3; goto execFuncs;
|
||||||
|
f4: _acts = _ini_actions + 5; goto execFuncs;
|
||||||
|
f1: _acts = _ini_actions + 7; goto execFuncs;
|
||||||
|
f8: _acts = _ini_actions + 9; goto execFuncs;
|
||||||
|
f9: _acts = _ini_actions + 11; goto execFuncs;
|
||||||
|
f10: _acts = _ini_actions + 13; goto execFuncs;
|
||||||
|
f7: _acts = _ini_actions + 15; goto execFuncs;
|
||||||
|
f12: _acts = _ini_actions + 17; goto execFuncs;
|
||||||
|
f11: _acts = _ini_actions + 19; goto execFuncs;
|
||||||
|
f13: _acts = _ini_actions + 21; goto execFuncs;
|
||||||
|
f0: _acts = _ini_actions + 23; goto execFuncs;
|
||||||
|
f3: _acts = _ini_actions + 25; goto execFuncs;
|
||||||
|
f2: _acts = _ini_actions + 28; goto execFuncs;
|
||||||
|
|
||||||
|
execFuncs:
|
||||||
|
_nacts = *_acts++;
|
||||||
|
while ( _nacts-- > 0 ) {
|
||||||
|
switch ( *_acts++ ) {
|
||||||
|
case 0:
|
||||||
|
/* #line 130 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
buff_i = 0;
|
||||||
|
{cs = 2;goto _again;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* #line 135 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
if (buff_i >= INI_KEY_MAX) {
|
||||||
|
ini_parser_error("Section name too long");
|
||||||
|
{cs = 10;goto _again;}
|
||||||
|
}
|
||||||
|
keybuf[buff_i++] = (*p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* #line 143 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
// we need a separate buffer for the result, otherwise a failed
|
||||||
|
// partial parse would corrupt the section string
|
||||||
|
rtrim_buf(keybuf, buff_i);
|
||||||
|
for (i = 0; (c = keybuf[i]) != 0; i++) secbuf[i] = c;
|
||||||
|
secbuf[i] = 0;
|
||||||
|
{cs = 1;goto _again;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
/* #line 155 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
ini_parser_error("Syntax error in [section]");
|
||||||
|
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
/* #line 162 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
buff_i = 0;
|
||||||
|
keybuf[buff_i++] = (*p); // add the first char
|
||||||
|
{cs = 4;goto _again;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
/* #line 168 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
if (buff_i >= INI_KEY_MAX) {
|
||||||
|
ini_parser_error("Key too long");
|
||||||
|
{cs = 10;goto _again;}
|
||||||
|
}
|
||||||
|
keybuf[buff_i++] = (*p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
/* #line 176 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
rtrim_buf(keybuf, buff_i);
|
||||||
|
|
||||||
|
// --- Value begin ---
|
||||||
|
buff_i = 0;
|
||||||
|
value_quote = 0;
|
||||||
|
value_nextesc = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
/* #line 185 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
isnl = ((*p) == '\r' || (*p) == '\n');
|
||||||
|
isquot = ((*p) == '\'' || (*p) == '"');
|
||||||
|
|
||||||
|
// detect our starting quote
|
||||||
|
if (isquot && !value_nextesc && buff_i == 0 && value_quote == 0) {
|
||||||
|
value_quote = (*p);
|
||||||
|
goto valueCharDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buff_i >= INI_VALUE_MAX) {
|
||||||
|
ini_parser_error("Value too long");
|
||||||
|
{cs = 10;goto _again;}
|
||||||
|
}
|
||||||
|
|
||||||
|
// end of string - clean up and report
|
||||||
|
if ((!value_nextesc && (*p) == value_quote) || isnl) {
|
||||||
|
if (isnl && value_quote) {
|
||||||
|
ini_parser_error("Unterminated string");
|
||||||
|
{cs = 1;goto _again;}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unquoted: trim from the end
|
||||||
|
if (!value_quote) {
|
||||||
|
rtrim_buf(valbuf, buff_i);
|
||||||
|
} else {
|
||||||
|
valbuf[buff_i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyCallback) {
|
||||||
|
keyCallback(secbuf, keybuf, valbuf, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don't want to discard to eol if the string was terminated by eol
|
||||||
|
// - would delete the next line
|
||||||
|
|
||||||
|
if (isnl) {cs = 1;goto _again;} else {cs = 10;goto _again;}
|
||||||
|
}
|
||||||
|
|
||||||
|
c = (*p);
|
||||||
|
// escape...
|
||||||
|
if (value_nextesc) {
|
||||||
|
if ((*p) == 'n') c = '\n';
|
||||||
|
else if ((*p) == 'r') c = '\r';
|
||||||
|
else if ((*p) == 't') c = '\t';
|
||||||
|
else if ((*p) == 'e') c = '\033';
|
||||||
|
}
|
||||||
|
|
||||||
|
// collecting characters...
|
||||||
|
if (value_nextesc || (*p) != '\\') { // is quoted, or is not a quoting backslash - literal character
|
||||||
|
valbuf[buff_i++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_nextesc = (!value_nextesc && (*p) == '\\');
|
||||||
|
valueCharDone:;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
/* #line 247 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
ini_parser_error("Syntax error in key=value");
|
||||||
|
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
/* #line 257 "user/ini_parser.rl" */
|
||||||
|
{ {cs = 1;goto _again;} }
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
/* #line 258 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
ini_parser_error("Syntax error in comment");
|
||||||
|
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
/* #line 265 "user/ini_parser.rl" */
|
||||||
|
{ {cs = 1;goto _again;} }
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
/* #line 273 "user/ini_parser.rl" */
|
||||||
|
{ {cs = 8;goto _again;} }
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
/* #line 276 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
ini_parser_error("Syntax error in root");
|
||||||
|
{cs = 10;goto _again;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* #line 458 "user/ini_parser.c" */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto _again;
|
||||||
|
|
||||||
|
_again:
|
||||||
|
if ( cs == 0 )
|
||||||
|
goto _out;
|
||||||
|
if ( ++p != pe )
|
||||||
|
goto _resume;
|
||||||
|
_test_eof: {}
|
||||||
|
if ( p == eof )
|
||||||
|
{
|
||||||
|
const char *__acts = _ini_actions + _ini_eof_actions[cs];
|
||||||
|
unsigned int __nacts = (unsigned int) *__acts++;
|
||||||
|
while ( __nacts-- > 0 ) {
|
||||||
|
switch ( *__acts++ ) {
|
||||||
|
case 3:
|
||||||
|
/* #line 155 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
ini_parser_error("Syntax error in [section]");
|
||||||
|
if((*p) == '\n') {cs = 1; if ( p == pe )
|
||||||
|
goto _test_eof;
|
||||||
|
goto _again;} else {cs = 10; if ( p == pe )
|
||||||
|
goto _test_eof;
|
||||||
|
goto _again;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
/* #line 247 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
ini_parser_error("Syntax error in key=value");
|
||||||
|
if((*p) == '\n') {cs = 1; if ( p == pe )
|
||||||
|
goto _test_eof;
|
||||||
|
goto _again;} else {cs = 10; if ( p == pe )
|
||||||
|
goto _test_eof;
|
||||||
|
goto _again;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
/* #line 258 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
ini_parser_error("Syntax error in comment");
|
||||||
|
if((*p) == '\n') {cs = 1; if ( p == pe )
|
||||||
|
goto _test_eof;
|
||||||
|
goto _again;} else {cs = 10; if ( p == pe )
|
||||||
|
goto _test_eof;
|
||||||
|
goto _again;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
/* #line 276 "user/ini_parser.rl" */
|
||||||
|
{
|
||||||
|
ini_parser_error("Syntax error in root");
|
||||||
|
{cs = 10; if ( p == pe )
|
||||||
|
goto _test_eof;
|
||||||
|
goto _again;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* #line 517 "user/ini_parser.c" */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_out: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #line 283 "user/ini_parser.rl" */
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
#ifndef INIPARSE_STREAM_H
|
||||||
|
#define INIPARSE_STREAM_H
|
||||||
|
|
||||||
|
#include <esp8266.h>
|
||||||
|
|
||||||
|
#ifdef DEBUG_INI
|
||||||
|
#define ini_error(fmt, ...) error("[INI] "#fmt, ##__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define ini_error(fmt, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// buffer sizes
|
||||||
|
#define INI_KEY_MAX 64
|
||||||
|
#define INI_VALUE_MAX 256
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INI parser callback, called for each found key-value pair.
|
||||||
|
*
|
||||||
|
* @param section - current section, empty string for global keys
|
||||||
|
* @param key - found key (trimmed of whitespace)
|
||||||
|
* @param value - value, trimmed of quotes or whitespace
|
||||||
|
* @param userData - opaque user data pointer, general purpose
|
||||||
|
*/
|
||||||
|
typedef void (*IniParserCallback)(const char *section, const char *key, const char *value, void *userData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begin parsing a stream
|
||||||
|
*
|
||||||
|
* @param callback - key callback to assign
|
||||||
|
* @param userData - optional user data that willb e passed to the callback
|
||||||
|
*/
|
||||||
|
void ini_parse_begin(IniParserCallback callback, void *userData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End parse stream.
|
||||||
|
* Flushes what remains in the buffer and removes callback.
|
||||||
|
*
|
||||||
|
* @returns userData or NULL if none
|
||||||
|
*/
|
||||||
|
void* ini_parse_end(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a string (needn't be complete line or file)
|
||||||
|
*
|
||||||
|
* @param data - string to parse
|
||||||
|
* @param len - string length (0 = use strlen)
|
||||||
|
*/
|
||||||
|
void ini_parse(const char *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a complete file loaded to string
|
||||||
|
*
|
||||||
|
* @param text - entire file as string
|
||||||
|
* @param len - file length (0 = use strlen)
|
||||||
|
* @param callback - key callback
|
||||||
|
* @param userData - optional user data for key callback
|
||||||
|
*/
|
||||||
|
void ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Explicitly reset the parser
|
||||||
|
*/
|
||||||
|
void ini_parse_reset(void);
|
||||||
|
|
||||||
|
#endif // INIPARSE_STREAM_H
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
|
||||||
|
/* Ragel constants block */
|
||||||
|
#include "ini_parser.h"
|
||||||
|
|
||||||
|
// Ragel setup
|
||||||
|
%%{
|
||||||
|
machine ini;
|
||||||
|
write data;
|
||||||
|
alphtype unsigned char;
|
||||||
|
}%%
|
||||||
|
|
||||||
|
// Persistent state
|
||||||
|
static int8_t cs = -1; //!< Ragel's Current State variable
|
||||||
|
static uint32_t buff_i = 0; //!< Write pointer for the buffers
|
||||||
|
static char value_quote = 0; //!< Quote character of the currently collected value
|
||||||
|
static bool value_nextesc = false; //!< Next character is escaped, trated specially, and if quote, as literal quote character
|
||||||
|
static IniParserCallback keyCallback = NULL; //!< Currently assigned callback
|
||||||
|
static void *userdata = NULL; //!< Currently assigned user data for the callback
|
||||||
|
|
||||||
|
// Buffers
|
||||||
|
static char keybuf[INI_KEY_MAX];
|
||||||
|
static char secbuf[INI_KEY_MAX];
|
||||||
|
static char valbuf[INI_VALUE_MAX];
|
||||||
|
|
||||||
|
// See header for doxygen!
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parse_reset_partial(void)
|
||||||
|
{
|
||||||
|
buff_i = 0;
|
||||||
|
value_quote = 0;
|
||||||
|
value_nextesc = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parse_reset(void)
|
||||||
|
{
|
||||||
|
ini_parse_reset_partial();
|
||||||
|
keybuf[0] = secbuf[0] = valbuf[0] = 0;
|
||||||
|
%% write init;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parser_error(const char* msg)
|
||||||
|
{
|
||||||
|
ini_error("Parser error: %s", msg);
|
||||||
|
ini_parse_reset_partial();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parse_begin(IniParserCallback callback, void *userData)
|
||||||
|
{
|
||||||
|
keyCallback = callback;
|
||||||
|
userdata = userData;
|
||||||
|
ini_parse_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
*ini_parse_end(void)
|
||||||
|
{
|
||||||
|
ini_parse("\n", 1);
|
||||||
|
if (keyCallback) {
|
||||||
|
keyCallback = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ud = userdata;
|
||||||
|
userdata = NULL;
|
||||||
|
return ud;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData)
|
||||||
|
{
|
||||||
|
ini_parse_begin(callback, userData);
|
||||||
|
ini_parse(text, len);
|
||||||
|
ini_parse_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ICACHE_FLASH_ATTR
|
||||||
|
rtrim_buf(char *buf, int32_t end)
|
||||||
|
{
|
||||||
|
if (end > 0) {
|
||||||
|
while ((uint8_t)buf[--end] < 33);
|
||||||
|
end++; // go past the last character
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[end] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
ini_parse(const char *newstr, size_t len)
|
||||||
|
{
|
||||||
|
int32_t i;
|
||||||
|
char c;
|
||||||
|
bool isnl;
|
||||||
|
bool isquot;
|
||||||
|
|
||||||
|
// Load new data to Ragel vars
|
||||||
|
const uint8_t *p;
|
||||||
|
const uint8_t *eof;
|
||||||
|
const uint8_t *pe;
|
||||||
|
|
||||||
|
if (len == 0) while(newstr[++len] != 0); // alternative to strlen
|
||||||
|
|
||||||
|
p = (const uint8_t *) newstr;
|
||||||
|
eof = NULL;
|
||||||
|
pe = (const uint8_t *) (newstr + len);
|
||||||
|
|
||||||
|
// Init Ragel on the first run
|
||||||
|
if (cs == -1) {
|
||||||
|
ini_parse_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The parser
|
||||||
|
%%{
|
||||||
|
#/ *
|
||||||
|
ispace = [ \t]; # inline space
|
||||||
|
wchar = any - 0..8 - 10..31;
|
||||||
|
#apos = '\'';
|
||||||
|
#quot = '\"';
|
||||||
|
nonl = [^\r\n];
|
||||||
|
nl = '\r'? '\n';
|
||||||
|
|
||||||
|
# ---- [SECTION] ----
|
||||||
|
|
||||||
|
action sectionStart {
|
||||||
|
buff_i = 0;
|
||||||
|
fgoto section;
|
||||||
|
}
|
||||||
|
|
||||||
|
action sectionChar {
|
||||||
|
if (buff_i >= INI_KEY_MAX) {
|
||||||
|
ini_parser_error("Section name too long");
|
||||||
|
fgoto discard2eol;
|
||||||
|
}
|
||||||
|
keybuf[buff_i++] = fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
action sectionEnd {
|
||||||
|
// we need a separate buffer for the result, otherwise a failed
|
||||||
|
// partial parse would corrupt the section string
|
||||||
|
rtrim_buf(keybuf, buff_i);
|
||||||
|
for (i = 0; (c = keybuf[i]) != 0; i++) secbuf[i] = c;
|
||||||
|
secbuf[i] = 0;
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
|
||||||
|
section :=
|
||||||
|
(
|
||||||
|
ispace* <: ((wchar - ']')+ @sectionChar) ']' @sectionEnd
|
||||||
|
) $!{
|
||||||
|
ini_parser_error("Syntax error in [section]");
|
||||||
|
if(fc == '\n') fgoto main; else fgoto discard2eol;
|
||||||
|
};
|
||||||
|
|
||||||
|
# ---- KEY=VALUE ----
|
||||||
|
|
||||||
|
action keyStart {
|
||||||
|
buff_i = 0;
|
||||||
|
keybuf[buff_i++] = fc; // add the first char
|
||||||
|
fgoto keyvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
action keyChar {
|
||||||
|
if (buff_i >= INI_KEY_MAX) {
|
||||||
|
ini_parser_error("Key too long");
|
||||||
|
fgoto discard2eol;
|
||||||
|
}
|
||||||
|
keybuf[buff_i++] = fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
action keyEnd {
|
||||||
|
rtrim_buf(keybuf, buff_i);
|
||||||
|
|
||||||
|
// --- Value begin ---
|
||||||
|
buff_i = 0;
|
||||||
|
value_quote = 0;
|
||||||
|
value_nextesc = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
action valueChar {
|
||||||
|
isnl = (fc == '\r' || fc == '\n');
|
||||||
|
isquot = (fc == '\'' || fc == '"');
|
||||||
|
|
||||||
|
// detect our starting quote
|
||||||
|
if (isquot && !value_nextesc && buff_i == 0 && value_quote == 0) {
|
||||||
|
value_quote = fc;
|
||||||
|
goto valueCharDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buff_i >= INI_VALUE_MAX) {
|
||||||
|
ini_parser_error("Value too long");
|
||||||
|
fgoto discard2eol;
|
||||||
|
}
|
||||||
|
|
||||||
|
// end of string - clean up and report
|
||||||
|
if ((!value_nextesc && fc == value_quote) || isnl) {
|
||||||
|
if (isnl && value_quote) {
|
||||||
|
ini_parser_error("Unterminated string");
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unquoted: trim from the end
|
||||||
|
if (!value_quote) {
|
||||||
|
rtrim_buf(valbuf, buff_i);
|
||||||
|
} else {
|
||||||
|
valbuf[buff_i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyCallback) {
|
||||||
|
keyCallback(secbuf, keybuf, valbuf, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don't want to discard to eol if the string was terminated by eol
|
||||||
|
// - would delete the next line
|
||||||
|
|
||||||
|
if (isnl) fgoto main; else fgoto discard2eol;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = fc;
|
||||||
|
// escape...
|
||||||
|
if (value_nextesc) {
|
||||||
|
if (fc == 'n') c = '\n';
|
||||||
|
else if (fc == 'r') c = '\r';
|
||||||
|
else if (fc == 't') c = '\t';
|
||||||
|
else if (fc == 'e') c = '\033';
|
||||||
|
}
|
||||||
|
|
||||||
|
// collecting characters...
|
||||||
|
if (value_nextesc || fc != '\\') { // is quoted, or is not a quoting backslash - literal character
|
||||||
|
valbuf[buff_i++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_nextesc = (!value_nextesc && fc == '\\');
|
||||||
|
valueCharDone:;
|
||||||
|
}
|
||||||
|
|
||||||
|
# use * for key, first char is already consumed.
|
||||||
|
keyvalue :=
|
||||||
|
(
|
||||||
|
([^\n=:]* @keyChar %keyEnd)
|
||||||
|
[=:] ispace* <: nonl* @valueChar nl @valueChar
|
||||||
|
) $!{
|
||||||
|
ini_parser_error("Syntax error in key=value");
|
||||||
|
if(fc == '\n') fgoto main; else fgoto discard2eol;
|
||||||
|
};
|
||||||
|
|
||||||
|
# ---- COMMENT ----
|
||||||
|
|
||||||
|
comment :=
|
||||||
|
(
|
||||||
|
nonl* nl
|
||||||
|
@{ fgoto main; }
|
||||||
|
) $!{
|
||||||
|
ini_parser_error("Syntax error in comment");
|
||||||
|
if(fc == '\n') fgoto main; else fgoto discard2eol;
|
||||||
|
};
|
||||||
|
|
||||||
|
# ---- CLEANUP ----
|
||||||
|
|
||||||
|
discard2eol := nonl* nl @{ fgoto main; };
|
||||||
|
|
||||||
|
# ---- ROOT ----
|
||||||
|
|
||||||
|
main :=
|
||||||
|
(space*
|
||||||
|
(
|
||||||
|
'[' @sectionStart |
|
||||||
|
[#;] @{ fgoto comment; } |
|
||||||
|
(wchar - [\t =:]) @keyStart
|
||||||
|
)
|
||||||
|
) $!{
|
||||||
|
ini_parser_error("Syntax error in root");
|
||||||
|
fgoto discard2eol;
|
||||||
|
};
|
||||||
|
|
||||||
|
write exec;
|
||||||
|
#*/
|
||||||
|
}%%
|
||||||
|
}
|
||||||
+4
-2
@@ -23,8 +23,8 @@
|
|||||||
static int ICACHE_FLASH_ATTR wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen)
|
static int ICACHE_FLASH_ATTR wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen)
|
||||||
{
|
{
|
||||||
if (no == 0) {
|
if (no == 0) {
|
||||||
os_strcpy(user, sysconf->access_name);
|
os_strcpy(user, (const char *) sysconf->access_name);
|
||||||
os_strcpy(pass, sysconf->access_pw);
|
os_strcpy(pass, (const char *) sysconf->access_pw);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (no == 1) {
|
if (no == 1) {
|
||||||
@@ -131,6 +131,8 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = {
|
|||||||
|
|
||||||
ROUTE_TPL_FILE("/cfg/system/?", tplSystemCfg, "/cfg_system.tpl"),
|
ROUTE_TPL_FILE("/cfg/system/?", tplSystemCfg, "/cfg_system.tpl"),
|
||||||
ROUTE_CGI("/cfg/system/set", cgiSystemCfgSetParams),
|
ROUTE_CGI("/cfg/system/set", cgiSystemCfgSetParams),
|
||||||
|
ROUTE_CGI("/cfg/system/export", cgiPersistExport),
|
||||||
|
ROUTE_CGI("/cfg/system/import", cgiPersistImport),
|
||||||
ROUTE_CGI("/cfg/system/write_defaults", cgiPersistWriteDefaults),
|
ROUTE_CGI("/cfg/system/write_defaults", cgiPersistWriteDefaults),
|
||||||
ROUTE_CGI("/cfg/system/restore_defaults", cgiPersistRestoreDefaults),
|
ROUTE_CGI("/cfg/system/restore_defaults", cgiPersistRestoreDefaults),
|
||||||
ROUTE_CGI("/cfg/system/restore_hard", cgiPersistRestoreHard),
|
ROUTE_CGI("/cfg/system/restore_hard", cgiPersistRestoreHard),
|
||||||
|
|||||||
+520
-79
@@ -8,6 +8,7 @@
|
|||||||
#include "character_sets.h"
|
#include "character_sets.h"
|
||||||
#include "utf8.h"
|
#include "utf8.h"
|
||||||
#include "cgi_sockets.h"
|
#include "cgi_sockets.h"
|
||||||
|
#include "cgi_logging.h"
|
||||||
|
|
||||||
TerminalConfigBundle * const termconf = &persist.current.termconf;
|
TerminalConfigBundle * const termconf = &persist.current.termconf;
|
||||||
TerminalConfigBundle termconf_live;
|
TerminalConfigBundle termconf_live;
|
||||||
@@ -38,6 +39,7 @@ static Cell screen[MAX_SCREEN_SIZE];
|
|||||||
|
|
||||||
|
|
||||||
#define TABSTOP_WORDS 5
|
#define TABSTOP_WORDS 5
|
||||||
|
#define LINE_ATTRS_COUNT 64
|
||||||
/**
|
/**
|
||||||
* Screen state structure
|
* Screen state structure
|
||||||
*/
|
*/
|
||||||
@@ -56,9 +58,12 @@ static struct {
|
|||||||
int vm1;
|
int vm1;
|
||||||
|
|
||||||
u32 tab_stops[TABSTOP_WORDS]; // tab stops bitmap
|
u32 tab_stops[TABSTOP_WORDS]; // tab stops bitmap
|
||||||
|
u8 line_attribs[LINE_ATTRS_COUNT]; // assume that's quite enough...
|
||||||
char last_char[4];
|
char last_char[4];
|
||||||
} scr;
|
} scr;
|
||||||
|
|
||||||
|
#define IS_DOUBLE_WIDTH() (scr.line_attribs[cursor.y]&0b001)
|
||||||
|
|
||||||
#define TOP scr.vm0
|
#define TOP scr.vm0
|
||||||
#define BTM scr.vm1
|
#define BTM scr.vm1
|
||||||
#define RH (scr.vm1 - scr.vm0 + 1)
|
#define RH (scr.vm1 - scr.vm0 + 1)
|
||||||
@@ -106,8 +111,19 @@ bool cursor_saved = false;
|
|||||||
static struct {
|
static struct {
|
||||||
bool alternate_active;
|
bool alternate_active;
|
||||||
char title[TERM_TITLE_LEN];
|
char title[TERM_TITLE_LEN];
|
||||||
char btn[TERM_BTN_COUNT][TERM_BTN_LEN];
|
|
||||||
char btn_msg[TERM_BTN_COUNT][TERM_BTN_MSG_LEN];
|
char btn1[TERM_BTN_LEN];
|
||||||
|
char btn2[TERM_BTN_LEN];
|
||||||
|
char btn3[TERM_BTN_LEN];
|
||||||
|
char btn4[TERM_BTN_LEN];
|
||||||
|
char btn5[TERM_BTN_LEN];
|
||||||
|
|
||||||
|
char btn1_msg[TERM_BTN_MSG_LEN];
|
||||||
|
char btn2_msg[TERM_BTN_MSG_LEN];
|
||||||
|
char btn3_msg[TERM_BTN_MSG_LEN];
|
||||||
|
char btn4_msg[TERM_BTN_MSG_LEN];
|
||||||
|
char btn5_msg[TERM_BTN_MSG_LEN];
|
||||||
|
|
||||||
u32 width;
|
u32 width;
|
||||||
u32 height;
|
u32 height;
|
||||||
int vm0;
|
int vm0;
|
||||||
@@ -143,46 +159,200 @@ static struct {
|
|||||||
int x_min, y_min, x_max, y_max;
|
int x_min, y_min, x_max, y_max;
|
||||||
} scr_dirty;
|
} scr_dirty;
|
||||||
|
|
||||||
#define reset_screen_dirty() do { \
|
static void ICACHE_FLASH_ATTR reset_screen_dirty(void)
|
||||||
scr_dirty.x_min = W; \
|
{
|
||||||
scr_dirty.x_max = -1; \
|
scr_dirty.x_min = W;
|
||||||
scr_dirty.y_min = H; \
|
scr_dirty.x_max = -1;
|
||||||
scr_dirty.y_max = -1; \
|
scr_dirty.y_min = H;
|
||||||
} while(0)
|
scr_dirty.y_max = -1;
|
||||||
|
}
|
||||||
|
|
||||||
#define expand_dirty(y0, y1, x0, x1) do { \
|
static void ICACHE_FLASH_ATTR expand_dirty(int y0, int y1, int x0, int x1)
|
||||||
seri_dbg("Expand: X: (%d..%d) -> %d..%d, Y: (%d..%d) -> %d..%d", scr_dirty.x_min, scr_dirty.x_max, x0, x1, scr_dirty.y_min, scr_dirty.y_max, y0, y1); \
|
{
|
||||||
if ((int)(y0) < scr_dirty.y_min) scr_dirty.y_min = (y0); \
|
seri_dbg("Expand: X: (%d..%d) -> %d..%d, Y: (%d..%d) -> %d..%d",
|
||||||
if ((int)(x0) < scr_dirty.x_min) scr_dirty.x_min = (x0); \
|
scr_dirty.x_min, scr_dirty.x_max, x0, x1, scr_dirty.y_min, scr_dirty.y_max, y0, y1);
|
||||||
if ((int)(y1) > scr_dirty.y_max) scr_dirty.y_max = (y1); \
|
|
||||||
if ((int)(x1) > scr_dirty.x_max) scr_dirty.x_max = (x1); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define NOTIFY_LOCK() do { \
|
if ((y0) < scr_dirty.y_min) scr_dirty.y_min = (y0);
|
||||||
notifyLock++; \
|
if ((x0) < scr_dirty.x_min) scr_dirty.x_min = (x0);
|
||||||
} while(0)
|
if ((y1) > scr_dirty.y_max) scr_dirty.y_max = (y1);
|
||||||
|
if ((x1) > scr_dirty.x_max) scr_dirty.x_max = (x1);
|
||||||
#define NOTIFY_DONE(updateTopics) do { \
|
}
|
||||||
lockTopics |= (updateTopics); \
|
|
||||||
if (notifyLock > 0) notifyLock--; \
|
#define NOTIFY_LOCK() { notifyLock++; }
|
||||||
if (notifyLock == 0) { \
|
|
||||||
screen_notifyChange(lockTopics); \
|
static void ICACHE_FLASH_ATTR NOTIFY_DONE(u32 updateTopics)
|
||||||
lockTopics = 0;\
|
{
|
||||||
} \
|
lockTopics |= (updateTopics);
|
||||||
} while(0)
|
if (notifyLock > 0) notifyLock--;
|
||||||
|
if (notifyLock == 0) {
|
||||||
|
screen_notifyChange(lockTopics);
|
||||||
|
lockTopics = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Clear the hanging attribute if the cursor is no longer >= W */
|
/** Clear the hanging attribute if the cursor is no longer >= W */
|
||||||
#define clear_invalid_hanging() do { \
|
static void ICACHE_FLASH_ATTR clear_invalid_hanging(void)
|
||||||
if (cursor.hanging && cursor.x != W-1) { \
|
{
|
||||||
cursor.hanging = false; \
|
if (cursor.hanging && cursor.x != W-1) {
|
||||||
screen_notifyChange(TOPIC_CHANGE_CURSOR); \
|
cursor.hanging = false;
|
||||||
} \
|
screen_notifyChange(TOPIC_CHANGE_CURSOR);
|
||||||
} while(false)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define cursor_inside_region() (cursor.y >= TOP && cursor.y <= BTM)
|
#define cursor_inside_region() (cursor.y >= TOP && cursor.y <= BTM)
|
||||||
|
|
||||||
//region --- Settings ---
|
//region --- Settings ---
|
||||||
|
|
||||||
|
/** Export color for config */
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
xget_term_color(char *buff, u32 value)
|
||||||
|
{
|
||||||
|
if (value < 256) {
|
||||||
|
sprintf(buff, "%d", value);
|
||||||
|
} else {
|
||||||
|
sprintf(buff, "#%06X", value - 256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Export button message as stirng for config */
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
xget_term_bm(char *buff, char *value)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
char *bp = buff;
|
||||||
|
char *cp = value;
|
||||||
|
buff[0] = 0;
|
||||||
|
int n = 0;
|
||||||
|
while((c = *cp++) != 0) {
|
||||||
|
if(n>0) {
|
||||||
|
*bp = ',';
|
||||||
|
bp++;
|
||||||
|
}
|
||||||
|
bp += sprintf(bp, "%d", (u8)c);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_term_bm(const char *name, char *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
|
||||||
|
// parse: comma,space or semicolon separated decimal values of ASCII codes
|
||||||
|
char c;
|
||||||
|
const char *cp = buff;
|
||||||
|
int char_i = 0;
|
||||||
|
int acu = 0;
|
||||||
|
bool lastsp = 1;
|
||||||
|
char buff_bm[TERM_BTN_MSG_LEN];
|
||||||
|
while ((c = *cp++) != 0) {
|
||||||
|
if (c == ' ' || c == ',' || c == ';') {
|
||||||
|
if(lastsp) continue;
|
||||||
|
|
||||||
|
if (acu==0 || acu>255) {
|
||||||
|
cgi_warn("Bad value! %d", acu);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char_i >= TERM_BTN_MSG_LEN-1) {
|
||||||
|
cgi_warn("Too long! %d", char_i);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg("+ %c", acu);
|
||||||
|
buff_bm[char_i++] = (char)acu;
|
||||||
|
|
||||||
|
// prepare for next char
|
||||||
|
acu = 0;
|
||||||
|
lastsp = 1;
|
||||||
|
} else if (c>='0'&&c<='9') {
|
||||||
|
lastsp = 0;
|
||||||
|
acu *= 10;
|
||||||
|
acu += c - '0';
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad syntax!");
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastsp && char_i == 0) {
|
||||||
|
cgi_warn("Required!");
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
if (!lastsp&&acu>0) {
|
||||||
|
dbg("+ %c", acu);
|
||||||
|
buff_bm[char_i++] = (char)acu;
|
||||||
|
}
|
||||||
|
buff_bm[char_i] = 0;
|
||||||
|
|
||||||
|
if (char_i == 0) {
|
||||||
|
cgi_warn("Required!");
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!streq(field, buff_bm)) {
|
||||||
|
strncpy(field, buff_bm, TERM_BTN_MSG_LEN);
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_term_color(const char *name, u32 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
u32 n;
|
||||||
|
|
||||||
|
if (buff[0] == '#') {
|
||||||
|
// decode hex
|
||||||
|
n = decodehex(buff+1);
|
||||||
|
n += 256;
|
||||||
|
} else {
|
||||||
|
n = atoi(buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*field != n) {
|
||||||
|
*field = n;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_term_cursorshape(const char *name, u32 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
u32 n = atoi(buff);
|
||||||
|
|
||||||
|
if (n >= 0 && n <= 6 && n != 1) {
|
||||||
|
if (*field != n) {
|
||||||
|
*field = n;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad cursor_shape num: %s", buff);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore hard defaults
|
* Restore hard defaults
|
||||||
*/
|
*/
|
||||||
@@ -194,10 +364,21 @@ terminal_restore_defaults(void)
|
|||||||
termconf->default_bg = 0;
|
termconf->default_bg = 0;
|
||||||
termconf->default_fg = 7;
|
termconf->default_fg = 7;
|
||||||
sprintf(termconf->title, SCR_DEF_TITLE);
|
sprintf(termconf->title, SCR_DEF_TITLE);
|
||||||
for(int i=1; i <= TERM_BTN_COUNT; i++) {
|
|
||||||
sprintf(termconf->btn[i-1], "%d", i);
|
strcpy(termconf->btn1, "1");
|
||||||
sprintf(termconf->btn_msg[i-1], "%c", i);
|
strcpy(termconf->btn2, "2");
|
||||||
}
|
strcpy(termconf->btn3, "3");
|
||||||
|
strcpy(termconf->btn4, "4");
|
||||||
|
strcpy(termconf->btn5, "5");
|
||||||
|
|
||||||
|
strcpy((char*)termconf->bm1, "\x01");
|
||||||
|
strcpy((char*)termconf->bm2, "\x02");
|
||||||
|
strcpy((char*)termconf->bm3, "\x03");
|
||||||
|
strcpy((char*)termconf->bm4, "\x04");
|
||||||
|
strcpy((char*)termconf->bm5, "\x05");
|
||||||
|
|
||||||
|
termconf->bc1 = termconf->bc2 = termconf->bc3 = termconf->bc4 = termconf->bc5 = 0;
|
||||||
|
|
||||||
termconf->theme = 0;
|
termconf->theme = 0;
|
||||||
termconf->parser_tout_ms = SCR_DEF_PARSER_TOUT_MS;
|
termconf->parser_tout_ms = SCR_DEF_PARSER_TOUT_MS;
|
||||||
termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS;
|
termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS;
|
||||||
@@ -213,6 +394,9 @@ terminal_restore_defaults(void)
|
|||||||
termconf->debugbar = SCR_DEF_DEBUGBAR;
|
termconf->debugbar = SCR_DEF_DEBUGBAR;
|
||||||
termconf->allow_decopt_12 = SCR_DEF_DECOPT12;
|
termconf->allow_decopt_12 = SCR_DEF_DECOPT12;
|
||||||
termconf->ascii_debug = SCR_DEF_ASCIIDEBUG;
|
termconf->ascii_debug = SCR_DEF_ASCIIDEBUG;
|
||||||
|
termconf->backdrop[0] = 0;
|
||||||
|
termconf->font_stack[0] = 0;
|
||||||
|
termconf->font_size = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -231,22 +415,47 @@ terminal_apply_settings_noclear(void)
|
|||||||
{
|
{
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
|
// char buff[64];
|
||||||
|
//#define XSTRUCT termconf
|
||||||
|
//#define X XDUMP_FIELD
|
||||||
|
// XTABLE_TERMCONF
|
||||||
|
//#undef X
|
||||||
|
// return;
|
||||||
|
|
||||||
// Migrate
|
// Migrate
|
||||||
if (termconf->config_version < 1) {
|
if (termconf->config_version < 1) {
|
||||||
persist_dbg("termconf: Updating to version %d", 1);
|
persist_dbg("termconf: Updating to version 1");
|
||||||
termconf->debugbar = SCR_DEF_DEBUGBAR;
|
termconf->debugbar = SCR_DEF_DEBUGBAR;
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
if (termconf->config_version < 2) {
|
if (termconf->config_version < 2) {
|
||||||
persist_dbg("termconf: Updating to version %d", 1);
|
persist_dbg("termconf: Updating to version 2");
|
||||||
termconf->allow_decopt_12 = SCR_DEF_DECOPT12;
|
termconf->allow_decopt_12 = SCR_DEF_DECOPT12;
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
if (termconf->config_version < 3) {
|
if (termconf->config_version < 3) {
|
||||||
persist_dbg("termconf: Updating to version %d", 1);
|
persist_dbg("termconf: Updating to version 3");
|
||||||
termconf->ascii_debug = SCR_DEF_ASCIIDEBUG;
|
termconf->ascii_debug = SCR_DEF_ASCIIDEBUG;
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
|
if (termconf->config_version < 4) {
|
||||||
|
persist_dbg("termconf: Updating to version 4");
|
||||||
|
termconf->backdrop[0] = 0;
|
||||||
|
changed = 1;
|
||||||
|
}
|
||||||
|
if (termconf->config_version < 5) {
|
||||||
|
persist_dbg("termconf: Updating to version 5");
|
||||||
|
termconf->button_count = SCR_DEF_BUTTON_COUNT;
|
||||||
|
changed = 1;
|
||||||
|
}
|
||||||
|
if (termconf->config_version < 6) {
|
||||||
|
persist_dbg("termconf: Updating to version 6");
|
||||||
|
termconf->backdrop[0] = 0;
|
||||||
|
termconf->font_stack[0] = 0;
|
||||||
|
termconf->font_size = 20;
|
||||||
|
termconf->bc1 = termconf->bc2 = termconf->bc3 = termconf->bc4 = termconf->bc5 = 0;
|
||||||
|
changed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
termconf->config_version = TERMCONF_VERSION;
|
termconf->config_version = TERMCONF_VERSION;
|
||||||
|
|
||||||
@@ -346,7 +555,12 @@ screen_reset_sgr(void)
|
|||||||
static void ICACHE_FLASH_ATTR
|
static void ICACHE_FLASH_ATTR
|
||||||
screen_reset_do(bool size, bool labels)
|
screen_reset_do(bool size, bool labels)
|
||||||
{
|
{
|
||||||
ScreenNotifyTopics topics = TOPIC_CHANGE_SCREEN_OPTS | TOPIC_CHANGE_CURSOR | TOPIC_CHANGE_CONTENT_ALL;
|
ScreenNotifyTopics topics =
|
||||||
|
TOPIC_CHANGE_SCREEN_OPTS
|
||||||
|
| TOPIC_CHANGE_CURSOR
|
||||||
|
| TOPIC_CHANGE_CONTENT_ALL
|
||||||
|
| TOPIC_DOUBLE_LINES;
|
||||||
|
|
||||||
NOTIFY_LOCK();
|
NOTIFY_LOCK();
|
||||||
|
|
||||||
// DECopts
|
// DECopts
|
||||||
@@ -383,18 +597,23 @@ screen_reset_do(bool size, bool labels)
|
|||||||
scr.tab_stops[i] = 0x80808080;
|
scr.tab_stops[i] = 0x80808080;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear line attribs
|
||||||
|
memset(scr.line_attribs, 0, LINE_ATTRS_COUNT);
|
||||||
|
|
||||||
if (labels) {
|
if (labels) {
|
||||||
strcpy(termconf_live.title, termconf->title);
|
strcpy(termconf_live.title, termconf->title);
|
||||||
|
strcpy(termconf_live.backdrop, termconf->backdrop);
|
||||||
|
|
||||||
for (int i = 1; i <= TERM_BTN_COUNT; i++) {
|
strcpy(termconf_live.btn1, termconf->btn1);
|
||||||
strcpy(termconf_live.btn[i], termconf->btn[i]);
|
strcpy(termconf_live.btn2, termconf->btn2);
|
||||||
strcpy(termconf_live.btn_msg[i], termconf->btn_msg[i]);
|
strcpy(termconf_live.btn3, termconf->btn3);
|
||||||
}
|
strcpy(termconf_live.btn4, termconf->btn4);
|
||||||
|
strcpy(termconf_live.btn5, termconf->btn5);
|
||||||
|
|
||||||
termconf_live.show_buttons = termconf->show_buttons;
|
termconf_live.show_buttons = termconf->show_buttons;
|
||||||
termconf_live.show_config_links = termconf->show_config_links;
|
termconf_live.show_config_links = termconf->show_config_links;
|
||||||
|
|
||||||
topics |= TOPIC_CHANGE_TITLE | TOPIC_CHANGE_BUTTONS;
|
topics |= TOPIC_CHANGE_TITLE | TOPIC_CHANGE_BUTTONS | TOPIC_CHANGE_BACKDROP;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial values in the save buffer in case of receiving restore without storing first
|
// initial values in the save buffer in case of receiving restore without storing first
|
||||||
@@ -445,8 +664,11 @@ screen_swap_state(bool alternate)
|
|||||||
ansi_dbg("Swap to alternate");
|
ansi_dbg("Swap to alternate");
|
||||||
// store old state
|
// store old state
|
||||||
memcpy(state_backup.title, termconf_live.title, TERM_TITLE_LEN);
|
memcpy(state_backup.title, termconf_live.title, TERM_TITLE_LEN);
|
||||||
memcpy(state_backup.btn, termconf_live.btn, sizeof(termconf_live.btn));
|
|
||||||
memcpy(state_backup.btn_msg, termconf_live.btn_msg, sizeof(termconf_live.btn_msg));
|
// copy multiple fields at once
|
||||||
|
memcpy(state_backup.btn1, termconf_live.btn1, sizeof(termconf_live.btn1)*TERM_BTN_COUNT);
|
||||||
|
memcpy(state_backup.btn1_msg, termconf_live.bm1, sizeof(termconf_live.bm1)*TERM_BTN_COUNT);
|
||||||
|
|
||||||
memcpy(state_backup.tab_stops, scr.tab_stops, sizeof(scr.tab_stops));
|
memcpy(state_backup.tab_stops, scr.tab_stops, sizeof(scr.tab_stops));
|
||||||
state_backup.vm0 = scr.vm0;
|
state_backup.vm0 = scr.vm0;
|
||||||
state_backup.vm1 = scr.vm1;
|
state_backup.vm1 = scr.vm1;
|
||||||
@@ -458,8 +680,11 @@ screen_swap_state(bool alternate)
|
|||||||
else {
|
else {
|
||||||
ansi_dbg("Unswap from alternate");
|
ansi_dbg("Unswap from alternate");
|
||||||
memcpy(termconf_live.title, state_backup.title, TERM_TITLE_LEN);
|
memcpy(termconf_live.title, state_backup.title, TERM_TITLE_LEN);
|
||||||
memcpy(termconf_live.btn, state_backup.btn, sizeof(termconf_live.btn));
|
|
||||||
memcpy(termconf_live.btn_msg, state_backup.btn_msg, sizeof(termconf_live.btn_msg));
|
// copy multiple fields at once
|
||||||
|
memcpy(termconf_live.btn1, state_backup.btn1, sizeof(termconf_live.btn1)*TERM_BTN_COUNT);
|
||||||
|
memcpy(termconf_live.bm1, state_backup.btn1_msg, sizeof(termconf_live.bm1)*TERM_BTN_COUNT);
|
||||||
|
|
||||||
memcpy(scr.tab_stops, state_backup.tab_stops, sizeof(scr.tab_stops));
|
memcpy(scr.tab_stops, state_backup.tab_stops, sizeof(scr.tab_stops));
|
||||||
scr.vm0 = state_backup.vm0;
|
scr.vm0 = state_backup.vm0;
|
||||||
scr.vm1 = state_backup.vm1;
|
scr.vm1 = state_backup.vm1;
|
||||||
@@ -474,6 +699,32 @@ screen_swap_state(bool alternate)
|
|||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
//region --- Double lines ---
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
screen_set_line_attr(uint8_t double_w, uint8_t double_h_top, uint8_t double_h_bot)
|
||||||
|
{
|
||||||
|
NOTIFY_LOCK();
|
||||||
|
u8 attr = scr.line_attribs[cursor.y];
|
||||||
|
if (double_w==2) attr |= 0b001;
|
||||||
|
else if (double_w==1) attr &= ~0b001;
|
||||||
|
if (double_h_top==2) attr |= 0b010;
|
||||||
|
else if (double_h_top==1) attr &= ~0b010;
|
||||||
|
if (double_h_bot==2) attr |= 0b100;
|
||||||
|
else if (double_h_bot==1) attr &= ~0b100;
|
||||||
|
scr.line_attribs[cursor.y] = attr;
|
||||||
|
|
||||||
|
if (attr & 0b001) {
|
||||||
|
// if we're using double width - clamp cursor X position
|
||||||
|
// TODO this should happen in all cursor ops - now it can sometimes go offscreen
|
||||||
|
if (cursor.x >= W/2) cursor.x = W/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
NOTIFY_DONE(TOPIC_DOUBLE_LINES);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
//region --- Tab stops ---
|
//region --- Tab stops ---
|
||||||
|
|
||||||
void ICACHE_FLASH_ATTR
|
void ICACHE_FLASH_ATTR
|
||||||
@@ -509,6 +760,7 @@ next_tab_stop(void)
|
|||||||
{
|
{
|
||||||
// cursor must never go past EOL
|
// cursor must never go past EOL
|
||||||
if (cursor.x >= W-1) return -1;
|
if (cursor.x >= W-1) return -1;
|
||||||
|
if (IS_DOUBLE_WIDTH() && cursor.x >= W/2-1) return -1;
|
||||||
|
|
||||||
// find first word to inspect
|
// find first word to inspect
|
||||||
int idx = (cursor.x+1)/32;
|
int idx = (cursor.x+1)/32;
|
||||||
@@ -520,6 +772,7 @@ next_tab_stop(void)
|
|||||||
for(;offs<32;offs++) {
|
for(;offs<32;offs++) {
|
||||||
cp++;
|
cp++;
|
||||||
if (cp >= W) return -1;
|
if (cp >= W) return -1;
|
||||||
|
if (IS_DOUBLE_WIDTH() && cp >= W/2) return -1;
|
||||||
if (w & 1) return cp;
|
if (w & 1) return cp;
|
||||||
w >>= 1;
|
w >>= 1;
|
||||||
}
|
}
|
||||||
@@ -577,6 +830,7 @@ screen_tab_forward(int count)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cursor.x = W - 1;
|
cursor.x = W - 1;
|
||||||
|
if (IS_DOUBLE_WIDTH()) cursor.x = W/2 - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
|
NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
|
||||||
@@ -778,19 +1032,23 @@ screen_clear(ClearMode mode)
|
|||||||
unicode_cache_clear();
|
unicode_cache_clear();
|
||||||
clear_range_noutf(0, W * H - 1);
|
clear_range_noutf(0, W * H - 1);
|
||||||
scr.last_char[0] = 0;
|
scr.last_char[0] = 0;
|
||||||
|
for (int i = 0; i < LINE_ATTRS_COUNT; i++) scr.line_attribs[i] = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CLEAR_FROM_CURSOR:
|
case CLEAR_FROM_CURSOR:
|
||||||
clear_range_utf((cursor.y * W) + cursor.x, W * H - 1);
|
clear_range_utf((cursor.y * W) + cursor.x, W * H - 1);
|
||||||
expand_dirty(cursor.y, H-1, 0, W-1);
|
expand_dirty(cursor.y, H-1, 0, W-1);
|
||||||
|
for (int i = cursor.y; i < LINE_ATTRS_COUNT; i++) scr.line_attribs[i] = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CLEAR_TO_CURSOR:
|
case CLEAR_TO_CURSOR:
|
||||||
clear_range_utf(0, (cursor.y * W) + cursor.x);
|
clear_range_utf(0, (cursor.y * W) + cursor.x);
|
||||||
expand_dirty(0, cursor.y, 0, W-1);
|
expand_dirty(0, cursor.y, 0, W-1);
|
||||||
|
for (int i = 0; i <= cursor.y; i++) scr.line_attribs[i] = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
NOTIFY_DONE(mode == CLEAR_ALL ? TOPIC_CHANGE_CONTENT_ALL : TOPIC_CHANGE_CONTENT_PART);
|
NOTIFY_DONE((mode == CLEAR_ALL ? TOPIC_CHANGE_CONTENT_ALL : TOPIC_CHANGE_CONTENT_PART)
|
||||||
|
| TOPIC_DOUBLE_LINES);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -842,11 +1100,15 @@ screen_insert_lines(unsigned int lines)
|
|||||||
int targetStart = cursor.y + lines;
|
int targetStart = cursor.y + lines;
|
||||||
if (targetStart > BTM) {
|
if (targetStart > BTM) {
|
||||||
clear_range_utf(cursor.y*W, (BTM+1)*W-1);
|
clear_range_utf(cursor.y*W, (BTM+1)*W-1);
|
||||||
|
for (int i = cursor.y; i <= BTM; i++) {
|
||||||
|
scr.line_attribs[i] = 0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// do the moving
|
// do the moving
|
||||||
for (int i = BTM; i >= targetStart; i--) {
|
for (int i = BTM; i >= targetStart; i--) {
|
||||||
utf_free_row(i); // release old characters
|
utf_free_row(i); // release old characters
|
||||||
copy_row(i, i - lines);
|
copy_row(i, i - lines);
|
||||||
|
scr.line_attribs[i] = scr.line_attribs[i-lines];
|
||||||
if (i != targetStart) utf_backup_row(i);
|
if (i != targetStart) utf_backup_row(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -858,7 +1120,7 @@ screen_insert_lines(unsigned int lines)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
expand_dirty(cursor.y, BTM, 0, W - 1);
|
expand_dirty(cursor.y, BTM, 0, W - 1);
|
||||||
NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
|
NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART|TOPIC_DOUBLE_LINES);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICACHE_FLASH_ATTR
|
void ICACHE_FLASH_ATTR
|
||||||
@@ -873,18 +1135,26 @@ screen_delete_lines(unsigned int lines)
|
|||||||
// clear the entire rest of the screen
|
// clear the entire rest of the screen
|
||||||
movedBlockEnd = cursor.y;
|
movedBlockEnd = cursor.y;
|
||||||
clear_range_utf(movedBlockEnd*W, (BTM+1)*W-1);
|
clear_range_utf(movedBlockEnd*W, (BTM+1)*W-1);
|
||||||
|
for (int i = movedBlockEnd; i <= BTM; i++) {
|
||||||
|
scr.line_attribs[i] = 0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// move some lines up, clear the rest
|
// move some lines up, clear the rest
|
||||||
for (int i = cursor.y; i <= movedBlockEnd; i++) {
|
for (int i = cursor.y; i <= movedBlockEnd; i++) {
|
||||||
utf_free_row(i);
|
utf_free_row(i);
|
||||||
copy_row(i, i+lines);
|
copy_row(i, i+lines);
|
||||||
|
scr.line_attribs[i] = scr.line_attribs[i+lines];
|
||||||
if (i != movedBlockEnd) utf_backup_row(i);
|
if (i != movedBlockEnd) utf_backup_row(i);
|
||||||
}
|
}
|
||||||
clear_range_noutf((movedBlockEnd+1)*W, (BTM+1)*W-1);
|
clear_range_noutf((movedBlockEnd+1)*W, (BTM+1)*W-1);
|
||||||
|
|
||||||
|
for (int i = movedBlockEnd+1; i <= BTM; i++) {
|
||||||
|
scr.line_attribs[i] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expand_dirty(cursor.y, BTM, 0, W - 1);
|
expand_dirty(cursor.y, BTM, 0, W - 1);
|
||||||
NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
|
NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART|TOPIC_DOUBLE_LINES);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICACHE_FLASH_ATTR
|
void ICACHE_FLASH_ATTR
|
||||||
@@ -982,8 +1252,8 @@ screen_resize(int rows, int cols)
|
|||||||
if (W == cols && H == rows) return; // Do nothing
|
if (W == cols && H == rows) return; // Do nothing
|
||||||
|
|
||||||
NOTIFY_LOCK();
|
NOTIFY_LOCK();
|
||||||
W = cols;
|
W = (u32) cols;
|
||||||
H = rows;
|
H = (u32) rows;
|
||||||
screen_reset_on_resize();
|
screen_reset_on_resize();
|
||||||
NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS|TOPIC_CHANGE_CONTENT_ALL|TOPIC_CHANGE_CURSOR);
|
NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS|TOPIC_CHANGE_CONTENT_ALL|TOPIC_CHANGE_CURSOR);
|
||||||
}
|
}
|
||||||
@@ -1005,10 +1275,82 @@ void ICACHE_FLASH_ATTR
|
|||||||
screen_set_button_text(int num, const char *text)
|
screen_set_button_text(int num, const char *text)
|
||||||
{
|
{
|
||||||
NOTIFY_LOCK();
|
NOTIFY_LOCK();
|
||||||
strncpy(termconf_live.btn[num-1], text, TERM_BTN_LEN);
|
|
||||||
|
if (num >= 1 && num <= TERM_BTN_COUNT) {
|
||||||
|
char *buf = TERM_BTN_N(&termconf_live, num - 1);
|
||||||
|
strncpy(buf, text, TERM_BTN_MSG_LEN);
|
||||||
|
}
|
||||||
|
else ansi_warn("Bad button num: %d", num);
|
||||||
|
|
||||||
NOTIFY_DONE(TOPIC_CHANGE_BUTTONS);
|
NOTIFY_DONE(TOPIC_CHANGE_BUTTONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to set terminal button label
|
||||||
|
* @param num - button number 1-5
|
||||||
|
* @param str - button text
|
||||||
|
*/
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
screen_set_button_message(int num, const char *msg)
|
||||||
|
{
|
||||||
|
NOTIFY_LOCK();
|
||||||
|
|
||||||
|
if (num >= 1 && num <= TERM_BTN_COUNT) {
|
||||||
|
char *buf = TERM_BM_N(&termconf_live, num - 1);
|
||||||
|
strncpy(buf, msg, TERM_BTN_MSG_LEN);
|
||||||
|
}
|
||||||
|
else ansi_warn("Bad button num: %d", num);
|
||||||
|
|
||||||
|
NOTIFY_DONE(TOPIC_CHANGE_BUTTONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to set terminal button label
|
||||||
|
* @param num - button number 1-5
|
||||||
|
* @param str - button text
|
||||||
|
*/
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
screen_set_button_color(int num, const char *buf)
|
||||||
|
{
|
||||||
|
NOTIFY_LOCK();
|
||||||
|
|
||||||
|
if (num >= 1 && num <= TERM_BTN_COUNT) {
|
||||||
|
u32 *fieldptr = &termconf_live.bc1 + (num-1);
|
||||||
|
xset_term_color("", fieldptr, buf, NULL);
|
||||||
|
}
|
||||||
|
else ansi_warn("Bad button num: %d", num);
|
||||||
|
|
||||||
|
NOTIFY_DONE(TOPIC_CHANGE_BUTTONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set button count
|
||||||
|
* @param count - count 1-5
|
||||||
|
*/
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
screen_set_button_count(int count)
|
||||||
|
{
|
||||||
|
NOTIFY_LOCK();
|
||||||
|
if (count >= 0 && count <= 5) {
|
||||||
|
dbg("%d ", count);
|
||||||
|
termconf_live.button_count = (u8) count;
|
||||||
|
}
|
||||||
|
else ansi_warn("Bad button count: %d", count);
|
||||||
|
NOTIFY_DONE(TOPIC_CHANGE_BUTTONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to set terminalbackdrop
|
||||||
|
* @param url - url
|
||||||
|
*/
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
screen_set_backdrop(const char *url)
|
||||||
|
{
|
||||||
|
NOTIFY_LOCK();
|
||||||
|
strncpy(termconf_live.backdrop, url, TERM_BACKDROP_LEN);
|
||||||
|
NOTIFY_DONE(TOPIC_CHANGE_BACKDROP);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shift screen upwards
|
* Shift screen upwards
|
||||||
*/
|
*/
|
||||||
@@ -1019,6 +1361,9 @@ screen_scroll_up(unsigned int lines)
|
|||||||
if (lines >= RH) {
|
if (lines >= RH) {
|
||||||
// clear entire region
|
// clear entire region
|
||||||
clear_range_utf(TOP * W, (BTM + 1) * W - 1);
|
clear_range_utf(TOP * W, (BTM + 1) * W - 1);
|
||||||
|
for (int i = TOP; i <= BTM; i++) {
|
||||||
|
scr.line_attribs[i] = 0;
|
||||||
|
}
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1031,14 +1376,18 @@ screen_scroll_up(unsigned int lines)
|
|||||||
for (y = TOP; y <= BTM - lines; y++) {
|
for (y = TOP; y <= BTM - lines; y++) {
|
||||||
utf_free_row(y);
|
utf_free_row(y);
|
||||||
copy_row(y, y+lines);
|
copy_row(y, y+lines);
|
||||||
|
scr.line_attribs[y] = scr.line_attribs[y+lines];
|
||||||
if (y < BTM - lines) utf_backup_row(y);
|
if (y < BTM - lines) utf_backup_row(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_range_noutf(y * W, (BTM + 1) * W - 1);
|
clear_range_noutf(y * W, (BTM + 1) * W - 1);
|
||||||
|
for (int i = y; i <= BTM; i++) {
|
||||||
|
scr.line_attribs[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
expand_dirty(TOP, BTM, 0, W - 1);
|
expand_dirty(TOP, BTM, 0, W - 1);
|
||||||
NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
|
NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART|TOPIC_DOUBLE_LINES);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1051,6 +1400,9 @@ screen_scroll_down(unsigned int lines)
|
|||||||
if (lines >= RH) {
|
if (lines >= RH) {
|
||||||
// clear entire region
|
// clear entire region
|
||||||
clear_range_utf(TOP * W, (BTM + 1) * W - 1);
|
clear_range_utf(TOP * W, (BTM + 1) * W - 1);
|
||||||
|
for (int i = TOP; i <= BTM; i++) {
|
||||||
|
scr.line_attribs[i] = 0;
|
||||||
|
}
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1063,13 +1415,17 @@ screen_scroll_down(unsigned int lines)
|
|||||||
for (y = BTM; y >= TOP+lines; y--) {
|
for (y = BTM; y >= TOP+lines; y--) {
|
||||||
utf_free_row(y);
|
utf_free_row(y);
|
||||||
copy_row(y, y-lines);
|
copy_row(y, y-lines);
|
||||||
|
scr.line_attribs[y] = scr.line_attribs[y-lines];
|
||||||
if (y > TOP + lines) utf_backup_row(y);
|
if (y > TOP + lines) utf_backup_row(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_range_noutf(TOP * W, TOP * W + lines * W - 1);
|
clear_range_noutf(TOP * W, (TOP + lines) * W - 1);
|
||||||
|
for (int i = TOP; i < TOP + lines; i++) {
|
||||||
|
scr.line_attribs[i] = 0;
|
||||||
|
}
|
||||||
done:
|
done:
|
||||||
expand_dirty(TOP, BTM, 0, W - 1);
|
expand_dirty(TOP, BTM, 0, W - 1);
|
||||||
NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART);
|
NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART|TOPIC_DOUBLE_LINES);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set scrolling region */
|
/** Set scrolling region */
|
||||||
@@ -1102,7 +1458,7 @@ void ICACHE_FLASH_ATTR
|
|||||||
screen_cursor_shape(enum CursorShape shape)
|
screen_cursor_shape(enum CursorShape shape)
|
||||||
{
|
{
|
||||||
NOTIFY_LOCK();
|
NOTIFY_LOCK();
|
||||||
if (shape == CURSOR_DEFAULT) shape = termconf->cursor_shape;
|
if (shape == CURSOR_DEFAULT) shape = (enum CursorShape) termconf->cursor_shape;
|
||||||
termconf_live.cursor_shape = shape;
|
termconf_live.cursor_shape = shape;
|
||||||
NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS);
|
NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS);
|
||||||
}
|
}
|
||||||
@@ -1174,6 +1530,9 @@ screen_cursor_set_x(int x)
|
|||||||
// hanging happens when the cursor is virtually at col=81, which
|
// hanging happens when the cursor is virtually at col=81, which
|
||||||
// cannot be set using the cursor-set commands.
|
// cannot be set using the cursor-set commands.
|
||||||
cursor.hanging = false;
|
cursor.hanging = false;
|
||||||
|
|
||||||
|
if (IS_DOUBLE_WIDTH() && cursor.x >= W/2) cursor.x = W/2-1;
|
||||||
|
|
||||||
NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
|
NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1218,6 +1577,7 @@ screen_cursor_move(int dy, int dx, bool scroll)
|
|||||||
cursor.x += dx;
|
cursor.x += dx;
|
||||||
cursor.y += dy;
|
cursor.y += dy;
|
||||||
if (cursor.x >= (int)W) cursor.x = W - 1;
|
if (cursor.x >= (int)W) cursor.x = W - 1;
|
||||||
|
if (IS_DOUBLE_WIDTH() && cursor.x >= W/2) cursor.x = W/2-1;
|
||||||
if (cursor.x < (int)0) {
|
if (cursor.x < (int)0) {
|
||||||
if (cursor.auto_wrap && cursor.reverse_wrap) {
|
if (cursor.auto_wrap && cursor.reverse_wrap) {
|
||||||
// this is mimicking a behavior from xterm that allows any number of steps backwards with reverse wraparound enabled
|
// this is mimicking a behavior from xterm that allows any number of steps backwards with reverse wraparound enabled
|
||||||
@@ -1324,6 +1684,8 @@ screen_cursor_restore(bool withAttrs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IS_DOUBLE_WIDTH() && cursor.x >= W/2) cursor.x = W/2-1;
|
||||||
|
|
||||||
NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
|
NOTIFY_DONE(TOPIC_CHANGE_CURSOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1337,7 +1699,7 @@ screen_back_index(int count)
|
|||||||
cursor.x = new_x;
|
cursor.x = new_x;
|
||||||
} else {
|
} else {
|
||||||
cursor.x = 0;
|
cursor.x = 0;
|
||||||
screen_insert_characters(-new_x);
|
screen_insert_characters((unsigned int) -new_x);
|
||||||
topics |= TOPIC_CHANGE_CONTENT_PART;
|
topics |= TOPIC_CHANGE_CONTENT_PART;
|
||||||
}
|
}
|
||||||
NOTIFY_DONE(topics);
|
NOTIFY_DONE(topics);
|
||||||
@@ -1529,7 +1891,7 @@ do_save_private_opt(int n, bool save)
|
|||||||
{
|
{
|
||||||
ScreenNotifyTopics topics = TOPIC_INTERNAL;
|
ScreenNotifyTopics topics = TOPIC_INTERNAL;
|
||||||
if (!save) NOTIFY_LOCK();
|
if (!save) NOTIFY_LOCK();
|
||||||
#define SAVE_RESTORE(sf, of) do { if (save) sf=(of); else of=(sf); } while(0)
|
#define SAVE_RESTORE(sf, of) do { if (save) (sf)=(of); else (of)=(sf); } while(0)
|
||||||
switch (n) {
|
switch (n) {
|
||||||
case 1:
|
case 1:
|
||||||
SAVE_RESTORE(opt_backup.cursors_alt_mode, scr.cursors_alt_mode);
|
SAVE_RESTORE(opt_backup.cursors_alt_mode, scr.cursors_alt_mode);
|
||||||
@@ -1692,6 +2054,10 @@ putchar_graphic(const char *ch)
|
|||||||
cursor.hanging = true; // hanging - next typed char wraps around, but backspace and arrows still stay on the same line.
|
cursor.hanging = true; // hanging - next typed char wraps around, but backspace and arrows still stay on the same line.
|
||||||
cursor.x = W - 1;
|
cursor.x = W - 1;
|
||||||
}
|
}
|
||||||
|
if (IS_DOUBLE_WIDTH() && cursor.x >= W/2) {
|
||||||
|
cursor.hanging = true; // hanging
|
||||||
|
cursor.x = W/2 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
NOTIFY_DONE(topics);
|
NOTIFY_DONE(topics);
|
||||||
return ch;
|
return ch;
|
||||||
@@ -1794,6 +2160,20 @@ utf8_remap(char *out, char g, char charset)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CS_2_BLOCKS_LINES: /* ESPTerm Character Rom 2 */
|
||||||
|
if ((g >= CODEPAGE_2_BEGIN) && (g <= CODEPAGE_2_END)) {
|
||||||
|
n = codepage_2[g - CODEPAGE_2_BEGIN];
|
||||||
|
if (n) utf = n;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CS_3_LINES_EXTRA: /* ESPTerm Character Rom 3 */
|
||||||
|
if ((g >= CODEPAGE_3_BEGIN) && (g <= CODEPAGE_3_END)) {
|
||||||
|
n = codepage_3[g - CODEPAGE_3_BEGIN];
|
||||||
|
if (n) utf = n;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case CS_A_UKASCII: /* UK, replaces # with GBP */
|
case CS_A_UKASCII: /* UK, replaces # with GBP */
|
||||||
if (g == '#') utf = 0x20a4; // £
|
if (g == '#') utf = 0x20a4; // £
|
||||||
break;
|
break;
|
||||||
@@ -1877,6 +2257,15 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics,
|
|||||||
bufput_utf8((num)); \
|
bufput_utf8((num)); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
#define bufput_color_utf8(c) do { \
|
||||||
|
if ((c) < 256) { \
|
||||||
|
bufput_utf8(c); \
|
||||||
|
} else { \
|
||||||
|
bufput_utf8((((c)-256)&0xFFF) | 0x10000); \
|
||||||
|
bufput_utf8((((c)-256)>>12)&0xFFF); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
// tags for screen serialization
|
// tags for screen serialization
|
||||||
#define SEQ_TAG_SKIP '\x01'
|
#define SEQ_TAG_SKIP '\x01'
|
||||||
#define SEQ_TAG_REPEAT '\x02'
|
#define SEQ_TAG_REPEAT '\x02'
|
||||||
@@ -1887,12 +2276,15 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics,
|
|||||||
#define SEQ_TAG_ATTRS_0 '\x07'
|
#define SEQ_TAG_ATTRS_0 '\x07'
|
||||||
|
|
||||||
#define TOPICMARK_SCREEN_OPTS 'O'
|
#define TOPICMARK_SCREEN_OPTS 'O'
|
||||||
|
#define TOPICMARK_STATIC_OPTS 'P'
|
||||||
#define TOPICMARK_TITLE 'T'
|
#define TOPICMARK_TITLE 'T'
|
||||||
#define TOPICMARK_BUTTONS 'B'
|
#define TOPICMARK_BUTTONS 'B'
|
||||||
#define TOPICMARK_DEBUG 'D'
|
#define TOPICMARK_DEBUG 'D'
|
||||||
#define TOPICMARK_BELL '!'
|
#define TOPICMARK_BELL '!'
|
||||||
#define TOPICMARK_CURSOR 'C'
|
#define TOPICMARK_CURSOR 'C'
|
||||||
#define TOPICMARK_SCREEN 'S'
|
#define TOPICMARK_SCREEN 'S'
|
||||||
|
#define TOPICMARK_BACKDROP 'W'
|
||||||
|
#define TOPICMARK_DBL_LINE 'H'
|
||||||
|
|
||||||
if (ss == NULL) {
|
if (ss == NULL) {
|
||||||
// START!
|
// START!
|
||||||
@@ -1983,12 +2375,8 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics,
|
|||||||
bufput_utf8(H);
|
bufput_utf8(H);
|
||||||
bufput_utf8(W);
|
bufput_utf8(W);
|
||||||
bufput_utf8(termconf_live.theme);
|
bufput_utf8(termconf_live.theme);
|
||||||
|
bufput_color_utf8(termconf_live.default_fg);
|
||||||
bufput_utf8(termconf_live.default_fg & 0xFFFF);
|
bufput_color_utf8(termconf_live.default_bg);
|
||||||
bufput_utf8((termconf_live.default_fg >> 16) & 0xFFFF);
|
|
||||||
|
|
||||||
bufput_utf8(termconf_live.default_bg & 0xFFFF);
|
|
||||||
bufput_utf8((termconf_live.default_bg >> 16) & 0xFFFF);
|
|
||||||
|
|
||||||
bufput_utf8(
|
bufput_utf8(
|
||||||
(scr.cursor_visible << 0) |
|
(scr.cursor_visible << 0) |
|
||||||
@@ -2007,28 +2395,79 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics,
|
|||||||
);
|
);
|
||||||
END_TOPIC
|
END_TOPIC
|
||||||
|
|
||||||
|
BEGIN_TOPIC(TOPIC_CHANGE_STATIC_OPTS, 110)
|
||||||
|
bufput_c(TOPICMARK_STATIC_OPTS);
|
||||||
|
|
||||||
|
size_t len = strlen(termconf_live.font_stack);
|
||||||
|
if (len > 0) {
|
||||||
|
memcpy(bb, termconf_live.font_stack, len);
|
||||||
|
bb += len;
|
||||||
|
remain -= len;
|
||||||
|
}
|
||||||
|
bufput_c('\x01');
|
||||||
|
|
||||||
|
bufput_utf8(termconf_live.font_size);
|
||||||
|
END_TOPIC
|
||||||
|
|
||||||
|
BEGIN_TOPIC(TOPIC_DOUBLE_LINES, 70)
|
||||||
|
bufput_c(TOPICMARK_DBL_LINE);
|
||||||
|
|
||||||
|
int cnt = 0;
|
||||||
|
for (int i = 0; i < LINE_ATTRS_COUNT; i++) {
|
||||||
|
if (scr.line_attribs[i] != 0) cnt++;
|
||||||
|
}
|
||||||
|
bufput_utf8(cnt);
|
||||||
|
|
||||||
|
for (int i = 0; i < LINE_ATTRS_COUNT; i++) {
|
||||||
|
if (scr.line_attribs[i] != 0) {
|
||||||
|
bufput_utf8((i << 3) | (scr.line_attribs[i]&0b111));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END_TOPIC
|
||||||
|
|
||||||
BEGIN_TOPIC(TOPIC_CHANGE_TITLE, TERM_TITLE_LEN+4+1)
|
BEGIN_TOPIC(TOPIC_CHANGE_TITLE, TERM_TITLE_LEN+4+1)
|
||||||
bufput_c(TOPICMARK_TITLE);
|
bufput_c(TOPICMARK_TITLE);
|
||||||
|
|
||||||
int len = (int) strlen(termconf_live.title);
|
size_t len = strlen(termconf_live.title);
|
||||||
memcpy(bb, termconf_live.title, len);
|
if (len > 0) {
|
||||||
bb += len;
|
memcpy(bb, termconf_live.title, len);
|
||||||
remain -= len;
|
bb += len;
|
||||||
|
remain -= len;
|
||||||
|
}
|
||||||
bufput_c('\x01');
|
bufput_c('\x01');
|
||||||
END_TOPIC
|
END_TOPIC
|
||||||
|
|
||||||
BEGIN_TOPIC(TOPIC_CHANGE_BUTTONS, (TERM_BTN_LEN+4)*TERM_BTN_COUNT+1+4)
|
BEGIN_TOPIC(TOPIC_CHANGE_BUTTONS, (TERM_BTN_LEN+4)*termconf_live.button_count+1+4)
|
||||||
bufput_c(TOPICMARK_BUTTONS);
|
bufput_c(TOPICMARK_BUTTONS);
|
||||||
|
|
||||||
bufput_utf8(TERM_BTN_COUNT);
|
bufput_utf8(termconf_live.button_count);
|
||||||
|
|
||||||
for (int i = 0; i < TERM_BTN_COUNT; i++) {
|
u32 *cp = &termconf_live.bc1;
|
||||||
int len = (int) strlen(termconf_live.btn[i]);
|
for (int i = 0; i < termconf_live.button_count; i++) {
|
||||||
memcpy(bb, termconf_live.btn[i], len);
|
uint32_t c = *cp++;
|
||||||
bb += len;
|
bufput_color_utf8(c);
|
||||||
remain -= len;
|
|
||||||
|
size_t len = strlen(TERM_BTN_N(&termconf_live, i));
|
||||||
|
if (len > 0) {
|
||||||
|
memcpy(bb, TERM_BTN_N(&termconf_live, i), len);
|
||||||
|
bb += len;
|
||||||
|
remain -= len;
|
||||||
|
}
|
||||||
bufput_c('\x01');
|
bufput_c('\x01');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
END_TOPIC
|
||||||
|
|
||||||
|
BEGIN_TOPIC(TOPIC_CHANGE_BACKDROP, TERM_BACKDROP_LEN+1+1)
|
||||||
|
bufput_c(TOPICMARK_BACKDROP);
|
||||||
|
|
||||||
|
size_t len = strlen(termconf_live.backdrop);
|
||||||
|
if (len > 0) {
|
||||||
|
memcpy(bb, termconf_live.backdrop, len);
|
||||||
|
bb += len;
|
||||||
|
remain -= len;
|
||||||
|
}
|
||||||
|
bufput_c('\x01');
|
||||||
END_TOPIC
|
END_TOPIC
|
||||||
|
|
||||||
BEGIN_TOPIC(TOPIC_INTERNAL, 45)
|
BEGIN_TOPIC(TOPIC_INTERNAL, 45)
|
||||||
@@ -2049,6 +2488,8 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics,
|
|||||||
bufput_utf8(cursor.charsetN);
|
bufput_utf8(cursor.charsetN);
|
||||||
bufput_c(cursor.charset0);
|
bufput_c(cursor.charset0);
|
||||||
bufput_c(cursor.charset1);
|
bufput_c(cursor.charset1);
|
||||||
|
bufput_color_utf8(cursor.fg);
|
||||||
|
bufput_color_utf8(cursor.bg);
|
||||||
bufput_utf8(system_get_free_heap_size());
|
bufput_utf8(system_get_free_heap_size());
|
||||||
bufput_utf8(term_active_clients);
|
bufput_utf8(term_active_clients);
|
||||||
END_TOPIC
|
END_TOPIC
|
||||||
|
|||||||
+85
-25
@@ -5,6 +5,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <esp8266.h>
|
#include <esp8266.h>
|
||||||
#include <httpd.h>
|
#include <httpd.h>
|
||||||
|
#include "config_xmacros.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This module handles the virtual screen and operations on it.
|
* This module handles the virtual screen and operations on it.
|
||||||
@@ -38,6 +39,8 @@
|
|||||||
#define TERM_BTN_MSG_LEN 10
|
#define TERM_BTN_MSG_LEN 10
|
||||||
#define TERM_TITLE_LEN 64
|
#define TERM_TITLE_LEN 64
|
||||||
#define TERM_BTN_COUNT 5
|
#define TERM_BTN_COUNT 5
|
||||||
|
#define TERM_BACKDROP_LEN 100
|
||||||
|
#define TERM_FONTSTACK_LEN 100
|
||||||
|
|
||||||
#define SCR_DEF_DISPLAY_TOUT_MS 12
|
#define SCR_DEF_DISPLAY_TOUT_MS 12
|
||||||
#define SCR_DEF_DISPLAY_COOLDOWN_MS 35
|
#define SCR_DEF_DISPLAY_COOLDOWN_MS 35
|
||||||
@@ -68,38 +71,79 @@ enum CursorShape {
|
|||||||
#define SCR_DEF_DEBUGBAR 0
|
#define SCR_DEF_DEBUGBAR 0
|
||||||
#define SCR_DEF_DECOPT12 0
|
#define SCR_DEF_DECOPT12 0
|
||||||
#define SCR_DEF_ASCIIDEBUG 0
|
#define SCR_DEF_ASCIIDEBUG 0
|
||||||
|
#define SCR_DEF_BUTTON_COUNT 5
|
||||||
|
|
||||||
// --- Persistent Settings ---
|
// --- Persistent Settings ---
|
||||||
#define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL)
|
#define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL)
|
||||||
|
|
||||||
// Size designed for the terminal config structure
|
// Size designed for the terminal config structure
|
||||||
// Must be constant to avoid corrupting user config after upgrade
|
// Must be constant to avoid corrupting user config after upgrade
|
||||||
#define TERMCONF_SIZE 300
|
#define TERMCONF_SIZE 500
|
||||||
#define TERMCONF_VERSION 3
|
#define TERMCONF_VERSION 6
|
||||||
|
|
||||||
|
//....Type................Name..Suffix...............Deref..XGET.........Cast..XSET...........NOTIFY..Allow
|
||||||
|
// Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays)
|
||||||
|
#define XTABLE_TERMCONF \
|
||||||
|
X(u32, width, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \
|
||||||
|
X(u32, height, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \
|
||||||
|
X(u32, default_bg, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
|
||||||
|
X(u32, default_fg, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
|
||||||
|
X(char, title, [TERM_TITLE_LEN], /**/, xget_string, xset_string, TERM_TITLE_LEN, /**/, 1) \
|
||||||
|
X(char, btn1, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \
|
||||||
|
X(char, btn2, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \
|
||||||
|
X(char, btn3, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \
|
||||||
|
X(char, btn4, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \
|
||||||
|
X(char, btn5, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \
|
||||||
|
X(u8, theme, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) \
|
||||||
|
X(u32, parser_tout_ms, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \
|
||||||
|
X(u32, display_tout_ms, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \
|
||||||
|
X(bool, fn_alt_mode, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
|
||||||
|
X(u8, config_version, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) \
|
||||||
|
X(u32, display_cooldown_ms, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \
|
||||||
|
X(bool, loopback, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
|
||||||
|
X(bool, show_buttons, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
|
||||||
|
X(bool, show_config_links, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
|
||||||
|
X(char, bm1, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \
|
||||||
|
X(char, bm2, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \
|
||||||
|
X(char, bm3, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \
|
||||||
|
X(char, bm4, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \
|
||||||
|
X(char, bm5, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \
|
||||||
|
X(u32, cursor_shape, /**/, /**/, xget_dec, xset_term_cursorshape, NULL, /**/, 1) \
|
||||||
|
X(bool, crlf_mode, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
|
||||||
|
X(bool, want_all_fn, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
|
||||||
|
X(bool, debugbar, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
|
||||||
|
X(bool, allow_decopt_12, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
|
||||||
|
X(bool, ascii_debug, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
|
||||||
|
X(char, backdrop, [TERM_BACKDROP_LEN], /**/, xget_string, xset_string, TERM_BACKDROP_LEN, /**/, 1) \
|
||||||
|
X(u8, button_count, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) \
|
||||||
|
X(u32, bc1, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
|
||||||
|
X(u32, bc2, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
|
||||||
|
X(u32, bc3, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
|
||||||
|
X(u32, bc4, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
|
||||||
|
X(u32, bc5, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
|
||||||
|
X(char, font_stack, [TERM_FONTSTACK_LEN], /**/, xget_string, xset_string, TERM_FONTSTACK_LEN, /**/, 1) \
|
||||||
|
X(u8, font_size, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1)
|
||||||
|
|
||||||
|
/** Get button N message from the passed config structure pointer */
|
||||||
|
#define TERM_BM_N(tc, n) ((tc)->bm1+(TERM_BTN_MSG_LEN*(n)))
|
||||||
|
/** Get button N text from the passed config structure pointer */
|
||||||
|
#define TERM_BTN_N(tc, n) ((tc)->btn1+(TERM_BTN_LEN*(n)))
|
||||||
|
|
||||||
|
/** Export color for config */
|
||||||
|
void xget_term_color(char *buff, u32 value);
|
||||||
|
/** Export button message as stirng for config */
|
||||||
|
void xget_term_bm(char *buff, char *value);
|
||||||
|
/** Set button message */
|
||||||
|
enum xset_result xset_term_bm(const char *name, char *field, const char *buff, const void *arg);
|
||||||
|
/** Set color */
|
||||||
|
enum xset_result xset_term_color(const char *name, u32 *field, const char *buff, const void *arg);
|
||||||
|
/** Set cursor shape */
|
||||||
|
enum xset_result xset_term_cursorshape(const char *name, u32 *field, const char *buff, const void *arg);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 width;
|
#define X XSTRUCT_FIELD
|
||||||
u32 height;
|
XTABLE_TERMCONF
|
||||||
u32 default_bg; // 00-FFh - ANSI colors, (00:00:00-FF:FF:FF)+256 - True Color
|
#undef X
|
||||||
u32 default_fg;
|
|
||||||
char title[TERM_TITLE_LEN];
|
|
||||||
char btn[TERM_BTN_COUNT][TERM_BTN_LEN];
|
|
||||||
u8 theme;
|
|
||||||
u32 parser_tout_ms;
|
|
||||||
u32 display_tout_ms;
|
|
||||||
bool fn_alt_mode; // xterm compatibility mode (alternate codes for some FN keys)
|
|
||||||
u8 config_version;
|
|
||||||
u32 display_cooldown_ms;
|
|
||||||
bool loopback;
|
|
||||||
bool show_buttons;
|
|
||||||
bool show_config_links;
|
|
||||||
char btn_msg[TERM_BTN_COUNT][TERM_BTN_MSG_LEN];
|
|
||||||
enum CursorShape cursor_shape;
|
|
||||||
bool crlf_mode;
|
|
||||||
bool want_all_fn;
|
|
||||||
bool debugbar;
|
|
||||||
bool allow_decopt_12;
|
|
||||||
bool ascii_debug;
|
|
||||||
} TerminalConfigBundle;
|
} TerminalConfigBundle;
|
||||||
|
|
||||||
// Live config
|
// Live config
|
||||||
@@ -148,6 +192,11 @@ void screen_resize(int rows, int cols);
|
|||||||
void screen_set_title(const char *title);
|
void screen_set_title(const char *title);
|
||||||
/** Set a button text */
|
/** Set a button text */
|
||||||
void screen_set_button_text(int num, const char *text);
|
void screen_set_button_text(int num, const char *text);
|
||||||
|
void screen_set_button_message(int num, const char *msg);
|
||||||
|
void screen_set_button_color(int num, const char *buf);
|
||||||
|
void screen_set_button_count(int count);
|
||||||
|
/** Change backdrop */
|
||||||
|
void screen_set_backdrop(const char *url);
|
||||||
|
|
||||||
// --- Encoding ---
|
// --- Encoding ---
|
||||||
|
|
||||||
@@ -156,6 +205,8 @@ typedef enum {
|
|||||||
CS_A_UKASCII = 'A',
|
CS_A_UKASCII = 'A',
|
||||||
CS_0_DEC_SUPPLEMENTAL = '0',
|
CS_0_DEC_SUPPLEMENTAL = '0',
|
||||||
CS_1_DOS_437 = '1',
|
CS_1_DOS_437 = '1',
|
||||||
|
CS_2_BLOCKS_LINES = '2',
|
||||||
|
CS_3_LINES_EXTRA = '3',
|
||||||
} CHARSET;
|
} CHARSET;
|
||||||
|
|
||||||
enum ScreenSerializeTopic {
|
enum ScreenSerializeTopic {
|
||||||
@@ -167,15 +218,21 @@ enum ScreenSerializeTopic {
|
|||||||
TOPIC_CHANGE_CURSOR = (1<<5),
|
TOPIC_CHANGE_CURSOR = (1<<5),
|
||||||
TOPIC_INTERNAL = (1<<6), // debugging internal state
|
TOPIC_INTERNAL = (1<<6), // debugging internal state
|
||||||
TOPIC_BELL = (1<<7), // beep
|
TOPIC_BELL = (1<<7), // beep
|
||||||
|
TOPIC_CHANGE_BACKDROP = (1<<8),
|
||||||
|
TOPIC_CHANGE_STATIC_OPTS = (1<<9),
|
||||||
|
TOPIC_DOUBLE_LINES = (1<<10),
|
||||||
TOPIC_FLAG_NOCLEAN = (1<<15), // do not clean dirty extents
|
TOPIC_FLAG_NOCLEAN = (1<<15), // do not clean dirty extents
|
||||||
|
|
||||||
// combos
|
// combos
|
||||||
TOPIC_INITIAL =
|
TOPIC_INITIAL =
|
||||||
TOPIC_CHANGE_SCREEN_OPTS |
|
TOPIC_CHANGE_SCREEN_OPTS |
|
||||||
|
TOPIC_CHANGE_STATIC_OPTS |
|
||||||
TOPIC_CHANGE_CONTENT_ALL |
|
TOPIC_CHANGE_CONTENT_ALL |
|
||||||
TOPIC_CHANGE_CURSOR |
|
TOPIC_CHANGE_CURSOR |
|
||||||
TOPIC_CHANGE_TITLE |
|
TOPIC_CHANGE_TITLE |
|
||||||
TOPIC_CHANGE_BUTTONS,
|
TOPIC_CHANGE_BACKDROP |
|
||||||
|
TOPIC_CHANGE_BUTTONS |
|
||||||
|
TOPIC_DOUBLE_LINES,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef u16 ScreenNotifyTopics;
|
typedef u16 ScreenNotifyTopics;
|
||||||
@@ -327,6 +384,9 @@ void screen_tab_reverse(int count);
|
|||||||
/** Move left, shift right if at the boundary */
|
/** Move left, shift right if at the boundary */
|
||||||
void screen_back_index(int count);
|
void screen_back_index(int count);
|
||||||
|
|
||||||
|
/** Set line attribs; 0-no change, 1,2 - single,double */
|
||||||
|
void screen_set_line_attr(uint8_t double_w, uint8_t double_h_top, uint8_t double_h_bot);
|
||||||
|
|
||||||
// --- Printing characters ---
|
// --- Printing characters ---
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ buf_pop(void *unused)
|
|||||||
|
|
||||||
LOCAL void my_putc(char c)
|
LOCAL void my_putc(char c)
|
||||||
{
|
{
|
||||||
UART_WriteCharCRLF(UART1, (u8) c, 10);
|
UART_WriteCharCRLF(UART1, (u8) c, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+102
-2
@@ -6,12 +6,112 @@
|
|||||||
#include "persist.h"
|
#include "persist.h"
|
||||||
#include "uart_driver.h"
|
#include "uart_driver.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
#include "cgi_logging.h"
|
||||||
|
|
||||||
SystemConfigBundle * const sysconf = &persist.current.sysconf;
|
SystemConfigBundle * const sysconf = &persist.current.sysconf;
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_sys_baudrate(const char *name, u32 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, 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) {
|
||||||
|
if (*field != baud) {
|
||||||
|
*field = (u32) baud;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad baud rate %s", buff);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_sys_parity(const char *name, u8 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
int parity = atoi(buff);
|
||||||
|
if (parity >= 0 && parity <= 2) {
|
||||||
|
if (*field != parity) {
|
||||||
|
*field = (UartParityMode) parity;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad parity %s", buff);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_sys_stopbits(const char *name, u8 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
int stopbits = atoi(buff);
|
||||||
|
if (stopbits >= 1 && stopbits <= 3) {
|
||||||
|
if (*field != stopbits) {
|
||||||
|
*field = (UartParityMode) stopbits;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad stopbits %s", buff);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_sys_pwlock(const char *name, u8 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
|
||||||
|
int pwlock = atoi(buff);
|
||||||
|
if (pwlock >= 0 && pwlock < PWLOCK_MAX) {
|
||||||
|
if (*field != pwlock) {
|
||||||
|
*field = (enum pwlock) pwlock;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad pwlock %s", buff);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_sys_accesspw(const char *name, uchar *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
// Do not overwrite pw if empty
|
||||||
|
if (strlen(buff) == 0) return XSET_UNCHANGED;
|
||||||
|
return xset_ustring(name, field, buff, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ICACHE_FLASH_ATTR
|
void ICACHE_FLASH_ATTR
|
||||||
sysconf_apply_settings(void)
|
sysconf_apply_settings(void)
|
||||||
{
|
{
|
||||||
|
// char buff[64];
|
||||||
|
//#define XSTRUCT sysconf
|
||||||
|
//#define X XDUMP_FIELD
|
||||||
|
// XTABLE_SYSCONF
|
||||||
|
//#undef X
|
||||||
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
if (sysconf->config_version < 1) {
|
if (sysconf->config_version < 1) {
|
||||||
dbg("Upgrading syscfg to v 1");
|
dbg("Upgrading syscfg to v 1");
|
||||||
@@ -40,7 +140,7 @@ sysconf_restore_defaults(void)
|
|||||||
sysconf->config_version = SYSCONF_VERSION;
|
sysconf->config_version = SYSCONF_VERSION;
|
||||||
sysconf->access_pw[0] = 0;
|
sysconf->access_pw[0] = 0;
|
||||||
sysconf->pwlock = PWLOCK_NONE;
|
sysconf->pwlock = PWLOCK_NONE;
|
||||||
strcpy(sysconf->access_pw, DEF_ACCESS_PW);
|
strcpy((char *)sysconf->access_pw, DEF_ACCESS_PW);
|
||||||
strcpy(sysconf->access_name, DEF_ACCESS_NAME);
|
strcpy((char *)sysconf->access_name, DEF_ACCESS_NAME);
|
||||||
sysconf->overclock = false;
|
sysconf->overclock = false;
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-8
@@ -6,6 +6,7 @@
|
|||||||
#define ESP_VT100_FIRMWARE_SYSCFG_H
|
#define ESP_VT100_FIRMWARE_SYSCFG_H
|
||||||
|
|
||||||
#include <esp8266.h>
|
#include <esp8266.h>
|
||||||
|
#include "config_xmacros.h"
|
||||||
|
|
||||||
// Size designed for the wifi config structure
|
// Size designed for the wifi config structure
|
||||||
// Must be constant to avoid corrupting user config after upgrade
|
// Must be constant to avoid corrupting user config after upgrade
|
||||||
@@ -24,15 +25,35 @@ enum pwlock {
|
|||||||
PWLOCK_MAX = 5,
|
PWLOCK_MAX = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//....Type................Name..Suffix...............Deref..XGET............XSET.........................NOTIFY....Allow
|
||||||
|
// Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays)
|
||||||
|
#define XTABLE_SYSCONF \
|
||||||
|
X(u32, uart_baudrate, /**/, /**/, xget_dec, xset_sys_baudrate, NULL, uart_changed=true, 1) \
|
||||||
|
X(u8, uart_parity, /**/, /**/, xget_dec, xset_sys_parity, NULL, uart_changed=true, 1) \
|
||||||
|
X(u8, uart_stopbits, /**/, /**/, xget_dec, xset_sys_stopbits, NULL, uart_changed=true, 1) \
|
||||||
|
\
|
||||||
|
X(u8, config_version, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) \
|
||||||
|
\
|
||||||
|
X(u8, pwlock, /**/, /**/, xget_dec, xset_sys_pwlock, NULL, /**/, admin|tpl) \
|
||||||
|
X(uchar, access_pw, [64], /**/, xget_ustring, xset_sys_accesspw, NULL, /**/, admin) \
|
||||||
|
X(uchar, access_name, [32], /**/, xget_ustring, xset_ustring, NULL, /**/, admin|tpl) \
|
||||||
|
\
|
||||||
|
X(bool, overclock, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 uart_baudrate;
|
#define X XSTRUCT_FIELD
|
||||||
u8 uart_parity;
|
XTABLE_SYSCONF
|
||||||
u8 uart_stopbits;
|
#undef X
|
||||||
u8 config_version;
|
|
||||||
enum pwlock pwlock : 8; // page access lock
|
// u32 uart_baudrate;
|
||||||
char access_pw[64]; // access password
|
// u8 uart_parity;
|
||||||
char access_name[32]; // access name
|
// u8 uart_stopbits;
|
||||||
bool overclock;
|
// 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;
|
} SystemConfigBundle;
|
||||||
|
|
||||||
extern SystemConfigBundle * const sysconf;
|
extern SystemConfigBundle * const sysconf;
|
||||||
@@ -41,4 +62,10 @@ void sysconf_apply_settings(void);
|
|||||||
|
|
||||||
void sysconf_restore_defaults(void);
|
void sysconf_restore_defaults(void);
|
||||||
|
|
||||||
|
enum xset_result xset_sys_baudrate(const char *name, u32 *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_sys_parity(const char *name, u8 *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_sys_stopbits(const char *name, u8 *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_sys_pwlock(const char *name, u8 *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_sys_accesspw(const char *name, uchar *field, const char *buff, const void *arg);
|
||||||
|
|
||||||
#endif //ESP_VT100_FIRMWARE_SYSCFG_H
|
#endif //ESP_VT100_FIRMWARE_SYSCFG_H
|
||||||
|
|||||||
+9
-7
@@ -74,12 +74,12 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg)
|
|||||||
if (diff == 0) {
|
if (diff == 0) {
|
||||||
if (cnt == 5) {
|
if (cnt == 5) {
|
||||||
// only every 5 secs if no change
|
// only every 5 secs if no change
|
||||||
dbg("Rx: %2d%c, Tx: %2d%c, Hp: %d", rxp, '%', txp, '%', heap);
|
dbg("Rx/Tx: %d/%d%c, Hp: %d", rxp, txp, '%', heap);
|
||||||
cnt = 0;
|
cnt = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// report change
|
// report change
|
||||||
dbg("Rx: %2d%c, Tx: %2d%c, Hp: %d (%s%d)", rxp, '%', txp, '%', heap, cc, diff);
|
dbg("Rx/Tx: %d/%d%c, Hp: %d (%s%d)", rxp, txp, '%', heap, cc, diff);
|
||||||
cnt = 0;
|
cnt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,11 +131,6 @@ void ICACHE_FLASH_ATTR user_init(void)
|
|||||||
espFsInit((void *) (webpages_espfs_start));
|
espFsInit((void *) (webpages_espfs_start));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DEBUG_HEAP
|
|
||||||
// Heap use timer & blink
|
|
||||||
TIMER_START(&prHeapTimer, prHeapTimerCb, HEAP_TIMER_MS, 1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// do later (some functions do not work if called from user_init)
|
// do later (some functions do not work if called from user_init)
|
||||||
TIMER_START(&userStartTimer, user_start, 10, 0);
|
TIMER_START(&userStartTimer, user_start, 10, 0);
|
||||||
}
|
}
|
||||||
@@ -154,6 +149,13 @@ static void ICACHE_FLASH_ATTR user_start(void *unused)
|
|||||||
// Print the CANCEL character to indicate the module has restarted
|
// Print the CANCEL character to indicate the module has restarted
|
||||||
// Critically important for client application if any kind of screen persistence / content re-use is needed
|
// Critically important for client application if any kind of screen persistence / content re-use is needed
|
||||||
UART_WriteChar(UART0, CAN, UART_TIMEOUT_US); // 0x18 - 24 - CAN
|
UART_WriteChar(UART0, CAN, UART_TIMEOUT_US); // 0x18 - 24 - CAN
|
||||||
|
|
||||||
|
dbg("tsize=%d", sizeof(TerminalConfigBundle));
|
||||||
|
|
||||||
|
#if DEBUG_HEAP
|
||||||
|
// Heap use timer & blink
|
||||||
|
TIMER_START(&prHeapTimer, prHeapTimerCb, HEAP_TIMER_MS, 1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- unused funcs removed from sdk to save space ---
|
// ---- unused funcs removed from sdk to save space ---
|
||||||
|
|||||||
+4
-5
@@ -9,11 +9,10 @@
|
|||||||
#define STR(x) STR_HELPER(x)
|
#define STR(x) STR_HELPER(x)
|
||||||
|
|
||||||
#define FW_V_MAJOR 2
|
#define FW_V_MAJOR 2
|
||||||
#define FW_V_MINOR 1
|
#define FW_V_MINOR 3
|
||||||
#define FW_V_PATCH 0
|
#define FW_V_PATCH 1
|
||||||
#define FW_V_SUFFIX "-beta3"
|
#define FW_V_SUFFIX ""
|
||||||
//#define FW_V_SUFFIX ""
|
#define FW_CODENAME "Cricket" // 2.3
|
||||||
#define FW_CODENAME "Anthill" // 2.1.0
|
|
||||||
#define FW_CODENAME_QUOTED "\""FW_CODENAME"\""
|
#define FW_CODENAME_QUOTED "\""FW_CODENAME"\""
|
||||||
|
|
||||||
#define FW_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) FW_V_SUFFIX
|
#define FW_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) FW_V_SUFFIX
|
||||||
|
|||||||
+187
-22
@@ -4,10 +4,143 @@
|
|||||||
|
|
||||||
#include "wifimgr.h"
|
#include "wifimgr.h"
|
||||||
#include "persist.h"
|
#include "persist.h"
|
||||||
|
#include "cgi_logging.h"
|
||||||
|
#include "config_xmacros.h"
|
||||||
|
|
||||||
WiFiConfigBundle * const wificonf = &persist.current.wificonf;
|
WiFiConfigBundle * const wificonf = &persist.current.wificonf;
|
||||||
WiFiConfChangeFlags wifi_change_flags;
|
WiFiConfChangeFlags wifi_change_flags;
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_wifi_lease_time(const char *name, u16 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s min", name, buff);
|
||||||
|
int min = atoi(buff);
|
||||||
|
if (min >= 1 && min <= 2880) {
|
||||||
|
if (*field != min) {
|
||||||
|
*field = (u16) min;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("Lease time %s out of allowed range 1-2880.", buff);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_wifi_opmode(const char *name, u8 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
int mode = atoi(buff);
|
||||||
|
if (mode > NULL_MODE && mode < MAX_MODE) {
|
||||||
|
if (*field != mode) {
|
||||||
|
*field = (WIFI_MODE) mode;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED; // opmode does not use flags
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad opmode value \"%s\"", buff);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_wifi_tpw(const char *name, u8 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
int tpw = atoi(buff);
|
||||||
|
if (tpw >= 0 && tpw <= 82) { // 0 actually isn't 0 but quite low. 82 is very strong
|
||||||
|
if (*field != tpw) {
|
||||||
|
*field = (u8) tpw;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("tpw %s out of allowed range 0-82.", buff);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_wifi_ap_channel(const char *name, u8 *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
int channel = atoi(buff);
|
||||||
|
if (channel > 0 && channel < 15) {
|
||||||
|
if (*field != channel) {
|
||||||
|
*field = (u8) channel;
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad channel value \"%s\", allowed 1-14", buff);
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_wifi_ssid(const char *name, uchar *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
u8 buff2[SSID_LEN];
|
||||||
|
|
||||||
|
bool want_subs = arg!=0;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < SSID_LEN; i++) {
|
||||||
|
char c = buff[i];
|
||||||
|
if (c == 0) break;
|
||||||
|
if (want_subs && (c < 32 || c >= 127)) c = '_';
|
||||||
|
buff2[i] = (u8) c;
|
||||||
|
}
|
||||||
|
buff2[i] = 0;
|
||||||
|
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
if (strlen((char *)buff2) > 0) {
|
||||||
|
if (!streq(field, buff2)) {
|
||||||
|
strncpy_safe(field, buff2, SSID_LEN);
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad SSID len.");
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set PW - allow len 0 or 8-64 */
|
||||||
|
enum xset_result ICACHE_FLASH_ATTR
|
||||||
|
xset_wifi_pwd(const char *name, uchar *field, const char *buff, const void *arg)
|
||||||
|
{
|
||||||
|
cgi_dbg("Setting %s = %s", name, buff);
|
||||||
|
if (strlen(buff) == 0 || (strlen(buff) >= 8 && strlen(buff) < PASSWORD_LEN-1)) {
|
||||||
|
if (!streq(field, buff)) {
|
||||||
|
strncpy_safe(field, buff, PASSWORD_LEN);
|
||||||
|
return XSET_SET;
|
||||||
|
}
|
||||||
|
return XSET_UNCHANGED;
|
||||||
|
} else {
|
||||||
|
cgi_warn("Bad password len.");
|
||||||
|
return XSET_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ICACHE_FLASH_ATTR getStaIpAsString(char *buffer)
|
||||||
|
{
|
||||||
|
WIFI_MODE x = wifi_get_opmode();
|
||||||
|
STATION_STATUS connectStatus = wifi_station_get_connect_status();
|
||||||
|
|
||||||
|
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP || wificonf->opmode == SOFTAP_MODE) {
|
||||||
|
strcpy(buffer, "");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
struct ip_info info;
|
||||||
|
wifi_get_ip_info(STATION_IF, &info);
|
||||||
|
return sprintf(buffer, IPSTR, GOOD_IP2STR(info.ip.addr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore defaults in the WiFi config block.
|
* Restore defaults in the WiFi config block.
|
||||||
* This is to be called if the WiFi config is corrupted on startup,
|
* This is to be called if the WiFi config is corrupted on startup,
|
||||||
@@ -26,13 +159,11 @@ wifimgr_restore_defaults(void)
|
|||||||
wificonf->ap_password[0] = 0; // PSK2 always if password is not null.
|
wificonf->ap_password[0] = 0; // PSK2 always if password is not null.
|
||||||
wificonf->ap_hidden = false;
|
wificonf->ap_hidden = false;
|
||||||
|
|
||||||
IP4_ADDR(&wificonf->ap_addr.ip, 192, 168, 4, 1);
|
IP4_ADDR(&wificonf->ap_addr_ip, 192, 168, 4, 1);
|
||||||
IP4_ADDR(&wificonf->ap_addr.netmask, 255, 255, 255, 0);
|
IP4_ADDR(&wificonf->ap_addr_mask, 255, 255, 255, 0);
|
||||||
wificonf->ap_addr.gw.addr = wificonf->ap_addr.gw.addr;
|
|
||||||
|
|
||||||
IP4_ADDR(&wificonf->ap_dhcp_range.start_ip, 192, 168, 4, 100);
|
IP4_ADDR(&wificonf->ap_dhcp_start, 192, 168, 4, 100);
|
||||||
IP4_ADDR(&wificonf->ap_dhcp_range.end_ip, 192, 168, 4, 200);
|
IP4_ADDR(&wificonf->ap_dhcp_end, 192, 168, 4, 200);
|
||||||
wificonf->ap_dhcp_range.enable = 1; // this will never get changed, idk why it's even there
|
|
||||||
wificonf->ap_dhcp_time = 120;
|
wificonf->ap_dhcp_time = 120;
|
||||||
|
|
||||||
// --- Client config ---
|
// --- Client config ---
|
||||||
@@ -40,9 +171,9 @@ wifimgr_restore_defaults(void)
|
|||||||
wificonf->sta_password[0] = 0;
|
wificonf->sta_password[0] = 0;
|
||||||
wificonf->sta_dhcp_enable = true;
|
wificonf->sta_dhcp_enable = true;
|
||||||
|
|
||||||
IP4_ADDR(&wificonf->sta_addr.ip, 192, 168, 0, (mac[5] == 1 ? 2 : mac[5])); // avoid being the same as "default gw"
|
IP4_ADDR(&wificonf->sta_addr_ip, 192, 168, 0, (mac[5] == 1 ? 2 : mac[5])); // avoid being the same as "default gw"
|
||||||
IP4_ADDR(&wificonf->sta_addr.netmask, 255, 255, 255, 0);
|
IP4_ADDR(&wificonf->sta_addr_mask, 255, 255, 255, 0);
|
||||||
IP4_ADDR(&wificonf->sta_addr.gw, 192, 168, 0, 1);
|
IP4_ADDR(&wificonf->sta_addr_gw, 192, 168, 0, 1); // a common default...
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ICACHE_FLASH_ATTR
|
static void ICACHE_FLASH_ATTR
|
||||||
@@ -67,13 +198,18 @@ configure_station(void)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
wifi_info("[WiFi] Setting up static IP...");
|
wifi_info("[WiFi] Setting up static IP...");
|
||||||
wifi_dbg("[WiFi] Client.ip = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr));
|
wifi_dbg("[WiFi] Client.ip = "IPSTR, GOOD_IP2STR(wificonf->sta_addr_ip.addr));
|
||||||
wifi_dbg("[WiFi] Client.mask = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr));
|
wifi_dbg("[WiFi] Client.mask = "IPSTR, GOOD_IP2STR(wificonf->sta_addr_mask.addr));
|
||||||
wifi_dbg("[WiFi] Client.gw = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr));
|
wifi_dbg("[WiFi] Client.gw = "IPSTR, GOOD_IP2STR(wificonf->sta_addr_gw.addr));
|
||||||
|
|
||||||
wifi_station_dhcpc_stop();
|
wifi_station_dhcpc_stop();
|
||||||
// Load static IP config
|
// Load static IP config
|
||||||
if (!wifi_set_ip_info(STATION_IF, &wificonf->sta_addr)) {
|
struct ip_info ipstruct;
|
||||||
|
ipstruct.ip.addr = wificonf->sta_addr_ip.addr;
|
||||||
|
ipstruct.netmask.addr = wificonf->sta_addr_mask.addr;
|
||||||
|
ipstruct.gw.addr = wificonf->sta_addr_gw.addr;
|
||||||
|
|
||||||
|
if (!wifi_set_ip_info(STATION_IF, &ipstruct)) {
|
||||||
error("[WiFi] Error setting static IP!");
|
error("[WiFi] Error setting static IP!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -96,7 +232,7 @@ configure_ap(void)
|
|||||||
strcpy((char *) conf.password, (char *) wificonf->ap_password);
|
strcpy((char *) conf.password, (char *) wificonf->ap_password);
|
||||||
conf.authmode = (wificonf->ap_password[0] == 0 ? AUTH_OPEN : AUTH_WPA2_PSK);
|
conf.authmode = (wificonf->ap_password[0] == 0 ? AUTH_OPEN : AUTH_WPA2_PSK);
|
||||||
conf.ssid_len = (uint8_t) strlen((char *) conf.ssid);
|
conf.ssid_len = (uint8_t) strlen((char *) conf.ssid);
|
||||||
conf.ssid_hidden = wificonf->ap_hidden;
|
conf.ssid_hidden = (uint8) wificonf->ap_hidden;
|
||||||
conf.max_connection = 4; // default 4 (max possible)
|
conf.max_connection = 4; // default 4 (max possible)
|
||||||
conf.beacon_interval = 100; // default 100 ms
|
conf.beacon_interval = 100; // default 100 ms
|
||||||
|
|
||||||
@@ -111,24 +247,32 @@ configure_ap(void)
|
|||||||
|
|
||||||
// Set IP
|
// Set IP
|
||||||
wifi_info("[WiFi] Configuring SoftAP local IP...");
|
wifi_info("[WiFi] Configuring SoftAP local IP...");
|
||||||
wifi_dbg("[WiFi] SoftAP.ip = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.ip.addr));
|
wifi_dbg("[WiFi] SoftAP.ip = "IPSTR, GOOD_IP2STR(wificonf->ap_addr_ip.addr));
|
||||||
wifi_dbg("[WiFi] SoftAP.mask = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.netmask.addr));
|
wifi_dbg("[WiFi] SoftAP.mask = "IPSTR, GOOD_IP2STR(wificonf->ap_addr_mask.addr));
|
||||||
wifi_dbg("[WiFi] SoftAP.gw = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.gw.addr));
|
|
||||||
|
|
||||||
wifi_softap_dhcps_stop();
|
wifi_softap_dhcps_stop();
|
||||||
|
|
||||||
// Configure DHCP
|
// Configure DHCP
|
||||||
if (!wifi_set_ip_info(SOFTAP_IF, &wificonf->ap_addr)) {
|
struct ip_info ipstruct;
|
||||||
|
ipstruct.ip.addr = wificonf->ap_addr_ip.addr;
|
||||||
|
ipstruct.netmask.addr = wificonf->ap_addr_mask.addr;
|
||||||
|
ipstruct.gw.addr = wificonf->ap_addr_ip.addr;
|
||||||
|
|
||||||
|
if (!wifi_set_ip_info(SOFTAP_IF, &ipstruct)) {
|
||||||
error("[WiFi] IP set fail!");
|
error("[WiFi] IP set fail!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wifi_info("[WiFi] Configuring SoftAP DHCP server...");
|
wifi_info("[WiFi] Configuring SoftAP DHCP server...");
|
||||||
wifi_dbg("[WiFi] DHCP.start = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr));
|
wifi_dbg("[WiFi] DHCP.start = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_start.addr));
|
||||||
wifi_dbg("[WiFi] DHCP.end = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_ip.addr));
|
wifi_dbg("[WiFi] DHCP.end = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_end.addr));
|
||||||
wifi_dbg("[WiFi] DHCP.lease = %d minutes", wificonf->ap_dhcp_time);
|
wifi_dbg("[WiFi] DHCP.lease = %d minutes", wificonf->ap_dhcp_time);
|
||||||
|
|
||||||
if (!wifi_softap_set_dhcps_lease(&wificonf->ap_dhcp_range)) {
|
struct dhcps_lease dhcpstruct;
|
||||||
|
dhcpstruct.start_ip = wificonf->ap_dhcp_start;
|
||||||
|
dhcpstruct.end_ip = wificonf->ap_dhcp_end;
|
||||||
|
dhcpstruct.enable = 1; // ???
|
||||||
|
if (!wifi_softap_set_dhcps_lease(&dhcpstruct)) {
|
||||||
error("[WiFi] DHCP address range set fail!");
|
error("[WiFi] DHCP address range set fail!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -148,6 +292,21 @@ configure_ap(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ETSTimer tim;
|
||||||
|
|
||||||
|
static void ICACHE_FLASH_ATTR
|
||||||
|
wifimgr_apply_settings_later_Cb(void *unused)
|
||||||
|
{
|
||||||
|
wifimgr_apply_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
wifimgr_apply_settings_later(uint32_t delay_ms)
|
||||||
|
{
|
||||||
|
wifi_info("[WiFi] Scheduling settings apply in %d ms", delay_ms);
|
||||||
|
TIMER_START(&tim, wifimgr_apply_settings_later_Cb, delay_ms, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the WiFi event listener, cycle WiFi, apply settings
|
* Register the WiFi event listener, cycle WiFi, apply settings
|
||||||
*/
|
*/
|
||||||
@@ -156,6 +315,12 @@ wifimgr_apply_settings(void)
|
|||||||
{
|
{
|
||||||
wifi_info("[WiFi] Initializing...");
|
wifi_info("[WiFi] Initializing...");
|
||||||
|
|
||||||
|
// char buff[64];
|
||||||
|
//#define XSTRUCT wificonf
|
||||||
|
//#define X XDUMP_FIELD
|
||||||
|
// XTABLE_WIFICONF
|
||||||
|
//#undef X
|
||||||
|
|
||||||
// !!! Update to current version !!!
|
// !!! Update to current version !!!
|
||||||
|
|
||||||
// Force wifi cycle
|
// Force wifi cycle
|
||||||
@@ -174,7 +339,7 @@ wifimgr_apply_settings(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opmode != wificonf->opmode) {
|
if (opmode != wificonf->opmode) {
|
||||||
wifi_set_opmode_current(wificonf->opmode);
|
wifi_set_opmode_current((WIFI_MODE) wificonf->opmode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the client
|
// Configure the client
|
||||||
|
|||||||
+52
-19
@@ -8,6 +8,7 @@
|
|||||||
#define ESP_VT100_FIRMWARE_WIFI_MANAGER_H
|
#define ESP_VT100_FIRMWARE_WIFI_MANAGER_H
|
||||||
|
|
||||||
#include <esp8266.h>
|
#include <esp8266.h>
|
||||||
|
#include "config_xmacros.h"
|
||||||
#include "cgi_wifi.h"
|
#include "cgi_wifi.h"
|
||||||
|
|
||||||
#define SSID_LEN 32
|
#define SSID_LEN 32
|
||||||
@@ -19,6 +20,44 @@
|
|||||||
|
|
||||||
#define WIFICONF_VERSION 0
|
#define WIFICONF_VERSION 0
|
||||||
|
|
||||||
|
#define wifimgr_notify_ap() { wifi_change_flags.ap = true; }
|
||||||
|
#define wifimgr_notify_sta() { wifi_change_flags.ap = true; }
|
||||||
|
|
||||||
|
//....Type................Name..Suffix...............Deref..XGET...........XSET.........................NOTIFY................Allow
|
||||||
|
// Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays)
|
||||||
|
#define XTABLE_WIFICONF \
|
||||||
|
X(u8, opmode, /**/, /**/, xget_dec, xset_wifi_opmode, NULL, /**/, 1) \
|
||||||
|
\
|
||||||
|
X(u8, tpw, /**/, /**/, xget_dec, xset_wifi_tpw, NULL, wifimgr_notify_ap(), 1) \
|
||||||
|
X(u8, ap_channel, /**/, /**/, xget_dec, xset_wifi_ap_channel, NULL, wifimgr_notify_ap(), 1) \
|
||||||
|
X(uchar, ap_ssid, [SSID_LEN], /**/, xget_ustring, xset_wifi_ssid, 1, wifimgr_notify_ap(), 1) \
|
||||||
|
X(uchar, ap_password, [PASSWORD_LEN], /**/, xget_ustring, xset_wifi_pwd, NULL, wifimgr_notify_ap(), 1) \
|
||||||
|
X(bool, ap_hidden, /**/, /**/, xget_bool, xset_bool, NULL, wifimgr_notify_ap(), 1) \
|
||||||
|
\
|
||||||
|
X(u16, ap_dhcp_time, /**/, /**/, xget_dec, xset_wifi_lease_time, NULL, wifimgr_notify_ap(), 1) \
|
||||||
|
X(u32, unused1, /**/, /**/, xget_dummy, xset_dummy, NULL, /**/, 0) \
|
||||||
|
X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \
|
||||||
|
X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \
|
||||||
|
\
|
||||||
|
X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \
|
||||||
|
X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
X(u32, unused2, /**/, /**/, xget_dummy, xset_dummy, NULL, /**/, 0) \
|
||||||
|
X(uchar, sta_ssid, [SSID_LEN], /**/, xget_ustring, xset_wifi_ssid, 0, wifimgr_notify_sta(), 1) \
|
||||||
|
X(uchar, sta_password, [PASSWORD_LEN], /**/, xget_ustring, xset_wifi_pwd, NULL, wifimgr_notify_sta(), 1) \
|
||||||
|
X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, xset_bool, NULL, wifimgr_notify_sta(), 1) \
|
||||||
|
\
|
||||||
|
X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_sta(), 1) \
|
||||||
|
X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_sta(), 1) \
|
||||||
|
X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_sta(), 1) \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
X(u8, config_version, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1)
|
||||||
|
|
||||||
|
// unused1 - replaces 'enabled' bit from old dhcps_lease struct
|
||||||
|
// unused2 - gap after 'ap_gw' which isn't used and doesn't make sense
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A structure holding all configured WiFi parameters
|
* A structure holding all configured WiFi parameters
|
||||||
* and the active state.
|
* and the active state.
|
||||||
@@ -26,25 +65,9 @@
|
|||||||
* This block can be used eg. for WiFi config backup.
|
* This block can be used eg. for WiFi config backup.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
WIFI_MODE opmode : 8;
|
#define X XSTRUCT_FIELD
|
||||||
u8 tpw;
|
XTABLE_WIFICONF
|
||||||
|
#undef X
|
||||||
// AP config
|
|
||||||
u8 ap_channel;
|
|
||||||
u8 ap_ssid[SSID_LEN];
|
|
||||||
u8 ap_password[PASSWORD_LEN];
|
|
||||||
bool ap_hidden;
|
|
||||||
//
|
|
||||||
u16 ap_dhcp_time; // in minutes
|
|
||||||
struct dhcps_lease ap_dhcp_range;
|
|
||||||
struct ip_info ap_addr;
|
|
||||||
|
|
||||||
// Client config
|
|
||||||
u8 sta_ssid[SSID_LEN];
|
|
||||||
u8 sta_password[PASSWORD_LEN];
|
|
||||||
bool sta_dhcp_enable;
|
|
||||||
struct ip_info sta_addr;
|
|
||||||
u8 config_version;
|
|
||||||
} WiFiConfigBundle;
|
} WiFiConfigBundle;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -59,6 +82,16 @@ extern WiFiConfigBundle * const wificonf;
|
|||||||
void wifimgr_restore_defaults(void);
|
void wifimgr_restore_defaults(void);
|
||||||
|
|
||||||
void wifimgr_apply_settings(void);
|
void wifimgr_apply_settings(void);
|
||||||
|
void wifimgr_apply_settings_later(uint32_t delay_ms);
|
||||||
|
|
||||||
|
int getStaIpAsString(char *buffer);
|
||||||
|
|
||||||
|
enum xset_result xset_wifi_lease_time(const char *name, u16 *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_wifi_opmode(const char *name, u8 *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_wifi_tpw(const char *name, u8 *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_wifi_ap_channel(const char *name, u8 *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_wifi_ssid(const char *name, uchar *field, const char *buff, const void *arg);
|
||||||
|
enum xset_result xset_wifi_pwd(const char *name, uchar *field, const char *buff, const void *arg);
|
||||||
|
|
||||||
#if DEBUG_WIFI
|
#if DEBUG_WIFI
|
||||||
#define wifi_warn warn
|
#define wifi_warn warn
|
||||||
|
|||||||
Reference in New Issue
Block a user