From 00153656f1f9c97436cdc16bafb872dce6589323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 4 Oct 2017 00:22:08 +0200 Subject: [PATCH 01/33] flip front-end back to "work" --- front-end | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front-end b/front-end index 4a032ee..8b610aa 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 4a032ee3b52920bf59ae21b4effc31fdf3cece33 +Subproject commit 8b610aa95bf8f7185c28cbe96c7485ec1af2383c From df404f5f63f1f36888c91db0885a2fa963abb718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 4 Oct 2017 21:47:32 +0200 Subject: [PATCH 02/33] 2.1.1 - patched cslang --- front-end | 2 +- user/version.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/front-end b/front-end index 4a032ee..420353e 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 4a032ee3b52920bf59ae21b4effc31fdf3cece33 +Subproject commit 420353efc9bdfc6bf4ffbd180b97bc49c4fdc3d5 diff --git a/user/version.h b/user/version.h index 08fc00d..b45211f 100644 --- a/user/version.h +++ b/user/version.h @@ -10,9 +10,9 @@ #define FW_V_MAJOR 2 #define FW_V_MINOR 1 -#define FW_V_PATCH 0 +#define FW_V_PATCH 1 #define FW_V_SUFFIX "" -#define FW_CODENAME "Anthill" // 2.1.0 +#define FW_CODENAME "Anthill" // 2.1 #define FW_CODENAME_QUOTED "\""FW_CODENAME"\"" #define FW_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) FW_V_SUFFIX From c90de79a34a720f870aa8993b81cd026f71b7b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 6 Oct 2017 00:12:28 +0200 Subject: [PATCH 03/33] Update README.md --- README.md | 53 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index d6aad11..1a4479a 100644 --- a/README.md +++ b/README.md @@ -5,21 +5,24 @@ ![Photo][photo-hw]
*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 -functionally comparable to eg. gnome-terminal, terminator, konsole, GtkTerm or PuTTY. +ESPTerm **passes most of VTTEST test cases**, making it functionally comparable to eg. +gnome-terminal, terminator, konsole, GtkTerm or PuTTY. 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 -implemented to work with ncurses. +mouse support**, provided agetty is made to believe it's Xterm, which shows ESPTerm +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] 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. -It works with ESP-01, ESP-01S, ESP-12 and likely many other modules (I use an ESP-12 -on a LoLin NodeMCU board from eBay for development). +It works with ESP-01, ESP-01S, ESP-12 and likely many other modules. 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 @@ -48,6 +51,8 @@ It **does not work with**: - Internet Explorer (any version) - crashes, missing JS features - Opera Mini - crashes, missing JS and CSS features - 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 ## Main features @@ -58,6 +63,10 @@ It **does not work with**: - Full UTF-8 support, alternate character sets - Standard mouse tracking modes - 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** - Real-time screen update via WebSocket - 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) - Static IP, DHCP, channel selection, power - 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? @@ -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. - 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 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 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. - -The default password is `19738426`. This password can't presently be changed without re-flashing the firmware. +which can (and should!) be changed. This password can't be easily recovered when forgotten. 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 -Developing ESPTerm wasn't an easy task, because the information is scattered across many places and the existing -terminal emulators I originally used for reference (terminator, Konsole) are not implemented correctly in some details. +Developing the terminal emulator was complicated by the information being scattered across many places and the existing +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 -implementation that is probably the most complete emulator available, although it's cumbersome to use and its age -really shows in the looks. +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. -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 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 -and changes required by the project. +[MightyPork/libesphttpd][httpdlib]. This fork includes various improvements and changes required by the project. ### Installation for development From 81c3e426e7862a25d607f40cc5b15664deb1228f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 6 Oct 2017 22:28:36 +0200 Subject: [PATCH 04/33] added quarter blocks to codepage 1 --- user/character_sets.h | 254 +++++++++++++++++++++--------------------- 1 file changed, 127 insertions(+), 127 deletions(-) diff --git a/user/character_sets.h b/user/character_sets.h index ae79948..030cf48 100644 --- a/user/character_sets.h +++ b/user/character_sets.h @@ -19,39 +19,39 @@ * Based on rxvt-unicode screen.C table. */ static const u16 codepage_0[] ESP_CONST_DATA = - {// Unicode ASCII SYM + { // Unicode ASCII SYM // %%BEGIN:0%% - 0x2666, // 96 ` ♦ - 0x2592, // 97 a ▒ - 0x2409, // 98 b HT - 0x240c, // 99 c FF - 0x240d, // 100 d CR - 0x240a, // 101 e LF - 0x00b0, // 102 f ° - 0x00b1, // 103 g ± - 0x2424, // 104 h NL - 0x240b, // 105 i VT - 0x2518, // 106 j ┘ - 0x2510, // 107 k ┐ - 0x250c, // 108 l ┌ - 0x2514, // 109 m └ - 0x253c, // 110 n ┼ - 0x23ba, // 111 o ⎺ - 0x23bb, // 112 p ⎻ - 0x2500, // 113 q ─ - 0x23bc, // 114 r ⎼ - 0x23bd, // 115 s ⎽ - 0x251c, // 116 t ├ - 0x2524, // 117 u ┤ - 0x2534, // 118 v ┴ - 0x252c, // 119 w ┬ - 0x2502, // 120 x │ - 0x2264, // 121 y ≤ - 0x2265, // 122 z ≥ - 0x03c0, // 123 { π - 0x2260, // 124 | ≠ - 0x20a4, // 125 } £ - 0x00b7, // 126 ~ · + u'♦', // 0x2666 96 ` + u'▒', // 0x2592 97 a + u'␉', // 0x2409 98 b + u'␌', // 0x240c 99 c FF + u'␍', // 0x240d 100 d CR + u'␊', // 0x240a 101 e LF + u'°', // 0x00b0 102 f + u'±', // 0x00b1 103 g + u'␤', // 0x2424 104 h NL + u'␋', // 0x240b 105 i VT + u'┘', // 0x2518 106 j + u'┐', // 0x2510 107 k + u'┌', // 0x250c 108 l + u'└', // 0x2514 109 m + u'┼', // 0x253c 110 n + u'⎺', // 0x23ba 111 o + u'⎻', // 0x23bb 112 p + u'─', // 0x2500 113 q + u'⎼', // 0x23bc 114 r + u'⎽', // 0x23bd 115 s + u'├', // 0x251c 116 t + u'┤', // 0x2524 117 u + u'┴', // 0x2534 118 v + u'┬', // 0x252c 119 w + u'│', // 0x2502 120 x + u'≤', // 0x2264 121 y + u'≥', // 0x2265 122 z + u'π', // 0x03c0 123 { + u'≠', // 0x2260 124 | + u'£', // 0x20a4 125 } + u'·', // 0x00b7 126 ~ // %%END:0%% }; @@ -59,102 +59,102 @@ static const u16 codepage_0[] ESP_CONST_DATA = #define CODEPAGE_1_END 126 static const u16 codepage_1[] ESP_CONST_DATA = - {// Unicode ASCII SYM DOS + {// Unicode ASCII DOS // %%BEGIN:1%% - 0x263A, // 33 ! ☺ (1) - low ASCII symbols from DOS, moved to +32 - 0x263B, // 34 " ☻ (2) - 0x2665, // 35 # ♥ (3) - 0x2666, // 36 $ ♦ (4) - 0x2663, // 37 % ♣ (5) - 0x2660, // 38 & ♠ (6) - 0x2022, // 39 ' • (7) - inverse dot and circle left out, can be done with SGR - 0x231B, // 40 ( ⌛ - hourglass (timer icon) - 0x25CB, // 41 ) ○ (9) - 0x21AF, // 42 * ↯ - electricity (lightning monitor...) - 0x266A, // 43 + ♪ (13) - 0x266B, // 44 , ♫ (14) - 0x263C, // 45 - ☼ (15) - 0x2302, // 46 . ⌂ (127) - 0x2622, // 47 / ☢ - radioactivity (geiger counter...) - 0x2591, // 48 0 ░ (176) - this block is kept aligned and ordered from DOS, moved -128 - 0x2592, // 49 1 ▒ (177) - 0x2593, // 50 2 ▓ (178) - 0x2502, // 51 3 │ (179) - 0x2524, // 52 4 ┤ (180) - 0x2561, // 53 5 ╡ (181) - 0x2562, // 54 6 ╢ (182) - 0x2556, // 55 7 ╖ (183) - 0x2555, // 56 8 ╕ (184) - 0x2563, // 57 9 ╣ (185) - 0x2551, // 58 : ║ (186) - 0x2557, // 59 ; ╗ (187) - 0x255D, // 60 < ╝ (188) - 0x255C, // 61 = ╜ (189) - 0x255B, // 62 > ╛ (190) - 0x2510, // 63 ? ┐ (191) - 0x2514, // 64 @ └ (192) - 0x2534, // 65 A ┴ (193) - 0x252C, // 66 B ┬ (194) - 0x251C, // 67 C ├ (195) - 0x2500, // 68 D ─ (196) - 0x253C, // 69 E ┼ (197) - 0x255E, // 70 F ╞ (198) - 0x255F, // 71 G ╟ (199) - 0x255A, // 72 H ╚ (200) - 0x2554, // 73 I ╔ (201) - 0x2569, // 74 J ╩ (202) - 0x2566, // 75 K ╦ (203) - 0x2560, // 76 L ╠ (204) - 0x2550, // 77 M ═ (205) - 0x256C, // 78 N ╬ (206) - 0x2567, // 79 O ╧ (207) - 0x2568, // 80 P ╨ (208) - 0x2564, // 81 Q ╤ (209) - 0x2565, // 82 R ╥ (210) - 0x2559, // 83 S ╙ (211) - 0x2558, // 84 T ╘ (212) - 0x2552, // 85 U ╒ (213) - 0x2553, // 86 V ╓ (214) - 0x256B, // 87 W ╫ (215) - 0x256A, // 88 X ╪ (216) - 0x2518, // 89 Y ┘ (217) - 0x250C, // 90 Z ┌ (218) - 0x2588, // 91 [ █ (219) - 0x2584, // 92 \ ▄ (220) - 0x258C, // 93 ] ▌ (221) - 0x2590, // 94 ^ ▐ (222) - 0x2580, // 95 _ ▀ (223) - 0x2195, // 96 ` ↕ (18) - moved from low DOS ASCII - 0x2191, // 97 a ↑ (24) - 0x2193, // 98 b ↓ (25) - 0x2192, // 99 c → (26) - 0x2190, // 100 d ← (27) - 0x2194, // 101 e ↔ (29) - 0x25B2, // 102 f ▲ (30) - 0x25BC, // 103 g ▼ (31) - 0x25BA, // 104 h ► (16) - 0x25C4, // 105 i ◄ (17) - 0x25E2, // 106 j ◢ - added for slanted corners - 0x25E3, // 107 k ◣ - 0x25E4, // 108 l ◤ - 0x25E5, // 109 m ◥ - 0x256D, // 110 n ╭ - rounded corners - 0x256E, // 111 o ╮ - 0x256F, // 112 p ╯ - 0x2570, // 113 q ╰ - 0x0, // 114 r - free positions for future expansion - 0x0, // 115 s - 0x0, // 116 t - 0x0, // 117 u - 0x0, // 118 v - 0x0, // 119 w - 0x0, // 120 x - 0x0, // 121 y - 0x0, // 122 z - 0x0, // 123 { - 0x0, // 124 | - 0x2714, // 125 } ✔ - checkboxes or checklist items - 0x2718, // 126 ~ ✘ + u'☺', // 0x263A, 33 ! (1) - low ASCII symbols from DOS, moved to +32 + u'☻', // 0x263B, 34 " (2) + u'♥', // 0x2665, 35 # (3) + u'♦', // 0x2666, 36 $ (4) + u'♣', // 0x2663, 37 % (5) + u'♠', // 0x2660, 38 & (6) + u'•', // 0x2022, 39 ' (7) - inverse dot and circle left out, can be done with SGR + u'⌛', // 0x231B, 40 ( - hourglass (timer icon) + u'○', // 0x25CB, 41 ) (9) + u'↯', // 0x21AF, 42 * - electricity (lightning monitor...) + u'♪', // 0x266A, 43 + (13) + u'♫', // 0x266B, 44 , (14) + u'☼', // 0x263C, 45 - (15) + u'⌂', // 0x2302, 46 . (127) + u'☢', // 0x2622, 47 / - radioactivity (geiger counter...) + u'░', // 0x2591, 48 0 (176) - this block is kept aligned and ordered from DOS, moved -128 + u'▒', // 0x2592, 49 1 (177) + u'▓', // 0x2593, 50 2 (178) + u'│', // 0x2502, 51 3 (179) + u'┤', // 0x2524, 52 4 (180) + u'╡', // 0x2561, 53 5 (181) + u'╢', // 0x2562, 54 6 (182) + u'╖', // 0x2556, 55 7 (183) + u'╕', // 0x2555, 56 8 (184) + u'╣', // 0x2563, 57 9 (185) + u'║', // 0x2551, 58 : (186) + u'╗', // 0x2557, 59 ; (187) + u'╝', // 0x255D, 60 < (188) + u'╜', // 0x255C, 61 = (189) + u'╛', // 0x255B, 62 > (190) + u'┐', // 0x2510, 63 ? (191) + u'└', // 0x2514, 64 @ (192) + u'┴', // 0x2534, 65 A (193) + u'┬', // 0x252C, 66 B (194) + u'├', // 0x251C, 67 C (195) + u'─', // 0x2500, 68 D (196) + u'┼', // 0x253C, 69 E (197) + u'╞', // 0x255E, 70 F (198) + u'╟', // 0x255F, 71 G (199) + u'╚', // 0x255A, 72 H (200) + u'╔', // 0x2554, 73 I (201) + u'╩', // 0x2569, 74 J (202) + u'╦', // 0x2566, 75 K (203) + u'╠', // 0x2560, 76 L (204) + u'═', // 0x2550, 77 M (205) + u'╬', // 0x256C, 78 N (206) + u'╧', // 0x2567, 79 O (207) + u'╨', // 0x2568, 80 P (208) + u'╤', // 0x2564, 81 Q (209) + u'╥', // 0x2565, 82 R (210) + u'╙', // 0x2559, 83 S (211) + u'╘', // 0x2558, 84 T (212) + u'╒', // 0x2552, 85 U (213) + u'╓', // 0x2553, 86 V (214) + u'╫', // 0x256B, 87 W (215) + u'╪', // 0x256A, 88 X (216) + u'┘', // 0x2518, 89 Y (217) + u'┌', // 0x250C, 90 Z (218) + u'█', // 0x2588, 91 [ (219) + u'▄', // 0x2584, 92 \ (220) + u'▌', // 0x258C, 93 ] (221) + u'▐', // 0x2590, 94 ^ (222) + u'▀', // 0x2580, 95 _ (223) + u'↕', // 0x2195, 96 ` (18) - moved from low DOS ASCII + u'↑', // 0x2191, 97 a (24) + u'↓', // 0x2193, 98 b (25) + u'→', // 0x2192, 99 c (26) + u'←', // 0x2190, 100 d (27) + u'↔', // 0x2194, 101 e (29) + u'▲', // 0x25B2, 102 f (30) + u'▼', // 0x25BC, 103 g (31) + u'►', // 0x25BA, 104 h (16) + u'◄', // 0x25C4, 105 i (17) + u'◢', // 0x25E2, 106 j - added for slanted corners + u'◣', // 0x25E3, 107 k + u'◤', // 0x25E4, 108 l + u'◥', // 0x25E5, 109 m + u'╭', // 0x256D, 110 n - rounded corners + u'╮', // 0x256E, 111 o + u'╯', // 0x256F, 112 p + u'╰', // 0x2570, 113 q + u'▖', // 0x259, 114 r + u'▗', // 0x259, 115 s + u'▘', // 0x259, 116 t + u'▙', // 0x259, 117 u + u'▚', // 0x259, 118 v + u'▛', // 0x259, 119 w + u'▜', // 0x259, 120 x + u'▝', // 0x259, 121 y + u'▞', // 0x259, 122 z + u'▟', // 0x259, 123 { + 0, // 0x0, 124 | - reserved + u'✔', // 0x2714, 125 } - checkboxes or checklist items + u'✘', // 0x2718, 126 ~ // %%END:1%% }; From e49a8b0adf23148c6e8c08c2a9bd422970909379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 6 Oct 2017 23:25:01 +0200 Subject: [PATCH 05/33] added codepages 2 and 3 --- user/character_sets.h | 231 ++++++++++++++++++++++++++++++++++++++++-- user/screen.c | 14 +++ user/screen.h | 2 + 3 files changed, 236 insertions(+), 11 deletions(-) diff --git a/user/character_sets.h b/user/character_sets.h index 030cf48..21d9987 100644 --- a/user/character_sets.h +++ b/user/character_sets.h @@ -58,6 +58,7 @@ static const u16 codepage_0[] ESP_CONST_DATA = #define CODEPAGE_1_BEGIN 33 #define CODEPAGE_1_END 126 +// DOS, thin and double lines, arrows, angles, diagonals, thick border static const u16 codepage_1[] ESP_CONST_DATA = {// Unicode ASCII DOS // %%BEGIN:1%% @@ -142,20 +143,228 @@ static const u16 codepage_1[] ESP_CONST_DATA = u'╮', // 0x256E, 111 o u'╯', // 0x256F, 112 p u'╰', // 0x2570, 113 q - u'▖', // 0x259, 114 r - u'▗', // 0x259, 115 s - u'▘', // 0x259, 116 t - u'▙', // 0x259, 117 u - u'▚', // 0x259, 118 v - u'▛', // 0x259, 119 w - u'▜', // 0x259, 120 x - u'▝', // 0x259, 121 y - u'▞', // 0x259, 122 z - u'▟', // 0x259, 123 { - 0, // 0x0, 124 | - reserved + u'╱', // 0x0, 114 r - right up diagonal + u'╲', // 0x0, 115 s - right down diagonal + u'╳', // 0x0, 116 t + 0, // 0x0, 117 u + 0, // 0x0, 118 v + 0, // 0x0, 119 w + 0, // 0x0, 120 x + 0xE0B0, // powerline right triangle (filled), 121 y + 0xE0B1, // powerline right triangle (hollow), 122 z + 0xE0B2, // powerline left triangle (filled), 123 { + 0xE0B3, // powerline left triangle (hollow), 124 | - reserved u'✔', // 0x2714, 125 } - checkboxes or checklist items u'✘', // 0x2718, 126 ~ // %%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 diff --git a/user/screen.c b/user/screen.c index 755aa44..68e6854 100644 --- a/user/screen.c +++ b/user/screen.c @@ -1794,6 +1794,20 @@ utf8_remap(char *out, char g, char charset) } 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 */ if (g == '#') utf = 0x20a4; // £ break; diff --git a/user/screen.h b/user/screen.h index e38f776..422384c 100644 --- a/user/screen.h +++ b/user/screen.h @@ -156,6 +156,8 @@ typedef enum { CS_A_UKASCII = 'A', CS_0_DEC_SUPPLEMENTAL = '0', CS_1_DOS_437 = '1', + CS_2_BLOCKS_LINES = '2', + CS_3_LINES_EXTRA = '3', } CHARSET; enum ScreenSerializeTopic { From ef3b02e68ce1e2d2cf4fa29fe2640d37672d90cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 7 Oct 2017 00:46:19 +0200 Subject: [PATCH 06/33] added new chars to cp 1 --- front-end | 2 +- user/character_sets.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/front-end b/front-end index 8b610aa..d4931c1 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 8b610aa95bf8f7185c28cbe96c7485ec1af2383c +Subproject commit d4931c1f3125b5df1ad86c5a755f29c679a9b142 diff --git a/user/character_sets.h b/user/character_sets.h index 21d9987..436f743 100644 --- a/user/character_sets.h +++ b/user/character_sets.h @@ -146,10 +146,10 @@ static const u16 codepage_1[] ESP_CONST_DATA = u'╱', // 0x0, 114 r - right up diagonal u'╲', // 0x0, 115 s - right down diagonal u'╳', // 0x0, 116 t - 0, // 0x0, 117 u - 0, // 0x0, 118 v - 0, // 0x0, 119 w - 0, // 0x0, 120 x + u'↺', // 0x0, 117 u + u'↻', // 0x0, 118 v + u'¶', // 0x0, 119 w + u'⏻', // 0x0, 120 x 0xE0B0, // powerline right triangle (filled), 121 y 0xE0B1, // powerline right triangle (hollow), 122 z 0xE0B2, // powerline left triangle (filled), 123 { From be3e06aac13e2f42c791eacbfaf2b9cfba8de560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 8 Oct 2017 11:21:11 +0200 Subject: [PATCH 07/33] version bump --- user/version.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user/version.h b/user/version.h index b45211f..2742035 100644 --- a/user/version.h +++ b/user/version.h @@ -9,10 +9,10 @@ #define STR(x) STR_HELPER(x) #define FW_V_MAJOR 2 -#define FW_V_MINOR 1 -#define FW_V_PATCH 1 +#define FW_V_MINOR 2 +#define FW_V_PATCH 0 #define FW_V_SUFFIX "" -#define FW_CODENAME "Anthill" // 2.1 +#define FW_CODENAME "Bluet" // 2.2 #define FW_CODENAME_QUOTED "\""FW_CODENAME"\"" #define FW_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) FW_V_SUFFIX From c0adc253b4d366bae745d8d957e16f797bc9d392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 8 Oct 2017 12:34:50 +0200 Subject: [PATCH 08/33] add cgi for setting background image --- front-end | 2 +- user/apars_osc.c | 4 ++++ user/cgi_term_cfg.c | 13 ++++++++++-- user/screen.c | 48 +++++++++++++++++++++++++++++++++++++++------ user/screen.h | 10 ++++++++-- user/user_main.c | 2 ++ 6 files changed, 68 insertions(+), 11 deletions(-) diff --git a/front-end b/front-end index d4931c1..bd93365 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit d4931c1f3125b5df1ad86c5a755f29c679a9b142 +Subproject commit bd933657f453586cd837c84b410d081f2fd958ce diff --git a/user/apars_osc.c b/user/apars_osc.c index df68d07..387b329 100644 --- a/user/apars_osc.c +++ b/user/apars_osc.c @@ -49,6 +49,10 @@ apars_handle_osc(char *buffer) buffer[0] = 'G'; notify_growl(buffer); } + else if (n == 70) { + // ESPTerm: backdrop + screen_set_backdrop(buffer); + } else if (n >= 81 && n <= 85) { // ESPTerm: action button label screen_set_button_text(n - 80, buffer); diff --git a/user/cgi_term_cfg.c b/user/cgi_term_cfg.c index fe68015..13064a8 100644 --- a/user/cgi_term_cfg.c +++ b/user/cgi_term_cfg.c @@ -256,10 +256,16 @@ cgiTermCfgSetParams(HttpdConnData *connData) if (GET_ARG("term_title")) { cgi_dbg("Terminal title default text: \"%s\"", buff); - strncpy_safe(termconf->title, buff, 64); // ATTN those must match the values in + strncpy_safe(termconf->title, buff, TERM_TITLE_LEN); // ATTN those must match the values in topics |= TOPIC_CHANGE_TITLE; } + if (GET_ARG("backdrop")) { + cgi_dbg("Terminal backdrop url: \"%s\"", buff); + strncpy_safe(termconf->backdrop, buff, TERM_BACKDROP_LEN); // ATTN those must match the values in + topics |= TOPIC_CHANGE_BACKDROP; + } + for (int btn_i = 1; btn_i <= TERM_BTN_COUNT; btn_i++) { sprintf(buff, "btn%d", btn_i); if (GET_ARG(buff)) { @@ -416,7 +422,7 @@ cgiTermCfgSetParams(HttpdConnData *connData) httpd_cgi_state ICACHE_FLASH_ATTR tplTermCfg(HttpdConnData *connData, char *token, void **arg) { -#define BUFLEN TERM_TITLE_LEN +#define BUFLEN 100 // large enough for backdrop char buff[BUFLEN]; char buff2[10]; @@ -492,6 +498,9 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg) else if (streq(token, "term_title")) { strncpy_safe(buff, termconf->title, BUFLEN); } + else if (streq(token, "backdrop")) { + strncpy_safe(buff, termconf->backdrop, BUFLEN); + } else if (streq(token, "uart_baud")) { sprintf(buff, "%d", sysconf->uart_baudrate); } diff --git a/user/screen.c b/user/screen.c index 68e6854..a675a71 100644 --- a/user/screen.c +++ b/user/screen.c @@ -213,6 +213,7 @@ terminal_restore_defaults(void) termconf->debugbar = SCR_DEF_DEBUGBAR; termconf->allow_decopt_12 = SCR_DEF_DECOPT12; termconf->ascii_debug = SCR_DEF_ASCIIDEBUG; + termconf->backdrop[0] = 0; } /** @@ -247,6 +248,11 @@ terminal_apply_settings_noclear(void) termconf->ascii_debug = SCR_DEF_ASCIIDEBUG; changed = 1; } + if (termconf->config_version < 4) { + persist_dbg("termconf: Updating to version %d", 1); + termconf->backdrop[0] = 0; + changed = 1; + } termconf->config_version = TERMCONF_VERSION; @@ -385,6 +391,7 @@ screen_reset_do(bool size, bool labels) if (labels) { strcpy(termconf_live.title, termconf->title); + strcpy(termconf_live.backdrop, termconf->backdrop); for (int i = 1; i <= TERM_BTN_COUNT; i++) { strcpy(termconf_live.btn[i], termconf->btn[i]); @@ -394,7 +401,7 @@ screen_reset_do(bool size, bool labels) termconf_live.show_buttons = termconf->show_buttons; 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 @@ -1009,6 +1016,18 @@ screen_set_button_text(int num, const char *text) 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 */ @@ -1907,6 +1926,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, #define TOPICMARK_BELL '!' #define TOPICMARK_CURSOR 'C' #define TOPICMARK_SCREEN 'S' +#define TOPICMARK_BACKDROP 'W' if (ss == NULL) { // START! @@ -2025,9 +2045,11 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_c(TOPICMARK_TITLE); int len = (int) strlen(termconf_live.title); - memcpy(bb, termconf_live.title, len); - bb += len; - remain -= len; + if (len > 0) { + memcpy(bb, termconf_live.title, len); + bb += len; + remain -= len; + } bufput_c('\x01'); END_TOPIC @@ -2038,11 +2060,25 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, for (int i = 0; i < TERM_BTN_COUNT; i++) { int len = (int) strlen(termconf_live.btn[i]); - memcpy(bb, termconf_live.btn[i], len); + if (len > 0) { + memcpy(bb, termconf_live.btn[i], len); + bb += len; + remain -= len; + } + bufput_c('\x01'); + } + END_TOPIC + + BEGIN_TOPIC(TOPIC_CHANGE_BACKDROP, TERM_BACKDROP_LEN+1+1) + bufput_c(TOPICMARK_BACKDROP); + + int len = (int) strlen(termconf_live.backdrop); + if (len > 0) { + memcpy(bb, termconf_live.backdrop, len); bb += len; remain -= len; - bufput_c('\x01'); } + bufput_c('\x01'); END_TOPIC BEGIN_TOPIC(TOPIC_INTERNAL, 45) diff --git a/user/screen.h b/user/screen.h index 422384c..50c1fb5 100644 --- a/user/screen.h +++ b/user/screen.h @@ -38,6 +38,7 @@ #define TERM_BTN_MSG_LEN 10 #define TERM_TITLE_LEN 64 #define TERM_BTN_COUNT 5 +#define TERM_BACKDROP_LEN 100 #define SCR_DEF_DISPLAY_TOUT_MS 12 #define SCR_DEF_DISPLAY_COOLDOWN_MS 35 @@ -74,8 +75,8 @@ enum CursorShape { // Size designed for the terminal config structure // Must be constant to avoid corrupting user config after upgrade -#define TERMCONF_SIZE 300 -#define TERMCONF_VERSION 3 +#define TERMCONF_SIZE 400 +#define TERMCONF_VERSION 4 typedef struct { u32 width; @@ -100,6 +101,7 @@ typedef struct { bool debugbar; bool allow_decopt_12; bool ascii_debug; + char backdrop[TERM_BACKDROP_LEN]; } TerminalConfigBundle; // Live config @@ -148,6 +150,8 @@ void screen_resize(int rows, int cols); void screen_set_title(const char *title); /** Set a button text */ void screen_set_button_text(int num, const char *text); +/** Change backdrop */ +void screen_set_backdrop(const char *url); // --- Encoding --- @@ -169,6 +173,7 @@ enum ScreenSerializeTopic { TOPIC_CHANGE_CURSOR = (1<<5), TOPIC_INTERNAL = (1<<6), // debugging internal state TOPIC_BELL = (1<<7), // beep + TOPIC_CHANGE_BACKDROP = (1<<8), TOPIC_FLAG_NOCLEAN = (1<<15), // do not clean dirty extents // combos @@ -177,6 +182,7 @@ enum ScreenSerializeTopic { TOPIC_CHANGE_CONTENT_ALL | TOPIC_CHANGE_CURSOR | TOPIC_CHANGE_TITLE | + TOPIC_CHANGE_BACKDROP | TOPIC_CHANGE_BUTTONS, }; diff --git a/user/user_main.c b/user/user_main.c index db52518..9dee6b7 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -150,6 +150,8 @@ static void ICACHE_FLASH_ATTR user_start(void *unused) // 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 + dbg("tsize=%d", sizeof(TerminalConfigBundle)); + #if DEBUG_HEAP // Heap use timer & blink TIMER_START(&prHeapTimer, prHeapTimerCb, HEAP_TIMER_MS, 1); From 4e74d1cd79b9e100f286efd0973baa2ccbe081cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 8 Oct 2017 12:54:26 +0200 Subject: [PATCH 09/33] updated front-end ref --- front-end | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front-end b/front-end index 855d308..4115dec 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 855d308962c6232a5bb9df21c3ab93622d220a06 +Subproject commit 4115decb128c6c9fbaa7d49a0aa6ef0e8b36c5ac From c5670c09bb274f8e7f0a391862d334edf36ae159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 8 Oct 2017 12:57:36 +0200 Subject: [PATCH 10/33] update frontend ref --- front-end | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front-end b/front-end index 4115dec..d2fabc4 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 4115decb128c6c9fbaa7d49a0aa6ef0e8b36c5ac +Subproject commit d2fabc40f1874cabd7f7ceca690a5874887c3c5d From cabd42f2b4884be316bce301edd9a09ef3887eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 16:34:00 +0200 Subject: [PATCH 11/33] base of xmarco-based config, used for wifi and network --- CMakeLists.txt | 2 +- front-end | 2 +- prepro.sh | 3 + user/cgi_network.c | 172 +++--------------------------------- user/cgi_wifi.c | 199 +++--------------------------------------- user/config_xmacros.c | 99 +++++++++++++++++++++ user/config_xmacros.h | 85 ++++++++++++++++++ user/wifimgr.c | 172 +++++++++++++++++++++++++++++++----- user/wifimgr.h | 72 +++++++++++---- 9 files changed, 415 insertions(+), 391 deletions(-) create mode 100755 prepro.sh create mode 100644 user/config_xmacros.c create mode 100644 user/config_xmacros.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e65181..6345770 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,7 +148,7 @@ set(SOURCE_FILES user/character_sets.h user/utf8.h user/utf8.c - user/cgi_logging.h) + user/cgi_logging.h user/config_xmacros.h user/config_xmacros.c) include_directories(include) include_directories(libesphttpd/esphttpclient) diff --git a/front-end b/front-end index d2fabc4..32c889b 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit d2fabc40f1874cabd7f7ceca690a5874887c3c5d +Subproject commit 32c889b714dae859f51e6b46829fd85be59d9ed0 diff --git a/prepro.sh b/prepro.sh new file mode 100755 index 0000000..997d376 --- /dev/null +++ b/prepro.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +xtensa-lx106-elf-gcc -E -Iuser -Ilibesphttpd/include -Iesp_iot_sdk_v1.5.2/include -Iinclude $@ diff --git a/user/cgi_network.c b/user/cgi_network.c index db86874..6ed9d40 100644 --- a/user/cgi_network.c +++ b/user/cgi_network.c @@ -9,6 +9,7 @@ configuring the network settings #include "persist.h" #include "helpers.h" #include "cgi_logging.h" +#include "config_xmacros.h" #define SET_REDIR_SUC "/cfg/network" #define SET_REDIR_ERR SET_REDIR_SUC"?err=" @@ -48,138 +49,9 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData) // ---- AP DHCP server lease time ---- - if (GET_ARG("ap_dhcp_time")) { - cgi_dbg("Setting DHCP lease time to: %s min.", buff); - int min = atoi(buff); - if (min >= 1 && min <= 2880) { - 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,"); - } - } +#define X XSET_CGI_FUNC + XTABLE_WIFI +#undef X (void) redir_url; @@ -216,7 +88,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData) //Template code for the WLAN page. httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *token, void **arg) { - char buff[20]; + char buff[64]; u8 mac[6]; if (token == NULL) { @@ -226,34 +98,12 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *toke strcpy(buff, ""); // fallback - if (streq(token, "ap_dhcp_time")) { - sprintf(buff, "%d", wificonf->ap_dhcp_time); - } - else if (streq(token, "ap_dhcp_start")) { - sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr)); - } - else if (streq(token, "ap_dhcp_end")) { - 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")) { +#define X XGET_CGI_FUNC + XTABLE_WIFI +#undef X + + // non-config + if (streq(token, "sta_mac")) { wifi_get_macaddr(STATION_IF, mac); sprintf(buff, MACSTR, MAC2STR(mac)); } diff --git a/user/cgi_wifi.c b/user/cgi_wifi.c index 7378977..41be75d 100644 --- a/user/cgi_wifi.c +++ b/user/cgi_wifi.c @@ -19,6 +19,7 @@ Cgi/template routines for the /wifi url. #include "wifimgr.h" #include "persist.h" #include "helpers.h" +#include "config_xmacros.h" #include "cgi_logging.h" #define SET_REDIR_SUC "/cfg/wifi" @@ -66,46 +67,6 @@ int ICACHE_FLASH_ATTR rssi2perc(int rssi) 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 * the static cgiWifiAps struct. @@ -363,18 +324,13 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData) bool sta_turned_on = false; bool sta_ssid_pw_changed = false; +#define X XSET_CGI_FUNC + XTABLE_WIFI +#undef X + // ---- WiFi opmode ---- - if (GET_ARG("opmode")) { - 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,"); - } - } + // those are helpers, not a real prop if (GET_ARG("ap_enable")) { cgi_dbg("Enable AP: %s", buff); @@ -399,114 +355,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; if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) { @@ -552,7 +400,6 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData) return HTTPD_CGI_DONE; } - //Template code for the WLAN page. httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) { @@ -567,39 +414,17 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, strcpy(buff, ""); // fallback - if (streq(token, "opmode_name")) { - strcpy(buff, opmode2str(wificonf->opmode)); - } - else if (streq(token, "opmode")) { - sprintf(buff, "%d", wificonf->opmode); - } - else if (streq(token, "sta_enable")) { +#define X XGET_CGI_FUNC + XTABLE_WIFI +#undef X + + // non-config + if (streq(token, "sta_enable")) { sprintf(buff, "%d", (wificonf->opmode & STATION_MODE) != 0); } else if (streq(token, "ap_enable")) { 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")) { sprintf(buff, "%d", wifi_station_get_rssi()); } diff --git a/user/config_xmacros.c b/user/config_xmacros.c new file mode 100644 index 0000000..55105f8 --- /dev/null +++ b/user/config_xmacros.c @@ -0,0 +1,99 @@ +// +// 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_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; +} diff --git a/user/config_xmacros.h b/user/config_xmacros.h new file mode 100644 index 0000000..bad4d18 --- /dev/null +++ b/user/config_xmacros.h @@ -0,0 +1,85 @@ +// +// Created by MightyPork on 2017/10/22. +// + +#ifndef ESPTERM_CONFIG_XMACROS_H +#define ESPTERM_CONFIG_XMACROS_H + +#include +#include + +#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 +}; + +// 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); + +/** + * @param arg - max string length + */ +enum xset_result xset_string(const char *name, char *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, cast, xset, xsarg, xnotify) \ + if (GET_ARG(#name)) { \ + enum xset_result res = xset(#name, cast &wificonf->name, 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, cast, xset, xsarg, xnotify) \ + if (streq(token, #name)) xget(buff, deref wificonf->name); + +#endif //ESPTERM_CONFIG_XMACROS_H diff --git a/user/wifimgr.c b/user/wifimgr.c index 2588365..46d028a 100644 --- a/user/wifimgr.c +++ b/user/wifimgr.c @@ -4,10 +4,127 @@ #include "wifimgr.h" #include "persist.h" +#include "cgi_logging.h" +#include "config_xmacros.h" WiFiConfigBundle * const wificonf = &persist.current.wificonf; 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, u8 **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, u8 **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(); @@ -42,13 +159,11 @@ wifimgr_restore_defaults(void) wificonf->ap_password[0] = 0; // PSK2 always if password is not null. wificonf->ap_hidden = false; - IP4_ADDR(&wificonf->ap_addr.ip, 192, 168, 4, 1); - IP4_ADDR(&wificonf->ap_addr.netmask, 255, 255, 255, 0); - wificonf->ap_addr.gw.addr = wificonf->ap_addr.gw.addr; + IP4_ADDR(&wificonf->ap_addr_ip, 192, 168, 4, 1); + IP4_ADDR(&wificonf->ap_addr_mask, 255, 255, 255, 0); - IP4_ADDR(&wificonf->ap_dhcp_range.start_ip, 192, 168, 4, 100); - IP4_ADDR(&wificonf->ap_dhcp_range.end_ip, 192, 168, 4, 200); - wificonf->ap_dhcp_range.enable = 1; // this will never get changed, idk why it's even there + IP4_ADDR(&wificonf->ap_dhcp_start, 192, 168, 4, 100); + IP4_ADDR(&wificonf->ap_dhcp_end, 192, 168, 4, 200); wificonf->ap_dhcp_time = 120; // --- Client config --- @@ -56,9 +171,9 @@ wifimgr_restore_defaults(void) wificonf->sta_password[0] = 0; 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.netmask, 255, 255, 255, 0); - IP4_ADDR(&wificonf->sta_addr.gw, 192, 168, 0, 1); + 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_mask, 255, 255, 255, 0); + IP4_ADDR(&wificonf->sta_addr_gw, 192, 168, 0, 1); // a common default... } static void ICACHE_FLASH_ATTR @@ -83,13 +198,18 @@ configure_station(void) } else { wifi_info("[WiFi] Setting up static IP..."); - 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.gw = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.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_mask.addr)); + wifi_dbg("[WiFi] Client.gw = "IPSTR, GOOD_IP2STR(wificonf->sta_addr_gw.addr)); wifi_station_dhcpc_stop(); // 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!"); return; } @@ -112,7 +232,7 @@ configure_ap(void) strcpy((char *) conf.password, (char *) wificonf->ap_password); conf.authmode = (wificonf->ap_password[0] == 0 ? AUTH_OPEN : AUTH_WPA2_PSK); 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.beacon_interval = 100; // default 100 ms @@ -127,24 +247,32 @@ configure_ap(void) // Set 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.mask = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.netmask.addr)); - wifi_dbg("[WiFi] SoftAP.gw = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.gw.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_mask.addr)); wifi_softap_dhcps_stop(); // 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!"); return; } 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.end = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_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_end.addr)); 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!"); return; } @@ -190,7 +318,7 @@ wifimgr_apply_settings(void) } if (opmode != wificonf->opmode) { - wifi_set_opmode_current(wificonf->opmode); + wifi_set_opmode_current((WIFI_MODE) wificonf->opmode); } // Configure the client diff --git a/user/wifimgr.h b/user/wifimgr.h index 146c3c8..22e70b0 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -19,6 +19,43 @@ #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.........Cast..XSET.........................NOTIFY +#define XTABLE_WIFI \ + X(u8, opmode, , , xget_dec, , xset_wifi_opmode, NULL, xnoop) \ + \ + X(u8, tpw, , , xget_dec, , xset_wifi_tpw, NULL, wifimgr_notify_ap) \ + X(u8, ap_channel, , , xget_dec, , xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ + X(u8, ap_ssid, [SSID_LEN], , xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ + X(u8, ap_password, [PASSWORD_LEN], , xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ + X(bool, ap_hidden, , , xget_bool, , xset_bool, NULL, wifimgr_notify_ap) \ + \ + X(u16, ap_dhcp_time, , , xget_dec, , xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ + X(u32, unused1, , , xget_dummy, , xset_dummy, NULL, xnoop) \ + X(struct ip_addr, ap_dhcp_start, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_dhcp_end, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + \ + X(struct ip_addr, ap_addr_ip, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_mask, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + \ + \ + X(u32, unused2, , , xget_dummy, , xset_dummy, NULL, xnoop) \ + X(u8, sta_ssid, [SSID_LEN], , xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ + X(u8, sta_password, [PASSWORD_LEN], , xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ + X(bool, sta_dhcp_enable, , , xget_bool, , xset_bool, NULL, wifimgr_notify_sta) \ + \ + X(struct ip_addr, sta_addr_ip, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_mask, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_gw, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ + \ + \ + X(u8, config_version, , , xget_dec, , xset_u8, NULL, xnoop) + +// 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 * and the active state. @@ -26,25 +63,15 @@ * This block can be used eg. for WiFi config backup. */ typedef struct { - WIFI_MODE opmode : 8; - u8 tpw; - - // 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; +#define X( \ + type, name, suffix, \ + deref, xget, \ + cast, xset, xsarg, \ + xnotify) type name suffix; + + XTABLE_WIFI + +#undef X } WiFiConfigBundle; typedef struct { @@ -62,6 +89,13 @@ void wifimgr_apply_settings(void); 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, u8 **field, const char *buff, const void *arg); +enum xset_result xset_wifi_pwd(const char *name, u8 **field, const char *buff, const void *arg); + #if DEBUG_WIFI #define wifi_warn warn #define wifi_dbg dbg From 9146c9880ac02594c9a6cec1380a21f9d32a19c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 16:45:45 +0200 Subject: [PATCH 12/33] slightly cleaner XTABLE_WIFI --- user/wifimgr.h | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/user/wifimgr.h b/user/wifimgr.h index 22e70b0..ee28d40 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -23,35 +23,36 @@ #define wifimgr_notify_sta() { wifi_change_flags.ap = true; } //....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY +// 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_WIFI \ - X(u8, opmode, , , xget_dec, , xset_wifi_opmode, NULL, xnoop) \ + X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, xnoop) \ \ - X(u8, tpw, , , xget_dec, , xset_wifi_tpw, NULL, wifimgr_notify_ap) \ - X(u8, ap_channel, , , xget_dec, , xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ - X(u8, ap_ssid, [SSID_LEN], , xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ - X(u8, ap_password, [PASSWORD_LEN], , xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ - X(bool, ap_hidden, , , xget_bool, , xset_bool, NULL, wifimgr_notify_ap) \ + X(u8, tpw, /**/, /**/, xget_dec, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ + X(u8, ap_channel, /**/, /**/, xget_dec, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ + X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ + X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ + X(bool, ap_hidden, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_ap) \ \ - X(u16, ap_dhcp_time, , , xget_dec, , xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ - X(u32, unused1, , , xget_dummy, , xset_dummy, NULL, xnoop) \ - X(struct ip_addr, ap_dhcp_start, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_dhcp_end, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + X(u16, ap_dhcp_time, /**/, /**/, xget_dec, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ + X(u32, unused1, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ + X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ - X(struct ip_addr, ap_addr_ip, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_addr_mask, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ \ - X(u32, unused2, , , xget_dummy, , xset_dummy, NULL, xnoop) \ - X(u8, sta_ssid, [SSID_LEN], , xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ - X(u8, sta_password, [PASSWORD_LEN], , xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ - X(bool, sta_dhcp_enable, , , xget_bool, , xset_bool, NULL, wifimgr_notify_sta) \ + X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ + X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ + X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ + X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_sta) \ \ - X(struct ip_addr, sta_addr_ip, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_mask, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_gw, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ \ \ - X(u8, config_version, , , xget_dec, , xset_u8, NULL, xnoop) + X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, xnoop) // unused1 - replaces 'enabled' bit from old dhcps_lease struct // unused2 - gap after 'ap_gw' which isn't used and doesn't make sense From 2c80b59d7118d9ee9f56d89e1359900e79669230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 16:46:28 +0200 Subject: [PATCH 13/33] formatting --- user/wifimgr.h | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/user/wifimgr.h b/user/wifimgr.h index ee28d40..a90510f 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -25,34 +25,34 @@ //....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY // 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_WIFI \ - X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, xnoop) \ + X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, xnoop) \ \ - X(u8, tpw, /**/, /**/, xget_dec, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ - X(u8, ap_channel, /**/, /**/, xget_dec, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ - X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ - X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ - X(bool, ap_hidden, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_ap) \ + X(u8, tpw, /**/, /**/, xget_dec, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ + X(u8, ap_channel, /**/, /**/, xget_dec, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ + X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ + X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ + X(bool, ap_hidden, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_ap) \ \ - X(u16, ap_dhcp_time, /**/, /**/, xget_dec, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ - X(u32, unused1, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ - X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(u16, ap_dhcp_time, /**/, /**/, xget_dec, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ + X(u32, unused1, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ + X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ - X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ \ - X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ - X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ - X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ - X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_sta) \ + X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ + X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ + X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ + X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_sta) \ \ - X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ \ \ - X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, xnoop) + X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, xnoop) // unused1 - replaces 'enabled' bit from old dhcps_lease struct // unused2 - gap after 'ap_gw' which isn't used and doesn't make sense From 3eac0ee7946d3d69e67d3f05cc01e4ea0f07f26c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 16:50:45 +0200 Subject: [PATCH 14/33] fmt again --- user/config_xmacros.h | 3 +++ user/wifimgr.h | 9 ++------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/user/config_xmacros.h b/user/config_xmacros.h index bad4d18..9da5daf 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -82,4 +82,7 @@ enum xset_result xset_string(const char *name, char *field, const char *buff, co #define XGET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify) \ if (streq(token, #name)) xget(buff, deref wificonf->name); +#define XSTRUCT_FIELD(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify) \ + type name suffix; + #endif //ESPTERM_CONFIG_XMACROS_H diff --git a/user/wifimgr.h b/user/wifimgr.h index a90510f..2302571 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -8,6 +8,7 @@ #define ESP_VT100_FIRMWARE_WIFI_MANAGER_H #include +#include "config_xmacros.h" #include "cgi_wifi.h" #define SSID_LEN 32 @@ -64,14 +65,8 @@ * This block can be used eg. for WiFi config backup. */ typedef struct { -#define X( \ - type, name, suffix, \ - deref, xget, \ - cast, xset, xsarg, \ - xnotify) type name suffix; - +#define X XSTRUCT_FIELD XTABLE_WIFI - #undef X } WiFiConfigBundle; From f0bc70553a9649944220f8bb54520bd98a3f00af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 17:55:52 +0200 Subject: [PATCH 15/33] use xmacros for system --- user/cgi_network.c | 2 + user/cgi_system.c | 135 +++++++++++++++++------------------------- user/cgi_wifi.c | 2 + user/config_xmacros.c | 21 ++++++- user/config_xmacros.h | 18 +++--- user/routes.c | 4 +- user/syscfg.c | 104 +++++++++++++++++++++++++++++++- user/syscfg.h | 44 +++++++++++--- user/wifimgr.c | 6 ++ user/wifimgr.h | 42 ++++++------- 10 files changed, 255 insertions(+), 123 deletions(-) diff --git a/user/cgi_network.c b/user/cgi_network.c index 6ed9d40..e417dd7 100644 --- a/user/cgi_network.c +++ b/user/cgi_network.c @@ -49,6 +49,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData) // ---- AP DHCP server lease time ---- +#define XSTRUCT wificonf #define X XSET_CGI_FUNC XTABLE_WIFI #undef X @@ -98,6 +99,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *toke strcpy(buff, ""); // fallback +#define XSTRUCT wificonf #define X XGET_CGI_FUNC XTABLE_WIFI #undef X diff --git a/user/cgi_system.c b/user/cgi_system.c index d23d20b..3f939a7 100755 --- a/user/cgi_system.c +++ b/user/cgi_system.c @@ -12,6 +12,9 @@ #define SET_REDIR_SUC "/cfg/system" #define SET_REDIR_ERR SET_REDIR_SUC"?err=" +// Select which struct we want to use for X tables +#define XSTRUCT sysconf + static ETSTimer tmr; static void ICACHE_FLASH_ATTR tmrCb(void *arg) @@ -95,106 +98,77 @@ cgiSystemCfgSetParams(HttpdConnData *connData) memcpy(admin_backup, &persist.admin, sizeof(AdminConfigBlock)); memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle)); + // flags for the template builder + bool admin = false, tpl = false; do { - if (!GET_ARG("pw")) { - break; // if no PW in GET, not trying to configure anything protected - } - - if (!streq(buff, persist.admin.pw)) { - warn("Bad admin pw!"); - redir_url += sprintf(redir_url, "pw,"); - break; - } - - // authenticated OK - if (GET_ARG("pwlock")) { - cgi_dbg("pwlock: %s", buff); - int pwlock = atoi(buff); - if (pwlock < 0 || pwlock >= PWLOCK_MAX) { - cgi_warn("Bad pwlock %s", buff); - redir_url += sprintf(redir_url, "pwlock,"); - break; + // Check admin PW + if (GET_ARG("pw")) { + if (!streq(buff, persist.admin.pw)) { + warn("Bad admin pw!"); + redir_url += sprintf(redir_url, "pw,"); + break; // Abort + } else { + admin = true; } - - sysconf->pwlock = (enum pwlock) pwlock; } - if (GET_ARG("access_pw")) { - cgi_dbg("access_pw: %s", buff); - + // Changing admin PW + if (admin && GET_ARG("admin_pw")) { if (strlen(buff)) { - strcpy(buff2, buff); - if (!GET_ARG("access_pw2")) { - cgi_warn("Missing repeated access_pw %s", buff); - redir_url += sprintf(redir_url, "access_pw2,"); - break; - } - - if (!streq(buff, buff2)) { - cgi_warn("Bad repeated access_pw %s", buff); - redir_url += sprintf(redir_url, "access_pw2,"); - break; - } - - if (strlen(buff) >= 64) { - cgi_warn("Too long access_pw %s", buff); - redir_url += sprintf(redir_url, "access_pw,"); - break; - } - - cgi_dbg("Changing access PW!"); - strncpy(sysconf->access_pw, buff, 64); - } - } - - if (GET_ARG("access_name")) { - cgi_dbg("access_name: %s", buff); - - if (!strlen(buff) || strlen(buff) >= 32) { - cgi_warn("Too long access_name %s", buff); - redir_url += sprintf(redir_url, "access_name,"); - break; - } - - strncpy(sysconf->access_name, buff, 32); - } - - if (GET_ARG("admin_pw")) { - cgi_dbg("admin_pw: %s", buff); + cgi_dbg("admin_pw: %s", buff); - if (strlen(buff)) { strcpy(buff2, buff); if (!GET_ARG("admin_pw2")) { cgi_warn("Missing repeated admin_pw %s", buff); redir_url += sprintf(redir_url, "admin_pw2,"); - break; + break; // Abort } if (!streq(buff, buff2)) { cgi_warn("Bad repeated admin_pw %s", buff); redir_url += sprintf(redir_url, "admin_pw2,"); - break; + break; // Abort } if (strlen(buff) >= 64) { cgi_warn("Too long admin_pw %s", buff); redir_url += sprintf(redir_url, "admin_pw,"); - break; + break; // Abort } cgi_dbg("Changing admin PW!"); strncpy(persist.admin.pw, buff, 64); + + break; // this is the only field in this form } } - } while (0); - if (GET_ARG("overclock")) { - cgi_dbg("overclock = %s", buff); - int enable = atoi(buff); - if (sysconf->overclock != enable) { - sysconf->overclock = (bool)enable; + // 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 X XSET_CGI_FUNC + XTABLE_SYSCONF +#undef X + + } while (0); (void)redir_url; @@ -236,13 +210,13 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg) strcpy(buff, ""); // fallback - if (streq(token, "pwlock")) { - sprintf(buff, "%d", sysconf->pwlock); - } - else if (streq(token, "access_name")) { - sprintf(buff, "%s", sysconf->access_name); - } - else if (streq(token, "def_access_name")) { + const bool admin = false, tpl=true; + +#define X XGET_CGI_FUNC + XTABLE_SYSCONF +#undef X + + if (streq(token, "def_access_name")) { sprintf(buff, "%s", DEF_ACCESS_NAME); } else if (streq(token, "def_access_pw")) { @@ -251,9 +225,6 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg) else if (streq(token, "def_admin_pw")) { sprintf(buff, "%s", DEFAULT_ADMIN_PW); } - else if (streq(token, "overclock")) { - sprintf(buff, "%d", sysconf->overclock); - } tplSend(connData, buff, -1); return HTTPD_CGI_DONE; diff --git a/user/cgi_wifi.c b/user/cgi_wifi.c index 41be75d..fe09fda 100644 --- a/user/cgi_wifi.c +++ b/user/cgi_wifi.c @@ -324,6 +324,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData) bool sta_turned_on = false; bool sta_ssid_pw_changed = false; +#define XSTRUCT wificonf #define X XSET_CGI_FUNC XTABLE_WIFI #undef X @@ -414,6 +415,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, strcpy(buff, ""); // fallback +#define XSTRUCT wificonf #define X XGET_CGI_FUNC XTABLE_WIFI #undef X diff --git a/user/config_xmacros.c b/user/config_xmacros.c index 55105f8..d52f6cf 100644 --- a/user/config_xmacros.c +++ b/user/config_xmacros.c @@ -81,7 +81,7 @@ xset_u8(const char *name, u8 *field, const char *buff, const void *arg) } enum xset_result ICACHE_FLASH_ATTR -xset_string(const char *name, char *field, const char *buff, const void *arg) +xset_string(const char *name, s8 **field, const char *buff, const void *arg) { cgi_dbg("Setting %s = %s", name, buff); u32 maxlen = (u32) arg; @@ -97,3 +97,22 @@ xset_string(const char *name, char *field, const char *buff, const void *arg) } return XSET_UNCHANGED; } + + +enum xset_result ICACHE_FLASH_ATTR +xset_ustring(const char *name, u8 **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((char *)field, buff)) { + strncpy_safe(field, buff, (u32)arg); + return XSET_SET; + } + return XSET_UNCHANGED; +} diff --git a/user/config_xmacros.h b/user/config_xmacros.h index 9da5daf..53495b0 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -64,7 +64,8 @@ enum xset_result xset_u8(const char *name, u8 *field, const char *buff, const vo /** * @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_string(const char *name, s8 **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 @@ -72,17 +73,20 @@ enum xset_result xset_string(const char *name, char *field, const char *buff, co * 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, cast, xset, xsarg, xnotify) \ - if (GET_ARG(#name)) { \ - enum xset_result res = xset(#name, cast &wificonf->name, buff, (const void*) (xsarg)); \ +#define XSET_CGI_FUNC(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ + if ((allow) && GET_ARG(#name)) { \ + enum xset_result res = xset(#name, cast &XSTRUCT->name, 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, cast, xset, xsarg, xnotify) \ - if (streq(token, #name)) xget(buff, deref wificonf->name); +#define XGET_CGI_FUNC(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ + if ((allow) && streq(token, #name)) xget(buff, deref XSTRUCT->name); -#define XSTRUCT_FIELD(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify) \ +#define XSTRUCT_FIELD(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ type name suffix; +#define XDUMP_FIELD(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ + { xget(buff, deref XSTRUCT->name); dbg(#name " = %s", buff); } + #endif //ESPTERM_CONFIG_XMACROS_H diff --git a/user/routes.c b/user/routes.c index 4b6b502..895133e 100644 --- a/user/routes.c +++ b/user/routes.c @@ -23,8 +23,8 @@ static int ICACHE_FLASH_ATTR wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) { if (no == 0) { - os_strcpy(user, sysconf->access_name); - os_strcpy(pass, sysconf->access_pw); + os_strcpy(user, (const char *) sysconf->access_name); + os_strcpy(pass, (const char *) sysconf->access_pw); return 1; } if (no == 1) { diff --git a/user/syscfg.c b/user/syscfg.c index cd22d77..9a2a6f2 100644 --- a/user/syscfg.c +++ b/user/syscfg.c @@ -6,12 +6,112 @@ #include "persist.h" #include "uart_driver.h" #include "serial.h" +#include "cgi_logging.h" 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, u8 **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 sysconf_apply_settings(void) { +// char buff[64]; +//#define XSTRUCT sysconf +//#define X XDUMP_FIELD +// XTABLE_SYSCONF +//#undef X + bool changed = false; if (sysconf->config_version < 1) { dbg("Upgrading syscfg to v 1"); @@ -40,7 +140,7 @@ sysconf_restore_defaults(void) sysconf->config_version = SYSCONF_VERSION; sysconf->access_pw[0] = 0; sysconf->pwlock = PWLOCK_NONE; - strcpy(sysconf->access_pw, DEF_ACCESS_PW); - strcpy(sysconf->access_name, DEF_ACCESS_NAME); + strcpy((char *)sysconf->access_pw, DEF_ACCESS_PW); + strcpy((char *)sysconf->access_name, DEF_ACCESS_NAME); sysconf->overclock = false; } diff --git a/user/syscfg.h b/user/syscfg.h index e316b68..70ea94b 100644 --- a/user/syscfg.h +++ b/user/syscfg.h @@ -6,6 +6,7 @@ #define ESP_VT100_FIRMWARE_SYSCFG_H #include +#include "config_xmacros.h" // Size designed for the wifi config structure // Must be constant to avoid corrupting user config after upgrade @@ -24,15 +25,36 @@ enum pwlock { PWLOCK_MAX = 5, }; + +//....Type................Name..Suffix...............Deref..XGET........Allow..Cast..XSET.........................NOTIFY +// 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, 1, /**/, xset_sys_baudrate, NULL, xnoop) \ + X(u8, uart_parity, /**/, /**/, xget_dec, 1, /**/, xset_sys_parity, NULL, xnoop) \ + X(u8, uart_stopbits, /**/, /**/, xget_dec, 1, /**/, xset_sys_stopbits, NULL, xnoop) \ + \ + X(u8, config_version, /**/, /**/, xget_dec, 0, /**/, xset_u8, NULL, xnoop) \ + \ + X(u8, pwlock, /**/, /**/, xget_dec, admin|tpl, /**/, xset_sys_pwlock, NULL, xnoop) \ + X(u8, access_pw, [64], /**/, xget_ustring, admin, (u8**), xset_sys_accesspw, NULL, xnoop) \ + X(u8, access_name, [32], /**/, xget_ustring, admin|tpl, (u8**), xset_ustring, NULL, xnoop) \ + \ + X(bool, overclock, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, xnoop) \ + + typedef struct { - u32 uart_baudrate; - u8 uart_parity; - u8 uart_stopbits; - u8 config_version; - enum pwlock pwlock : 8; // page access lock - char access_pw[64]; // access password - char access_name[32]; // access name - bool overclock; +#define X XSTRUCT_FIELD + XTABLE_SYSCONF +#undef X + +// u32 uart_baudrate; +// u8 uart_parity; +// u8 uart_stopbits; +// u8 config_version; +// enum pwlock pwlock : 8; // page access lock +// char access_pw[64]; // access password +// char access_name[32]; // access name +// bool overclock; } SystemConfigBundle; extern SystemConfigBundle * const sysconf; @@ -41,4 +63,10 @@ void sysconf_apply_settings(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, u8 **field, const char *buff, const void *arg); + #endif //ESP_VT100_FIRMWARE_SYSCFG_H diff --git a/user/wifimgr.c b/user/wifimgr.c index 46d028a..b0376c9 100644 --- a/user/wifimgr.c +++ b/user/wifimgr.c @@ -300,6 +300,12 @@ wifimgr_apply_settings(void) { wifi_info("[WiFi] Initializing..."); +// char buff[64]; +//#define XSTRUCT wificonf +//#define X XDUMP_FIELD +// XTABLE_WIFICONF +//#undef X + // !!! Update to current version !!! // Force wifi cycle diff --git a/user/wifimgr.h b/user/wifimgr.h index 2302571..5e0952f 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -23,37 +23,37 @@ #define wifimgr_notify_ap() { wifi_change_flags.ap = true; } #define wifimgr_notify_sta() { wifi_change_flags.ap = true; } -//....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY +//....Type................Name..Suffix...............Deref..XGET.......Allow..Cast..XSET.........................NOTIFY // 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_WIFI \ - X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, xnoop) \ + X(u8, opmode, /**/, /**/, xget_dec, 1, /**/, xset_wifi_opmode, NULL, xnoop) \ \ - X(u8, tpw, /**/, /**/, xget_dec, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ - X(u8, ap_channel, /**/, /**/, xget_dec, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ - X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ - X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ - X(bool, ap_hidden, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_ap) \ + X(u8, tpw, /**/, /**/, xget_dec, 1, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ + X(u8, ap_channel, /**/, /**/, xget_dec, 1, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ + X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ + X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ + X(bool, ap_hidden, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, wifimgr_notify_ap) \ \ - X(u16, ap_dhcp_time, /**/, /**/, xget_dec, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ - X(u32, unused1, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ - X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(u16, ap_dhcp_time, /**/, /**/, xget_dec, 1, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ + X(u32, unused1, /**/, /**/, xget_dummy, 0, /**/, xset_dummy, NULL, xnoop) \ + X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ - X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ \ - X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ - X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ - X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ - X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_sta) \ + X(u32, unused2, /**/, /**/, xget_dummy, 0, /**/, xset_dummy, NULL, xnoop) \ + X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ + X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ + X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, wifimgr_notify_sta) \ \ - X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ \ \ - X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, xnoop) + X(u8, config_version, /**/, /**/, xget_dec, 0, /**/, xset_u8, NULL, xnoop) // unused1 - replaces 'enabled' bit from old dhcps_lease struct // unused2 - gap after 'ap_gw' which isn't used and doesn't make sense From 1ab4101ac0649f95362c89179627b455928d2f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 20:30:24 +0200 Subject: [PATCH 16/33] use xmacros for terminal config --- front-end | 2 +- user/apars_osc.c | 2 +- user/cgi_sockets.c | 4 +- user/cgi_system.c | 15 +- user/cgi_term_cfg.c | 486 ++++-------------------------------------- user/config_xmacros.c | 26 +++ user/config_xmacros.h | 10 +- user/screen.c | 315 ++++++++++++++++++++++----- user/screen.h | 77 +++++-- user/syscfg.h | 19 +- user/wifimgr.h | 42 ++-- 11 files changed, 427 insertions(+), 571 deletions(-) diff --git a/front-end b/front-end index 32c889b..560f578 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 32c889b714dae859f51e6b46829fd85be59d9ed0 +Subproject commit 560f5783bc0eb5bf845a55cc30a3928f1d01b85f diff --git a/user/apars_osc.c b/user/apars_osc.c index 387b329..0bb7352 100644 --- a/user/apars_osc.c +++ b/user/apars_osc.c @@ -59,7 +59,7 @@ apars_handle_osc(char *buffer) } else if (n >= 91 && n <= 95) { // ESPTerm: action button message - strncpy(termconf_live.btn_msg[n - 91], buffer, TERM_BTN_MSG_LEN); + screen_set_button_message(n - 90, buffer); } else { ansi_noimpl("OSC %d ; %s ST", n, buffer); diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index 565afa5..4263cd1 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -271,8 +271,8 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int case 'b': // action button press btnNum = (u8) (data[1]); - if (btnNum > 0 && btnNum < 10) { - UART_SendAsync(termconf_live.btn_msg[btnNum-1], -1); + if (btnNum > 0 && btnNum <= TERM_BTN_COUNT) { + UART_SendAsync(TERM_BM_N(&termconf_live, btnNum-1), -1); } break; diff --git a/user/cgi_system.c b/user/cgi_system.c index 3f939a7..b780268 100755 --- a/user/cgi_system.c +++ b/user/cgi_system.c @@ -12,9 +12,6 @@ #define SET_REDIR_SUC "/cfg/system" #define SET_REDIR_ERR SET_REDIR_SUC"?err=" -// Select which struct we want to use for X tables -#define XSTRUCT sysconf - static ETSTimer tmr; static void ICACHE_FLASH_ATTR tmrCb(void *arg) @@ -99,7 +96,9 @@ cgiSystemCfgSetParams(HttpdConnData *connData) memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle)); // flags for the template builder - bool admin = false, tpl = false; + 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 { // Check admin PW if (GET_ARG("pw")) { @@ -164,13 +163,16 @@ cgiSystemCfgSetParams(HttpdConnData *connData) } // Settings in the system config block +#define XSTRUCT sysconf #define X XSET_CGI_FUNC XTABLE_SYSCONF #undef X +#undef XSTRUCT } while (0); (void)redir_url; + (void)uart_changed; // unused if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) { // All was OK @@ -210,11 +212,14 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg) strcpy(buff, ""); // fallback - const bool admin = false, tpl=true; + const bool admin = false; + const bool tpl=true; +#define XSTRUCT sysconf #define X XGET_CGI_FUNC XTABLE_SYSCONF #undef X +#undef XSTRUCT if (streq(token, "def_access_name")) { sprintf(buff, "%s", DEF_ACCESS_NAME); diff --git a/user/cgi_term_cfg.c b/user/cgi_term_cfg.c index 13064a8..9972981 100644 --- a/user/cgi_term_cfg.c +++ b/user/cgi_term_cfg.c @@ -15,22 +15,6 @@ Cgi/template routines for configuring non-wifi settings #define SET_REDIR_SUC "/cfg/term" #define SET_REDIR_ERR SET_REDIR_SUC"?err=" -/** convert hex number to int */ -static ICACHE_FLASH_ATTR u32 -decodehex(const char *buf) { - u32 n = 0; - char c; - while ((c = *buf++) != 0) { - if (c >= '0' && c <= '9') c -= '0'; - else if (c >= 'a' && c <= 'f') c -= 'a'-10; - else if (c >= 'A' && c <= 'F') c -= 'A'-10; - else c = 0; - n *= 16; - n += c; - } - return n; -} - /** * Universal CGI endpoint to set Terminal params. */ @@ -39,15 +23,11 @@ cgiTermCfgSetParams(HttpdConnData *connData) { char buff[50]; char redir_url_buf[100]; - int32 n, w, h; ScreenNotifyTopics topics = 0; - bool shall_clear_screen = false; - bool shall_init_uart = false; - char *redir_url = redir_url_buf; redir_url += sprintf(redir_url, SET_REDIR_ERR); - // we'll test if anything was printed by looking for \0 in failed_keys_buf + // we'll test if anything was printed by looking for \0 in redir_url SystemConfigBundle *sysconf_backup = malloc(sizeof(SystemConfigBundle)); TerminalConfigBundle *termconf_backup = malloc(sizeof(TerminalConfigBundle)); @@ -59,328 +39,29 @@ cgiTermCfgSetParams(HttpdConnData *connData) return HTTPD_CGI_DONE; } - // width and height must always go together so we can do max size validation - if (GET_ARG("term_width")) { - do { - cgi_dbg("Default screen width: %s", buff); - w = atoi(buff); - if (w < 1) { - cgi_warn("Bad width: \"%s\"", buff); - redir_url += sprintf(redir_url, "term_width,"); - break; - } - - if (!GET_ARG("term_height")) { - cgi_warn("Missing height arg!"); - // this wont happen normally when the form is used - redir_url += sprintf(redir_url, "term_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")) { - cgi_dbg("Screen default BG: %s", buff); - - if (buff[0] == '#') { - // decode hex - n = decodehex(buff+1); - n += 256; - } else { - n = atoi(buff); - } - - 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, TERM_TITLE_LEN); // ATTN those must match the values in - topics |= TOPIC_CHANGE_TITLE; - } - - if (GET_ARG("backdrop")) { - cgi_dbg("Terminal backdrop url: \"%s\"", buff); - strncpy_safe(termconf->backdrop, buff, TERM_BACKDROP_LEN); // ATTN those must match the values in - topics |= TOPIC_CHANGE_BACKDROP; - } - - 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,"); - } - } +#define XSTRUCT termconf +#define X XSET_CGI_FUNC + XTABLE_TERMCONF +#undef X +#undef XSTRUCT - 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,"); - } - } + // width and height must always go together so we can do max size validation + u32 siz = termconf->width*termconf->height; + if (siz == 0 || siz > MAX_SCREEN_SIZE) { + cgi_warn("Bad dimensions: %d x %d (total %d)", termconf->width, termconf->height, termconf->width*termconf->height); + redir_url += sprintf(redir_url, "term_width,term_height,"); + } + + // Settings from SysConf - UART + bool uart_changed = false; + // restrict what keys are allowed +#define admin 0 +#define tpl 0 +#define XSTRUCT sysconf +#define X XSET_CGI_FUNC + XTABLE_SYSCONF +#undef X +#undef XSTRUCT (void)redir_url; @@ -390,17 +71,23 @@ cgiTermCfgSetParams(HttpdConnData *connData) 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) { terminal_apply_settings(); } else { terminal_apply_settings_noclear(); } - if (shall_init_uart) { - serialInit(); + if (uart_changed) { + 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."); } else { @@ -424,7 +111,6 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg) { #define BUFLEN 100 // large enough for backdrop char buff[BUFLEN]; - char buff2[10]; if (token == NULL) { // We're done @@ -433,109 +119,11 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg) strcpy(buff, ""); // fallback - if (streq(token, "term_width")) { - sprintf(buff, "%d", termconf->width); - } - else if (streq(token, "term_height")) { - sprintf(buff, "%d", termconf->height); - } - 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, "backdrop")) { - strncpy_safe(buff, termconf->backdrop, 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); - if (streq(token, buff2)) { - char c; - char *bp = buff; - char *cp = termconf->btn_msg[btn_i-1]; - int n = 0; - while((c = *cp++) != 0) { - if(n>0) { - *bp = ','; - bp++; - } - bp += sprintf(bp, "%d", (int)c); - n++; - } - break; - } - } - } +#define XSTRUCT termconf +#define X XGET_CGI_FUNC + XTABLE_TERMCONF +#undef X +#undef XSTRUCT tplSend(connData, buff, -1); return HTTPD_CGI_DONE; diff --git a/user/config_xmacros.c b/user/config_xmacros.c index d52f6cf..f47e329 100644 --- a/user/config_xmacros.c +++ b/user/config_xmacros.c @@ -80,6 +80,32 @@ xset_u8(const char *name, u8 *field, const char *buff, const void *arg) } } +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, s8 **field, const char *buff, const void *arg) { diff --git a/user/config_xmacros.h b/user/config_xmacros.h index 53495b0..59cef6f 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -60,6 +60,8 @@ static inline enum xset_result xset_dummy(const char *name, void *field, const c 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); /** * @param arg - max string length @@ -73,17 +75,17 @@ enum xset_result xset_ustring(const char *name, u8 **field, const char *buff, co * 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, allow, cast, xset, xsarg, xnotify) \ +#define XSET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify, allow) \ if ((allow) && GET_ARG(#name)) { \ enum xset_result res = xset(#name, cast &XSTRUCT->name, buff, (const void*) (xsarg)); \ - if (res == XSET_SET) { xnotify(); } \ + 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, allow, cast, xset, xsarg, xnotify) \ +#define XGET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify, allow) \ if ((allow) && streq(token, #name)) xget(buff, deref XSTRUCT->name); -#define XSTRUCT_FIELD(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ +#define XSTRUCT_FIELD(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify, allow) \ type name suffix; #define XDUMP_FIELD(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ diff --git a/user/screen.c b/user/screen.c index a675a71..9160d08 100644 --- a/user/screen.c +++ b/user/screen.c @@ -8,6 +8,7 @@ #include "character_sets.h" #include "utf8.h" #include "cgi_sockets.h" +#include "cgi_logging.h" TerminalConfigBundle * const termconf = &persist.current.termconf; TerminalConfigBundle termconf_live; @@ -106,8 +107,19 @@ bool cursor_saved = false; static struct { bool alternate_active; 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 height; int vm0; @@ -143,46 +155,194 @@ static struct { int x_min, y_min, x_max, y_max; } scr_dirty; -#define reset_screen_dirty() do { \ - scr_dirty.x_min = W; \ - scr_dirty.x_max = -1; \ - scr_dirty.y_min = H; \ - scr_dirty.y_max = -1; \ - } while(0) +static void ICACHE_FLASH_ATTR reset_screen_dirty(void) +{ + scr_dirty.x_min = W; + scr_dirty.x_max = -1; + scr_dirty.y_min = H; + scr_dirty.y_max = -1; +} -#define expand_dirty(y0, y1, x0, x1) do { \ - seri_dbg("Expand: X: (%d..%d) -> %d..%d, Y: (%d..%d) -> %d..%d", scr_dirty.x_min, scr_dirty.x_max, x0, x1, scr_dirty.y_min, scr_dirty.y_max, y0, y1); \ - if ((int)(y0) < scr_dirty.y_min) scr_dirty.y_min = (y0); \ - if ((int)(x0) < scr_dirty.x_min) scr_dirty.x_min = (x0); \ - if ((int)(y1) > scr_dirty.y_max) scr_dirty.y_max = (y1); \ - if ((int)(x1) > scr_dirty.x_max) scr_dirty.x_max = (x1); \ - } while(0) +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 ((y0) < scr_dirty.y_min) scr_dirty.y_min = (y0); + if ((x0) < scr_dirty.x_min) scr_dirty.x_min = (x0); + if ((y1) > scr_dirty.y_max) scr_dirty.y_max = (y1); + if ((x1) > scr_dirty.x_max) scr_dirty.x_max = (x1); +} + +#define NOTIFY_LOCK() { notifyLock++; } -#define NOTIFY_LOCK() do { \ - notifyLock++; \ - } while(0) - -#define NOTIFY_DONE(updateTopics) do { \ - lockTopics |= (updateTopics); \ - if (notifyLock > 0) notifyLock--; \ - if (notifyLock == 0) { \ - screen_notifyChange(lockTopics); \ - lockTopics = 0;\ - } \ - } while(0) +static void ICACHE_FLASH_ATTR NOTIFY_DONE(u32 updateTopics) +{ + lockTopics |= (updateTopics); + if (notifyLock > 0) notifyLock--; + if (notifyLock == 0) { + screen_notifyChange(lockTopics); + lockTopics = 0; + } +} /** Clear the hanging attribute if the cursor is no longer >= W */ -#define clear_invalid_hanging() do { \ - if (cursor.hanging && cursor.x != W-1) { \ - cursor.hanging = false; \ - screen_notifyChange(TOPIC_CHANGE_CURSOR); \ - } \ - } while(false) +static void ICACHE_FLASH_ATTR clear_invalid_hanging(void) +{ + if (cursor.hanging && cursor.x != W-1) { + cursor.hanging = false; + screen_notifyChange(TOPIC_CHANGE_CURSOR); + } +} #define cursor_inside_region() (cursor.y >= TOP && cursor.y <= BTM) //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) +{ + u8 c; + char *bp = buff; + char *cp = value; + int n = 0; + while((c = (u8) *cp++) != 0) { + if(n>0) { + *bp = ','; + bp++; + } + bp += sprintf(bp, "%d", c); + n++; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_term_bm(const char *name, s8 **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", acu); + return XSET_FAIL; + } + + 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!"); + return XSET_FAIL; + } + } + if (lastsp && char_i == 0) { + cgi_warn("Required!"); + return XSET_FAIL; + } + if (!lastsp) { + buff_bm[char_i++] = (char)acu; + } + buff_bm[char_i] = 0; + cgi_dbg("%s, chari = %d", buff_bm, char_i); + + if (!streq(*field, buff_bm)) { + strncpy((char*)*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 */ @@ -194,10 +354,19 @@ terminal_restore_defaults(void) termconf->default_bg = 0; termconf->default_fg = 7; sprintf(termconf->title, SCR_DEF_TITLE); - for(int i=1; i <= TERM_BTN_COUNT; i++) { - sprintf(termconf->btn[i-1], "%d", i); - sprintf(termconf->btn_msg[i-1], "%c", i); - } + + strcpy(termconf->btn1, "1"); + strcpy(termconf->btn2, "2"); + strcpy(termconf->btn3, "3"); + strcpy(termconf->btn4, "4"); + strcpy(termconf->btn5, "5"); + + strcpy(termconf->bm1, "\x01"); + strcpy(termconf->bm2, "\x02"); + strcpy(termconf->bm3, "\x03"); + strcpy(termconf->bm4, "\x04"); + strcpy(termconf->bm5, "\x05"); + termconf->theme = 0; termconf->parser_tout_ms = SCR_DEF_PARSER_TOUT_MS; termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS; @@ -232,6 +401,13 @@ terminal_apply_settings_noclear(void) { bool changed = false; +// char buff[64]; +//#define XSTRUCT termconf +//#define X XDUMP_FIELD +// XTABLE_TERMCONF +//#undef X +// return; + // Migrate if (termconf->config_version < 1) { persist_dbg("termconf: Updating to version %d", 1); @@ -393,10 +569,11 @@ screen_reset_do(bool size, bool labels) strcpy(termconf_live.title, termconf->title); strcpy(termconf_live.backdrop, termconf->backdrop); - for (int i = 1; i <= TERM_BTN_COUNT; i++) { - strcpy(termconf_live.btn[i], termconf->btn[i]); - strcpy(termconf_live.btn_msg[i], termconf->btn_msg[i]); - } + strcpy(termconf_live.btn1, termconf->btn1); + strcpy(termconf_live.btn2, termconf->btn2); + 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_config_links = termconf->show_config_links; @@ -452,8 +629,11 @@ screen_swap_state(bool alternate) ansi_dbg("Swap to alternate"); // store old state 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)); state_backup.vm0 = scr.vm0; state_backup.vm1 = scr.vm1; @@ -465,8 +645,11 @@ screen_swap_state(bool alternate) else { ansi_dbg("Unswap from alternate"); 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)); scr.vm0 = state_backup.vm0; scr.vm1 = state_backup.vm1; @@ -989,8 +1172,8 @@ screen_resize(int rows, int cols) if (W == cols && H == rows) return; // Do nothing NOTIFY_LOCK(); - W = cols; - H = rows; + W = (u32) cols; + H = (u32) rows; screen_reset_on_resize(); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS|TOPIC_CHANGE_CONTENT_ALL|TOPIC_CHANGE_CURSOR); } @@ -1012,7 +1195,28 @@ void ICACHE_FLASH_ATTR screen_set_button_text(int num, const char *text) { NOTIFY_LOCK(); - strncpy(termconf_live.btn[num-1], text, TERM_BTN_LEN); + if (num == 1) strncpy(termconf_live.btn1, text, TERM_BTN_LEN); + else if (num == 2) strncpy(termconf_live.btn2, text, TERM_BTN_LEN); + else if (num == 3) strncpy(termconf_live.btn3, text, TERM_BTN_LEN); + else if (num == 4) strncpy(termconf_live.btn4, text, TERM_BTN_LEN); + else if (num == 5) strncpy(termconf_live.btn5, text, TERM_BTN_LEN); + 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) strncpy(termconf_live.bm1, msg, TERM_BTN_MSG_LEN); + else if (num == 2) strncpy(termconf_live.bm2, msg, TERM_BTN_MSG_LEN); + else if (num == 3) strncpy(termconf_live.bm3, msg, TERM_BTN_MSG_LEN); + else if (num == 4) strncpy(termconf_live.bm4, msg, TERM_BTN_MSG_LEN); + else if (num == 5) strncpy(termconf_live.bm5, msg, TERM_BTN_MSG_LEN); NOTIFY_DONE(TOPIC_CHANGE_BUTTONS); } @@ -1121,7 +1325,7 @@ void ICACHE_FLASH_ATTR screen_cursor_shape(enum CursorShape shape) { 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; NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS); } @@ -1356,7 +1560,7 @@ screen_back_index(int count) cursor.x = new_x; } else { cursor.x = 0; - screen_insert_characters(-new_x); + screen_insert_characters((unsigned int) -new_x); topics |= TOPIC_CHANGE_CONTENT_PART; } NOTIFY_DONE(topics); @@ -1548,7 +1752,7 @@ do_save_private_opt(int n, bool save) { ScreenNotifyTopics topics = TOPIC_INTERNAL; if (!save) NOTIFY_LOCK(); -#define SAVE_RESTORE(sf, of) do { if (save) sf=(of); else of=(sf); } while(0) +#define SAVE_RESTORE(sf, of) do { if (save) (sf)=(of); else (of)=(sf); } while(0) switch (n) { case 1: SAVE_RESTORE(opt_backup.cursors_alt_mode, scr.cursors_alt_mode); @@ -2044,7 +2248,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, BEGIN_TOPIC(TOPIC_CHANGE_TITLE, TERM_TITLE_LEN+4+1) bufput_c(TOPICMARK_TITLE); - int len = (int) strlen(termconf_live.title); + size_t len = strlen(termconf_live.title); if (len > 0) { memcpy(bb, termconf_live.title, len); bb += len; @@ -2059,20 +2263,21 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_utf8(TERM_BTN_COUNT); for (int i = 0; i < TERM_BTN_COUNT; i++) { - int len = (int) strlen(termconf_live.btn[i]); + size_t len = strlen(TERM_BTN_N(&termconf_live, i)); if (len > 0) { - memcpy(bb, termconf_live.btn[i], len); + memcpy(bb, TERM_BTN_N(&termconf_live, i), len); bb += len; remain -= len; } bufput_c('\x01'); } + END_TOPIC BEGIN_TOPIC(TOPIC_CHANGE_BACKDROP, TERM_BACKDROP_LEN+1+1) bufput_c(TOPICMARK_BACKDROP); - int len = (int) strlen(termconf_live.backdrop); + size_t len = strlen(termconf_live.backdrop); if (len > 0) { memcpy(bb, termconf_live.backdrop, len); bb += len; diff --git a/user/screen.h b/user/screen.h index 50c1fb5..d52f89b 100644 --- a/user/screen.h +++ b/user/screen.h @@ -5,6 +5,7 @@ #include #include #include +#include "config_xmacros.h" /** * This module handles the virtual screen and operations on it. @@ -78,30 +79,59 @@ enum CursorShape { #define TERMCONF_SIZE 400 #define TERMCONF_VERSION 4 +//....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, (s8**), xset_string, TERM_TITLE_LEN, /**/, 1) \ + X(char, btn1, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn2, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn3, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn4, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn5, [TERM_BTN_LEN], /**/, xget_string, (s8**), 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, (s8**), xset_term_bm, NULL, /**/, 1) \ + X(char, bm2, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ + X(char, bm3, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ + X(char, bm4, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ + X(char, bm5, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), 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, (s8**), xset_string, TERM_BACKDROP_LEN, /**/, 1) + +#define TERM_BM_N(tc, n) ((tc)->bm1+(TERM_BTN_MSG_LEN*n)) +#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, s8 **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 { - u32 width; - u32 height; - u32 default_bg; // 00-FFh - ANSI colors, (00:00:00-FF:FF:FF)+256 - True Color - u32 default_fg; - char title[TERM_TITLE_LEN]; - char btn[TERM_BTN_COUNT][TERM_BTN_LEN]; - u8 theme; - 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; - char backdrop[TERM_BACKDROP_LEN]; +#define X XSTRUCT_FIELD + XTABLE_TERMCONF +#undef X } TerminalConfigBundle; // Live config @@ -150,6 +180,7 @@ void screen_resize(int rows, int cols); void screen_set_title(const char *title); /** Set a button text */ void screen_set_button_text(int num, const char *text); +void screen_set_button_message(int num, const char *msg); /** Change backdrop */ void screen_set_backdrop(const char *url); diff --git a/user/syscfg.h b/user/syscfg.h index 70ea94b..9d7300a 100644 --- a/user/syscfg.h +++ b/user/syscfg.h @@ -25,21 +25,20 @@ enum pwlock { PWLOCK_MAX = 5, }; - -//....Type................Name..Suffix...............Deref..XGET........Allow..Cast..XSET.........................NOTIFY +//....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_SYSCONF \ - X(u32, uart_baudrate, /**/, /**/, xget_dec, 1, /**/, xset_sys_baudrate, NULL, xnoop) \ - X(u8, uart_parity, /**/, /**/, xget_dec, 1, /**/, xset_sys_parity, NULL, xnoop) \ - X(u8, uart_stopbits, /**/, /**/, xget_dec, 1, /**/, xset_sys_stopbits, NULL, xnoop) \ + 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, 0, /**/, xset_u8, NULL, xnoop) \ + X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) \ \ - X(u8, pwlock, /**/, /**/, xget_dec, admin|tpl, /**/, xset_sys_pwlock, NULL, xnoop) \ - X(u8, access_pw, [64], /**/, xget_ustring, admin, (u8**), xset_sys_accesspw, NULL, xnoop) \ - X(u8, access_name, [32], /**/, xget_ustring, admin|tpl, (u8**), xset_ustring, NULL, xnoop) \ + X(u8, pwlock, /**/, /**/, xget_dec, /**/, xset_sys_pwlock, NULL, /**/, admin|tpl) \ + X(u8, access_pw, [64], /**/, xget_ustring, (u8**), xset_sys_accesspw, NULL, /**/, admin) \ + X(u8, access_name, [32], /**/, xget_ustring, (u8**), xset_ustring, NULL, /**/, admin|tpl) \ \ - X(bool, overclock, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, xnoop) \ + X(bool, overclock, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ typedef struct { diff --git a/user/wifimgr.h b/user/wifimgr.h index 5e0952f..06d145e 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -23,37 +23,37 @@ #define wifimgr_notify_ap() { wifi_change_flags.ap = true; } #define wifimgr_notify_sta() { wifi_change_flags.ap = true; } -//....Type................Name..Suffix...............Deref..XGET.......Allow..Cast..XSET.........................NOTIFY +//....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_WIFI \ - X(u8, opmode, /**/, /**/, xget_dec, 1, /**/, xset_wifi_opmode, NULL, xnoop) \ + X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, /**/, 1) \ \ - X(u8, tpw, /**/, /**/, xget_dec, 1, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ - X(u8, ap_channel, /**/, /**/, xget_dec, 1, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ - X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ - X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ - X(bool, ap_hidden, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, wifimgr_notify_ap) \ + 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(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap(), 1) \ + X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), 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, 1, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ - X(u32, unused1, /**/, /**/, xget_dummy, 0, /**/, xset_dummy, NULL, xnoop) \ - X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + 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, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + 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, 0, /**/, xset_dummy, NULL, xnoop) \ - X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ - X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ - X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, wifimgr_notify_sta) \ + X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, /**/, 0) \ + X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta(), 1) \ + X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), 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, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + 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, 0, /**/, xset_u8, NULL, xnoop) + 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 From 97004659279aaa36ea463cd37ca5f4522705c193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 22:23:09 +0200 Subject: [PATCH 17/33] fixed many bugs --- front-end | 2 +- user/cgi_term_cfg.c | 7 +++++++ user/config_xmacros.c | 6 +++--- user/config_xmacros.h | 14 ++++++++------ user/screen.c | 22 +++++++++++----------- user/screen.h | 26 +++++++++++++------------- user/syscfg.c | 2 +- user/syscfg.h | 6 +++--- user/wifimgr.c | 12 ++++++------ user/wifimgr.h | 12 ++++++------ 10 files changed, 59 insertions(+), 50 deletions(-) diff --git a/front-end b/front-end index 560f578..8327ff0 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 560f5783bc0eb5bf845a55cc30a3928f1d01b85f +Subproject commit 8327ff010893935c0c517c729727464f66badd89 diff --git a/user/cgi_term_cfg.c b/user/cgi_term_cfg.c index 9972981..df5be73 100644 --- a/user/cgi_term_cfg.c +++ b/user/cgi_term_cfg.c @@ -123,6 +123,13 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg) #define X XGET_CGI_FUNC XTABLE_TERMCONF #undef X +#undef XSTRUCT + + // for uart +#define XSTRUCT sysconf +#define X XGET_CGI_FUNC + XTABLE_SYSCONF +#undef X #undef XSTRUCT tplSend(connData, buff, -1); diff --git a/user/config_xmacros.c b/user/config_xmacros.c index f47e329..ccac330 100644 --- a/user/config_xmacros.c +++ b/user/config_xmacros.c @@ -107,7 +107,7 @@ xset_u16(const char *name, u16 *field, const char *buff, const void *arg) } enum xset_result ICACHE_FLASH_ATTR -xset_string(const char *name, s8 **field, const char *buff, const void *arg) +xset_string(const char *name, char *field, const char *buff, const void *arg) { cgi_dbg("Setting %s = %s", name, buff); u32 maxlen = (u32) arg; @@ -126,7 +126,7 @@ xset_string(const char *name, s8 **field, const char *buff, const void *arg) enum xset_result ICACHE_FLASH_ATTR -xset_ustring(const char *name, u8 **field, const char *buff, const void *arg) +xset_ustring(const char *name, uchar *field, const char *buff, const void *arg) { cgi_dbg("Setting %s = %s", name, buff); u32 maxlen = (u32) arg; @@ -136,7 +136,7 @@ xset_ustring(const char *name, u8 **field, const char *buff, const void *arg) return XSET_FAIL; } - if (!streq((char *)field, buff)) { + if (!streq(field, buff)) { strncpy_safe(field, buff, (u32)arg); return XSET_SET; } diff --git a/user/config_xmacros.h b/user/config_xmacros.h index 59cef6f..6fa3859 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -8,6 +8,8 @@ #include #include +typedef unsigned char uchar; + #define XJOIN(a, b) a##b /**Do nothing xnotify */ @@ -63,11 +65,10 @@ enum xset_result xset_u8(const char *name, u8 *field, const char *buff, const vo 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); -/** - * @param arg - max string length - */ -enum xset_result xset_string(const char *name, s8 **field, const char *buff, const void *arg); -enum xset_result xset_ustring(const char *name, u8 **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 @@ -77,7 +78,8 @@ enum xset_result xset_ustring(const char *name, u8 **field, const char *buff, co */ #define XSET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify, allow) \ if ((allow) && GET_ARG(#name)) { \ - enum xset_result res = xset(#name, cast &XSTRUCT->name, buff, (const void*) (xsarg)); \ + type *_p = (type *) &XSTRUCT->name; \ + enum xset_result res = xset(#name, cast _p, buff, (const void*) (xsarg)); \ if (res == XSET_SET) { xnotify; } \ else if (res == XSET_FAIL) { redir_url += sprintf(redir_url, #name","); } \ } diff --git a/user/screen.c b/user/screen.c index 9160d08..7a9f54f 100644 --- a/user/screen.c +++ b/user/screen.c @@ -214,22 +214,22 @@ xget_term_color(char *buff, u32 value) void ICACHE_FLASH_ATTR xget_term_bm(char *buff, char *value) { - u8 c; + char c; char *bp = buff; char *cp = value; int n = 0; - while((c = (u8) *cp++) != 0) { + while((c = *cp++) != 0) { if(n>0) { *bp = ','; bp++; } - bp += sprintf(bp, "%d", c); + bp += sprintf(bp, "%d", (u8)c); n++; } } enum xset_result ICACHE_FLASH_ATTR -xset_term_bm(const char *name, s8 **field, const char *buff, const void *arg) +xset_term_bm(const char *name, char *field, const char *buff, const void *arg) { cgi_dbg("Setting %s = %s", name, buff); @@ -279,8 +279,8 @@ xset_term_bm(const char *name, s8 **field, const char *buff, const void *arg) buff_bm[char_i] = 0; cgi_dbg("%s, chari = %d", buff_bm, char_i); - if (!streq(*field, buff_bm)) { - strncpy((char*)*field, buff_bm, TERM_BTN_MSG_LEN); + if (!streq(field, buff_bm)) { + strncpy(field, buff_bm, TERM_BTN_MSG_LEN); return XSET_SET; } return XSET_UNCHANGED; @@ -361,11 +361,11 @@ terminal_restore_defaults(void) strcpy(termconf->btn4, "4"); strcpy(termconf->btn5, "5"); - strcpy(termconf->bm1, "\x01"); - strcpy(termconf->bm2, "\x02"); - strcpy(termconf->bm3, "\x03"); - strcpy(termconf->bm4, "\x04"); - strcpy(termconf->bm5, "\x05"); + 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->theme = 0; termconf->parser_tout_ms = SCR_DEF_PARSER_TOUT_MS; diff --git a/user/screen.h b/user/screen.h index d52f89b..c9bcc00 100644 --- a/user/screen.h +++ b/user/screen.h @@ -86,12 +86,12 @@ enum CursorShape { 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, (s8**), xset_string, TERM_TITLE_LEN, /**/, 1) \ - X(char, btn1, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn2, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn3, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn4, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn5, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 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) \ @@ -101,18 +101,18 @@ enum CursorShape { 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, (s8**), xset_term_bm, NULL, /**/, 1) \ - X(char, bm2, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ - X(char, bm3, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ - X(char, bm4, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ - X(char, bm5, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, 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, (s8**), xset_string, TERM_BACKDROP_LEN, /**/, 1) + X(char, backdrop, [TERM_BACKDROP_LEN], /**/, xget_string, /**/, xset_string, TERM_BACKDROP_LEN, /**/, 1) #define TERM_BM_N(tc, n) ((tc)->bm1+(TERM_BTN_MSG_LEN*n)) #define TERM_BTN_N(tc, n) ((tc)->btn1+(TERM_BTN_LEN*n)) @@ -122,7 +122,7 @@ 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, s8 **field, const char *buff, const void *arg); +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 */ diff --git a/user/syscfg.c b/user/syscfg.c index 9a2a6f2..fc5f17c 100644 --- a/user/syscfg.c +++ b/user/syscfg.c @@ -95,7 +95,7 @@ xset_sys_pwlock(const char *name, u8 *field, const char *buff, const void *arg) } enum xset_result ICACHE_FLASH_ATTR -xset_sys_accesspw(const char *name, u8 **field, const char *buff, const void *arg) +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; diff --git a/user/syscfg.h b/user/syscfg.h index 9d7300a..0dacd65 100644 --- a/user/syscfg.h +++ b/user/syscfg.h @@ -35,8 +35,8 @@ enum pwlock { X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) \ \ X(u8, pwlock, /**/, /**/, xget_dec, /**/, xset_sys_pwlock, NULL, /**/, admin|tpl) \ - X(u8, access_pw, [64], /**/, xget_ustring, (u8**), xset_sys_accesspw, NULL, /**/, admin) \ - X(u8, access_name, [32], /**/, xget_ustring, (u8**), xset_ustring, 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) \ @@ -66,6 +66,6 @@ enum xset_result xset_sys_baudrate(const char *name, u32 *field, const char *buf 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, 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 diff --git a/user/wifimgr.c b/user/wifimgr.c index b0376c9..efb1cd2 100644 --- a/user/wifimgr.c +++ b/user/wifimgr.c @@ -79,7 +79,7 @@ xset_wifi_ap_channel(const char *name, u8 *field, const char *buff, const void * } enum xset_result ICACHE_FLASH_ATTR -xset_wifi_ssid(const char *name, u8 **field, const char *buff, const void *arg) +xset_wifi_ssid(const char *name, uchar *field, const char *buff, const void *arg) { u8 buff2[SSID_LEN]; @@ -96,8 +96,8 @@ xset_wifi_ssid(const char *name, u8 **field, const char *buff, const void *arg) cgi_dbg("Setting %s = %s", name, buff); if (strlen((char *)buff2) > 0) { - if (!streq(*field, buff2)) { - strncpy_safe(*field, buff2, SSID_LEN); + if (!streq(field, buff2)) { + strncpy_safe(field, buff2, SSID_LEN); return XSET_SET; } return XSET_UNCHANGED; @@ -109,12 +109,12 @@ xset_wifi_ssid(const char *name, u8 **field, const char *buff, const void *arg) /** Set PW - allow len 0 or 8-64 */ enum xset_result ICACHE_FLASH_ATTR -xset_wifi_pwd(const char *name, u8 **field, const char *buff, const void *arg) +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); + if (!streq(field, buff)) { + strncpy_safe(field, buff, PASSWORD_LEN); return XSET_SET; } return XSET_UNCHANGED; diff --git a/user/wifimgr.h b/user/wifimgr.h index 06d145e..85b2b77 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -30,8 +30,8 @@ \ 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(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap(), 1) \ - X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap(), 1) \ + X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, /**/, xset_wifi_ssid, 1, wifimgr_notify_ap(), 1) \ + X(u8, 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) \ @@ -44,8 +44,8 @@ \ \ X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, /**/, 0) \ - X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta(), 1) \ - X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta(), 1) \ + X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, /**/, xset_wifi_ssid, 0, wifimgr_notify_sta(), 1) \ + X(u8, 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) \ @@ -89,8 +89,8 @@ enum xset_result xset_wifi_lease_time(const char *name, u16 *field, const char * 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, u8 **field, const char *buff, const void *arg); -enum xset_result xset_wifi_pwd(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 #define wifi_warn warn From 4e285694d113a30f1ec9e7741f230ce108d8dea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 23:41:07 +0200 Subject: [PATCH 18/33] ini export! --- user/cgi_network.c | 4 +- user/cgi_persist.c | 133 ++++++++++++++++++++++++++++++++++++++++++ user/cgi_persist.h | 1 + user/cgi_wifi.c | 4 +- user/config_xmacros.h | 10 ++-- user/routes.c | 1 + user/screen.h | 62 ++++++++++---------- user/syscfg.h | 16 ++--- user/wifimgr.h | 46 +++++++-------- 9 files changed, 206 insertions(+), 71 deletions(-) diff --git a/user/cgi_network.c b/user/cgi_network.c index e417dd7..a3fe1a8 100644 --- a/user/cgi_network.c +++ b/user/cgi_network.c @@ -51,7 +51,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData) #define XSTRUCT wificonf #define X XSET_CGI_FUNC - XTABLE_WIFI + XTABLE_WIFICONF #undef X (void) redir_url; @@ -101,7 +101,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *toke #define XSTRUCT wificonf #define X XGET_CGI_FUNC - XTABLE_WIFI + XTABLE_WIFICONF #undef X // non-config diff --git a/user/cgi_persist.c b/user/cgi_persist.c index 82a9d04..4f5e56f 100644 --- a/user/cgi_persist.c +++ b/user/cgi_persist.c @@ -7,6 +7,9 @@ Cgi/template routines for configuring non-wifi settings #include "persist.h" #include "helpers.h" #include "cgi_logging.h" +#include "version.h" +#include "screen.h" +#include "config_xmacros.h" #define SET_REDIR_SUC "/cfg/system" @@ -74,3 +77,133 @@ cgiPersistRestoreHard(HttpdConnData *connData) httpdRedirect(connData, SET_REDIR_SUC "?msg=All%20settings%20restored%20to%20factory%20defaults."); return HTTPD_CGI_DONE; } + + + +#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); + } + + bool quoted; +#define X(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \ + if (allow) { \ + xget(buff, deref XSTRUCT->name); \ + \ + 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); \ + } \ + } + +#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; +} diff --git a/user/cgi_persist.h b/user/cgi_persist.h index 4875407..b6f626e 100644 --- a/user/cgi_persist.h +++ b/user/cgi_persist.h @@ -6,5 +6,6 @@ httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData); httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData); httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData); +httpd_cgi_state cgiPersistExport(HttpdConnData *connData); #endif diff --git a/user/cgi_wifi.c b/user/cgi_wifi.c index fe09fda..ca62a78 100644 --- a/user/cgi_wifi.c +++ b/user/cgi_wifi.c @@ -326,7 +326,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData) #define XSTRUCT wificonf #define X XSET_CGI_FUNC - XTABLE_WIFI + XTABLE_WIFICONF #undef X // ---- WiFi opmode ---- @@ -417,7 +417,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, #define XSTRUCT wificonf #define X XGET_CGI_FUNC - XTABLE_WIFI + XTABLE_WIFICONF #undef X // non-config diff --git a/user/config_xmacros.h b/user/config_xmacros.h index 6fa3859..09c639a 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -76,21 +76,21 @@ enum xset_result xset_ustring(const char *name, u8 *field, const char *buff, con * 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, cast, xset, xsarg, xnotify, allow) \ +#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, cast _p, buff, (const void*) (xsarg)); \ + 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, cast, xset, xsarg, xnotify, allow) \ +#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, cast, xset, xsarg, xnotify, allow) \ +#define XSTRUCT_FIELD(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \ type name suffix; -#define XDUMP_FIELD(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ +#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 diff --git a/user/routes.c b/user/routes.c index 895133e..ea9748c 100644 --- a/user/routes.c +++ b/user/routes.c @@ -131,6 +131,7 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = { ROUTE_TPL_FILE("/cfg/system/?", tplSystemCfg, "/cfg_system.tpl"), ROUTE_CGI("/cfg/system/set", cgiSystemCfgSetParams), + ROUTE_CGI("/cfg/system/export", cgiPersistExport), ROUTE_CGI("/cfg/system/write_defaults", cgiPersistWriteDefaults), ROUTE_CGI("/cfg/system/restore_defaults", cgiPersistRestoreDefaults), ROUTE_CGI("/cfg/system/restore_hard", cgiPersistRestoreHard), diff --git a/user/screen.h b/user/screen.h index c9bcc00..a335f19 100644 --- a/user/screen.h +++ b/user/screen.h @@ -82,37 +82,37 @@ enum CursorShape { //....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(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) #define TERM_BM_N(tc, n) ((tc)->bm1+(TERM_BTN_MSG_LEN*n)) #define TERM_BTN_N(tc, n) ((tc)->btn1+(TERM_BTN_LEN*n)) diff --git a/user/syscfg.h b/user/syscfg.h index 0dacd65..e9ce0d0 100644 --- a/user/syscfg.h +++ b/user/syscfg.h @@ -28,17 +28,17 @@ enum pwlock { //....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_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(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, 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(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) \ + X(bool, overclock, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \ typedef struct { diff --git a/user/wifimgr.h b/user/wifimgr.h index 85b2b77..07d3d61 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -23,37 +23,37 @@ #define wifimgr_notify_ap() { wifi_change_flags.ap = true; } #define wifimgr_notify_sta() { wifi_change_flags.ap = true; } -//....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY................Allow +//....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_WIFI \ - X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, /**/, 1) \ +#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(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, /**/, xset_wifi_ssid, 1, wifimgr_notify_ap(), 1) \ - X(u8, 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(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(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(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(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, /**/, xset_wifi_ssid, 0, wifimgr_notify_sta(), 1) \ - X(u8, 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(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(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) + 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 @@ -66,7 +66,7 @@ */ typedef struct { #define X XSTRUCT_FIELD - XTABLE_WIFI + XTABLE_WIFICONF #undef X } WiFiConfigBundle; From ba2b73a287074897481016f2b4cd7ae517c17650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 23 Oct 2017 01:40:56 +0200 Subject: [PATCH 19/33] ini upload and parse (no loading to conf yet) --- CMakeLists.txt | 3 + build_parser.sh | 4 + esphttpdconfig.mk.example | 1 + libesphttpd | 2 +- user/cgi_persist.c | 61 ++++- user/cgi_persist.h | 1 + user/ini_parser.c | 526 ++++++++++++++++++++++++++++++++++++++ user/ini_parser.h | 65 +++++ user/ini_parser.rl | 284 ++++++++++++++++++++ user/routes.c | 1 + user/syscfg.h | 2 +- 11 files changed, 946 insertions(+), 4 deletions(-) create mode 100644 user/ini_parser.c create mode 100644 user/ini_parser.h create mode 100644 user/ini_parser.rl diff --git a/CMakeLists.txt b/CMakeLists.txt index 6345770..82e7a13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,9 @@ set(SOURCE_FILES user/jstring.c user/jstring.h user/character_sets.h + user/ini_parser.h + user/ini_parser.c + user/ini_parser.rl user/utf8.h user/utf8.c user/cgi_logging.h user/config_xmacros.h user/config_xmacros.c) diff --git a/build_parser.sh b/build_parser.sh index ebcbdc2..af37cf3 100755 --- a/build_parser.sh +++ b/build_parser.sh @@ -3,6 +3,10 @@ echo "-- Building parser from Ragel source --" 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_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 diff --git a/esphttpdconfig.mk.example b/esphttpdconfig.mk.example index d95d005..d6f0315 100644 --- a/esphttpdconfig.mk.example +++ b/esphttpdconfig.mk.example @@ -40,6 +40,7 @@ ESP_SPI_FLASH_SIZE_K = 1024 GLOBAL_CFLAGS = \ -DASYNC_LOG=1 \ + -DDEBUG_INI=0 \ -DDEBUG_D2D=0 \ -DDEBUG_ROUTER=0 \ -DDEBUG_CAPTDNS=0 \ diff --git a/libesphttpd b/libesphttpd index e4ecf07..d6651ca 160000 --- a/libesphttpd +++ b/libesphttpd @@ -1 +1 @@ -Subproject commit e4ecf0724e36c828be5222eddce58a6a5cd2386f +Subproject commit d6651ca0d11b7950078247b8305f5b23a6be6c6c diff --git a/user/cgi_persist.c b/user/cgi_persist.c index 4f5e56f..0b6b9dc 100644 --- a/user/cgi_persist.c +++ b/user/cgi_persist.c @@ -10,6 +10,7 @@ Cgi/template routines for configuring non-wifi settings #include "version.h" #include "screen.h" #include "config_xmacros.h" +#include "ini_parser.h" #define SET_REDIR_SUC "/cfg/system" @@ -155,10 +156,17 @@ cgiPersistExport(HttpdConnData *connData) 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) \ - if (allow) { \ + do { if (allow) { \ xget(buff, deref XSTRUCT->name); \ + if (streq(#name, "ap_ssid") && streq(buff, defSSID)) break; \ \ quoted = false; \ quoted |= streq(#type, "char"); \ @@ -173,7 +181,7 @@ cgiPersistExport(HttpdConnData *connData) } else { \ httpdSend(connData, buff, -1); \ } \ - } + } } while(0); #define admin 1 #define tpl 1 @@ -207,3 +215,52 @@ cgiPersistExport(HttpdConnData *connData) connData->cgiData = (void *) (step + 1); return HTTPD_CGI_MORE; } + + +void iniCb(const char *section, const char *key, const char *value, void *userData) +{ + dbg(">>> SET: [%s] %s = %s", section, key, value); +} + + +httpd_cgi_state ICACHE_FLASH_ATTR +postRecvHdl(HttpdConnData *connData, char *data, int len) +{ + ini_parse(data, (size_t) len); + ini_parse_end(); + return HTTPD_CGI_DONE; +} + +/** + * Import settings from INI + * + * @param connData + * @return status + */ +httpd_cgi_state ICACHE_FLASH_ATTR +cgiPersistImport(HttpdConnData *connData) +{ + if (connData->conn == NULL) { + //Connection aborted. Clean up. + return HTTPD_CGI_DONE; + } + + httpdStartResponse(connData, 200); + httpdHeader(connData, "Content-Type", "text/plain"); + httpdEndHeaders(connData); + + char *start = strstr(connData->post->buff, "\r\n\r\n"); + if (start == NULL) { + error("Malformed attachment POST!"); + goto end; + } + + ini_parse_begin(iniCb, NULL); + ini_parse(start, (size_t) connData->post->buffLen - (start - connData->post->buff)); + + connData->recvHdl = postRecvHdl; + +end: + // TODO redirect + return HTTPD_CGI_DONE; +} diff --git a/user/cgi_persist.h b/user/cgi_persist.h index b6f626e..e37c0df 100644 --- a/user/cgi_persist.h +++ b/user/cgi_persist.h @@ -7,5 +7,6 @@ httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData); httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData); httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData); httpd_cgi_state cgiPersistExport(HttpdConnData *connData); +httpd_cgi_state cgiPersistImport(HttpdConnData *connData); #endif diff --git a/user/ini_parser.c b/user/ini_parser.c new file mode 100644 index 0000000..fc99c5c --- /dev/null +++ b/user/ini_parser.c @@ -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" */ + +} diff --git a/user/ini_parser.h b/user/ini_parser.h new file mode 100644 index 0000000..2be0f5b --- /dev/null +++ b/user/ini_parser.h @@ -0,0 +1,65 @@ +#ifndef INIPARSE_STREAM_H +#define INIPARSE_STREAM_H + +#include + +#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 diff --git a/user/ini_parser.rl b/user/ini_parser.rl new file mode 100644 index 0000000..e85e035 --- /dev/null +++ b/user/ini_parser.rl @@ -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; +#*/ + }%% +} diff --git a/user/routes.c b/user/routes.c index ea9748c..add3c97 100644 --- a/user/routes.c +++ b/user/routes.c @@ -132,6 +132,7 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = { ROUTE_TPL_FILE("/cfg/system/?", tplSystemCfg, "/cfg_system.tpl"), 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/restore_defaults", cgiPersistRestoreDefaults), ROUTE_CGI("/cfg/system/restore_hard", cgiPersistRestoreHard), diff --git a/user/syscfg.h b/user/syscfg.h index e9ce0d0..2bb0684 100644 --- a/user/syscfg.h +++ b/user/syscfg.h @@ -25,7 +25,7 @@ enum pwlock { PWLOCK_MAX = 5, }; -//....Type................Name..Suffix...............Deref..XGET..........Cast..XSET.........................NOTIFY....Allow +//....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) \ From 2f4cd08c6030f61784af93fcca4dccc997da3fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 23 Oct 2017 22:43:04 +0200 Subject: [PATCH 20/33] import now fully working --- esphttpdconfig.mk.example | 2 +- front-end | 2 +- user/cgi_persist.c | 198 +++++++++++++++++++++++++++++++++++--- user/config_xmacros.h | 10 ++ user/screen.c | 6 +- user/serial.c | 2 +- user/wifimgr.c | 15 +++ user/wifimgr.h | 1 + 8 files changed, 216 insertions(+), 20 deletions(-) diff --git a/esphttpdconfig.mk.example b/esphttpdconfig.mk.example index d6f0315..97c8999 100644 --- a/esphttpdconfig.mk.example +++ b/esphttpdconfig.mk.example @@ -40,7 +40,7 @@ ESP_SPI_FLASH_SIZE_K = 1024 GLOBAL_CFLAGS = \ -DASYNC_LOG=1 \ - -DDEBUG_INI=0 \ + -DDEBUG_INI=1 \ -DDEBUG_D2D=0 \ -DDEBUG_ROUTER=0 \ -DDEBUG_CAPTDNS=0 \ diff --git a/front-end b/front-end index 8327ff0..a96b522 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 8327ff010893935c0c517c729727464f66badd89 +Subproject commit a96b522ca899c687d70b9ad92f708cd416b8d460 diff --git a/user/cgi_persist.c b/user/cgi_persist.c index 0b6b9dc..46393ad 100644 --- a/user/cgi_persist.c +++ b/user/cgi_persist.c @@ -13,6 +13,7 @@ Cgi/template routines for configuring non-wifi settings #include "ini_parser.h" #define SET_REDIR_SUC "/cfg/system" +#define SET_REDIR_ERR SET_REDIR_SUC"?err=" static bool ICACHE_FLASH_ATTR verify_admin_pw(const char *pw) @@ -79,7 +80,7 @@ cgiPersistRestoreHard(HttpdConnData *connData) return HTTPD_CGI_DONE; } - +// -------------- Export -------------- #define httpdSend_orDie(conn, data, len) do { if (!httpdSend((conn), (data), (len))) return false; } while (0) @@ -217,17 +218,154 @@ cgiPersistExport(HttpdConnData *connData) } -void iniCb(const char *section, const char *key, const char *value, void *userData) +// -------------- IMPORT -------------- + +struct IniUpload { + TerminalConfigBundle *term_backup; + WiFiConfigBundle *wifi_backup; + SystemConfigBundle *sys_backup; + bool term_ok; + bool wifi_ok; + bool sys_ok; +}; + +static void ICACHE_FLASH_ATTR iniCb(const char *section, const char *key, const char *value, void *userData) { - dbg(">>> SET: [%s] %s = %s", section, key, value); + 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); + +#define X XSET_ASSIGN + + bool suc = true; + bool found = false; + + // notify flag, unused here + bool uart_changed = false; + + if (streq(section, "terminal")) { +#define XSTRUCT termconf + XTABLE_TERMCONF +#undef XSTRUCT + if (!suc) state->term_ok = false; + } + else if (streq(section, "wifi")) { +#define XSTRUCT wificonf + XTABLE_WIFICONF +#undef XSTRUCT + if (!suc) state->wifi_ok = false; + } + else if (streq(section, "system")) { +#define XSTRUCT sysconf + XTABLE_SYSCONF +#undef XSTRUCT + if (!suc) state->sys_ok = false; + } + + 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; + } +} -httpd_cgi_state ICACHE_FLASH_ATTR +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; + + // 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 + } + + cgi_dbg("INI parse - postRecvHdl"); + 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; + + 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 { + cgi_dbg("Applying terminal settings"); + terminal_apply_settings(); + cgi_dbg("Applying system settings"); + sysconf_apply_settings(); + cgi_dbg("Applying WiFi settings (scheduling...)"); + wifimgr_apply_settings_later(1000); + + cgi_dbg("Persisting results"); + persist_store(); + } + + cgi_dbg("Redirect"); + char buff[100]; + char *b = buff; + if (suc) { + httpdRedirect(connData, SET_REDIR_SUC"?msg=Settings%20loaded%20and%20applied."); + } 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; } @@ -240,27 +378,59 @@ postRecvHdl(HttpdConnData *connData, char *data, int len) 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; } - httpdStartResponse(connData, 200); - httpdHeader(connData, "Content-Type", "text/plain"); - httpdEndHeaders(connData); - char *start = strstr(connData->post->buff, "\r\n\r\n"); if (start == NULL) { error("Malformed attachment POST!"); - goto end; + + httpdStartResponse(connData, 400); + httpdHeader(connData, "Content-Type", "text/plain"); + httpdEndHeaders(connData); + httpdSend(connData, "Bad format.", -1); + return HTTPD_CGI_DONE; } - ini_parse_begin(iniCb, NULL); - ini_parse(start, (size_t) connData->post->buffLen - (start - connData->post->buff)); + 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; + + 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; -end: - // TODO redirect - return HTTPD_CGI_DONE; + // continues in recvHdl + return HTTPD_CGI_MORE; } diff --git a/user/config_xmacros.h b/user/config_xmacros.h index 09c639a..3c26f06 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -84,6 +84,16 @@ enum xset_result xset_ustring(const char *name, u8 *field, const char *buff, con else if (res == XSET_FAIL) { redir_url += sprintf(redir_url, #name","); } \ } +/** used for INI */ +#define XSET_ASSIGN(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) { xnotify; } \ + else if (res == XSET_FAIL) { suc = false; } \ + } + #define XGET_CGI_FUNC(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \ if ((allow) && streq(token, #name)) xget(buff, deref XSTRUCT->name); diff --git a/user/screen.c b/user/screen.c index 7a9f54f..1853c41 100644 --- a/user/screen.c +++ b/user/screen.c @@ -250,11 +250,11 @@ xset_term_bm(const char *name, char *field, const char *buff, const void *arg) } if (char_i >= TERM_BTN_MSG_LEN-1) { - cgi_warn("Too long! %d", acu); + cgi_warn("Too long! %d", char_i); return XSET_FAIL; } - cgi_dbg("acu %d", acu); +// cgi_dbg("acu %d", acu); buff_bm[char_i++] = (char)acu; // prepare for next char @@ -277,7 +277,7 @@ xset_term_bm(const char *name, char *field, const char *buff, const void *arg) buff_bm[char_i++] = (char)acu; } buff_bm[char_i] = 0; - cgi_dbg("%s, chari = %d", buff_bm, char_i); +// cgi_dbg("%s, chari = %d", buff_bm, char_i); if (!streq(field, buff_bm)) { strncpy(field, buff_bm, TERM_BTN_MSG_LEN); diff --git a/user/serial.c b/user/serial.c index 9c9edce..d7382a2 100644 --- a/user/serial.c +++ b/user/serial.c @@ -42,7 +42,7 @@ buf_pop(void *unused) LOCAL void my_putc(char c) { - UART_WriteCharCRLF(UART1, (u8) c, 10); + UART_WriteCharCRLF(UART1, (u8) c, 200); } /** diff --git a/user/wifimgr.c b/user/wifimgr.c index efb1cd2..7083b3f 100644 --- a/user/wifimgr.c +++ b/user/wifimgr.c @@ -292,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 */ diff --git a/user/wifimgr.h b/user/wifimgr.h index 07d3d61..5637b51 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -82,6 +82,7 @@ extern WiFiConfigBundle * const wificonf; void wifimgr_restore_defaults(void); void wifimgr_apply_settings(void); +void wifimgr_apply_settings_later(uint32_t delay_ms); int getStaIpAsString(char *buffer); From 0e6c297fe455b815a2a67d13b2297964fdc261ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 23 Oct 2017 23:24:20 +0200 Subject: [PATCH 21/33] be smarter about detecting changes in loaded ini --- front-end | 2 +- user/cgi_persist.c | 119 ++++++++++++++++++++++++++++++++---------- user/config_xmacros.h | 13 +---- 3 files changed, 94 insertions(+), 40 deletions(-) diff --git a/front-end b/front-end index a96b522..c8305de 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit a96b522ca899c687d70b9ad92f708cd416b8d460 +Subproject commit c8305dea1a9ab38a13de442152c1fb9819bf68b1 diff --git a/user/cgi_persist.c b/user/cgi_persist.c index 46393ad..01581c0 100644 --- a/user/cgi_persist.c +++ b/user/cgi_persist.c @@ -227,8 +227,12 @@ struct IniUpload { 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; @@ -241,31 +245,59 @@ static void ICACHE_FLASH_ATTR iniCb(const char *section, const char *key, const // cgi_dbg("%s.%s = %s", section, key, value); -#define X XSET_ASSIGN - - bool suc = true; - bool found = false; +/** 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 + XTABLE_TERMCONF #undef XSTRUCT - if (!suc) state->term_ok = false; + } 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 + XTABLE_WIFICONF #undef XSTRUCT - if (!suc) state->wifi_ok = false; + } 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 + XTABLE_SYSCONF #undef XSTRUCT - if (!suc) state->sys_ok = false; + } while (0); + + if (found) { + if (!ok) state->sys_ok = false; + state->sys_any |= changed; + } } if (!found) cgi_warn("Unknown key %s.%s!", section, key); @@ -294,16 +326,18 @@ postRecvHdl(HttpdConnData *connData, char *data, int len) struct IniUpload *state = connData->cgiData; if (!state) return HTTPD_CGI_DONE; - // 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 - } - cgi_dbg("INI parse - postRecvHdl"); - ini_parse(data, (size_t) len); + 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(); @@ -316,6 +350,7 @@ postRecvHdl(HttpdConnData *connData, char *data, int len) 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..."); @@ -330,22 +365,38 @@ postRecvHdl(HttpdConnData *connData, char *data, int len) memcpy(sysconf, state->sys_backup, sizeof(SystemConfigBundle)); } else { - cgi_dbg("Applying terminal settings"); - terminal_apply_settings(); - cgi_dbg("Applying system settings"); - sysconf_apply_settings(); - cgi_dbg("Applying WiFi settings (scheduling...)"); - wifimgr_apply_settings_later(1000); - - cgi_dbg("Persisting results"); - persist_store(); + 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) { - httpdRedirect(connData, SET_REDIR_SUC"?msg=Settings%20loaded%20and%20applied."); + 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; @@ -423,6 +474,10 @@ cgiPersistImport(HttpdConnData *connData) 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); @@ -431,6 +486,14 @@ cgiPersistImport(HttpdConnData *connData) 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; } diff --git a/user/config_xmacros.h b/user/config_xmacros.h index 3c26f06..ce099ab 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -50,7 +50,8 @@ void xget_dhcp(char *buff, const struct dhcps_lease *value); enum xset_result { XSET_FAIL = 0, XSET_SET = 1, - XSET_UNCHANGED = 2 + XSET_UNCHANGED = 2, + XSET_NONE = 3, }; // Dummy for unimplemented setters @@ -84,16 +85,6 @@ enum xset_result xset_ustring(const char *name, u8 *field, const char *buff, con else if (res == XSET_FAIL) { redir_url += sprintf(redir_url, #name","); } \ } -/** used for INI */ -#define XSET_ASSIGN(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) { xnotify; } \ - else if (res == XSET_FAIL) { suc = false; } \ - } - #define XGET_CGI_FUNC(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \ if ((allow) && streq(token, #name)) xget(buff, deref XSTRUCT->name); From e98588b3171f9d0ed5e04ff6bb0433729eb6886f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 23 Oct 2017 23:49:19 +0200 Subject: [PATCH 22/33] Fix broken validation for bm* --- front-end | 2 +- user/screen.c | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/front-end b/front-end index c8305de..cf29dea 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit c8305dea1a9ab38a13de442152c1fb9819bf68b1 +Subproject commit cf29deaa0cf6fa4dc600c0684b009f4ec02ce5e7 diff --git a/user/screen.c b/user/screen.c index 1853c41..3e4a3ef 100644 --- a/user/screen.c +++ b/user/screen.c @@ -217,6 +217,7 @@ 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) { @@ -254,7 +255,7 @@ xset_term_bm(const char *name, char *field, const char *buff, const void *arg) return XSET_FAIL; } -// cgi_dbg("acu %d", acu); + dbg("+ %c", acu); buff_bm[char_i++] = (char)acu; // prepare for next char @@ -273,11 +274,16 @@ xset_term_bm(const char *name, char *field, const char *buff, const void *arg) cgi_warn("Required!"); return XSET_FAIL; } - if (!lastsp) { + if (!lastsp&&acu>0) { + dbg("+ %c", acu); buff_bm[char_i++] = (char)acu; } buff_bm[char_i] = 0; -// cgi_dbg("%s, chari = %d", buff_bm, char_i); + + if (char_i == 0) { + cgi_warn("Required!"); + return XSET_FAIL; + } if (!streq(field, buff_bm)) { strncpy(field, buff_bm, TERM_BTN_MSG_LEN); From 3c12efe28f8dcbed3c6308937045898eab07dd13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 25 Oct 2017 23:24:07 +0200 Subject: [PATCH 23/33] updated submodule ref --- front-end | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front-end b/front-end index cf29dea..0794030 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit cf29deaa0cf6fa4dc600c0684b009f4ec02ce5e7 +Subproject commit 0794030210c5f268f879c1b82448f3b9525dc5f9 From dca44963e8a24ca30603b5d18874192f13796abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 26 Oct 2017 00:31:10 +0200 Subject: [PATCH 24/33] add new OSC and configurable button count --- front-end | 2 +- user/apars_osc.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++-- user/screen.c | 37 +++++++++++++++++++++++------ user/screen.h | 9 ++++--- 4 files changed, 97 insertions(+), 13 deletions(-) diff --git a/front-end b/front-end index 0794030..161707f 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 0794030210c5f268f879c1b82448f3b9525dc5f9 +Subproject commit 161707fa605f7043a2d28843fa9ea08fb05799e4 diff --git a/user/apars_osc.c b/user/apars_osc.c index 0bb7352..9be0a6a 100644 --- a/user/apars_osc.c +++ b/user/apars_osc.c @@ -16,6 +16,41 @@ #include "ansi_parser.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 goto bad; + return; + +bad: + ansi_warn("No ESPTerm option at %d.%d", n0, n1); +} + /** * Helper function to parse incoming OSC (Operating System Control) * @param buffer - the OSC body (after OSC and before ST) @@ -23,8 +58,9 @@ void ICACHE_FLASH_ATTR apars_handle_osc(char *buffer) { + const char *origbuf = buffer; int n = 0; - char c = 0; + char c; while ((c = *buffer++) != 0) { if (c >= '0' && c <= '9') { n = (n * 10 + (c - '0')); @@ -49,16 +85,38 @@ apars_handle_osc(char *buffer) buffer[0] = 'G'; 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) { // ESPTerm: action button label + ansi_warn("OSC 8x is deprecated, use 28;x"); screen_set_button_text(n - 80, buffer); } else if (n >= 91 && n <= 95) { // ESPTerm: action button message + ansi_warn("OSC 9x is deprecated, use 29;x"); screen_set_button_message(n - 90, buffer); } else { @@ -66,7 +124,7 @@ apars_handle_osc(char *buffer) } } else { - ansi_warn("BAD OSC: %s", buffer); + ansi_warn("BAD OSC: %s", origbuf); apars_show_context(); } } diff --git a/user/screen.c b/user/screen.c index 3e4a3ef..e25584c 100644 --- a/user/screen.c +++ b/user/screen.c @@ -416,25 +416,30 @@ terminal_apply_settings_noclear(void) // Migrate 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; changed = 1; } 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; changed = 1; } 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; changed = 1; } if (termconf->config_version < 4) { - persist_dbg("termconf: Updating to version %d", 1); + 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; + } termconf->config_version = TERMCONF_VERSION; @@ -1206,6 +1211,7 @@ screen_set_button_text(int num, const char *text) else if (num == 3) strncpy(termconf_live.btn3, text, TERM_BTN_LEN); else if (num == 4) strncpy(termconf_live.btn4, text, TERM_BTN_LEN); else if (num == 5) strncpy(termconf_live.btn5, text, TERM_BTN_LEN); + else ansi_warn("Bad button num: %d", num); NOTIFY_DONE(TOPIC_CHANGE_BUTTONS); } @@ -1223,6 +1229,23 @@ screen_set_button_message(int num, const char *msg) else if (num == 3) strncpy(termconf_live.bm3, msg, TERM_BTN_MSG_LEN); else if (num == 4) strncpy(termconf_live.bm4, msg, TERM_BTN_MSG_LEN); else if (num == 5) strncpy(termconf_live.bm5, msg, TERM_BTN_MSG_LEN); + 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); } @@ -2263,12 +2286,12 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_c('\x01'); 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_utf8(TERM_BTN_COUNT); + bufput_utf8(termconf_live.button_count); - for (int i = 0; i < TERM_BTN_COUNT; i++) { + for (int i = 0; i < termconf_live.button_count; i++) { size_t len = strlen(TERM_BTN_N(&termconf_live, i)); if (len > 0) { memcpy(bb, TERM_BTN_N(&termconf_live, i), len); diff --git a/user/screen.h b/user/screen.h index a335f19..9b5d53e 100644 --- a/user/screen.h +++ b/user/screen.h @@ -70,6 +70,7 @@ enum CursorShape { #define SCR_DEF_DEBUGBAR 0 #define SCR_DEF_DECOPT12 0 #define SCR_DEF_ASCIIDEBUG 0 +#define SCR_DEF_BUTTON_COUNT 5 // --- Persistent Settings --- #define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL) @@ -77,9 +78,9 @@ enum CursorShape { // Size designed for the terminal config structure // Must be constant to avoid corrupting user config after upgrade #define TERMCONF_SIZE 400 -#define TERMCONF_VERSION 4 +#define TERMCONF_VERSION 5 -//....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY................Allow +//....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) \ @@ -112,7 +113,8 @@ enum CursorShape { 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(char, backdrop, [TERM_BACKDROP_LEN], /**/, xget_string, xset_string, TERM_BACKDROP_LEN, /**/, 1) \ + X(u8, button_count, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) #define TERM_BM_N(tc, n) ((tc)->bm1+(TERM_BTN_MSG_LEN*n)) #define TERM_BTN_N(tc, n) ((tc)->btn1+(TERM_BTN_LEN*n)) @@ -181,6 +183,7 @@ void screen_set_title(const char *title); /** Set a button 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_count(int count); /** Change backdrop */ void screen_set_backdrop(const char *url); From ead9120ae8cd2a50367564edf262275c3f294b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 26 Oct 2017 00:41:24 +0200 Subject: [PATCH 25/33] ref update --- front-end | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front-end b/front-end index 161707f..9ae5e92 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 161707fa605f7043a2d28843fa9ea08fb05799e4 +Subproject commit 9ae5e928aceb507eeba537176b6649f4bdd4985c From 3af928b39d846a2b10d457ab2c71dda76c46bec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 26 Oct 2017 00:45:43 +0200 Subject: [PATCH 26/33] version bump for pre-release build --- user/version.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user/version.h b/user/version.h index 2742035..c581f65 100644 --- a/user/version.h +++ b/user/version.h @@ -9,10 +9,10 @@ #define STR(x) STR_HELPER(x) #define FW_V_MAJOR 2 -#define FW_V_MINOR 2 +#define FW_V_MINOR 3 #define FW_V_PATCH 0 -#define FW_V_SUFFIX "" -#define FW_CODENAME "Bluet" // 2.2 +#define FW_V_SUFFIX "-pre1" +#define FW_CODENAME "Cricket" // 2.3 #define FW_CODENAME_QUOTED "\""FW_CODENAME"\"" #define FW_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) FW_V_SUFFIX From bc240e676b351eeffc3afb3004ce0e751ce72a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 26 Oct 2017 00:51:51 +0200 Subject: [PATCH 27/33] added hu to build script --- ship.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ship.sh b/ship.sh index ae6e87f..0fa4a34 100755 --- a/ship.sh +++ b/ship.sh @@ -64,6 +64,7 @@ if [ -z "$ESP_LANG" ]; then buildlang cs buildlang en buildlang de + buildlang hu else buildlang ${ESP_LANG} fi From 9008f2e1af6cc46fe6f2d1de486ffc6daf6b2e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 26 Oct 2017 00:57:40 +0200 Subject: [PATCH 28/33] ref upd --- front-end | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front-end b/front-end index 9ae5e92..445d78d 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 9ae5e928aceb507eeba537176b6649f4bdd4985c +Subproject commit 445d78d4e18a976902753375351c2a39337dcac2 From d9aa0418faaedccc6fab3cba7fa12a0345528e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 30 Oct 2017 20:47:41 +0100 Subject: [PATCH 29/33] updated protocol, added button themeing and font conf --- user/screen.c | 50 ++++++++++++++++++++++++++++++++++++++++++++------ user/screen.h | 16 +++++++++++++--- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/user/screen.c b/user/screen.c index e25584c..a940305 100644 --- a/user/screen.c +++ b/user/screen.c @@ -373,6 +373,8 @@ terminal_restore_defaults(void) 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->parser_tout_ms = SCR_DEF_PARSER_TOUT_MS; termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS; @@ -389,6 +391,8 @@ terminal_restore_defaults(void) termconf->allow_decopt_12 = SCR_DEF_DECOPT12; termconf->ascii_debug = SCR_DEF_ASCIIDEBUG; termconf->backdrop[0] = 0; + termconf->font_stack[0] = 0; + termconf->font_size = 20; } /** @@ -440,6 +444,14 @@ terminal_apply_settings_noclear(void) 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; @@ -2143,6 +2155,15 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_utf8((num)); \ } 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 #define SEQ_TAG_SKIP '\x01' #define SEQ_TAG_REPEAT '\x02' @@ -2153,6 +2174,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, #define SEQ_TAG_ATTRS_0 '\x07' #define TOPICMARK_SCREEN_OPTS 'O' +#define TOPICMARK_STATIC_OPTS 'P' #define TOPICMARK_TITLE 'T' #define TOPICMARK_BUTTONS 'B' #define TOPICMARK_DEBUG 'D' @@ -2250,12 +2272,8 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_utf8(H); bufput_utf8(W); bufput_utf8(termconf_live.theme); - - bufput_utf8(termconf_live.default_fg & 0xFFFF); - bufput_utf8((termconf_live.default_fg >> 16) & 0xFFFF); - - bufput_utf8(termconf_live.default_bg & 0xFFFF); - bufput_utf8((termconf_live.default_bg >> 16) & 0xFFFF); + bufput_color_utf8(termconf_live.default_fg); + bufput_color_utf8(termconf_live.default_bg); bufput_utf8( (scr.cursor_visible << 0) | @@ -2274,6 +2292,20 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, ); 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_CHANGE_TITLE, TERM_TITLE_LEN+4+1) bufput_c(TOPICMARK_TITLE); @@ -2291,7 +2323,11 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_utf8(termconf_live.button_count); + u32 *cp = &termconf_live.bc1; for (int i = 0; i < termconf_live.button_count; i++) { + uint32_t c = *cp++; + bufput_color_utf8(c); + size_t len = strlen(TERM_BTN_N(&termconf_live, i)); if (len > 0) { memcpy(bb, TERM_BTN_N(&termconf_live, i), len); @@ -2333,6 +2369,8 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_utf8(cursor.charsetN); bufput_c(cursor.charset0); bufput_c(cursor.charset1); + bufput_color_utf8(cursor.fg); + bufput_color_utf8(cursor.bg); bufput_utf8(system_get_free_heap_size()); bufput_utf8(term_active_clients); END_TOPIC diff --git a/user/screen.h b/user/screen.h index 9b5d53e..f7c2b6f 100644 --- a/user/screen.h +++ b/user/screen.h @@ -40,6 +40,7 @@ #define TERM_TITLE_LEN 64 #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_COOLDOWN_MS 35 @@ -77,8 +78,8 @@ enum CursorShape { // Size designed for the terminal config structure // Must be constant to avoid corrupting user config after upgrade -#define TERMCONF_SIZE 400 -#define TERMCONF_VERSION 5 +#define TERMCONF_SIZE 500 +#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) @@ -114,7 +115,14 @@ enum CursorShape { 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(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) #define TERM_BM_N(tc, n) ((tc)->bm1+(TERM_BTN_MSG_LEN*n)) #define TERM_BTN_N(tc, n) ((tc)->btn1+(TERM_BTN_LEN*n)) @@ -208,11 +216,13 @@ enum ScreenSerializeTopic { TOPIC_INTERNAL = (1<<6), // debugging internal state TOPIC_BELL = (1<<7), // beep TOPIC_CHANGE_BACKDROP = (1<<8), + TOPIC_CHANGE_STATIC_OPTS = (1<<9), TOPIC_FLAG_NOCLEAN = (1<<15), // do not clean dirty extents // combos TOPIC_INITIAL = TOPIC_CHANGE_SCREEN_OPTS | + TOPIC_CHANGE_STATIC_OPTS | TOPIC_CHANGE_CONTENT_ALL | TOPIC_CHANGE_CURSOR | TOPIC_CHANGE_TITLE | From 5dbb5929467766a452c4a713f6c494f8da181379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 30 Oct 2017 21:25:56 +0100 Subject: [PATCH 30/33] added OSC for color buttons --- front-end | 2 +- user/apars_osc.c | 4 ++++ user/screen.c | 41 +++++++++++++++++++++++++++++++---------- user/screen.h | 1 + 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/front-end b/front-end index 445d78d..30af1ad 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 445d78d4e18a976902753375351c2a39337dcac2 +Subproject commit 30af1ad2f67cf100541e06ccd3760e9562f2f672 diff --git a/user/apars_osc.c b/user/apars_osc.c index 9be0a6a..40c062b 100644 --- a/user/apars_osc.c +++ b/user/apars_osc.c @@ -44,6 +44,10 @@ handle_espterm_osc(int n0, int n1, char *buffer) // Button message screen_set_button_message(n1, buffer); } + else if (n0 == 30) { + // Button color + screen_set_button_color(n1, buffer); + } else goto bad; return; diff --git a/user/screen.c b/user/screen.c index a940305..366771b 100644 --- a/user/screen.c +++ b/user/screen.c @@ -1218,12 +1218,13 @@ void ICACHE_FLASH_ATTR screen_set_button_text(int num, const char *text) { NOTIFY_LOCK(); - if (num == 1) strncpy(termconf_live.btn1, text, TERM_BTN_LEN); - else if (num == 2) strncpy(termconf_live.btn2, text, TERM_BTN_LEN); - else if (num == 3) strncpy(termconf_live.btn3, text, TERM_BTN_LEN); - else if (num == 4) strncpy(termconf_live.btn4, text, TERM_BTN_LEN); - else if (num == 5) strncpy(termconf_live.btn5, 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); } @@ -1236,12 +1237,32 @@ void ICACHE_FLASH_ATTR screen_set_button_message(int num, const char *msg) { NOTIFY_LOCK(); - if (num == 1) strncpy(termconf_live.bm1, msg, TERM_BTN_MSG_LEN); - else if (num == 2) strncpy(termconf_live.bm2, msg, TERM_BTN_MSG_LEN); - else if (num == 3) strncpy(termconf_live.bm3, msg, TERM_BTN_MSG_LEN); - else if (num == 4) strncpy(termconf_live.bm4, msg, TERM_BTN_MSG_LEN); - else if (num == 5) strncpy(termconf_live.bm5, msg, TERM_BTN_MSG_LEN); + + 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); } diff --git a/user/screen.h b/user/screen.h index f7c2b6f..ca37c8e 100644 --- a/user/screen.h +++ b/user/screen.h @@ -191,6 +191,7 @@ void screen_set_title(const char *title); /** Set a button 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); From 78aaa949a34033ae9205caee50186a29e8d4b218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 12 Nov 2017 11:40:36 +0100 Subject: [PATCH 31/33] support double lines --- .gitignore | 3 +- front-end | 2 +- user/apars_short.c | 24 +++++++--- user/screen.c | 112 ++++++++++++++++++++++++++++++++++++++++++--- user/screen.h | 7 ++- 5 files changed, 132 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 3334351..41ecbc9 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ cmake-build-debug/ .sass-cache *.map -.gitignore +!.gitignore + diff --git a/front-end b/front-end index 30af1ad..0893b0a 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 30af1ad2f67cf100541e06ccd3760e9562f2f672 +Subproject commit 0893b0a26886f64c0fa456a4cc69fef0992d7ad9 diff --git a/user/apars_short.c b/user/apars_short.c index 7fb35c5..921ba2d 100644 --- a/user/apars_short.c +++ b/user/apars_short.c @@ -94,21 +94,33 @@ apars_handle_space_cmd(char c) void ICACHE_FLASH_ATTR apars_handle_hash_cmd(char 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 '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 + screen_set_line_attr(1, 1, 1); + break; case '6': // Double width - ansi_noimpl("Double Size Line"); + screen_set_line_attr(2, 1, 1); break; case '8': screen_fill_with_E(); break; - // development codes - do not use! - case '7': - http_get("http://wtfismyip.com/text", NULL, http_callback_example); - break; +// // development codes - do not use! +// case '7': +// http_get("http://wtfismyip.com/text", NULL, http_callback_example); +// break; default: ansi_noimpl("ESC # %c", c); diff --git a/user/screen.c b/user/screen.c index 366771b..3e1a847 100644 --- a/user/screen.c +++ b/user/screen.c @@ -39,6 +39,7 @@ static Cell screen[MAX_SCREEN_SIZE]; #define TABSTOP_WORDS 5 +#define LINE_ATTRS_COUNT 64 /** * Screen state structure */ @@ -57,9 +58,12 @@ static struct { int vm1; u32 tab_stops[TABSTOP_WORDS]; // tab stops bitmap + u8 line_attribs[LINE_ATTRS_COUNT]; // assume that's quite enough... char last_char[4]; } scr; +#define IS_DOUBLE_WIDTH() (scr.line_attribs[cursor.y]&0b001) + #define TOP scr.vm0 #define BTM scr.vm1 #define RH (scr.vm1 - scr.vm0 + 1) @@ -551,7 +555,12 @@ screen_reset_sgr(void) static void ICACHE_FLASH_ATTR screen_reset_do(bool size, bool labels) { - ScreenNotifyTopics topics = TOPIC_CHANGE_SCREEN_OPTS | TOPIC_CHANGE_CURSOR | TOPIC_CHANGE_CONTENT_ALL; + ScreenNotifyTopics topics = + TOPIC_CHANGE_SCREEN_OPTS + | TOPIC_CHANGE_CURSOR + | TOPIC_CHANGE_CONTENT_ALL + | TOPIC_DOUBLE_LINES; + NOTIFY_LOCK(); // DECopts @@ -588,6 +597,9 @@ screen_reset_do(bool size, bool labels) scr.tab_stops[i] = 0x80808080; } + // clear line attribs + memset(scr.line_attribs, 0, LINE_ATTRS_COUNT); + if (labels) { strcpy(termconf_live.title, termconf->title); strcpy(termconf_live.backdrop, termconf->backdrop); @@ -687,6 +699,32 @@ screen_swap_state(bool alternate) //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 --- void ICACHE_FLASH_ATTR @@ -722,6 +760,7 @@ next_tab_stop(void) { // cursor must never go past EOL if (cursor.x >= W-1) return -1; + if (IS_DOUBLE_WIDTH() && cursor.x >= W/2-1) return -1; // find first word to inspect int idx = (cursor.x+1)/32; @@ -733,6 +772,7 @@ next_tab_stop(void) for(;offs<32;offs++) { cp++; if (cp >= W) return -1; + if (IS_DOUBLE_WIDTH() && cp >= W/2) return -1; if (w & 1) return cp; w >>= 1; } @@ -790,6 +830,7 @@ screen_tab_forward(int count) } else { cursor.x = W - 1; + if (IS_DOUBLE_WIDTH()) cursor.x = W/2 - 1; } } NOTIFY_DONE(TOPIC_CHANGE_CURSOR); @@ -991,19 +1032,23 @@ screen_clear(ClearMode mode) unicode_cache_clear(); clear_range_noutf(0, W * H - 1); scr.last_char[0] = 0; + for (int i = 0; i < LINE_ATTRS_COUNT; i++) scr.line_attribs[i] = 0; break; case CLEAR_FROM_CURSOR: clear_range_utf((cursor.y * W) + cursor.x, W * H - 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; case CLEAR_TO_CURSOR: clear_range_utf(0, (cursor.y * W) + cursor.x); expand_dirty(0, cursor.y, 0, W-1); + for (int i = 0; i <= cursor.y; i++) scr.line_attribs[i] = 0; 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); } /** @@ -1055,11 +1100,15 @@ screen_insert_lines(unsigned int lines) int targetStart = cursor.y + lines; if (targetStart > BTM) { clear_range_utf(cursor.y*W, (BTM+1)*W-1); + for (int i = cursor.y; i <= BTM; i++) { + scr.line_attribs[i] = 0; + } } else { // do the moving for (int i = BTM; i >= targetStart; i--) { utf_free_row(i); // release old characters copy_row(i, i - lines); + scr.line_attribs[i] = scr.line_attribs[i-lines]; if (i != targetStart) utf_backup_row(i); } @@ -1071,7 +1120,7 @@ screen_insert_lines(unsigned int lines) } } 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 @@ -1086,18 +1135,26 @@ screen_delete_lines(unsigned int lines) // clear the entire rest of the screen movedBlockEnd = cursor.y; clear_range_utf(movedBlockEnd*W, (BTM+1)*W-1); + for (int i = movedBlockEnd; i <= BTM; i++) { + scr.line_attribs[i] = 0; + } } else { // move some lines up, clear the rest for (int i = cursor.y; i <= movedBlockEnd; i++) { utf_free_row(i); copy_row(i, i+lines); + scr.line_attribs[i] = scr.line_attribs[i+lines]; if (i != movedBlockEnd) utf_backup_row(i); } 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); - NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); + NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART|TOPIC_DOUBLE_LINES); } void ICACHE_FLASH_ATTR @@ -1304,6 +1361,9 @@ screen_scroll_up(unsigned int lines) if (lines >= RH) { // clear entire region clear_range_utf(TOP * W, (BTM + 1) * W - 1); + for (int i = TOP; i <= BTM; i++) { + scr.line_attribs[i] = 0; + } goto done; } @@ -1316,14 +1376,18 @@ screen_scroll_up(unsigned int lines) for (y = TOP; y <= BTM - lines; y++) { utf_free_row(y); copy_row(y, y+lines); + scr.line_attribs[y] = scr.line_attribs[y+lines]; if (y < BTM - lines) utf_backup_row(y); } clear_range_noutf(y * W, (BTM + 1) * W - 1); + for (int i = y; i <= BTM; i++) { + scr.line_attribs[i] = 0; + } done: expand_dirty(TOP, BTM, 0, W - 1); - NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART); + NOTIFY_DONE(TOPIC_CHANGE_CONTENT_PART|TOPIC_DOUBLE_LINES); } /** @@ -1336,6 +1400,9 @@ screen_scroll_down(unsigned int lines) if (lines >= RH) { // clear entire region clear_range_utf(TOP * W, (BTM + 1) * W - 1); + for (int i = TOP; i <= BTM; i++) { + scr.line_attribs[i] = 0; + } goto done; } @@ -1348,13 +1415,17 @@ screen_scroll_down(unsigned int lines) for (y = BTM; y >= TOP+lines; y--) { utf_free_row(y); copy_row(y, y-lines); + scr.line_attribs[y] = scr.line_attribs[y-lines]; 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: 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 */ @@ -1459,6 +1530,9 @@ screen_cursor_set_x(int x) // hanging happens when the cursor is virtually at col=81, which // cannot be set using the cursor-set commands. cursor.hanging = false; + + if (IS_DOUBLE_WIDTH() && cursor.x >= W/2) cursor.x = W/2-1; + NOTIFY_DONE(TOPIC_CHANGE_CURSOR); } @@ -1503,6 +1577,7 @@ screen_cursor_move(int dy, int dx, bool scroll) cursor.x += dx; cursor.y += dy; 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.auto_wrap && cursor.reverse_wrap) { // this is mimicking a behavior from xterm that allows any number of steps backwards with reverse wraparound enabled @@ -1609,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); } @@ -1977,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.x = W - 1; } + if (IS_DOUBLE_WIDTH() && cursor.x >= W/2) { + cursor.hanging = true; // hanging + cursor.x = W/2 - 1; + } NOTIFY_DONE(topics); return ch; @@ -2203,6 +2284,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, #define TOPICMARK_CURSOR 'C' #define TOPICMARK_SCREEN 'S' #define TOPICMARK_BACKDROP 'W' +#define TOPICMARK_DBL_LINE 'H' if (ss == NULL) { // START! @@ -2327,6 +2409,22 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, 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) bufput_c(TOPICMARK_TITLE); diff --git a/user/screen.h b/user/screen.h index ca37c8e..7169c78 100644 --- a/user/screen.h +++ b/user/screen.h @@ -218,6 +218,7 @@ enum ScreenSerializeTopic { 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 // combos @@ -228,7 +229,8 @@ enum ScreenSerializeTopic { TOPIC_CHANGE_CURSOR | TOPIC_CHANGE_TITLE | TOPIC_CHANGE_BACKDROP | - TOPIC_CHANGE_BUTTONS, + TOPIC_CHANGE_BUTTONS | + TOPIC_DOUBLE_LINES, }; typedef u16 ScreenNotifyTopics; @@ -380,6 +382,9 @@ void screen_tab_reverse(int count); /** Move left, shift right if at the boundary */ 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 --- /** From 0438f88c83e401c8d0a902de5b22fd19191a38eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 12 Nov 2017 12:57:31 +0100 Subject: [PATCH 32/33] update frontend ref --- front-end | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front-end b/front-end index 0893b0a..68a467c 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 0893b0a26886f64c0fa456a4cc69fef0992d7ad9 +Subproject commit 68a467c33e55c4b125e22583d3d874177d5a29ed From 6b4013f779958a96b4dc005df04b7948e441b982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 19 Nov 2017 22:55:54 +0100 Subject: [PATCH 33/33] v bump --- front-end | 2 +- user/version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/front-end b/front-end index 68a467c..8b43b1d 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 68a467c33e55c4b125e22583d3d874177d5a29ed +Subproject commit 8b43b1d17182bf27c62b5d38467c69ee98257239 diff --git a/user/version.h b/user/version.h index c581f65..0d02054 100644 --- a/user/version.h +++ b/user/version.h @@ -11,7 +11,7 @@ #define FW_V_MAJOR 2 #define FW_V_MINOR 3 #define FW_V_PATCH 0 -#define FW_V_SUFFIX "-pre1" +#define FW_V_SUFFIX "" #define FW_CODENAME "Cricket" // 2.3 #define FW_CODENAME_QUOTED "\""FW_CODENAME"\""