Console.cpp 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316
  1. #include "Console.h"
  2. #include <iostream>
  3. #ifndef WIN32
  4. # include <sys/ioctl.h>
  5. # include <termios.h>
  6. # include <unistd.h>
  7. #endif
  8. using namespace Framework;
  9. ConsoleCommand::ConsoleCommand(Text name)
  10. : ReferenceCounter(),
  11. name(name)
  12. {}
  13. ConsoleCommand::~ConsoleCommand() {}
  14. const Text& Framework::ConsoleCommand::getName() const
  15. {
  16. return name;
  17. }
  18. Framework::StickyConsoleContent::StickyConsoleContent()
  19. : ReferenceCounter(),
  20. length(0),
  21. content(0),
  22. color(0),
  23. backgroundColor(0),
  24. zConsoleHandler(0)
  25. {}
  26. Framework::StickyConsoleContent::~StickyConsoleContent()
  27. {
  28. delete[] content;
  29. delete[] color;
  30. delete[] backgroundColor;
  31. }
  32. int Framework::StickyConsoleContent::getLength() const
  33. {
  34. return length;
  35. }
  36. void Framework::StickyConsoleContent::setContent(
  37. int length, const char* content)
  38. {
  39. delete[] this->content;
  40. this->content = new char[length];
  41. memcpy(this->content, content, length);
  42. this->length = length;
  43. delete[] color;
  44. delete[] backgroundColor;
  45. color = 0;
  46. backgroundColor = 0;
  47. }
  48. void Framework::StickyConsoleContent::setContent(
  49. int length, const char* content, Color color)
  50. {
  51. setContent(length, content);
  52. this->color = new Color[length];
  53. for (int i = 0; i < length; i++)
  54. {
  55. this->color[i] = color;
  56. }
  57. }
  58. void Framework::StickyConsoleContent::setContent(
  59. int length, const char* content, Color color, Color backgroundColor)
  60. {
  61. setContent(length, content, color);
  62. this->backgroundColor = new Color[length];
  63. for (int i = 0; i < length; i++)
  64. {
  65. this->backgroundColor[i] = backgroundColor;
  66. }
  67. }
  68. void Framework::StickyConsoleContent::setContent(
  69. int length, const char* content, Color* color)
  70. {
  71. setContent(length, content);
  72. this->color = new Color[length];
  73. for (int i = 0; i < length; i++)
  74. {
  75. this->color[i] = color[i];
  76. }
  77. }
  78. void Framework::StickyConsoleContent::setContent(
  79. int length, const char* content, Color* color, Color* backgroundColor)
  80. {
  81. setContent(length, content, color);
  82. this->backgroundColor = new Color[length];
  83. for (int i = 0; i < length; i++)
  84. {
  85. this->backgroundColor[i] = backgroundColor[i];
  86. }
  87. }
  88. void Framework::StickyConsoleContent::repaceContent(
  89. int start, int length, int newLength, const char* newContent)
  90. {
  91. int resultLength = this->length + newLength - length;
  92. char* resultContent = new char[resultLength];
  93. memcpy(resultContent, content, start);
  94. memcpy(resultContent + start, newContent, newLength);
  95. memcpy(resultContent + start + newLength,
  96. content + start + length,
  97. this->length - start - length);
  98. delete[] content;
  99. content = resultContent;
  100. if (color)
  101. {
  102. Color* resultColor = new Color[resultLength];
  103. memcpy(resultColor, color, start);
  104. for (int i = 0; i < newLength; i++)
  105. {
  106. resultColor[start + i] = color[start];
  107. }
  108. memcpy(resultColor + start + newLength,
  109. color + start + length,
  110. sizeof(Color) * (this->length - start - length));
  111. delete[] color;
  112. color = resultColor;
  113. }
  114. if (backgroundColor)
  115. {
  116. Color* resultBgColor = new Color[resultLength];
  117. memcpy(resultBgColor, backgroundColor, start);
  118. for (int i = 0; i < newLength; i++)
  119. {
  120. resultBgColor[start + i] = backgroundColor[start];
  121. }
  122. memcpy(resultBgColor + start + newLength,
  123. backgroundColor + start + length,
  124. sizeof(Color) * (this->length - start - length));
  125. delete[] backgroundColor;
  126. backgroundColor = resultBgColor;
  127. }
  128. this->length = resultLength;
  129. }
  130. void Framework::StickyConsoleContent::repaceContent(
  131. int start, int length, int newLength, const char* newContent, Color color)
  132. {
  133. repaceContent(start, length, newLength, newContent);
  134. if (!this->color)
  135. {
  136. this->color = new Color[this->length];
  137. for (int i = 0; i < this->length; i++)
  138. {
  139. this->color[i] = (Color)-1;
  140. }
  141. }
  142. for (int i = start; i < start + newLength; i++)
  143. {
  144. this->color[i] = color;
  145. }
  146. }
  147. void Framework::StickyConsoleContent::repaceContent(int start,
  148. int length,
  149. int newLength,
  150. const char* newContent,
  151. Color color,
  152. Color backgroundColor)
  153. {
  154. repaceContent(start, length, newLength, newContent, color);
  155. if (!this->backgroundColor)
  156. {
  157. this->backgroundColor = new Color[this->length];
  158. for (int i = 0; i < this->length; i++)
  159. {
  160. this->backgroundColor[i] = (Color)-1;
  161. }
  162. }
  163. for (int i = start; i < start + newLength; i++)
  164. {
  165. this->backgroundColor[i] = backgroundColor;
  166. }
  167. }
  168. void Framework::StickyConsoleContent::repaceContent(
  169. int start, int length, int newLength, const char* newContent, Color* color)
  170. {
  171. repaceContent(start, length, newLength, newContent);
  172. if (!this->color)
  173. {
  174. this->color = new Color[this->length];
  175. for (int i = 0; i < this->length; i++)
  176. {
  177. this->color[i] = (Color)-1;
  178. }
  179. }
  180. memcpy(this->color + start, color, sizeof(Color) * newLength);
  181. }
  182. void Framework::StickyConsoleContent::repaceContent(int start,
  183. int length,
  184. int newLength,
  185. const char* newContent,
  186. Color* color,
  187. Color* backgroundColor)
  188. {
  189. repaceContent(start, length, newLength, newContent, color);
  190. if (!this->backgroundColor)
  191. {
  192. this->backgroundColor = new Color[this->length];
  193. for (int i = 0; i < this->length; i++)
  194. {
  195. this->backgroundColor[i] = (Color)-1;
  196. }
  197. }
  198. memcpy(this->backgroundColor + start,
  199. backgroundColor,
  200. sizeof(Color) * newLength);
  201. }
  202. void Framework::StickyConsoleContent::triggerUpdate()
  203. {
  204. if (zConsoleHandler != 0)
  205. {
  206. zConsoleHandler->print();
  207. }
  208. }
  209. bool Framework::StickyConsoleContent::isInput()
  210. {
  211. return false;
  212. }
  213. void Framework::StickyConsoleContent::setConsoleHandlerZ(
  214. ConsoleHandler* zConsoleHandler)
  215. {
  216. this->zConsoleHandler = zConsoleHandler;
  217. }
  218. void Framework::StickyConsoleContent::setCursorToBeginning() {}
  219. int Framework::StickyConsoleContent::print() const
  220. {
  221. int lineCount = 0;
  222. int maxWidth = zConsoleHandler->getWidth();
  223. std::cout << "\r\33[0K"; // erase current line
  224. int lineLength = 0;
  225. int lastColor = -1;
  226. int lastBgColor = -1;
  227. for (int i = 0; i < length; i++)
  228. {
  229. if (color && (int)color[i] != lastColor)
  230. {
  231. if ((int)color[i] == -1)
  232. {
  233. std::cout << "\033[0m";
  234. if (backgroundColor && (int)backgroundColor[i] != -1)
  235. {
  236. if (backgroundColor[i] < Color::LIGHT_BLACK)
  237. {
  238. std::cout << Text("\033[1;")
  239. + Text(40 + (int)backgroundColor[i])
  240. + "m";
  241. }
  242. else
  243. {
  244. std::cout
  245. << Text("\033[1;")
  246. + Text(100 + (int)backgroundColor[i] - 8)
  247. + "m";
  248. }
  249. }
  250. }
  251. else if (color[i] < Color::LIGHT_BLACK)
  252. {
  253. std::cout << Text("\033[1;") + Text(30 + (int)color[i]) + "m";
  254. }
  255. else
  256. {
  257. std::cout << Text("\033[1;") + Text(90 + (int)color[i] - 8)
  258. + "m";
  259. }
  260. }
  261. if (backgroundColor && (int)backgroundColor[i] != lastBgColor)
  262. {
  263. if ((int)backgroundColor[i] == -1)
  264. {
  265. std::cout << "\033[0m";
  266. if (color && (int)color[i] != -1)
  267. {
  268. if (color[i] < Color::LIGHT_BLACK)
  269. {
  270. std::cout
  271. << Text("\033[1;") + Text(30 + (int)color[i]) + "m";
  272. }
  273. else
  274. {
  275. std::cout << Text("\033[1;")
  276. + Text(90 + (int)color[i] - 8) + "m";
  277. }
  278. }
  279. }
  280. else if (backgroundColor[i] < Color::LIGHT_BLACK)
  281. {
  282. std::cout << Text("\033[1;")
  283. + Text(40 + (int)backgroundColor[i]) + "m";
  284. }
  285. else
  286. {
  287. std::cout << Text("\033[1;")
  288. + Text(100 + (int)backgroundColor[i] - 8)
  289. + "m";
  290. }
  291. }
  292. std::cout << content[i];
  293. lastColor = color ? (int)color[i] : -1;
  294. lastBgColor = backgroundColor ? (int)backgroundColor[i] : -1;
  295. if ((content[i] == '\n' || lineLength == maxWidth) && i < length - 1)
  296. {
  297. if (lineLength == maxWidth && content[i] != '\n')
  298. {
  299. std::cout << "\n";
  300. }
  301. lineLength = 0;
  302. lineCount++;
  303. }
  304. else
  305. {
  306. lineLength++;
  307. }
  308. }
  309. if (lastColor != -1 || lastBgColor != -1) std::cout << "\033[0m";
  310. if (lineCount == 0 && lineLength > 0)
  311. {
  312. lineCount++;
  313. }
  314. return lineCount;
  315. }
  316. void Framework::StickyConsoleContent::restoreCursorPos() {}
  317. ConsoleHandler* Framework::StickyConsoleContent::zConsoleHandlerRef() const
  318. {
  319. return zConsoleHandler;
  320. }
  321. Framework::ConsoleProgressBar::ConsoleProgressBar()
  322. : StickyConsoleContent(),
  323. progress(0),
  324. maxProgress(1),
  325. maxWidth(-1)
  326. {}
  327. void Framework::ConsoleProgressBar::setMaxWidth(int maxWidth)
  328. {
  329. this->maxWidth = maxWidth;
  330. }
  331. void Framework::ConsoleProgressBar::setProgress(int progress)
  332. {
  333. if (progress < 0) progress = 0;
  334. this->progress = progress;
  335. }
  336. void Framework::ConsoleProgressBar::setMaxProgress(int maxProgress)
  337. {
  338. if (maxProgress < 0) maxProgress = 0;
  339. this->maxProgress = maxProgress;
  340. }
  341. int Framework::ConsoleProgressBar::getProgress() const
  342. {
  343. return progress;
  344. }
  345. int Framework::ConsoleProgressBar::print() const
  346. {
  347. int maxWidth = zConsoleHandlerRef()->getWidth();
  348. int width = this->maxWidth == -1 ? maxWidth : this->maxWidth;
  349. if (width > maxWidth)
  350. {
  351. width = maxWidth;
  352. }
  353. if (width < 7)
  354. {
  355. return 0;
  356. }
  357. std::cout << "\r\33[0K"; // erase current line
  358. int progress = this->progress > maxProgress ? maxProgress : this->progress;
  359. int progressChars = width - 7;
  360. std::cout << "[\033[1;47m";
  361. int progressWidth = (int)(((double)progress / maxProgress) * progressChars);
  362. if (progressWidth > progressChars)
  363. {
  364. progressWidth = progressChars;
  365. }
  366. for (int i = 0; i < progressWidth; i++)
  367. {
  368. std::cout << " ";
  369. }
  370. std::cout << "\033[0m";
  371. for (int i = 0; i < progressChars - progressWidth; i++)
  372. {
  373. std::cout << " ";
  374. }
  375. std::cout << "] ";
  376. Text str((int)(((double)progress / maxProgress) * 100));
  377. for (int i = 0; i < 3 - str.getLength(); i++)
  378. {
  379. std::cout << " ";
  380. }
  381. std::cout << str.getText() << "%";
  382. return 1;
  383. }
  384. Framework::InputLine::InputLine()
  385. : StickyConsoleContent(),
  386. Thread(),
  387. cursorPos(0),
  388. input(""),
  389. suggestions(0),
  390. closed(false)
  391. {}
  392. Framework::InputLine::~InputLine()
  393. {
  394. if (suggestions) suggestions->release();
  395. }
  396. void Framework::InputLine::addPossibleCommand(ConsoleCommand* command)
  397. {
  398. commands.add(command);
  399. }
  400. bool Framework::InputLine::isInput()
  401. {
  402. return true;
  403. }
  404. void Framework::InputLine::close()
  405. {
  406. closed = true;
  407. }
  408. void Framework::InputLine::setCursorToBeginning()
  409. {
  410. cs.lock();
  411. std::cout << "\r";
  412. }
  413. int Framework::InputLine::print() const
  414. {
  415. std::cout << "\33[0K" // clear current line
  416. << input.getText();
  417. if (suggestions)
  418. {
  419. std::cout << "\n";
  420. return 1 + dynamic_cast<StickyConsoleContent*>(suggestions)->print();
  421. }
  422. return 1;
  423. }
  424. void Framework::InputLine::restoreCursorPos()
  425. {
  426. if (cursorPos > 0)
  427. {
  428. std::cout << Text("\33[") + Text(cursorPos) + "C";
  429. }
  430. cs.unlock();
  431. }
  432. void Framework::InputLine::thread()
  433. {
  434. #ifdef WIN32
  435. INPUT_RECORD inputRecord;
  436. DWORD eventsRead;
  437. HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
  438. while (!closed)
  439. {
  440. if (ReadConsoleInput(handle, &inputRecord, 1, &eventsRead))
  441. {
  442. cs.lock();
  443. if (eventsRead > 0 && inputRecord.EventType == KEY_EVENT
  444. && inputRecord.Event.KeyEvent.bKeyDown)
  445. {
  446. if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_LEFT)
  447. {
  448. cursorPos--;
  449. if (cursorPos < 0)
  450. {
  451. cursorPos = 0;
  452. }
  453. else
  454. {
  455. std::cout << "\33[1D" << std::flush;
  456. }
  457. }
  458. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_HOME)
  459. { // Pos1 key moves cursor to beginning of line
  460. std::cout << "\r" << std::flush;
  461. cursorPos = 0;
  462. }
  463. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_RIGHT)
  464. {
  465. cursorPos++;
  466. if (cursorPos > input.getLength())
  467. {
  468. cursorPos = input.getLength();
  469. }
  470. else
  471. {
  472. std::cout << "\33[1C" << std::flush;
  473. }
  474. }
  475. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_END)
  476. { // Pos1 key moves cursor to beginning of line
  477. cursorPos = input.getLength();
  478. std::cout << Text("\r\33[") + Text(cursorPos) + "C"
  479. << std::flush;
  480. }
  481. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_TAB)
  482. {
  483. applyAutocompletion();
  484. }
  485. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode
  486. == VK_RETURN)
  487. {
  488. std::cout << "\r\33[0K" << std::flush; // eraze line
  489. Text cmd = input;
  490. cs.unlock();
  491. if (executeCommand(cmd))
  492. {
  493. cs.lock();
  494. input = "";
  495. cursorPos = 0;
  496. cs.unlock();
  497. triggerUpdate();
  498. }
  499. continue; // skip the unlock
  500. }
  501. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_BACK)
  502. {
  503. if (cursorPos > 0)
  504. {
  505. input.remove(cursorPos - 1, cursorPos);
  506. cursorPos--;
  507. std::cout << "\r\33[0K" // eraze line
  508. << input.getText() // output current input
  509. // move cursor to current position
  510. << Text("\r\33[") + Text(cursorPos) + "C"
  511. << std::flush;
  512. }
  513. }
  514. else if (inputRecord.Event.KeyEvent.uChar.AsciiChar)
  515. {
  516. input.insert(
  517. cursorPos, inputRecord.Event.KeyEvent.uChar.AsciiChar);
  518. cursorPos++;
  519. std::cout << "\r\33[0K" // eraze line
  520. << input.getText() // output current input
  521. // move cursor to current position
  522. << Text("\r\33[") + Text(cursorPos) + "C"
  523. << std::flush;
  524. }
  525. }
  526. cs.unlock();
  527. }
  528. }
  529. #else
  530. char c;
  531. while (!closed && read(STDIN_FILENO, &c, 1) == 1)
  532. {
  533. if (c == 27)
  534. { // Check for the escape key (27 is the ASCII code for escape)
  535. char seq[2];
  536. if (read(STDIN_FILENO, &seq, 2) == 2)
  537. {
  538. cs.lock();
  539. if (seq[0] == '[')
  540. {
  541. if (seq[1] == 'D')
  542. { // left arrow key
  543. cursorPos--;
  544. if (cursorPos < 0)
  545. {
  546. cursorPos = 0;
  547. }
  548. else
  549. {
  550. std::cout << "\33[1D" << std::flush;
  551. }
  552. }
  553. else if (seq[1] == 'H')
  554. { // Pos1 key moves cursor to beginning of line
  555. std::cout << "\r" << std::flush;
  556. cursorPos = 0;
  557. }
  558. else if (seq[1] == 'C')
  559. { // right arrow key
  560. cursorPos++;
  561. if (cursorPos > input.getLength())
  562. {
  563. cursorPos = input.getLength();
  564. }
  565. else
  566. {
  567. std::cout << "\33[1C" << std::flush;
  568. }
  569. }
  570. else if (seq[1] == 'F')
  571. { // End key moves cursor to end of line
  572. cursorPos = input.getLength();
  573. std::cout << Text("\r\33[") + Text(cursorPos) + "C"
  574. << std::flush;
  575. }
  576. }
  577. cs.unlock();
  578. }
  579. }
  580. else if (c == 8)
  581. { // backspace
  582. if (cursorPos > 0)
  583. {
  584. cs.lock();
  585. input.remove(cursorPos - 1, cursorPos);
  586. cursorPos--;
  587. std::cout << "\r\33[0K" // eraze line
  588. << input.getText() // output current input
  589. // move cursor to current position
  590. << Text("\r\33[") + Text(cursorPos) + "C"
  591. << std::flush;
  592. cs.unlock();
  593. }
  594. }
  595. else if (c == 9)
  596. { // tab
  597. cs.lock();
  598. applyAutocompletion();
  599. cs.unlock();
  600. }
  601. else if (c == 13)
  602. { // enter
  603. cs.lock();
  604. std::cout << "\r\33[0K" << std::flush; // eraze line
  605. Text cmd = input;
  606. cs.unlock();
  607. if (executeCommand(cmd))
  608. {
  609. cs.lock();
  610. input = "";
  611. cursorPos = 0;
  612. cs.unlock();
  613. }
  614. }
  615. else
  616. {
  617. cs.lock();
  618. input.insert(cursorPos, c);
  619. cursorPos++;
  620. std::cout << "\r\33[0K" // eraze line
  621. << input.getText() // output current input
  622. // move cursor to current position
  623. << Text("\r\33[") + Text(cursorPos) + "C" << std::flush;
  624. cs.unlock();
  625. }
  626. }
  627. #endif
  628. }
  629. void Framework::InputLine::applyAutocompletion()
  630. {
  631. RCArray<Text> params;
  632. bool lastArgFinished = 0;
  633. Text* cmd = input.getTeilText(0, cursorPos);
  634. parseCommand(*cmd, params, lastArgFinished);
  635. cmd->release();
  636. bool paramsStarted = params.getEntryCount() > 1 || lastArgFinished;
  637. Text* name;
  638. if (params.getEntryCount() > 0)
  639. {
  640. name = params.get(0);
  641. params.remove(0);
  642. }
  643. else
  644. {
  645. name = new Text("");
  646. }
  647. RCArray<Text> suggestions;
  648. for (ConsoleCommand* command : commands)
  649. {
  650. if ((!paramsStarted && command->getName().hasAt(0, *name))
  651. || (paramsStarted && command->getName().isEqual(*name)))
  652. {
  653. if (paramsStarted)
  654. {
  655. command->addAutocompletePossibilities(
  656. params, !lastArgFinished, suggestions);
  657. break;
  658. }
  659. else
  660. {
  661. suggestions.add(new Text(command->getName()));
  662. }
  663. }
  664. }
  665. if (this->suggestions)
  666. {
  667. this->suggestions->clear();
  668. }
  669. else
  670. {
  671. this->suggestions = new ConsoleListView();
  672. this->suggestions->setConsoleHandlerZ(zConsoleHandlerRef());
  673. }
  674. for (Text* suggestion : suggestions)
  675. {
  676. this->suggestions->addItem(*suggestion);
  677. }
  678. if (this->suggestions->getItems().getEntryCount() > 0)
  679. {
  680. bool commonChar = true;
  681. for (int i
  682. = lastArgFinished
  683. ? 0
  684. : (params.getEntryCount() > 0
  685. ? params.z(params.getEntryCount() - 1)->getLength()
  686. : name->getLength());
  687. true;
  688. i++)
  689. {
  690. if (i >= this->suggestions->getItems().z(0)->getLength())
  691. {
  692. commonChar = false;
  693. break;
  694. }
  695. for (int j = 1; j < this->suggestions->getItems().getEntryCount();
  696. j++)
  697. {
  698. if (i >= this->suggestions->getItems().z(j)->getLength())
  699. {
  700. commonChar = false;
  701. break;
  702. }
  703. if (this->suggestions->getItems().z(j)->getText()[i]
  704. != this->suggestions->getItems().z(0)->getText()[i])
  705. {
  706. commonChar = false;
  707. break;
  708. }
  709. }
  710. if (!commonChar)
  711. {
  712. break;
  713. }
  714. input.insert(
  715. cursorPos, this->suggestions->getItems().z(0)->getText()[i]);
  716. cursorPos++;
  717. }
  718. }
  719. name->release();
  720. if (this->suggestions->getItems().getEntryCount() <= 1)
  721. {
  722. this->suggestions->release();
  723. this->suggestions = 0;
  724. }
  725. triggerUpdate();
  726. }
  727. bool Framework::InputLine::executeCommand(Text command)
  728. {
  729. RCArray<Text> params;
  730. bool lastArgFinished = 0;
  731. Text* cmd = input.getTeilText(0, cursorPos);
  732. if (!parseCommand(*cmd, params, lastArgFinished))
  733. {
  734. cmd->release();
  735. return false;
  736. }
  737. cmd->release();
  738. Text* name;
  739. if (params.getEntryCount() > 0)
  740. {
  741. name = params.get(0);
  742. params.remove(0);
  743. }
  744. else
  745. {
  746. name = new Text("");
  747. }
  748. RCArray<Text> suggestions;
  749. for (ConsoleCommand* command : commands)
  750. {
  751. if (command->getName().isEqual(*name))
  752. {
  753. name->release();
  754. return command->execute(params);
  755. }
  756. }
  757. name->release();
  758. return false;
  759. }
  760. bool Framework::InputLine::parseCommand(
  761. Text command, RCArray<Text>& split, bool& lastFinished)
  762. {
  763. const char* cmd = command.getText();
  764. Text str;
  765. bool insideString = false;
  766. bool insideString2 = false;
  767. bool lastEscape = false;
  768. lastFinished = false;
  769. while (*cmd)
  770. {
  771. if (*cmd == ' ' && !insideString && !insideString2 && !lastEscape)
  772. {
  773. if (str.getLength() > 0)
  774. {
  775. split.add(new Text(str));
  776. str = "";
  777. lastFinished = true;
  778. }
  779. }
  780. else if (*cmd == '"' && !insideString2 && !lastEscape)
  781. {
  782. insideString = !insideString;
  783. lastFinished = !insideString;
  784. }
  785. else if (*cmd == '\'' && !insideString && !lastEscape)
  786. {
  787. insideString2 = !insideString2;
  788. lastFinished = !insideString2;
  789. }
  790. else if (*cmd == '\\' && !lastEscape)
  791. {
  792. lastEscape = true;
  793. lastFinished = false;
  794. }
  795. else
  796. {
  797. lastEscape = false;
  798. str.append(*cmd);
  799. lastFinished = false;
  800. }
  801. cmd++;
  802. }
  803. if (str.getLength() > 0)
  804. {
  805. split.add(new Text(str));
  806. }
  807. return !insideString && !insideString2 && !lastEscape;
  808. }
  809. Framework::ConsoleListView::ConsoleListView()
  810. : StickyConsoleContent(),
  811. maxColumns(-1),
  812. maxVisibleLines(5),
  813. lineOffset(0)
  814. {}
  815. int Framework::ConsoleListView::getUsedColumns() const
  816. {
  817. if (!zConsoleHandlerRef()) return 0;
  818. int maxWidth = zConsoleHandlerRef()->getWidth();
  819. int columnSize = 0;
  820. for (Text* item : items)
  821. {
  822. if (item->getLength() > columnSize)
  823. {
  824. columnSize = item->getLength();
  825. }
  826. }
  827. columnSize += 4;
  828. if (maxWidth < columnSize)
  829. {
  830. return 0;
  831. }
  832. int columns = maxWidth / columnSize;
  833. if (maxColumns > 0 && columns > maxColumns)
  834. {
  835. columns = maxColumns;
  836. }
  837. if (columns > items.getEntryCount())
  838. {
  839. columns = items.getEntryCount();
  840. }
  841. return columns;
  842. }
  843. int Framework::ConsoleListView::getNeededLines() const
  844. {
  845. int columns = getUsedColumns();
  846. if (!columns)
  847. {
  848. return 0;
  849. }
  850. if (maxColumns > 0 && columns > maxColumns)
  851. {
  852. columns = maxColumns;
  853. }
  854. int lines = items.getEntryCount() / columns;
  855. if (items.getEntryCount() % columns != 0)
  856. {
  857. lines++;
  858. }
  859. return lines;
  860. }
  861. void Framework::ConsoleListView::setMaxVisibleLines(int maxVisibleLines)
  862. {
  863. this->maxVisibleLines = maxVisibleLines;
  864. }
  865. void Framework::ConsoleListView::setLineOffset(int lineOffset)
  866. {
  867. this->lineOffset = lineOffset;
  868. int maxLines = getNeededLines();
  869. if (this->lineOffset > maxLines - maxVisibleLines)
  870. {
  871. this->lineOffset = maxLines - maxVisibleLines;
  872. }
  873. if (this->lineOffset < 0)
  874. {
  875. this->lineOffset = 0;
  876. }
  877. }
  878. void Framework::ConsoleListView::setMaxColumns(int maxColumns)
  879. {
  880. this->maxColumns = maxColumns;
  881. }
  882. void Framework::ConsoleListView::addItem(Text item)
  883. {
  884. item.replace("\n", " ");
  885. int index = 0;
  886. for (Text* curreent : items)
  887. {
  888. int i = 0;
  889. while (i < curreent->getLength() && i < item.getLength())
  890. {
  891. if (curreent->getText()[i] != item.getText()[i])
  892. {
  893. break;
  894. }
  895. i++;
  896. }
  897. if (i == curreent->getLength() && i == item.getLength())
  898. {
  899. return; // item already exists
  900. }
  901. if (i < curreent->getLength() && i == item.getLength())
  902. {
  903. items.add(new Text(item), index);
  904. return;
  905. }
  906. if (i < curreent->getLength() && i < item.getLength()
  907. && curreent->getText()[i] > item.getText()[i])
  908. {
  909. items.add(new Text(item), index);
  910. return;
  911. }
  912. index++;
  913. }
  914. items.add(new Text(item));
  915. }
  916. void Framework::ConsoleListView::clear()
  917. {
  918. items.clear();
  919. }
  920. const RCArray<Text>& Framework::ConsoleListView::getItems() const
  921. {
  922. return items;
  923. }
  924. int Framework::ConsoleListView::print() const
  925. {
  926. int lines = lineOffset;
  927. int maxLines = getNeededLines();
  928. if (lines > maxLines - maxVisibleLines)
  929. {
  930. lines = maxLines - maxVisibleLines;
  931. }
  932. if (lines < 0)
  933. {
  934. lines = 0;
  935. }
  936. int columns = getUsedColumns();
  937. if (!columns)
  938. {
  939. return 0;
  940. }
  941. int columnSize = zConsoleHandlerRef()->getWidth() / columns;
  942. int printetLines = 0;
  943. int currentColumn = 0;
  944. std::cout << "\33[0K"; // clear current line
  945. for (int i = columns * lines; i < items.getEntryCount(); i++)
  946. {
  947. if (i >= columns * (lines + maxVisibleLines))
  948. {
  949. break;
  950. }
  951. if (currentColumn >= columns)
  952. {
  953. std::cout << "\n\r\33[0K";
  954. printetLines++;
  955. currentColumn++;
  956. }
  957. if (!printetLines)
  958. {
  959. printetLines++;
  960. }
  961. std::cout << items.z(i)->getText();
  962. for (int j = items.z(i)->getLength(); j < columnSize; j++)
  963. {
  964. std::cout << " ";
  965. }
  966. currentColumn++;
  967. }
  968. return printetLines;
  969. }
  970. Framework::ConsoleHandler::ConsoleHandler()
  971. : ReferenceCounter(),
  972. lines(0),
  973. lineCounts(0),
  974. contentCount(0)
  975. {
  976. #ifdef WIN32
  977. hConsole = GetStdHandle(STD_INPUT_HANDLE);
  978. SetConsoleMode(hConsole, ENABLE_PROCESSED_INPUT); // set raw mode
  979. hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  980. #else
  981. struct termios t;
  982. tcgetattr(STDIN_FILENO, &t);
  983. t.c_lflag &= ~(ICANON | ECHO); // set raw mode
  984. tcsetattr(STDIN_FILENO, TCSANOW, &t);
  985. #endif
  986. }
  987. Framework::ConsoleHandler::~ConsoleHandler()
  988. {
  989. for (int i = 0; i < contentCount; i++)
  990. {
  991. lines[i]->release();
  992. }
  993. delete[] lines;
  994. delete[] lineCounts;
  995. }
  996. void Framework::ConsoleHandler::addContent(
  997. StickyConsoleContent* content, ConsoleContentPosition pos)
  998. {
  999. cs.lock();
  1000. if (content->isInput())
  1001. {
  1002. for (int i = 0; i < contentCount; i++)
  1003. {
  1004. if (lines[i]->isInput())
  1005. {
  1006. content->release();
  1007. cs.unlock();
  1008. throw "There can only be one input line";
  1009. }
  1010. }
  1011. }
  1012. if (content->zConsoleHandler)
  1013. {
  1014. cs.unlock();
  1015. throw "A console content can only be added to one console handler "
  1016. "at "
  1017. "the same time.";
  1018. }
  1019. content->zConsoleHandler = this;
  1020. contentCount++;
  1021. int* lineCounts = new int[contentCount];
  1022. StickyConsoleContent** lines = new StickyConsoleContent*[contentCount];
  1023. if (pos == ConsoleContentPosition::Top)
  1024. {
  1025. lines[0] = content;
  1026. lineCounts[0] = 0;
  1027. for (int i = 1; i < contentCount; i++)
  1028. {
  1029. lines[i] = this->lines[i - 1];
  1030. lineCounts[i] = this->lineCounts[i - 1];
  1031. }
  1032. }
  1033. else
  1034. {
  1035. lines[contentCount - 1] = content;
  1036. lineCounts[contentCount - 1] = content->print();
  1037. for (int i = 0; i < contentCount - 1; i++)
  1038. {
  1039. lines[i] = this->lines[i];
  1040. lineCounts[i] = this->lineCounts[i];
  1041. }
  1042. }
  1043. delete[] this->lines;
  1044. delete[] this->lineCounts;
  1045. this->lines = lines;
  1046. this->lineCounts = lineCounts;
  1047. InputLine* input = dynamic_cast<InputLine*>(content);
  1048. if (input)
  1049. {
  1050. input->start();
  1051. }
  1052. cs.unlock();
  1053. }
  1054. void Framework::ConsoleHandler::removeContent(StickyConsoleContent* zContent)
  1055. {
  1056. cs.lock();
  1057. int index = -1;
  1058. for (int i = 0; i < contentCount; i++)
  1059. {
  1060. if (lines[i] == zContent)
  1061. {
  1062. index = i;
  1063. break;
  1064. }
  1065. }
  1066. if (index == -1)
  1067. {
  1068. cs.unlock();
  1069. return;
  1070. }
  1071. zContent->zConsoleHandler = 0;
  1072. contentCount--;
  1073. int* lineCounts = new int[contentCount];
  1074. StickyConsoleContent** lines = new StickyConsoleContent*[contentCount];
  1075. for (int i = 0; i < index; i++)
  1076. {
  1077. lines[i] = this->lines[i];
  1078. lineCounts[i] = this->lineCounts[i];
  1079. }
  1080. for (int i = index; i < contentCount; i++)
  1081. {
  1082. lines[i] = this->lines[i + 1];
  1083. lineCounts[i] = this->lineCounts[i + 1];
  1084. }
  1085. delete[] this->lines;
  1086. delete[] this->lineCounts;
  1087. this->lines = lines;
  1088. this->lineCounts = lineCounts;
  1089. if (zContent->isInput())
  1090. {
  1091. InputLine* input = dynamic_cast<InputLine*>(zContent);
  1092. if (input)
  1093. {
  1094. input->start();
  1095. }
  1096. }
  1097. zContent->release();
  1098. cs.unlock();
  1099. }
  1100. int Framework::ConsoleHandler::getWidth() const
  1101. {
  1102. #ifdef WIN32
  1103. CONSOLE_SCREEN_BUFFER_INFO csbi;
  1104. GetConsoleScreenBufferInfo(hConsole, &csbi);
  1105. return csbi.dwSize.X;
  1106. #else
  1107. struct winsize ws;
  1108. if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
  1109. {
  1110. return 0;
  1111. }
  1112. return ws.ws_col;
  1113. #endif
  1114. }
  1115. int Framework::ConsoleHandler::getHeight() const
  1116. {
  1117. #ifdef WIN32
  1118. CONSOLE_SCREEN_BUFFER_INFO csbi;
  1119. GetConsoleScreenBufferInfo(hConsole, &csbi);
  1120. return csbi.dwSize.Y;
  1121. #else
  1122. struct winsize ws;
  1123. if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
  1124. {
  1125. return 0;
  1126. }
  1127. return ws.ws_row;
  1128. #endif
  1129. }
  1130. void Framework::ConsoleHandler::clear()
  1131. {
  1132. cs.lock();
  1133. for (int i = 0; i < contentCount; i++)
  1134. {
  1135. lines[i]->release();
  1136. }
  1137. delete[] lines;
  1138. delete[] lineCounts;
  1139. lines = 0;
  1140. lineCounts = 0;
  1141. contentCount = 0;
  1142. cs.unlock();
  1143. }
  1144. void Framework::ConsoleHandler::print()
  1145. {
  1146. cs.lock();
  1147. int totalLines = 0;
  1148. bool adittionalLine = 0;
  1149. for (int i = 0; i < contentCount; i++)
  1150. {
  1151. if (lines[i]->isInput())
  1152. {
  1153. lines[i]->setCursorToBeginning();
  1154. break;
  1155. }
  1156. totalLines += lineCounts[i];
  1157. }
  1158. if (totalLines > 0)
  1159. {
  1160. std::cout << "\33[" << totalLines << "A";
  1161. }
  1162. totalLines = 0;
  1163. for (int i = 0; i < contentCount; i++)
  1164. {
  1165. lineCounts[i] = lines[i]->print();
  1166. if (lineCounts[i] > 0)
  1167. {
  1168. adittionalLine = 0;
  1169. if (i < contentCount - 1)
  1170. {
  1171. std::cout << "\n";
  1172. adittionalLine = 1;
  1173. }
  1174. }
  1175. totalLines += lineCounts[i];
  1176. }
  1177. if (adittionalLine)
  1178. {
  1179. totalLines++;
  1180. }
  1181. std::cout << "\33[" << (totalLines - 1) << "A\r";
  1182. for (int i = 0; i < contentCount; i++)
  1183. {
  1184. if (lines[i]->isInput())
  1185. {
  1186. lines[i]->restoreCursorPos();
  1187. break;
  1188. }
  1189. if (i < contentCount - 1 && lineCounts[i])
  1190. {
  1191. std::cout << "\33[" << lineCounts[i] << "B";
  1192. }
  1193. }
  1194. std::cout << std::flush;
  1195. cs.unlock();
  1196. }
  1197. void Framework::ConsoleHandler::print(Text str)
  1198. {
  1199. std::cout << "\n";
  1200. cs.lock();
  1201. int totalLines = 0;
  1202. bool adittionalLine = 0;
  1203. for (int i = 0; i < contentCount; i++)
  1204. {
  1205. if (lines[i]->isInput())
  1206. {
  1207. lines[i]->setCursorToBeginning();
  1208. break;
  1209. }
  1210. totalLines += lineCounts[i];
  1211. }
  1212. std::cout << "\33[" << totalLines + 1 << "A";
  1213. std::cout << "\33[0K" // clear current line
  1214. << str.getText();
  1215. if (str.getText()[str.getLength() - 1] != '\n')
  1216. {
  1217. std::cout << "\n";
  1218. }
  1219. totalLines = 0;
  1220. for (int i = 0; i < contentCount; i++)
  1221. {
  1222. lineCounts[i] = lines[i]->print();
  1223. if (lineCounts[i] > 0)
  1224. {
  1225. adittionalLine = 0;
  1226. if (i < contentCount - 1)
  1227. {
  1228. std::cout << "\n";
  1229. adittionalLine = 1;
  1230. }
  1231. }
  1232. totalLines += lineCounts[i];
  1233. }
  1234. if (adittionalLine)
  1235. {
  1236. totalLines++;
  1237. }
  1238. std::cout << "\33[" << (totalLines - 1) << "A\r";
  1239. for (int i = 0; i < contentCount; i++)
  1240. {
  1241. if (lines[i]->isInput())
  1242. {
  1243. lines[i]->restoreCursorPos();
  1244. break;
  1245. }
  1246. if (i < contentCount - 1 && lineCounts[i])
  1247. {
  1248. std::cout << "\33[" << lineCounts[i] << "B";
  1249. }
  1250. }
  1251. std::cout << std::flush;
  1252. cs.unlock();
  1253. }