Wi-Fi autoconnect and title bar status

This adds support for CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD
in `/.env`. When both are defined, CircuitPython will attempt to
connect to the network even when user code isn't running. If the
user code attempts to a network with the same SSID, it will return
immediately. Connecting to another SSID will disconnect from the
auto-connected network. If the user code initiates the connection,
then it will be shutdown after user code exits. (Should match <8
behavior.)

This PR also reworks the default displayio terminal. It now supports
a title bar TileGrid in addition to the (newly renamed) scroll area.
The default title bar is the top row of the display and is positioned
to the right of the Blinka logo when it is enabled. The scroll area
is now below the Blinka logo.

The Wi-Fi auto-connect code now uses the title bar to show its
state including the IP address when connected. It does this through
the "standard" OSC control sequence `ESC ] 0 ; <s> ESC \` where <s>
is the title bar string. This is commonly supported by terminals
so it should work over USB and UART as well.

Related to #6174
This commit is contained in:
Scott Shawcroft 2022-06-06 16:54:02 -07:00
parent 6925a00138
commit 6446010753
No known key found for this signature in database
GPG Key ID: 0DFD512649C052DA
35 changed files with 587 additions and 144 deletions

View File

@ -120,7 +120,7 @@ Behavior
make each file independent from each other.
- ``boot.py`` runs only once on start up before
USB is initialized. This lays the ground work for configuring USB at
workflows are initialized. This lays the ground work for configuring USB at
startup rather than it being fixed. Since serial is not available,
output is written to ``boot_out.txt``.
- ``code.py`` (or ``main.py``) is run after every reload until it
@ -135,7 +135,10 @@ Behavior
possible to fix code that causes nasty crashes by making it available through mass storage after
the crash. A reset (the button) is needed after it's fixed to get back into normal mode.
- RGB status LED indicating CircuitPython state.
- Re-runs ``code.py`` or other main file after file system writes over USB mass storage. (Disable with
- One green flash - code completed without error.
- Two red flashes - code ended due to an exception.
- Three yellow flashes - safe mode. May be due to CircuitPython internal error.
- Re-runs ``code.py`` or other main file after file system writes by a workflow. (Disable with
``supervisor.disable_autoreload()``)
- Autoreload is disabled while the REPL is active.
- Main is one of these: ``code.txt``, ``code.py``, ``main.py``,

40
docs/environment.rst Normal file
View File

@ -0,0 +1,40 @@
Environment Variables
=====================
CircuitPython 8.0.0 introduces support for environment variables. Environment
variables are commonly used to store "secrets" such as Wi-Fi passwords and API
keys. This method *does not* make them secure. It only separates them from the
code.
CircuitPython supports these by mimicking the `dotenv <https://github.com/theskumar/python-dotenv>`_
CPython library. Other languages such as Javascript, PHP and Ruby also have
dotenv libraries.
These libraries store environment variables in a ``.env`` file. Here is a simple
example:
.. code-block:: bash
KEY1='value1'
# Comment
KEY2='value2
is multiple lines'
CircuitPython uses the ``.env`` at the drive root (no folder) as the environment.
User code can access the values from the file using `os.getenv()`. It is
recommended to save any values used repeatedly in a variable because `os.getenv()`
will parse the ``/.env`` on every access.
CircuitPython behavior
----------------------
CircuitPython will also read the environment to configure its behavior. Other
keys are ignored by CircuitPython. Here are the keys it uses:
CIRCUITPY_WIFI_PASSWORD
~~~~~~~~~~~~~~~~~~~~~~~
Wi-Fi password used to auto connect to CIRCUITPY_WIFI_SSID
CIRCUITPY_WIFI_SSID
~~~~~~~~~~~~~~~~~~~
Wi-Fi SSID to auto-connect to even if user code is not running.

View File

@ -22,6 +22,7 @@ Full Table of Contents
supported_ports.rst
troubleshooting.rst
drivers.rst
environment.rst
.. toctree::
:maxdepth: 1

View File

@ -1375,6 +1375,10 @@ msgstr ""
msgid "No I2C device at address: 0x%x"
msgstr ""
#: supervisor/shared/web_workflow/web_workflow.c
msgid "No IP"
msgstr ""
#: ports/espressif/common-hal/busio/SPI.c
#: ports/mimxrt10xx/common-hal/busio/SPI.c
msgid "No MISO Pin"
@ -2254,6 +2258,10 @@ msgid ""
"To list built-in modules type `help(\"modules\")`.\n"
msgstr ""
#: supervisor/shared/web_workflow/web_workflow.c
msgid "Wi-Fi: "
msgstr ""
#: main.c
msgid "Woken up by alarm.\n"
msgstr ""
@ -3579,6 +3587,10 @@ msgstr ""
msgid "odd-length string"
msgstr ""
#: supervisor/shared/web_workflow/web_workflow.c
msgid "off"
msgstr ""
#: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c
msgid "offset is too large"
msgstr ""

33
main.c
View File

