// script console #include "bmp.h" #include "dryos.h" #include "menu.h" #include "gui.h" #include "property.h" #include "config.h" #include "zebra.h" #include "shoot.h" #include "alloca.h" #ifndef CONFIG_CONSOLE #error Something went wrong CONFIg_CONSOLE should be defined #endif #undef CONSOLE_DEBUG // logs things to file etc #define CONSOLE_W 80 #define CONSOLE_H 21 #define CONSOLE_FONT FONT_MONO_20 // buffer is circular and filled with spaces #define BUFSIZE (CONSOLE_H * CONSOLE_W) static char console_buffer[BUFSIZE] = {[0 ... BUFSIZE-1] = ' '}; static int console_buffer_index = 0; #define CONSOLE_BUFFER(i) console_buffer[MOD((i), BUFSIZE)] int console_visible = 0; void console_show() { console_visible = 1; redraw(); } void console_hide() { console_visible = 0; msleep(100); canon_gui_enable_front_buffer(1); } void console_toggle() { if (console_visible) console_hide(); else console_show(); } static void console_toggle_menu( void * priv, int delta ) { if (console_visible) console_hide(); else console_show(); } #ifdef CONSOLE_DEBUG static void console_test( void * priv ) { console_visible = 1; printf("Hello World!\n"); printf("The quick brown fox jumps over the lazy dog. Computer programs expand so as to fill the core available. El trabajo y la economia son la mejor loteria. \n"); } static struct menu_entry script_menu[] = { { .name = "Debug Console", .priv = &console_visible, .select = console_toggle_menu, .min = 0, .max = 1, }, }; #endif void console_clear() { int i; for (i = 0; i < BUFSIZE; i++) console_buffer[i] = ' '; } static void console_init() { #ifdef CONSOLE_DEBUG menu_add( "Debug", script_menu, COUNT(script_menu) ); FIO_RemoveFile("ML/LOGS/console.log"); #endif } void console_puts(const char* str) // don't DebugMsg from here! { #define NEW_CHAR(c) CONSOLE_BUFFER(console_buffer_index++) = (c) /* this only runs when compiling with CONFIG_QEMU */ qprintf("%s", str); #ifdef CONSOLE_DEBUG bmp_printf(FONT_MED, 0, 0, "%s ", str); FILE* f = FIO_CreateFileOrAppend("ML/LOGS/console.log"); if (f) { FIO_WriteFile( f, str, strlen(str) ); FIO_CloseFile(f); } //~ msleep(100); /* uncomment this to troubleshoot things that lockup the camera - to make sure FIO tasks actually flushed everything */ #endif /* for handling carriage returns */ static int cr = 0; const char* c = str; while (*c) { if (*c == '\n') { if (MOD(console_buffer_index, CONSOLE_W) == 0) NEW_CHAR(' '); while (MOD(console_buffer_index, CONSOLE_W) != 0) NEW_CHAR(' '); cr = 0; } else if (*c == '\t') { NEW_CHAR(' '); while (MOD(console_buffer_index, 4) != 0) NEW_CHAR(' '); } else if (*c == '\b') { /* only erase on current line */ if (MOD(console_buffer_index, CONSOLE_W) != 0) { console_buffer_index--; CONSOLE_BUFFER(console_buffer_index) = ' '; } } else if (*c == '\r') { cr = 1; /* will handle it later */ } else { if (cr) /* need to handle a carriage return without line feed */ { while (MOD(console_buffer_index, CONSOLE_W)) console_buffer_index--; cr = 0; } NEW_CHAR(*c); } c++; } console_buffer_index = MOD(console_buffer_index, BUFSIZE); } static void console_draw(int tiny) { int cbpos0 = MOD((console_buffer_index / CONSOLE_W) * CONSOLE_W + CONSOLE_W, BUFSIZE); /* display last two lines that actually contain something (don't display the cursor-only line) */ if (tiny && console_buffer_index % CONSOLE_W == 0) cbpos0 -= CONSOLE_W; int skipped_lines = 0; int chopped_columns = 0; /* skip empty lines at the top */ for (int i = 0; i < CONSOLE_H; i++) { int cbpos = cbpos0 + i * CONSOLE_W; int empty = 1; for (int j = 0; j < CONSOLE_W; j++) if (CONSOLE_BUFFER(cbpos + j) != ' ') { empty = 0; break; } if (empty) skipped_lines++; else break; } if (skipped_lines == CONSOLE_H) // nothing to show return; if (tiny) skipped_lines = CONSOLE_H - 3; /* chop empty columns from the right */ for (int j = CONSOLE_W-1; j > 0; j--) { int empty = 1; for (int i = skipped_lines; i < CONSOLE_H; i++) if (CONSOLE_BUFFER(cbpos0 + i*CONSOLE_W + j) != ' ') { empty = 0; break; } if (empty) chopped_columns++; else break; } chopped_columns = MIN(chopped_columns, CONSOLE_W - (console_buffer_index % CONSOLE_W)); if (skipped_lines < 5) skipped_lines = 0; if (chopped_columns < 5) chopped_columns = 0; /* top-left corner of "full" console (without lines/columns skipped) */ unsigned x0 = (chopped_columns < 7) ? 0 : 8; unsigned y0 = 480/2 - fontspec_font(CONSOLE_FONT)->height * CONSOLE_H/2; /* correct y to account for skipped lines */ int yc = y0; if (tiny) { yc = gui_menu_shown() || MENU_MODE ? 415 : y0; } else { yc = y0 + fontspec_font(CONSOLE_FONT)->height * skipped_lines; } int fnt = FONT(CONSOLE_FONT,COLOR_WHITE, (lv || PLAY_OR_QR_MODE) ? COLOR_BG_DARK : COLOR_ALMOST_BLACK); int w = MIN((chopped_columns < 7) ? 720 : 704, fontspec_font(fnt)->width * (CONSOLE_W - chopped_columns) + 2); int h = fontspec_font(fnt)->height * (CONSOLE_H - skipped_lines); /* did the console shrink? if so, redraw Canon GUI around it */ static int prev_w = 0; static int prev_h = 0; if (w < prev_w || h < prev_h) { canon_gui_enable_front_buffer(1); // force a redraw prev_w = w; prev_h = h; //return; // better luck next time :) } else if (!tiny) { /* fixme: prevent Canon code from drawing over the console (ugly) */ canon_gui_disable_front_buffer(); } prev_w = w; prev_h = h; /* display each line */ int found_cursor = 0; int printed_width = 0; for (int i = skipped_lines; i < CONSOLE_H; i++) { char buf[CONSOLE_W+1]; int cbpos = cbpos0 + i * CONSOLE_W; for (int j = 0; j < CONSOLE_W; j++) { // last character should be on last line => this ensures proper scrolling if (MOD(cbpos+j, BUFSIZE) == MOD(console_buffer_index, BUFSIZE)) // end of data { if (!found_cursor) { buf[j] = '_'; found_cursor = 1; continue; } } buf[j] = found_cursor ? ' ' : CONSOLE_BUFFER(cbpos+j); if (buf[j] == 0) buf[j] = '?'; } buf[CONSOLE_W - chopped_columns] = 0; int y = yc + fontspec_font(fnt)->height * (i - skipped_lines); printed_width = bmp_printf(fnt | FONT_ALIGN_JUSTIFIED | FONT_TEXT_WIDTH(w), x0, y, "%s", buf); } bmp_draw_rect(60, x0-1, yc-1, printed_width+2, h+2); bmp_draw_rect(COLOR_BLACK, x0-2, yc-2, printed_width+4, h+4); } void console_draw_from_menu() { if (console_visible) console_draw(1); } static void console_task( void* unused ) { console_init(); #ifdef CONSOLE_DEBUG console_show(); #endif int dirty = 0; TASK_LOOP { // show the console only when there are no Canon dialogs on the screen if (console_visible && (display_idle() || is_pure_play_photo_or_movie_mode())) { if (dirty) console_draw(0); dirty = 1; } else if (dirty) { canon_gui_enable_front_buffer(1); dirty = 0; } else if (console_visible && !gui_menu_shown()) { console_draw(1); } msleep(200); } } TASK_CREATE( "console_task", console_task, 0, 0x1d, 0x1000 ); /* some functions from standard I/O */ int printf(const char* fmt, ...) { /* when called from init_task, 512 bytes are enough to cause stack overflow */ extern int ml_started; int buf_size = (ml_started) ? 512 : 64; char* buf = alloca(buf_size); va_list ap; va_start( ap, fmt ); int len = vsnprintf( buf, buf_size-1, fmt, ap ); va_end( ap ); console_puts(buf); return len; } int puts(const char * fmt) { console_puts(fmt); console_puts("\n"); return 0; }