curseOutput.cpp 8.4 KB

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