Start tweaking the workflow to sleep

This commit is contained in:
Scott Shawcroft 2020-10-27 17:55:03 -07:00
parent 85dadf3a56
commit 9a4efed8cb
No known key found for this signature in database
GPG Key ID: 0DFD512649C052DA
17 changed files with 282 additions and 90 deletions

49
main.c
View File

@ -88,6 +88,10 @@
#include "common-hal/canio/CAN.h"
#endif
#if CIRCUITPY_SLEEPIO
#include "shared-bindings/sleepio/__init__.h"
#endif
void do_str(const char *src, mp_parse_input_kind_t input_kind) {
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
if (lex == NULL) {
@ -303,19 +307,6 @@ bool run_code_py(safe_mode_t safe_mode) {
}
}
for (uint8_t i = 0; i<=100; i++) {
if (!usb_msc_ejected()) {
//Go into light sleep
break;
}
mp_hal_delay_ms(10);
}
if (usb_msc_ejected()) {
//Go into deep sleep
common_hal_mcu_deep_sleep();
}
// Display a different completion message if the user has no USB attached (cannot save files)
if (!serial_connected_at_start) {
serial_write_compressed(translate("\nCode done running. Waiting for reload.\n"));
@ -326,6 +317,14 @@ bool run_code_py(safe_mode_t safe_mode) {
bool refreshed_epaper_display = false;
#endif
rgb_status_animation_t animation;
bool ok = result->return_code != PYEXEC_EXCEPTION;
#if CIRCUITPY_SLEEPIO
// If USB isn't enumerated then deep sleep.
if (ok && !supervisor_workflow_active() && supervisor_ticks_ms64() > CIRCUITPY_USB_ENUMERATION_DELAY * 1024) {
common_hal_sleepio_deep_sleep();
}
#endif
// Show the animation every N seconds.
prep_rgb_status_animation(&result, found_main, safe_mode, &animation);
while (true) {
RUN_BACKGROUND_TASKS;
@ -358,8 +357,24 @@ bool run_code_py(safe_mode_t safe_mode) {
refreshed_epaper_display = maybe_refresh_epaperdisplay();
}
#endif
tick_rgb_status_animation(&animation);
bool animation_done = tick_rgb_status_animation(&animation);
if (animation_done && supervisor_workflow_active()) {
#if CIRCUITPY_SLEEPIO
int64_t remaining_enumeration_wait = CIRCUITPY_USB_ENUMERATION_DELAY * 1024 - supervisor_ticks_ms64();
// If USB isn't enumerated then deep sleep after our waiting period.
if (ok && remaining_enumeration_wait < 0) {
common_hal_sleepio_deep_sleep();
return; // Doesn't actually get here.
}
#endif
// Wake up every so often to flash the error code.
if (!ok) {
port_interrupt_after_ticks(CIRCUITPY_FLASH_ERROR_PERIOD * 1024);
} else {
port_interrupt_after_ticks(remaining_enumeration_wait);
}
port_sleep_until_interrupt();
}
}
}
@ -406,7 +421,9 @@ void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
if (!skip_boot_output) {
// Wait 1.5 seconds before opening CIRCUITPY_BOOT_OUTPUT_FILE for write,
// in case power is momentary or will fail shortly due to, say a low, battery.
mp_hal_delay_ms(1500);
if (common_hal_sleepio_get_reset_reason() == RESET_REASON_POWER_VALID) {
mp_hal_delay_ms(1500);
}
// USB isn't up, so we can write the file.
filesystem_set_internal_writable_by_usb(false);

View File

@ -1,7 +0,0 @@
#include "esp_sleep.h"
#include "shared-bindings/alarm_touch/__init__.h"
void common_hal_alarm_touch_disable (void) {
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TOUCHPAD);
}

View File

@ -1,28 +0,0 @@
#include "py/obj.h"
#include "shared-bindings/alarm_touch/__init__.h"
//| """alarm_touch module
//|
//| The `alarm_touch` module implements deep sleep."""
STATIC mp_obj_t alarm_touch_disable(void) {
common_hal_alarm_touch_disable();
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(alarm_touch_disable_obj, alarm_touch_disable);
STATIC const mp_rom_map_elem_t alarm_touch_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_alarm_touch) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_Disable), MP_ROM_PTR(&alarm_touch_disable_obj) },
};
STATIC MP_DEFINE_CONST_DICT(alarm_touch_module_globals, alarm_touch_module_globals_table);
const mp_obj_module_t alarm_touch_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&alarm_touch_module_globals,
};
const mp_obj_type_t alarm_touch_type = {
{ &mp_type_type },
.name = MP_QSTR_touchAlarm,
};

View File

@ -1,14 +0,0 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_TOUCH___INIT___H
#define MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_TOUCH___INIT___H
#include "py/runtime.h"
typedef struct {
mp_obj_base_t base;
} alarm_touch_obj_t;
extern const mp_obj_type_t alarm_touch_type;
extern void common_hal_alarm_touch_disable (void);
#endif //MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_TOUCH___INIT___H

