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