curseOutput.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. /**
  2. * curseOutput.cpp for jsonstroller
  3. *
  4. * Author: isundil <isundill@gmail.com>
  5. **/
  6. #include <iostream>
  7. #include <sys/ioctl.h>
  8. #include <unistd.h>
  9. #include <signal.h>
  10. #include <string.h>
  11. #include "curseOutput.hh"
  12. #include "jsonPrimitive.hh"
  13. #include "jsonObject.hh"
  14. #include "jsonArray.hh"
  15. static CurseOutput *runningInst = nullptr;
  16. class SelectionOutOfRange {};
  17. CurseOutput::CurseOutput(JSonElement *root, const Params &p): data(root), selection(root), params(p)
  18. { }
  19. CurseOutput::~CurseOutput()
  20. { }
  21. void CurseOutput::run()
  22. {
  23. runningInst = this;
  24. init();
  25. loop();
  26. shutdown();
  27. runningInst = nullptr;
  28. }
  29. void CurseOutput::loop()
  30. {
  31. breakLoop = false;
  32. do
  33. {
  34. while (!redraw()) //TODO opti going down
  35. ;
  36. } while(readInput());
  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. 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. static void _resizeFnc(int signo)
  60. {
  61. if (!runningInst)
  62. return;
  63. runningInst->onsig(signo);
  64. }
  65. bool CurseOutput::redraw()
  66. {
  67. std::pair<unsigned int, unsigned int> screenSize;
  68. std::pair<int, int> cursor;
  69. bool result;
  70. select_up = select_down = nullptr;
  71. selectFound = selectIsLast = selectIsFirst = false;
  72. getScreenSize(screenSize, cursor);
  73. cursor.first = cursor.second = 0;
  74. clear();
  75. try {
  76. result = redraw(cursor, screenSize, data, dynamic_cast<const JSonContainer *> (data));
  77. }
  78. catch (SelectionOutOfRange &e)
  79. {
  80. return false;
  81. }
  82. if (!result && !selectFound)
  83. {
  84. topleft++;
  85. return false;
  86. }
  87. if (!result && !select_down)
  88. selectIsLast = true;
  89. if (!select_down)
  90. {
  91. const JSonContainer *pselect = dynamic_cast<const JSonContainer*>(selection);
  92. if (pselect && !pselect->empty())
  93. select_down = *(pselect->cbegin());
  94. else
  95. {
  96. const JSonElement *next = selection->findNext();
  97. select_down = next ? next : selection;
  98. }
  99. }
  100. if (!select_up)
  101. select_up = selection;
  102. refresh();
  103. return true;
  104. }
  105. bool CurseOutput::redraw(const std::string &errorMsg)
  106. {
  107. bool result = redraw();
  108. writeBottomLine(errorMsg, OutputFlag::SPECIAL_ERROR);
  109. return result;
  110. }
  111. bool CurseOutput::redraw(std::pair<int, int> &cursor, const std::pair<unsigned int, unsigned int> &maxSize, const JSonElement *item, const JSonContainer *parent)
  112. {
  113. checkSelection(item, parent, cursor);
  114. if (dynamic_cast<const JSonContainer*>(item))
  115. {
  116. if (!writeContainer(cursor, maxSize, (const JSonContainer *) item))
  117. return false;
  118. }
  119. else
  120. {
  121. cursor.second += write(cursor.first, cursor.second, item, maxSize.first, getFlag(item));
  122. if (cursor.second - topleft > 0 && (unsigned)(cursor.second - topleft) > maxSize.second -1)
  123. return false;
  124. }
  125. return true;
  126. }
  127. bool CurseOutput::writeContainer(std::pair<int, int> &cursor, const std::pair<unsigned int, unsigned int> &maxSize, const JSonContainer *item)
  128. {
  129. char childDelimiter[2];
  130. if (dynamic_cast<const JSonObject *>(item))
  131. memcpy(childDelimiter, "{}", sizeof(*childDelimiter) * 2);
  132. else
  133. memcpy(childDelimiter, "[]", sizeof(*childDelimiter) * 2);
  134. if (collapsed.find((const JSonContainer *)item) != collapsed.end())
  135. {
  136. std::string ss;
  137. ss.append(&childDelimiter[0], 1).append(" ... ").append(&childDelimiter[1], 1);
  138. cursor.second += write(cursor.first, cursor.second, ss, maxSize.first, selection == item);
  139. }
  140. else
  141. {
  142. cursor.second += write(cursor.first, cursor.second, childDelimiter[0], maxSize.first, getFlag(item));
  143. if (cursor.second - topleft > 0 && (unsigned)(cursor.second - topleft) > maxSize.second -1)
  144. return false;
  145. if (!writeContent(cursor, maxSize, (const std::list<JSonElement *> *)item))
  146. return false;
  147. cursor.second += write(cursor.first, cursor.second, childDelimiter[1], maxSize.first, getFlag(item));
  148. }
  149. return (cursor.second - topleft < 0 || (unsigned)(cursor.second - topleft) <= maxSize.second -1);
  150. }
  151. bool CurseOutput::writeContent(std::pair<int, int> &cursor, const std::pair<unsigned int, unsigned int> &maxSize, const std::list<JSonElement*> *_item)
  152. {
  153. const JSonContainer *item = (const JSonContainer *)_item;
  154. bool containerIsObject = (dynamic_cast<const JSonObject *>(item) != nullptr);
  155. bool result = true;
  156. cursor.first += INDENT_LEVEL;
  157. for (JSonElement *i : *item)
  158. {
  159. result = false;
  160. if (containerIsObject)
  161. {
  162. JSonObjectEntry *ent = (JSonObjectEntry*) i;
  163. bool isContainer = (dynamic_cast<const JSonContainer *>(**ent) != nullptr);
  164. std::string key = ent->stringify();
  165. checkSelection(ent, (JSonContainer*) item, cursor);
  166. if (isContainer && collapsed.find((const JSonContainer*)(**ent)) != collapsed.cend())
  167. {
  168. if (dynamic_cast<const JSonObject *>(**ent))
  169. {
  170. if (!writeKey(key, "{ ... }", cursor, maxSize, getFlag(ent)))
  171. break;
  172. }
  173. else if (!writeKey(key, "[ ... ]", cursor, maxSize, getFlag(ent)) || (cursor.second - topleft > 0 && (unsigned)(cursor.second - topleft) > maxSize.second -1))
  174. break;
  175. }
  176. else if (!isContainer)
  177. {
  178. if (!writeKey(key, ((**ent)->stringify()), cursor, maxSize, getFlag(ent)) || (cursor.second - topleft > 0 && (unsigned)(cursor.second - topleft) > maxSize.second -1))
  179. break;
  180. }
  181. else if (((JSonContainer*)(**ent))->size() == 0)
  182. {
  183. if (dynamic_cast<const JSonObject *>(**ent) )
  184. {
  185. if (!writeKey(key, "{ }", cursor, maxSize, getFlag(ent)))
  186. break;
  187. }
  188. else if (!writeKey(key, "[ ]", cursor, maxSize, getFlag(ent)) || (cursor.second - topleft > 0 && (unsigned)(cursor.second - topleft) > maxSize.second -1))
  189. break;
  190. }
  191. else
  192. {
  193. if (!writeKey(key, cursor, maxSize, selection == ent))
  194. break;
  195. const JSonElement *saveSelection = selection;
  196. if (selection == ent)
  197. selection = **ent;
  198. cursor.first += INDENT_LEVEL /2;
  199. if (!redraw(cursor, maxSize, **ent, (const JSonContainer *)item))
  200. {
  201. selection = saveSelection;
  202. cursor.first -= INDENT_LEVEL /2;
  203. return false;
  204. }
  205. selection = saveSelection;
  206. cursor.first -= INDENT_LEVEL /2;
  207. }
  208. }
  209. else
  210. {
  211. if (!redraw(cursor, maxSize, i, (const JSonContainer *)item))
  212. break;
  213. }
  214. result = true;
  215. }
  216. cursor.first -= INDENT_LEVEL;
  217. //result will be false if for loop break'd at some time, true otherwise
  218. return result;
  219. }
  220. bool CurseOutput::writeKey(const std::string &key, const std::string &after, std::pair<int, int> &cursor, const std::pair<unsigned int, unsigned int> &maxSize, OutputFlag flags)
  221. {
  222. if (cursor.second - topleft < 0)
  223. {
  224. cursor.second++;
  225. return true;
  226. }
  227. if (!writeKey(key, cursor, maxSize, flags, after.size()))
  228. return false;
  229. //TODO check result if write goes to new line
  230. write(after.c_str(), maxSize.first, flags);
  231. return true;
  232. }
  233. bool CurseOutput::writeKey(const std::string &key, std::pair<int, int> &cursor, const std::pair<unsigned int, unsigned int> &maxSize, OutputFlag flags, unsigned int extraLen)
  234. {
  235. if (cursor.second - topleft < 0)
  236. {
  237. cursor.second++;
  238. return true;
  239. }
  240. char oldType = flags.type();
  241. flags.type(OutputFlag::TYPE_OBJKEY);
  242. write(cursor.first, cursor.second, key, maxSize.first -extraLen -2, flags);
  243. cursor.second ++;
  244. flags.type(OutputFlag::TYPE_OBJ);
  245. write(": ", maxSize.first, flags);
  246. flags.type(oldType);
  247. return (cursor.second - topleft < 0 || (unsigned)(cursor.second - topleft) <= maxSize.second);
  248. }
  249. unsigned int CurseOutput::write(const int &x, const int &y, const JSonElement *item, unsigned int maxWidth, OutputFlag flags)
  250. {
  251. return write(x, y, item->stringify(), maxWidth, flags);
  252. }
  253. unsigned int CurseOutput::write(const int &x, const int &y, const char item, unsigned int maxWidth, OutputFlag flags)
  254. {
  255. int offsetY = y - topleft;
  256. if (offsetY < 0)
  257. return 1;
  258. if (flags.selected())
  259. attron(A_REVERSE | A_BOLD);
  260. char color = OutputFlag::SPECIAL_NONE;
  261. if (params.colorEnabled() && search_pattern.size() == 1 && search_pattern.c_str()[0] == item)
  262. color = OutputFlag::SPECIAL_SEARCH;
  263. else if (colors.find(flags.type()) != colors.end())
  264. color = flags.type();
  265. if (color != OutputFlag::SPECIAL_NONE)
  266. attron(COLOR_PAIR(color));
  267. mvprintw(offsetY, x, "%c", item);
  268. attroff(A_REVERSE | A_BOLD);
  269. if (color != OutputFlag::SPECIAL_NONE)
  270. attroff(COLOR_PAIR(color));
  271. return getNbLines(x +1, maxWidth);
  272. }
  273. void CurseOutput::write(const std::string &str, unsigned int maxWidth, const OutputFlag flags) const
  274. {
  275. char color = OutputFlag::SPECIAL_NONE;
  276. if (params.colorEnabled() && !search_pattern.empty() && str.find(search_pattern) != str.npos)
  277. color = OutputFlag::SPECIAL_SEARCH;
  278. else if (colors.find(flags.type()) != colors.end())
  279. color = flags.type();
  280. if (color != OutputFlag::SPECIAL_NONE)
  281. attron(COLOR_PAIR(color));
  282. if (flags.selected())
  283. attron(A_REVERSE | A_BOLD);
  284. printw("%s", str.c_str());
  285. attroff(A_REVERSE | A_BOLD);
  286. if (color != OutputFlag::SPECIAL_NONE)
  287. attroff(COLOR_PAIR(color));
  288. }
  289. unsigned int CurseOutput::write(const int &x, const int &y, const std::string &str, unsigned int maxWidth, const OutputFlag flags)
  290. {
  291. int offsetY = y - topleft;
  292. if (offsetY < 0)
  293. return 1;
  294. move(offsetY, x);
  295. write(str, maxWidth, flags);
  296. return getNbLines(str.size() +x, maxWidth);
  297. }
  298. unsigned int CurseOutput::getNbLines(float nbChar, unsigned int maxWidth)
  299. {
  300. float nLine = nbChar / maxWidth;
  301. if (nLine == (unsigned int) nLine)
  302. return nLine;
  303. return nLine +1;
  304. }
  305. void CurseOutput::getScreenSize(std::pair<unsigned int, unsigned int> &screenSize) const
  306. {
  307. getmaxyx(stdscr, screenSize.second, screenSize.first);
  308. }
  309. void CurseOutput::getScreenSize(std::pair<unsigned int, unsigned int> &screenSize, std::pair<int, int> &bs) const
  310. {
  311. getScreenSize(screenSize);
  312. getbegyx(stdscr, bs.second, bs.first);
  313. }
  314. const OutputFlag CurseOutput::getFlag(const JSonElement *item) const
  315. {
  316. OutputFlag res;
  317. const JSonElement *i = dynamic_cast<const JSonObjectEntry*>(item) ? **((const JSonObjectEntry*)item) : item;
  318. res.selected(item == selection);
  319. if (dynamic_cast<const JSonPrimitive<std::string> *>(i))
  320. res.type(OutputFlag::TYPE_STRING);
  321. else if (dynamic_cast<const JSonPrimitive<bool> *>(i))
  322. res.type(OutputFlag::TYPE_BOOL);
  323. else if (dynamic_cast<const AJSonPrimitive *>(i))
  324. res.type(OutputFlag::TYPE_NUMBER);
  325. else if (dynamic_cast<const JSonObject*>(i))
  326. res.type(OutputFlag::TYPE_OBJ);
  327. else if (dynamic_cast<const JSonArray*>(i))
  328. res.type(OutputFlag::TYPE_ARR);
  329. return res;
  330. }
  331. void CurseOutput::checkSelection(const JSonElement *item, const JSonElement *parent, const std::pair<int, int> &cursor)
  332. {
  333. if (!selectFound)
  334. {
  335. if (selection == item)
  336. {
  337. if (cursor.second == topleft)
  338. selectIsFirst = true;
  339. else if (cursor.second < topleft)
  340. {
  341. topleft = cursor.second;
  342. throw SelectionOutOfRange(); //break and restart painting
  343. }
  344. selectFound = true;
  345. }
  346. else if (!item->getParent() || !dynamic_cast<const JSonObjectEntry*>(item->getParent()))
  347. select_up = item;
  348. }
  349. else if (!select_down)
  350. {
  351. const JSonElement *parent = item->getParent();
  352. if (!dynamic_cast<const JSonContainer*>(item) && parent && selection != parent && dynamic_cast<const JSonObjectEntry*>(parent))
  353. item = parent;
  354. if (!parent || !dynamic_cast<const JSonObjectEntry*>(parent))
  355. select_down = item;
  356. }
  357. }
  358. /**
  359. * Read input and expect signal
  360. * @Return true on:
  361. * - Windows resized
  362. * - Key press and need redraw
  363. * false on:
  364. * - exit signal
  365. **/
  366. bool CurseOutput::readInput()
  367. {
  368. while (!breakLoop)
  369. {
  370. int c;
  371. c = getch();
  372. switch (c)
  373. {
  374. case 'q':
  375. case 'Q':
  376. return false;
  377. case KEY_UP:
  378. case 'K':
  379. case 'k':
  380. if (selectIsFirst && topleft)
  381. topleft = std::max(topleft -3, 0);
  382. else
  383. selection = select_up;
  384. return true;
  385. case KEY_DOWN:
  386. case 'j':
  387. case 'J':
  388. if (selectIsLast)
  389. topleft += 2;
  390. else if (selection != select_down)
  391. selection = select_down;
  392. else
  393. break;
  394. return true;
  395. case KEY_PPAGE:
  396. {
  397. const JSonElement *brother = selection->findPrev();
  398. if (brother == nullptr)
  399. {
  400. const JSonElement *parent = selection->getParent();
  401. if (parent && dynamic_cast<const JSonContainer*>(parent))
  402. {
  403. selection = parent;
  404. if (selection->getParent() && dynamic_cast<const JSonObjectEntry*> (selection->getParent()))
  405. selection = selection->getParent();
  406. }
  407. else
  408. break;
  409. }
  410. else
  411. selection = brother;
  412. return true;
  413. break;
  414. }
  415. case KEY_NPAGE:
  416. {
  417. const JSonElement *brother = selection->findNext();
  418. if (brother)
  419. {
  420. selection = brother;
  421. return true;
  422. }
  423. break;
  424. }
  425. case 'l':
  426. case 'L':
  427. case KEY_RIGHT:
  428. {
  429. const JSonElement *_selection = selection;
  430. if (dynamic_cast<const JSonObjectEntry*>(selection))
  431. _selection = **((const JSonObjectEntry*)_selection);
  432. if (!dynamic_cast<const JSonContainer*>(_selection))
  433. break;
  434. if (collapsed.erase((const JSonContainer *)_selection))
  435. return true;
  436. if (!((const JSonContainer*)_selection)->size())
  437. break;
  438. selection = select_down;
  439. return true;
  440. }
  441. case 'h':
  442. case 'H':
  443. case KEY_LEFT:
  444. {
  445. const JSonElement *_selection = selection;
  446. if (dynamic_cast<const JSonObjectEntry*>(_selection))
  447. _selection = **((const JSonObjectEntry*)_selection);
  448. if (selection->getParent() && (!dynamic_cast<const JSonContainer*>(_selection)
  449. || collapsed.find((const JSonContainer *)_selection) != collapsed.end()
  450. || (dynamic_cast<const JSonContainer*>(_selection) && ((const JSonContainer*)_selection)->size() == 0)))
  451. {
  452. selection = selection->getParent();
  453. if (selection->getParent() && dynamic_cast<const JSonObjectEntry*>(selection->getParent()))
  454. selection = selection->getParent();
  455. }
  456. else if (_selection)
  457. collapsed.insert((const JSonContainer *)_selection);
  458. else
  459. break;
  460. return true;
  461. }
  462. case '/':
  463. search_pattern = search();
  464. case 'n':
  465. case 'N':
  466. jumpToNextSearch(true, true, selection);
  467. // jumpToNextSearch have its own redraw, so no need to return true here
  468. break;
  469. }
  470. }
  471. return false;
  472. }
  473. //TODO move to JSonElement ?
  474. bool CurseOutput::jumpToNextSearch(bool scanParent, bool redraw, const JSonElement *initial_selection)
  475. {
  476. bool found = false;
  477. const JSonElement *current = selection;
  478. const JSonElement *prev = nullptr;
  479. while (current && !found)
  480. {
  481. if (dynamic_cast<const JSonContainer *> (current) == nullptr)
  482. {
  483. //ObjectEntry or Primitive
  484. const JSonElement* entryChild = nullptr;
  485. char isObjectEntry = 0;
  486. if (dynamic_cast<const JSonObjectEntry *>(current) != nullptr)
  487. {
  488. entryChild = **((const JSonObjectEntry*)current);
  489. if (dynamic_cast<const JSonContainer *> (entryChild) == nullptr)
  490. isObjectEntry = 1;
  491. else
  492. isObjectEntry = 2;
  493. }
  494. const std::string str = current->stringify();
  495. std::string strEntry;
  496. if (isObjectEntry == 1)
  497. strEntry = entryChild->stringify();
  498. if (current != initial_selection && entryChild != initial_selection && current->match(search_pattern))
  499. {
  500. selection = current;
  501. found = true;
  502. break;
  503. }
  504. if (isObjectEntry == 2)
  505. {
  506. const JSonElement *_selection = selection;
  507. selection = entryChild;
  508. if (jumpToNextSearch(false, false, initial_selection))
  509. {
  510. found = true;
  511. break;
  512. }
  513. selection = _selection;
  514. }
  515. }
  516. else
  517. {
  518. //Object or array
  519. for (const JSonElement *i : *(const JSonContainer*)current)
  520. {
  521. if (prev != nullptr)
  522. {
  523. if (prev != i)
  524. continue;
  525. else
  526. {
  527. prev = nullptr;
  528. continue;
  529. }
  530. }
  531. const JSonElement *_selection = selection;
  532. selection = i;
  533. if (jumpToNextSearch(false, false, initial_selection))
  534. {
  535. found = true;
  536. break;
  537. }
  538. selection = _selection;
  539. }
  540. }
  541. if (!scanParent)
  542. break;
  543. prev = current;
  544. current = current->getParent();
  545. }
  546. bool foundAfterLoop = false;
  547. if (!found && scanParent && selection != data)
  548. {
  549. const JSonElement *_selection = selection;
  550. selection = data;
  551. if (jumpToNextSearch(false, false, initial_selection))
  552. foundAfterLoop = true;
  553. else
  554. selection = _selection;
  555. }
  556. if (!redraw)
  557. return found;
  558. if (foundAfterLoop || (!found && selection->match(search_pattern)))
  559. {
  560. this->redraw("Search hit BOTTOM, continuing at TOP");
  561. return true;
  562. }
  563. if (!found)
  564. {
  565. this->redraw("Pattern not found");
  566. return false;
  567. }
  568. unfold(selection);
  569. this->redraw();
  570. return true;
  571. }
  572. void CurseOutput::unfold(const JSonElement *item)
  573. {
  574. while (item->getParent())
  575. {
  576. collapsed.erase((const JSonContainer*)item->getParent());
  577. item = item->getParent();
  578. }
  579. }
  580. const std::string CurseOutput::search()
  581. {
  582. std::string buffer;
  583. curs_set(true);
  584. keypad(stdscr, false);
  585. wtimeout(stdscr, -1);
  586. while (true)
  587. {
  588. int c;
  589. clear();
  590. redraw();
  591. writeBottomLine('/' +buffer, OutputFlag::SPECIAL_SEARCH);
  592. refresh();
  593. c = getch();
  594. if (c == '\n')
  595. break;
  596. else if (c == '\b' || c == 127)
  597. buffer.pop_back();
  598. else
  599. buffer += c;
  600. }
  601. wtimeout(stdscr, 150);
  602. keypad(stdscr, true);
  603. curs_set(false);
  604. return buffer;
  605. }
  606. void CurseOutput::writeBottomLine(const std::string &buffer, short color) const
  607. {
  608. std::pair<unsigned int, unsigned int> screenSize;
  609. getScreenSize(screenSize);
  610. size_t bufsize = buffer.size();
  611. if (params.colorEnabled())
  612. attron(COLOR_PAIR(color));
  613. mvprintw(screenSize.second -1, 0, "%s%*c", buffer.c_str(), screenSize.first - bufsize, ' ');
  614. move(screenSize.second -1, bufsize);
  615. if (params.colorEnabled())
  616. attroff(COLOR_PAIR(color));
  617. }
  618. void CurseOutput::init()
  619. {
  620. if (!isatty(fileno(stdin)) || !isatty(fileno(stdout)))
  621. {
  622. screen_fd = fopen("/dev/tty", "r+");
  623. setbuf(screen_fd, nullptr);
  624. screen = newterm(nullptr, screen_fd, screen_fd);
  625. }
  626. else
  627. {
  628. screen = newterm(nullptr, stdout, stdin);
  629. screen_fd = nullptr;
  630. }
  631. wtimeout(stdscr, 150);
  632. cbreak();
  633. clear();
  634. noecho();
  635. curs_set(false);
  636. keypad(stdscr, true);
  637. if (params.colorEnabled())
  638. {
  639. start_color();
  640. init_pair(OutputFlag::TYPE_NUMBER, COLOR_GREEN, COLOR_BLACK);
  641. init_pair(OutputFlag::TYPE_BOOL, COLOR_RED, COLOR_BLACK);
  642. init_pair(OutputFlag::TYPE_STRING, COLOR_CYAN, COLOR_BLACK);
  643. init_pair(OutputFlag::TYPE_OBJKEY, COLOR_CYAN, COLOR_BLACK);
  644. init_pair(OutputFlag::SPECIAL_SEARCH, COLOR_WHITE, COLOR_BLUE);
  645. init_pair(OutputFlag::SPECIAL_ERROR, COLOR_WHITE, COLOR_RED);
  646. colors.insert(OutputFlag::TYPE_NUMBER);
  647. colors.insert(OutputFlag::TYPE_BOOL);
  648. colors.insert(OutputFlag::TYPE_STRING);
  649. colors.insert(OutputFlag::TYPE_OBJKEY);
  650. }
  651. signal(SIGWINCH, _resizeFnc);
  652. signal(SIGINT, _resizeFnc);
  653. signal(SIGTERM, _resizeFnc);
  654. signal(SIGKILL, _resizeFnc);
  655. topleft = 0;
  656. }
  657. void CurseOutput::shutdown()
  658. {
  659. endwin();
  660. delscreen(screen);
  661. if (screen_fd)
  662. {
  663. fclose(screen_fd);
  664. screen_fd = nullptr;
  665. }
  666. screen = nullptr;
  667. }