View File

@ -0,0 +1,61 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 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 "py/enum.h"
#include "shared-bindings/sleepio/ResetReason.h"
MAKE_ENUM_VALUE(sleepio_reset_reason_type, reset_reason, POWER_VALID, RESET_REASON_POWER_VALID);
MAKE_ENUM_VALUE(sleepio_reset_reason_type, reset_reason, SOFTWARE, RESET_REASON_SOFTWARE);
MAKE_ENUM_VALUE(sleepio_reset_reason_type, reset_reason, DEEP_SLEEP_ALARM, RESET_REASON_DEEP_SLEEP_ALARM);
MAKE_ENUM_VALUE(sleepio_reset_reason_type, reset_reason, EXTERNAL, RESET_REASON_EXTERNAL);
//| class ResetReason:
//| """The reason the chip was last reset"""
//|
//| POWER_VALID: object
//| """The chip was reset and started once power levels were valid."""
//|
//| SOFTWARE: object
//| """The chip was reset from software."""
//|
//| DEEP_SLEEP_ALARM: object
//| """The chip was reset for deep sleep and started by an alarm."""
//|
//| EXTERNAL: object
//| """The chip was reset by an external input such as a button."""
//|
MAKE_ENUM_MAP(sleepio_reset_reason) {
MAKE_ENUM_MAP_ENTRY(reset_reason, POWER_VALID),
MAKE_ENUM_MAP_ENTRY(reset_reason, SOFTWARE),
MAKE_ENUM_MAP_ENTRY(reset_reason, DEEP_SLEEP_ALARM),
MAKE_ENUM_MAP_ENTRY(reset_reason, EXTERNAL),
};
STATIC MP_DEFINE_CONST_DICT(sleepio_reset_reason_locals_dict, sleepio_reset_reason_locals_table);
MAKE_PRINTER(sleepio, sleepio_reset_reason);
MAKE_ENUM_TYPE(sleepio, ResetReason, sleepio_reset_reason);

View File

@ -0,0 +1,36 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 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
typedef enum {
RESET_REASON_POWER_APPLIED,
RESET_REASON_SOFTWARE,
RESET_REASON_DEEP_SLEEP_ALARM,
RESET_REASON_BUTTON,
} sleepio_reset_reason_t;
extern const mp_obj_type_t sleepio_reset_reason_type;

View File

@ -47,6 +47,10 @@
//| """The most recent alarm to wake us up from a sleep (light or deep.)"""
//|
//| reset_reason: ResetReason
//| """The reason the chip started up from reset state. This can may be power up or due to an alarm."""
//|
//| def sleep_until_alarm(alarm: Alarm, ...) -> Alarm:
//| """Performs a light sleep until woken by one of the alarms. The alarm that woke us up is
//| returned."""
@ -54,14 +58,6 @@
//|
STATIC mp_obj_t sleepio_sleep_until_alarm(size_t n_args, const mp_obj_t *args) {
// mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
// vstr_t vstr;
// vstr_init_len(&vstr, size);
// byte *p = (byte*)vstr.buf;
// memset(p, 0, size);
// byte *end_p = &p[size];
// shared_modules_struct_pack_into(args[0], p, end_p, n_args - 1, &args[1]);
// return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(sleepio_sleep_until_alarm_obj, 1, MP_OBJ_FUN_ARGS_MAX, sleepio_sleep_until_alarm);
@ -71,14 +67,6 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(sleepio_sleep_until_alarm_obj, 1, MP_OBJ_FUN
//| ...
//|
STATIC mp_obj_t sleepio_set_alarms(size_t n_args, const mp_obj_t *args) {
// mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
// vstr_t vstr;
// vstr_init_len(&vstr, size);
// byte *p = (byte*)vstr.buf;
// memset(p, 0, size);
// byte *end_p = &p[size];
// shared_modules_struct_pack_into(args[0], p, end_p, n_args - 1, &args[1]);
// return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(sleepio_set_alarms_obj, 1, MP_OBJ_FUN_ARGS_MAX, sleepio_set_alarms);
@ -87,16 +75,23 @@ mp_map_elem_t sleepio_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sleepio) },
{ MP_ROM_QSTR(MP_QSTR_wake_alarm), mp_const_none },
{ MP_ROM_QSTR(MP_QSTR_reset_reason), mp_const_none },
{ MP_ROM_QSTR(MP_QSTR_sleep_until_alarm), sleepio_sleep_until_alarm_obj },
{ MP_ROM_QSTR(MP_QSTR_set_alarms), sleepio_set_alarms_obj },
};
STATIC MP_DEFINE_CONST_DICT(sleepio_module_globals, sleepio_module_globals_table);
// These are called from common hal code to set the current wake alarm.
void common_hal_sleepio_set_wake_alarm(mp_obj_t alarm) {
// sleepio_module_globals_table[1].value = alarm;
}
// These are called from common hal code to set the current wake alarm.
void common_hal_sleepio_set_reset_reason(mp_obj_t reset_reason) {
// sleepio_module_globals_table[1].value = alarm;
}
const mp_obj_module_t sleepio_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&sleepio_module_globals,

