/* radare - LGPL - Copyright 2009-2018 - pancake */ #include #include #if __UNIX__ #include #endif /* experimental support for x/y click */ #define USE_CLICK 0 #define I r_cons_singleton () #if 0 //__UNIX__ #include static int is_fd_ready(int fd) { fd_set rfds; struct timeval tv; if (fd==-1) return 0; FD_ZERO (&rfds); FD_SET (fd, &rfds); tv.tv_sec = 0; tv.tv_usec = 1; if (select (1, &rfds, NULL, NULL, &tv) == -1) return 0; return 1; return !FD_ISSET (0, &rfds); } #endif R_API int r_cons_controlz(int ch) { #if __UNIX__ if (ch == 0x1a) { r_cons_show_cursor (true); r_cons_enable_mouse (false); r_sys_stop (); return 0; } #endif return ch; } static int parseMouseEvent() { int ch = r_cons_readchar (); /* Skip the x/y coordinates */ #if USE_CLICK int x = r_cons_readchar() - 33; int y = r_cons_readchar() - 33; #else (void) r_cons_readchar (); (void) r_cons_readchar (); #endif #if USE_CLICK if (ch == 35) { /* handle click */ #define CLICK_DEBUG 1 #if CLICK_DEBUG r_cons_gotoxy (0, 0); r_cons_printf ("Click at %d %d\n", x, y); r_cons_flush (); #endif RCons *cons = r_cons_singleton (); if (cons->onclick) { cons->onclick (cons->data, x, y); } r_cons_enable_mouse (false); (void)r_cons_readchar (); return 0; } #endif if (ch != 0x20 && ch >= 64 + 32) { /* Grab wheel events only */ I->mouse_event = 1; return "kj"[(ch - (64 + 32))&1]; } // temporary disable the mouse wheel to allow select r_cons_enable_mouse (false); (void)r_cons_readchar (); return 0; } R_API int r_cons_arrow_to_hjkl(int ch) { #if __WINDOWS_ && !__CYGWIN__ return ch; #endif I->mouse_event = 0; /* emacs */ switch ((ut8)ch) { case 0xc3: r_cons_readchar(); ch='K'; break; // emacs repag (alt + v) case 0x16: ch='J'; break; // emacs avpag (ctrl + v) case 0x10: ch='k'; break; // emacs up (ctrl + p) case 0x0e: ch='j'; break; // emacs down (ctrl + n) case 0x06: ch='l'; break; // emacs right (ctrl + f) case 0x02: ch='h'; break; // emacs left (ctrl + b) } if (ch != 0x1b) { return ch; } ch = r_cons_readchar (); if (!ch) { return 0; } switch (ch) { case 0x1b: ch = 'q'; // XXX: must be 0x1b (R_CONS_KEY_ESC) break; case 0x4f: // function keys from f1 to f4 ch = r_cons_readchar (); #if defined(__HAIKU__) /* Haiku don use the '[' char for funcion keys */ if (ch > 'O') {/* only in f1..f12 funcion keys */ ch = 0xf1 + (ch&0xf); break; } case '[': // 0x5b function keys (2) /* Haiku need ESC + [ for PageUp and PageDown */ if (ch < 'A' || ch == '[') { ch = r_cons_readchar (); } #else ch = 0xf1 + (ch & 0xf); break; case '[': // function keys (2) ch = r_cons_readchar (); #endif switch (ch) { case '[': ch = r_cons_readchar (); switch (ch) { case '2': ch = R_CONS_KEY_F11; break; case 'A': ch = R_CONS_KEY_F1; break; case 'B': ch = R_CONS_KEY_F2; break; case 'C': ch = R_CONS_KEY_F3; break; case 'D': ch = R_CONS_KEY_F4; break; } break; case '2': ch = r_cons_readchar (); switch (ch) { case 0x7e: ch = R_CONS_KEY_F12; break; default: r_cons_readchar (); switch (ch) { case '0': ch = R_CONS_KEY_F9; break; case '1': ch = R_CONS_KEY_F10; break; case '3': ch = R_CONS_KEY_F11; break; } break; } break; case '1': ch = r_cons_readchar (); switch (ch) { case ':': // arrow+shift ch = r_cons_readchar (); ch = r_cons_readchar (); switch (ch) { case 'A': ch = 'K'; break; case 'B': ch = 'J'; break; case 'C': ch = 'L'; break; case 'D': ch = 'H'; break; } break; /* case '1': ch = R_CONS_KEY_F1; break; case '2': ch = R_CONS_KEY_F2; break; case '3': ch = R_CONS_KEY_F3; break; case '4': ch = R_CONS_KEY_F4; break; */ case '5': r_cons_readchar (); ch = 0xf5; break; case '6': r_cons_readchar (); ch = 0xf7; break; case '7': r_cons_readchar (); ch = 0xf6; break; case '8': r_cons_readchar (); ch = 0xf7; break; case '9': r_cons_readchar (); ch = 0xf8; break; } // F9-F12 not yet supported!! break; case '5': ch = 'K'; r_cons_readchar(); break; // repag case '6': ch = 'J'; r_cons_readchar(); break; // avpag /* arrow keys */ case 'A': ch = 'k'; break; // up case 'B': ch = 'j'; break; // down case 'C': ch = 'l'; break; // right case 'D': ch = 'h'; break; // left case 'M': ch = parseMouseEvent(); break; } break; } return ch; } // XXX no control for max length here?!?! R_API int r_cons_fgets(char *buf, int len, int argc, const char **argv) { #define RETURN(x) { ret=x; goto beach; } RCons *cons = r_cons_singleton (); int ret = 0, color = cons->pal.input && *cons->pal.input; if (cons->echo) { r_cons_set_raw (false); r_cons_show_cursor (true); } #if 0 int mouse = r_cons_enable_mouse (false); r_cons_enable_mouse (false); r_cons_flush (); #endif errno = 0; if (cons->user_fgets) { RETURN (cons->user_fgets (buf, len)); } printf ("%s", cons->line->prompt); fflush (stdout); *buf = '\0'; fflush (cons->fdin); if (color) { const char *p = cons->pal.input; int len = p? strlen (p): 0; if (len > 0) { fwrite (p, len, 1, stdout); } fflush (stdout); } if (!fgets (buf, len, cons->fdin)) { if (color) { printf (Color_RESET); fflush (stdout); } RETURN (-1); } if (feof (cons->fdin)) { if (color) { printf (Color_RESET); } RETURN (-2); } buf[strlen (buf)-1] = '\0'; if (color) { printf (Color_RESET); } ret = strlen (buf); beach: //r_cons_enable_mouse (mouse); return ret; } R_API int r_cons_any_key(const char *msg) { if (msg && *msg) { r_cons_printf ("\n-- %s --\n", msg); } else { r_cons_print ("\n--press any key--\n"); } r_cons_flush (); return r_cons_readchar (); //r_cons_strcat ("\x1b[2J\x1b[0;0H"); // wtf? } #if __WINDOWS__ && !__CYGWIN__ static int readchar_win(ut32 usec) { int ch = 0; BOOL ret; BOOL bCtrl = FALSE; DWORD mode, out; HANDLE h; INPUT_RECORD irInBuf[128]; int i; do_it_again: h = GetStdHandle (STD_INPUT_HANDLE); GetConsoleMode (h, &mode); SetConsoleMode (h, 0 | ENABLE_MOUSE_INPUT); // RAW void *bed = r_cons_sleep_begin (); if (usec) { if (WaitForSingleObject (h, usec) == WAIT_TIMEOUT) { r_cons_sleep_end (bed); return -1; } } ret = ReadConsoleInput (h, irInBuf, 128, &out); r_cons_sleep_end (bed); if (ret) { for (i = 0; i < out; i++) { if (irInBuf[i].EventType==MOUSE_EVENT) { switch (irInBuf[i].Event.MouseEvent.dwEventFlags) { case MOUSE_WHEELED: if (irInBuf[i].Event.MouseEvent.dwButtonState & 0xFF000000) ch='j'; else ch='k'; break; } } if (irInBuf[i].EventType==KEY_EVENT) { if (irInBuf[i].Event.KeyEvent.bKeyDown) { ch=irInBuf[i].Event.KeyEvent.uChar.AsciiChar; bCtrl=irInBuf[i].Event.KeyEvent.dwControlKeyState & 8; if (irInBuf[i].Event.KeyEvent.uChar.AsciiChar==0) { ch = 0; switch (irInBuf[i].Event.KeyEvent.wVirtualKeyCode) { case VK_DOWN: // key down ch = bCtrl ? 'J': 'j'; break; case VK_RIGHT: // key right ch = bCtrl ? 'L': 'l'; break; case VK_UP: // key up if (bCtrl) ch='K'; else ch='k'; break; case VK_LEFT: // key left if (bCtrl) ch='H'; else ch='h'; break; case VK_PRIOR: // key home if (bCtrl) ch='K'; else ch='K'; break; case VK_NEXT: // key end if (bCtrl) ch='J'; else ch='J'; break; case VK_F1: ch = R_CONS_KEY_F1; break; case VK_F2: ch = R_CONS_KEY_F2; break; case VK_F3: ch = R_CONS_KEY_F3; break; case VK_F4: ch = R_CONS_KEY_F4; break; case VK_F5: if (bCtrl) ch=0xcf5; else ch=R_CONS_KEY_F5; break; case VK_F6: ch = R_CONS_KEY_F6; break; case VK_F7: ch = R_CONS_KEY_F7; break; case VK_F8: ch = R_CONS_KEY_F8; break; case VK_F9: ch = R_CONS_KEY_F9; break; case VK_F10: ch = R_CONS_KEY_F10; break; case VK_F11: ch = R_CONS_KEY_F11; break; case VK_F12: ch = R_CONS_KEY_F12; break; default: ch = 0; break; } } } } } } FlushConsoleInputBuffer (h); SetConsoleMode (h, mode); if (ch == 0) { goto do_it_again; } /*r_cons_gotoxy (1, 2); r_cons_printf ("\n"); r_cons_printf ("| buf = %x |\n", ch); r_cons_printf ("\n"); r_cons_flush (); r_sys_sleep (1);*/ return ch; } #endif R_API int r_cons_readchar_timeout(ut32 usec) { #if __UNIX__ struct timeval tv; fd_set fdset, errset; FD_ZERO (&fdset); FD_ZERO (&errset); FD_SET (0, &fdset); tv.tv_sec = 0; // usec / 1000; tv.tv_usec = 1000 * usec; r_cons_set_raw (1); if (select (1, &fdset, NULL, &errset, &tv) == 1) { return r_cons_readchar (); } r_cons_set_raw (0); // timeout return -1; #else return readchar_win (usec); #endif } // TODO: support binary? buf+len static char *readbuffer = NULL; static int readbuffer_length = 0; static bool bufactive = true; R_API bool r_cons_readpush(const char *str, int len) { char *res = (len + readbuffer_length > 0) ? realloc (readbuffer, len + readbuffer_length) : NULL; if (res) { readbuffer = res; memmove (readbuffer + readbuffer_length, str, len); readbuffer_length += len; return true; } return false; } R_API void r_cons_readflush() { R_FREE (readbuffer); readbuffer_length = 0; } R_API void r_cons_switchbuf(bool active) { bufactive = active; } #if !(__WINDOWS__ && !__CYGWIN__) extern volatile sig_atomic_t sigwinchFlag; extern void resizeWin(void); #endif R_API int r_cons_readchar() { void *bed; char buf[2]; buf[0] = -1; if (readbuffer_length > 0) { int ch = *readbuffer; readbuffer_length--; memmove (readbuffer, readbuffer + 1, readbuffer_length); return ch; } #if __WINDOWS__ && !__CYGWIN__ //&& !MINGW32 #if 1 // if something goes wrong set this to 0. skuater..... return readchar_win(0); #endif BOOL ret; DWORD out; DWORD mode; HANDLE h = GetStdHandle (STD_INPUT_HANDLE); GetConsoleMode (h, &mode); SetConsoleMode (h, 0); // RAW bed = r_cons_sleep_begin (); ret = ReadConsole (h, buf, 1, &out, NULL); r_cons_sleep_end (bed); FlushConsoleInputBuffer (h); if (!ret) { return -1; } SetConsoleMode (h, mode); #else r_cons_set_raw (1); bed = r_cons_sleep_begin (); // Blocks until either stdin has something to read or a signal happens. // This serves to check if the terminal window was resized. It avoids the race // condition that could happen if we did not use pselect or select in case SIGWINCH // was handled immediately before the blocking call (select or read). The race is // prevented from happening by having SIGWINCH blocked process-wide except for in // pselect (that is what pselect is for). fd_set readfds; sigset_t sigmask; FD_ZERO (&readfds); FD_SET (STDIN_FILENO, &readfds); r_signal_sigmask (0, NULL, &sigmask); sigdelset (&sigmask, SIGWINCH); while (pselect (STDIN_FILENO + 1, &readfds, NULL, NULL, NULL, &sigmask) == -1) { if (errno == EBADF) { eprintf ("r_cons_readchar(): EBADF\n"); return -1; } if (sigwinchFlag) { sigwinchFlag = 0; resizeWin (); } } ssize_t ret = read (STDIN_FILENO, buf, 1); r_cons_sleep_end (bed); if (ret != 1) { return -1; } if (bufactive) { r_cons_set_raw (0); } #endif return r_cons_controlz (buf[0]); } R_API int r_cons_yesno(int def, const char *fmt, ...) { va_list ap; ut8 key = (ut8)def; va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); fflush (stderr); r_cons_set_raw (true); (void)read (0, &key, 1); write (2, " ", 1); write (2, &key, 1); write (2, "\n", 1); if (key == 'Y') { key = 'y'; } r_cons_set_raw (false); if (key == '\n' || key == '\r') { key = def; } return key == 'y'; } R_API char *r_cons_input(const char *msg) { char *oprompt = r_line_get_prompt (); //r_cons_singleton ()->line->prompt); if (!oprompt) { return NULL; } char buf[1024]; if (msg) { //r_cons_printf ("%s\n", msg); r_line_set_prompt (msg); } else { r_line_set_prompt (""); } buf[0] = 0; r_cons_fgets (buf, sizeof (buf), 0, NULL); r_line_set_prompt (oprompt); free (oprompt); return strdup (buf); }