CursesでReadlineを使う
boostを若干濫用気味にCursesのメインループを書いてみた。
さらにreadlineでのプロンプトも実験。
// g++ main.cpp -lreadline -lcurses #include <curses.h> #include <termios.h> #include <errno.h> #include <stdlib.h> #include <readline/readline.h> #include <readline/history.h> #include <string> #include <list> #include <map> #include <algorithm> #include <boost/function.hpp> #include <boost/bind.hpp> #include <boost/shared_ptr.hpp> namespace Ncurses { struct Point { int x; int y; }; struct Size { int w; int h; }; struct Rect { Point point; Size size; }; class Window { WINDOW *window_; Rect rect_; Point cursor_; public: Window(const Rect &rect) : rect_(rect) { cursor_.x=0; cursor_.y=0; window_=newwin(rect.size.h, rect.size.w , rect.point.y, rect.point.x); } void update() { wmove(window_, rect_.size.h-1, 0); wprintw(window_, "line: %d/%d - col: %d/%d" , cursor_.y+1, rect_.size.h-1 , cursor_.x+1, rect_.size.w); wclrtoeol(window_); wmove(window_, cursor_.y, cursor_.x); wrefresh(window_); } void left() { --cursor_.x; clamp_(); } void right() { ++cursor_.x; clamp_(); } void down() { ++cursor_.y; clamp_(); } void up() { --cursor_.y; clamp_(); } void head(){ cursor_.x=0; } void tail(){ cursor_.x=rect_.size.w-1; } void begin(){ cursor_.y=0; } void end(){ cursor_.y=rect_.size.h-2; } void clear() { wclear(window_); } int getChar() { return wgetch(window_); } void lastline() { wmove(window_, rect_.size.h-1, 0); wclrtoeol(window_); wrefresh(window_); } private: void clamp_(){ cursor_.x=std::max(0, std::min(cursor_.x, rect_.size.w-1)); cursor_.y=std::max(0, std::min(cursor_.y, rect_.size.h-2)); } }; typedef boost::shared_ptr<Window> WindowPtr; class App { typedef boost::function<void(void)> Action; typedef std::map<int,Action> ActionMap; ActionMap actionMap_; std::list <WindowPtr> windowStack_; bool doLoop_; public: App() : doLoop_(true) { initscr(); noecho(); cbreak(); } ~App() { endwin(); } WindowPtr createWindow(const Rect &rect){ windowStack_.push_back(WindowPtr(new Window(rect))); return windowStack_.back(); } void loop() { while(doLoop_) { windowStack_.back()->update(); int key=windowStack_.back()->getChar(); //int key=getch(); ActionMap::iterator action=actionMap_.find(key); if(action==actionMap_.end()){ // no action continue; } action->second(); } } template<typename Callback> void registAction(int key, Callback callback) { actionMap_[key]=callback; } bool promptQuit(const std::string &input) { if(input=="y" || input=="Y"){ doLoop_=false; } return true; } }; struct Readline { struct termios keep_term; Readline() { // setup term mode struct termios temp_term; errno = 0; if(tcgetattr(fileno(stdin), &keep_term) == -1){ perror("tcgetattr failure"); exit(EXIT_FAILURE); } temp_term = keep_term; temp_term.c_lflag |= ECHO; errno = 0; if(tcsetattr(fileno(stdin), TCSANOW, &temp_term) == -1){ perror("tcsetattr(temp_term) failure"); exit(EXIT_FAILURE); } } ~Readline() { // restore term mode errno = 0; if(tcsetattr(fileno(stdin), TCSANOW, &keep_term) == -1){ perror("tcsetattr(keep_term) failure"); exit(EXIT_FAILURE); } } std::string operator()(const std::string &prompt) { char *input=readline(prompt.c_str()); std::string line(input); free(input); return line; } }; struct Prompt { boost::function<void(void)> preFunc_; std::string prompt_; boost::function<void(const char *)> callback_; boost::function<void(void)> postFunc_; template<typename PreFunc, typename Callback, typename PostFunc> Prompt(PreFunc preFunc, const std::string &prompt, Callback callback, PostFunc postFunc) : preFunc_(preFunc), prompt_(prompt), callback_(callback), postFunc_(postFunc) {} void operator()() { preFunc_(); Readline rl; std::string input=rl(prompt_); callback_(input.c_str()); postFunc_(); } }; } // namespace int main() { Ncurses::App app; Ncurses::Rect rect={{0, 0}, {COLS, LINES}}; Ncurses::WindowPtr window=app.createWindow(rect); app.registAction('h', boost::bind(&Ncurses::Window::left, window)); app.registAction('j', boost::bind(&Ncurses::Window::down, window)); app.registAction('k', boost::bind(&Ncurses::Window::up, window)); app.registAction('l', boost::bind(&Ncurses::Window::right, window)); app.registAction('0', boost::bind(&Ncurses::Window::head, window)); app.registAction('$', boost::bind(&Ncurses::Window::tail, window)); app.registAction('g', boost::bind(&Ncurses::Window::begin, window)); app.registAction('G', boost::bind(&Ncurses::Window::end, window)); app.registAction('q', Ncurses::Prompt( boost::bind(&Ncurses::Window::lastline, window) , "quit?(y/n): " , boost::bind(&Ncurses::App::promptQuit, &app, _1) , boost::bind(&Ncurses::Window::clear, window) )); app.loop(); return 0; }
この上にイベントキューを実装して、
各種シグナル(SIGCHLD、SIGALRM、SIGPIPE、SIGWINCH辺り)を捌ける
仕組みを作るのが目的。