@ -101,6 +101,10 @@
#include "shared-module/memorymonitor/__init__.h"
#endif
#if CIRCUITPY_SOCKETPOOL
#include "shared-bindings/socketpool/__init__.h"
#endif
#if CIRCUITPY_USB_HID
#include "shared-module/usb_hid/__init__.h"
#endif
@ -290,6 +294,16 @@ STATIC void cleanup_after_vm(supervisor_allocation *heap, mp_obj_t exception) {
keypad_reset();
#endif
// Close user-initiated sockets.
#if CIRCUITPY_SOCKETPOOL
socketpool_user_reset();
#endif
// Turn off user initiated WiFi connections.
#if CIRCUITPY_WIFI
wifi_user_reset();
#endif
// reset_board_buses() first because it may release pins from the never_reset state, so that
// reset_port() can reset them.
#if CIRCUITPY_BOARD
@ -303,6 +317,9 @@ STATIC void cleanup_after_vm(supervisor_allocation *heap, mp_obj_t exception) {
stop_mp();
free_memory(heap);
supervisor_move_memory();
// Let the workflows know we've reset in case they want to restart.
supervisor_workflow_reset();
}
STATIC void print_code_py_status_message(safe_mode_t safe_mode) {
@ -889,21 +906,7 @@ int __attribute__((used)) main(void) {
run_boot_py(safe_mode);
// Start USB after giving boot.py a chance to tweak behavior.
#if CIRCUITPY_USB
// Setup USB connection after heap is available.
// It needs the heap to build descriptors.
usb_init();
#endif
// Set up any other serial connection.
serial_init();
#if CIRCUITPY_BLEIO
bleio_reset();
supervisor_bluetooth_enable_workflow();
supervisor_start_bluetooth();
#endif
supervisor_workflow_start();
// Boot script is finished, so now go into REPL or run code.py.
int exit_code = PYEXEC_FORCED_EXIT;

View File

@ -117,6 +117,10 @@ void board_init(void) {
true, // backlight_on_high
false, // SH1107_addressing
50000); // backlight pwm frequency
#if CIRCUITPY_DEBUG
common_hal_never_reset_pin(DEFAULT_UART_BUS_TX);
#endif
}
bool espressif_board_reset_pin_number(gpio_num_t pin_number) {

View File

@ -39,7 +39,7 @@
STATIC socketpool_socket_obj_t *open_socket_handles[CONFIG_LWIP_MAX_SOCKETS];
void socket_reset(void) {
void socket_user_reset(void) {
for (size_t i = 0; i < MP_ARRAY_SIZE(open_socket_handles); i++) {
if (open_socket_handles[i]) {
if (open_socket_handles[i]->num > 0) {

View File

@ -45,7 +45,7 @@ typedef struct {
mp_uint_t timeout_ms;
} socketpool_socket_obj_t;
void socket_reset(void);
void socket_user_reset(void);
bool register_open_socket(socketpool_socket_obj_t *self);
#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_SOCKETPOOL_SOCKET_H

View File

@ -23,3 +23,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "shared-bindings/socketpool/__init__.h"
#include "common-hal/socketpool/Socket.h"
void socketpool_user_reset(void) {
socket_user_reset();
}

View File

@ -236,6 +236,7 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t
if (!common_hal_wifi_radio_get_enabled(self)) {
mp_raise_RuntimeError(translate("wifi is not enabled"));
}
wifi_config_t *config = &self->sta_config;
EventBits_t bits;
// can't block since both bits are false after wifi_init
@ -245,16 +246,31 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t
pdTRUE,
pdTRUE,
0);
if (((bits & WIFI_CONNECTED_BIT) != 0) &&
!((bits & WIFI_DISCONNECTED_BIT) != 0)) {
return WIFI_RADIO_ERROR_NONE;
bool connected = ((bits & WIFI_CONNECTED_BIT) != 0) &&
!((bits & WIFI_DISCONNECTED_BIT) != 0);
if (connected) {
if (memcmp(ssid, config->sta.ssid, ssid_len) == 0) {
// Already connected to the desired network.
return WIFI_RADIO_ERROR_NONE;
} else {
xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT);
// Trying to switch networks so disconnect first.
esp_wifi_disconnect();
do {
RUN_BACKGROUND_TASKS;
bits = xEventGroupWaitBits(self->event_group_handle,
WIFI_DISCONNECTED_BIT,
pdTRUE,
pdTRUE,
0);
} while ((bits & WIFI_DISCONNECTED_BIT) == 0 && !mp_hal_is_interrupted());
}
}
// explicitly clear bits since xEventGroupWaitBits may have timed out
xEventGroupClearBits(self->event_group_handle, WIFI_CONNECTED_BIT);
xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT);
set_mode_station(self, true);
wifi_config_t *config = &self->sta_config;
memcpy(&config->sta.ssid, ssid, ssid_len);
config->sta.ssid[ssid_len] = 0;
memcpy(&config->sta.password, password, password_len);
@ -368,6 +384,14 @@ mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self) {
return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.netmask.addr);
}
uint32_t wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) {
if (!esp_netif_is_netif_up(self->netif)) {
return 0;
}
esp_netif_get_ip_info(self->netif, &self->ip_info);
return self->ip_info.ip.addr;
}
mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) {
if (!esp_netif_is_netif_up(self->netif)) {
return mp_const_none;

View File

@ -42,6 +42,8 @@ wifi_radio_obj_t common_hal_wifi_radio_obj;
#include "components/log/include/esp_log.h"
#include "supervisor/workflow.h"
static const char *TAG = "wifi";
static void event_handler(void *arg, esp_event_base_t event_base,
@ -106,12 +108,19 @@ static void event_handler(void *arg, esp_event_base_t event_base,
radio->retries_left = radio->starting_retries;
xEventGroupSetBits(radio->event_group_handle, WIFI_CONNECTED_BIT);
}
supervisor_workflow_request_background();
}
static bool wifi_inited, wifi_ever_inited;
static bool wifi_inited;
static bool wifi_ever_inited;
static bool wifi_user_initiated;
void common_hal_wifi_init(void) {
void common_hal_wifi_init(bool user_initiated) {
if (wifi_inited) {
return;
}
wifi_inited = true;
wifi_user_initiated = user_initiated;
common_hal_wifi_radio_obj.base.type = &wifi_radio_type;
if (!wifi_ever_inited) {
@ -157,6 +166,12 @@ void common_hal_wifi_init(void) {
common_hal_wifi_radio_set_enabled(self, true);
}
void wifi_user_reset(void) {
if (wifi_user_initiated) {
wifi_reset();
}
}
void wifi_reset(void) {
if (!wifi_inited) {
return;
@ -176,6 +191,7 @@ void wifi_reset(void) {
esp_netif_destroy(radio->ap_netif);
radio->ap_netif = NULL;
wifi_inited = false;
supervisor_workflow_request_background();
}
void ipaddress_ipaddress_to_esp_idf(mp_obj_t ip_address, ip_addr_t *esp_ip_address) {

View File

@ -282,14 +282,6 @@ void reset_port(void) {
#if CIRCUITPY_WATCHDOG
watchdog_reset();
#endif
#if CIRCUITPY_WIFI
wifi_reset();
#endif
#if CIRCUITPY_SOCKETPOOL
socket_reset();
#endif
}
void reset_to_bootloader(void) {

View File

@ -70,7 +70,9 @@ endif
CIRCUITPY_LTO ?= 0
CIRCUITPY_LTO_PARTITION ?= balanced
ifeq ($(CIRCUITPY_LTO),1)
CFLAGS += -flto -flto-partition=$(CIRCUITPY_LTO_PARTITION)
CFLAGS += -flto -flto-partition=$(CIRCUITPY_LTO_PARTITION) -DCIRCUITPY_LTO=1
else
CFLAGS += -DCIRCUITPY_LTO=0
endif
# Produce an object file for translate.c instead of including it in a header.

View File

@ -480,6 +480,9 @@ CFLAGS += -DCIRCUITPY_WATCHDOG=$(CIRCUITPY_WATCHDOG)
CIRCUITPY_WIFI ?= 0
CFLAGS += -DCIRCUITPY_WIFI=$(CIRCUITPY_WIFI)
CIRCUITPY_WEB_WORKFLOW ?= $(CIRCUITPY_WIFI)
CFLAGS += -DCIRCUITPY_WEB_WORKFLOW=$(CIRCUITPY_WEB_WORKFLOW)
# tinyusb port tailored configuration
CIRCUITPY_TUSB_MEM_ALIGN ?= 4
CFLAGS += -DCIRCUITPY_TUSB_MEM_ALIGN=$(CIRCUITPY_TUSB_MEM_ALIGN)

View File

@ -69,5 +69,6 @@ void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t
// Private API for scrolling the TileGrid.
void common_hal_displayio_tilegrid_set_top_left(displayio_tilegrid_t *self, uint16_t x, uint16_t y);
void common_hal_displayio_tilegrid_set_all_tiles(displayio_tilegrid_t *self, uint8_t tile_index);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_TILEGRID_H

View File

@ -27,4 +27,6 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL___INIT___H
#define MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL___INIT___H
void socketpool_user_reset(void);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL___INIT___H

View File

@ -38,31 +38,52 @@
#include "supervisor/shared/translate/translate.h"
//| class Terminal:
//| """Display a character stream with a TileGrid"""
//| """Display a character stream with a TileGrid
//|
//| def __init__(self, tilegrid: displayio.TileGrid, font: fontio.BuiltinFont) -> None:
//| ASCII control:
//| * ``\\r`` - Move cursor to column 1
//| * ``\\n`` - Move cursor down a row
//| * ``\\b`` - Move cursor left one if possible
//|
//| OSC control sequences:
//| * ``ESC ] 0; <s> ESC \\`` - Set title bar to <s>
//| * ``ESC ] ####; <s> ESC \\`` - Ignored
//|
//| VT100 control sequences:
//| * ``ESC [ K`` - Clear the remainder of the line
//| * ``ESC [ #### D`` - Move the cursor to the left by ####
//| * ``ESC [ 2 J`` - Erase the entire display
//| * ``ESC [ nnnn ; mmmm H`` - Move the cursor to mmmm, nnnn.
//| """
//|
//| def __init__(self, scroll_area: displayio.TileGrid, font: fontio.BuiltinFont, *, title_bar: displayio.TileGrid = None) -> None:
//| """Terminal manages tile indices and cursor position based on VT100 commands. The font should be
//| a `fontio.BuiltinFont` and the TileGrid's bitmap should match the font's bitmap."""
//| ...
//|
STATIC mp_obj_t terminalio_terminal_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_tilegrid, ARG_font };
enum { ARG_scroll_area, ARG_font, ARG_title_bar };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_tilegrid, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_scroll_area, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_font, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_title_bar, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = mp_const_none } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
displayio_tilegrid_t *tilegrid = mp_arg_validate_type(args[ARG_tilegrid].u_obj, &displayio_tilegrid_type, MP_QSTR_tilegrid);
displayio_tilegrid_t *scroll_area = mp_arg_validate_type(args[ARG_scroll_area].u_obj, &displayio_tilegrid_type, MP_QSTR_scroll_area);
displayio_tilegrid_t *title_bar = NULL;
if (args[ARG_title_bar].u_obj != mp_const_none) {
title_bar = mp_arg_validate_type(args[ARG_title_bar].u_obj, &displayio_tilegrid_type, MP_QSTR_title_bar);
}
fontio_builtinfont_t *font = mp_arg_validate_type(args[ARG_font].u_obj, &fontio_builtinfont_type, MP_QSTR_font);
terminalio_terminal_obj_t *self = m_new_obj(terminalio_terminal_obj_t);
self->base.type = &terminalio_terminal_type;
common_hal_terminalio_terminal_construct(self, tilegrid, font);
common_hal_terminalio_terminal_construct(self, scroll_area, font, title_bar);
return MP_OBJ_FROM_PTR(self);
}

View File

@ -34,7 +34,7 @@
extern const mp_obj_type_t terminalio_terminal_type;
extern void common_hal_terminalio_terminal_construct(terminalio_terminal_obj_t *self,
displayio_tilegrid_t *tilegrid, const fontio_builtinfont_t *font);
displayio_tilegrid_t *scroll_area, const fontio_builtinfont_t *font, displayio_tilegrid_t *title_bar);
// Write characters. len is in characters NOT bytes!
extern size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self,

View File

@ -439,11 +439,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_set_ipv4_address_obj, 1, wifi_radio
//| ipv4_address: Optional[ipaddress.IPv4Address]
//| """IP v4 Address of the station when connected to an access point. None otherwise."""
//|
STATIC mp_obj_t wifi_radio_get_ipv4_address(mp_obj_t self) {
STATIC mp_obj_t _wifi_radio_get_ipv4_address(mp_obj_t self) {
return common_hal_wifi_radio_get_ipv4_address(self);
}
MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ipv4_address_obj, wifi_radio_get_ipv4_address);
MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ipv4_address_obj, _wifi_radio_get_ipv4_address);
MP_PROPERTY_GETTER(wifi_radio_ipv4_address_obj,
(mp_obj_t)&wifi_radio_get_ipv4_address_obj);

View File

@ -103,6 +103,7 @@ extern mp_obj_t common_hal_wifi_radio_get_ipv4_gateway(wifi_radio_obj_t *self);
extern mp_obj_t common_hal_wifi_radio_get_ipv4_gateway_ap(wifi_radio_obj_t *self);
extern mp_obj_t common_hal_wifi_radio_get_ipv4_subnet(wifi_radio_obj_t *self);
extern mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self);
uint32_t wifi_radio_get_ipv4_address(wifi_radio_obj_t *self);
extern mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self);
extern mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self);

View File

@ -42,7 +42,7 @@
// Called when wifi is imported.
STATIC mp_obj_t wifi___init__(void) {
common_hal_wifi_init();
common_hal_wifi_init(true);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(wifi___init___obj, wifi___init__);

View File

@ -31,7 +31,9 @@
extern wifi_radio_obj_t common_hal_wifi_radio_obj;
void common_hal_wifi_init(void);
void common_hal_wifi_init(bool user_initiated);
void common_hal_wifi_gc_collect(void);
void wifi_user_reset(void);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WIFI___INIT___H

View File

@ -296,6 +296,27 @@ void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t
self->partial_change = true;
}
void common_hal_displayio_tilegrid_set_all_tiles(displayio_tilegrid_t *self, uint8_t tile_index) {
if (tile_index >= self->tiles_in_bitmap) {
mp_raise_ValueError(translate("Tile index out of bounds"));
}
uint8_t *tiles = self->tiles;
if (self->inline_tiles) {
tiles = (uint8_t *)&self->tiles;
}
if (tiles == NULL) {
return;
}
for (uint16_t x = 0; x < self->width_in_tiles; x++) {
for (uint16_t y = 0; y < self->height_in_tiles; y++) {
tiles[y * self->width_in_tiles + x] = tile_index;
}
}
self->full_change = true;
}
bool common_hal_displayio_tilegrid_get_flip_x(displayio_tilegrid_t *self) {
return self->flip_x;
}

View File

@ -30,24 +30,28 @@
#include "shared-bindings/displayio/TileGrid.h"
#include "shared-bindings/terminalio/Terminal.h"
void common_hal_terminalio_terminal_construct(terminalio_terminal_obj_t *self, displayio_tilegrid_t *tilegrid, const fontio_builtinfont_t *font) {
void common_hal_terminalio_terminal_construct(terminalio_terminal_obj_t *self,
displayio_tilegrid_t *scroll_area, const fontio_builtinfont_t *font,
displayio_tilegrid_t *title_bar) {
self->cursor_x = 0;
self->cursor_y = 0;
self->font = font;
self->tilegrid = tilegrid;
self->scroll_area = scroll_area;
self->title_bar = title_bar;
self->title_x = 0;
self->title_y = 0;
self->first_row = 0;
for (uint16_t x = 0; x < self->tilegrid->width_in_tiles; x++) {
for (uint16_t y = 0; y < self->tilegrid->height_in_tiles; y++) {
common_hal_displayio_tilegrid_set_tile(self->tilegrid, x, y, 0);
}
common_hal_displayio_tilegrid_set_all_tiles(self->scroll_area, 0);
if (self->title_bar) {
common_hal_displayio_tilegrid_set_all_tiles(self->title_bar, 0);
}
common_hal_displayio_tilegrid_set_top_left(self->tilegrid, 0, 1);
common_hal_displayio_tilegrid_set_top_left(self->scroll_area, 0, 1);
}
size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, const byte *data, size_t len, int *errcode) {
// Make sure the terminal is initialized before we do anything with it.
if (self->tilegrid == NULL) {
if (self->scroll_area == NULL) {
return len;
}
const byte *i = data;
@ -55,11 +59,34 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con
while (i < data + len) {
unichar c = utf8_get_char(i);
i = utf8_next_char(i);
if (self->in_osc_command) {
if (c == 0x1b && i[0] == '\\') {
self->in_osc_command = false;
self->title_x = 0;
self->title_y = 0;
i += 1;
} else if (self->osc_command == 0 && self->title_bar != NULL && self->title_y < self->title_bar->height_in_tiles) {
uint8_t tile_index = fontio_builtinfont_get_glyph_index(self->font, c);
if (tile_index != 0xff) {
// Clear the tile grid before we start putting new info.
if (self->title_x == 0 && self->title_y == 0) {
common_hal_displayio_tilegrid_set_all_tiles(self->title_bar, 0);
}
common_hal_displayio_tilegrid_set_tile(self->title_bar, self->title_x, self->title_y, tile_index);
self->title_x++;
if (self->title_x >= self->title_bar->width_in_tiles) {
self->title_y++;
self->title_x %= self->title_bar->width_in_tiles;
}
}
}
continue;
}
// Always handle ASCII.
if (c < 128) {
if (c >= 0x20 && c <= 0x7e) {
uint8_t tile_index = fontio_builtinfont_get_glyph_index(self->font, c);
common_hal_displayio_tilegrid_set_tile(self->tilegrid, self->cursor_x, self->cursor_y, tile_index);
common_hal_displayio_tilegrid_set_tile(self->scroll_area, self->cursor_x, self->cursor_y, tile_index);
self->cursor_x++;
} else if (c == '\r') {
self->cursor_x = 0;
@ -71,25 +98,25 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con
self->cursor_x--;
}
} else if (c == 0x1b) {
// Handle commands of the form \x1b.####D where . is ignored.
uint16_t n = 0;
uint8_t j = 1;
for (; j < 6; j++) {
if ('0' <= i[j] && i[j] <= '9') {
n = n * 10 + (i[j] - '0');
} else {
c = i[j];
break;
}
}
if (i[0] == '[') {
if (i[1] == 'K') {
// Clear the rest of the line.
for (uint16_t j = self->cursor_x; j < self->tilegrid->width_in_tiles; j++) {
common_hal_displayio_tilegrid_set_tile(self->tilegrid, j, self->cursor_y, 0);
for (uint16_t k = self->cursor_x; k < self->scroll_area->width_in_tiles; k++) {
common_hal_displayio_tilegrid_set_tile(self->scroll_area, k, self->cursor_y, 0);
}
i += 2;
} else {
// Handle commands of the form \x1b[####D
uint16_t n = 0;
uint8_t j = 1;
for (; j < 6; j++) {
if ('0' <= i[j] && i[j] <= '9') {
n = n * 10 + (i[j] - '0');
} else {
c = i[j];
break;
}
}
if (c == 'D') {
if (n > self->cursor_x) {
self->cursor_x = 0;
@ -99,14 +126,10 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con
}
if (c == 'J') {
if (n == 2) {
common_hal_displayio_tilegrid_set_top_left(self->tilegrid, 0, 0);
common_hal_displayio_tilegrid_set_top_left(self->scroll_area, 0, 0);
self->cursor_x = self->cursor_y = start_y = 0;
n = 0;
for (uint16_t x = 0; x < self->tilegrid->width_in_tiles; x++) {
for (uint16_t y = 0; y < self->tilegrid->height_in_tiles; y++) {
common_hal_displayio_tilegrid_set_tile(self->tilegrid, x, y, 0);
}
}
common_hal_displayio_tilegrid_set_all_tiles(self->scroll_area, 0);
}
}
if (c == ';') {
@ -126,13 +149,13 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con
if (m > 0) {
m--;
}
if (n >= self->tilegrid->height_in_tiles) {
n = self->tilegrid->height_in_tiles - 1;
if (n >= self->scroll_area->height_in_tiles) {
n = self->scroll_area->height_in_tiles - 1;
}
if (m >= self->tilegrid->width_in_tiles) {
m = self->tilegrid->width_in_tiles - 1;
if (m >= self->scroll_area->width_in_tiles) {
m = self->scroll_area->width_in_tiles - 1;
}
n = (n + self->tilegrid->top_left_y) % self->tilegrid->height_in_tiles;
n = (n + self->scroll_area->top_left_y) % self->scroll_area->height_in_tiles;
self->cursor_x = m;
self->cursor_y = n;
start_y = self->cursor_y;
@ -141,30 +164,34 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con
i += j + 1;
continue;
}
} else if (i[0] == ']' && c == ';') {
self->in_osc_command = true;
self->osc_command = n;
i += j + 1;
}
}
} else {
uint8_t tile_index = fontio_builtinfont_get_glyph_index(self->font, c);
if (tile_index != 0xff) {
common_hal_displayio_tilegrid_set_tile(self->tilegrid, self->cursor_x, self->cursor_y, tile_index);
common_hal_displayio_tilegrid_set_tile(self->scroll_area, self->cursor_x, self->cursor_y, tile_index);
self->cursor_x++;
}
}
if (self->cursor_x >= self->tilegrid->width_in_tiles) {
if (self->cursor_x >= self->scroll_area->width_in_tiles) {
self->cursor_y++;
self->cursor_x %= self->tilegrid->width_in_tiles;
self->cursor_x %= self->scroll_area->width_in_tiles;
}
if (self->cursor_y >= self->tilegrid->height_in_tiles) {
self->cursor_y %= self->tilegrid->height_in_tiles;
if (self->cursor_y >= self->scroll_area->height_in_tiles) {
self->cursor_y %= self->scroll_area->height_in_tiles;
}
if (self->cursor_y != start_y) {
// clear the new row in case of scroll up
if (self->cursor_y == self->tilegrid->top_left_y) {
for (uint16_t j = 0; j < self->tilegrid->width_in_tiles; j++) {
common_hal_displayio_tilegrid_set_tile(self->tilegrid, j, self->cursor_y, 0);
if (self->cursor_y == self->scroll_area->top_left_y) {
for (uint16_t j = 0; j < self->scroll_area->width_in_tiles; j++) {
common_hal_displayio_tilegrid_set_tile(self->scroll_area, j, self->cursor_y, 0);
}
common_hal_displayio_tilegrid_set_top_left(self->tilegrid, 0, (self->cursor_y + self->tilegrid->height_in_tiles + 1) % self->tilegrid->height_in_tiles);
common_hal_displayio_tilegrid_set_top_left(self->scroll_area, 0, (self->cursor_y + self->scroll_area->height_in_tiles + 1) % self->scroll_area->height_in_tiles);
}
start_y = self->cursor_y;
}
@ -173,5 +200,5 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con
}
bool common_hal_terminalio_terminal_ready_to_tx(terminalio_terminal_obj_t *self) {
return self->tilegrid != NULL;
return self->scroll_area != NULL;
}

View File

@ -39,8 +39,13 @@ typedef struct {
const fontio_builtinfont_t *font;
uint16_t cursor_x;
uint16_t cursor_y;
displayio_tilegrid_t *tilegrid;
displayio_tilegrid_t *scroll_area;
displayio_tilegrid_t *title_bar;
uint16_t title_x;
uint16_t title_y;
uint16_t first_row;
uint16_t osc_command;
bool in_osc_command;
} terminalio_terminal_obj_t;
#endif /* SHARED_MODULE_TERMINALIO_TERMINAL_H */

View File

@ -111,16 +111,28 @@ void background_callback_end_critical_section() {
CALLBACK_CRITICAL_END;
}
// Filter out queued callbacks if they are allocated on the heap.
void background_callback_reset() {
background_callback_t *new_head = NULL;
background_callback_t **previous_next = &new_head;
background_callback_t *new_tail = NULL;
CALLBACK_CRITICAL_BEGIN;
background_callback_t *cb = (background_callback_t *)callback_head;
while (cb) {
background_callback_t *next = cb->next;
memset(cb, 0, sizeof(*cb));
if (!HEAP_PTR((void *)cb)) {
*previous_next = cb;
previous_next = &cb->next;
cb->next = NULL;
new_tail = cb;
} else {
memset(cb, 0, sizeof(*cb));
}
cb = next;
}
callback_head = NULL;
callback_tail = NULL;
callback_head = new_head;
callback_tail = new_tail;
in_background_callback = false;
CALLBACK_CRITICAL_END;
}

View File

@ -61,37 +61,26 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
uint8_t scale = 2;
#if CIRCUITPY_TERMINALIO
displayio_tilegrid_t *grid = &supervisor_terminal_text_grid;
bool tall = height_px > width_px;
displayio_tilegrid_t *scroll_area = &supervisor_terminal_scroll_area_text_grid;
displayio_tilegrid_t *title_bar = &supervisor_terminal_title_bar_text_grid;
bool reset_tiles = false;
#if CIRCUITPY_REPL_LOGO
uint16_t terminal_width_px = tall ? width_px : width_px - blinka_bitmap.width;
uint16_t terminal_height_px = tall ? height_px - blinka_bitmap.height : height_px;
#else
uint16_t terminal_width_px = width_px;
uint16_t terminal_height_px = height_px;
#endif
uint16_t width_in_tiles = terminal_width_px / grid->tile_width;
uint16_t width_in_tiles = width_px / scroll_area->tile_width;
// determine scale based on h
if (width_in_tiles < 80) {
scale = 1;
}
width_in_tiles = terminal_width_px / (grid->tile_width * scale);
width_in_tiles = width_px / (scroll_area->tile_width * scale);
if (width_in_tiles < 1) {
width_in_tiles = 1;
}
uint16_t height_in_tiles = terminal_height_px / (grid->tile_height * scale);
uint16_t remaining_pixels = tall ? 0 : terminal_height_px % (grid->tile_height * scale);
if (height_in_tiles < 1 || remaining_pixels > 0) {
height_in_tiles += 1;
}
uint16_t height_in_tiles = height_px / (scroll_area->tile_height * scale);
uint16_t total_tiles = width_in_tiles * height_in_tiles;
// check if the terminal tile dimensions are the same
if ((grid->width_in_tiles != width_in_tiles) ||
(grid->height_in_tiles != height_in_tiles)) {
if ((scroll_area->width_in_tiles != width_in_tiles) ||
(scroll_area->height_in_tiles != height_in_tiles - 1)) {
reset_tiles = true;
}
// Reuse the previous allocation if possible
@ -114,27 +103,39 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
uint8_t *tiles = (uint8_t *)tilegrid_tiles->ptr;
#if CIRCUITPY_REPL_LOGO
grid->y = tall ? blinka_bitmap.height : 0;
grid->x = tall ? 0 : blinka_bitmap.width;
title_bar->x = blinka_bitmap.width;
// Align the title bar to the bottom of the logo.
title_bar->y = blinka_bitmap.height - title_bar->tile_height;
#else
grid->y = 0;
grid->x = 0;
title_bar->x = 0;
title_bar->y = 0;
#endif
grid->top_left_y = 0;
if (remaining_pixels > 0) {
grid->y -= (grid->tile_height - remaining_pixels);
}
grid->width_in_tiles = width_in_tiles;
grid->height_in_tiles = height_in_tiles;
title_bar->top_left_y = 0;
title_bar->width_in_tiles = width_in_tiles;
title_bar->height_in_tiles = 1;
assert(width_in_tiles > 0);
assert(height_in_tiles > 0);
grid->pixel_width = width_in_tiles * grid->tile_width;
grid->pixel_height = height_in_tiles * grid->tile_height;
grid->tiles = tiles;
title_bar->pixel_width = width_in_tiles * title_bar->tile_width;
title_bar->pixel_height = title_bar->tile_height;
title_bar->tiles = tiles;
title_bar->full_change = true;
grid->full_change = true;
scroll_area->x = 0;
#if CIRCUITPY_REPL_LOGO
scroll_area->y = blinka_bitmap.height;
#else
scroll_area->y = scroll_area->tile_height;
#endif
scroll_area->top_left_y = 0;
scroll_area->width_in_tiles = width_in_tiles;
scroll_area->height_in_tiles = height_in_tiles - 1;
assert(width_in_tiles > 0);
assert(height_in_tiles > 1);
scroll_area->pixel_width = width_in_tiles * scroll_area->tile_width;
scroll_area->pixel_height = (height_in_tiles - 1) * scroll_area->tile_height;
scroll_area->tiles = tiles + width_in_tiles;
scroll_area->full_change = true;
common_hal_terminalio_terminal_construct(&supervisor_terminal, grid, &supervisor_terminal_font);
common_hal_terminalio_terminal_construct(&supervisor_terminal, scroll_area, &supervisor_terminal_font, title_bar);
}
#endif
@ -146,18 +147,24 @@ void supervisor_stop_terminal(void) {
if (tilegrid_tiles != NULL) {
free_memory(tilegrid_tiles);
tilegrid_tiles = NULL;
supervisor_terminal_text_grid.tiles = NULL;
supervisor_terminal.tilegrid = NULL;
supervisor_terminal_scroll_area_text_grid.tiles = NULL;
supervisor_terminal_title_bar_text_grid.tiles = NULL;
supervisor_terminal.scroll_area = NULL;
supervisor_terminal.title_bar = NULL;
}
#endif
}
void supervisor_display_move_memory(void) {
#if CIRCUITPY_TERMINALIO
displayio_tilegrid_t *scroll_area = &supervisor_terminal_scroll_area_text_grid;
displayio_tilegrid_t *title_bar = &supervisor_terminal_title_bar_text_grid;
if (tilegrid_tiles != NULL) {
supervisor_terminal_text_grid.tiles = (uint8_t *)tilegrid_tiles->ptr;
title_bar->tiles = (uint8_t *)tilegrid_tiles->ptr;
scroll_area->tiles = (uint8_t *)tilegrid_tiles->ptr + scroll_area->width_in_tiles;
} else {
supervisor_terminal_text_grid.tiles = NULL;
scroll_area->tiles = NULL;
title_bar->tiles = NULL;
}
#endif
@ -298,21 +305,21 @@ displayio_tilegrid_t blinka_sprite = {
#if CIRCUITPY_TERMINALIO
#if CIRCUITPY_REPL_LOGO
mp_obj_t members[] = { &blinka_sprite, &supervisor_terminal_text_grid, };
mp_obj_t members[] = { &blinka_sprite, &supervisor_terminal_title_bar_text_grid, &supervisor_terminal_scroll_area_text_grid, };
mp_obj_list_t splash_children = {
.base = {.type = &mp_type_list },
.alloc = 3,
.len = 3,
.items = members,
};
#else
mp_obj_t members[] = { &supervisor_terminal_title_bar_text_grid, &supervisor_terminal_scroll_area_text_grid, };
mp_obj_list_t splash_children = {
.base = {.type = &mp_type_list },
.alloc = 2,
.len = 2,
.items = members,
};
#else
mp_obj_t members[] = { &supervisor_terminal_text_grid, };
mp_obj_list_t splash_children = {
.base = {.type = &mp_type_list },
.alloc = 1,
.len = 1,
.items = members,
};
#endif
#else
#if CIRCUITPY_REPL_LOGO

View File

@ -43,7 +43,8 @@ extern const fontio_builtinfont_t supervisor_terminal_font;
// These will change so they must live in RAM.
extern displayio_bitmap_t supervisor_terminal_font_bitmap;
extern displayio_tilegrid_t supervisor_terminal_text_grid;
extern displayio_tilegrid_t supervisor_terminal_scroll_area_text_grid;
extern displayio_tilegrid_t supervisor_terminal_title_bar_text_grid;
extern terminalio_terminal_obj_t supervisor_terminal;
#endif

View File

@ -45,7 +45,7 @@ static
#endif
inline
// gcc10 -flto has issues with this being always_inline for debug builds.
#if CIRCUITPY_DEBUG < 1
#if !CIRCUITPY_LTO || CIRCUITPY_DEBUG < 1
__attribute__((always_inline))
#endif
const compressed_string_t *translate(const char *original) {

View File

@ -0,0 +1,92 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2022 Scott Shawcroft for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include "shared-bindings/wifi/Radio.h"
#include "supervisor/shared/translate/translate.h"
#include "supervisor/shared/web_workflow/web_workflow.h"
#if CIRCUITPY_WIFI
#include "shared-bindings/wifi/__init__.h"
#endif
#if CIRCUITPY_DOTENV
#include "shared-module/dotenv/__init__.h"
#endif
static wifi_radio_error_t wifi_status = WIFI_RADIO_ERROR_NONE;
void supervisor_web_workflow_status(void) {
serial_write_compressed(translate("Wi-Fi: "));
if (common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj)) {
uint32_t ipv4_address = wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj);
if (wifi_status != WIFI_RADIO_ERROR_NONE) {
mp_printf(&mp_plat_print, "%d", wifi_status);
} else if (ipv4_address == 0) {
serial_write_compressed(translate("No IP"));
} else {
uint8_t *octets = (uint8_t *)&ipv4_address;
mp_printf(&mp_plat_print, "%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]);
}
} else {
serial_write_compressed(translate("off"));
}
}
void supervisor_start_web_workflow(void) {
#if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI
char ssid[33];
char password[64];
mp_int_t ssid_len = 0;
mp_int_t password_len = 0;
#if CIRCUITPY_DOTENV
ssid_len = dotenv_get_key("/.env", "CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid) - 1);
password_len = dotenv_get_key("/.env", "CIRCUITPY_WIFI_PASSWORD", password, sizeof(password) - 1);
#endif
if (ssid_len <= 0 || (size_t)ssid_len >= sizeof(ssid) ||
password_len <= 0 || (size_t)password_len >= sizeof(password)) {
return;
}
common_hal_wifi_init(false);
common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, true);
// NUL terminate the strings because dotenv doesn't.
ssid[ssid_len] = '\0';
password[password_len] = '\0';
wifi_status = common_hal_wifi_radio_connect(
&common_hal_wifi_radio_obj, (uint8_t *)ssid, ssid_len, (uint8_t *)password, password_len,
0, 1, NULL, 0);
if (wifi_status != WIFI_RADIO_ERROR_NONE) {
common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, false);
}
#endif
}
void supervisor_stop_web_workflow(void) {
}

View File

@ -0,0 +1,35 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2022 Scott Shawcroft for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <stdbool.h>
void supervisor_wifi_background(void);
void supervisor_wifi_init(void);
void supervisor_web_workflow_status(void);
void supervisor_start_web_workflow(void);
void supervisor_stop_web_workflow(void);

View File

@ -26,14 +26,61 @@
#include <stdbool.h>
#include "py/mpconfig.h"
#include "supervisor/background_callback.h"
#include "supervisor/workflow.h"
#include "supervisor/serial.h"
#include "supervisor/shared/workflow.h"
#if CIRCUITPY_BLEIO
#include "shared-bindings/_bleio/__init__.h"
#include "supervisor/shared/bluetooth/bluetooth.h"
#endif
#if CIRCUITPY_USB
#include "supervisor/usb.h"
#include "tusb.h"
#endif
#if CIRCUITPY_WEB_WORKFLOW
#include "supervisor/shared/web_workflow/web_workflow.h"
#endif
static background_callback_t workflow_background_cb;
static void supervisor_workflow_update_status_bar(void) {
// Neighboring "" "" are concatenated by the compiler. Without this separation, the hex code
// doesn't get terminated after two following characters and the value is invalid.
// This is the OSC command to set the title and the icon text. It can be up to 255 characters
// but some may be cut off.
serial_write("\x1b" "]0;");
serial_write("🐍 ");
#if CIRCUITPY_WEB_WORKFLOW
supervisor_web_workflow_status();
#endif
// Send string terminator
serial_write("\x1b" "\\");
}
static void workflow_background(void *data) {
supervisor_workflow_update_status_bar();
}
// Called during a VM reset. Doesn't actually reset things.
void supervisor_workflow_reset(void) {
#if CIRCUITPY_BLEIO
supervisor_start_bluetooth();
#endif
#if CIRCUITPY_WEB_WORKFLOW
supervisor_start_web_workflow();
#endif
workflow_background_cb.fun = workflow_background;
workflow_background_cb.data = NULL;
supervisor_workflow_request_background();
}
void supervisor_workflow_request_background(void) {
background_callback_add_core(&workflow_background_cb);
}
// Return true as soon as USB communication with host has started,
@ -58,3 +105,25 @@ bool supervisor_workflow_active(void) {
return false;
#endif
}
void supervisor_workflow_start(void) {
// Start USB after giving boot.py a chance to tweak behavior.
#if CIRCUITPY_USB
// Setup USB connection after heap is available.
// It needs the heap to build descriptors.
usb_init();
#endif
// Set up any other serial connection.
serial_init();
#if CIRCUITPY_BLEIO
bleio_reset();
supervisor_bluetooth_enable_workflow();
supervisor_start_bluetooth();
#endif
#if CIRCUITPY_WEB_WORKFLOW
supervisor_start_web_workflow();
#endif
}

View File

@ -146,6 +146,10 @@ ifeq ($(CIRCUITPY_USB),1)
endif
endif
ifeq ($(CIRCUITPY_WEB_WORKFLOW),1)
SRC_SUPERVISOR += supervisor/shared/web_workflow/web_workflow.c
endif
SRC_TINYUSB = $(filter lib/tinyusb/%.c, $(SRC_SUPERVISOR))
$(patsubst %.c,$(BUILD)/%.o,$(SRC_TINYUSB)): CFLAGS += -Wno-missing-prototypes

View File

@ -30,3 +30,7 @@ void supervisor_workflow_reset(void);
// True when the user could be actively iterating on their code.
bool supervisor_workflow_active(void);
void supervisor_workflow_request_background(void);
void supervisor_workflow_start(void);

View File

@ -139,11 +139,41 @@ displayio_palette_t supervisor_terminal_color = {
c_file.write(
"""\
displayio_tilegrid_t supervisor_terminal_text_grid = {{
displayio_tilegrid_t supervisor_terminal_scroll_area_text_grid = {{
.base = {{ .type = &displayio_tilegrid_type }},
.bitmap = (displayio_bitmap_t*) &supervisor_terminal_font_bitmap,
.pixel_shader = &supervisor_terminal_color,
.x = 16,
.x = 0,
.y = 0,
.pixel_width = {1},
.pixel_height = {2},
.bitmap_width_in_tiles = {0},
.tiles_in_bitmap = {0},
.width_in_tiles = 1,
.height_in_tiles = 1,
.tile_width = {1},
.tile_height = {2},
.tiles = NULL,
.partial_change = false,
.full_change = false,
.hidden = false,
.hidden_by_parent = false,
.moved = false,
.inline_tiles = false,
.in_group = true
}};
""".format(
len(all_characters), tile_x, tile_y
)
)
c_file.write(
"""\
displayio_tilegrid_t supervisor_terminal_title_bar_text_grid = {{
.base = {{ .type = &displayio_tilegrid_type }},
.bitmap = (displayio_bitmap_t*) &supervisor_terminal_font_bitmap,
.pixel_shader = &supervisor_terminal_color,
.x = 0,
.y = 0,
.pixel_width = {1},
.pixel_height = {2},
@ -228,7 +258,8 @@ terminalio_terminal_obj_t supervisor_terminal = {
.font = &supervisor_terminal_font,
.cursor_x = 0,
.cursor_y = 0,
.tilegrid = NULL
.scroll_area = NULL,
.title_bar = NULL
};
"""
)