gcsx_debug.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. /* GCSx
  2. ** DEBUG.CPP
  3. **
  4. ** Debugging functions, including console I/O
  5. */
  6. /*****************************************************************************
  7. ** Copyright (C) 2003-2006 Janson
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ** GNU General Public License for more details.
  18. **
  19. ** You should have received a copy of the GNU General Public License
  20. ** along with this program; if not, write to the Free Software
  21. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  22. *****************************************************************************/
  23. #include "all.h"
  24. // Debug level
  25. int currentDebugLevel = 0;
  26. int debugLevel(int newLevel) {
  27. if (newLevel >= 0) currentDebugLevel = newLevel;
  28. return currentDebugLevel;
  29. }
  30. // Debugging console
  31. DebugWin* debug = NULL;
  32. list<string>* debugText = NULL;
  33. int inExitStage = 0;
  34. char* dwBuffer = NULL;
  35. int DebugWin::wantsToBeDeleted() const { start_func
  36. return 0;
  37. }
  38. DebugWin::DebugWin() : EditBox(debugText, FONT_MONO, 1), commandHistory() { start_func
  39. commandHistoryPosition = 0;
  40. newCommand = 1;
  41. }
  42. DebugWin::~DebugWin() { start_func
  43. }
  44. void DebugWin::selectAllConsole() { start_func
  45. cursorToStart(0);
  46. cursorToEnd(1);
  47. }
  48. void DebugWin::cursorToStart(int drag) { start_func
  49. TextPoint point;
  50. findDLine(dNumLines - 1, &point.vRow);
  51. point.column = CONSOLE_LENGTH;
  52. insertionState(point, drag);
  53. }
  54. void DebugWin::cursorToEnd(int drag) { start_func
  55. TextPoint point;
  56. point.vRow = vNumLines - 1;
  57. point.column = (*findVLine(vNumLines - 1)).length;
  58. insertionState(point, drag);
  59. }
  60. // Typing-related events handled like a console
  61. int DebugWin::event(int hasFocus, const SDL_Event* event) { start_func
  62. string clipStorage;
  63. TextPoint point;
  64. list<string>::iterator pointLine;
  65. int onLastLine = 0;
  66. int onLastLineInitial = 0;
  67. VLine* vl = &*findVLine(insertion.vRow);
  68. if ((vl->dLine == dNumLines - 1) && (vl->colStart + insertion.column >= CONSOLE_LENGTH)) {
  69. if (vl->colStart + insertion.column == CONSOLE_LENGTH) onLastLineInitial = 1;
  70. onLastLine = 1;
  71. }
  72. if (isSelect) {
  73. vl = &*findVLine(selectBegin.vRow);
  74. if ((vl->dLine < dNumLines - 1) || (vl->colStart + selectBegin.column < CONSOLE_LENGTH)) onLastLine = 0;
  75. }
  76. // Readonly unless solely on last line
  77. if (onLastLine) readonly = 0;
  78. switch (event->type) {
  79. case SDL_COMMAND:
  80. switch (event->user.code) {
  81. case EDIT_PASTE:
  82. // Only allow pasting single line text
  83. if (!canConvertClipboard(CLIPBOARD_TEXT_LINE)) readonly = 1;
  84. break;
  85. case EDIT_SELECTALL:
  86. selectAllConsole();
  87. return 1;
  88. }
  89. break;
  90. case SDL_KEYDOWN: {
  91. int drag = 0;
  92. Sint32 key;
  93. if (event->key.keysym.mod & KMOD_SHIFT) {
  94. drag = 1; // We'll check this if we care about SHIFT
  95. key = combineKey(event->key.keysym.sym, event->key.keysym.mod & ~KMOD_SHIFT);
  96. }
  97. else {
  98. key = combineKey(event->key.keysym.sym, event->key.keysym.mod);
  99. }
  100. switch (key) {
  101. case SDLK_BACKSPACE:
  102. // Disallow if first character of console line and no selection
  103. if ((onLastLineInitial) && (!isSelect)) readonly = 1;
  104. break;
  105. case SDLK_DOWN:
  106. case SDLK_UP:
  107. // Manage command history
  108. if (onLastLine) {
  109. // Get cmd
  110. point = insertion;
  111. vl = &*findVLine(point.vRow);
  112. string cmd = (*vl->dLineI).substr(CONSOLE_LENGTH, string::npos);
  113. // Only spaces?
  114. string::size_type pos = 0;
  115. pos = cmd.find_first_not_of(' ', pos);
  116. // (only spaces present?)
  117. if (pos != string::npos) {
  118. // Add to command history
  119. if ((newCommand) || (commandHistoryPosition >= commandHistory.size())) {
  120. if (commandHistoryPosition >= commandHistory.size()) {
  121. // So adding cmd to end of history will allow us to "down" to a blank line
  122. newCommand = 0;
  123. commandHistoryPosition = commandHistory.size();
  124. }
  125. commandHistory.push_back(cmd);
  126. if (commandHistory.size() > MAX_COMMAND_HISTORY) {
  127. commandHistory.erase(commandHistory.begin());
  128. --commandHistoryPosition;
  129. }
  130. }
  131. // Update existing command
  132. else if (cmd != commandHistory[commandHistoryPosition]) {
  133. commandHistory[commandHistoryPosition] = cmd;
  134. }
  135. }
  136. else if (commandHistoryPosition > commandHistory.size()) {
  137. commandHistoryPosition = commandHistory.size();
  138. }
  139. if (key == SDLK_DOWN) {
  140. // If was a new command, we don't move as we didn't
  141. // insert that command right where we are
  142. if (!newCommand) {
  143. if (++commandHistoryPosition > commandHistory.size()) {
  144. commandHistoryPosition = commandHistory.size();
  145. }
  146. }
  147. }
  148. else {
  149. if (commandHistoryPosition > 0) {
  150. --commandHistoryPosition;
  151. }
  152. }
  153. // Show new command
  154. selectAllConsole();
  155. if (commandHistoryPosition >= commandHistory.size()) {
  156. pasteText(blankString);
  157. newCommand = 1;
  158. }
  159. else {
  160. pasteText(commandHistory[commandHistoryPosition]);
  161. newCommand = 0;
  162. }
  163. return 1;
  164. }
  165. break;
  166. case SDLK_LEFT:
  167. // Disallow if first character of console line
  168. if (onLastLineInitial) return 1;
  169. break;
  170. case SDLK_HOME:
  171. case combineKey(SDLK_HOME, KMOD_CTRL):
  172. if (onLastLine) {
  173. cursorToStart(drag);
  174. return 1;
  175. }
  176. break;
  177. case SDLK_PAGEUP:
  178. if (myFrame) {
  179. myFrame->scrollBy(0, max(1, (viewHeight / lineHeight) - 1) * lineHeight);
  180. }
  181. if (myScroll) {
  182. myScroll->scrollBy(0, max(1, (viewHeight / lineHeight) - 1) * lineHeight);
  183. }
  184. return 1;
  185. case SDLK_PAGEDOWN:
  186. if (myFrame) {
  187. myFrame->scrollBy(0, max(1, (viewHeight / lineHeight) - 1) * -lineHeight);
  188. }
  189. if (myScroll) {
  190. myScroll->scrollBy(0, max(1, (viewHeight / lineHeight) - 1) * -lineHeight);
  191. }
  192. return 1;
  193. case combineKey(SDLK_LEFT, KMOD_CTRL):
  194. // Disallow if first character of console line
  195. if (onLastLineInitial) return 1;
  196. break;
  197. case SDLK_TAB:
  198. // @TODO: command completion
  199. return 1;
  200. case SDLK_RETURN:
  201. case SDLK_KP_ENTER:
  202. if (onLastLine) {
  203. // Get cmd
  204. point = insertion;
  205. vl = &*findVLine(point.vRow);
  206. string cmd = (*vl->dLineI).substr(CONSOLE_LENGTH, string::npos);
  207. // Only spaces?
  208. string::size_type pos = 0;
  209. pos = cmd.find_first_not_of(' ', pos);
  210. // (only spaces present?)
  211. if (pos != string::npos) {
  212. // Add to command history
  213. if ((newCommand) || (commandHistoryPosition >= commandHistory.size())) {
  214. commandHistory.push_back(cmd);
  215. if (commandHistory.size() > MAX_COMMAND_HISTORY) {
  216. commandHistory.erase(commandHistory.begin());
  217. }
  218. commandHistoryPosition = commandHistory.size();
  219. }
  220. // Update existing history
  221. else {
  222. if (cmd != commandHistory[commandHistoryPosition]) {
  223. commandHistory[commandHistoryPosition] = cmd;
  224. }
  225. // After this, "up" should put us at the same command
  226. ++commandHistoryPosition;
  227. }
  228. }
  229. // Add new prompt
  230. cursorToEnd();
  231. pasteText("\n");
  232. pasteText(CONSOLE_PROMPT);
  233. newCommand = 1;
  234. // Process cmd
  235. consoleParse(cmd);
  236. return 1;
  237. }
  238. break;
  239. default:
  240. // Special-case to ignore default VIEW_CONSOLE shortcut key
  241. if (key == config->readShortcut(VIEW_CONSOLE)) return 0;
  242. break;
  243. }
  244. break;
  245. }
  246. }
  247. int result = EditBox::event(hasFocus, event);
  248. readonly = 1;
  249. return result;
  250. }
  251. void DebugWin::updateTitlebar() { start_func
  252. if (myFrame) {
  253. myFrame->setTitle("Console");
  254. }
  255. }
  256. void DebugWin::prepOpen() { start_func
  257. EditBox::prepOpen();
  258. // Move cursor to end
  259. cursorToEnd();
  260. }
  261. void DebugWin::paintText(const string& text, const string& entireLine, int posInLine, int x, int y, SDL_Surface* destSurface, int isSelected) const { start_func
  262. if (strcmp(CONSOLE_PROMPT, entireLine.substr(0, CONSOLE_LENGTH).c_str())) {
  263. drawText(text, guiRGB[isSelected ? COLOR_TEXTBOX : COLOR_TEXT], x, y, destSurface, font);
  264. }
  265. else {
  266. if (posInLine < CONSOLE_LENGTH) {
  267. drawText(text.substr(0, CONSOLE_LENGTH - posInLine), guiRGB[isSelected ? COLOR_TEXTBOX : COLOR_CONSOLEPROMPT], x, y, destSurface, font);
  268. if (text.size() > (string::size_type)(CONSOLE_LENGTH - posInLine)) {
  269. x += fontWidth(text.substr(0, CONSOLE_LENGTH - posInLine), font);
  270. drawText(text.substr(CONSOLE_LENGTH - posInLine, string::npos), guiRGB[isSelected ? COLOR_TEXTBOX : COLOR_CONSOLEINPUT], x, y, destSurface, font);
  271. }
  272. }
  273. else {
  274. drawText(text, guiRGB[isSelected ? COLOR_TEXTBOX : COLOR_CONSOLEINPUT], x, y, destSurface, font);
  275. }
  276. }
  277. }
  278. void DebugWin::addLine(const char* text) { start_func
  279. VLine* vl = &*findDLine(dNumLines - 1);
  280. debugText->insert(vl->dLineI, text);
  281. contentInsert(dNumLines - 1, 1);
  282. }
  283. void initDebugBuffer() { start_func
  284. if (!debugText) {
  285. debugText = new list<string>;
  286. // Console prompt
  287. debugText->push_back(CONSOLE_PROMPT);
  288. }
  289. if (!dwBuffer) dwBuffer = new char[DEBUG_BUFFER_SIZE];
  290. }
  291. void initDebugWindow() { start_func
  292. if (!debug) {
  293. debug = new DebugWin();
  294. debug->init();
  295. }
  296. }
  297. void beginExitStage() { start_func
  298. inExitStage = 1;
  299. }
  300. void exitDebug() { start_func
  301. delete debug;
  302. delete[] dwBuffer;
  303. delete debugText;
  304. debug = NULL;
  305. dwBuffer = NULL;
  306. debugText = NULL;
  307. }
  308. DebugWin* debugWindow() { start_func
  309. return debug;
  310. }
  311. int inDebug = 0;
  312. void debugClear() { start_func
  313. inDebug = 1;
  314. if (debug) {
  315. debug->selectAll();
  316. debug->pasteText(CONSOLE_PROMPT);
  317. }
  318. else if (debugText) {
  319. debugText->clear();
  320. debugText->push_back(CONSOLE_PROMPT);
  321. }
  322. inDebug = 0;
  323. }
  324. void debugStdout(const char* text, va_list& args) {
  325. if (stdout) {
  326. vfprintf(stdout, text, args);
  327. fputc('\n', stdout);
  328. fflush(stdout);
  329. }
  330. }
  331. void debugWrite(const char* text, va_list& args) {
  332. assert(text);
  333. if (inDebug) return;
  334. inDebug = 1;
  335. if (inExitStage) {
  336. debugStdout(text, args);
  337. }
  338. else {
  339. if (!dwBuffer) dwBuffer = new char[DEBUG_BUFFER_SIZE];
  340. vsnprintf(dwBuffer, DEBUG_BUFFER_SIZE, text, args);
  341. dwBuffer[DEBUG_BUFFER_SIZE - 1] = 0;
  342. if (stdout) {
  343. fwrite(dwBuffer, 1, strlen(dwBuffer), stdout);
  344. fputc('\n', stdout);
  345. fflush(stdout);
  346. }
  347. if (debug) {
  348. debug->addLine(dwBuffer);
  349. }
  350. else if (debugText) {
  351. // Insert before console prompt
  352. list<string>::iterator pos = debugText->end();
  353. --pos;
  354. debugText->insert(pos, dwBuffer);
  355. }
  356. }
  357. inDebug = 0;
  358. }
  359. void debugStdout(const char* text, ...) {
  360. va_list arglist;
  361. va_start(arglist, text);
  362. debugStdout(text, arglist);
  363. va_end(arglist);
  364. }
  365. void debugWrite(const char* text, ...) {
  366. if (inDebug) return;
  367. va_list arglist;
  368. va_start(arglist, text);
  369. debugWrite(text, arglist);
  370. va_end(arglist);
  371. }
  372. void debugStdout(int level, const char* text, ...) {
  373. if ((level) && (!(currentDebugLevel & level))) return;
  374. va_list arglist;
  375. va_start(arglist, text);
  376. debugStdout(text, arglist);
  377. va_end(arglist);
  378. }
  379. void debugWrite(int level, const char* text, ...) {
  380. if (inDebug) return;
  381. if ((level) && (!(currentDebugLevel & level))) return;
  382. va_list arglist;
  383. va_start(arglist, text);
  384. debugWrite(text, arglist);
  385. va_end(arglist);
  386. }
  387. void debugDump(const void* data, int size, int toStdOut) {
  388. if (inDebug) return;
  389. if (!data) {
  390. if (toStdOut) debugStdout("<NULL>");
  391. else debugWrite("<NULL>");
  392. return;
  393. }
  394. string output;
  395. const Uint8* data8 = (Uint8*)data;
  396. while (size) {
  397. output = "";
  398. for (int pos = 0; (pos < 48) && (size); ++pos, --size, ++data8) {
  399. if (((pos % 4) == 0) && (pos)) output += " ";
  400. int nib = (*data8) >> 4;
  401. if (nib < 10) output += '0' + nib;
  402. else output += 'A' - 10 + nib;
  403. nib = (*data8) & 0xF;
  404. if (nib < 10) output += '0' + nib;
  405. else output += 'A' - 10 + nib;
  406. }
  407. if (toStdOut) debugStdout(output.c_str());
  408. else debugWrite(output.c_str());
  409. }
  410. }
  411. // Other stuff
  412. const string errorTitleInvalidEntry("Invalid Entry");
  413. const string errorTitleVideo("Video Error");
  414. const string errorTitleException("Internal Exception");
  415. const string errorTitleImageOverflow("Image Memory Overflow");
  416. const string errorTitleResourceLoad("Resource Loading Error");
  417. const string errorTitleFile("File Error");
  418. const string errorTitleImport("Import Error");
  419. const string errorTitleDuplicateName("Duplicate Name");
  420. const string errorTitleMissingName("Missing Name");
  421. const string errorTitleInUse("Resource In Use");
  422. const string warnTitleReminder("Reminder");
  423. const string messageBoxYes("\tYes");
  424. const string messageBoxNo("\tNo");
  425. const string messageBoxOK("OK");
  426. const string messageBoxCancel("Cancel");
  427. const string messageBoxReminder("\tDon't show this warning again");
  428. #if defined(WIN32)
  429. #include <windows.h>
  430. void systemErrorBox(const char* text, const char* title) {
  431. assert(text);
  432. debugWrite(DEBUG_FATALERROR, text);
  433. if (title == NULL) {
  434. MessageBoxA(NULL, text, "An Error Has Occurred", 16);
  435. }
  436. else {
  437. MessageBoxA(NULL, text, title, 16);
  438. }
  439. }
  440. #else
  441. void systemErrorBox(const char* text, const char* title) {
  442. debugWrite(DEBUG_FATALERROR, text);
  443. }
  444. #endif
  445. int guiRemindBox(const std::string& text, const std::string& title) { start_func
  446. Dialog* d = NULL;
  447. Widget* w = NULL;
  448. d = new Dialog(title);
  449. w = new WStatic(0, text);
  450. w->addTo(d);
  451. int checked = 0;
  452. w = new WCheckBox(0, messageBoxReminder, &checked, 1);
  453. w->addTo(d);
  454. w = new WButton(0, messageBoxOK, Dialog::BUTTON_OK);
  455. w->addTo(d);
  456. d->makePretty(1);
  457. d->runModal();
  458. delete d;
  459. return checked;
  460. }
  461. void guiErrorBox(const string& text, const string& title) { start_func
  462. Dialog* d = NULL;
  463. Widget* w = NULL;
  464. d = new Dialog(title);
  465. w = new WStatic(0, text);
  466. w->addTo(d);
  467. w = new WButton(0, messageBoxOK, Dialog::BUTTON_OK);
  468. w->addTo(d);
  469. d->makePretty();
  470. d->runModal();
  471. delete d;
  472. }
  473. int guiConfirmBox(const string& text, const string& title) { start_func
  474. Dialog* d = NULL;
  475. Widget* w = NULL;
  476. int result = 0;
  477. d = new Dialog(title);
  478. w = new WStatic(0, text);
  479. w->addTo(d);
  480. w = new WButton(1, messageBoxOK, Dialog::BUTTON_OK);
  481. w->addTo(d);
  482. w = new WButton(0, messageBoxCancel, Dialog::BUTTON_CANCEL);
  483. w->addTo(d);
  484. d->makePretty();
  485. result = d->runModal();
  486. delete d;
  487. return result;
  488. }
  489. int guiMessageBox(const string& text, const string& title, const string* buttonA, const string* buttonB, const string* buttonC, const string* buttonD, const string* buttonE) { start_func
  490. Dialog* d = NULL;
  491. Widget* w = NULL;
  492. int result = 0;
  493. d = new Dialog(title);
  494. w = new WStatic(0, text);
  495. w->addTo(d);
  496. if (buttonA) {
  497. w = new WButton(1, *buttonA, Dialog::BUTTON_OK);
  498. w->addTo(d);
  499. }
  500. if (buttonB) {
  501. w = new WButton(2, *buttonB, Dialog::BUTTON_OK);
  502. w->addTo(d);
  503. }
  504. if (buttonC) {
  505. w = new WButton(3, *buttonC, Dialog::BUTTON_OK);
  506. w->addTo(d);
  507. }
  508. if (buttonD) {
  509. w = new WButton(4, *buttonD, Dialog::BUTTON_OK);
  510. w->addTo(d);
  511. }
  512. if (buttonE) {
  513. w = new WButton(5, *buttonE, Dialog::BUTTON_OK);
  514. w->addTo(d);
  515. }
  516. d->makePretty();
  517. result = d->runModal();
  518. delete d;
  519. return result;
  520. }