Merge Console-linux and Console-darwin into Console-posix
							parent
							
								
									eb40420282
								
							
						
					
					
						commit
						fc24d24ccc
					
				| @ -1,838 +0,0 @@ | |||||||
| /*
 |  | ||||||
| https://github.com/peterix/dfhack
 |  | ||||||
| 
 |  | ||||||
| A thread-safe logging console with a line editor. |  | ||||||
| 
 |  | ||||||
| Based on linenoise: |  | ||||||
| linenoise -- guerrilla line editing library against the idea that a |  | ||||||
| line editing lib needs to be 20,000 lines of C code. |  | ||||||
| 
 |  | ||||||
| You can find the latest source code at: |  | ||||||
| 
 |  | ||||||
|   http://github.com/antirez/linenoise
 |  | ||||||
| 
 |  | ||||||
| Does a number of crazy assumptions that happen to be true in 99.9999% of |  | ||||||
| the 2010 UNIX computers around. |  | ||||||
| 
 |  | ||||||
| ------------------------------------------------------------------------ |  | ||||||
| 
 |  | ||||||
| Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com> |  | ||||||
| Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com> |  | ||||||
| Copyright (c) 2011, Petr Mrázek <peterix@gmail.com> |  | ||||||
| 
 |  | ||||||
| All rights reserved. |  | ||||||
| 
 |  | ||||||
| Redistribution and use in source and binary forms, with or without |  | ||||||
| modification, are permitted provided that the following conditions are |  | ||||||
| met: |  | ||||||
| 
 |  | ||||||
|  *  Redistributions of source code must retain the above copyright |  | ||||||
|     notice, this list of conditions and the following disclaimer. |  | ||||||
| 
 |  | ||||||
|  *  Redistributions in binary form must reproduce the above copyright |  | ||||||
|     notice, this list of conditions and the following disclaimer in the |  | ||||||
|     documentation and/or other materials provided with the distribution. |  | ||||||
| 
 |  | ||||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |  | ||||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |  | ||||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |  | ||||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |  | ||||||
| HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |  | ||||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |  | ||||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |  | ||||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |  | ||||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |  | ||||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |  | ||||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
| #include <cstdio> |  | ||||||
| #include <cstdlib> |  | ||||||
| #include <iostream> |  | ||||||
| #include <sstream> |  | ||||||
| #include <string.h> |  | ||||||
| #include <string> |  | ||||||
| #include <stdarg.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <sys/types.h> |  | ||||||
| #include <sys/ioctl.h> |  | ||||||
| #include <termios.h> |  | ||||||
| #include <errno.h> |  | ||||||
| #include <deque> |  | ||||||
| 
 |  | ||||||
| // George Vulov for MacOSX
 |  | ||||||
| #ifndef __LINUX__ |  | ||||||
| #define TMP_FAILURE_RETRY(expr) \ |  | ||||||
|     ({ long int _res; \ |  | ||||||
|         do _res = (long int) (expr); \ |  | ||||||
|         while (_res == -1L && errno == EINTR); \ |  | ||||||
|         _res; }) |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #include "Console.h" |  | ||||||
| #include "Hooks.h" |  | ||||||
| using namespace DFHack; |  | ||||||
| 
 |  | ||||||
| #include "tinythread.h" |  | ||||||
| using namespace tthread; |  | ||||||
| 
 |  | ||||||
| static int isUnsupportedTerm(void) |  | ||||||
| { |  | ||||||
|     static const char *unsupported_term[] = {"dumb","cons25",NULL}; |  | ||||||
|     char *term = getenv("TERM"); |  | ||||||
|     int j; |  | ||||||
| 
 |  | ||||||
|     if (term == NULL) return 0; |  | ||||||
|     for (j = 0; unsupported_term[j]; j++) |  | ||||||
|         if (!strcasecmp(term,unsupported_term[j])) return 1; |  | ||||||
|         return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const char * ANSI_CLS = "\033[2J"; |  | ||||||
| const char * ANSI_BLACK = "\033[22;30m"; |  | ||||||
| const char * ANSI_RED = "\033[22;31m"; |  | ||||||
| const char * ANSI_GREEN = "\033[22;32m"; |  | ||||||
| const char * ANSI_BROWN = "\033[22;33m"; |  | ||||||
| const char * ANSI_BLUE = "\033[22;34m"; |  | ||||||
| const char * ANSI_MAGENTA = "\033[22;35m"; |  | ||||||
| const char * ANSI_CYAN = "\033[22;36m"; |  | ||||||
| const char * ANSI_GREY = "\033[22;37m"; |  | ||||||
| const char * ANSI_DARKGREY = "\033[01;30m"; |  | ||||||
| const char * ANSI_LIGHTRED = "\033[01;31m"; |  | ||||||
| const char * ANSI_LIGHTGREEN = "\033[01;32m"; |  | ||||||
| const char * ANSI_YELLOW = "\033[01;33m"; |  | ||||||
| const char * ANSI_LIGHTBLUE = "\033[01;34m"; |  | ||||||
| const char * ANSI_LIGHTMAGENTA = "\033[01;35m"; |  | ||||||
| const char * ANSI_LIGHTCYAN = "\033[01;36m"; |  | ||||||
| const char * ANSI_WHITE = "\033[01;37m"; |  | ||||||
| const char * RESETCOLOR = "\033[0m"; |  | ||||||
| 
 |  | ||||||
| const char * getANSIColor(const int c) |  | ||||||
| { |  | ||||||
|     switch (c) |  | ||||||
|     { |  | ||||||
|         case -1: return RESETCOLOR; // HACK! :P
 |  | ||||||
|         case 0 : return ANSI_BLACK; |  | ||||||
|         case 1 : return ANSI_BLUE; // non-ANSI
 |  | ||||||
|         case 2 : return ANSI_GREEN; |  | ||||||
|         case 3 : return ANSI_CYAN; // non-ANSI
 |  | ||||||
|         case 4 : return ANSI_RED; // non-ANSI
 |  | ||||||
|         case 5 : return ANSI_MAGENTA; |  | ||||||
|         case 6 : return ANSI_BROWN; |  | ||||||
|         case 7 : return ANSI_GREY; |  | ||||||
|         case 8 : return ANSI_DARKGREY; |  | ||||||
|         case 9 : return ANSI_LIGHTBLUE; // non-ANSI
 |  | ||||||
|         case 10: return ANSI_LIGHTGREEN; |  | ||||||
|         case 11: return ANSI_LIGHTCYAN; // non-ANSI;
 |  | ||||||
|         case 12: return ANSI_LIGHTRED; // non-ANSI;
 |  | ||||||
|         case 13: return ANSI_LIGHTMAGENTA; |  | ||||||
|         case 14: return ANSI_YELLOW; // non-ANSI
 |  | ||||||
|         case 15: return ANSI_WHITE; |  | ||||||
|         default: return ""; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| namespace DFHack |  | ||||||
| { |  | ||||||
|     class Private |  | ||||||
|     { |  | ||||||
|     public: |  | ||||||
|         Private() |  | ||||||
|         { |  | ||||||
|             dfout_C = NULL; |  | ||||||
|             rawmode = false; |  | ||||||
|             in_batch = false; |  | ||||||
|             supported_terminal = false; |  | ||||||
|             state = con_unclaimed; |  | ||||||
|         }; |  | ||||||
|         virtual ~Private() |  | ||||||
|         { |  | ||||||
|             //sync();
 |  | ||||||
|         } |  | ||||||
|     private: |  | ||||||
|         bool read_char(unsigned char & out) |  | ||||||
|         { |  | ||||||
|             FD_ZERO(&descriptor_set); |  | ||||||
|             FD_SET(STDIN_FILENO, &descriptor_set); |  | ||||||
|             FD_SET(exit_pipe[0], &descriptor_set); |  | ||||||
|             int ret = TMP_FAILURE_RETRY( |  | ||||||
|                 select (FD_SETSIZE,&descriptor_set, NULL, NULL, NULL) |  | ||||||
|             ); |  | ||||||
|             if(ret == -1) |  | ||||||
|                 return false; |  | ||||||
|             if (FD_ISSET(exit_pipe[0], &descriptor_set)) |  | ||||||
|                 return false; |  | ||||||
|             if (FD_ISSET(STDIN_FILENO, &descriptor_set)) |  | ||||||
|             { |  | ||||||
|                 // read byte from stdin
 |  | ||||||
|                 ret = TMP_FAILURE_RETRY( |  | ||||||
|                     read(STDIN_FILENO, &out, 1) |  | ||||||
|                 ); |  | ||||||
|                 if(ret == -1) |  | ||||||
|                     return false; |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     public: |  | ||||||
|         void print(const char *data) |  | ||||||
|         { |  | ||||||
|             fputs(data, dfout_C); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void print_text(color_ostream::color_value clr, const std::string &chunk) |  | ||||||
|         { |  | ||||||
|             if(!in_batch && state == con_lineedit) |  | ||||||
|             { |  | ||||||
|                 disable_raw(); |  | ||||||
|                 fprintf(dfout_C,"\x1b[1G"); |  | ||||||
|                 fprintf(dfout_C,"\x1b[0K"); |  | ||||||
| 
 |  | ||||||
|                 color(clr); |  | ||||||
|                 print(chunk.c_str()); |  | ||||||
| 
 |  | ||||||
|                 reset_color(); |  | ||||||
|                 enable_raw(); |  | ||||||
|                 prompt_refresh(); |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 color(clr); |  | ||||||
|                 print(chunk.c_str()); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void begin_batch() |  | ||||||
|         { |  | ||||||
|             assert(!in_batch); |  | ||||||
| 
 |  | ||||||
|             in_batch = true; |  | ||||||
| 
 |  | ||||||
|             if (state == con_lineedit) |  | ||||||
|             { |  | ||||||
|                 disable_raw(); |  | ||||||
|                 fprintf(dfout_C,"\x1b[1G"); |  | ||||||
|                 fprintf(dfout_C,"\x1b[0K"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void end_batch() |  | ||||||
|         { |  | ||||||
|             assert(in_batch); |  | ||||||
| 
 |  | ||||||
|             flush(); |  | ||||||
| 
 |  | ||||||
|             in_batch = false; |  | ||||||
| 
 |  | ||||||
|             if (state == con_lineedit) |  | ||||||
|             { |  | ||||||
|                 reset_color(); |  | ||||||
|                 enable_raw(); |  | ||||||
|                 prompt_refresh(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void flush() |  | ||||||
|         { |  | ||||||
|             if (!rawmode) |  | ||||||
|                 fflush(dfout_C); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         /// Clear the console, along with its scrollback
 |  | ||||||
|         void clear() |  | ||||||
|         { |  | ||||||
|             if(rawmode) |  | ||||||
|             { |  | ||||||
|                 const char * clr = "\033c\033[3J\033[H"; |  | ||||||
|                 if (::write(STDIN_FILENO,clr,strlen(clr)) == -1) |  | ||||||
|                     ; |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 print("\033c\033[3J\033[H"); |  | ||||||
|                 fflush(dfout_C); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         /// Position cursor at x,y. 1,1 = top left corner
 |  | ||||||
|         void gotoxy(int x, int y) |  | ||||||
|         { |  | ||||||
|             char tmp[64]; |  | ||||||
|             sprintf(tmp,"\033[%d;%dH", y,x); |  | ||||||
|             print(tmp); |  | ||||||
|         } |  | ||||||
|         /// Set color (ANSI color number)
 |  | ||||||
|         void color(Console::color_value index) |  | ||||||
|         { |  | ||||||
|             if(!rawmode) |  | ||||||
|                 fprintf(dfout_C, "%s", getANSIColor(index)); |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 const char * colstr = getANSIColor(index); |  | ||||||
|                 int lstr = strlen(colstr); |  | ||||||
|                 if (::write(STDIN_FILENO,colstr,lstr) == -1) |  | ||||||
|                     ; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         /// Reset color to default
 |  | ||||||
|         void reset_color(void) |  | ||||||
|         { |  | ||||||
|             color(COLOR_RESET); |  | ||||||
|             if(!rawmode) |  | ||||||
|                 fflush(dfout_C); |  | ||||||
|         } |  | ||||||
|         /// Enable or disable the caret/cursor
 |  | ||||||
|         void cursor(bool enable = true) |  | ||||||
|         { |  | ||||||
|             if(enable) |  | ||||||
|                 print("\033[?25h"); |  | ||||||
|             else |  | ||||||
|                 print("\033[?25l"); |  | ||||||
|         } |  | ||||||
|         /// Waits given number of milliseconds before continuing.
 |  | ||||||
|         void msleep(unsigned int msec); |  | ||||||
|         /// get the current number of columns
 |  | ||||||
|         int  get_columns(void) |  | ||||||
|         { |  | ||||||
|             winsize ws; |  | ||||||
|             if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 80; |  | ||||||
|             return ws.ws_col; |  | ||||||
|         } |  | ||||||
|         /// get the current number of rows
 |  | ||||||
|         int  get_rows(void) |  | ||||||
|         { |  | ||||||
|             winsize ws; |  | ||||||
|             if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 25; |  | ||||||
|             return ws.ws_row; |  | ||||||
|         } |  | ||||||
|         /// beep. maybe?
 |  | ||||||
|         //void beep (void);
 |  | ||||||
|         void back_word() |  | ||||||
|         { |  | ||||||
|             if (raw_cursor == 0) |  | ||||||
|                 return; |  | ||||||
|             raw_cursor--; |  | ||||||
|             while (raw_cursor > 0 && !isalnum(raw_buffer[raw_cursor])) |  | ||||||
|                 raw_cursor--; |  | ||||||
|             while (raw_cursor > 0 && isalnum(raw_buffer[raw_cursor])) |  | ||||||
|                 raw_cursor--; |  | ||||||
|             if (!isalnum(raw_buffer[raw_cursor]) && raw_cursor != 0) |  | ||||||
|                 raw_cursor++; |  | ||||||
|             prompt_refresh(); |  | ||||||
|         } |  | ||||||
|         void forward_word() |  | ||||||
|         { |  | ||||||
|             int len = raw_buffer.size(); |  | ||||||
|             if (raw_cursor == len) |  | ||||||
|                 return; |  | ||||||
|             raw_cursor++; |  | ||||||
|             while (raw_cursor <= len && !isalnum(raw_buffer[raw_cursor])) |  | ||||||
|                 raw_cursor++; |  | ||||||
|             while (raw_cursor <= len && isalnum(raw_buffer[raw_cursor])) |  | ||||||
|                 raw_cursor++; |  | ||||||
|             if (raw_cursor > len) |  | ||||||
|                 raw_cursor = len; |  | ||||||
|             prompt_refresh(); |  | ||||||
|         } |  | ||||||
|         /// A simple line edit (raw mode)
 |  | ||||||
|         int lineedit(const std::string& prompt, std::string& output, recursive_mutex * lock, CommandHistory & ch) |  | ||||||
|         { |  | ||||||
|             output.clear(); |  | ||||||
|             reset_color(); |  | ||||||
|             this->prompt = prompt; |  | ||||||
|             if (!supported_terminal) |  | ||||||
|             { |  | ||||||
|                 print(prompt.c_str()); |  | ||||||
|                 fflush(dfout_C); |  | ||||||
|                 // FIXME: what do we do here???
 |  | ||||||
|                 //SDL_recursive_mutexV(lock);
 |  | ||||||
|                 std::getline(std::cin, output); |  | ||||||
|                 //SDL_recursive_mutexP(lock);
 |  | ||||||
|                 return output.size(); |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 int count; |  | ||||||
|                 if (enable_raw() == -1) return 0; |  | ||||||
|                 if(state == con_lineedit) |  | ||||||
|                     return -1; |  | ||||||
|                 state = con_lineedit; |  | ||||||
|                 count = prompt_loop(lock,ch); |  | ||||||
|                 state = con_unclaimed; |  | ||||||
|                 disable_raw(); |  | ||||||
|                 print("\n"); |  | ||||||
|                 if(count != -1) |  | ||||||
|                 { |  | ||||||
|                     output = raw_buffer; |  | ||||||
|                 } |  | ||||||
|                 return count; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         int enable_raw() |  | ||||||
|         { |  | ||||||
|             struct termios raw; |  | ||||||
| 
 |  | ||||||
|             if (!supported_terminal) |  | ||||||
|                 return -1; |  | ||||||
|             if (tcgetattr(STDIN_FILENO,&orig_termios) == -1) |  | ||||||
|                 return -1; |  | ||||||
| 
 |  | ||||||
|             raw = orig_termios; //modify the original mode
 |  | ||||||
|             // input modes: no break, no CR to NL, no parity check, no strip char,
 |  | ||||||
|             // no start/stop output control.
 |  | ||||||
|             raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); |  | ||||||
|             // output modes - disable post processing
 |  | ||||||
|             raw.c_oflag &= ~(OPOST); |  | ||||||
|             // control modes - set 8 bit chars
 |  | ||||||
|             raw.c_cflag |= (CS8); |  | ||||||
|             // local modes - choing off, canonical off, no extended functions,
 |  | ||||||
|             // no signal chars (^Z,^C)
 |  | ||||||
| #ifdef CONSOLE_NO_CATCH |  | ||||||
|             raw.c_lflag &= ~( ECHO | ICANON | IEXTEN ); |  | ||||||
| #else |  | ||||||
|             raw.c_lflag &= ~( ECHO | ICANON | IEXTEN | ISIG ); |  | ||||||
| #endif |  | ||||||
|             // control chars - set return condition: min number of bytes and timer.
 |  | ||||||
|             // We want read to return every single byte, without timeout.
 |  | ||||||
|             raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0;// 1 byte, no timer
 |  | ||||||
|             // put terminal in raw mode after flushing
 |  | ||||||
|             if (tcsetattr(STDIN_FILENO,TCSAFLUSH,&raw) < 0) |  | ||||||
|                 return -1; |  | ||||||
|             rawmode = 1; |  | ||||||
|             return 0; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void disable_raw() |  | ||||||
|         { |  | ||||||
|             /* Don't even check the return value as it's too late. */ |  | ||||||
|             if (rawmode && tcsetattr(STDIN_FILENO,TCSAFLUSH,&orig_termios) != -1) |  | ||||||
|                 rawmode = 0; |  | ||||||
|         } |  | ||||||
|         void prompt_refresh() |  | ||||||
|         { |  | ||||||
|             char seq[64]; |  | ||||||
|             int cols = get_columns(); |  | ||||||
|             int plen = prompt.size(); |  | ||||||
|             const char * buf = raw_buffer.c_str(); |  | ||||||
|             int len = raw_buffer.size(); |  | ||||||
|             int cooked_cursor = raw_cursor; |  | ||||||
|             // Use math! This is silly.
 |  | ||||||
|             while((plen+cooked_cursor) >= cols) |  | ||||||
|             { |  | ||||||
|                 buf++; |  | ||||||
|                 len--; |  | ||||||
|                 cooked_cursor--; |  | ||||||
|             } |  | ||||||
|             while (plen+len > cols) |  | ||||||
|             { |  | ||||||
|                 len--; |  | ||||||
|             } |  | ||||||
|             /* Cursor to left edge */ |  | ||||||
|             snprintf(seq,64,"\x1b[1G"); |  | ||||||
|             if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return; |  | ||||||
|             /* Write the prompt and the current buffer content */ |  | ||||||
|             if (::write(STDIN_FILENO,prompt.c_str(),plen) == -1) return; |  | ||||||
|             if (::write(STDIN_FILENO,buf,len) == -1) return; |  | ||||||
|             /* Erase to right */ |  | ||||||
|             snprintf(seq,64,"\x1b[0K"); |  | ||||||
|             if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return; |  | ||||||
|             /* Move cursor to original position. */ |  | ||||||
|             snprintf(seq,64,"\x1b[1G\x1b[%dC", (int)(cooked_cursor+plen)); |  | ||||||
|             if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         int prompt_loop(recursive_mutex * lock, CommandHistory & history) |  | ||||||
|         { |  | ||||||
|             int fd = STDIN_FILENO; |  | ||||||
|             size_t plen = prompt.size(); |  | ||||||
|             int history_index = 0; |  | ||||||
|             raw_buffer.clear(); |  | ||||||
|             raw_cursor = 0; |  | ||||||
|             /* The latest history entry is always our current buffer, that
 |  | ||||||
|              * initially is just an empty string. */ |  | ||||||
|             const std::string empty; |  | ||||||
|             history.add(empty); |  | ||||||
|             if (::write(fd,prompt.c_str(),prompt.size()) == -1) return -1; |  | ||||||
|             while(1) |  | ||||||
|             { |  | ||||||
|                 unsigned char c; |  | ||||||
|                 int isok; |  | ||||||
|                 unsigned char seq[2], seq2; |  | ||||||
|                 lock->unlock(); |  | ||||||
|                 if(!read_char(c)) |  | ||||||
|                 { |  | ||||||
|                     lock->lock(); |  | ||||||
|                     return -2; |  | ||||||
|                 } |  | ||||||
|                 lock->lock(); |  | ||||||
|                 /* Only autocomplete when the callback is set. It returns < 0 when
 |  | ||||||
|                  * there was an error reading from fd. Otherwise it will return the |  | ||||||
|                  * character that should be handled next. */ |  | ||||||
|                 if (c == 9) |  | ||||||
|                 { |  | ||||||
|                     /*
 |  | ||||||
|                     if( completionCallback != NULL) { |  | ||||||
|                         c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols); |  | ||||||
|                         // Return on errors
 |  | ||||||
|                         if (c < 0) return len; |  | ||||||
|                         // Read next character when 0
 |  | ||||||
|                         if (c == 0) continue; |  | ||||||
|                     } |  | ||||||
|                     else |  | ||||||
|                     { |  | ||||||
|                         // ignore tab
 |  | ||||||
|                         continue; |  | ||||||
|                     } |  | ||||||
|                     */ |  | ||||||
|                     // just ignore tabs
 |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 switch(c) |  | ||||||
|                 { |  | ||||||
|                 case 13:    // enter
 |  | ||||||
|                     history.remove(); |  | ||||||
|                     return raw_buffer.size(); |  | ||||||
|                 case 3:     // ctrl-c
 |  | ||||||
|                     errno = EAGAIN; |  | ||||||
|                     return -1; |  | ||||||
|                 case 127:   // backspace
 |  | ||||||
|                 case 8:     // ctrl-h
 |  | ||||||
|                     if (raw_cursor > 0 && raw_buffer.size() > 0) |  | ||||||
|                     { |  | ||||||
|                         raw_buffer.erase(raw_cursor-1,1); |  | ||||||
|                         raw_cursor--; |  | ||||||
|                         prompt_refresh(); |  | ||||||
|                     } |  | ||||||
|                     break; |  | ||||||
|                 case 27:    // escape sequence
 |  | ||||||
|                     lock->unlock(); |  | ||||||
|                     if (!read_char(seq[0])) |  | ||||||
|                     { |  | ||||||
|                         lock->lock(); |  | ||||||
|                         return -2; |  | ||||||
|                     } |  | ||||||
|                     lock->lock(); |  | ||||||
|                     if (seq[0] == 'b') |  | ||||||
|                     { |  | ||||||
|                         back_word(); |  | ||||||
|                     } |  | ||||||
|                     else if (seq[0] == 'f') |  | ||||||
|                     { |  | ||||||
|                         forward_word(); |  | ||||||
|                     } |  | ||||||
|                     else if(seq[0] == '[') |  | ||||||
|                     { |  | ||||||
|                         if (!read_char(seq[1])) |  | ||||||
|                         { |  | ||||||
|                             lock->lock(); |  | ||||||
|                             return -2; |  | ||||||
|                         } |  | ||||||
|                         if (seq[1] == 'D') |  | ||||||
|                         { |  | ||||||
|                             left_arrow: |  | ||||||
|                             if (raw_cursor > 0) |  | ||||||
|                             { |  | ||||||
|                                 raw_cursor--; |  | ||||||
|                                 prompt_refresh(); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         else if ( seq[1] == 'C') |  | ||||||
|                         { |  | ||||||
|                             right_arrow: |  | ||||||
|                             /* right arrow */ |  | ||||||
|                             if (size_t(raw_cursor) != raw_buffer.size()) |  | ||||||
|                             { |  | ||||||
|                                 raw_cursor++; |  | ||||||
|                                 prompt_refresh(); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         else if (seq[1] == 'A' || seq[1] == 'B') |  | ||||||
|                         { |  | ||||||
|                             /* up and down arrow: history */ |  | ||||||
|                             if (history.size() > 1) |  | ||||||
|                             { |  | ||||||
|                                 /* Update the current history entry before to
 |  | ||||||
|                                  * overwrite it with tne next one. */ |  | ||||||
|                                 history[history_index] = raw_buffer; |  | ||||||
|                                 /* Show the new entry */ |  | ||||||
|                                 history_index += (seq[1] == 'A') ? 1 : -1; |  | ||||||
|                                 if (history_index < 0) |  | ||||||
|                                 { |  | ||||||
|                                     history_index = 0; |  | ||||||
|                                     break; |  | ||||||
|                                 } |  | ||||||
|                                 else if (size_t(history_index) >= history.size()) |  | ||||||
|                                 { |  | ||||||
|                                     history_index = history.size()-1; |  | ||||||
|                                     break; |  | ||||||
|                                 } |  | ||||||
|                                 raw_buffer = history[history_index]; |  | ||||||
|                                 raw_cursor = raw_buffer.size(); |  | ||||||
|                                 prompt_refresh(); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         else if(seq[1] == 'H') |  | ||||||
|                         { |  | ||||||
|                             // home
 |  | ||||||
|                             raw_cursor = 0; |  | ||||||
|                             prompt_refresh(); |  | ||||||
|                         } |  | ||||||
|                         else if(seq[1] == 'F') |  | ||||||
|                         { |  | ||||||
|                             // end
 |  | ||||||
|                             raw_cursor = raw_buffer.size(); |  | ||||||
|                             prompt_refresh(); |  | ||||||
|                         } |  | ||||||
|                         else if (seq[1] > '0' && seq[1] < '7') |  | ||||||
|                         { |  | ||||||
|                             // extended escape
 |  | ||||||
|                             unsigned char seq3[3]; |  | ||||||
|                             lock->unlock(); |  | ||||||
|                             if(!read_char(seq2)) |  | ||||||
|                             { |  | ||||||
|                                 lock->lock(); |  | ||||||
|                                 return -2; |  | ||||||
|                             } |  | ||||||
|                             lock->lock(); |  | ||||||
|                             if (seq[1] == '3' && seq2 == '~' ) |  | ||||||
|                             { |  | ||||||
|                                 // delete
 |  | ||||||
|                                 if (raw_buffer.size() > 0 && size_t(raw_cursor) < raw_buffer.size()) |  | ||||||
|                                 { |  | ||||||
|                                     raw_buffer.erase(raw_cursor,1); |  | ||||||
|                                     prompt_refresh(); |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                             if (!read_char(seq3[0]) || !read_char(seq3[1])) |  | ||||||
|                             { |  | ||||||
|                                 lock->lock(); |  | ||||||
|                                 return -2; |  | ||||||
|                             } |  | ||||||
|                             if (seq2 == ';') |  | ||||||
|                             { |  | ||||||
|                                 // Format: esc [ n ; n DIRECTION
 |  | ||||||
|                                 // Ignore first character (second "n")
 |  | ||||||
|                                 if (seq3[1] == 'C') |  | ||||||
|                                 { |  | ||||||
|                                     forward_word(); |  | ||||||
|                                 } |  | ||||||
|                                 else if (seq3[1] == 'D') |  | ||||||
|                                 { |  | ||||||
|                                     back_word(); |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     break; |  | ||||||
|                 default: |  | ||||||
|                     if (raw_buffer.size() == size_t(raw_cursor)) |  | ||||||
|                     { |  | ||||||
|                         raw_buffer.append(1,c); |  | ||||||
|                         raw_cursor++; |  | ||||||
|                         if (plen+raw_buffer.size() < size_t(get_columns())) |  | ||||||
|                         { |  | ||||||
|                             /* Avoid a full update of the line in the
 |  | ||||||
|                              * trivial case. */ |  | ||||||
|                             if (::write(fd,&c,1) == -1) return -1; |  | ||||||
|                         } |  | ||||||
|                         else |  | ||||||
|                         { |  | ||||||
|                             prompt_refresh(); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     else |  | ||||||
|                     { |  | ||||||
|                         raw_buffer.insert(raw_cursor,1,c); |  | ||||||
|                         raw_cursor++; |  | ||||||
|                         prompt_refresh(); |  | ||||||
|                     } |  | ||||||
|                     break; |  | ||||||
|                 case 21: // Ctrl+u, delete the whole line.
 |  | ||||||
|                     raw_buffer.clear(); |  | ||||||
|                     raw_cursor = 0; |  | ||||||
|                     prompt_refresh(); |  | ||||||
|                     break; |  | ||||||
|                 case 11: // Ctrl+k, delete from current to end of line.
 |  | ||||||
|                     raw_buffer.erase(raw_cursor); |  | ||||||
|                     prompt_refresh(); |  | ||||||
|                     break; |  | ||||||
|                 case 1: // Ctrl+a, go to the start of the line
 |  | ||||||
|                     raw_cursor = 0; |  | ||||||
|                     prompt_refresh(); |  | ||||||
|                     break; |  | ||||||
|                 case 5: // ctrl+e, go to the end of the line
 |  | ||||||
|                     raw_cursor = raw_buffer.size(); |  | ||||||
|                     prompt_refresh(); |  | ||||||
|                     break; |  | ||||||
|                 case 12: // ctrl+l, clear screen
 |  | ||||||
|                     clear(); |  | ||||||
|                     prompt_refresh(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             return raw_buffer.size(); |  | ||||||
|         } |  | ||||||
|         FILE * dfout_C; |  | ||||||
|         bool supported_terminal; |  | ||||||
|         // state variables
 |  | ||||||
|         bool rawmode;           // is raw mode active?
 |  | ||||||
|         termios orig_termios;   // saved/restored by raw mode
 |  | ||||||
|         // current state
 |  | ||||||
|         enum console_state |  | ||||||
|         { |  | ||||||
|             con_unclaimed, |  | ||||||
|             con_lineedit |  | ||||||
|         } state; |  | ||||||
|         bool in_batch; |  | ||||||
|         std::string prompt;     // current prompt string
 |  | ||||||
|         std::string raw_buffer; // current raw mode buffer
 |  | ||||||
|         int raw_cursor;         // cursor position in the buffer
 |  | ||||||
|         // thread exit mechanism
 |  | ||||||
|         int exit_pipe[2]; |  | ||||||
|         fd_set descriptor_set; |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Console::Console() |  | ||||||
| { |  | ||||||
|     d = 0; |  | ||||||
|     inited = false; |  | ||||||
|     // we can't create the mutex at this time. the SDL functions aren't hooked yet.
 |  | ||||||
|     wlock = new recursive_mutex(); |  | ||||||
| } |  | ||||||
| Console::~Console() |  | ||||||
| { |  | ||||||
|     if(inited) |  | ||||||
|         shutdown(); |  | ||||||
|     if(wlock) |  | ||||||
|         delete wlock; |  | ||||||
|     if(d) |  | ||||||
|         delete d; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool Console::init(bool sharing) |  | ||||||
| { |  | ||||||
|     if(sharing) |  | ||||||
|     { |  | ||||||
|         inited = false; |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     if (!freopen("stdout.log", "w", stdout)) |  | ||||||
|         ; |  | ||||||
|     d = new Private(); |  | ||||||
|     // make our own weird streams so our IO isn't redirected
 |  | ||||||
|     d->dfout_C = fopen("/dev/tty", "w"); |  | ||||||
|     std::cin.tie(this); |  | ||||||
|     clear(); |  | ||||||
|     d->supported_terminal = !isUnsupportedTerm() &&  isatty(STDIN_FILENO); |  | ||||||
|     // init the exit mechanism
 |  | ||||||
|     if (pipe(d->exit_pipe) == -1) |  | ||||||
|         ; |  | ||||||
|     FD_ZERO(&d->descriptor_set); |  | ||||||
|     FD_SET(STDIN_FILENO, &d->descriptor_set); |  | ||||||
|     FD_SET(d->exit_pipe[0], &d->descriptor_set); |  | ||||||
|     inited = true; |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool Console::shutdown(void) |  | ||||||
| { |  | ||||||
|     if(!d) |  | ||||||
|         return true; |  | ||||||
|     lock_guard <recursive_mutex> g(*wlock); |  | ||||||
|     if(d->rawmode) |  | ||||||
|         d->disable_raw(); |  | ||||||
|     d->print("\n"); |  | ||||||
|     inited = false; |  | ||||||
|     // kill the thing
 |  | ||||||
|     close(d->exit_pipe[1]); |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Console::begin_batch() |  | ||||||
| { |  | ||||||
|     //color_ostream::begin_batch();
 |  | ||||||
| 
 |  | ||||||
|     wlock->lock(); |  | ||||||
| 
 |  | ||||||
|     if (inited) |  | ||||||
|         d->begin_batch(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Console::end_batch() |  | ||||||
| { |  | ||||||
|     if (inited) |  | ||||||
|         d->end_batch(); |  | ||||||
| 
 |  | ||||||
|     wlock->unlock(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Console::flush_proxy() |  | ||||||
| { |  | ||||||
|     lock_guard <recursive_mutex> g(*wlock); |  | ||||||
|     if (inited) |  | ||||||
|         d->flush(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Console::add_text(color_value color, const std::string &text) |  | ||||||
| { |  | ||||||
|     lock_guard <recursive_mutex> g(*wlock); |  | ||||||
|     if (inited) |  | ||||||
|         d->print_text(color, text); |  | ||||||
|     else |  | ||||||
|         fwrite(text.data(), 1, text.size(), stderr); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int Console::get_columns(void) |  | ||||||
| { |  | ||||||
|     lock_guard <recursive_mutex> g(*wlock); |  | ||||||
|     int ret = -1; |  | ||||||
|     if(inited) |  | ||||||
|         ret = d->get_columns(); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int Console::get_rows(void) |  | ||||||
| { |  | ||||||
|     lock_guard <recursive_mutex> g(*wlock); |  | ||||||
|     int ret = -1; |  | ||||||
|     if(inited) |  | ||||||
|         ret = d->get_rows(); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Console::clear() |  | ||||||
| { |  | ||||||
|     lock_guard <recursive_mutex> g(*wlock); |  | ||||||
|     if(inited) |  | ||||||
|         d->clear(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Console::gotoxy(int x, int y) |  | ||||||
| { |  | ||||||
|     lock_guard <recursive_mutex> g(*wlock); |  | ||||||
|     if(inited) |  | ||||||
|         d->gotoxy(x,y); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Console::cursor(bool enable) |  | ||||||
| { |  | ||||||
|     lock_guard <recursive_mutex> g(*wlock); |  | ||||||
|     if(inited) |  | ||||||
|         d->cursor(enable); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch) |  | ||||||
| { |  | ||||||
|     lock_guard <recursive_mutex> g(*wlock); |  | ||||||
|     int ret = -2; |  | ||||||
|     if(inited) |  | ||||||
|         ret = d->lineedit(prompt,output,wlock,ch); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Console::msleep (unsigned int msec) |  | ||||||
| { |  | ||||||
|     if (msec > 1000) sleep(msec/1000000); |  | ||||||
|     usleep((msec % 1000000) * 1000); |  | ||||||
| } |  | ||||||
		Loading…
	
		Reference in New Issue