pe: cap shell refresh rate at 30 FPS

This commit is contained in:
Lephenixnoir 2022-10-30 17:53:13 +01:00
parent d0654fc5e7
commit 8809f3eee4
Signed by untrusted user: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
7 changed files with 127 additions and 29 deletions

View File

@ -15,7 +15,6 @@ Most of the code is in `ports/sh` and is shared between the platforms.
**TODO list**
Bugs to fix:
- Don't refresh screen at every low-level console print
- Fix not world switching during filesystem accesses (very unstable)
Python features:
@ -25,6 +24,8 @@ Python features:
- Interrupt with AC/ON
UI:
- Shell escapes: move cursor, history
- Better input system in the shell
- Use [JustUI](/Lephenixnoir/JustUI) to get a file browser (already available)
- Add an option for fixed-width font which also sets $COLUMNS properly so that
MicroPython paginates (requires better getenv/setenv support in fxlib)

View File

@ -15,6 +15,7 @@ SRC_C = \
ports/sh/keymap.c \
ports/sh/modgint.c \
ports/sh/mphalport.c \
ports/sh/shell.c \
shared/readline/readline.c \
shared/runtime/gchelper_generic.c \
shared/runtime/pyexec.c \

View File

@ -13,7 +13,9 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "console.h"
#include "shell.h"
//=== Console-based standard streams ===//
@ -33,33 +35,15 @@ fs_descriptor_type_t stdouterr_type = {
//=== Main function ===//
static console_t *cons = NULL;
void pe_draw(void)
{
dclear(C_WHITE);
#ifdef FX9860G
int rows = 8;
console_render(1, 1, cons, DWIDTH-2, rows);
#else
dprint(3, 3, C_BLACK, "PythonExtra, very much WIP :)");
dline(2, 16, DWIDTH-3, 16, C_BLACK);
int rows = 12;
console_render(3, 20, cons, DWIDTH-6, rows);
int y = 20 + PE_CONSOLE_LINE_SPACING * rows;
dline(2, y, DWIDTH-3, y, C_BLACK);
#endif
dupdate();
}
int main(int argc, char **argv)
{
pe_shell_init();
/* Set up standard streams */
cons = console_create(8192);
close(STDOUT_FILENO);
close(STDERR_FILENO);
open_generic(&stdouterr_type, cons, STDOUT_FILENO);
open_generic(&stdouterr_type, cons, STDERR_FILENO);
open_generic(&stdouterr_type, pe_shell_console, STDOUT_FILENO);
open_generic(&stdouterr_type, pe_shell_console, STDERR_FILENO);
/* Initialize MicroPython */
#define HEAP_SIZE 32768
@ -86,11 +70,10 @@ int main(int argc, char **argv)
// Start a normal REPL; will exit when ctrl-D is entered on a blank line.
pyexec_friendly_repl();
console_destroy(cons);
// Deinitialise the runtime.
gc_sweep_all();
mp_deinit();
pe_shell_deinit();
return 0;
}

View File

@ -2,6 +2,7 @@
#include "py/objtuple.h"
#include <gint/display.h>
#include <gint/keyboard.h>
#include "shell.h"
#define FUN_0(NAME) \
MP_DEFINE_CONST_FUN_OBJ_0(modgint_ ## NAME ## _obj, modgint_ ## NAME)
@ -121,6 +122,7 @@ STATIC mp_obj_t modgint_dclear(mp_obj_t arg1)
STATIC mp_obj_t modgint_dupdate(void)
{
pe_shell_graphics_mode();
dupdate();
return mp_const_none;
}

View File

@ -4,11 +4,13 @@
#include "../../shared/readline/readline.h"
#include "keymap.h"
#include "shell.h"
#include <gint/display.h>
#include <gint/keyboard.h>
// Receive single character, blocking until one is available.
int mp_hal_stdin_rx_chr(void) {
int mp_hal_stdin_rx_chr(void)
{
while(1) {
key_event_t ev = getkey();
int key = ev.key;
@ -44,10 +46,10 @@ int mp_hal_stdin_rx_chr(void) {
}
// Send the string of given length.
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len)
{
int r = write(STDOUT_FILENO, str, len);
(void)r;
extern void pe_draw(void);
pe_draw();
pe_shell_schedule_update();
}

63
ports/sh/shell.c Normal file
View File

@ -0,0 +1,63 @@
#include "shell.h"
#include <gint/timer.h>
#include <gint/display.h>
/* The global terminal. */
console_t *pe_shell_console;
/* Timer handle for the refresh clock. */
static int pe_shell_timer = -1;
/* Whether there is new shell data to be shown on the next frame. */
static bool pe_shell_update = true;
static void pe_shell_draw(void)
{
dclear(C_WHITE);
#ifdef FX9860G
int rows = 8;
console_render(1, 1, pe_shell_console, DWIDTH-2, rows);
#else
dprint(3, 3, C_BLACK, "PythonExtra, very much WIP :)");
dline(2, 16, DWIDTH-3, 16, C_BLACK);
int rows = 12;
console_render(3, 20, pe_shell_console, DWIDTH-6, rows);
int y = 20 + PE_CONSOLE_LINE_SPACING * rows;
dline(2, y, DWIDTH-3, y, C_BLACK);
#endif
dupdate();
}
static int pe_shell_timer_handler(void)
{
if(pe_shell_update) {
pe_shell_draw();
pe_shell_update = false;
}
return TIMER_CONTINUE;
}
void pe_shell_schedule_update(void)
{
pe_shell_update = true;
}
void pe_shell_graphics_mode(void)
{
pe_shell_update = false;
}
void pe_shell_init(void)
{
pe_shell_console = console_create(8192);
pe_shell_timer = timer_configure(TIMER_ANY, 1000000 / PE_SHELL_FPS,
GINT_CALL(pe_shell_timer_handler));
timer_start(pe_shell_timer);
}
void pe_shell_deinit(void)
{
timer_stop(pe_shell_timer);
console_destroy(pe_shell_console);
}

46
ports/sh/shell.h Normal file
View File

@ -0,0 +1,46 @@
// pe.shell: Shell integration
//
// This header covers application-integration aspects of the shell. For the
// implementation of the console itself (input, escape sequences, etc). see
// <console.h> instead.
//
// The shell consists of a terminal-emulating `console_t` along with some input
// UI. During execution, the shell is redrawn only (1) when new data is printed
// or requested, and (2) a fixed refresh timer hits. Condition 1 avoids
// needless updates for non-printing programs, while condition 2 prevents
// performance issues for sequences that print one character at a time (which
// is quite common even within MicroPython builtins).
//
// If the built-in function gint.dupdate() is used, the shell also hands over
// display updates to "graphics mode", where only manual dupdate() calls push
// the VRAM. Switching to graphics mode cancels any scheduled shell updates.
// Graphics mode is exited once a new shell update is explicity scheduled,
// typically by a call to print() or input().
//---
#ifndef __PYTHONEXTRA_SHELL_H
#define __PYTHONEXTRA_SHELL_H
#include "console.h"
/* Shell frame frequency, ie. cap on the number of shell redraws per second. */
#define PE_SHELL_FPS 30
/* The terminal. */
extern console_t *pe_shell_console;
/* Schedule a redraw of the shell interface at the next shell frame. This is
called whenever data is printed into the shell or input() is used. The
scheduled redraw might be canceled by switching to graphics mode before the
frame timer fires. */
void pe_shell_schedule_update(void);
/* Switch to graphics mode. This cancels pending shell updates. */
void pe_shell_graphics_mode(void);
//=== Internal functions ===//
void pe_shell_init(void);
void pe_shell_deinit(void);
#endif /* __PYTHONEXTRA_SHELL_H */