diff --git a/libr/cons/cons.c b/libr/cons/cons.c index 7eb6ea42430c1..590fa397611aa 100644 --- a/libr/cons/cons.c +++ b/libr/cons/cons.c @@ -511,7 +511,7 @@ R_API void r_cons_fill_line() { R_API void r_cons_clear_line(int std_err) { #if __WINDOWS__ if (I.ansicon) { - fprintf (std_err? stderr: stdout,"\x1b[0K\r"); + fprintf (std_err? stderr: stdout,"%s", R_CONS_CLEAR_LINE); } else { char white[1024]; memset (&white, ' ', sizeof (white)); @@ -525,7 +525,7 @@ R_API void r_cons_clear_line(int std_err) { fprintf (std_err? stderr: stdout, "\r%s\r", white); } #else - fprintf (std_err? stderr: stdout,"\x1b[0K\r"); + fprintf (std_err? stderr: stdout,"%s", R_CONS_CLEAR_LINE); #endif fflush (std_err? stderr: stdout); } diff --git a/libr/cons/dietline.c b/libr/cons/dietline.c index fe9df4fd7b661..4cf97264ee1b4 100644 --- a/libr/cons/dietline.c +++ b/libr/cons/dietline.c @@ -442,7 +442,113 @@ R_API int r_line_hist_chop(const char *file, int limit) { return 0; } +static void draw_selection_widget() { + RCons *cons = r_cons_singleton (); + RSelWidget *sel_widget = I.sel_widget; + int y, pos_y = cons->rows, pos_x = r_str_ansi_len (I.prompt); + + for (y = 0; y < sel_widget->options_len; y++) { + sel_widget->w = R_MAX (sel_widget->w, strlen (sel_widget->options[y])); + } + sel_widget->w = R_MIN (sel_widget->w, R_SELWIDGET_MAXW); + + char *background_color = cons->color ? cons->pal.widget_bg : Color_INVERT_RESET; + char *selected_color = cons->color ? cons->pal.widget_sel : Color_INVERT; + bool scrollbar = sel_widget->options_len > R_SELWIDGET_MAXH; + int scrollbar_y = 0, scrollbar_l = 0; + if (scrollbar) { + scrollbar_y = (R_SELWIDGET_MAXH * (sel_widget->selection - sel_widget->scroll)) / sel_widget->options_len; + scrollbar_l = (R_SELWIDGET_MAXH * R_SELWIDGET_MAXH) / sel_widget->options_len; + } + + for (y = 0; y < R_MIN (sel_widget->h, R_SELWIDGET_MAXH); y++) { + r_cons_gotoxy (pos_x + 1, pos_y - y - 1); + int scroll = R_MAX (0, sel_widget->selection - sel_widget->scroll); + char *option = y < sel_widget->options_len ? sel_widget->options[y + scroll] : ""; + r_cons_printf ("%s", sel_widget->selection == y + scroll ? selected_color : background_color); + r_cons_printf ("%-*.*s", sel_widget->w, sel_widget->w, option); + if (scrollbar && R_BETWEEN (scrollbar_y, y, scrollbar_y + scrollbar_l)) { + r_cons_memcat (Color_INVERT" "Color_INVERT_RESET, 10); + } else { + r_cons_memcat (" ", 1); + } + } + + r_cons_gotoxy (pos_x + I.buffer.length, pos_y); + r_cons_memcat (Color_RESET_BG, 5); + r_cons_flush (); +} + +static void selection_widget_up() { + RSelWidget *sel_widget = I.sel_widget; + if (sel_widget && sel_widget->selection < sel_widget->options_len - 1) { + sel_widget->selection++; + sel_widget->scroll = R_MIN (sel_widget->scroll + 1, R_SELWIDGET_MAXH - 1); + } +} + +static void selection_widget_down() { + RSelWidget *sel_widget = I.sel_widget; + if (sel_widget && sel_widget->selection > 0) { + sel_widget->selection--; + sel_widget->scroll = R_MAX (sel_widget->scroll - 1, 0); + } +} + +static void print_rline_task(void *core) { + r_cons_clear_line (0); + r_cons_printf ("%s%s%s", Color_RESET, I.prompt, I.buffer.data); + r_cons_flush (); +} + +static void selection_widget_erase() { + RSelWidget *sel_widget = I.sel_widget; + if (sel_widget) { + sel_widget->options_len = 0; + sel_widget->selection = -1; + draw_selection_widget (); + R_FREE (I.sel_widget); + RCons *cons = r_cons_singleton (); + if (cons->event_resize && cons->event_data) { + cons->event_resize (cons->event_data); + cons->cb_task_oneshot (cons->user, print_rline_task, NULL); + } + } +} + +static void selection_widget_select() { + RSelWidget *sel_widget = I.sel_widget; + if (sel_widget && sel_widget->selection < sel_widget->options_len) { + I.buffer.length = R_MIN (strlen (sel_widget->options[sel_widget->selection]), R_LINE_BUFSIZE - 1); + memcpy (I.buffer.data, sel_widget->options[sel_widget->selection], I.buffer.length); + I.buffer.data[I.buffer.length] = '\0'; + I.buffer.index = I.buffer.length; + selection_widget_erase (); + } +} + +static void selection_widget_update() { + if (I.completion.argc == 0 || + (I.completion.argc == 1 && I.buffer.length >= strlen (I.completion.argv[0]))) { + selection_widget_erase (); + return; + } + if (!I.sel_widget) { + RSelWidget *sel_widget = R_NEW0 (RSelWidget); + I.sel_widget = sel_widget; + } + I.sel_widget->scroll = 0; + I.sel_widget->selection = 0; + I.sel_widget->options_len = I.completion.argc; + I.sel_widget->options = I.completion.argv; + I.sel_widget->h = R_MAX (I.sel_widget->h, I.completion.argc); + draw_selection_widget (); + r_cons_flush (); + return; +} + R_API void r_line_autocomplete() { + int argc = 0; char *p; const char **argv = NULL; @@ -456,6 +562,11 @@ R_API void r_line_autocomplete() { argv = I.completion.argv; } + if (I.sel_widget && !I.sel_widget->complete_common) { + selection_widget_update (); + return; + } + p = (char *) r_sub_str_lchr (I.buffer.data, 0, I.buffer.index, ' '); if (!p) { p = (char *) r_sub_str_lchr (I.buffer.data, 0, I.buffer.index, '@'); // HACK FOR r2 @@ -530,6 +641,14 @@ R_API void r_line_autocomplete() { } } + if (I.offset_prompt || I.file_prompt) { + selection_widget_update (); + if (I.sel_widget) { + I.sel_widget->complete_common = false; + } + return; + } + /* show options */ if (opt > 1 && I.echo) { const int sep = 3; @@ -575,6 +694,7 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { char *tmp_ed_cmd, prev = 0; HANDLE hClipBoard; char *clipText; + int prev_buflen = 0; I.buffer.index = I.buffer.length = 0; I.buffer.data[0] = '\0'; @@ -597,7 +717,7 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { if (I.echo) { r_cons_clear_line (0); - printf ("\x1b[0K\r%s%s", I.prompt, I.buffer.data); + printf ("%s%s%s", Color_RESET, I.prompt, I.buffer.data); fflush (stdout); } r_cons_break_push (NULL, NULL); @@ -632,7 +752,10 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { I.buffer.index = I.buffer.index? I.buffer.index - 1: 0; break; case 38: // up arrow - if (gcomp) { + if (I.sel_widget) { + selection_widget_up (); + draw_selection_widget (); + } else if (gcomp) { gcomp_idx++; } else if (r_line_hist_up () == -1) { r_cons_break_pop (); @@ -644,7 +767,10 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { I.buffer.index + 1: I.buffer.length; break; case 40:// down arrow - if (gcomp) { + if (I.sel_widget) { + selection_widget_up (); + draw_selection_widget (); + } else if (gcomp) { if (gcomp_idx > 0) { gcomp_idx--; } @@ -831,7 +957,10 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { paste (); break; case 14:// ^n - if (gcomp) { + if (I.sel_widget) { + selection_widget_down (); + draw_selection_widget (); + } else if (gcomp) { if (gcomp_idx > 0) { gcomp_idx--; } @@ -840,7 +969,10 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { } break; case 16:// ^p - if (gcomp) { + if (I.sel_widget) { + selection_widget_down (); + draw_selection_widget (); + } else if (gcomp) { gcomp_idx++; } else { r_line_hist_up (); @@ -874,10 +1006,17 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { break; /* tab */ case 9: // tab + if (I.sel_widget) { + I.sel_widget->complete_common = true; + } r_line_autocomplete (); break; /* enter */ case 13: + if (I.sel_widget) { + selection_widget_select (); + break; + } if (gcomp && I.buffer.length > 0) { strncpy (I.buffer.data, gcomp_line, R_LINE_BUFSIZE - 1); I.buffer.data[R_LINE_BUFSIZE - 1] = '\0'; @@ -905,6 +1044,10 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { I.buffer.index++; break; } + if (I.sel_widget && I.buffer.length != prev_buflen) { + prev_buflen = I.buffer.length; + r_line_autocomplete (); + } prev = buf[0]; if (I.echo) { if (gcomp) { @@ -927,7 +1070,7 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { int chars = R_MAX (1, strlen (I.buffer.data)); // wtf? int len, cols = R_MAX (1, columns - r_str_ansi_len (I.prompt) - 2); /* print line */ - printf ("\r%s", I.prompt); + printf ("\r%s%s", Color_RESET, I.prompt); fwrite (I.buffer.data, 1, R_MIN (cols, chars), stdout); /* place cursor */ printf ("\r%s", I.prompt); @@ -956,6 +1099,8 @@ R_API const char *r_line_readline_cb_win(RLineReadCallback cb, void *user) { fflush (stdout); } + R_FREE (I.sel_widget); + // should be here or not? if (!memcmp (I.buffer.data, "!history", 8)) { r_line_hist_list (); @@ -980,6 +1125,7 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { #endif int ch, i = 0; /* grep completion */ char *tmp_ed_cmd, prev = 0; + int prev_buflen = -1; I.buffer.index = I.buffer.length = 0; I.buffer.data[0] = '\0'; @@ -1002,7 +1148,7 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { if (I.echo) { r_cons_clear_line (0); - printf ("\x1b[0K\r%s%s", I.prompt, I.buffer.data); + printf ("%s%s%s", Color_RESET, I.prompt, I.buffer.data); fflush (stdout); } r_cons_break_push (NULL, NULL); @@ -1196,7 +1342,10 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { paste (); break; case 14:// ^n - if (gcomp) { + if (I.sel_widget) { + selection_widget_down (); + draw_selection_widget (); + } else if (gcomp) { if (gcomp_idx > 0) { gcomp_idx--; } @@ -1205,7 +1354,10 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { } break; case 16:// ^p - if (gcomp) { + if (I.sel_widget) { + selection_widget_up (); + draw_selection_widget (); + } else if (gcomp) { gcomp_idx++; } else { r_line_hist_up (); @@ -1278,7 +1430,10 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { break; /* arrows */ case 'A': // up arrow - if (gcomp) { + if (I.sel_widget) { + selection_widget_up (); + draw_selection_widget (); + } else if (gcomp) { gcomp_idx++; } else if (r_line_hist_up () == -1) { r_cons_break_pop (); @@ -1286,7 +1441,10 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { } break; case 'B': // down arrow - if (gcomp) { + if (I.sel_widget) { + selection_widget_down (); + draw_selection_widget (); + } else if (gcomp) { if (gcomp_idx > 0) { gcomp_idx--; } @@ -1450,9 +1608,16 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { I.buffer.length++; I.buffer.index++; } + if (I.sel_widget) { + I.sel_widget->complete_common = true; + } r_line_autocomplete (); break; - case 13: + case 13: // enter + if (I.sel_widget) { + selection_widget_select (); + break; + } if (gcomp && I.buffer.length > 0) { strncpy (I.buffer.data, gcomp_line, R_LINE_BUFSIZE - 1); I.buffer.data[R_LINE_BUFSIZE - 1] = '\0'; @@ -1506,6 +1671,10 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { #endif break; } + if (I.sel_widget && I.buffer.length != prev_buflen) { + prev_buflen = I.buffer.length; + r_line_autocomplete (); + } prev = buf[0]; if (I.echo) { if (gcomp) { @@ -1528,7 +1697,7 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { int chars = R_MAX (1, strlen (I.buffer.data)); // wtf? int len, cols = R_MAX (1, columns - r_str_ansi_len (I.prompt) - 2); /* print line */ - printf ("\r%s", I.prompt); + printf ("\r%s%s", Color_RESET, I.prompt); fwrite (I.buffer.data, 1, R_MIN (cols, chars), stdout); /* place cursor */ printf ("\r%s", I.prompt); @@ -1557,6 +1726,8 @@ R_API const char *r_line_readline_cb(RLineReadCallback cb, void *user) { fflush (stdout); } + R_FREE (I.sel_widget); + // should be here or not? if (!memcmp (I.buffer.data, "!history", 8)) { // if (I.buffer.data[0]=='!' && I.buffer.data[1]=='\0') { diff --git a/libr/cons/pal.c b/libr/cons/pal.c index 04962d8336322..0929eae84e998 100644 --- a/libr/cons/pal.c +++ b/libr/cons/pal.c @@ -50,6 +50,8 @@ static struct { { "func_var", r_offsetof (RConsPrintablePalette, func_var), r_offsetof (RConsPalette, func_var) }, { "func_var_type", r_offsetof (RConsPrintablePalette, func_var_type), r_offsetof (RConsPalette, func_var_type) }, { "func_var_addr", r_offsetof (RConsPrintablePalette, func_var_addr), r_offsetof (RConsPalette, func_var_addr) }, + { "widget_bg", r_offsetof (RConsPrintablePalette, widget_bg), r_offsetof (RConsPalette, widget_bg) }, + { "widget_sel", r_offsetof (RConsPrintablePalette, widget_sel), r_offsetof (RConsPalette, widget_sel) }, { "ai.read", r_offsetof (RConsPrintablePalette, ai_read), r_offsetof (RConsPalette, ai_read) }, { "ai.write", r_offsetof (RConsPrintablePalette, ai_write), r_offsetof (RConsPalette, ai_write) }, @@ -173,6 +175,9 @@ R_API void r_cons_pal_init() { cons->cpal.func_var_type = (RColor) RColor_BLUE; cons->cpal.func_var_addr = (RColor) RColor_CYAN; + cons->cpal.widget_bg = (RColor) RCOLOR (ALPHA_BG, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00); + cons->cpal.widget_sel = (RColor) RColor_BGRED; + cons->cpal.graph_box = (RColor) RColor_NULL; cons->cpal.graph_box2 = (RColor) RColor_BLUE; cons->cpal.graph_box3 = (RColor) RColor_MAGENTA; diff --git a/libr/cons/rgb.c b/libr/cons/rgb.c index ef74ad4acb6e5..374c6b87541bd 100644 --- a/libr/cons/rgb.c +++ b/libr/cons/rgb.c @@ -204,12 +204,18 @@ static void r_cons_rgb_gen(char *outstr, size_t sz, ut8 attr, ut8 a, ut8 r, ut8 case COLOR_MODE_16: // ansi 16 colors { fgbg -= 8; - ut8 k = (r + g + b) / 3; - r = (r >= k) ? 1 : 0; - g = (g >= k) ? 1 : 0; - b = (b >= k) ? 1 : 0; - k = (r ? 1 : 0) + (g ? (b ? 6 : 2) : (b ? 4 : 0)); - written = snprintf (outstr + i, sz - i, "%dm", fgbg + k); + if (r == g && g == b) { + r = (r > 0x7f) ? 1 : 0; + g = (g > 0x7f) ? 1 : 0; + b = (b > 0x7f) ? 1 : 0; + } else { + ut8 k = (r + g + b) / 3; + r = (r >= k) ? 1 : 0; + g = (g >= k) ? 1 : 0; + b = (b >= k) ? 1 : 0; + } + ut8 c = (r ? 1 : 0) + (g ? (b ? 6 : 2) : (b ? 4 : 0)); + written = snprintf (outstr + i, sz - i, "%dm", fgbg + c); } break; } diff --git a/libr/core/core.c b/libr/core/core.c index b867bc4c659e5..e6b1a866f8309 100644 --- a/libr/core/core.c +++ b/libr/core/core.c @@ -1407,6 +1407,8 @@ static int autocomplete(RLine *line) { ADDARG("func_var") ADDARG("func_var_type") ADDARG("func_var_addr") + ADDARG("widget_bg") + ADDARG("widget_sel") ADDARG("ai.read") ADDARG("ai.write") ADDARG("ai.exec") @@ -2236,6 +2238,7 @@ R_API void r_core_bind_cons(RCore *core) { core->cons->cb_break = (RConsBreakCallback)r_core_break; core->cons->cb_sleep_begin = (RConsSleepBeginCallback)r_core_sleep_begin; core->cons->cb_sleep_end = (RConsSleepEndCallback)r_core_sleep_end; + core->cons->cb_task_oneshot = (RConsQueueTaskOneshot) r_core_task_enqueue_oneshot; core->cons->user = (void*)core; } diff --git a/libr/core/graph.c b/libr/core/graph.c index 595d26ba19c4f..74703fdf3b588 100644 --- a/libr/core/graph.c +++ b/libr/core/graph.c @@ -3649,11 +3649,10 @@ static void goto_asmqjmps(RAGraph *g, RCore *core) { r_cons_get_size (&rows); r_cons_gotoxy (0, rows); + r_cons_clear_line (0); r_cons_printf (Color_RESET); r_cons_printf (h); r_cons_flush (); - r_cons_clear_line (0); - r_cons_gotoxy (strlen (h) + 1, rows); do { char ch = r_cons_readchar (); diff --git a/libr/include/r_cons.h b/libr/include/r_cons.h index fa07caa495ff1..47eb2bf613702 100644 --- a/libr/include/r_cons.h +++ b/libr/include/r_cons.h @@ -210,6 +210,8 @@ typedef struct r_cons_palette_t { RColor func_var; RColor func_var_type; RColor func_var_addr; + RColor widget_bg; + RColor widget_sel; /* Graph colors */ RColor graph_box; @@ -278,6 +280,8 @@ typedef struct r_cons_printable_palette_t { char *func_var; char *func_var_type; char *func_var_addr; + char *widget_bg; + char *widget_sel; /* graph colors */ char *graph_box; @@ -377,6 +381,7 @@ typedef int (*RConsClickCallback)(void *core, int x, int y); typedef void (*RConsBreakCallback)(void *core); typedef void *(*RConsSleepBeginCallback)(void *core); typedef void (*RConsSleepEndCallback)(void *core, void *user); +typedef void (*RConsQueueTaskOneshot)(void *core, void *task, void *user); typedef struct r_cons_context_t { RConsGrep grep; @@ -420,6 +425,7 @@ typedef struct r_cons_t { RConsSleepBeginCallback cb_sleep_begin; RConsSleepEndCallback cb_sleep_end; RConsClickCallback cb_click; + RConsQueueTaskOneshot cb_task_oneshot; void *user; // Used by #if __UNIX__ || __CYGWIN__ && !defined(MINGW32) @@ -487,6 +493,8 @@ typedef struct r_cons_t { #define R_CONS_KEY_ESC 0x1b +#define R_CONS_CLEAR_LINE "\x1b[2K\r" + #define Color_BLINK "\x1b[5m" #define Color_INVERT "\x1b[7m" #define Color_INVERT_RESET "\x1b[27m" @@ -795,6 +803,18 @@ R_API const char* r_cons_get_rune(const ut8 ch); #define R_EDGES_X_INC 4 +#define R_SELWIDGET_MAXH 15 +#define R_SELWIDGET_MAXW 30 + +typedef struct r_selection_widget_t { + char **options; + int options_len; + int selection; + int w, h; + int scroll; + bool complete_common; +} RSelWidget; + typedef struct r_line_hist_t { char **data; int size; @@ -827,6 +847,7 @@ struct r_line_t { RLineCompletion completion; RLineBuffer buffer; RLineHistory history; + RSelWidget *sel_widget; /* callbacks */ RLineHistoryUpCb cb_history_up; RLineHistoryDownCb cb_history_down;