View File

@ -3,8 +3,7 @@
#include "py/obj.h"
// This is implemented by shared-bindings so that implementations can set the
// newest alarm source.
extern void common_hal_sleepio_set_wake_alarm(mp_obj_t alarm);
extern mp_obj_t common_hal_sleepio_get_wake_alarm(void);
extern sleepio_reset_reason_t common_hal_sleepio_get_reset_reason(void);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SLEEPIO___INIT___H

View File

@ -47,5 +47,4 @@ char serial_read(void);
bool serial_bytes_available(void);
bool serial_connected(void);
extern volatile bool _serial_connected;
#endif // MICROPY_INCLUDED_SUPERVISOR_SERIAL_H

View File

@ -367,6 +367,7 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
status->found_main = found_main;
status->total_exception_cycle = 0;
status->ok = result->return_code != PYEXEC_EXCEPTION;
status->cycles = 0;
if (status->ok) {
// If this isn't an exception, skip exception sorting and handling
return;
@ -411,14 +412,16 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
#endif
}
void tick_rgb_status_animation(rgb_status_animation_t* status) {
bool tick_rgb_status_animation(rgb_status_animation_t* status) {
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
uint32_t tick_diff = supervisor_ticks_ms32() - status->pattern_start;
if (status->ok) {
// All is good. Ramp ALL_DONE up and down.
if (tick_diff > ALL_GOOD_CYCLE_MS) {
status->pattern_start = supervisor_ticks_ms32();
tick_diff = 0;
status->cycles++;
new_status_color(BLACK);
return status->cycles;
}
uint16_t brightness = tick_diff * 255 / (ALL_GOOD_CYCLE_MS / 2);
@ -433,7 +436,8 @@ void tick_rgb_status_animation(rgb_status_animation_t* status) {
} else {
if (tick_diff > status->total_exception_cycle) {
status->pattern_start = supervisor_ticks_ms32();
tick_diff = 0;
status->cycles++;
return;
}
// First flash the file color.
if (tick_diff < EXCEPTION_TYPE_LENGTH_MS) {

View File

@ -52,6 +52,10 @@ safe_mode_t wait_for_safe_mode_reset(void) {
current_safe_mode = safe_mode;
return safe_mode;
}
if (common_hal_sleepio_get_reset_reason() != RESET_REASON_POWER_VALID &&
common_hal_sleepio_get_reset_reason() != RESET_REASON_BUTTON) {
return NO_SAFE_MODE;
}
port_set_saved_word(SAFE_MODE_DATA_GUARD | (MANUAL_SAFE_MODE << 8));
// Wait for a while to allow for reset.
temp_status_color(SAFE_MODE);

View File

@ -0,0 +1,29 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 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
extern volatile bool _serial_connected;

View File

@ -30,6 +30,7 @@
#include "supervisor/background_callback.h"
#include "supervisor/port.h"
#include "supervisor/serial.h"
#include "supervisor/shared/serial.h"
#include "supervisor/usb.h"
#include "lib/utils/interrupt_char.h"
#include "lib/mp-readline/readline.h"
@ -102,14 +103,16 @@ void usb_irq_handler(void) {
// tinyusb callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
// Invoked when device is plugged into a host
void tud_mount_cb(void) {
usb_msc_mount();
_workflow_active = true;
}
// Invoked when device is unmounted
// Invoked when device is unplugged from the host
void tud_umount_cb(void) {
usb_msc_umount();
_workflow_active = false;
}
// Invoked when usb bus is suspended
@ -117,10 +120,12 @@ void tud_umount_cb(void) {
// USB Specs: Within 7ms, device must draw an average current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en) {
_serial_connected = false;
_workflow_active = false;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void) {
_workflow_active = true;
}
// Invoked when cdc when line state changed e.g connected/disconnected

View File

@ -0,0 +1,32 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 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.
*/
// Set by the shared USB code.
volatile bool _workflow_active;
bool workflow_active(void) {
return _workflow_active;
}

View File

@ -0,0 +1,29 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 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
extern volatile bool _workflow_active;

View File

@ -75,6 +75,7 @@ else
lib/tinyusb/src/class/cdc/cdc_device.c \
lib/tinyusb/src/tusb.c \
supervisor/shared/serial.c \
supervisor/shared/workflow.c \
supervisor/usb.c \
supervisor/shared/usb/usb_desc.c \
supervisor/shared/usb/usb.c \

30
supervisor/workflow.h Executable file
View File

@ -0,0 +1,30 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 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
// True when the user could be actively iterating on their code.
bool workflow_active(void);