summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPetr Mrázek2011-07-15 15:55:01 +0200
committerPetr Mrázek2011-07-15 15:55:01 +0200
commit459d48d75a017a3b20ac532fdac3181845c282be (patch)
treedb886a1c6580292730af0ba35fbe3ba18fff9551
parent0af631aaa397e36263c7918cc823c737d6acc64f (diff)
downloaddfhack-459d48d75a017a3b20ac532fdac3181845c282be.tar.gz
dfhack-459d48d75a017a3b20ac532fdac3181845c282be.tar.bz2
dfhack-459d48d75a017a3b20ac532fdac3181845c282be.tar.xz
Made the linux console super awesome. The dfhack script won't LD_PRELOAD dfhack for gdb.
-rw-r--r--library/Console-linux.cpp904
-rw-r--r--library/include/dfhack/Console.h37
-rwxr-xr-xpackage/linux/dfhack14
-rw-r--r--plugins/kittens.cpp12
-rw-r--r--plugins/reveal.cpp24
-rw-r--r--plugins/vdig.cpp8
6 files changed, 594 insertions, 405 deletions
diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp
index c17860ef..e97cc9d3 100644
--- a/library/Console-linux.cpp
+++ b/library/Console-linux.cpp
@@ -80,42 +80,527 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <termios.h>
#include <errno.h>
#include <deque>
+#include <dfhack/FakeSDL.h>
using namespace DFHack;
-#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
-#define LINENOISE_MAX_LINE 4096
+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 "";
+ }
+}
-static const char *unsupported_term[] = {"dumb","cons25",NULL};
namespace DFHack
{
class Private
{
public:
+ enum console_state
+ {
+ con_unclaimed,
+ con_lineedit
+ };
Private()
{
- dfout_C = 0;
- stream_o = 0;
- rawmode = 0;
+ dfout_C = NULL;
+ stream_o = NULL;
+ rawmode = false;
+ supported_terminal = false;
+ state = con_unclaimed;
};
+ /// Print a formatted string, like printf
+ int print(const char * format, ...)
+ {
+ va_list args;
+ va_start( args, format );
+ int ret = vprint( format, args );
+ va_end( args );
+ return ret;
+ }
+ int vprint(const char * format, va_list vl)
+ {
+ if(state == con_lineedit)
+ {
+ disable_raw();
+ fprintf(dfout_C,"\x1b[1G");
+ fprintf(dfout_C,"\x1b[0K");
+ int ret = vfprintf( dfout_C, format, vl );
+ enable_raw();
+ prompt_refresh();
+ return ret;
+ }
+ else return vfprintf( dfout_C, format, vl );
+ }
+ int vprinterr(const char * format, va_list vl)
+ {
+ if(state == con_lineedit)
+ {
+ disable_raw();
+ color(Console::COLOR_LIGHTRED);
+ fprintf(dfout_C,"\x1b[1G");
+ fprintf(dfout_C,"\x1b[0K");
+ int ret = vfprintf( dfout_C, format, vl );
+ reset_color();
+ enable_raw();
+ prompt_refresh();
+ return ret;
+ }
+ else
+ {
+ color(Console::COLOR_LIGHTRED);
+ int ret = vfprintf( dfout_C, format, vl );
+ reset_color();
+ }
+ }
+ /// Print a formatted string, like printf, in red
+ int printerr(const char * format, ...)
+ {
+ va_list args;
+ va_start( args, format );
+ int ret = vprinterr( format, args );
+ va_end( args );
+ return ret;
+ }
+ /// Clear the console, along with its scrollback
+ void clear()
+ {
+ if(rawmode)
+ {
+ const char * clr = "\033c\033[3J\033[H";
+ ::write(STDIN_FILENO,clr,strlen(clr));
+ }
+ else
+ {
+ print("\033c\033[3J\033[H");
+ }
+ }
+ /// Position cursor at x,y. 1,1 = top left corner
+ void gotoxy(int x, int y)
+ {
+ print("\033[%d;%dH", y,x);
+ }
+ /// Set color (ANSI color number)
+ void color(Console::color_value index)
+ {
+ if(!rawmode)
+ fprintf(dfout_C,getANSIColor(index));
+ else
+ {
+ const char * colstr = getANSIColor(index);
+ int lstr = strlen(colstr);
+ ::write(STDIN_FILENO,colstr,lstr);
+ }
+ }
+ /// Reset color to default
+ void reset_color(void)
+ {
+ color(Console::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);
+ /// A simple line edit (raw mode)
+ int lineedit(const std::string& prompt, std::string& output)
+ {
+ output.clear();
+ this->prompt = prompt;
+ if (!supported_terminal)
+ {
+ print(prompt.c_str());
+ fflush(dfout_C);
+ // FIXME: what do we do here???
+ //SDL_mutexV(wlock);
+ std::getline(std::cin, output);
+ //SDL_mutexP(wlock);
+ return output.size();
+ }
+ else
+ {
+ int count;
+ if (enable_raw() == -1) return 0;
+ if(state == con_lineedit)
+ return -1;
+ state = con_lineedit;
+ count = prompt_loop();
+ state = con_unclaimed;
+ disable_raw();
+ print("\n");
+ if(count != -1)
+ {
+ output = raw_buffer;
+ }
+ return count;
+ }
+ }
+ /// add a command to the history
+ void history_add(const std::string& command)
+ {
+ // if current command = last in history -> do not add. Always add if history is empty.
+ if(!history.empty() && history.front() == command)
+ return;
+ history.push_front(command);
+ if(history.size() > 100)
+ history.pop_back();
+ }
+ /// clear the command history
+ void history_clear();
- int isUnsupportedTerm(void)
+ int enable_raw()
{
- char *term = getenv("TERM");
- int j;
+ 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)
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ // 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;
+ }
- if (term == NULL) return 0;
- for (j = 0; unsupported_term[j]; j++)
- if (!strcasecmp(term,unsupported_term[j])) return 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()
+ {
+ 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)
+ {
+ char c;
+ int nread;
+ char seq[2], seq2[2];
+ SDL_mutexV(wlock);
+ nread = ::read(fd,&c,1);
+ SDL_mutexP(wlock);
+ if (nread <= 0) return raw_buffer.size();
+
+ /* 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.pop_front();
+ 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
+ SDL_mutexV(wlock);
+ if (::read(fd,seq,2) == -1)
+ {
+ SDL_mutexP(wlock);
+ break;
+ }
+ SDL_mutexP(wlock);
+ if(seq[0] == '[')
+ {
+ if (seq[1] == 'D')
+ {
+ left_arrow:
+ if (raw_cursor > 0)
+ {
+ raw_cursor--;
+ prompt_refresh();
+ }
+ }
+ else if ( seq[1] == 'C')
+ {
+ right_arrow:
+ /* right arrow */
+ if (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 (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
+ SDL_mutexV(wlock);
+ if (::read(fd,seq2,2) == -1)
+ {
+ SDL_mutexP(wlock);
+ break;
+ }
+ SDL_mutexP(wlock);
+ if (seq2[0] == '~' && seq[1] == '3')
+ {
+ // delete
+ if (raw_buffer.size() > 0 && raw_cursor < raw_buffer.size())
+ {
+ raw_buffer.erase(raw_cursor,1);
+ prompt_refresh();
+ }
+ }
+ }
+ }
+ break;
+ default:
+ if (raw_buffer.size() == raw_cursor)
+ {
+ raw_buffer.append(1,c);
+ raw_cursor++;
+ if (plen+raw_buffer.size() < 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;
duthomhas::stdiobuf * stream_o;
- termios orig_termios; /* in order to restore at exit */
- int rawmode; /* for atexit() function to check if restore is needed*/
std::deque <std::string> history;
+ bool supported_terminal;
+ // state variables
+ bool rawmode; // is raw mode active?
+ termios orig_termios; // saved/restored by raw mode
+ int state; // current state
+ std::string prompt; // current prompt string
+ std::string raw_buffer; // current raw mode buffer
+ int raw_cursor; // cursor position in the buffer
+ // locks
+ SDL::Mutex *wlock;
};
}
+
Console::Console():std::ostream(0), std::ios(0)
{
d = 0;
@@ -132,131 +617,85 @@ bool Console::init(void)
// make our own weird streams so our IO isn't redirected
d->dfout_C = fopen("/dev/tty", "w");
d->stream_o = new duthomhas::stdiobuf(d->dfout_C);
+ d->wlock = SDL_CreateMutex();
rdbuf(d->stream_o);
std::cin.tie(this);
clear();
+ d->supported_terminal = !isUnsupportedTerm() && isatty(STDIN_FILENO);
}
bool Console::shutdown(void)
{
if(d->rawmode)
- disable_raw();
+ d->disable_raw();
print("\n");
}
int Console::print( const char* format, ... )
{
va_list args;
+ SDL_mutexP(d->wlock);
va_start( args, format );
- int ret = vfprintf( d->dfout_C, format, args );
- va_end( args );
+ int ret = d->vprint(format, args);
+ va_end(args);
+ SDL_mutexV(d->wlock);
return ret;
}
-
int Console::printerr( const char* format, ... )
{
- color(12);
va_list args;
+ SDL_mutexP(d->wlock);
va_start( args, format );
- int ret = vfprintf( d->dfout_C, format, args );
- va_end( args );
- reset_color();
+ int ret = d->vprinterr(format, args);
+ va_end(args);
+ SDL_mutexV(d->wlock);
return ret;
}
int Console::get_columns(void)
{
- winsize ws;
- if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 80;
- return ws.ws_col;
+ return d->get_columns();
}
int Console::get_rows(void)
{
- winsize ws;
- if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 25;
- return ws.ws_row;
+ return d->get_rows();
}
void Console::clear()
{
- if(d->rawmode)
- {
- const char * clr = "\033c\033[3J\033[H";
- ::write(STDIN_FILENO,clr,strlen(clr));
- }
- else
- {
- print("\033c\033[3J\033[H");
- }
+ SDL_mutexP(d->wlock);
+ d->clear();
+ SDL_mutexV(d->wlock);
}
void Console::gotoxy(int x, int y)
{
- print("\033[%d;%dH", y,x);
-}
-
-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 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 "";
- }
+ SDL_mutexP(d->wlock);
+ d->gotoxy(x,y);
+ SDL_mutexV(d->wlock);
}
-void Console::color(int index)
+void Console::color(color_value index)
{
- print(getANSIColor(index));
+ SDL_mutexP(d->wlock);
+ d->color(index);
+ SDL_mutexV(d->wlock);
}
void Console::reset_color( void )
{
- print(RESETCOLOR);
- fflush(d->dfout_C);
+ SDL_mutexP(d->wlock);
+ d->reset_color();
+ SDL_mutexV(d->wlock);
}
void Console::cursor(bool enable)
{
- if(enable)
- print("\033[?25h");
- else
- print("\033[?25l");
+ SDL_mutexP(d->wlock);
+ d->cursor(enable);
+ SDL_mutexV(d->wlock);
}
void Console::msleep (unsigned int msec)
@@ -265,289 +704,18 @@ void Console::msleep (unsigned int msec)
usleep((msec % 1000000) * 1000);
}
-int Console::enable_raw()
-{
- struct termios raw;
-
- if (!isatty(STDIN_FILENO)) goto fatal;
- if (tcgetattr(STDIN_FILENO,&d->orig_termios) == -1) goto fatal;
-
- raw = d->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) */
- raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
- /* 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) goto fatal;
- d->rawmode = 1;
- return 0;
-
-fatal:
- errno = ENOTTY;
- return -1;
-}
-
-void Console::disable_raw()
-{
- /* Don't even check the return value as it's too late. */
- if (d->rawmode && tcsetattr(STDIN_FILENO,TCSAFLUSH,&d->orig_termios) != -1)
- d->rawmode = 0;
-}
-
-void Console::prompt_refresh( const std::string& prompt, const std::string& buffer, size_t pos)
-{
- char seq[64];
- int cols = get_columns();
- int plen = prompt.size();
- const char * buf = buffer.c_str();
- int len = buffer.size();
- // Use math! This is silly.
- while((plen+pos) >= cols)
- {
- buf++;
- len--;
- pos--;
- }
- 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)(pos+plen));
- if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
-}
-
-int Console::prompt_loop(const std::string & prompt, std::string & buffer)
-{
- int fd = STDIN_FILENO;
- size_t plen = prompt.size();
- size_t pos = 0;
- size_t cols = get_columns();
- int history_index = 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(),plen) == -1) return -1;
- while(1)
- {
- char c;
- int nread;
- char seq[2], seq2[2];
-
- nread = ::read(fd,&c,1);
- if (nread <= 0) return buffer.size();
-
- /* 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 */
- d->history.pop_front();
- return buffer.size();
- case 3: /* ctrl-c */
- errno = EAGAIN;
- return -1;
- case 127: /* backspace */
- case 8: /* ctrl-h */
- if (pos > 0 && buffer.size() > 0)
- {
- buffer.erase(pos-1,1);
- pos--;
- prompt_refresh(prompt,buffer,pos);
- }
- break;
- case 27: /* escape sequence */
- if (::read(fd,seq,2) == -1) break;
- if(seq[0] == '[')
- {
- if (seq[1] == 'D')
- {
- left_arrow:
- if (pos > 0)
- {
- pos--;
- prompt_refresh(prompt,buffer,pos);
- }
- }
- else if ( seq[1] == 'C')
- {
- right_arrow:
- /* right arrow */
- if (pos != buffer.size())
- {
- pos++;
- prompt_refresh(prompt,buffer,pos);
- }
- }
- else if (seq[1] == 'A' || seq[1] == 'B')
- {
- /* up and down arrow: history */
- if (d->history.size() > 1)
- {
- /* Update the current history entry before to
- * overwrite it with tne next one. */
- d->history[history_index] = buffer;
- /* Show the new entry */
- history_index += (seq[1] == 'A') ? 1 : -1;
- if (history_index < 0)
- {
- history_index = 0;
- break;
- }
- else if (history_index >= d->history.size())
- {
- history_index = d->history.size()-1;
- break;
- }
- buffer = d->history[history_index];
- pos = buffer.size();
- prompt_refresh(prompt,buffer,pos);
- }
- }
- else if(seq[1] == 'H')
- {
- // home
- pos = 0;
- prompt_refresh(prompt,buffer,pos);
- }
- else if(seq[1] == 'F')
- {
- // end
- pos = buffer.size();
- prompt_refresh(prompt,buffer,pos);
- }
- else if (seq[1] > '0' && seq[1] < '7')
- {
- // extended escape
- if (::read(fd,seq2,2) == -1) break;
- if (seq2[0] == '~' && seq[1] == '3')
- {
- // delete
- if (buffer.size() > 0 && pos < buffer.size())
- {
- buffer.erase(pos,1);
- prompt_refresh(prompt,buffer,pos);
- }
- }
- }
- }
- break;
- default:
- if (buffer.size() == pos)
- {
- buffer.append(1,c);
- pos++;
- if (plen+buffer.size() < cols)
- {
- /* Avoid a full update of the line in the
- * trivial case. */
- if (::write(fd,&c,1) == -1) return -1;
- }
- else
- {
- prompt_refresh(prompt,buffer,pos);
- }
- }
- else
- {
- buffer.insert(pos,1,c);
- pos++;
- prompt_refresh(prompt,buffer,pos);
- }
- break;
- case 21: // Ctrl+u, delete the whole line.
- buffer.clear();
- pos = 0;
- prompt_refresh(prompt,buffer,pos);
- break;
- case 11: // Ctrl+k, delete from current to end of line.
- buffer.erase(pos);
- prompt_refresh(prompt,buffer,pos);
- break;
- case 1: // Ctrl+a, go to the start of the line
- pos = 0;
- prompt_refresh(prompt,buffer,pos);
- break;
- case 5: // ctrl+e, go to the end of the line
- pos = buffer.size();
- prompt_refresh(prompt,buffer,pos);
- break;
- case 12: // ctrl+l, clear screen
- clear();
- prompt_refresh(prompt,buffer,pos);
- }
- }
- return buffer.size();
-}
// push to front, remove from back if we are above maximum. ignore immediate duplicates
void Console::history_add(const std::string & command)
{
- if(!d->history.empty() && d->history.front() == command)
- return;
- d->history.push_front(command);
- if(d->history.size() > 100)
- d->history.pop_back();
+ SDL_mutexP(d->wlock);
+ d->history_add(command);
+ SDL_mutexV(d->wlock);
}
int Console::lineedit(const std::string & prompt, std::string & output)
{
- output.clear();
- int count;
- if (d->isUnsupportedTerm() || !isatty(STDIN_FILENO))
- {
- print(prompt.c_str());
- fflush(d->dfout_C);
- std::getline(std::cin, output);
- return output.size();
- }
- else
- {
- if (enable_raw() == -1) return 0;
- count = prompt_loop(prompt, output);
- disable_raw();
- print("\n");
- return output.size();
- }
+ SDL_mutexP(d->wlock);
+ int ret = d->lineedit(prompt,output);
+ SDL_mutexV(d->wlock);
+ return ret;
} \ No newline at end of file
diff --git a/library/include/dfhack/Console.h b/library/include/dfhack/Console.h
index 628cc8ec..5c9cf07e 100644
--- a/library/include/dfhack/Console.h
+++ b/library/include/dfhack/Console.h
@@ -32,12 +32,36 @@ namespace DFHack
class DFHACK_EXPORT Console : public std::ostream
{
public:
+ enum color_value
+ {
+ COLOR_RESET = -1,
+ COLOR_BLACK = 0,
+ COLOR_BLUE,
+ COLOR_GREEN,
+ COLOR_CYAN,
+ COLOR_RED,
+ COLOR_MAGENTA,
+ COLOR_BROWN,
+ COLOR_GREY,
+ COLOR_DARKGREY,
+ COLOR_LIGHTBLUE,
+ COLOR_LIGHTGREEN,
+ COLOR_LIGHTCYAN,
+ COLOR_LIGHTRED,
+ COLOR_LIGHTMAGENTA,
+ COLOR_YELLOW,
+ COLOR_WHITE,
+ COLOR_MAX = COLOR_WHITE
+ };
+ ///ctor, NOT thread-safe
Console();
+ ///dtor, NOT thread-safe
~Console();
- /// initialize the console
+ /// initialize the console. NOT thread-safe
bool init( void );
- /// shutdown the console
+ /// shutdown the console. NOT thread-safe
bool shutdown( void );
+
/// Print a formatted string, like printf
int print(const char * format, ...);
/// Print a formatted string, like printf, in red
@@ -47,7 +71,7 @@ namespace DFHack
/// Position cursor at x,y. 1,1 = top left corner
void gotoxy(int x, int y);
/// Set color (ANSI color number)
- void color(int index);
+ void color(color_value c);
/// Reset color to default
void reset_color(void);
/// Enable or disable the caret/cursor
@@ -64,12 +88,9 @@ namespace DFHack
int lineedit(const std::string& prompt, std::string& output);
/// add a command to the history
void history_add(const std::string& command);
- private:
- int prompt_loop(const std::string & prompt, std::string & buffer);
- void prompt_refresh( const std::string & prompt, const std::string & buffer, size_t pos);
- int enable_raw();
- void disable_raw();
+ /// clear the command history
void history_clear();
+ private:
Private * d;
};
} \ No newline at end of file
diff --git a/package/linux/dfhack b/package/linux/dfhack
index 3b6708f9..d5d22187 100755
--- a/package/linux/dfhack
+++ b/package/linux/dfhack
@@ -10,31 +10,31 @@ cd "${DF_DIR}"
export SDL_DISABLE_LOCK_KEYS=1 # Work around for bug in Debian/Ubuntu SDL patch.
#export SDL_VIDEO_CENTERED=1 # Centre the screen. Messes up resizing.
-export LD_PRELOAD=./libdfhack.so
-
case "$1" in
-g | --gdb)
shift
- gdb ./libs/Dwarf_Fortress $*
+ echo "set environment LD_PRELOAD=./libdfhack.so" > gdbcmd.tmp
+ gdb -x gdbcmd.tmp ./libs/Dwarf_Fortress $*
+ rm gdbcmd.tmp
ret=$?
;;
-h | --helgrind)
shift
- valgrind --tool=helgrind --log-file=helgrind.log ./libs/Dwarf_Fortress $*
+ LD_PRELOAD=./libdfhack.so valgrind --tool=helgrind --log-file=helgrind.log ./libs/Dwarf_Fortress $*
ret=$?
;;
-v | --valgrind)
shift
- valgrind --log-file=valgrind.log ./libs/Dwarf_Fortress $*
+ LD_PRELOAD=./libdfhack.so valgrind --log-file=valgrind.log ./libs/Dwarf_Fortress $*
ret=$?
;;
*)
- ./libs/Dwarf_Fortress $*
+ LD_PRELOAD=./libdfhack.so ./libs/Dwarf_Fortress $*
ret=$?
;;
esac
# Reset terminal to sane state in case of a crash
-reset -I
+reset
exit $ret
diff --git a/plugins/kittens.cpp b/plugins/kittens.cpp
index 0c8dffb0..c6c4728a 100644
--- a/plugins/kittens.cpp
+++ b/plugins/kittens.cpp
@@ -50,7 +50,7 @@ DFhackCExport command_result plugin_onupdate ( Core * c )
uint64_t time2 = GetTimeMs64();
uint64_t delta = time2-timeLast;
timeLast = time2;
- c->con << "Time delta = " << delta << " ms" << std::endl;
+ c->con.print("Time delta = %d ms\n", delta);
}
return CR_OK;
}
@@ -66,7 +66,7 @@ DFhackCExport command_result ktimer (Core * c, vector <string> & parameters)
c->Suspend();
c->Resume();
uint64_t timeend = GetTimeMs64();
- c->con << "Time to suspend = " << timeend - timestart << " ms" << std::endl;
+ c->con.print("Time to suspend = %d ms\n",timeend - timestart);
timeLast = timeend;
timering = true;
return CR_OK;
@@ -111,7 +111,7 @@ DFhackCExport command_result kittens (Core * c, vector <string> & parameters)
};
con.cursor(false);
con.clear();
- int color = 1;
+ Console::color_value color = Console::COLOR_BLUE;
while(1)
{
if(shutdown_flag)
@@ -135,8 +135,8 @@ DFhackCExport command_result kittens (Core * c, vector <string> & parameters)
}
con.flush();
con.msleep(60);
- color ++;
- if(color > 15)
- color = 1;
+ ((int&)color) ++;
+ if(color > Console::COLOR_MAX)
+ color = Console::COLOR_BLUE;
}
}
diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp
index efdf6133..d387b099 100644
--- a/plugins/reveal.cpp
+++ b/plugins/reveal.cpp
@@ -104,7 +104,7 @@ DFhackCExport command_result reveal(DFHack::Core * c, std::vector<std::string> &
Console & con = c->con;
if(revealed != NOT_REVEALED)
{
- con << "Map is already revealed or this is a different map." << std::endl;
+ con.printerr("Map is already revealed or this is a different map.\n");
return CR_FAILURE;
}
@@ -115,20 +115,20 @@ DFhackCExport command_result reveal(DFHack::Core * c, std::vector<std::string> &
World->ReadGameMode(gm);
if(gm.g_mode != GAMEMODE_DWARF)
{
- con << "Only in fortress mode." << std::endl;
+ con.printerr("Only in fortress mode.\n");
c->Resume();
return CR_FAILURE;
}
if(!Maps->Start())
{
- con << "Can't init map." << std::endl;
+ con.printerr("Can't init map.\n");
c->Resume();
return CR_FAILURE;
}
if(no_hell && !Maps->StartFeatures())
{
- con << "Unable to read local features; can't reveal map safely" << std::endl;
+ con.printerr("Unable to read local features; can't reveal map safely.\n");
c->Resume();
return CR_FAILURE;
}
@@ -175,10 +175,10 @@ DFhackCExport command_result reveal(DFHack::Core * c, std::vector<std::string> &
World->SetPauseState(true);
}
c->Resume();
- con << "Map revealed." << std::endl;
+ con.print("Map revealed.\n");
if(!no_hell)
- con << "Unpausing can unleash the forces of hell, so it has been temporarily disabled." << std::endl;
- con << "Run 'unreveal' to revert to previous state." << std::endl;
+ con.print("Unpausing can unleash the forces of hell, so it has been temporarily disabled.\n");
+ con.print("Run 'unreveal' to revert to previous state.\n");
return CR_OK;
}
@@ -187,7 +187,7 @@ DFhackCExport command_result unreveal(DFHack::Core * c, std::vector<std::string>
Console & con = c->con;
if(!revealed)
{
- con << "There's nothing to revert!" << std::endl;
+ con.printerr("There's nothing to revert!\n");
return CR_FAILURE;
}
c->Suspend();
@@ -197,14 +197,14 @@ DFhackCExport command_result unreveal(DFHack::Core * c, std::vector<std::string>
World->ReadGameMode(gm);
if(gm.g_mode != GAMEMODE_DWARF)
{
- con << "Only in fortress mode." << std::endl;
+ con.printerr("Only in fortress mode.\n");
c->Resume();
return CR_FAILURE;
}
Maps = c->getMaps();
if(!Maps->Start())
{
- con << "Can't init map." << std::endl;
+ con.printerr("Can't init map.\n");
c->Resume();
return CR_FAILURE;
}
@@ -214,7 +214,7 @@ DFhackCExport command_result unreveal(DFHack::Core * c, std::vector<std::string>
Maps->getSize(x_max_b,y_max_b,z_max_b);
if(x_max != x_max_b || y_max != y_max_b || z_max != z_max_b)
{
- con << "The map is not of the same size..." << std::endl;
+ con.printerr("The map is not of the same size...\n");
c->Resume();
return CR_FAILURE;
}
@@ -233,7 +233,7 @@ DFhackCExport command_result unreveal(DFHack::Core * c, std::vector<std::string>
// give back memory.
hidesaved.clear();
revealed = NOT_REVEALED;
- con << "Map hidden!" << std::endl;
+ con.print("Map hidden!\n");
c->Resume();
return CR_OK;
}
diff --git a/plugins/vdig.cpp b/plugins/vdig.cpp
index 459bf224..eed39c87 100644
--- a/plugins/vdig.cpp
+++ b/plugins/vdig.cpp
@@ -51,7 +51,7 @@ DFhackCExport command_result vdig (Core * c, vector <string> & parameters)
// init the map
if(!Maps->Start())
{
- con << "Can't init map. Make sure you have a map loaded in DF.\n";
+ con.printerr("Can't init map. Make sure you have a map loaded in DF.\n");
c->Resume();
return CR_FAILURE;
}
@@ -63,14 +63,14 @@ DFhackCExport command_result vdig (Core * c, vector <string> & parameters)
Gui->getCursorCoords(cx,cy,cz);
while(cx == -30000)
{
- con << "Cursor is not active. Point the cursor at a vein.\n";
+ con.printerr("Cursor is not active. Point the cursor at a vein.\n");
c->Resume();
return CR_FAILURE;
}
DFHack::DFCoord xy ((uint32_t)cx,(uint32_t)cy,cz);
if(xy.x == 0 || xy.x == tx_max - 1 || xy.y == 0 || xy.y == ty_max - 1)
{
- con << "I won't dig the borders. That would be cheating!\n";
+ con.printerr("I won't dig the borders. That would be cheating!\n");
c->Resume();
return CR_FAILURE;
}
@@ -80,7 +80,7 @@ DFhackCExport command_result vdig (Core * c, vector <string> & parameters)
int16_t veinmat = MCache->veinMaterialAt(xy);
if( veinmat == -1 )
{
- con << "This tile is not a vein.\n";
+ con.printerr("This tile is not a vein.\n");
delete MCache;
c->Resume();
return CR_FAILURE;