Allow canceling lineedit with ctrl+c

develop
Pauli 2018-07-04 15:21:25 +03:00
parent ae5a1fad84
commit d1a3f1a738
9 changed files with 74 additions and 49 deletions

@ -423,13 +423,13 @@ namespace DFHack
int count; int count;
if (enable_raw() == -1) return 0; if (enable_raw() == -1) return 0;
if(state == con_lineedit) if(state == con_lineedit)
return -1; return Console::FAILURE;
state = con_lineedit; state = con_lineedit;
count = prompt_loop(lock,ch); count = prompt_loop(lock,ch);
state = con_unclaimed; state = con_unclaimed;
disable_raw(); disable_raw();
print("\n"); print("\n");
if(count != -1) if(count > Console::FAILURE)
{ {
output = toLocaleMB(raw_buffer); output = toLocaleMB(raw_buffer);
} }
@ -441,9 +441,9 @@ namespace DFHack
struct termios raw; struct termios raw;
if (!supported_terminal) if (!supported_terminal)
return -1; return Console::FAILURE;
if (tcgetattr(STDIN_FILENO,&orig_termios) == -1) if (tcgetattr(STDIN_FILENO,&orig_termios) == -1)
return -1; return Console::FAILURE;
raw = orig_termios; //modify the original mode raw = orig_termios; //modify the original mode
// input modes: no break, no CR to NL, no parity check, no strip char, // input modes: no break, no CR to NL, no parity check, no strip char,
@ -465,7 +465,7 @@ namespace DFHack
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0;// 1 byte, no timer raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0;// 1 byte, no timer
// put terminal in raw mode // put terminal in raw mode
if (tcsetattr(STDIN_FILENO, TCSADRAIN, &raw) < 0) if (tcsetattr(STDIN_FILENO, TCSADRAIN, &raw) < 0)
return -1; return Console::FAILURE;
rawmode = 1; rawmode = 1;
return 0; return 0;
} }
@ -518,7 +518,8 @@ namespace DFHack
* initially is just an empty string. */ * initially is just an empty string. */
const std::string empty; const std::string empty;
history.add(empty); history.add(empty);
if (::write(fd,prompt.c_str(),prompt.size()) == -1) return -1; if (::write(fd,prompt.c_str(),prompt.size()) == -1)
return Console::FAILURE;
while(1) while(1)
{ {
unsigned char c; unsigned char c;
@ -528,7 +529,7 @@ namespace DFHack
if(!read_char(c)) if(!read_char(c))
{ {
lock->lock(); lock->lock();
return -2; return Console::SHUTDOWN;
} }
lock->lock(); lock->lock();
/* Only autocomplete when the callback is set. It returns < 0 when /* Only autocomplete when the callback is set. It returns < 0 when
@ -561,8 +562,7 @@ namespace DFHack
history.remove(); history.remove();
return raw_buffer.size(); return raw_buffer.size();
case 3: // ctrl-c case 3: // ctrl-c
errno = EAGAIN; return Console::RETRY;
return -1;
case 127: // backspace case 127: // backspace
case 8: // ctrl-h case 8: // ctrl-h
if (raw_cursor > 0 && raw_buffer.size() > 0) if (raw_cursor > 0 && raw_buffer.size() > 0)
@ -577,7 +577,7 @@ namespace DFHack
if (!read_char(seq[0])) if (!read_char(seq[0]))
{ {
lock->lock(); lock->lock();
return -2; return Console::SHUTDOWN;
} }
lock->lock(); lock->lock();
if (seq[0] == 'b') if (seq[0] == 'b')
@ -593,7 +593,7 @@ namespace DFHack
if (!read_char(seq[1])) if (!read_char(seq[1]))
{ {
lock->lock(); lock->lock();
return -2; return Console::SHUTDOWN;
} }
if (seq[1] == 'D') if (seq[1] == 'D')
{ {
@ -658,7 +658,7 @@ namespace DFHack
if(!read_char(seq2)) if(!read_char(seq2))
{ {
lock->lock(); lock->lock();
return -2; return Console::SHUTDOWN;
} }
lock->lock(); lock->lock();
if (seq[1] == '3' && seq2 == '~' ) if (seq[1] == '3' && seq2 == '~' )
@ -674,7 +674,7 @@ namespace DFHack
if (!read_char(seq3[0]) || !read_char(seq3[1])) if (!read_char(seq3[0]) || !read_char(seq3[1]))
{ {
lock->lock(); lock->lock();
return -2; return Console::SHUTDOWN;
} }
if (seq2 == ';') if (seq2 == ';')
{ {
@ -747,9 +747,9 @@ namespace DFHack
// character starting from the first bye already red // character starting from the first bye already red
while ((sz = mbrtoc32(&c32,&mb[count-1],1, &state)) < 0) { while ((sz = mbrtoc32(&c32,&mb[count-1],1, &state)) < 0) {
if (sz == -1 || sz == -3) if (sz == -1 || sz == -3)
return -1; /* mbrtoc32 error (not valid utf-32 character */ return Console::FAILURE; /* mbrtoc32 error (not valid utf-32 character */
if(!read_char(c)) if(!read_char(c))
return -2; return Console::SHUTDOWN;
mb[count++] = c; mb[count++] = c;
} }
if (raw_buffer.size() == size_t(raw_cursor)) if (raw_buffer.size() == size_t(raw_cursor))
@ -760,7 +760,8 @@ namespace DFHack
{ {
/* Avoid a full update of the line in the /* Avoid a full update of the line in the
* trivial case. */ * trivial case. */
if (::write(fd,mb,count) == -1) return -1; if (::write(fd,mb,count) == -1)
return Console::FAILURE;
} }
else else
{ {
@ -888,7 +889,7 @@ void Console::add_text(color_value color, const std::string &text)
int Console::get_columns(void) int Console::get_columns(void)
{ {
lock_guard <recursive_mutex> g(*wlock); lock_guard <recursive_mutex> g(*wlock);
int ret = -1; int ret = Console::FAILURE;
if(inited) if(inited)
ret = d->get_columns(); ret = d->get_columns();
return ret; return ret;
@ -897,7 +898,7 @@ int Console::get_columns(void)
int Console::get_rows(void) int Console::get_rows(void)
{ {
lock_guard <recursive_mutex> g(*wlock); lock_guard <recursive_mutex> g(*wlock);
int ret = -1; int ret = Console::FAILURE;
if(inited) if(inited)
ret = d->get_rows(); ret = d->get_rows();
return ret; return ret;
@ -927,10 +928,10 @@ void Console::cursor(bool enable)
int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch) int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch)
{ {
lock_guard <recursive_mutex> g(*wlock); lock_guard <recursive_mutex> g(*wlock);
int ret = -2; int ret = Console::SHUTDOWN;
if(inited) { if(inited) {
ret = d->lineedit(prompt,output,wlock,ch); ret = d->lineedit(prompt,output,wlock,ch);
if (ret == -2) { if (ret == Console::SHUTDOWN) {
// kill the thing // kill the thing
if(d->rawmode) if(d->rawmode)
d->disable_raw(); d->disable_raw();

@ -287,7 +287,7 @@ namespace DFHack
lock->unlock(); lock->unlock();
if (ReadConsoleInputA(console_in, &rec, 1, &count) != 0) { if (ReadConsoleInputA(console_in, &rec, 1, &count) != 0) {
lock->lock(); lock->lock();
return -2; return Console::SHUTDOWN;
} }
lock->lock(); lock->lock();
if (rec.EventType != KEY_EVENT || !rec.Event.KeyEvent.bKeyDown) if (rec.EventType != KEY_EVENT || !rec.Event.KeyEvent.bKeyDown)
@ -382,7 +382,7 @@ namespace DFHack
state = con_lineedit; state = con_lineedit;
this->prompt = prompt; this->prompt = prompt;
count = prompt_loop(lock, ch); count = prompt_loop(lock, ch);
if(count != -1) if(count > Console::FAILURE)
output = raw_buffer; output = raw_buffer;
state = con_unclaimed; state = con_unclaimed;
print("\n"); print("\n");
@ -591,7 +591,7 @@ void Console::cursor(bool enable)
int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch) int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch)
{ {
wlock->lock(); wlock->lock();
int ret = -2; int ret = Console::SHUTDOWN;
if(inited) if(inited)
ret = d->lineedit(prompt,output,wlock,ch); ret = d->lineedit(prompt,output,wlock,ch);
wlock->unlock(); wlock->unlock();

@ -1421,13 +1421,15 @@ void fIOthread(void * iodata)
while (true) while (true)
{ {
string command = ""; string command = "";
int ret = con.lineedit("[DFHack]# ",command, main_history); int ret;
if(ret == -2) while ((ret = con.lineedit("[DFHack]# ",command, main_history))
== Console::RETRY);
if(ret == Console::SHUTDOWN)
{ {
cerr << "Console is shutting down properly." << endl; cerr << "Console is shutting down properly." << endl;
return; return;
} }
else if(ret == -1) else if(ret == Console::FAILURE)
{ {
cerr << "Console caught an unspecified error." << endl; cerr << "Console caught an unspecified error." << endl;
continue; continue;

@ -293,10 +293,13 @@ static int dfhack_lineedit_sync(lua_State *S, Console *pstream)
std::string ret; std::string ret;
int rv = pstream->lineedit(prompt, ret, hist); int rv = pstream->lineedit(prompt, ret, hist);
if (rv == Console::RETRY)
rv = 0; /* return empty string to lua */
if (rv < 0) if (rv < 0)
{ {
lua_pushnil(S); lua_pushnil(S);
if (rv == -2) if (rv == Console::SHUTDOWN)
lua_pushstring(S, "shutdown requested"); lua_pushstring(S, "shutdown requested");
else else
lua_pushstring(S, "input error"); lua_pushstring(S, "input error");
@ -1061,9 +1064,9 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state,
prompt = ">> "; prompt = ">> ";
std::string curline; std::string curline;
rv = con.lineedit(prompt,curline,hist); while((rv = con.lineedit(prompt,curline,hist)) == Console::RETRY);
if (rv < 0) { if (rv <= Console::FAILURE) {
rv = rv == -2 ? LUA_OK : LUA_ERRRUN; rv = rv == Console::SHUTDOWN ? LUA_OK : LUA_ERRRUN;
break; break;
} }
hist.add(curline); hist.add(curline);

@ -153,6 +153,12 @@ namespace DFHack
int get_rows(void); int get_rows(void);
/// beep. maybe? /// beep. maybe?
//void beep (void); //void beep (void);
//! \defgroup lineedit_return_values Possible errors from lineedit
//! \{
static constexpr int FAILURE = -1;
static constexpr int SHUTDOWN = -2;
static constexpr int RETRY = -3;
//! \}
/// A simple line edit (raw mode) /// A simple line edit (raw mode)
int lineedit(const std::string& prompt, std::string& output, CommandHistory & history ); int lineedit(const std::string& prompt, std::string& output, CommandHistory & history );
bool isInited (void) { return inited; }; bool isInited (void) { return inited; };

@ -237,8 +237,10 @@ DFHack::command_result parseRectangle(DFHack::color_ostream & out,
str.str(""); str.str("");
str << "Set range width <" << width << "> "; str << "Set range width <" << width << "> ";
if ((rv = con.lineedit(str.str(), command, hist)) < 0) while ((rv = con.lineedit(str.str(), command, hist))
return rv == -2 ? CR_OK : CR_FAILURE; == Console::RETRY);
if (rv <= Console::FAILURE)
return rv == Console::FAILURE ? CR_FAILURE : CR_FAILURE;
hist.add(command); hist.add(command);
newWidth = command.empty() ? width : atoi(command.c_str()); newWidth = command.empty() ? width : atoi(command.c_str());
} else { } else {
@ -252,8 +254,10 @@ DFHack::command_result parseRectangle(DFHack::color_ostream & out,
str.str(""); str.str("");
str << "Set range height <" << height << "> "; str << "Set range height <" << height << "> ";
if ((rv = con.lineedit(str.str(), command, hist)) < 0) while ((rv = con.lineedit(str.str(), command, hist))
return rv == -2 ? CR_OK : CR_FAILURE; == Console::RETRY);
if (rv <= Console::FAILURE)
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
hist.add(command); hist.add(command);
newHeight = command.empty() ? height : atoi(command.c_str()); newHeight = command.empty() ? height : atoi(command.c_str());
} else { } else {
@ -267,8 +271,10 @@ DFHack::command_result parseRectangle(DFHack::color_ostream & out,
str.str(""); str.str("");
str << "Set range z-levels <" << zLevels << "> "; str << "Set range z-levels <" << zLevels << "> ";
if ((rv = con.lineedit(str.str(), command, hist)) < 0) while ((rv = con.lineedit(str.str(), command, hist))
return rv == -2 ? CR_OK : CR_FAILURE; == Console::RETRY);
if (rv <= Console::FAILURE)
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
hist.add(command); hist.add(command);
newZLevels = command.empty() ? zLevels : atoi(command.c_str()); newZLevels = command.empty() ? zLevels : atoi(command.c_str());
} else { } else {

@ -189,8 +189,10 @@ command_result df_liquids (color_ostream &out_, vector <string> & parameters)
print_prompt(str, cur_mode); print_prompt(str, cur_mode);
str << "# "; str << "# ";
int rv; int rv;
if((rv = out.lineedit(str.str(),input,liquids_hist)) < 0) while ((rv = out.lineedit(str.str(),input,liquids_hist))
return rv == -2 ? CR_OK : CR_FAILURE; == Console::RETRY);
if (rv <= Console::FAILURE)
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
liquids_hist.add(input); liquids_hist.add(input);
commands.clear(); commands.clear();

@ -140,9 +140,10 @@ command_result mode (color_ostream &out_, vector <string> & parameters)
string selected; string selected;
input_again: input_again:
CommandHistory hist; CommandHistory hist;
rv = out.lineedit("Enter new mode: ",selected, hist); while((rv = out.lineedit("Enter new mode: ",selected, hist))
if(rv < 0 || selected == "c") == Console::RETRY);
return rv == -2 ? CR_OK : CR_FAILURE; if(rv <= Console::FAILURE || selected == "c")
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
const char * start = selected.c_str(); const char * start = selected.c_str();
char * end = 0; char * end = 0;
select = strtol(start, &end, 10); select = strtol(start, &end, 10);
@ -179,14 +180,16 @@ command_result mode (color_ostream &out_, vector <string> & parameters)
{ {
CommandHistory hist; CommandHistory hist;
string selected; string selected;
rv = out.lineedit("Enter new game mode number (c for exit): ",selected, hist); while ((rv = out.lineedit("Enter new game mode number (c for exit): ",selected, hist))
if(rv < 0 || selected == "c") == Console::RETRY);
return rv == -2 ? CR_OK : CR_FAILURE; if(rv <= Console::FAILURE || selected == "c")
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
const char * start = selected.c_str(); const char * start = selected.c_str();
gm.g_mode = (GameMode) strtol(start, 0, 10); gm.g_mode = (GameMode) strtol(start, 0, 10);
rv = out.lineedit("Enter new game type number (c for exit): ",selected, hist); while((rv = out.lineedit("Enter new game type number (c for exit): ",selected, hist))
if(rv < 0 || selected == "c") == Console::RETRY);
return rv == -2 ? CR_OK : CR_FAILURE; if(rv <= Console::FAILURE || selected == "c")
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
start = selected.c_str(); start = selected.c_str();
gm.g_type = (GameType) strtol(start, 0, 10); gm.g_type = (GameType) strtol(start, 0, 10);
} }

@ -986,8 +986,10 @@ command_result df_tiletypes (color_ostream &out_, vector <string> & parameters)
std::string input = ""; std::string input = "";
int rv = 0; int rv = 0;
if ((rv = out.lineedit("tiletypes> ",input,tiletypes_hist)) < 0) while ((rv = out.lineedit("tiletypes> ",input,tiletypes_hist))
return rv == -2 ? CR_OK : CR_FAILURE; == Console::RETRY);
if (rv <= Console::FAILURE)
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
tiletypes_hist.add(input); tiletypes_hist.add(input);
commands.clear(); commands.clear();