curseOutput.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /**
  2. * curseOutput.cpp for jsonstroller
  3. *
  4. * Author: isundil <isundill@gmail.com>
  5. **/
  6. #include <iostream>
  7. #include <sys/ioctl.h>
  8. #include <algorithm>
  9. #include <unistd.h>
  10. #include <signal.h>
  11. #include <string.h>
  12. #include "searchPattern.hh"
  13. #include "curseOutput.hh"
  14. #include "jsonPrimitive.hh"
  15. #include "jsonObject.hh"
  16. #include "jsonArray.hh"
  17. #include "except.hh"
  18. #include "streamConsumer.hh"
  19. static CurseOutput *runningInst = nullptr;
  20. CurseOutput::CurseOutput(const Params &p): params(p)
  21. {
  22. runningInst = this;
  23. }
  24. CurseOutput::~CurseOutput()
  25. {
  26. runningInst = nullptr;
  27. }
  28. void CurseOutput::loop()
  29. {
  30. inputResult read;
  31. breakLoop = false;
  32. do
  33. {
  34. while (!redraw());
  35. read = readInput();
  36. } while (read != inputResult::quit);
  37. }
  38. bool CurseOutput::onsig(int signo)
  39. {
  40. struct winsize size;
  41. switch (signo)
  42. {
  43. case SIGWINCH:
  44. if (ioctl(fileno(screen_fd ? screen_fd : stdout), TIOCGWINSZ, &size) == 0)
  45. resize_term(size.ws_row, size.ws_col);
  46. clear();
  47. while (!redraw());
  48. break;
  49. case SIGKILL:
  50. case SIGINT:
  51. case SIGTERM:
  52. breakLoop = true;
  53. break;
  54. default:
  55. return false;
  56. }
  57. return true;
  58. }
  59. void _resizeFnc(int signo)
  60. {
  61. if (!runningInst)
  62. return;
  63. runningInst->onsig(signo);
  64. }
  65. /**
  66. * Read input and expect signal
  67. * @Return true on:
  68. * - Windows resized
  69. * - Key press and need redraw
  70. * false on:
  71. * - exit signal
  72. **/
  73. inputResult CurseOutput::readInput()
  74. {
  75. while (!breakLoop)
  76. {
  77. inputResult r = evalKey(InputSequence::read());
  78. if (r != inputResult::nextInput)
  79. return r;
  80. }
  81. return inputResult::quit;
  82. }
  83. inputResult CurseOutput::evalKey(const InputSequence &c)
  84. {
  85. const std::string key = c.key();
  86. if (key == "Q")
  87. return inputResult::quit;
  88. else if (key == "K" || key == "KEY_UP")
  89. return selectUp();
  90. else if (key == "J" || key == "KEY_DOWN")
  91. return selectDown();
  92. else if (key == "KEY_PPAGE")
  93. return selectPUp();
  94. else if (key == "KEY_NPAGE")
  95. return selectPDown();
  96. else if (key == "L" || key == "KEY_RIGHT")
  97. return expandSelection();
  98. else if (key == "H" || key == "KEY_LEFT")
  99. return collapseSelection();
  100. else if (key == "/")
  101. return initSearch();
  102. else if (key == "N")
  103. return nextResult();
  104. else if (key == "^W-W")
  105. return changeWindow(1, true);
  106. else if (key == "^W-KEY_RIGHT" || key == "^W-L")
  107. return changeWindow(1, false);
  108. else if (key == "^W-KEY_LEFT" || key == "^W-H")
  109. return changeWindow(-1, false);
  110. return inputResult::nextInput;
  111. }
  112. bool CurseOutput::redraw(const std::string &errorMsg)
  113. {
  114. bool result = redraw();
  115. writeBottomLine(errorMsg, OutputFlag::SPECIAL_ERROR);
  116. return result;
  117. }
  118. bool CurseOutput::writeKey(const std::string &key, const size_t keylen, const std::string &after, std::pair<int, int> &cursor, const std::pair<unsigned int, unsigned int> &maxSize, OutputFlag flags)
  119. {
  120. return writeKey(key, keylen, after, after.size(), cursor, maxSize, flags);
  121. }
  122. unsigned int CurseOutput::write(const int &x, const int &y, JSonElement *item, unsigned int maxWidth, OutputFlag flags)
  123. {
  124. return write(x, y, item->stringify(), item->lazystrlen(), maxWidth, flags);
  125. }
  126. unsigned int CurseOutput::getNbLines(const size_t nbChar, unsigned int maxWidth)
  127. {
  128. double nLine = (double) nbChar / maxWidth;
  129. if (nLine == (unsigned int) nLine)
  130. return nLine;
  131. return nLine +1;
  132. }
  133. const std::pair<unsigned int, unsigned int> CurseOutput::getScreenSize() const
  134. {
  135. return getScreenSizeUnsafe();
  136. }
  137. const std::pair<unsigned int, unsigned int> CurseOutput::getScreenSizeUnsafe() const
  138. {
  139. std::pair<int, int> bs;
  140. std::pair<int, int> sc;
  141. getmaxyx(stdscr, sc.second, sc.first);
  142. getbegyx(stdscr, bs.second, bs.first);
  143. sc.first -= bs.first;
  144. sc.second -= bs.second;
  145. return sc;
  146. }
  147. void CurseOutput::unfold(const JSonElement *item)
  148. {
  149. while (item->getParent())
  150. {
  151. collapsed.erase((const JSonContainer*)item->getParent());
  152. item = item->getParent();
  153. }
  154. }
  155. const SearchPattern *CurseOutput::inputSearch()
  156. {
  157. std::wstring buffer;
  158. bool abort = false;
  159. curs_set(true);
  160. wtimeout(stdscr, -1);
  161. while (!abort)
  162. {
  163. int c;
  164. writeBottomLine(L'/' +buffer, OutputFlag::SPECIAL_SEARCH);
  165. refresh();
  166. c = getwchar();
  167. if (c == L'\r')
  168. break;
  169. else if (c == L'\b' || c == 127)
  170. {
  171. if (!buffer.empty())
  172. buffer.pop_back();
  173. }
  174. else if (c == 27)
  175. abort = true;
  176. else
  177. buffer += c;
  178. }
  179. wtimeout(stdscr, 150);
  180. curs_set(false);
  181. {
  182. const size_t size = buffer.size();
  183. char bytesString[size * sizeof(wchar_t)];
  184. wcstombs(&bytesString[0], buffer.c_str(), sizeof(bytesString));
  185. std::string str;
  186. if (params.isIgnoringUnicode())
  187. str = bytesString;
  188. else
  189. str = StreamConsumer::extractUnicode(bytesString);
  190. return abort ? nullptr : new SearchPattern(str);
  191. }
  192. }
  193. void CurseOutput::writeTopLine(const std::string &buffer, short color) const
  194. {
  195. const std::pair<unsigned int, unsigned int> screenSize = getScreenSize();
  196. const size_t bufsize = buffer.size();
  197. if (params.colorEnabled())
  198. attron(COLOR_PAIR(color));
  199. mvprintw(0, 0, "%s%*c", buffer.c_str(), screenSize.first - bufsize, ' ');
  200. if (params.colorEnabled())
  201. attroff(COLOR_PAIR(color));
  202. }
  203. void CurseOutput::writeBottomLine(const std::string &buffer, short color) const
  204. {
  205. const std::pair<unsigned int, unsigned int> screenSize = getScreenSizeUnsafe();
  206. const size_t bufsize = buffer.size();
  207. if (params.colorEnabled())
  208. attron(COLOR_PAIR(color));
  209. mvprintw(screenSize.second -1, 0, "%s%*c", buffer.c_str(), screenSize.first - bufsize, ' ');
  210. move(screenSize.second -1, bufsize);
  211. if (params.colorEnabled())
  212. attroff(COLOR_PAIR(color));
  213. }
  214. void CurseOutput::writeBottomLine(const std::wstring &buffer, short color) const
  215. {
  216. const std::pair<unsigned int, unsigned int> screenSize = getScreenSizeUnsafe();
  217. const size_t bufsize = buffer.size();
  218. if (params.colorEnabled())
  219. attron(COLOR_PAIR(color));
  220. mvprintw(screenSize.second -1, 0, "%S%*c", buffer.c_str(), screenSize.first - bufsize, ' ');
  221. move(screenSize.second -1, bufsize);
  222. if (params.colorEnabled())
  223. attroff(COLOR_PAIR(color));
  224. }
  225. void CurseOutput::init()
  226. {
  227. if (!isatty(fileno(stdin)) || !isatty(fileno(stdout)))
  228. {
  229. screen_fd = fopen("/dev/tty", "r+");
  230. setbuf(screen_fd, nullptr);
  231. screen = newterm(nullptr, screen_fd, screen_fd);
  232. }
  233. else
  234. {
  235. screen = newterm(nullptr, stdout, stdin);
  236. screen_fd = nullptr;
  237. }
  238. wtimeout(stdscr, 150);
  239. cbreak();
  240. clear();
  241. noecho();
  242. curs_set(false);
  243. keypad(stdscr, true);
  244. if (params.colorEnabled())
  245. {
  246. start_color();
  247. init_pair(OutputFlag::TYPE_NUMBER, COLOR_GREEN, COLOR_BLACK);
  248. init_pair(OutputFlag::TYPE_BOOL, COLOR_RED, COLOR_BLACK);
  249. init_pair(OutputFlag::TYPE_NULL, COLOR_RED, COLOR_BLACK);
  250. init_pair(OutputFlag::TYPE_STRING, COLOR_CYAN, COLOR_BLACK);
  251. init_pair(OutputFlag::TYPE_OBJKEY, COLOR_CYAN, COLOR_BLACK);
  252. init_pair(OutputFlag::SPECIAL_SEARCH, COLOR_WHITE, COLOR_BLUE);
  253. init_pair(OutputFlag::SPECIAL_ERROR, COLOR_WHITE, COLOR_RED);
  254. init_pair(OutputFlag::SPECIAL_ACTIVEINPUTNAME, COLOR_WHITE, COLOR_GREEN);
  255. init_pair(OutputFlag::SPECIAL_INPUTNAME, COLOR_BLACK, COLOR_WHITE);
  256. colors.insert(OutputFlag::TYPE_NUMBER);
  257. colors.insert(OutputFlag::TYPE_BOOL);
  258. colors.insert(OutputFlag::TYPE_STRING);
  259. colors.insert(OutputFlag::TYPE_OBJKEY);
  260. colors.insert(OutputFlag::TYPE_NULL);
  261. }
  262. signal(SIGWINCH, _resizeFnc);
  263. signal(SIGINT, _resizeFnc);
  264. signal(SIGTERM, _resizeFnc);
  265. signal(SIGKILL, _resizeFnc);
  266. }