//---------------------------------------------------------------------------// // ____ PythonExtra // //.-'`_ o `;__, A community port of MicroPython for CASIO calculators. // //.-'` `---` ' License: MIT (except some files; see LICENSE) // //---------------------------------------------------------------------------// #include "py/compile.h" #include "py/gc.h" #include "py/stackctrl.h" #include "py/builtin.h" #include "py/mphal.h" #include "py/repl.h" #include "shared/runtime/gchelper.h" #include "shared/runtime/pyexec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mpconfigport.h" #include "console.h" #include "widget_shell.h" #include "debug.h" #ifdef FX9860G extern bopti_image_t const img_fkeys_main; extern bopti_image_t const img_modifier_states; extern font_t const font_4x4, font_4x6, font_5x7; #define _(fx, cg) (fx) #else extern bopti_image_t const img_modifier_states; #define _(fx, cg) (cg) #endif static ssize_t stdouterr_write(void *data, void const *buf, size_t size) { console_t *cons = data; console_write_at_cursor(cons, buf, size); return size; } fs_descriptor_type_t stdouterr_type = { .read = NULL, .write = stdouterr_write, .lseek = NULL, .close = NULL, }; /* The global terminal. */ console_t *pe_shell_console; static bool strendswith(char const *str, char const *suffix) { size_t l1 = strlen(str); size_t l2 = strlen(suffix); return l1 >= l2 && strcmp(str + l1 - l2, suffix) == 0; } static bool py_file_filter(struct dirent const *ent) { if(!jfileselect_default_filter(ent)) return false; if(ent->d_type == DT_DIR) return true; 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] == '/') path++; int i, n = strlen(path); char *module = malloc(n + 1); if(!module) return NULL; for(i = 0; i < n; i++) { if(i == n - 3 && !strcmp(path + i, ".py")) break; module[i] = (path[i] == '/') ? '.' : path[i]; } module[i] = 0; return module; } /* Filter AC/ON push events asynchronously from the keyboard driver and interrupt MicroPython instead. */ static bool async_filter(key_event_t ev) { if(mp_interrupt_char < 0) return true; if(ev.type == KEYEV_DOWN && ev.key == KEY_ACON) { /* This function is designed to be called asynchronously. */ mp_sched_keyboard_interrupt(); return false; } return true; } int main(int argc, char **argv) { pe_debug_init(); pe_debug_kmalloc(); //=== Init sequence ===// keydev_set_async_filter(keydev_std(), async_filter); pe_shell_console = console_create(8192); /* 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); /* Initialize the MicroPython GC with most available memory */ mp_stack_ctrl_init(); #ifdef FX9860G /* Get *some* memory from the OS heap */ void *gc_area = malloc(32768); if(!gc_area) pe_debug_panic("No heap!"); gc_init(gc_area, gc_area + 32768); /* Add some Python ram */ void *py_ram_start = (void*)0x88053800; void *py_ram_end = (void*)0x8807f000; gc_add(py_ram_start, py_ram_end); #else /* Get everything from the OS stack (~ 350 ko) */ size_t gc_area_size; void *gc_area = kmalloc_max(&gc_area_size, "_ostk"); gc_init(gc_area, gc_area + gc_area_size); /* Other options: - All of _uram (leaving the OS heap for the shell/GUI/etc) - The OS' extra VRAM - Memory past the 2 MB boundary on tested OSes */ // gc_add(start, end)... #endif mp_init(); /* TODO: Add an option for the shorter prompt */ #ifdef FX9860G MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS1]) = MP_OBJ_NEW_QSTR(qstr_from_str(">")); MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS2]) = MP_OBJ_NEW_QSTR(qstr_from_str(".")); #endif /* Run REPL manually */ pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; pyexec_event_repl_init(); //=== 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); (void)fkeys; jwidget_set_stretch(title, 1, 0, false); jlayout_set_vbox(scene)->spacing = _(0, 3); jlayout_set_stack(stack); jwidget_set_stretch(stack, 1, 1, false); /* Filesystem tab */ jfileselect *fileselect = jfileselect_create(stack); jfileselect_set_filter(fileselect, py_file_filter); jfileselect_set_show_file_size(fileselect, true); 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); #ifdef FX9860G bool show_title_in_shell = false; jwidget_set_padding(title, 0, 0, 1, 0); widget_shell_set_font(shell, &font_4x6); #else bool show_title_in_shell = true; jwidget_set_background(title, C_BLACK); jlabel_set_text_color(title, C_WHITE); jwidget_set_padding(title, 3, 6, 3, 6); jwidget_set_padding(stack, 0, 6, 0, 6); #endif /* Initial state */ jfileselect_browse(fileselect, "/"); jscene_show_and_focus(scene, fileselect); //=== Event handling ===// while(1) { jevent e = jscene_run(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(); } if(e.type == WIDGET_SHELL_MOD_CHANGED) scene->widget.update = true; if(e.type == WIDGET_SHELL_CHAR_INPUT) shell_write_char(e.data); if(e.type == JFILESELECT_VALIDATED) { char const *path = jfileselect_selected_file(fileselect); char *module = path_to_module(path); if(module) { jscene_show_and_focus(scene, shell); jwidget_set_visible(title, show_title_in_shell); shell_write_str("import "); shell_write_str(module); shell_write_str("\r\n"); free(module); } } if(e.type != JWIDGET_KEY || e.key.type == KEYEV_UP) continue; int key = e.key.key; if(key == KEY_SQUARE && !e.key.shift && e.key.alpha) pe_debug_screenshot(); if(key == KEY_TAN) pe_debug_kmalloc(); if(key == KEY_F1) { jscene_show_and_focus(scene, fileselect); jwidget_set_visible(title, true); } if(key == KEY_F2) { jscene_show_and_focus(scene, shell); jwidget_set_visible(title, show_title_in_shell); } } //=== Deinitialization ===// gc_sweep_all(); mp_deinit(); console_destroy(pe_shell_console); return 0; } /* Handle uncaught exceptions (normally unreachable). */ void nlr_jump_fail(void *val) { dclear(C_BLACK); dtext(2, 2, C_WHITE, "nlr_jump_fail!"); dprint(2, 2, C_WHITE, "val = %p", val); dupdate(); while(1) getkey(); } /* Do a garbage collection cycle. */ void gc_collect(void) { gc_collect_start(); gc_helper_collect_regs_and_stack(); gc_collect_end(); } mp_import_stat_t mp_import_stat(const char *path) { struct stat st; int rc = stat(path, &st); if(rc < 0) return MP_IMPORT_STAT_NO_EXIST; if(S_ISDIR(st.st_mode)) return MP_IMPORT_STAT_DIR; else return MP_IMPORT_STAT_FILE; } // TODO: See branch 'posix-open' for a relevant attempt at using the POSIX API mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { mp_obj_t *args_items; size_t len; mp_obj_get_array(*args, &len, &args_items); printf("%d %p\n", (int)len, args_items); if(len != 2) return mp_const_none; char const *path = mp_obj_str_get_str(args_items[0]); char const *mode = mp_obj_str_get_str(args_items[1]); printf("'%s' '%s'\n", path, mode); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);