pe: rotating console lines and basic benchmarks

This commit is contained in:
Lephenixnoir 2022-12-17 16:09:32 +01:00
parent 0da3395e89
commit 189aadff1c
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
8 changed files with 412 additions and 211 deletions

View File

@ -65,3 +65,15 @@ Future wishes:
| Island of the Dead Kings | TODO | TODO | TODO | TODO |
| Synchro-Donjon | TODO | TODO | TODO | TODO |
| Flappy Bird | Yes | Yes | TODO | TODO |
**Basic benchmarks**
PythonExtra is slightly faster than the official Python app, probably due to
optimization during compilation of the VM. The tests below show the effect on
basic Python operations.
| Test | Program | Official Python (fx-CG 50) | PythonExtra (fx-CG 50) |
| ---- | ------- | -------------------------- | ---------------------- |
| VM speed | `for i in range(1000000): pass` | ~12 seconds | 8.9 seconds |
| Shell output | `for i in range(100000): print(i)` | ~22 seconds | 11.3 seconds |
| Large integers | 250! (500 repeats) | ~15 seconds | 8.6 seconds |

View File

@ -35,9 +35,7 @@ bool console_line_init(console_line_t *line, int prealloc_size)
void console_line_deinit(console_line_t *line)
{
free(line->data);
line->data = NULL;
line->size = 0;
line->alloc_size = 0;
memset(line, 0, sizeof *line);
}
bool console_line_alloc(console_line_t *line, int n)
@ -46,7 +44,7 @@ bool console_line_alloc(console_line_t *line, int n)
return true;
/* Always increase the size by at least 16 so we can insert many times in a
row without worrying about excessive insertions. */
row without worrying about excessive allocations. */
int newsize = max(line->alloc_size + 16, n + 1);
char *newdata = realloc(line->data, newsize);
if(!newdata)
@ -147,86 +145,188 @@ int console_line_render(int x, int y, console_line_t *line, int w, int dy,
return y;
}
//=== Rotating line storage ===//
bool linebuf_init(linebuf_t *buf, int capacity, int backlog_size)
{
if(capacity <= 0)
return false;
buf->lines = malloc(capacity * sizeof *buf->lines);
if(!buf->lines)
return false;
buf->capacity = capacity;
buf->start = 0;
buf->size = 0;
buf->absolute = 1;
buf->backlog_size = backlog_size;
buf->total_size_except_last = 0;
buf->absolute_rendered = 0;
buf->total_rendered = 0;
return true;
}
void linebuf_deinit(linebuf_t *buf)
{
free(buf->lines);
memset(buf, 0, sizeof *buf);
}
/* Lines in the buffer are identified by their positions within the `lines`
array, which are integers equipped with modulo arithmetic (we call them
"indices"). We abstract away the rotation by numbering stored lines from 0
to buf->size - 1, and we call these numbers "nths". When we want to identify
lines independent of rotation, we use their absolute line number.
Such numbers refer to lines stored in the buffer if:
(index) 0 <= linebuf_index_to_nth(buf, index) < size
(nth) 0 <= nth < size
(abs) 0 <= abs - buf->absolute < size */
GINLINE static int linebuf_nth_to_index(linebuf_t const *buf, int nth)
{
return (buf->start + nth) % buf->capacity;
}
/* Get the nth line. */
GINLINE static console_line_t *linebuf_get_nth_line(linebuf_t const *buf,
int nth)
{
if(nth < 0 || nth >= buf->size)
return NULL;
return &buf->lines[linebuf_nth_to_index(buf, nth)];
}
/* Move `index` by `diff`; assumes |diff| <= buf->capacity. */
GINLINE static int linebuf_index_add(linebuf_t const *buf, int index, int diff)
{
return (index + diff + buf->capacity) % buf->capacity;
}
int linebuf_start(linebuf_t const *buf)
{
return buf->absolute;
}
int linebuf_end(linebuf_t const *buf)
{
return buf->absolute + buf->size;
}
console_line_t *linebuf_get_line(linebuf_t const *buf, int abs)
{
return linebuf_get_nth_line(buf, abs - buf->absolute);
}
console_line_t *linebuf_get_last_line(linebuf_t const *buf)
{
return linebuf_get_nth_line(buf, buf->size - 1);
}
console_line_t *linebuf_newline(linebuf_t *buf)
{
/* Make space if the buffer is full */
linebuf_recycle_oldest_lines(buf, buf->size - buf->capacity + 1);
/* Freeze the current last line's size */
if(buf->size > 0) {
console_line_t *L = linebuf_get_nth_line(buf, buf->size - 1);
buf->total_size_except_last += L->size;
}
buf->size++;
console_line_t *L = linebuf_get_nth_line(buf, buf->size - 1);
console_line_init(L, 16);
return L;
}
void linebuf_recycle_oldest_lines(linebuf_t *buf, int count)
{
count = min(count, buf->size);
if(count <= 0)
return;
for(int nth = 0; nth < count; nth++) {
console_line_t *L = linebuf_get_nth_line(buf, nth);
buf->total_rendered -= L->render_lines;
if(nth != buf->size - 1)
buf->total_size_except_last -= L->size;
console_line_deinit(L);
}
buf->start = linebuf_index_add(buf, buf->start, count);
buf->size -= count;
buf->absolute += count;
}
void linebuf_clean_backlog(linebuf_t *buf)
{
if(buf->size <= 0)
return;
int remove = 0;
int n = buf->total_size_except_last + linebuf_get_last_line(buf)->size;
while(remove < buf->size - 1 && n > buf->backlog_size) {
n -= linebuf_get_nth_line(buf, remove)->size;
remove++;
}
linebuf_recycle_oldest_lines(buf, remove);
}
void linebuf_update_render(linebuf_t *buf, int width, bool lazy)
{
int start = linebuf_start(buf);
int end = linebuf_end(buf);
if(lazy)
start = max(start, buf->absolute_rendered + 1);
for(int abs = start; abs < end; abs++) {
console_line_t *L = linebuf_get_nth_line(buf, abs - buf->absolute);
buf->total_rendered -= L->render_lines;
console_line_update_render_lines(L, width);
buf->total_rendered += L->render_lines;
}
buf->absolute_rendered = max(buf->absolute_rendered, end - 2);
}
//=== Terminal emulator ===//
console_t *console_create(int backlog_size)
console_t *console_create(int backlog_size, int maximum_line_count)
{
backlog_size = max(backlog_size, PE_CONSOLE_LINE_MAX_LENGTH);
maximum_line_count = max(maximum_line_count, 1);
console_t *cons = malloc(sizeof *cons);
cons->lines = NULL;
cons->line_count = 0;
cons->absolute_lineno = 1;
cons->backlog_size = max(backlog_size, PE_CONSOLE_LINE_MAX_LENGTH);
if(!linebuf_init(&cons->lines, maximum_line_count, backlog_size)) {
free(cons);
return NULL;
}
cons->cursor = -1;
cons->render_needed = true;
cons->render_font = NULL;
cons->render_width = 0;
cons->render_lines = 0;
cons->render_total_lines = 0;
if(!console_new_line(cons)) {
free(cons);
return NULL;
}
console_newline(cons);
return cons;
}
bool console_new_line(console_t *cons)
void console_newline(console_t *cons)
{
int newsize = (cons->line_count + 1) * sizeof *cons->lines;
console_line_t *newlines = realloc(cons->lines, newsize);
if(!newlines)
return false;
cons->lines = newlines;
/* If this fails we keep the extended lines buffer. The next realloc() will
be a no-op and we don't have to track anything. */
if(!console_line_init(&cons->lines[cons->line_count], 16))
return false;
cons->line_count++;
linebuf_newline(&cons->lines);
cons->cursor = 0;
cons->render_needed = true;
/* Routinely clean the backlog every time a line is added. */
console_clean_backlog(cons);
return true;
}
/* static console_line_t *console_get_absolute_line(console_t const *cons,
int lineno)
{
int i = lineno - cons->absolute_lineno;
return (i >= 0 && i < cons->line_count) ? &cons->lines[i] : NULL;
} */
void console_clean_backlog(console_t *cons)
{
/* Find how many lines we can keep while using at most cons->backlog_size
bytes to store them. The console must always have at least one line. */
int first_kept = cons->line_count;
int line_size = 0;
int total_size = 0;
do {
total_size += line_size;
first_kept--;
line_size = cons->lines[first_kept].size;
}
while(first_kept > 0 && total_size + line_size <= cons->backlog_size);
/* Remove old lines */
for(int j = 0; j < first_kept; j++)
console_line_deinit(&cons->lines[j]);
/* Don't realloc() yet, it will happen soon enough anyway */
memmove(cons->lines, cons->lines + first_kept,
(cons->line_count - first_kept) * sizeof cons->lines[0]);
cons->line_count -= first_kept;
/* Update the absolute number of the first line */
cons->absolute_lineno += first_kept;
cons->render_needed = true;
/* This is a good time to clean up the backlog. */
// TODO: This might actually be the performance bottleneck, because we do a
// long loop only to find out that the total size is still reasonable!
linebuf_clean_backlog(&cons->lines);
}
void console_clear_render_flag(console_t *cons)
@ -236,9 +336,7 @@ void console_clear_render_flag(console_t *cons)
void console_destroy(console_t *cons)
{
for(int i = 0; i < cons->line_count; i++)
console_line_deinit(&cons->lines[i]);
free(cons->lines);
linebuf_deinit(&cons->lines);
free(cons);
}
@ -271,38 +369,31 @@ static void view_params(int w,
void console_compute_view(console_t *cons, font_t const *font,
int width, int lines)
{
/* If a view with the same width was previously computed, do a lazy
update: recompute only the last lines. */
bool lazy = (width != 0 && cons->render_width == width);
cons->render_font = font;
cons->render_width = width;
cons->render_lines = lines;
cons->render_total_lines = 0;
int text_w;
view_params(width, &text_w, NULL, NULL);
for(int i = 0; i < cons->line_count; i++) {
console_line_update_render_lines(&cons->lines[i], text_w);
cons->render_total_lines += cons->lines[i].render_lines;
}
linebuf_update_render(&cons->lines, width, lazy);
}
console_scrollpos_t console_clamp_scrollpos(console_t const *cons,
console_scrollpos_t pos)
{
/* No scrolling case */
if(cons->render_total_lines < cons->render_lines)
if(cons->lines.total_rendered < cons->render_lines)
return 0;
/* Normal clamp */
return max(0, min(pos, cons->render_total_lines - cons->render_lines));
return max(0, min(pos, cons->lines.total_rendered - cons->render_lines));
}
void console_render(int x, int y0, console_t const *cons, int dy,
console_scrollpos_t pos)
{
int total_lines = cons->render_total_lines;
int total_lines = cons->lines.total_rendered;
int visible_lines = cons->render_lines;
int w = cons->render_width;
int watermark = visible_lines - total_lines + pos;
int y = y0;
int text_w, scroll_spacing, scroll_w;
@ -310,15 +401,27 @@ void console_render(int x, int y0, console_t const *cons, int dy,
font_t const *old_font = dfont(cons->render_font);
/* Show only visible lines */
for(int i = 0; i < cons->line_count; i++) {
console_line_t *line = &cons->lines[i];
bool show_cursor = (i == cons->line_count - 1);
/* Show only visible lines. We want to avoid counting all the lines in the
console, and instead start from the end. */
int line_y = visible_lines + pos;
int L_start = linebuf_start(&cons->lines);
int L_end = linebuf_end(&cons->lines);
int i = linebuf_end(&cons->lines);
if(watermark + line->render_lines > 0)
y = console_line_render(x, y, line, text_w, dy, -watermark,
visible_lines - watermark, show_cursor ? cons->cursor : -1);
watermark += line->render_lines;
while(i > L_start && line_y > 0)
line_y -= linebuf_get_line(&cons->lines, --i)->render_lines;
/* If there isn't enough content to fill the view, start at the top. */
line_y = min(line_y, pos);
while(i < L_end && line_y < visible_lines) {
console_line_t *line = linebuf_get_line(&cons->lines, i);
bool show_cursor = (i == L_end - 1);
y = console_line_render(x, y, line, text_w, dy, -line_y,
visible_lines - line_y, show_cursor ? cons->cursor : -1);
line_y += line->render_lines;
i++;
}
dfont(old_font);
@ -342,11 +445,16 @@ void console_render(int x, int y0, console_t const *cons, int dy,
//=== Edition functions ===//
GINLINE static console_line_t *last_line(console_t *cons)
{
return linebuf_get_last_line(&cons->lines);
}
bool console_set_cursor(console_t *cons, int pos)
{
console_line_t *last_line = &cons->lines[cons->line_count - 1];
console_line_t *L = last_line(cons);
if(pos < last_line->prefix || pos > last_line->size)
if(pos < L->prefix || pos > L->size)
return false;
cons->cursor = pos;
@ -360,37 +468,35 @@ bool console_move_cursor(console_t *cons, int cursor_movement)
char *console_get_line(console_t *cons, bool copy)
{
console_line_t *last_line = &cons->lines[cons->line_count - 1];
char *str = last_line->data + last_line->prefix;
console_line_t *L = last_line(cons);
char *str = L->data + L->prefix;
return copy ? strdup(str) : str;
}
bool console_write_block_at_cursor(console_t *cons, char const *str, int n)
bool console_write_raw(console_t *cons, char const *str, int n)
{
if(!cons->line_count && !console_new_line(cons))
return false;
if(!cons->lines.size)
console_newline(cons);
/* Split up n characters into chunks that remain within each line's storage
capacity. */
/* Split string into chunks smaller than each line's storage capacity. */
while(n > 0) {
console_line_t *last_line = &cons->lines[cons->line_count - 1];
int capacity = console_line_capacity(last_line);
console_line_t *L = last_line(cons);
int capacity = console_line_capacity(L);
int round_size = min(n, capacity);
if(!console_line_insert(last_line, cons->cursor, str, round_size))
if(!console_line_insert(L, cons->cursor, str, round_size))
return false;
cons->cursor += round_size;
cons->render_needed = true;
if(round_size < n && !console_new_line(cons))
return false;
if(round_size < n)
console_newline(cons);
n -= round_size;
}
return true;
}
bool console_write_at_cursor(console_t *cons, char const *buf, int n)
bool console_write(console_t *cons, char const *buf, int n)
{
int offset = 0;
if(n < 0)
@ -403,7 +509,7 @@ bool console_write_at_cursor(console_t *cons, char const *buf, int n)
end++;
int line_size = end - (buf + offset);
if(!console_write_block_at_cursor(cons, buf + offset, line_size))
if(!console_write_raw(cons, buf + offset, line_size))
return false;
offset += line_size;
@ -411,33 +517,32 @@ bool console_write_at_cursor(console_t *cons, char const *buf, int n)
break;
if(buf[offset] == '\n') {
if(!console_new_line(cons))
return false;
console_newline(cons);
offset++;
}
else if(buf[offset] == 8) {
offset++;
console_line_t *last_line = &cons->lines[cons->line_count - 1];
console_line_t *L = last_line(cons);
if(cons->cursor > 0) {
console_line_delete(last_line, cons->cursor-1, 1);
console_line_delete(L, cons->cursor-1, 1);
cons->cursor--;
}
}
else if(buf[offset] == '\e') {
console_line_t *last_line = &cons->lines[cons->line_count - 1];
console_line_t *L = last_line(cons);
offset++;
/* TODO: Handle more complex escape sequences */
if(offset + 2 <= n && buf[offset] == '[' && buf[offset+1] == 'K') {
console_line_delete(last_line, cons->cursor,
last_line->size - cons->cursor);
console_line_delete(L, cons->cursor,
L->size - cons->cursor);
offset += 2;
}
if(offset + 2 <= n && buf[offset] == 1 && buf[offset+1] == 'D') {
cons->cursor -= (cons->cursor > 0);
}
if(offset + 2 <= n && buf[offset] == 1 && buf[offset+1] == 'C') {
cons->cursor += (cons->cursor < last_line->size);
cons->cursor += (cons->cursor < L->size);
}
}
@ -449,23 +554,21 @@ bool console_write_at_cursor(console_t *cons, char const *buf, int n)
void console_lock_prefix(console_t *cons)
{
console_line_set_prefix(&cons->lines[cons->line_count - 1], cons->cursor);
console_line_set_prefix(last_line(cons), cons->cursor);
}
void console_delete_at_cursor(console_t *cons, int n)
{
console_line_t *last_line = &cons->lines[cons->line_count - 1];
int real_n = console_line_delete(last_line, cons->cursor - n, n);
int real_n = console_line_delete(last_line(cons), cons->cursor - n, n);
cons->cursor -= real_n;
cons->render_needed = true;
}
void console_clear_current_line(console_t *cons)
{
console_line_t *last_line = &cons->lines[cons->line_count - 1];
console_line_delete(last_line, last_line->prefix,
last_line->size - last_line->prefix);
cons->cursor = last_line->prefix;
console_line_t *L = last_line(cons);
console_line_delete(L, L->prefix, L->size - L->prefix);
cons->cursor = L->prefix;
cons->render_needed = true;
}

View File

@ -37,9 +37,9 @@ typedef struct
/* Line contents, NUL-terminated. The buffer might be larger. */
char *data;
/* Size of contents (not counting the NUL). */
int size;
int16_t size;
/* Allocated size (always ≥ size+1). */
int alloc_size;
int16_t alloc_size;
/* Number or render lines used (updated on-demand). */
int16_t render_lines;
/* Number of initial characters that can't be edited. */
@ -79,33 +79,103 @@ void console_line_update_render_lines(console_line_t *line, int width);
int console_line_render(int x, int y, console_line_t *line, int w, int dy,
int show_from, int show_until, int cursor);
//=== Rotating line storage ===//
/* linebuf_t: A rotating array of console lines. */
typedef struct
{
/* A rotating array of `capacity` lines starting at position `start` and
holding `size` lines. The array is pre-allocated. */
console_line_t *lines;
/* Invariants:
- capacity > 0
- 0 <= size <= capacity
- 0 <= start < capacity
- When size is 0, start is undefined. */
int capacity, start, size;
/* To keep track of lines' identity, the rotating array includes an extra
numbering system. Each line is assigned an *absolute* line number which
starts at 1 and increases every time a line is added. That number is
independent of rotation.
Absolute line number of the next line to be removed. This identifies
the `start` line, unless the buffer is empty. Regardless, the interval
[buf->absolute .. buf->absolute + buf->size) always covers exactly the
set of lines that are held in the buffer. */
int absolute;
/* To avoid memory explosion, the rotating array can be set to clean up old
lines when the total memory consumption is too high. `backlog_size`
specifies how many bytes of text lines are allowed to hold. */
int backlog_size;
/* Total size of current lines, in bytes, excluding the last line. */
int total_size_except_last;
/* Last absolute line that has been both laid out for rendering and frozen
for edition (ie. followed by another line). Lazy rendering can always
start at `absolute_rendered+1`. */
int absolute_rendered;
/* Total number of rendered lines for the buffer. */
int total_rendered;
} linebuf_t;
/* Initialize a rotating buffer by allocating `line_count` lines. The buffer
will allow up to `backlog_size` bytes of text data and clean up lines past
that limit. This function does not free pre-existing data in `buf`. */
bool linebuf_init(linebuf_t *buf, int capacity, int backlog_size);
/* Free a rotating buffer and clean it up. */
void linebuf_deinit(linebuf_t *buf);
/* Absolute line numbers of the "start" and "end" of the buffer. The set of
lines in the buffer is always [start ... end). If the buffer is empty, the
interval is empty and neither line number is in the buffer. */
int linebuf_start(linebuf_t const *buf);
int linebuf_end(linebuf_t const *buf);
/* Get a pointer to the line with the given absolute number. */
console_line_t *linebuf_get_line(linebuf_t const *buf, int absolute_number);
/* Get a pointer to the last line in the buffer (usually the editable line). */
console_line_t *linebuf_get_last_line(linebuf_t const *buf);
/* Add a new line to the buffer (recycling an old one if needed). */
console_line_t *linebuf_new_line(linebuf_t *buf);
/* Recycle the `n` oldest lines from the buffer. */
void linebuf_recycle_oldest_lines(linebuf_t *buf, int n);
/* Clean up lines to try and keep the memory footprint of the text under
`backlog_size` bytes. Always keeps at least the last line. */
void linebuf_clean_backlog(linebuf_t *buf);
/* Update the render width computation for all lines in the buffer. If `lazy`
is false, all lines are re-laid out. But in the console the width often
remains the same for many renders, and only the last line can be edited. In
this case, `lazy` can be set to true, and only lines added or edited since
the previous render will be re-laid out. */
void linebuf_update_render(linebuf_t *buf, int width, bool lazy);
//=== Terminal emulator ===//
typedef struct
{
/* A dynamic array of console_line_t. Never empty. */
console_line_t *lines;
int line_count;
/* A rotating array of console_line_t. Never empty. */
linebuf_t lines;
/* Maximum (not 100% strict) amount of storage that is conserved in log.
When this limit is exceeded, old lines are cleaned. This must be more
than PE_CONSOLE_LINE_MAX_LENGTH. */
int backlog_size;
/* Absolute number of the first line in the dynamic line array. This is
the line number in the user-facing sense (it includes lines that were
cleaned up to free memory). */
int absolute_lineno;
/* Cursor position within the last line. */
int cursor;
/* Whether new data has been added and a frame should be rendered. */
bool render_needed;
/* View parameters from last console_compute_view() */
/* View geometry parameters from last console_compute_view(). */
font_t const *render_font;
int render_width;
int render_lines;
int render_total_lines;
} console_t;
@ -113,14 +183,11 @@ typedef struct
typedef int console_scrollpos_t;
/* Create a new console with the specified backlog size. */
console_t *console_create(int backlog_size);
console_t *console_create(int backlog_size, int maximum_line_count);
/* Create a new empty line at the bottom of the console, and move the cursor
there. Previous lines can no longer be edited. Returns false on error. */
bool console_new_line(console_t *cons);
/* Clean up backlog if the total memory usage is exceeded. */
void console_clean_backlog(console_t *cons);
there. Previous lines can no longer be edited. */
void console_newline(console_t *cons);
/* Clear the console's render flag, which is used to notify of changes. This
function is used when handing control of the display from the console to a
@ -167,11 +234,11 @@ char *console_get_line(console_t *cons, bool copy);
/* Write string at the cursor's position within the last line. This writes a
raw string without interpreting escape sequences and newlines. */
bool console_write_block_at_cursor(console_t *cons, char const *str, int n);
bool console_write_raw(console_t *cons, char const *str, int n);
/* Write string at the cursor's position within the last line. This function
interprets escape sequences and newlines. */
bool console_write_at_cursor(console_t *cons, char const *str, int n);
bool console_write(console_t *cons, char const *str, int n);
/* Set the current cursor position to mark the prefix of the current line. */
void console_lock_prefix(console_t *cons);

View File

@ -11,7 +11,7 @@
#include "py/mphal.h"
#include "py/repl.h"
#include "shared/runtime/gchelper.h"
#include "shared/runtime/pyexec.h"
#include "pyexec.h"
#include <gint/display.h>
#include <gint/drivers/keydev.h>
@ -49,7 +49,7 @@ extern bopti_image_t const img_modifier_states;
static ssize_t stdouterr_write(void *data, void const *buf, size_t size)
{
console_t *cons = data;
console_write_at_cursor(cons, buf, size);
console_write(cons, buf, size);
return size;
}
@ -61,7 +61,10 @@ fs_descriptor_type_t stdouterr_type = {
};
/* The global terminal. */
console_t *pe_shell_console;
console_t *pe_console = NULL;
/* The global JustUI scene and widget shell. */
jscene *pe_scene = NULL;
widget_shell *pe_shell = NULL;
static bool strendswith(char const *str, char const *suffix)
{
@ -80,16 +83,6 @@ static bool py_file_filter(struct dirent const *ent)
return strendswith(ent->d_name, ".py");
}
static void shell_write_char(int c)
{
pyexec_event_repl_process_char(c);
}
static void shell_write_str(char const *str)
{
while(*str)
pyexec_event_repl_process_char(*str++);
}
static char *path_to_module(char const *path)
{
if(path[0] == '/')
@ -145,8 +138,8 @@ static void pe_print_prompt(int which)
else
prompt = mp_repl_get_ps1();
console_write_at_cursor(pe_shell_console, prompt, -1);
console_lock_prefix(pe_shell_console);
console_write(pe_console, prompt, -1);
console_lock_prefix(pe_console);
}
static void pe_reset_micropython(void)
@ -160,12 +153,33 @@ static void pe_reset_micropython(void)
char const *msg = "*** SHELL INITIALIZED ***\n";
#endif
console_new_line(pe_shell_console);
console_write_at_cursor(pe_shell_console, msg, -1);
console_newline(pe_console);
console_write(pe_console, msg, -1);
pyexec_event_repl_init();
pe_print_prompt(1);
}
void pe_draw(void)
{
dclear(C_WHITE);
jscene_render(pe_scene);
/* Render shell modifiers above the scene in a convenient spot */
int shift, alpha, layer;
bool instant;
widget_shell_get_modifiers(pe_shell, &shift, &alpha);
widget_shell_modifier_info(shift, alpha, &layer, &instant);
int icon = 2 * layer + !instant;
#ifdef FX9860G
dsubimage(118, 58, &img_modifier_states, 9*icon+1, 1, 8, 6,
DIMAGE_NONE);
#else
dsubimage(377, 207, &img_modifier_states, 16*icon, 0, 15, 14,
DIMAGE_NONE);
#endif
dupdate();
}
int main(int argc, char **argv)
{
pe_debug_init();
@ -175,13 +189,13 @@ int main(int argc, char **argv)
keydev_set_async_filter(keydev_std(), async_filter);
pe_shell_console = console_create(8192);
pe_console = console_create(8192, 200);
/* Set up standard streams */
close(STDOUT_FILENO);
close(STDERR_FILENO);
open_generic(&stdouterr_type, pe_shell_console, STDOUT_FILENO);
open_generic(&stdouterr_type, pe_shell_console, STDERR_FILENO);
open_generic(&stdouterr_type, pe_console, STDOUT_FILENO);
open_generic(&stdouterr_type, pe_console, STDERR_FILENO);
/* Initialize the MicroPython GC with most available memory */
mp_stack_ctrl_init();
@ -225,15 +239,15 @@ int main(int argc, char **argv)
//=== GUI setup ===//
jscene *scene = jscene_create_fullscreen(NULL);
jlabel *title = jlabel_create("PythonExtra", scene);
jwidget *stack = jwidget_create(scene);
jfkeys *fkeys = jfkeys_create2(&img_fkeys_main, "/FILES;/SHELL", scene);
pe_scene = jscene_create_fullscreen(NULL);
jlabel *title = jlabel_create("PythonExtra", pe_scene);
jwidget *stack = jwidget_create(pe_scene);
jfkeys *fkeys = jfkeys_create2(&img_fkeys_main, "/FILES;/SHELL", pe_scene);
(void)fkeys;
jwidget_set_stretch(title, 1, 0, false);
jlayout_set_vbox(scene)->spacing = _(0, 3);
jlayout_set_vbox(pe_scene)->spacing = _(0, 3);
jlayout_set_stack(stack);
jwidget_set_stretch(stack, 1, 1, false);
@ -244,9 +258,9 @@ int main(int argc, char **argv)
jwidget_set_stretch(fileselect, 1, 1, false);
/* Shell tab */
widget_shell *shell = widget_shell_create(pe_shell_console, stack);
widget_shell_set_line_spacing(shell, _(1, 3));
jwidget_set_stretch(shell, 1, 1, false);
pe_shell = widget_shell_create(pe_console, stack);
widget_shell_set_line_spacing(pe_shell, _(1, 3));
jwidget_set_stretch(pe_shell, 1, 1, false);
#ifdef FX9860G
bool show_title_in_shell = false;
@ -262,41 +276,24 @@ int main(int argc, char **argv)
/* Initial state */
jfileselect_browse(fileselect, "/");
jscene_show_and_focus(scene, fileselect);
jscene_show_and_focus(pe_scene, fileselect);
//=== Event handling ===//
while(1) {
jevent e = jscene_run(scene);
jevent e = jscene_run(pe_scene);
if(e.type == JSCENE_PAINT) {
dclear(C_WHITE);
jscene_render(scene);
/* Render shell modifiers above the scene in a convenient spot */
int shift, alpha, layer;
bool instant;
widget_shell_get_modifiers(shell, &shift, &alpha);
widget_shell_modifier_info(shift, alpha, &layer, &instant);
int icon = 2 * layer + !instant;
#ifdef FX9860G
dsubimage(118, 58, &img_modifier_states, 9*icon+1, 1, 8, 6,
DIMAGE_NONE);
#else
dsubimage(377, 207, &img_modifier_states, 16*icon, 0, 15, 14,
DIMAGE_NONE);
#endif
dupdate();
pe_draw();
}
if(e.type == WIDGET_SHELL_MOD_CHANGED)
scene->widget.update = true;
pe_scene->widget.update = true;
if(e.type == WIDGET_SHELL_INPUT) {
char *line = (char *)e.data;
shell_write_str(line);
pyexec_repl_execute(line);
free(line);
shell_write_char('\r');
pe_print_prompt(1);
}
@ -304,13 +301,18 @@ int main(int argc, char **argv)
char const *path = jfileselect_selected_file(fileselect);
char *module = path_to_module(path);
if(module) {
jscene_show_and_focus(scene, shell);
jscene_show_and_focus(pe_scene, pe_shell);
jwidget_set_visible(title, show_title_in_shell);
pe_reset_micropython();
shell_write_str("import ");
shell_write_str(module);
shell_write_str("\r\n");
char *str = malloc(8 + strlen(module) + 1);
if(str) {
strcpy(str, "import ");
strcat(str, module);
pyexec_repl_execute(str);
free(str);
}
free(module);
}
}
@ -325,11 +327,11 @@ int main(int argc, char **argv)
pe_debug_kmalloc();
if(key == KEY_F1) {
jscene_show_and_focus(scene, fileselect);
jscene_show_and_focus(pe_scene, fileselect);
jwidget_set_visible(title, true);
}
if(key == KEY_F2) {
jscene_show_and_focus(scene, shell);
jscene_show_and_focus(pe_scene, pe_shell);
jwidget_set_visible(title, show_title_in_shell);
}
}
@ -338,7 +340,7 @@ int main(int argc, char **argv)
gc_sweep_all();
mp_deinit();
console_destroy(pe_shell_console);
console_destroy(pe_console);
return 0;
}

View File

@ -56,6 +56,7 @@ STATIC bool repl_display_debugging_info = 0;
#define EXEC_FLAG_SOURCE_IS_VSTR (1 << 4)
#define EXEC_FLAG_SOURCE_IS_FILENAME (1 << 5)
#define EXEC_FLAG_SOURCE_IS_READER (1 << 6)
#define EXEC_FLAG_SOURCE_IS_STR (1 << 7)
// parses, compiles and executes the code in the lexer
// frees the lexer before returning
@ -95,6 +96,9 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) {
const vstr_t *vstr = source;
lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0);
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_STR) {
lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_,
source, strlen(source), 0);
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) {
lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source);
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) {
@ -184,9 +188,6 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
#if MICROPY_ENABLE_COMPILER
STATIC int pyexec_raw_repl_process_char(int c);
void pyexec_event_repl_init(void) {
MP_STATE_VM(repl_line) = vstr_new(32);
@ -225,6 +226,21 @@ int pyexec_event_repl_process_char(int c) {
return res;
}
int pyexec_repl_execute(char const *line) {
pyexec_repl_active = 1;
int ret = parse_compile_execute(line,
MP_PARSE_SINGLE_INPUT,
EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL |
EXEC_FLAG_SOURCE_IS_STR);
if (ret & PYEXEC_FORCED_EXIT) {
return ret;
}
pyexec_repl_active = 0;
return 0;
}
MP_REGISTER_ROOT_POINTER(vstr_t * repl_line);
#endif // MICROPY_ENABLE_COMPILER

View File

@ -41,6 +41,7 @@ int pyexec_file_if_exists(const char *filename);
int pyexec_frozen_module(const char *name);
void pyexec_event_repl_init(void);
int pyexec_event_repl_process_char(int c);
int pyexec_repl_execute(char const *line);
extern uint8_t pyexec_repl_active;
#if MICROPY_REPL_INFO

View File

@ -250,7 +250,7 @@ static bool widget_shell_poly_event(void *s0, jevent e)
if(ev.key == KEY_EXE) {
char *line = console_get_line(s->console, true);
console_new_line(s->console);
console_newline(s->console);
jevent e = {
.type = WIDGET_SHELL_INPUT,
@ -262,7 +262,7 @@ static bool widget_shell_poly_event(void *s0, jevent e)
char c = keymap_translate(ev.key, ev.shift, ev.alpha);
if(c != 0) {
console_write_at_cursor(s->console, &c, 1);
console_write_raw(s->console, &c, 1);
return true;
}

View File

@ -71,7 +71,7 @@ extern uint16_t WIDGET_SHELL_MOD_CHANGED;
extern uint16_t WIDGET_SHELL_INPUT;
/* Update frequency, ie. cap on the number of shell redraws per second. */
#define WIDGET_SHELL_FPS 30
#define WIDGET_SHELL_FPS 10
/* widget_shell_create(): Create a shell widget tied to a console */
widget_shell *widget_shell_create(console_t *console, void *parent);