From 6c2aa39e56d83d10f3986fc9a52c7f770184fe80 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sun, 6 Nov 2022 17:08:17 +0100 Subject: [PATCH] pe: show shell modifier state in GUI --- README.md | 2 - ports/fx9860g3/Makefile | 4 +- ports/fx9860g3/fxconv-metadata.txt | 3 +- ports/fx9860g3/img_modifier_states.png | Bin 0 -> 8073 bytes ports/fxcg50/Makefile | 4 +- ports/fxcg50/fxconv-metadata.txt | 4 ++ ports/fxcg50/img_modifier_states.png | Bin 0 -> 802 bytes ports/sh/console.c | 4 +- ports/sh/main.c | 19 +++++++ ports/sh/widget_shell.c | 68 ++++++++++++++++++------- ports/sh/widget_shell.h | 28 ++++++++++ 11 files changed, 108 insertions(+), 28 deletions(-) create mode 100644 ports/fx9860g3/img_modifier_states.png create mode 100644 ports/fxcg50/fxconv-metadata.txt create mode 100644 ports/fxcg50/img_modifier_states.png diff --git a/README.md b/README.md index 645c64072..edfef00bc 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ Most of the code is in `ports/sh` and is shared between the platforms. Bugs to fix: - Fix not world switching during filesystem accesses (very unstable) -- Fix console line spacing being hardcoded instead of using font data - Fix the console not garbage collecting its lines (enable and test the feature) Python features: @@ -28,7 +27,6 @@ Python features: 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) - Use [unicode-fonts](/Lephenixnoir/unicode-fonts) to provide Unicode support diff --git a/ports/fx9860g3/Makefile b/ports/fx9860g3/Makefile index 07f8efb1a..a06997e56 100644 --- a/ports/fx9860g3/Makefile +++ b/ports/fx9860g3/Makefile @@ -3,7 +3,9 @@ include ../../py/mkenv.mk SH_CFLAGS := -DFX9860G SH_LDFLAGS := -T fx9860g.ld -ljustui-fx -lm -lgint-fx -lc -lgint-fx -lgcc -SH_ASSETS := img_fkeys_main.png font_5x7.png font_4x4.png font_4x6.png +SH_ASSETS := \ + img_fkeys_main.png img_modifier_states.png \ + font_5x7.png font_4x4.png font_4x6.png SH_METADATA := fxconv-metadata.txt SH_CONVFLAGS := --fx diff --git a/ports/fx9860g3/fxconv-metadata.txt b/ports/fx9860g3/fxconv-metadata.txt index 1214382c3..33128f0f9 100644 --- a/ports/fx9860g3/fxconv-metadata.txt +++ b/ports/fx9860g3/fxconv-metadata.txt @@ -1,8 +1,9 @@ -*.png: +img_*.png: type: bopti-image name_regex: (.*)\.png \1 font_*.png: + name_regex: (.*)\.png \1 type: font charset: print grid.padding: 1 diff --git a/ports/fx9860g3/img_modifier_states.png b/ports/fx9860g3/img_modifier_states.png new file mode 100644 index 0000000000000000000000000000000000000000..35f1617796adbe99a21ccb47a0fd42751c0a5976 GIT binary patch literal 8073 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.update = true; + 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(); } diff --git a/ports/sh/widget_shell.c b/ports/sh/widget_shell.c index 0239f5d98..5ebab020a 100644 --- a/ports/sh/widget_shell.c +++ b/ports/sh/widget_shell.c @@ -12,18 +12,11 @@ /* Type identified for widget_shell */ static int widget_shell_id = -1; +/* Events */ +uint16_t WIDGET_SHELL_MOD_CHANGED; + //=== Modifier states ===// -enum { - MOD_IDLE, /* Not active */ - MOD_INSTANT, /* Instant-loaded but not yet used */ - MOD_INSTANT_USED, /* Instant-loaded and has been used */ - - MOD_LOCKED, /* Locked */ - MOD_LOCKED_INSTANT, /* Locked and instant-loaded but not used */ - MOD_LOCKED_INSTANT_USED, /* Locked and instant-loaded and used */ -}; - /* Handle a key press/release for a modifier */ static int mod_down(int state) { @@ -60,6 +53,11 @@ static bool mod_active(int state) return state == MOD_LOCKED || state == MOD_INSTANT || state == MOD_INSTANT_USED; } +/* Whether a modifier is in an instant mode */ +static bool mod_instant(int state) +{ + return state != MOD_IDLE && state != MOD_LOCKED; +} //=== Shell widget ===// @@ -123,6 +121,23 @@ void widget_shell_set_line_spacing(widget_shell *s, int line_spacing) s->widget.dirty = 1; } +void widget_shell_get_modifiers(widget_shell *s, int *shift, int *alpha) +{ + if(shift) + *shift = s->shift; + if(alpha) + *alpha = s->alpha; +} + +void widget_shell_modifier_info(int shift, int alpha, + int *layer, bool *instant) +{ + if(layer) + *layer = mod_active(shift) + 2 * mod_active(alpha); + if(instant) + *instant = mod_instant(shift) || mod_instant(alpha); +} + //--- // Polymorphic widget operations //--- @@ -158,6 +173,26 @@ static void widget_shell_poly_render(void *s0, int x, int y) dfont(old_font); } +static void widget_shell_update_mod(widget_shell *s, key_event_t ev) +{ + int *mod = (ev.key == KEY_SHIFT) ? &s->shift : &s->alpha; + int new_mod = (ev.type == KEYEV_UP) ? mod_up(*mod) : mod_down(*mod); + if(new_mod != *mod) + jwidget_emit(s, (jevent){ .type = WIDGET_SHELL_MOD_CHANGED }); + *mod = new_mod; +} +static void widget_shell_use_mods(widget_shell *s) +{ + int new_shift = mod_down_other(s->shift); + int new_alpha = mod_down_other(s->alpha); + + if(new_shift != s->shift || new_alpha != s->alpha) + jwidget_emit(s, (jevent){ .type = WIDGET_SHELL_MOD_CHANGED }); + + s->shift = new_shift; + s->alpha = new_alpha; +} + static bool widget_shell_poly_event(void *s0, jevent e) { widget_shell *s = s0; @@ -166,12 +201,8 @@ static bool widget_shell_poly_event(void *s0, jevent e) return false; key_event_t ev = e.key; - if(ev.key == KEY_SHIFT) { - s->shift = ev.type == KEYEV_UP ? mod_up(s->shift) : mod_down(s->shift); - return true; - } - if(ev.key == KEY_ALPHA) { - s->alpha = ev.type == KEYEV_UP ? mod_up(s->alpha) : mod_down(s->alpha); + if(ev.key == KEY_SHIFT || ev.key == KEY_ALPHA) { + widget_shell_update_mod(s, ev); return true; } @@ -181,9 +212,7 @@ static bool widget_shell_poly_event(void *s0, jevent e) ev.mod = true; ev.shift = mod_active(s->shift); ev.alpha = mod_active(s->alpha); - - s->shift = mod_down_other(s->shift); - s->alpha = mod_down_other(s->alpha); + widget_shell_use_mods(s); /* TODO: Handle input events better in the shell widget! */ int c = console_key_event_to_char(ev); @@ -216,4 +245,5 @@ __attribute__((constructor)) static void j_register_widget_shell(void) { widget_shell_id = j_register_widget(&type_widget_shell, "jwidget"); + WIDGET_SHELL_MOD_CHANGED = j_register_event(); } diff --git a/ports/sh/widget_shell.h b/ports/sh/widget_shell.h index c449578ec..8bcba174a 100644 --- a/ports/sh/widget_shell.h +++ b/ports/sh/widget_shell.h @@ -32,6 +32,17 @@ #include #include "console.h" +/* Modifier states */ +enum { + MOD_IDLE, /* Not active */ + MOD_INSTANT, /* Instant-loaded but not yet used */ + MOD_INSTANT_USED, /* Instant-loaded and has been used */ + + MOD_LOCKED, /* Locked */ + MOD_LOCKED_INSTANT, /* Locked and instant-loaded but not used */ + MOD_LOCKED_INSTANT_USED, /* Locked and instant-loaded and used */ +}; + /* widget_shell: Multi-line Python shell input */ typedef struct { jwidget widget; @@ -53,6 +64,9 @@ typedef struct { } widget_shell; +/* Event IDs */ +extern uint16_t WIDGET_SHELL_MOD_CHANGED; + /* Update frequency, ie. cap on the number of shell redraws per second. */ #define WIDGET_SHELL_FPS 30 @@ -64,4 +78,18 @@ void widget_shell_set_text_color(widget_shell *shell, int color); void widget_shell_set_font(widget_shell *shell, font_t const *font); void widget_shell_set_line_spacing(widget_shell *shell, int line_spacing); +/* Get current modifier states */ +void widget_shell_get_modifiers(widget_shell *shell, int *shift, int *alpha); + +/* Turn a pair of shift/alpha modifiers into a more presentable form: + - The *current layer* which shows currently active modifiers: + 0: Normal + 1: Shifted (SHIFT) + 2: Lowercase (ALPHA) + 3: Uppercase (SHIFT+ALPHA) + - The *instant* boolean which is set when one of the modifiers is in + instant mode. This is the case when either SHIFT or ALPHA is pressed. */ +void widget_shell_modifier_info(int shift, int alpha, + int *layer, bool *instant); + #endif /* __PYTHONEXTRA_WIDGET_SHELL_H */