DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/debug/debug_gui.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <stdlib.h>
00021 #include <stdarg.h>
00022 #include <string.h>
00023 #include <stdio.h>
00024 #include <errno.h>
00025 
00026 #if defined(WIN32)
00027 #include <conio.h>
00028 #include <SDL.h>
00029 #endif
00030 
00031 #include "dosbox.h"
00032 #include "logging.h"
00033 #include "support.h"
00034 #include "control.h"
00035 #include "regs.h"
00036 #include "menu.h"
00037 #include "debug.h"
00038 #include "debug_inc.h"
00039 
00040 #include <stdexcept>
00041 #include <exception>
00042 
00043 using namespace std;
00044 
00045 bool log_int21 = false;
00046 bool log_fileio = false;
00047 
00048 static bool has_LOG_Init = false;
00049 static bool has_LOG_EarlyInit = false;
00050 static bool do_LOG_stderr = false;
00051 
00052 bool logBuffSuppressConsole = false;
00053 bool logBuffSuppressConsoleNeedUpdate = false;
00054 
00055 _LogGroup loggrp[LOG_MAX]={{"",LOG_NORMAL},{0,LOG_NORMAL}};
00056 FILE* debuglog = NULL;
00057 
00058 #if C_DEBUG
00059 static bool logBuffHasDiscarded = false;
00060 
00061 #include <curses.h>
00062 
00063 #include <list>
00064 #include <string>
00065 using namespace std;
00066 
00067 std::string DBGBlock::windowlist_by_name(void) {
00068     std::string comp;
00069     unsigned int i;
00070 
00071     for (i=0;i < DBGBlock::WINI_MAX_INDEX;i++) {
00072         if (i != 0) comp += ",";
00073         comp += dbg_win_names[i];
00074     }
00075 
00076     return comp;
00077 }
00078 
00079 const unsigned int dbg_def_win_height[DBGBlock::WINI_MAX_INDEX] = {
00080     5,          /* WINI_REG */
00081     9,          /* WINI_DATA */
00082     12,         /* WINI_CODE */
00083     5,          /* WINI_VAR */
00084     6           /* WINI_OUT */
00085 };
00086 
00087 const char *dbg_def_win_titles[DBGBlock::WINI_MAX_INDEX] = {
00088     "Register Overview",        /* WINI_REG */
00089     "Data Overview",            /* WINI_DATA */
00090     "Code Overview",            /* WINI_CODE */
00091     "Variable Overview",        /* WINI_VAR */
00092     "Output"                    /* WINI_OUT */
00093 };
00094 
00095 const char *dbg_win_names[DBGBlock::WINI_MAX_INDEX] = {
00096     "REG",
00097     "DATA",
00098     "CODE",
00099     "VAR",
00100     "OUT"
00101 };
00102 
00103 #define MAX_LOG_BUFFER 4000
00104 static list<string> logBuff;
00105 static list<string>::iterator logBuffPos = logBuff.end();
00106 
00107 extern int old_cursor_state;
00108 
00109 const char *DBGBlock::get_winname(int idx) {
00110     if (idx >= 0 && idx < DBGBlock::WINI_MAX_INDEX)
00111         return dbg_win_names[idx];
00112 
00113     return NULL;
00114 }
00115 
00116 const char *DBGBlock::get_wintitle(int idx) {
00117     if (idx >= 0 && idx < DBGBlock::WINI_MAX_INDEX)
00118         return dbg.win_title[idx].c_str();
00119 
00120     return NULL;
00121 }
00122 
00123 int DBGBlock::name_to_win(const char *name) {
00124     for (unsigned int i=0;i < DBGBlock::WINI_MAX_INDEX;i++) {
00125         if (!strcasecmp(name,dbg_win_names[i]))
00126             return (int)i;
00127     }
00128 
00129     return -1;
00130 }
00131 
00132 void DBGBlock::next_window(void) {
00133     int order = win_find_order((int)active_win);
00134 
00135     if (order < 0) order = 0;
00136 
00137     order = win_next_by_order(order);
00138     if (order >= 0) active_win = win_order[order];
00139 }
00140 
00141 void DBGBlock::swap_order(int o1,int o2) {
00142     if (o1 != o2)
00143         std::swap(win_order[o1],win_order[o2]);
00144 }
00145 
00146 WINDOW* &DBGBlock::get_win_ref(int idx) {
00147     switch (idx) {
00148         case WINI_REG:  return win_reg;
00149         case WINI_DATA: return win_data;
00150         case WINI_CODE: return win_code;
00151         case WINI_VAR:  return win_var;
00152         case WINI_OUT:  return win_out;
00153     }
00154 
00155     throw domain_error("get_win_ref");
00156 }
00157 
00158 WINDOW* DBGBlock::get_win(int idx) {
00159     return get_win_ref(idx);
00160 }
00161 
00162 WINDOW *DBGBlock::get_active_win(void) {
00163     return get_win((int)active_win);
00164 }
00165 
00166 int DBGBlock::win_find_order(int wnd) {
00167     for (unsigned int i=0;i < DBGBlock::WINI_MAX_INDEX;i++) {
00168         if (dbg.win_order[i] == wnd)
00169             return (int)i;
00170     }
00171 
00172     return -1;
00173 }
00174 
00175 int DBGBlock::win_prev_by_order(int order) {
00176     int limit = DBGBlock::WINI_MAX_INDEX;
00177 
00178     do {
00179         if (--order < 0)
00180             order = DBGBlock::WINI_MAX_INDEX - 1;
00181 
00182         if (--limit <= 0)
00183             break;
00184     } while (get_win(win_order[order]) == NULL);
00185 
00186     return order;
00187 }
00188 
00189 int DBGBlock::win_next_by_order(int order) {
00190     int limit = DBGBlock::WINI_MAX_INDEX;
00191 
00192     do {
00193         if (++order >= DBGBlock::WINI_MAX_INDEX)
00194             order = 0;
00195 
00196         if (--limit <= 0)
00197             break;
00198     } while (get_win(win_order[order]) == NULL);
00199 
00200     return order;
00201 }
00202 
00203 void DBGUI_DrawBlankOutputLine(int y) {
00204     if (dbg.win_out == NULL) return;
00205 
00206     wattrset(dbg.win_out,COLOR_PAIR(PAIR_GREEN_BLACK));
00207     if (logBuffHasDiscarded)
00208         mvwprintw(dbg.win_out, y, 0, "<LOG BUFFER ENDS, OLDER CONTENT DISCARDED BEYOND THIS POINT>");
00209     else
00210         mvwprintw(dbg.win_out, y, 0, "<END OF LOG>");
00211 //    wclrtoeol(dbg.win_out);
00212 }
00213 
00214 void DBGUI_DrawDebugOutputLine(int y,std::string line) {
00215         if (dbg.win_out == NULL) return;
00216 
00217         int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx);
00218     bool ellipsisEnd = false;
00219 
00220     (void)maxy;//UNUSED
00221 
00222     /* cut the line short if it's too long for the terminal window */
00223     if (line.length() > (size_t)maxx) {
00224         line = line.substr(0,(size_t)(maxx - 3));
00225         ellipsisEnd = true;
00226     }
00227 
00228     /* Const cast is needed for pdcurses which has no const char in mvwprintw (bug maybe) */
00229     wattrset(dbg.win_out,0);
00230     mvwprintw(dbg.win_out, y, 0, const_cast<char*>(line.c_str()));
00231 
00232     if (ellipsisEnd) {
00233         wattrset(dbg.win_out,COLOR_PAIR(PAIR_GREEN_BLACK));
00234         mvwprintw(dbg.win_out, y, maxx-3,  "...");
00235     }
00236 
00237 //    wclrtoeol(dbg.win_out);
00238 }
00239 
00240 void DEBUG_LimitTopPos(void) {
00241         if (dbg.win_out != NULL) {
00242         int w,h;
00243 
00244         getmaxyx(dbg.win_out,h,w);
00245 
00246         auto i = logBuff.begin();
00247         for (int y=0;i != logBuff.end() && y < (h-1);y++) {
00248             if (i == logBuffPos) {
00249                 i++;
00250                 logBuffPos = i;
00251             }
00252             else {
00253                 i++;
00254             }
00255         }
00256     }
00257 }
00258 
00259 void DEBUG_RefreshPage(char scroll) {
00260         if (dbg.win_out == NULL) return;
00261 
00262         while (scroll < 0 && logBuffPos!=logBuff.begin()) {
00263         --logBuffPos;
00264         scroll++;
00265     }
00266         while (scroll > 0 && logBuffPos!=logBuff.end()) {
00267         ++logBuffPos;
00268         scroll--;
00269     }
00270 
00271     DEBUG_LimitTopPos();
00272 
00273         list<string>::iterator i = logBuffPos;
00274         int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx);
00275         int rem_lines = maxy;
00276         if(rem_lines <= 0) return;
00277 
00278         wclear(dbg.win_out);
00279 
00280     /* NTS: Often, i == logBuff.end() unless the user scrolled up in the interface.
00281      *
00282      *      So the original intent of this code is that the iterator is one entry PAST
00283      *      the line to begin displaying at the bottom just as end() is a sentinel
00284      *      value just past the last element of the list. So if i == logBuff.begin()
00285      *      then there's nothing to display.
00286      *
00287      *      Note that the iterator defines what is drawn on the bottom-most line,
00288      *      and iteration is done backwards until we've drawn the line at i == logBuff.begin()
00289      *      or until we've drawn something at line 0 of the subwin.
00290      *
00291      *      rem_lines starts out as the number of lines in the subwin. */
00292     if (i != logBuff.begin()) {
00293         --i;
00294 
00295         wattrset(dbg.win_out,0);
00296         while (rem_lines > 0) {
00297             rem_lines--;
00298 
00299             DBGUI_DrawDebugOutputLine(rem_lines,*i);
00300 
00301             if (i != logBuff.begin())
00302                 --i;
00303             else
00304                 break;
00305         }
00306 
00307         /* show that the lines above are beyond the end of the log */
00308         while (rem_lines > 0) {
00309             rem_lines--;
00310             DBGUI_DrawBlankOutputLine(rem_lines);
00311         }
00312     }
00313 
00314         wrefresh(dbg.win_out);
00315 }
00316 
00317 void DEBUG_ScrollHomeOutput(void) {
00318     logBuffPos = logBuff.begin();
00319     DEBUG_RefreshPage(0);
00320 }
00321 
00322 void DEBUG_ScrollToEndOutput(void) {
00323     logBuffPos = logBuff.end();
00324     DEBUG_RefreshPage(0);
00325 }
00326 
00327 static void Draw_RegisterLayout(void) {
00328         if (dbg.win_main == NULL)
00329                 return;
00330 
00331         mvwaddstr(dbg.win_reg,0,0,"EAX=");
00332         mvwaddstr(dbg.win_reg,1,0,"EBX=");
00333         mvwaddstr(dbg.win_reg,2,0,"ECX=");
00334         mvwaddstr(dbg.win_reg,3,0,"EDX=");
00335 
00336         mvwaddstr(dbg.win_reg,0,14,"ESI=");
00337         mvwaddstr(dbg.win_reg,1,14,"EDI=");
00338         mvwaddstr(dbg.win_reg,2,14,"EBP=");
00339         mvwaddstr(dbg.win_reg,3,14,"ESP=");
00340 
00341         mvwaddstr(dbg.win_reg,0,28,"DS=");
00342         mvwaddstr(dbg.win_reg,0,38,"ES=");
00343         mvwaddstr(dbg.win_reg,0,48,"FS=");
00344         mvwaddstr(dbg.win_reg,0,58,"GS=");
00345         mvwaddstr(dbg.win_reg,0,68,"SS=");
00346 
00347         mvwaddstr(dbg.win_reg,1,28,"CS=");
00348         mvwaddstr(dbg.win_reg,1,38,"EIP=");
00349 
00350         mvwaddstr(dbg.win_reg,2,75,"CPL");
00351         mvwaddstr(dbg.win_reg,2,68,"IOPL");
00352 
00353         mvwaddstr(dbg.win_reg,1,52,"C  Z  S  O  A  P  D  I  T ");
00354 }
00355 
00356 static void DrawSubWinBox(WINDOW *wnd,const char *title) {
00357     bool active = false;
00358     int x,y;
00359     int w,h;
00360 
00361     if (wnd == NULL) return;
00362 
00363     const WINDOW *active_win = dbg.get_active_win();
00364 
00365     if (wnd == active_win)
00366         active = true;
00367 
00368     getbegyx(wnd,y,x);
00369     getmaxyx(wnd,h,w);
00370 
00371     (void)h;//UNUSED
00372 
00373         if (has_colors()) {
00374         if (active)
00375                 attrset(COLOR_PAIR(PAIR_BLACK_BLUE));
00376         else
00377                 attrset(COLOR_PAIR(PAIR_WHITE_BLUE));
00378     }
00379 
00380     mvhline(y-1,x,ACS_HLINE,w);
00381     if (title != NULL) mvaddstr(y-1,x+4,title);
00382 }
00383 
00384 void DrawBars(void) {
00385         if (dbg.win_main == NULL)
00386                 return;
00387 
00388     for (unsigned int wnd=0;wnd < DBGBlock::WINI_MAX_INDEX;wnd++) {
00389         WINDOW* &ref = dbg.get_win_ref((int)wnd);
00390 
00391         if (ref != NULL) DrawSubWinBox(ref,dbg.get_wintitle((int)wnd));
00392     }
00393 
00394         attrset(0);
00395 }
00396 
00397 static void DestroySubWindows(void) {
00398     for (unsigned int wnd=0;wnd < DBGBlock::WINI_MAX_INDEX;wnd++) {
00399         WINDOW* &ref = dbg.get_win_ref((int)wnd);
00400 
00401         if (ref != NULL) {
00402             if (ref) delwin(ref);
00403             ref = NULL;
00404         }
00405     }
00406 }
00407 
00408 void DEBUG_GUI_DestroySubWindows(void) {
00409     DestroySubWindows();
00410 }
00411 
00412 static void MakeSubWindows(void) {
00413         /* The Std output win should go at the bottom */
00414         /* Make all the subwindows */
00415         int win_main_maxy, win_main_maxx; getmaxyx(dbg.win_main,win_main_maxy,win_main_maxx);
00416     unsigned int yheight[DBGBlock::WINI_MAX_INDEX];
00417     unsigned int yofs[DBGBlock::WINI_MAX_INDEX];
00418     int win_limit = win_main_maxy - 1;
00419     int expand_wndi = -1;
00420         int outy=0,height;
00421 
00422     /* main window needs to clear itself */
00423     werase(dbg.win_main);
00424 
00425     /* arrange windows */
00426     for (unsigned int wndi=0;wndi < DBGBlock::WINI_MAX_INDEX;wndi++) {
00427         unsigned int wnd = dbg.win_order[wndi];
00428 
00429         /* window must have been freed, must have height greater than 1 (more than just titlebar)
00430          * must be visible and there must be room for the window on the screen, where the last
00431          * row is reserved for the input bar */
00432         if (dbg.win_height[wnd] > 1 &&
00433             dbg.win_vis[wnd] && (outy+1) < win_limit) {
00434             outy++; // header
00435             height=(int)dbg.win_height[wnd] - 1;
00436             if ((outy+height) > win_limit) height = win_limit - outy;
00437             assert(height > 0);
00438             yofs[wndi]=(unsigned int)outy;
00439             yheight[wndi]=(unsigned int)height;
00440             outy+=height;
00441 
00442             if (wnd == DBGBlock::WINI_OUT)
00443                 expand_wndi = (int)wndi;
00444         }
00445         else {
00446             yofs[wndi]=0;
00447             yheight[wndi]=0;
00448         }
00449     }
00450 
00451     /* last window expands if output not there */
00452     if (expand_wndi < 0) {
00453         for (int wndi=DBGBlock::WINI_MAX_INDEX-1;wndi >= 0;wndi--) {
00454             if (yheight[wndi] != 0) {
00455                 expand_wndi = wndi;
00456                 break;
00457             }
00458         }
00459     }
00460 
00461     /* we give one window the power to expand to help fill the screen */
00462     if (outy < win_limit) {
00463         int expand_by = win_limit - outy;
00464 
00465         if (expand_wndi >= 0 && expand_wndi < DBGBlock::WINI_MAX_INDEX) {
00466             unsigned int wndi = (unsigned int)expand_wndi;
00467 
00468             /* add height to the window */
00469             yheight[wndi] += (unsigned int)expand_by;
00470             outy += (int)expand_by;
00471             wndi++;
00472 
00473             /* move the others down */
00474             for (;wndi < DBGBlock::WINI_MAX_INDEX;wndi++)
00475                 yofs[wndi] += (unsigned int)expand_by;
00476         }
00477     }
00478 
00479     for (unsigned int wndi=0;wndi < DBGBlock::WINI_MAX_INDEX;wndi++) {
00480         if (yheight[wndi] != 0) {
00481             unsigned int wnd = dbg.win_order[wndi];
00482             WINDOW* &ref = dbg.get_win_ref((int)wnd);
00483             assert(ref == NULL);
00484             ref = subwin(dbg.win_main,(int)yheight[wndi],win_main_maxx,(int)yofs[wndi],0);
00485         }
00486     }
00487 
00488     if (outy < win_main_maxy) {
00489         // no header
00490         height = 1;
00491         outy = win_main_maxy - height;
00492         dbg.win_inp=subwin(dbg.win_main,height,win_main_maxx,outy,0);
00493         outy += height;
00494     }
00495 
00496         DrawBars();
00497         Draw_RegisterLayout();
00498         refresh();
00499 }
00500 
00501 void DEBUG_GUI_Rebuild(void) {
00502     DestroySubWindows();
00503     MakeSubWindows();
00504     DEBUG_RefreshPage(0);
00505 }
00506 
00507 void DEBUG_GUI_OnResize(void) {
00508     DEBUG_GUI_Rebuild();
00509 }
00510 
00511 static void MakePairs(void) {
00512         init_pair(PAIR_BLACK_BLUE, COLOR_BLACK, COLOR_CYAN);
00513         init_pair(PAIR_BYELLOW_BLACK, COLOR_YELLOW /*| FOREGROUND_INTENSITY */, COLOR_BLACK);
00514         init_pair(PAIR_GREEN_BLACK, COLOR_GREEN /*| FOREGROUND_INTENSITY */, COLOR_BLACK);
00515         init_pair(PAIR_BLACK_GREY, COLOR_BLACK /*| FOREGROUND_INTENSITY */, COLOR_WHITE);
00516         init_pair(PAIR_GREY_RED, COLOR_WHITE/*| FOREGROUND_INTENSITY */, COLOR_RED);
00517     init_pair(PAIR_BLACK_GREEN, COLOR_BLACK, COLOR_GREEN);
00518         init_pair(PAIR_WHITE_BLUE, COLOR_WHITE/* | FOREGROUND_INTENSITY*/, COLOR_BLUE);
00519 }
00520 
00521 void DBGUI_NextWindow(void) {
00522     dbg.next_window();
00523         DrawBars();
00524         DEBUG_DrawScreen();
00525 }
00526 
00527 void DBGUI_NextWindowIfActiveHidden(void) {
00528     if (dbg.get_active_win() == NULL)
00529         DBGUI_NextWindow();
00530 }
00531 
00532 bool DEBUG_IsDebuggerConsoleVisible(void) {
00533         return (dbg.win_main != NULL);
00534 }
00535 
00536 void DEBUG_FlushInput(void) {
00537     if (dbg.win_main != NULL) {
00538         while (getch() >= 0); /* remember nodelay() is called to make getch() non-blocking */
00539     }
00540 }
00541 
00542 void DBGUI_StartUp(void) {
00543         mainMenu.get_item("show_console").check(true).refresh_item(mainMenu);
00544 
00545         LOG(LOG_MISC,LOG_DEBUG)("DEBUG GUI startup");
00546         /* Start the main window */
00547         dbg.win_main=initscr();
00548 
00549 #ifdef WIN32
00550     /* Tell Windows 10 we DONT want a thin tall console window that fills the screen top to bottom.
00551        It's a nuisance especially when the user attempts to move the windwo up to the top to see
00552        the status, only for Windows to auto-maximize. 30 lines is enough, thanks. */
00553     {
00554         if (dbg.win_main) {
00555             int win_main_maxy, win_main_maxx; getmaxyx(dbg.win_main, win_main_maxy, win_main_maxx);
00556             if (win_main_maxx > 100) win_main_maxy = 100;
00557             if (win_main_maxy > 40) win_main_maxy = 40;
00558             resize_term(win_main_maxy, win_main_maxx);
00559         }
00560     }
00561 #endif
00562 
00563         cbreak();       /* take input chars one at a time, no wait for \n */
00564         noecho();       /* don't echo input */
00565         scrollok(stdscr,false);
00566         nodelay(dbg.win_main,true);
00567         keypad(dbg.win_main,true);
00568         #ifndef WIN32
00569         touchwin(dbg.win_main);
00570         #endif
00571         old_cursor_state = curs_set(0);
00572         start_color();
00573         cycle_count=0;
00574         MakePairs();
00575         MakeSubWindows();
00576 
00577     /* make sure the output window is synced up */
00578     logBuffPos = logBuff.end();
00579     DEBUG_RefreshPage(0);
00580 }
00581 
00582 #endif
00583 
00584 int debugPageCounter = 0;
00585 int debugPageStopAt = 0;
00586 
00587 bool DEBUG_IsPagingOutput(void) {
00588     return debugPageStopAt > 0;
00589 }
00590 
00591 void DEBUG_DrawInput(void);
00592 
00593 void DEBUG_BeginPagedContent(void) {
00594 #if C_DEBUG
00595         int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx);
00596 
00597     debugPageCounter = 0;
00598     debugPageStopAt = maxy;
00599 #endif
00600 }
00601 
00602 void DEBUG_EndPagedContent(void) {
00603 #if C_DEBUG
00604     debugPageCounter = 0;
00605     debugPageStopAt = 0;
00606     DEBUG_DrawInput();
00607 #endif
00608 }
00609 
00610 extern bool gfx_in_mapper;
00611 
00612 bool in_debug_showmsg = false;
00613 
00614 bool IsDebuggerActive(void);
00615 
00616 void DEBUG_ShowMsg(char const* format,...) {
00617         bool stderrlog = false;
00618         char buf[512];
00619         va_list msg;
00620         size_t len;
00621 
00622     in_debug_showmsg = true;
00623 
00624     // in case of runaway error from the CPU core, user responsiveness can be helpful
00625     CPU_CycleLeft += CPU_Cycles;
00626     CPU_Cycles = 0;
00627 
00628     if (!gfx_in_mapper && !in_debug_showmsg) {
00629         void GFX_Events();
00630         GFX_Events();
00631     }
00632 
00633     va_start(msg,format);
00634         len = (size_t)vsnprintf(buf,sizeof(buf)-2u,format,msg); /* <- NTS: Did you know sprintf/vsnprintf returns number of chars written? */
00635         va_end(msg);
00636 
00637     /* remove newlines if present */
00638     while (len > 0 && buf[len-1] == '\n') buf[--len] = 0;
00639 
00640 #if C_DEBUG
00641         if (dbg.win_out != NULL)
00642                 stderrlog = false;
00643     else
00644         stderrlog = true;
00645 #else
00646         if (do_LOG_stderr || debuglog == NULL)
00647                 stderrlog = true;
00648 #endif
00649 
00650         if (debuglog != NULL) {
00651                 fprintf(debuglog,"%s\n",buf);
00652                 fflush(debuglog);
00653         }
00654         if (stderrlog) {
00655 #if C_EMSCRIPTEN
00656         /* Emscripten routes stderr to the browser console.error() function, and
00657          * stdout to a console window below ours on the browser page. We want the
00658          * user to see our blather, so print to stdout */
00659                 fprintf(stdout,"LOG: %s\n",buf);
00660                 fflush(stdout);
00661 #else
00662                 fprintf(stderr,"LOG: %s\n",buf);
00663                 fflush(stderr);
00664 #endif
00665         }
00666 
00667 #if C_DEBUG
00668         if (logBuffPos!=logBuff.end()) {
00669                 logBuffPos=logBuff.end();
00670                 DEBUG_RefreshPage(0);
00671         }
00672         logBuff.push_back(buf);
00673         if (logBuff.size() > MAX_LOG_BUFFER) {
00674         logBuffHasDiscarded = true;
00675         if (logBuffPos == logBuff.begin()) ++logBuffPos; /* keep the iterator valid */
00676                 logBuff.pop_front();
00677     }
00678 
00679         logBuffPos = logBuff.end();
00680 
00681         if (dbg.win_out != NULL) {
00682         if (logBuffSuppressConsole) {
00683             logBuffSuppressConsoleNeedUpdate = true;
00684         }
00685         else {
00686             int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx);
00687 
00688             scrollok(dbg.win_out,TRUE);
00689             scroll(dbg.win_out);
00690             scrollok(dbg.win_out,FALSE);
00691 
00692             DBGUI_DrawDebugOutputLine(maxy-1,buf);
00693 
00694             wrefresh(dbg.win_out);
00695         }
00696         }
00697 
00698     if (IsDebuggerActive() && debugPageStopAt > 0) {
00699         if (++debugPageCounter >= debugPageStopAt) {
00700             debugPageCounter = 0;
00701             DEBUG_RefreshPage(0);
00702             DEBUG_DrawInput();
00703 
00704             /* pause, wait for input */
00705             do {
00706 #if defined(WIN32)
00707                 int key;
00708 
00709                 if (kbhit())
00710                     key = getch();
00711                 else
00712                     key = -1;
00713 #else
00714                 int key = getch();
00715 #endif
00716                 if (key > 0) {
00717                     if (key == ' ' || key == 0x0A) {
00718                         /* continue */
00719                         break;
00720                     }
00721                     else if (key == 0x27/*ESC*/ || key == 0x7F/*DEL*/ || key == 0x08/*BKSP*/ ||
00722                              key == 'q' || key == 'Q') {
00723                         /* user wants to stop paging */
00724                         debugPageStopAt = 0;
00725                         break;
00726                     }
00727                 }
00728 
00729 #if defined(WIN32)
00730                 /* help inspire confidence in users and in Windows by keeping the event loop going.
00731                    This is to prevent Windows from graying out the main window during this loop
00732                    as "application not responding" */
00733                 SDL_Event ev;
00734 
00735                 if (SDL_PollEvent(&ev)) {
00736                     /* TODO */
00737                 }
00738 #endif
00739             } while (1);
00740         }
00741     }
00742 #endif
00743 
00744     in_debug_showmsg = false;
00745 }
00746 
00747 /* callback function when DOSBox-X exits */
00748 void LOG::Exit() {
00749         if (debuglog != NULL) {
00750                 fprintf(debuglog,"--END OF LOG--\n");
00751                 fclose(debuglog);
00752                 debuglog = NULL;
00753         }
00754 }
00755 
00756 void Null_Init(Section *sec);
00757 
00758 void LOG::operator() (char const* format, ...){
00759         const char *s_severity = "";
00760         char buf[512];
00761         va_list msg;
00762 
00763         switch (d_severity) {
00764                 case LOG_DEBUG: s_severity = " DEBUG"; break;
00765                 case LOG_NORMAL:s_severity = "      "; break;
00766                 case LOG_WARN:  s_severity = " WARN "; break;
00767                 case LOG_ERROR: s_severity = " ERROR"; break;
00768                 case LOG_FATAL: s_severity = " FATAL"; break;
00769                 default: break;
00770         }
00771 
00772         va_start(msg,format);
00773         vsnprintf(buf,sizeof(buf)-1,format,msg);
00774         va_end(msg);
00775 
00776         if (d_type>=LOG_MAX) return;
00777         if (d_severity < loggrp[d_type].min_severity) return;
00778         DEBUG_ShowMsg("%10u%s %s:%s\n",static_cast<Bit32u>(cycle_count),s_severity,loggrp[d_type].front,buf);
00779 }
00780 
00781 void LOG::ParseEnableSetting(_LogGroup &group,const char *setting) {
00782         if (!strcmp(setting,"true") || !strcmp(setting,"1") || !strcmp(setting,"normal"))
00783                 group.min_severity = LOG_NORMAL; /* original code's handling is equivalent to our "normal" setting */
00784         else if (!strcmp(setting,"false") || !strcmp(setting,"0") || !strcmp(setting,""))
00785                 group.min_severity = LOG_ERROR; /* original code's handling is equivalent to our "error" setting */
00786         else if (!strcmp(setting,"debug"))
00787                 group.min_severity = LOG_DEBUG;
00788         else if (!strcmp(setting,"warn"))
00789                 group.min_severity = LOG_WARN;
00790         else if (!strcmp(setting,"error"))
00791                 group.min_severity = LOG_ERROR;
00792         else if (!strcmp(setting,"fatal"))
00793                 group.min_severity = LOG_FATAL;
00794         else if (!strcmp(setting,"never"))
00795                 group.min_severity = LOG_NEVER;
00796         else
00797                 group.min_severity = LOG_NORMAL;
00798 }
00799 
00800 void LOG::Init() {
00801         char buf[64];
00802 
00803         assert(control != NULL);
00804 
00805         /* do not init twice */
00806         if (has_LOG_Init) return;
00807         has_LOG_Init = true;
00808 
00809         /* announce */
00810         LOG_MSG("Logging init: beginning logging proper. This is the end of the early init logging");
00811 
00812         /* get the [log] section */
00813         const Section_prop *sect = static_cast<Section_prop *>(control->GetSection("log"));
00814         assert(sect != NULL);
00815 
00816         /* do we write to a logfile, or not? */
00817         const char *blah = sect->Get_string("logfile");
00818         if (blah != NULL && blah[0] != 0) {
00819                 if ((debuglog=fopen(blah,"wt+")) != NULL) {
00820                         LOG_MSG("Logging: opened logfile '%s' successfully. All further logging will go to that file.",blah);
00821                         setbuf(debuglog,NULL);
00822                 }
00823                 else {
00824                         LOG_MSG("Logging: failed to open logfile '%s'. All further logging will be discarded. Error: %s",blah,strerror(errno));
00825                 }
00826         }
00827         else {
00828                 LOG_MSG("Logging: No logfile was given. All further logging will be discarded.");
00829                 debuglog=0;
00830         }
00831 
00832     log_int21 = sect->Get_bool("int21") || control->opt_logint21;
00833     log_fileio = sect->Get_bool("fileio") || control->opt_logfileio;
00834 
00835         /* end of early init logging */
00836         do_LOG_stderr = false;
00837 
00838         /* read settings for each log category, unless the -debug option was given,
00839          * in which case everything is set to debug level */
00840         for (Bitu i = LOG_ALL + 1;i < LOG_MAX;i++) { //Skip LOG_ALL, it is always enabled
00841                 safe_strncpy(buf,loggrp[i].front,sizeof(buf));
00842                 lowcase(buf);
00843 
00844                 if (control->opt_debug)
00845                         ParseEnableSetting(/*&*/loggrp[i],"debug");
00846                 else
00847                         ParseEnableSetting(/*&*/loggrp[i],sect->Get_string(buf));
00848         }
00849 
00850         LOG(LOG_MISC,LOG_DEBUG)("Logging init complete");
00851 }
00852 
00853 void LOG::EarlyInit(void) {
00854         assert(control != NULL);
00855 
00856         /* do not init twice */
00857         if (has_LOG_EarlyInit) return;
00858         has_LOG_EarlyInit = true;
00859 
00860         /* Setup logging groups */
00861         loggrp[LOG_ALL].front="ALL";
00862         loggrp[LOG_VGA].front="VGA";
00863         loggrp[LOG_VGAGFX].front="VGAGFX";
00864         loggrp[LOG_VGAMISC].front="VGAMISC";
00865         loggrp[LOG_INT10].front="INT10";
00866         loggrp[LOG_SB].front="SBLASTER";
00867         loggrp[LOG_DMACONTROL].front="DMA_CONTROL";
00868         
00869         loggrp[LOG_FPU].front="FPU";
00870         loggrp[LOG_CPU].front="CPU";
00871         loggrp[LOG_PAGING].front="PAGING";
00872 
00873         loggrp[LOG_FCB].front="FCB";
00874         loggrp[LOG_FILES].front="FILES";
00875         loggrp[LOG_IOCTL].front="IOCTL";
00876         loggrp[LOG_EXEC].front="EXEC";
00877         loggrp[LOG_DOSMISC].front="DOSMISC";
00878 
00879         loggrp[LOG_PIT].front="PIT";
00880         loggrp[LOG_KEYBOARD].front="KEYBOARD";
00881         loggrp[LOG_PIC].front="PIC";
00882 
00883         loggrp[LOG_MOUSE].front="MOUSE";
00884         loggrp[LOG_BIOS].front="BIOS";
00885         loggrp[LOG_GUI].front="GUI";
00886         loggrp[LOG_MISC].front="MISC";
00887 
00888         loggrp[LOG_IO].front="IO";
00889         loggrp[LOG_PCI].front="PCI";
00890         
00891         loggrp[LOG_VOODOO].front="SST";
00892 
00893         do_LOG_stderr = control->opt_earlydebug;
00894 
00895         for (Bitu i=1;i<LOG_MAX;i++) {
00896                 if (control->opt_earlydebug)
00897                         ParseEnableSetting(/*&*/loggrp[i],"debug");
00898                 else
00899                         ParseEnableSetting(/*&*/loggrp[i],"warn");
00900         }
00901 
00902         LOG_MSG("Early LOG Init complete");
00903 }
00904 
00905 void LOG::SetupConfigSection(void) {
00906         const char *log_values[] = {
00907                 /* compatibility with existing dosbox.conf files */
00908                 "true", "false",
00909 
00910                 /* log levels */
00911                 "debug",
00912                 "normal",
00913                 "warn",
00914                 "error",
00915                 "fatal",
00916                 "never",                /* <- this means NEVER EVER log anything */
00917 
00918                 0};
00919 
00920         /* Register the log section */
00921         Section_prop * sect=control->AddSection_prop("log",Null_Init);
00922         Prop_string* Pstring = sect->Add_string("logfile",Property::Changeable::Always,"");
00923         Pstring->Set_help("file where the log messages will be saved to");
00924         char buf[64];
00925         for (Bitu i = LOG_ALL + 1;i < LOG_MAX;i++) {
00926                 safe_strncpy(buf,loggrp[i].front, sizeof(buf));
00927                 lowcase(buf);
00928 
00929                 Pstring = sect->Add_string(buf,Property::Changeable::Always,"false");
00930                 Pstring->Set_values(log_values);
00931                 Pstring->Set_help("Enable/Disable logging of this type.");
00932     }
00933 
00934     Prop_bool* Pbool;
00935 
00936     Pbool = sect->Add_bool("int21",Property::Changeable::Always,false);
00937     Pbool->Set_help("Log all INT 21h calls");
00938 
00939     Pbool = sect->Add_bool("fileio",Property::Changeable::Always,false);
00940     Pbool->Set_help("Log file I/O through INT 21h");
00941 }
00942