From 52080e24ebf80f4e91bd0ec1177ceadc4505dffb Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 28 Aug 2022 21:55:13 -0400 Subject: [PATCH] status bar control --- main.c | 52 ++++++-- ports/espressif/common-hal/wifi/__init__.c | 9 +- py/circuitpy_defns.mk | 36 ++--- shared-bindings/supervisor/StatusBar.c | 126 ++++++++++++++++++ shared-bindings/supervisor/StatusBar.h | 42 ++++++ shared-bindings/supervisor/__init__.c | 10 ++ shared-bindings/supervisor/__init__.h | 2 + shared-bindings/terminalio/Terminal.c | 14 +- shared-bindings/terminalio/Terminal.h | 2 +- shared-module/supervisor/StatusBar.c | 74 ++++++++++ shared-module/supervisor/StatusBar.h | 42 ++++++ shared-module/supervisor/__init__.c | 39 ++++++ shared-module/terminalio/Terminal.c | 59 +++++--- shared-module/terminalio/Terminal.h | 8 +- supervisor/shared/bluetooth/bluetooth.c | 22 ++- supervisor/shared/display.c | 55 ++++---- supervisor/shared/display.h | 2 +- supervisor/shared/serial.c | 16 +++ .../shared/{title_bar.c => status_bar.c} | 73 +++++----- .../shared/{title_bar.h => status_bar.h} | 12 +- supervisor/shared/usb/usb.c | 9 +- supervisor/shared/web_workflow/web_workflow.c | 8 +- supervisor/shared/web_workflow/websocket.c | 9 +- supervisor/supervisor.mk | 7 +- tools/gen_display_resources.py | 4 +- 25 files changed, 597 insertions(+), 135 deletions(-) create mode 100644 shared-bindings/supervisor/StatusBar.c create mode 100644 shared-bindings/supervisor/StatusBar.h create mode 100644 shared-module/supervisor/StatusBar.c create mode 100644 shared-module/supervisor/StatusBar.h create mode 100644 shared-module/supervisor/__init__.c rename supervisor/shared/{title_bar.c => status_bar.c} (66%) rename supervisor/shared/{title_bar.h => status_bar.h} (85%) diff --git a/main.c b/main.c index de2a28271c..4c3bae6948 100644 --- a/main.c +++ b/main.c @@ -57,7 +57,6 @@ #include "supervisor/shared/stack.h" #include "supervisor/shared/status_leds.h" #include "supervisor/shared/tick.h" -#include "supervisor/shared/title_bar.h" #include "supervisor/shared/traceback.h" #include "supervisor/shared/translate/translate.h" #include "supervisor/shared/workflow.h" @@ -106,6 +105,10 @@ #include "shared-bindings/socketpool/__init__.h" #endif +#if CIRCUITPY_STATUS_BAR +#include "supervisor/shared/status_bar.h" +#endif + #if CIRCUITPY_USB_HID #include "shared-module/usb_hid/__init__.h" #endif @@ -208,6 +211,7 @@ STATIC const char *_current_executing_filename = NULL; STATIC pyexec_result_t _exec_result = {0, MP_OBJ_NULL, 0}; +#if CIRCUITPY_STATUS_BAR void supervisor_execution_status(void) { mp_obj_exception_t *exception = MP_OBJ_TO_PTR(_exec_result.exception); if (_current_executing_filename != NULL) { @@ -220,6 +224,7 @@ void supervisor_execution_status(void) { serial_write_compressed(translate("Done")); } } +#endif #define STRING_LIST(...) {__VA_ARGS__, ""} @@ -245,13 +250,23 @@ STATIC bool maybe_run_list(const char *const *filenames) { } mp_hal_stdout_tx_str(_current_executing_filename); serial_write_compressed(translate(" output:\n")); - supervisor_title_bar_update(); + + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_update(); + #endif + pyexec_file(_current_executing_filename, &_exec_result); + #if CIRCUITPY_ATEXIT shared_module_atexit_execute(&_exec_result); #endif + _current_executing_filename = NULL; - supervisor_title_bar_update(); + + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_update(); + #endif + return true; } @@ -749,8 +764,10 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { if (ok_to_run) { #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - // Turn off title bar updates when writing out to boot_out.txt. - supervisor_title_bar_suspend(); + #if CIRCUITPY_STATUS_BAR + // Turn off status bar updates when writing out to boot_out.txt. + supervisor_status_bar_suspend(); + #endif vstr_t boot_text; vstr_init(&boot_text, 512); boot_output = &boot_text; @@ -778,7 +795,9 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { FATFS *fs = &vfs->fatfs; boot_output = NULL; - supervisor_title_bar_resume(); + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_resume(); + #endif bool write_boot_output = true; FIL boot_output_file; if (f_open(fs, &boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) { @@ -854,15 +873,23 @@ STATIC int run_repl(bool first_run) { status_led_deinit(); #endif if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { - supervisor_title_bar_suspend(); + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_suspend(); + #endif exit_code = pyexec_raw_repl(); - supervisor_title_bar_resume(); + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_resume(); + #endif } else { _current_executing_filename = "REPL"; - supervisor_title_bar_update(); + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_update(); + #endif exit_code = pyexec_friendly_repl(); _current_executing_filename = NULL; - supervisor_title_bar_update(); + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_update(); + #endif } #if CIRCUITPY_ATEXIT pyexec_result_t result; @@ -959,7 +986,10 @@ int __attribute__((used)) main(void) { run_boot_py(safe_mode); supervisor_workflow_start(); - supervisor_title_bar_start(); + + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_start(); + #endif // Boot script is finished, so now go into REPL or run code.py. int exit_code = PYEXEC_FORCED_EXIT; diff --git a/ports/espressif/common-hal/wifi/__init__.c b/ports/espressif/common-hal/wifi/__init__.c index ca0533525d..42ea078810 100644 --- a/ports/espressif/common-hal/wifi/__init__.c +++ b/ports/espressif/common-hal/wifi/__init__.c @@ -44,9 +44,12 @@ wifi_radio_obj_t common_hal_wifi_radio_obj; #include "components/log/include/esp_log.h" #include "supervisor/port.h" -#include "supervisor/shared/title_bar.h" #include "supervisor/workflow.h" +#if CIRCUITPY_STATUS_BAR +#include "supervisor/shared/status_bar.h" +#endif + #include "esp_ipc.h" #ifdef CONFIG_IDF_TARGET_ESP32 @@ -56,7 +59,9 @@ wifi_radio_obj_t common_hal_wifi_radio_obj; static const char *TAG = "CP wifi"; STATIC void schedule_background_on_cp_core(void *arg) { - supervisor_title_bar_request_update(false); + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_request_update(false); + #endif // CircuitPython's VM is run in a separate FreeRTOS task from wifi callbacks. So, we have to // notify the main task every time in case it's waiting for us. diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index f22a96853e..a9be64428e 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -101,6 +101,7 @@ endif ### # Select which builtin modules to compile and include. +# Keep alphabetical. ifeq ($(CIRCUITPY_AESIO),1) SRC_PATTERNS += aesio/% @@ -175,11 +176,8 @@ endif ifeq ($(CIRCUITPY_DOTENV),1) SRC_PATTERNS += dotenv/% endif -ifeq ($(CIRCUITPY_PARALLELDISPLAY),1) -SRC_PATTERNS += paralleldisplay/% -endif -ifeq ($(CIRCUITPY_VECTORIO),1) -SRC_PATTERNS += vectorio/% +ifeq ($(CIRCUITPY__EVE),1) +SRC_PATTERNS += _eve/% endif ifeq ($(CIRCUITPY_FLOPPYIO),1) SRC_PATTERNS += floppyio/% @@ -187,17 +185,12 @@ endif ifeq ($(CIRCUITPY_FRAMEBUFFERIO),1) SRC_PATTERNS += framebufferio/% endif -ifeq ($(CIRCUITPY__EVE),1) -SRC_PATTERNS += _eve/% -endif ifeq ($(CIRCUITPY_FREQUENCYIO),1) SRC_PATTERNS += frequencyio/% endif - ifeq ($(CIRCUITPY_FUTURE),1) SRC_PATTERNS += __future__/% endif - ifeq ($(CIRCUITPY_GETPASS),1) SRC_PATTERNS += getpass/% endif @@ -237,6 +230,9 @@ endif ifeq ($(CIRCUITPY_MDNS),1) SRC_PATTERNS += mdns/% endif +ifeq ($(CIRCUITPY_MSGPACK),1) +SRC_PATTERNS += msgpack/% +endif ifeq ($(CIRCUITPY_NEOPIXEL_WRITE),1) SRC_PATTERNS += neopixel_write/% endif @@ -252,6 +248,12 @@ endif ifeq ($(CIRCUITPY_DUALBANK),1) SRC_PATTERNS += dualbank/% endif +ifeq ($(CIRCUITPY_PARALLELDISPLAY),1) +SRC_PATTERNS += paralleldisplay/% +endif +ifeq ($(CIRCUITPY_PEW),1) +SRC_PATTERNS += _pew/% +endif ifeq ($(CIRCUITPY_PIXELBUF),1) SRC_PATTERNS += adafruit_pixelbuf/% endif @@ -351,8 +353,8 @@ endif ifeq ($(CIRCUITPY_USTACK),1) SRC_PATTERNS += ustack/% endif -ifeq ($(CIRCUITPY_ZLIB),1) -SRC_PATTERNS += zlib/% +ifeq ($(CIRCUITPY_VECTORIO),1) +SRC_PATTERNS += vectorio/% endif ifeq ($(CIRCUITPY_VIDEOCORE),1) SRC_PATTERNS += videocore/% @@ -363,11 +365,8 @@ endif ifeq ($(CIRCUITPY_WIFI),1) SRC_PATTERNS += wifi/% endif -ifeq ($(CIRCUITPY_PEW),1) -SRC_PATTERNS += _pew/% -endif -ifeq ($(CIRCUITPY_MSGPACK),1) -SRC_PATTERNS += msgpack/% +ifeq ($(CIRCUITPY_ZLIB),1) +SRC_PATTERNS += zlib/% endif # All possible sources are listed here, and are filtered by SRC_PATTERNS in SRC_COMMON_HAL @@ -511,6 +510,7 @@ $(filter $(SRC_PATTERNS), \ qrio/PixelPolicy.c \ qrio/QRInfo.c \ supervisor/RunReason.c \ + supervisor/StatusBar.c \ wifi/AuthMode.c \ wifi/Packet.c \ ) @@ -611,6 +611,8 @@ SRC_SHARED_MODULE_ALL = \ socket/__init__.c \ storage/__init__.c \ struct/__init__.c \ + supervisor/__init__.c \ + supervisor/StatusBar.c \ synthio/MidiTrack.c \ synthio/__init__.c \ terminalio/Terminal.c \ diff --git a/shared-bindings/supervisor/StatusBar.c b/shared-bindings/supervisor/StatusBar.c new file mode 100644 index 0000000000..3ee9ae9de4 --- /dev/null +++ b/shared-bindings/supervisor/StatusBar.c @@ -0,0 +1,126 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 by Dan Halbert 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 +#include "py/obj.h" +#include "py/enum.h" +#include "py/runtime.h" +#include "py/objproperty.h" +#include "shared-bindings/supervisor/StatusBar.h" + +//| class StatusBar: +//| """Current status of runtime objects. +//| +//| Usage:: +//| +//| import supervisor +//| +//| supervisor.status_bar.console = False +//| """ +//| + +//| def __init__(self) -> None: +//| """You cannot create an instance of `supervisor.StatusBar`. +//| Use `supervisor.status_bar` to access the sole instance available.""" +//| ... +//| + +//| console: bool +//| """Whether status bar information is sent over the console (REPL) serial connection, +//| using OSC terminal escape codes that change the terminal's title. Default is ``True``. +//| If set to ``False``, status bar will be cleared and then disabled. +//| May be set in ``boot.py`` or later. +//| """ +//| +STATIC mp_obj_t supervisor_status_bar_get_console(mp_obj_t self_in) { + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(shared_module_supervisor_status_bar_get_console(self)); + #else + mp_raise_NotImplementedError(NULL); + #endif +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_status_bar_get_console_obj, supervisor_status_bar_get_console); + +STATIC mp_obj_t supervisor_status_bar_set_console(mp_obj_t self_in, mp_obj_t state_in) { + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_obj_t *self = MP_OBJ_TO_PTR(self_in); + shared_module_supervisor_status_bar_set_console(self, mp_obj_is_true(state_in)); + return mp_const_none; + #else + mp_raise_NotImplementedError(NULL); + #endif +} +MP_DEFINE_CONST_FUN_OBJ_2(supervisor_status_bar_set_console_obj, supervisor_status_bar_set_console); + +MP_PROPERTY_GETSET(supervisor_status_bar_console_obj, + (mp_obj_t)&supervisor_status_bar_get_console_obj, + (mp_obj_t)&supervisor_status_bar_set_console_obj); + +//| display: bool +//| """Whether status bar information is displayed on the top line of the display. +//| Default is ``True``. If set to ``False``, status bar will be cleared and then disabled. +//| May be set in ``boot.py`` or later. Not available if `terminalio` is not available. +//| """ +//| +STATIC mp_obj_t supervisor_status_bar_get_display(mp_obj_t self_in) { + #if CIRCUITPY_STATUS_BAR && CIRCUITPY_TERMINALIO + supervisor_status_bar_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(shared_module_supervisor_status_bar_get_display(self)); + #else + mp_raise_NotImplementedError(NULL); + #endif +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_status_bar_get_display_obj, supervisor_status_bar_get_display); + +STATIC mp_obj_t supervisor_status_bar_set_display(mp_obj_t self_in, mp_obj_t state_in) { + #if CIRCUITPY_STATUS_BAR && CIRCUITPY_TERMINALIO + supervisor_status_bar_obj_t *self = MP_OBJ_TO_PTR(self_in); + shared_module_supervisor_status_bar_set_display(self, mp_obj_is_true(state_in)); + return mp_const_none; + #else + mp_raise_NotImplementedError(NULL); + #endif +} +MP_DEFINE_CONST_FUN_OBJ_2(supervisor_status_bar_set_display_obj, supervisor_status_bar_set_display); + +MP_PROPERTY_GETSET(supervisor_status_bar_display_obj, + (mp_obj_t)&supervisor_status_bar_get_display_obj, + (mp_obj_t)&supervisor_status_bar_set_display_obj); + + +STATIC const mp_rom_map_elem_t supervisor_status_bar_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_console), MP_ROM_PTR(&supervisor_status_bar_console_obj) }, + { MP_ROM_QSTR(MP_QSTR_display), MP_ROM_PTR(&supervisor_status_bar_display_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(supervisor_status_bar_locals_dict, supervisor_status_bar_locals_dict_table); + +const mp_obj_type_t supervisor_status_bar_type = { + .base = { &mp_type_type }, + .name = MP_QSTR_Status_Bar, + .locals_dict = (mp_obj_dict_t *)&supervisor_status_bar_locals_dict, +}; diff --git a/shared-bindings/supervisor/StatusBar.h b/shared-bindings/supervisor/StatusBar.h new file mode 100644 index 0000000000..12b337ea84 --- /dev/null +++ b/shared-bindings/supervisor/StatusBar.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 by Dan Halbert 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_STATUS_BAR_STATUS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_STATUS_BAR_STATUS_H + +#include +#include "py/obj.h" +#include "shared-module/supervisor/StatusBar.h" + +extern const mp_obj_type_t supervisor_status_bar_type; + +bool shared_module_supervisor_status_bar_get_console(supervisor_status_bar_obj_t *self); +void shared_module_supervisor_status_bar_set_console(supervisor_status_bar_obj_t *self, bool enabled); + +bool shared_module_supervisor_status_bar_get_display(supervisor_status_bar_obj_t *self); +void shared_module_supervisor_status_bar_set_display(supervisor_status_bar_obj_t *self, bool enabled); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SUPERVISOR_STATUS_BAR_H diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index c74b3751c0..01ac4984f1 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -43,6 +43,7 @@ #include "shared-bindings/supervisor/__init__.h" #include "shared-bindings/time/__init__.h" #include "shared-bindings/supervisor/Runtime.h" +#include "shared-bindings/supervisor/StatusBar.h" //| """Supervisor settings""" //| @@ -53,6 +54,14 @@ //| This object is the sole instance of `supervisor.Runtime`.""" //| +//| status_bar: StatusBar +//| """The status bar, shown on an attached display, and also sent to +//| an attached terminal via OSC escape codes over the REPL serial connection. +//| The status bar reports the current IP or BLE connection, what file is running, +//| the last exception name and location, and firmware version information. +//| This object is the sole instance of `supervisor.StatusBar`.""" +//| + //| def set_rgb_status_brightness(brightness: int) -> None: //| """Set brightness of status RGB LED from 0-255. This will take effect //| after the current code finishes and the status LED is used to show @@ -301,6 +310,7 @@ STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_get_previous_traceback), MP_ROM_PTR(&supervisor_get_previous_traceback_obj) }, { MP_ROM_QSTR(MP_QSTR_disable_ble_workflow), MP_ROM_PTR(&supervisor_disable_ble_workflow_obj) }, { MP_ROM_QSTR(MP_QSTR_reset_terminal), MP_ROM_PTR(&supervisor_reset_terminal_obj) }, + { MP_ROM_QSTR(MP_QSTR_status_bar), MP_ROM_PTR(&shared_module_supervisor_status_bar_obj) }, }; STATIC MP_DEFINE_CONST_DICT(supervisor_module_globals, supervisor_module_globals_table); diff --git a/shared-bindings/supervisor/__init__.h b/shared-bindings/supervisor/__init__.h index 40a1e73932..4b4dc4fc57 100644 --- a/shared-bindings/supervisor/__init__.h +++ b/shared-bindings/supervisor/__init__.h @@ -31,8 +31,10 @@ #include "py/obj.h" #include "common-hal/supervisor/Runtime.h" +#include "shared-module/supervisor/StatusBar.h" extern const super_runtime_obj_t common_hal_supervisor_runtime_obj; +extern supervisor_status_bar_obj_t shared_module_supervisor_status_bar_obj; extern mp_obj_t supervisor_ticks_ms(void); diff --git a/shared-bindings/terminalio/Terminal.c b/shared-bindings/terminalio/Terminal.c index 605ee2727f..948741a4f0 100644 --- a/shared-bindings/terminalio/Terminal.c +++ b/shared-bindings/terminalio/Terminal.c @@ -56,26 +56,26 @@ //| * ``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: +//| def __init__(self, scroll_area: displayio.TileGrid, font: fontio.BuiltinFont, *, status_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_scroll_area, ARG_font, ARG_title_bar }; + enum { ARG_scroll_area, ARG_font, ARG_status_bar }; static const mp_arg_t allowed_args[] = { { 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_QSTR_status_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 *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); + displayio_tilegrid_t *status_bar = NULL; + if (args[ARG_status_bar].u_obj != mp_const_none) { + status_bar = mp_arg_validate_type(args[ARG_status_bar].u_obj, &displayio_tilegrid_type, MP_QSTR_status_bar); } fontio_builtinfont_t *font = mp_arg_validate_type(args[ARG_font].u_obj, &fontio_builtinfont_type, MP_QSTR_font); @@ -83,7 +83,7 @@ STATIC mp_obj_t terminalio_terminal_make_new(const mp_obj_type_t *type, size_t n terminalio_terminal_obj_t *self = m_new_obj(terminalio_terminal_obj_t); self->base.type = &terminalio_terminal_type; - common_hal_terminalio_terminal_construct(self, scroll_area, font, title_bar); + common_hal_terminalio_terminal_construct(self, scroll_area, font, status_bar); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/terminalio/Terminal.h b/shared-bindings/terminalio/Terminal.h index 64609b49b1..fda1c29bdd 100644 --- a/shared-bindings/terminalio/Terminal.h +++ b/shared-bindings/terminalio/Terminal.h @@ -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 *scroll_area, const fontio_builtinfont_t *font, displayio_tilegrid_t *title_bar); + displayio_tilegrid_t *scroll_area, const fontio_builtinfont_t *font, displayio_tilegrid_t *status_bar); // Write characters. len is in characters NOT bytes! extern size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, diff --git a/shared-module/supervisor/StatusBar.c b/shared-module/supervisor/StatusBar.c new file mode 100644 index 0000000000..cb0882fd44 --- /dev/null +++ b/shared-module/supervisor/StatusBar.c @@ -0,0 +1,74 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Dan Halbert 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 +#include "py/obj.h" +#include "shared-bindings/supervisor/StatusBar.h" +#include "shared-bindings/supervisor/__init__.h" +#include "supervisor/shared/display.h" +#include "supervisor/shared/status_bar.h" + +#if CIRCUITPY_TERMINALIO +#include "shared-module/terminalio/Terminal.h" +#endif + +bool shared_module_supervisor_status_bar_get_console(supervisor_status_bar_obj_t *self) { + return self->console; +} + +void shared_module_supervisor_status_bar_set_console(supervisor_status_bar_obj_t *self, bool enabled) { + // Clear before changing state. If disabling, will remain cleared. + supervisor_status_bar_clear(); + + self->console = enabled; + + // Update may be ignored. + supervisor_status_bar_update(); +} + +bool shared_module_supervisor_status_bar_get_display(supervisor_status_bar_obj_t *self) { + return self->display; +} + +#if CIRCUITPY_TERMINALIO +void shared_module_supervisor_status_bar_set_display(supervisor_status_bar_obj_t *self, bool enabled) { + terminalio_terminal_clear_status_bar(&supervisor_terminal); + // Clear before changing state. If disabling, will remain cleared. + + self->display = enabled; + + // Update may be ignored. + supervisor_status_bar_update(); +} +#endif + +bool supervisor_status_bar_get_update_in_progress(supervisor_status_bar_obj_t *self) { + return self->update_in_progress; +} + +void supervisor_status_bar_set_update_in_progress(supervisor_status_bar_obj_t *self, bool update_in_progress) { + self->update_in_progress = update_in_progress; +} diff --git a/shared-module/supervisor/StatusBar.h b/shared-module/supervisor/StatusBar.h new file mode 100644 index 0000000000..88bf965864 --- /dev/null +++ b/shared-module/supervisor/StatusBar.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Dan Halbert 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_SUPERVISOR_STATUS_BAR_H +#define MICROPY_INCLUDED_SHARED_MODULE_SUPERVISOR_STATUS_BAR_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + bool console; + bool display; + bool update_in_progress; +} supervisor_status_bar_obj_t; + +extern bool supervisor_status_bar_get_update_in_progress(supervisor_status_bar_obj_t *self); +extern void supervisor_status_bar_set_update_in_progress(supervisor_status_bar_obj_t *self, bool in_progress); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_SUPERVISOR_STATUS_BAR_H diff --git a/shared-module/supervisor/__init__.c b/shared-module/supervisor/__init__.c new file mode 100644 index 0000000000..7921eec46a --- /dev/null +++ b/shared-module/supervisor/__init__.c @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Dan Halbert 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 +#include "py/obj.h" +#include "shared-bindings/supervisor/StatusBar.h" +#include "shared-bindings/supervisor/__init__.h" + +// The singleton supervisor.StatusBar object, bound to supervisor.status_bar +supervisor_status_bar_obj_t shared_module_supervisor_status_bar_obj = { + .base = { + .type = &supervisor_status_bar_type, + }, + .console = true, + .display = true, +}; diff --git a/shared-module/terminalio/Terminal.c b/shared-module/terminalio/Terminal.c index d45495d14a..b49f48c4e6 100644 --- a/shared-module/terminalio/Terminal.c +++ b/shared-module/terminalio/Terminal.c @@ -30,20 +30,31 @@ #include "shared-bindings/displayio/TileGrid.h" #include "shared-bindings/terminalio/Terminal.h" +#if CIRCUITPY_STATUS_BAR +#include "shared-bindings/supervisor/__init__.h" +#include "shared-bindings/supervisor/StatusBar.h" +#endif + +void terminalio_terminal_clear_status_bar(terminalio_terminal_obj_t *self) { + if (self->status_bar) { + common_hal_displayio_tilegrid_set_all_tiles(self->status_bar, 0); + } +} + 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) { + displayio_tilegrid_t *status_bar) { self->cursor_x = 0; self->cursor_y = 0; self->font = font; self->scroll_area = scroll_area; - self->title_bar = title_bar; - self->title_x = 0; - self->title_y = 0; + self->status_bar = status_bar; + self->status_x = 0; + self->status_y = 0; self->first_row = 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); + if (self->status_bar) { + common_hal_displayio_tilegrid_set_all_tiles(self->status_bar, 0); } common_hal_displayio_tilegrid_set_top_left(self->scroll_area, 0, 1); @@ -54,6 +65,14 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con if (self->scroll_area == NULL) { return len; } + + #if CIRCUITPY_STATUS_BAR + // Skip the status bar OSC sequence if it's disabled for the display. + const bool status_bar_write_ok = + shared_module_supervisor_status_bar_get_display(&shared_module_supervisor_status_bar_obj) || + !supervisor_status_bar_get_update_in_progress(&shared_module_supervisor_status_bar_obj); + #endif + const byte *i = data; uint16_t start_y = self->cursor_y; while (i < data + len) { @@ -62,21 +81,27 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con if (self->in_osc_command) { if (c == 0x1b && i[0] == '\\') { self->in_osc_command = false; - self->title_x = 0; - self->title_y = 0; + self->status_x = 0; + self->status_y = 0; i += 1; - } else if (self->osc_command == 0 && self->title_bar != NULL && self->title_y < self->title_bar->height_in_tiles) { + } else if ( + #if CIRCUITPY_STATUS_BAR + status_bar_write_ok && + #endif + self->osc_command == 0 && + self->status_bar != NULL && + self->status_y < self->status_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); + if (self->status_x == 0 && self->status_y == 0) { + common_hal_displayio_tilegrid_set_all_tiles(self->status_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; + common_hal_displayio_tilegrid_set_tile(self->status_bar, self->status_x, self->status_y, tile_index); + self->status_x++; + if (self->status_x >= self->status_bar->width_in_tiles) { + self->status_y++; + self->status_x %= self->status_bar->width_in_tiles; } } } @@ -98,7 +123,7 @@ 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. + // Handle commands of the form [ESC]. where . is not yet known. uint16_t n = 0; uint8_t j = 1; for (; j < 6; j++) { diff --git a/shared-module/terminalio/Terminal.h b/shared-module/terminalio/Terminal.h index 2e628f5df6..7f0545c6be 100644 --- a/shared-module/terminalio/Terminal.h +++ b/shared-module/terminalio/Terminal.h @@ -40,12 +40,14 @@ typedef struct { uint16_t cursor_x; uint16_t cursor_y; displayio_tilegrid_t *scroll_area; - displayio_tilegrid_t *title_bar; - uint16_t title_x; - uint16_t title_y; + displayio_tilegrid_t *status_bar; + uint16_t status_x; + uint16_t status_y; uint16_t first_row; uint16_t osc_command; bool in_osc_command; } terminalio_terminal_obj_t; +extern void terminalio_terminal_clear_status_bar(terminalio_terminal_obj_t *self); + #endif /* SHARED_MODULE_TERMINALIO_TERMINAL_H */ diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c index a007219e4d..472d2fa7d5 100644 --- a/supervisor/shared/bluetooth/bluetooth.c +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -42,7 +42,6 @@ #include "supervisor/serial.h" #include "supervisor/shared/status_leds.h" #include "supervisor/shared/tick.h" -#include "supervisor/shared/title_bar.h" #include "supervisor/shared/translate/translate.h" #include "py/mpstate.h" @@ -57,6 +56,10 @@ #include "bluetooth/ble_drv.h" #endif +#if CIRCUITPY_STATUS_BAR +#include "supervisor/shared/status_bar.h" +#endif + // This standard advertisement advertises the CircuitPython editing service and a CIRCUITPY short name. const uint8_t public_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags 0x02, 0x0a, 0xec, // 3-5 TX power level -20 @@ -94,16 +97,21 @@ STATIC bool ble_started = false; STATIC uint8_t workflow_state = WORKFLOW_UNSET; STATIC bool was_connected = false; +#if CIRCUITPY_STATUS_BAR // To detect when the title bar changes. STATIC bool _last_connected = false; STATIC bool _last_advertising = false; +#endif +#if CIRCUITPY_STATUS_BAR // Title bar status bool supervisor_bluetooth_status_dirty(void) { return _last_advertising != advertising || _last_connected != was_connected; } +#endif +#if CIRCUITPY_STATUS_BAR void supervisor_bluetooth_status(void) { serial_write("BLE:"); if (advertising) { @@ -123,6 +131,7 @@ void supervisor_bluetooth_status(void) { _last_connected = was_connected; _last_advertising = advertising; } +#endif STATIC void supervisor_bluetooth_start_advertising(void) { if (workflow_state != WORKFLOW_ENABLED) { @@ -273,9 +282,13 @@ void supervisor_bluetooth_background(void) { supervisor_bluetooth_file_transfer_disconnected(); #endif } + + #if CIRCUITPY_STATUS_BAR if (was_connected != is_connected) { - supervisor_title_bar_request_update(false); + supervisor_status_bar_request_update(false); } + #endif + was_connected = is_connected; if (!is_connected) { supervisor_bluetooth_start_advertising(); @@ -312,7 +325,10 @@ void supervisor_start_bluetooth(void) { // Kick off advertisements supervisor_bluetooth_background(); - supervisor_title_bar_request_update(false); + + #if CIRCUITPY_STATUS_BAR + supervisor_status_bar_request_update(false); + #endif #endif } diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c index 336819e10c..f573a92271 100644 --- a/supervisor/shared/display.c +++ b/supervisor/shared/display.c @@ -34,7 +34,6 @@ #include "shared-bindings/displayio/Palette.h" #include "shared-bindings/displayio/TileGrid.h" #include "supervisor/memory.h" -#include "supervisor/shared/title_bar.h" #if CIRCUITPY_RGBMATRIX #include "shared-module/displayio/__init__.h" @@ -46,6 +45,10 @@ #include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h" #endif +#if CIRCUITPY_STATUS_BAR +#include "supervisor/shared/status_bar.h" +#endif + #if CIRCUITPY_REPL_LOGO extern uint32_t blinka_bitmap_data[]; extern displayio_bitmap_t blinka_bitmap; @@ -63,7 +66,7 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { #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; + displayio_tilegrid_t *status_bar = &supervisor_terminal_status_bar_text_grid; bool reset_tiles = false; uint16_t width_in_tiles = width_px / scroll_area->tile_width; // determine scale based on width @@ -104,21 +107,21 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { uint8_t *tiles = (uint8_t *)tilegrid_tiles->ptr; #if CIRCUITPY_REPL_LOGO - title_bar->x = supervisor_blinka_sprite.pixel_width + 1; - // Align the title bar to the bottom of the logo. - title_bar->y = supervisor_blinka_sprite.pixel_height - title_bar->tile_height; + status_bar->x = supervisor_blinka_sprite.pixel_width + 1; + // Align the status bar to the bottom of the logo. + status_bar->y = supervisor_blinka_sprite.pixel_height - status_bar->tile_height; #else - title_bar->x = 0; - title_bar->y = 0; + status_bar->x = 0; + status_bar->y = 0; #endif - title_bar->top_left_y = 0; - title_bar->width_in_tiles = width_in_tiles; - title_bar->height_in_tiles = 1; + status_bar->top_left_y = 0; + status_bar->width_in_tiles = width_in_tiles; + status_bar->height_in_tiles = 1; assert(width_in_tiles > 0); - 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; + status_bar->pixel_width = width_in_tiles * status_bar->tile_width; + status_bar->pixel_height = status_bar->tile_height; + status_bar->tiles = tiles; + status_bar->full_change = true; scroll_area->x = 0; scroll_area->top_left_y = 0; @@ -131,7 +134,7 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { #if CIRCUITPY_REPL_LOGO scroll_area->y = blinka_bitmap.height; #else - scroll_area->y = title_bar->tile_height; + scroll_area->y = status_bar->tile_height; #endif int16_t extra_height = (scroll_area->pixel_height + scroll_area->y) - (height_px / scale); // Subtract extra height so that the bottom line fully shows. The top line will be under the @@ -140,9 +143,11 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { scroll_area->tiles = tiles + width_in_tiles; scroll_area->full_change = true; - common_hal_terminalio_terminal_construct(&supervisor_terminal, scroll_area, &supervisor_terminal_font, title_bar); - // Update the title bar since we just cleared the terminal. - supervisor_title_bar_update(); + common_hal_terminalio_terminal_construct(&supervisor_terminal, scroll_area, &supervisor_terminal_font, status_bar); + #if CIRCUITPY_STATUS_BAR + // Update the status bar since we just cleared the terminal. + supervisor_status_bar_update(); + #endif } #endif @@ -155,9 +160,9 @@ void supervisor_stop_terminal(void) { free_memory(tilegrid_tiles); tilegrid_tiles = NULL; supervisor_terminal_scroll_area_text_grid.tiles = NULL; - supervisor_terminal_title_bar_text_grid.tiles = NULL; + supervisor_terminal_status_bar_text_grid.tiles = NULL; supervisor_terminal.scroll_area = NULL; - supervisor_terminal.title_bar = NULL; + supervisor_terminal.status_bar = NULL; } #endif } @@ -165,13 +170,13 @@ void supervisor_stop_terminal(void) { 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; + displayio_tilegrid_t *status_bar = &supervisor_terminal_status_bar_text_grid; if (tilegrid_tiles != NULL) { - title_bar->tiles = (uint8_t *)tilegrid_tiles->ptr; + status_bar->tiles = (uint8_t *)tilegrid_tiles->ptr; scroll_area->tiles = (uint8_t *)tilegrid_tiles->ptr + scroll_area->width_in_tiles; } else { scroll_area->tiles = NULL; - title_bar->tiles = NULL; + status_bar->tiles = NULL; } #endif @@ -195,7 +200,7 @@ void supervisor_display_move_memory(void) { #if CIRCUITPY_TERMINALIO #if CIRCUITPY_REPL_LOGO -mp_obj_t members[] = { &supervisor_terminal_scroll_area_text_grid, &supervisor_blinka_sprite, &supervisor_terminal_title_bar_text_grid, }; +mp_obj_t members[] = { &supervisor_terminal_scroll_area_text_grid, &supervisor_blinka_sprite, &supervisor_terminal_status_bar_text_grid, }; mp_obj_list_t splash_children = { .base = {.type = &mp_type_list }, .alloc = 3, @@ -203,7 +208,7 @@ mp_obj_list_t splash_children = { .items = members, }; #else -mp_obj_t members[] = { &supervisor_terminal_scroll_area_text_grid, &supervisor_terminal_title_bar_text_grid,}; +mp_obj_t members[] = { &supervisor_terminal_scroll_area_text_grid, &supervisor_terminal_status_bar_text_grid,}; mp_obj_list_t splash_children = { .base = {.type = &mp_type_list }, .alloc = 2, diff --git a/supervisor/shared/display.h b/supervisor/shared/display.h index a7e28a8f75..fcaab10818 100644 --- a/supervisor/shared/display.h +++ b/supervisor/shared/display.h @@ -45,7 +45,7 @@ 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_scroll_area_text_grid; -extern displayio_tilegrid_t supervisor_terminal_title_bar_text_grid; +extern displayio_tilegrid_t supervisor_terminal_status_bar_text_grid; extern terminalio_terminal_obj_t supervisor_terminal; #endif diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index 974dd12b32..094c532781 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -41,6 +41,11 @@ #include "supervisor/shared/bluetooth/serial.h" #endif +#if CIRCUITPY_STATUS_BAR +#include "shared-bindings/supervisor/__init__.h" +#include "shared-bindings/supervisor/StatusBar.h" +#endif + #if CIRCUITPY_USB #include "tusb.h" #endif @@ -276,11 +281,22 @@ void serial_write_substring(const char *text, uint32_t length) { if (length == 0) { return; } + #if CIRCUITPY_TERMINALIO int errcode; + // We might be writing + // If the status bar is disabled for the display, common_hal_terminalio_terminal_write() will not write it. common_hal_terminalio_terminal_write(&supervisor_terminal, (const uint8_t *)text, length, &errcode); #endif + #if CIRCUITPY_STATUS_BAR + // If the status bar is disabled for the console, skip writing out the OSC sequence. + if (supervisor_status_bar_get_update_in_progress(&shared_module_supervisor_status_bar_obj) && + !shared_module_supervisor_status_bar_get_console(&shared_module_supervisor_status_bar_obj)) { + return; + } + #endif + #if CIRCUITPY_USB_VENDOR if (tud_vendor_connected()) { tud_vendor_write(text, length); diff --git a/supervisor/shared/title_bar.c b/supervisor/shared/status_bar.c similarity index 66% rename from supervisor/shared/title_bar.c rename to supervisor/shared/status_bar.c index 9326a1f9ed..2adc065e04 100644 --- a/supervisor/shared/title_bar.c +++ b/supervisor/shared/status_bar.c @@ -27,9 +27,15 @@ #include #include "genhdr/mpversion.h" #include "py/mpconfig.h" +#include "shared-bindings/supervisor/__init__.h" +#include "shared-bindings/supervisor/StatusBar.h" #include "supervisor/background_callback.h" #include "supervisor/serial.h" -#include "supervisor/shared/title_bar.h" +#include "supervisor/shared/status_bar.h" + +#if CIRCUITPY_TERMINALIO +#include "shared-module/terminalio/Terminal.h" +#endif #if CIRCUITPY_WEB_WORKFLOW #include "supervisor/shared/web_workflow/web_workflow.h" @@ -39,46 +45,56 @@ #include "supervisor/shared/bluetooth/bluetooth.h" #endif -static background_callback_t title_bar_background_cb; +static background_callback_t status_bar_background_cb; static bool _forced_dirty = false; static bool _suspended = false; +// Clear if possible, but give up if we can't do it now. +void supervisor_status_bar_clear(void) { + if (!_suspended) { + supervisor_status_bar_set_update_in_progress(&shared_module_supervisor_status_bar_obj, true); + serial_write("\x1b" "]0;" "\x1b" "\\"); + supervisor_status_bar_set_update_in_progress(&shared_module_supervisor_status_bar_obj, false); + } +} -void supervisor_title_bar_update(void) { - #if !CIRCUITPY_STATUS_BAR - return; - #endif +void supervisor_status_bar_update(void) { if (_suspended) { - supervisor_title_bar_request_update(true); + supervisor_status_bar_request_update(true); return; } _forced_dirty = false; + + supervisor_status_bar_set_update_in_progress(&shared_module_supervisor_status_bar_obj, true); + // 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(); serial_write(" | "); #endif + #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE supervisor_bluetooth_status(); serial_write(" | "); #endif + supervisor_execution_status(); serial_write(" | "); serial_write(MICROPY_GIT_TAG); // Send string terminator serial_write("\x1b" "\\"); + + supervisor_status_bar_set_update_in_progress(&shared_module_supervisor_status_bar_obj, false); } -static void title_bar_background(void *data) { - #if !CIRCUITPY_STATUS_BAR - return; - #endif +static void status_bar_background(void *data) { if (_suspended) { return; } @@ -92,42 +108,29 @@ static void title_bar_background(void *data) { dirty = dirty || supervisor_bluetooth_status_dirty(); #endif - if (!dirty) { - return; + if (dirty) { + supervisor_status_bar_update(); } - supervisor_title_bar_update(); } -void supervisor_title_bar_start(void) { - #if !CIRCUITPY_STATUS_BAR - return; - #endif - title_bar_background_cb.fun = title_bar_background; - title_bar_background_cb.data = NULL; - supervisor_title_bar_request_update(true); +void supervisor_status_bar_start(void) { + status_bar_background_cb.fun = status_bar_background; + status_bar_background_cb.data = NULL; + supervisor_status_bar_request_update(true); } -void supervisor_title_bar_request_update(bool force_dirty) { - #if !CIRCUITPY_STATUS_BAR - return; - #endif +void supervisor_status_bar_request_update(bool force_dirty) { if (force_dirty) { _forced_dirty = true; } - background_callback_add_core(&title_bar_background_cb); + background_callback_add_core(&status_bar_background_cb); } -void supervisor_title_bar_suspend(void) { - #if !CIRCUITPY_STATUS_BAR - return; - #endif +void supervisor_status_bar_suspend(void) { _suspended = true; } -void supervisor_title_bar_resume(void) { - #if !CIRCUITPY_STATUS_BAR - return; - #endif +void supervisor_status_bar_resume(void) { _suspended = false; - supervisor_title_bar_request_update(false); + supervisor_status_bar_request_update(false); } diff --git a/supervisor/shared/title_bar.h b/supervisor/shared/status_bar.h similarity index 85% rename from supervisor/shared/title_bar.h rename to supervisor/shared/status_bar.h index 092b5add32..db2c442276 100644 --- a/supervisor/shared/title_bar.h +++ b/supervisor/shared/status_bar.h @@ -28,17 +28,19 @@ #include -void supervisor_title_bar_start(void); -void supervisor_title_bar_suspend(void); -void supervisor_title_bar_resume(void); +void supervisor_status_bar_start(void); +void supervisor_status_bar_suspend(void); +void supervisor_status_bar_resume(void); + +void supervisor_status_bar_clear(void); // Update the title bar immediately. Useful from main.c where we know state has changed and the code // will only be run once. -void supervisor_title_bar_update(void); +void supervisor_status_bar_update(void); // Use this if requesting from the background, as code is executing or if the status may not have // changed. -void supervisor_title_bar_request_update(bool force_dirty); +void supervisor_status_bar_request_update(bool force_dirty); // Provided by main.c void supervisor_execution_status(void); diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index 8f571c8e10..7aae3b9b09 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -31,11 +31,14 @@ #include "supervisor/port.h" #include "supervisor/serial.h" #include "supervisor/usb.h" -#include "supervisor/shared/title_bar.h" #include "supervisor/shared/workflow.h" #include "shared/runtime/interrupt_char.h" #include "shared/readline/readline.h" +#if CIRCUITPY_STATUS_BAR +#include "supervisor/shared/status_bar.h" +#endif + #if CIRCUITPY_STORAGE #include "shared-module/storage/__init__.h" #endif @@ -244,8 +247,10 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { reset_to_bootloader(); } } else { + #if CIRCUITPY_STATUS_BAR // We are connected, let's request a title bar update. - supervisor_title_bar_request_update(true); + supervisor_status_bar_request_update(true); + #endif } } diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index 5d6429aaa3..e09c87f624 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -102,10 +102,12 @@ typedef struct { static wifi_radio_error_t _wifi_status = WIFI_RADIO_ERROR_NONE; -// Store last status state to compute dirty. +#if CIRCUITPY_STATUS_BAR +// Store various last states to compute if status bar needs an update. static bool _last_enabled = false; static uint32_t _last_ip = 0; static wifi_radio_error_t _last_wifi_status = WIFI_RADIO_ERROR_NONE; +#endif static mdns_server_obj_t mdns; static uint32_t web_api_port = 80; @@ -193,12 +195,15 @@ STATIC void _update_encoded_ip(void) { } } +#if CIRCUITPY_STATUS_BAR bool supervisor_web_workflow_status_dirty(void) { return common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj) != _last_enabled || _encoded_ip != _last_ip || _last_wifi_status != _wifi_status; } +#endif +#if CIRCUITPY_STATUS_BAR void supervisor_web_workflow_status(void) { _last_enabled = common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj); if (_last_enabled) { @@ -231,6 +236,7 @@ void supervisor_web_workflow_status(void) { serial_write_compressed(translate("off")); } } +#endif void supervisor_start_web_workflow(void) { #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI diff --git a/supervisor/shared/web_workflow/websocket.c b/supervisor/shared/web_workflow/websocket.c index 4b9e4796cd..3901c4eefe 100644 --- a/supervisor/shared/web_workflow/websocket.c +++ b/supervisor/shared/web_workflow/websocket.c @@ -29,9 +29,12 @@ #include "py/ringbuf.h" #include "py/runtime.h" #include "shared/runtime/interrupt_char.h" -#include "supervisor/shared/title_bar.h" #include "supervisor/shared/web_workflow/web_workflow.h" +#if CIRCUITPY_STATUS_BAR +#include "supervisor/shared/status_bar.h" +#endif + // TODO: Remove ESP specific stuff. For now, it is useful as we refine the server. #include "esp_log.h" @@ -73,8 +76,10 @@ void websocket_handoff(socketpool_socket_obj_t *socket) { socket->connected = false; socket->num = -1; + #if CIRCUITPY_STATUS_BAR // Send the title bar for the new client. - supervisor_title_bar_request_update(true); + supervisor_status_bar_request_update(true); + #endif } bool websocket_connected(void) { diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index cbfaae507e..a8deb9606d 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -15,7 +15,6 @@ SRC_SUPERVISOR = \ supervisor/shared/stack.c \ supervisor/shared/status_leds.c \ supervisor/shared/tick.c \ - supervisor/shared/title_bar.c \ supervisor/shared/traceback.c \ supervisor/shared/translate/translate.c \ supervisor/shared/workflow.c \ @@ -98,6 +97,12 @@ ifneq ($(wildcard supervisor/serial.c),) SRC_SUPERVISOR += supervisor/serial.c endif +ifeq ($(CIRCUITPY_STATUS_BAR),1) + SRC_SUPERVISOR += \ + supervisor/shared/status_bar.c \ + +endif + ifeq ($(CIRCUITPY_USB),1) SRC_SUPERVISOR += \ lib/tinyusb/src/class/cdc/cdc_device.c \ diff --git a/tools/gen_display_resources.py b/tools/gen_display_resources.py index 5da6062823..e5ce775e4a 100644 --- a/tools/gen_display_resources.py +++ b/tools/gen_display_resources.py @@ -324,7 +324,7 @@ displayio_tilegrid_t supervisor_terminal_scroll_area_text_grid = {{ c_file.write( """\ -displayio_tilegrid_t supervisor_terminal_title_bar_text_grid = {{ +displayio_tilegrid_t supervisor_terminal_status_bar_text_grid = {{ .base = {{ .type = &displayio_tilegrid_type }}, .bitmap = (displayio_bitmap_t*) &supervisor_terminal_font_bitmap, .pixel_shader = &supervisor_terminal_color, @@ -414,7 +414,7 @@ terminalio_terminal_obj_t supervisor_terminal = { .cursor_x = 0, .cursor_y = 0, .scroll_area = NULL, - .title_bar = NULL + .status_bar = NULL }; #endif