DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/gui/sdl_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 #if defined(_MSC_VER)
00020 #pragma warning(disable:4065) /* Please do not warn on default case without other case statements */
00021 #endif
00022 
00023 #include "SDL.h"
00024 #include "menu.h"
00025 #include "../libs/gui_tk/gui_tk.h"
00026 
00027 #include "build_timestamp.h"
00028 
00029 #include "dosbox.h"
00030 #include "keyboard.h"
00031 #include "video.h"
00032 #include "render.h"
00033 #include "mapper.h"
00034 #include "setup.h"
00035 #include "control.h"
00036 #include "shell.h"
00037 #include "cpu.h"
00038 
00039 #include <iostream>
00040 #include <sstream>
00041 #include <stdexcept>
00042 #include <algorithm>
00043 #include <cctype>
00044 #include <functional>
00045 #include <assert.h>
00046 
00047 #include "SDL_syswm.h"
00048 
00049 #ifdef DOSBOXMENU_EXTERNALLY_MANAGED
00050 static DOSBoxMenu guiMenu;
00051 #endif
00052 
00053 /* helper class for command execution */
00054 class VirtualBatch : public BatchFile {
00055 public:
00056                             VirtualBatch(DOS_Shell *host, const std::string& cmds);
00057     bool                    ReadLine(char *line);
00058 protected:
00059     std::istringstream      lines;
00060 };
00061 
00062 extern Bit8u                int10_font_14[256 * 14];
00063 
00064 extern uint32_t             GFX_Rmask;
00065 extern unsigned char        GFX_Rshift;
00066 extern uint32_t             GFX_Gmask;
00067 extern unsigned char        GFX_Gshift;
00068 extern uint32_t             GFX_Bmask;
00069 extern unsigned char        GFX_Bshift;
00070 
00071 extern bool                 dos_kernel_disabled;
00072 extern Bitu                 currentWindowWidth, currentWindowHeight;
00073 
00074 extern bool                 MSG_Write(const char *);
00075 extern void                 LoadMessageFile(const char * fname);
00076 extern void                 GFX_SetTitle(Bit32s cycles,Bits frameskip,Bits timing,bool paused);
00077 
00078 static int                  cursor;
00079 static bool                 running;
00080 static int                  saved_bpp;
00081 static bool                 shell_idle;
00082 static bool                 in_gui = false;
00083 #if !defined(C_SDL2)
00084 static int                  old_unicode;
00085 #endif
00086 static bool                 mousetoggle;
00087 static bool                 shortcut=false;
00088 static SDL_Surface*         screenshot = NULL;
00089 static SDL_Surface*         background = NULL;
00090 #ifdef DOSBOXMENU_EXTERNALLY_MANAGED
00091 static bool                 gui_menu_init = true;
00092 #endif
00093 
00094 void                        GFX_GetSizeAndPos(int &x,int &y,int &width, int &height, bool &fullscreen);
00095 
00096 #if defined(WIN32) && !defined(HX_DOS)
00097 void                        WindowsTaskbarUpdatePreviewRegion(void);
00098 void                        WindowsTaskbarResetPreviewRegion(void);
00099 #endif
00100 
00101 /* Prepare screen for UI */
00102 void GUI_LoadFonts(void) {
00103     GUI::Font::addFont("default",new GUI::BitmapFont(int10_font_14,14,10));
00104 }
00105 
00106 static void getPixel(Bits x, Bits y, int &r, int &g, int &b, int shift)
00107 {
00108     if (x >= (Bits)render.src.width) x = (Bits)render.src.width-1;
00109     if (y >= (Bits)render.src.height) x = (Bits)render.src.height-1;
00110     if (x < 0) x = 0;
00111     if (y < 0) y = 0;
00112 
00113     Bit8u* src = (Bit8u *)&scalerSourceCache;
00114     Bit32u pixel;
00115     switch (render.scale.inMode) {
00116         case scalerMode8:
00117             pixel = *((unsigned int)x+(Bit8u*)(src+(unsigned int)y*(unsigned int)render.scale.cachePitch));
00118             r += (int)((unsigned int)render.pal.rgb[pixel].red >> (unsigned int)shift);
00119             g += (int)((unsigned int)render.pal.rgb[pixel].green >> (unsigned int)shift);
00120             b += (int)((unsigned int)render.pal.rgb[pixel].blue >> (unsigned int)shift);
00121             break;
00122         case scalerMode15:
00123             pixel = *((unsigned int)x+(Bit16u*)(src+(unsigned int)y*(unsigned int)render.scale.cachePitch));
00124             r += (int)((pixel >> (7u+(unsigned int)shift)) & (0xf8u >> (unsigned int)shift));
00125             g += (int)((pixel >> (2u+(unsigned int)shift)) & (0xf8u >> (unsigned int)shift));
00126             b += (int)((pixel << (3u-(unsigned int)shift)) & (0xf8u >> (unsigned int)shift));
00127             break;
00128         case scalerMode16:
00129             pixel = *((unsigned int)x+(Bit16u*)(src+(unsigned int)y*(unsigned int)render.scale.cachePitch));
00130             r += (int)((pixel >> (8u+(unsigned int)shift)) & (0xf8u >> shift));
00131             g += (int)((pixel >> (3u+(unsigned int)shift)) & (0xfcu >> shift));
00132             b += (int)((pixel << (3u-(unsigned int)shift)) & (0xf8u >> shift));
00133             break;
00134         case scalerMode32:
00135             pixel = *((unsigned int)x+(Bit32u*)(src+(unsigned int)y*(unsigned int)render.scale.cachePitch));
00136             r += (int)(((pixel & GFX_Rmask) >> (GFX_Rshift + shift)) & (0xffu >> shift));
00137             g += (int)(((pixel & GFX_Gmask) >> (GFX_Gshift + shift)) & (0xffu >> shift));
00138             b += (int)(((pixel & GFX_Bmask) >> (GFX_Bshift + shift)) & (0xffu >> shift));
00139             break;
00140     }
00141 }
00142 
00143 bool gui_menu_exit(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
00144     (void)menu;//UNUSED
00145     (void)menuitem;//UNUSED
00146     running = false;
00147     return true;
00148 }
00149 
00150 static GUI::ScreenSDL *UI_Startup(GUI::ScreenSDL *screen) {
00151     in_gui = true;
00152 
00153     GFX_EndUpdate(0);
00154     GFX_SetTitle(-1,-1,-1,true);
00155     if(!screen) { //Coming from DOSBox. Clean up the keyboard buffer.
00156         KEYBOARD_ClrBuffer();//Clear buffer
00157     }
00158     GFX_LosingFocus();//Release any keys pressed (buffer gets filled again). (could be in above if, but clearing the mapper input when exiting the mapper is sensible as well
00159     SDL_Delay(20);
00160 
00161     LoadMessageFile(static_cast<Section_prop*>(control->GetSection("dosbox"))->Get_string("language"));
00162 
00163     // Comparable to the code of intro.com, but not the same! (the code of intro.com is called from within a com file)
00164     shell_idle = !dos_kernel_disabled && first_shell && (DOS_PSP(dos.psp()).GetSegment() == DOS_PSP(dos.psp()).GetParent());
00165 
00166     int sx, sy, sw, sh;
00167     bool fs;
00168     GFX_GetSizeAndPos(sx, sy, sw, sh, fs);
00169 
00170     int dw,dh;
00171 #if defined(C_SDL2)
00172     {
00173         dw = 640; dh = 480;
00174 
00175         SDL_Window* GFX_GetSDLWindow(void);
00176         SDL_Window *w = GFX_GetSDLWindow();
00177         SDL_GetWindowSize(w,&dw,&dh);
00178     }
00179 #elif defined(C_HX_DOS)
00180     /* FIXME: HX DOS builds are not updating the window dimensions vars.. */
00181     /*        However our window is always fullscreen (maximized) */
00182     {
00183         dw = GetSystemMetrics(SM_CXSCREEN);
00184         dh = GetSystemMetrics(SM_CYSCREEN);
00185     }
00186 #else
00187     void UpdateWindowDimensions(void);
00188     UpdateWindowDimensions();
00189     dw = (int)currentWindowWidth;
00190     dh = (int)currentWindowHeight;
00191 #endif
00192 
00193     if (dw < 640) dw = 640;
00194     if (dh < 350) dh = 350;
00195 
00196     assert(sx < dw);
00197     assert(sy < dh);
00198 
00199     int sw_draw = sw,sh_draw = sh;
00200 
00201     if ((sx+sw_draw) > dw) sw_draw = dw-sx;
00202     if ((sy+sh_draw) > dh) sh_draw = dh-sy;
00203 
00204     assert((sx+sw_draw) <= dw);
00205     assert((sy+sh_draw) <= dh);
00206 
00207     assert(sw_draw <= sw);
00208     assert(sh_draw <= sh);
00209 
00210 #if !defined(C_SDL2)
00211     old_unicode = SDL_EnableUNICODE(1);
00212     SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
00213 #endif
00214 
00215     if (sw_draw > 0 && sh_draw > 0) {
00216         screenshot = SDL_CreateRGBSurface(SDL_SWSURFACE, dw, dh, 32, GUI::Color::RedMask, GUI::Color::GreenMask, GUI::Color::BlueMask, 0);
00217         SDL_FillRect(screenshot,0,0);
00218 
00219         unsigned int rs = screenshot->format->Rshift, gs = screenshot->format->Gshift, bs = screenshot->format->Bshift;
00220 
00221         // create screenshot for fade effect
00222         for (unsigned int y = 0; (int)y < sh_draw; y++) {
00223             Bit32u *bg = (Bit32u*)((y+(unsigned int)sy)*(unsigned int)screenshot->pitch + (char*)screenshot->pixels) + (unsigned int)sx;
00224             for (unsigned int x = 0; (int)x < sw_draw; x++) {
00225                 int r = 0, g = 0, b = 0;
00226                 getPixel((int)(x*(unsigned int)render.src.width/(unsigned int)sw),
00227                         (int)(y*(unsigned int)render.src.height/(unsigned int)sh),
00228                         r, g, b, 0);
00229                 bg[x] = ((unsigned int)r << (unsigned int)rs) |
00230                     ((unsigned int)g << (unsigned int)gs) |
00231                     ((unsigned int)b << (unsigned int)bs);
00232             }
00233         }
00234 
00235         background = SDL_CreateRGBSurface(SDL_SWSURFACE, dw, dh, 32, GUI::Color::RedMask, GUI::Color::GreenMask, GUI::Color::BlueMask, 0);
00236         SDL_FillRect(background,0,0);
00237         for (int y = 0; y < sh_draw; y++) {
00238             Bit32u *bg = (Bit32u*)((unsigned int)(y+sy)*(unsigned int)background->pitch + (char*)background->pixels) + sx;
00239             for (int x = 0; x < sw_draw; x++) {
00240                 int r = 0, g = 0, b = 0;
00241                 getPixel(x    *(int)render.src.width/sw, y    *(int)render.src.height/sh, r, g, b, 3); 
00242                 getPixel((x-1)*(int)render.src.width/sw, y    *(int)render.src.height/sh, r, g, b, 3); 
00243                 getPixel(x    *(int)render.src.width/sw, (y-1)*(int)render.src.height/sh, r, g, b, 3); 
00244                 getPixel((x-1)*(int)render.src.width/sw, (y-1)*(int)render.src.height/sh, r, g, b, 3); 
00245                 getPixel((x+1)*(int)render.src.width/sw, y    *(int)render.src.height/sh, r, g, b, 3); 
00246                 getPixel(x    *(int)render.src.width/sw, (y+1)*(int)render.src.height/sh, r, g, b, 3); 
00247                 getPixel((x+1)*(int)render.src.width/sw, (y+1)*(int)render.src.height/sh, r, g, b, 3); 
00248                 getPixel((x-1)*(int)render.src.width/sw, (y+1)*(int)render.src.height/sh, r, g, b, 3); 
00249                 int r1 = (int)((r * 393 + g * 769 + b * 189) / 1351); // 1351 -- tweak colors 
00250                 int g1 = (int)((r * 349 + g * 686 + b * 168) / 1503); // 1203 -- for a nice 
00251                 int b1 = (int)((r * 272 + g * 534 + b * 131) / 2340); // 2140 -- golden hue 
00252                 bg[x] = ((unsigned int)r1 << (unsigned int)rs) |
00253                     ((unsigned int)g1 << (unsigned int)gs) |
00254                     ((unsigned int)b1 << (unsigned int)bs); 
00255             }
00256         }
00257     }
00258 
00259     cursor = SDL_ShowCursor(SDL_QUERY);
00260     SDL_ShowCursor(SDL_ENABLE);
00261 
00262     mousetoggle = mouselocked;
00263     if (mouselocked) GFX_CaptureMouse();
00264 
00265 #if defined(C_SDL2)
00266     extern SDL_Window * GFX_SetSDLSurfaceWindow(Bit16u width, Bit16u height);
00267 
00268     void GFX_SetResizeable(bool enable);
00269     GFX_SetResizeable(false);
00270 
00271     SDL_Window* window = GFX_SetSDLSurfaceWindow(dw, dh);
00272     if (window == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError());
00273     SDL_Surface* sdlscreen = SDL_GetWindowSurface(window);
00274     if (sdlscreen == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError());
00275 
00276     if (screenshot != NULL && background != NULL) {
00277         // fade out
00278         // Jonathan C: do it FASTER!
00279         SDL_Event event;
00280         SDL_SetSurfaceBlendMode(screenshot, SDL_BLENDMODE_BLEND);
00281         for (int i = 0xff; i > 0; i -= 0x40) {
00282             SDL_SetSurfaceAlphaMod(screenshot, i); 
00283             SDL_BlitSurface(background, NULL, sdlscreen, NULL); 
00284             SDL_BlitSurface(screenshot, NULL, sdlscreen, NULL);
00285             SDL_Window* GFX_GetSDLWindow(void);
00286             SDL_UpdateWindowSurface(GFX_GetSDLWindow());
00287             while (SDL_PollEvent(&event)); 
00288             SDL_Delay(40); 
00289         } 
00290         SDL_SetSurfaceBlendMode(screenshot, SDL_BLENDMODE_NONE);
00291     }
00292 #else
00293     SDL_Surface* sdlscreen = SDL_SetVideoMode(dw, dh, 32, SDL_SWSURFACE|(fs?SDL_FULLSCREEN:0));
00294     if (sdlscreen == NULL) E_Exit("Could not initialize video mode %ix%ix32 for UI: %s", dw, dh, SDL_GetError());
00295 
00296     if (screenshot != NULL && background != NULL) {
00297         // fade out
00298         // Jonathan C: do it FASTER!
00299         SDL_Event event; 
00300         for (int i = 0xff; i > 0; i -= 0x40) {
00301             SDL_SetAlpha(screenshot, SDL_SRCALPHA, i); 
00302             SDL_BlitSurface(background, NULL, sdlscreen, NULL); 
00303             SDL_BlitSurface(screenshot, NULL, sdlscreen, NULL); 
00304             SDL_UpdateRect(sdlscreen, 0, 0, 0, 0); 
00305             while (SDL_PollEvent(&event)); 
00306             SDL_Delay(40); 
00307         }
00308     }
00309 #endif
00310  
00311     if (screenshot != NULL && background != NULL)
00312         SDL_BlitSurface(background, NULL, sdlscreen, NULL);
00313 #if defined(C_SDL2)
00314     SDL_Window* GFX_GetSDLWindow(void);
00315     SDL_UpdateWindowSurface(GFX_GetSDLWindow());
00316 #else   
00317     SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
00318 #endif
00319 
00320 #if defined(WIN32) && !defined(HX_DOS)
00321     WindowsTaskbarResetPreviewRegion();
00322 #endif
00323 
00324 #ifdef DOSBOXMENU_EXTERNALLY_MANAGED
00325     if (gui_menu_init) {
00326         gui_menu_init = false;
00327 
00328         {
00329             DOSBoxMenu::item &item = guiMenu.alloc_item(DOSBoxMenu::submenu_type_id,"ConfigGuiMenu");
00330             item.set_text("Configuration GUI");
00331         }
00332 
00333         {
00334             DOSBoxMenu::item &item = guiMenu.alloc_item(DOSBoxMenu::item_type_id,"ExitGUI");
00335             item.set_callback_function(gui_menu_exit);
00336             item.set_text("Exit configuration GUI");
00337         }
00338 
00339         guiMenu.displaylist_clear(guiMenu.display_list);
00340 
00341         guiMenu.displaylist_append(
00342                 guiMenu.display_list,
00343                 guiMenu.get_item_id_by_name("ConfigGuiMenu"));
00344 
00345         {
00346             guiMenu.displaylist_append(
00347                     guiMenu.get_item("ConfigGuiMenu").display_list, guiMenu.get_item_id_by_name("ExitGUI"));
00348         }
00349     }
00350 
00351     guiMenu.rebuild();
00352     DOSBox_SetMenu(guiMenu);
00353 #endif
00354 
00355     if (screen) screen->setSurface(sdlscreen);
00356     else screen = new GUI::ScreenSDL(sdlscreen);
00357 
00358     saved_bpp = (int)render.src.bpp;
00359     render.src.bpp = 0;
00360     running = true;
00361 
00362 #if defined(MACOSX)
00363     void osx_reload_touchbar(void);
00364     osx_reload_touchbar();
00365 #endif
00366 
00367     return screen;
00368 }
00369 
00370 /* Restore screen */
00371 static void UI_Shutdown(GUI::ScreenSDL *screen) {
00372     SDL_Surface *sdlscreen = screen->getSurface();
00373     render.src.bpp = (Bitu)saved_bpp;
00374 
00375 #ifdef DOSBOXMENU_EXTERNALLY_MANAGED
00376     DOSBox_SetMenu(mainMenu);
00377 #endif
00378 
00379 #if defined(MACOSX)
00380     void osx_reload_touchbar(void);
00381     osx_reload_touchbar();
00382 #endif
00383 
00384 #if defined(C_SDL2)
00385     if (screenshot != NULL && background != NULL) {
00386         // fade in
00387         // Jonathan C: do it FASTER!
00388         SDL_Event event;
00389         SDL_SetSurfaceBlendMode(screenshot, SDL_BLENDMODE_BLEND);
00390         for (unsigned int i = 0x00; i < 0xff; i += 0x60) {
00391             SDL_SetSurfaceAlphaMod(screenshot, i); 
00392             SDL_BlitSurface(background, NULL, sdlscreen, NULL); 
00393             SDL_BlitSurface(screenshot, NULL, sdlscreen, NULL);
00394             SDL_Window* GFX_GetSDLWindow(void);
00395             SDL_UpdateWindowSurface(GFX_GetSDLWindow());
00396             while (SDL_PollEvent(&event)); 
00397             SDL_Delay(40); 
00398         } 
00399         SDL_SetSurfaceBlendMode(screenshot, SDL_BLENDMODE_NONE);
00400     }
00401 
00402     void GFX_SetResizeable(bool enable);
00403     GFX_SetResizeable(true);
00404 #else
00405     if (screenshot != NULL && background != NULL) {
00406         // fade in
00407         // Jonathan C: do it FASTER!
00408         SDL_Event event;
00409         for (unsigned int i = 0x00; i < 0xff; i += 0x60) {
00410             SDL_SetAlpha(screenshot, SDL_SRCALPHA, i);
00411             SDL_BlitSurface(background, NULL, sdlscreen, NULL);
00412             SDL_BlitSurface(screenshot, NULL, sdlscreen, NULL);
00413             SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
00414             while (SDL_PollEvent(&event)) {}
00415             SDL_Delay(40); 
00416         }
00417     }
00418 #endif
00419 
00420     // clean up
00421     if (mousetoggle) GFX_CaptureMouse();
00422     SDL_ShowCursor(cursor);
00423     if (background != NULL) {
00424         SDL_FreeSurface(background);
00425         background = NULL;
00426     }
00427     if (screenshot != NULL) {
00428         SDL_FreeSurface(screenshot);
00429         screenshot = NULL;
00430     }
00431     SDL_FreeSurface(sdlscreen);
00432     screen->setSurface(NULL);
00433 #ifdef WIN32
00434     void res_init(void);
00435     void change_output(int output);
00436     res_init();
00437     change_output(7);
00438 #else
00439 #if 1
00440     GFX_RestoreMode();
00441 #else
00442     GFX_ResetScreen();
00443 #endif
00444 #endif
00445 
00446 #if defined(WIN32) && !defined(HX_DOS)
00447     WindowsTaskbarUpdatePreviewRegion();
00448 #endif
00449 
00450 #if !defined(C_SDL2)
00451     SDL_EnableUNICODE(old_unicode);
00452     SDL_EnableKeyRepeat(0,0);
00453 #endif
00454     GFX_SetTitle(-1,-1,-1,false);
00455 
00456     void GFX_ForceRedrawScreen(void);
00457     GFX_ForceRedrawScreen();
00458 
00459     in_gui = false;
00460 }
00461 
00462 bool GUI_IsRunning(void) {
00463     return in_gui;
00464 }
00465 
00466 static void UI_RunCommands(GUI::ScreenSDL *s, const std::string &cmds) {
00467     DOS_Shell temp;
00468     temp.call = true;
00469     UI_Shutdown(s);
00470     Bit16u n=1; Bit8u c='\n';
00471     DOS_WriteFile(STDOUT,&c,&n);
00472     temp.bf = new VirtualBatch(&temp, cmds);
00473     temp.RunInternal();
00474     temp.ShowPrompt();
00475     UI_Startup(s);
00476 }
00477 
00478 VirtualBatch::VirtualBatch(DOS_Shell *host, const std::string& cmds) : BatchFile(host, "CON", "", ""), lines(cmds) {
00479 }
00480 
00481 bool VirtualBatch::ReadLine(char *line) {
00482     std::string l;
00483 
00484     if (!std::getline(lines,l)) {
00485         delete this;
00486         return false;
00487     }
00488 
00489     strcpy(line,l.c_str());
00490     return true;
00491 }
00492 
00493 /* stringification and conversion from the c++ FAQ */
00494 class BadConversion : public std::runtime_error {
00495 public: BadConversion(const std::string& s) : std::runtime_error(s) { }
00496 };
00497 
00498 template<typename T> inline std::string stringify(const T& x, std::ios_base& ( *pf )(std::ios_base&) = NULL) {
00499     std::ostringstream o;
00500     if (pf) o << pf;
00501     if (!(o << x)) throw BadConversion(std::string("stringify(") + typeid(x).name() + ")");
00502     return o.str();
00503 }
00504 
00505 template<typename T> inline void convert(const std::string& s, T& x, bool failIfLeftoverChars = true, std::ios_base& ( *pf )(std::ios_base&) = NULL) {
00506     std::istringstream i(s);
00507     if (pf) i >> pf;
00508     char c;
00509     if (!(i >> x) || (failIfLeftoverChars && i.get(c))) throw BadConversion(s);
00510 }
00511 
00512 /*****************************************************************************************************************************************/
00513 /* UI classes */
00514 
00515 class PropertyEditor : public GUI::Window, public GUI::ActionEventSource_Callback {
00516 protected:
00517     Section_prop * section;
00518     Property *prop;
00519 public:
00520     PropertyEditor(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00521         Window(parent, x, y, 500, 25), section(section), prop(prop) { }
00522 
00523     virtual bool prepare(std::string &buffer) = 0;
00524 
00525     void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00526         (void)b;//UNUSED
00527         // HACK: Attempting to cast a String to void causes "forming reference to void" errors when building with GCC 4.7
00528         (void)arg.size();//UNUSED
00529         std::string line;
00530         if (prepare(line)) {
00531             prop->SetValue(GUI::String(line));
00532         }
00533     }
00534 
00535     void paintVisGuideLineBetween(GUI::Drawable &d,const GUI::Window *lm,const GUI::Window *rm,const GUI::Window *pm) const {
00536         int sx = lm->getX() + lm->getWidth();
00537         int ex = rm->getX();
00538         int y = pm->getHeight() / 2;
00539 
00540         sx += 4;
00541         ex -= 4;
00542 
00543         d.setColor(GUI::Color::Shadow3D);
00544         d.drawDotLine(sx,y,ex,y);
00545     }
00546 };
00547 
00548 class PropertyEditorBool : public PropertyEditor {
00549     GUI::Checkbox *input;
00550     GUI::Label *label;
00551 public:
00552     PropertyEditorBool(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00553         PropertyEditor(parent, x, y, section, prop) {
00554         label = new GUI::Label(this, 0, 5, prop->propname);
00555         input = new GUI::Checkbox(this, 480, 3, "");
00556         input->setChecked(static_cast<bool>(prop->GetValue()));
00557     }
00558 
00559     bool prepare(std::string &buffer) {
00560         if (input->isChecked() == static_cast<bool>(prop->GetValue())) return false;
00561         buffer.append(input->isChecked()?"true":"false");
00562         return true;
00563     }
00564 
00566         virtual void paint(GUI::Drawable &d) const {
00567         paintVisGuideLineBetween(d,label,input,this);
00568     }
00569 };
00570 
00571 class PropertyEditorString : public PropertyEditor {
00572 protected:
00573     GUI::Input *input;
00574     GUI::Label *label;
00575 public:
00576     PropertyEditorString(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00577         PropertyEditor(parent, x, y, section, prop) {
00578         label = new GUI::Label(this, 0, 5, prop->propname);
00579         input = new GUI::Input(this, 270, 0, 230);
00580         std::string temps = prop->GetValue().ToString();
00581         input->setText(stringify(temps));
00582     }
00583 
00584     bool prepare(std::string &buffer) {
00585         std::string temps = prop->GetValue().ToString();
00586         if (input->getText() == GUI::String(temps)) return false;
00587         buffer.append(static_cast<const std::string&>(input->getText()));
00588         return true;
00589     }
00590 
00592         virtual void paint(GUI::Drawable &d) const {
00593         paintVisGuideLineBetween(d,label,input,this);
00594     }
00595 };
00596 
00597 class PropertyEditorFloat : public PropertyEditor {
00598 protected:
00599     GUI::Input *input;
00600     GUI::Label *label;
00601 public:
00602     PropertyEditorFloat(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00603         PropertyEditor(parent, x, y, section, prop) {
00604         label = new GUI::Label(this, 0, 5, prop->propname);
00605         input = new GUI::Input(this, 380, 0, 120);
00606         input->setText(stringify((double)prop->GetValue()));
00607     }
00608 
00609     bool prepare(std::string &buffer) {
00610         double val;
00611         convert(input->getText(), val, false);
00612         if (val == (double)prop->GetValue()) return false;
00613         buffer.append(stringify(val));
00614         return true;
00615     }
00616 
00618         virtual void paint(GUI::Drawable &d) const {
00619         paintVisGuideLineBetween(d,label,input,this);
00620     }
00621 };
00622 
00623 class PropertyEditorHex : public PropertyEditor {
00624 protected:
00625     GUI::Input *input;
00626     GUI::Label *label;
00627 public:
00628     PropertyEditorHex(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00629         PropertyEditor(parent, x, y, section, prop) {
00630         label = new GUI::Label(this, 0, 5, prop->propname);
00631         input = new GUI::Input(this, 380, 0, 120);
00632         std::string temps = prop->GetValue().ToString();
00633         input->setText(temps.c_str());
00634     }
00635 
00636     bool prepare(std::string &buffer) {
00637         int val;
00638         convert(input->getText(), val, false, std::hex);
00639         if ((Hex)val ==  prop->GetValue()) return false;
00640         buffer.append(stringify(val, std::hex));
00641         return true;
00642     }
00643 
00645         virtual void paint(GUI::Drawable &d) const {
00646         paintVisGuideLineBetween(d,label,input,this);
00647     }
00648 };
00649 
00650 class PropertyEditorInt : public PropertyEditor {
00651 protected:
00652     GUI::Input *input;
00653     GUI::Label *label;
00654 public:
00655     PropertyEditorInt(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00656         PropertyEditor(parent, x, y, section, prop) {
00657         label = new GUI::Label(this, 0, 5, prop->propname);
00658         input = new GUI::Input(this, 380, 0, 120);
00659         //Maybe use ToString() of Value
00660         input->setText(stringify(static_cast<int>(prop->GetValue())));
00661     };
00662 
00663     bool prepare(std::string &buffer) {
00664         int val;
00665         convert(input->getText(), val, false);
00666         if (val == static_cast<int>(prop->GetValue())) return false;
00667         buffer.append(stringify(val));
00668         return true;
00669     };
00670 
00672         virtual void paint(GUI::Drawable &d) const {
00673         paintVisGuideLineBetween(d,label,input,this);
00674     }
00675 };
00676 
00677 static std::map< std::vector<GUI::Char>, GUI::ToplevelWindow* > cfg_windows_active;
00678 
00679 class HelpWindow : public GUI::MessageBox2 {
00680 public:
00681     std::vector<GUI::Char> cfg_sname;
00682 public:
00683     HelpWindow(GUI::Screen *parent, int x, int y, Section *section) :
00684         MessageBox2(parent, x, y, 580, "", "") { // 740
00685         if (section == NULL) {
00686             LOG_MSG("BUG: HelpWindow constructor called with section == NULL\n");
00687             return;
00688         }
00689 
00690         std::string title(section->GetName());
00691         title.at(0) = std::toupper(title.at(0));
00692         setTitle("Help for "+title);
00693 
00694         Section_prop* sec = dynamic_cast<Section_prop*>(section);
00695         if (sec) {
00696             std::string msg;
00697             Property *p;
00698             int i = 0;
00699             while ((p = sec->Get_prop(i++))) {
00700                 msg += std::string("\033[31m")+p->propname+":\033[0m\n"+p->Get_help()+"\n\n";
00701             }
00702             if (!msg.empty()) msg.replace(msg.end()-1,msg.end(),"");
00703             setText(msg);
00704         } else {
00705         std::string name = section->GetName();
00706         std::transform(name.begin(), name.end(), name.begin(), (int(*)(int))std::toupper);
00707         name += "_CONFIGFILE_HELP";
00708         setText(MSG_Get(name.c_str()));
00709         }
00710     };
00711 
00712     ~HelpWindow() {
00713         if (!cfg_sname.empty()) {
00714             auto i = cfg_windows_active.find(cfg_sname);
00715             if (i != cfg_windows_active.end()) cfg_windows_active.erase(i);
00716         }
00717     }
00718 };
00719 
00720 class SectionEditor : public GUI::ToplevelWindow {
00721     Section_prop * section;
00722     GUI::Button * closeButton = NULL;
00723     GUI::WindowInWindow * wiw = NULL;
00724 public:
00725     std::vector<GUI::Char> cfg_sname;
00726 public:
00727     SectionEditor(GUI::Screen *parent, int x, int y, Section_prop *section) :
00728         ToplevelWindow(parent, x, y, 510, 442, ""), section(section) {
00729         if (section == NULL) {
00730             LOG_MSG("BUG: SectionEditor constructor called with section == NULL\n");
00731             return;
00732         }
00733 
00734         int first_row_y = 5;
00735         int row_height = 25;
00736         int column_width = 500;
00737         int button_row_h = 26;
00738         int button_row_padding_y = 5 + 5;
00739 
00740         int num_prop = 0;
00741         while (section->Get_prop(num_prop) != NULL) num_prop++;
00742 
00743         int allowed_dialog_y = parent->getHeight() - 25 - (border_top + border_bottom) - 50;
00744 
00745         int items_per_col = num_prop;
00746         int columns = 1;
00747 
00748         int scroll_h = items_per_col * row_height;
00749         if (scroll_h > allowed_dialog_y)
00750             scroll_h = allowed_dialog_y;
00751 
00752         scroll_h += 2; /* border */
00753 
00754         wiw = new GUI::WindowInWindow(this, 5, 5, width-border_left-border_right-10, scroll_h);
00755 
00756         int button_row_y = first_row_y + scroll_h + 5;
00757         int button_w = 70;
00758         int button_pad_w = 10;
00759         int button_row_w = ((button_pad_w + button_w) * 3) - button_pad_w;
00760         int button_row_cx = (((columns * column_width) - button_row_w) / 2) + 5;
00761 
00762         resize((columns * column_width) + border_left + border_right + 2/*wiw border*/ + wiw->vscroll_display_width/*scrollbar*/ + 10,
00763                button_row_y + button_row_h + button_row_padding_y + border_top + border_bottom);
00764 
00765         if ((this->y + this->getHeight()) > parent->getHeight())
00766             move(this->x,parent->getHeight() - this->getHeight());
00767 
00768         std::string title(section->GetName());
00769         title[0] = std::toupper(title[0]);
00770         setTitle("Configuration for "+title);
00771 
00772         GUI::Button *b = new GUI::Button(this, button_row_cx, button_row_y, "Cancel", button_w);
00773         b->addActionHandler(this);
00774         closeButton = b;
00775 
00776         b = new GUI::Button(this, button_row_cx + (button_w + button_pad_w), button_row_y, "Help", button_w);
00777         b->addActionHandler(this);
00778 
00779         b = new GUI::Button(this, button_row_cx + (button_w + button_pad_w)*2, button_row_y, "OK", button_w);
00780 
00781         int i = 0;
00782         Property *prop;
00783         while ((prop = section->Get_prop(i))) {
00784             Prop_bool   *pbool   = dynamic_cast<Prop_bool*>(prop);
00785             Prop_int    *pint    = dynamic_cast<Prop_int*>(prop);
00786             Prop_double  *pdouble  = dynamic_cast<Prop_double*>(prop);
00787             Prop_hex    *phex    = dynamic_cast<Prop_hex*>(prop);
00788             Prop_string *pstring = dynamic_cast<Prop_string*>(prop);
00789             Prop_multival* pmulti = dynamic_cast<Prop_multival*>(prop);
00790             Prop_multival_remain* pmulti_remain = dynamic_cast<Prop_multival_remain*>(prop);
00791 
00792             PropertyEditor *p;
00793             if (pbool) p = new PropertyEditorBool(wiw, column_width*(i/items_per_col), (i%items_per_col)*row_height, section, prop);
00794             else if (phex) p = new PropertyEditorHex(wiw, column_width*(i/items_per_col), (i%items_per_col)*row_height, section, prop);
00795             else if (pint) p = new PropertyEditorInt(wiw, column_width*(i/items_per_col), (i%items_per_col)*row_height, section, prop);
00796             else if (pdouble) p = new PropertyEditorFloat(wiw, column_width*(i/items_per_col), (i%items_per_col)*row_height, section, prop);
00797             else if (pstring) p = new PropertyEditorString(wiw, column_width*(i/items_per_col), (i%items_per_col)*row_height, section, prop);
00798             else if (pmulti) p = new PropertyEditorString(wiw, column_width*(i/items_per_col), (i%items_per_col)*row_height, section, prop);
00799             else if (pmulti_remain) p = new PropertyEditorString(wiw, column_width*(i/items_per_col), (i%items_per_col)*row_height, section, prop);
00800             else { i++; continue; }
00801             b->addActionHandler(p);
00802             i++;
00803         }
00804         b->addActionHandler(this);
00805 
00806         /* first child is first tabbable */
00807         {
00808             Window *w = wiw->getChild(0);
00809             if (w) w->first_tabbable = true;
00810         }
00811 
00812         /* last child is first tabbable */
00813         {
00814             Window *w = wiw->getChild(wiw->getChildCount()-1);
00815             if (w) w->last_tabbable = true;
00816         }
00817 
00818         /* the FIRST field needs to come first when tabbed to */
00819         {
00820             Window *w = wiw->getChild(0);
00821             if (w) w->raise(); /* NTS: This CHANGES the child element order, getChild(0) will return something else */
00822         }
00823 
00824         wiw->resize((columns * column_width) + 2/*border*/ + wiw->vscroll_display_width, scroll_h);
00825 
00826         if (wiw->scroll_pos_h != 0) {
00827             wiw->enableScrollBars(false/*h*/,true/*v*/);
00828             wiw->enableBorder(true);
00829         }
00830         else {
00831             wiw->enableScrollBars(false/*h*/,false/*v*/);
00832             wiw->enableBorder(false);
00833 
00834             resize((columns * column_width) + border_left + border_right + 2/*wiw border*/ + /*wiw->vscroll_display_width*//*scrollbar*/ + 10,
00835                     button_row_y + button_row_h + button_row_padding_y + border_top + border_bottom);
00836         }
00837     }
00838 
00839     ~SectionEditor() {
00840         if (!cfg_sname.empty()) {
00841             auto i = cfg_windows_active.find(cfg_sname);
00842             if (i != cfg_windows_active.end()) cfg_windows_active.erase(i);
00843         }
00844     }
00845 
00846     void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00847         if (arg == "OK" || arg == "Cancel" || arg == "Close") { close(); if(shortcut) running=false; }
00848         else if (arg == "Help") {
00849             std::vector<GUI::Char> new_cfg_sname;
00850 
00851             if (!cfg_sname.empty()) {
00852 //              new_cfg_sname = "help_";
00853                 new_cfg_sname.resize(5);
00854                 new_cfg_sname[0] = 'h';
00855                 new_cfg_sname[1] = 'e';
00856                 new_cfg_sname[2] = 'l';
00857                 new_cfg_sname[3] = 'p';
00858                 new_cfg_sname[4] = '_';
00859                 new_cfg_sname.insert(new_cfg_sname.end(),cfg_sname.begin(),cfg_sname.end());
00860             }
00861 
00862             auto lookup = cfg_windows_active.find(new_cfg_sname);
00863             if (lookup == cfg_windows_active.end()) {
00864                 int nx = getX() - 10;
00865                 int ny = getY() - 10;
00866                 if (nx < 0) nx = 0;
00867                 if (ny < 0) ny = 0;
00868                 auto *np = new HelpWindow(static_cast<GUI::Screen*>(parent), nx, ny, section);
00869                 cfg_windows_active[new_cfg_sname] = np;
00870                 np->cfg_sname = new_cfg_sname;
00871                 np->raise();
00872             }
00873             else {
00874                 lookup->second->raise();
00875             }
00876         }
00877         else
00878             ToplevelWindow::actionExecuted(b, arg);
00879     }
00880 
00881     virtual bool keyDown(const GUI::Key &key) {
00882         if (GUI::ToplevelWindow::keyDown(key)) return true;
00883         return false;
00884     }
00885 
00886     virtual bool keyUp(const GUI::Key &key) {
00887         if (GUI::ToplevelWindow::keyUp(key)) return true;
00888 
00889         if (key.special == GUI::Key::Escape) {
00890             closeButton->executeAction();
00891             return true;
00892         }
00893 
00894         return false;
00895     }
00896 
00897 };
00898 
00899 class AutoexecEditor : public GUI::ToplevelWindow {
00900     GUI::Button *closeButton = NULL;
00901     Section_line * section;
00902     GUI::Input *content = NULL;
00903 public:
00904     std::vector<GUI::Char> cfg_sname;
00905 public:
00906     AutoexecEditor(GUI::Screen *parent, int x, int y, Section_line *section) :
00907         ToplevelWindow(parent, x, y, 450, 260 + GUI::titlebar_y_stop, ""), section(section) {
00908         if (section == NULL) {
00909             LOG_MSG("BUG: AutoexecEditor constructor called with section == NULL\n");
00910             return;
00911         }
00912 
00913         std::string title(section->GetName());
00914         title[0] = std::toupper(title[0]);
00915         setTitle("Edit "+title);
00916         new GUI::Label(this, 5, 10, "Content:");
00917         content = new GUI::Input(this, 5, 30, 450 - 10 - border_left - border_right, 185);
00918         content->setText(section->data);
00919         if (first_shell) (new GUI::Button(this, 5, 220, "Append History"))->addActionHandler(this);
00920         if (shell_idle) (new GUI::Button(this, 180, 220, "Execute Now"))->addActionHandler(this);
00921         (closeButton = new GUI::Button(this, 290, 220, "Cancel", 70))->addActionHandler(this);
00922         (new GUI::Button(this, 360, 220, "OK", 70))->addActionHandler(this);
00923     }
00924 
00925     ~AutoexecEditor() {
00926         if (!cfg_sname.empty()) {
00927             auto i = cfg_windows_active.find(cfg_sname);
00928             if (i != cfg_windows_active.end()) cfg_windows_active.erase(i);
00929         }
00930     }
00931 
00932     void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00933         if (arg == "OK") section->data = *(std::string*)content->getText();
00934         if (arg == "OK" || arg == "Cancel" || arg == "Close") { close(); if(shortcut) running=false; }
00935         else if (arg == "Append History") {
00936             std::list<std::string>::reverse_iterator i = first_shell->l_history.rbegin();
00937             std::string lines = *(std::string*)content->getText();
00938             while (i != first_shell->l_history.rend()) {
00939                 lines += "\n";
00940                 lines += *i;
00941                 ++i;
00942             }
00943             content->setText(lines);
00944         } else if (arg == "Execute Now") {
00945             UI_RunCommands(dynamic_cast<GUI::ScreenSDL*>(getScreen()), content->getText());
00946         } else ToplevelWindow::actionExecuted(b, arg);
00947     }
00948 
00949     virtual bool keyDown(const GUI::Key &key) {
00950         if (GUI::ToplevelWindow::keyDown(key)) return true;
00951         return false;
00952     }
00953 
00954     virtual bool keyUp(const GUI::Key &key) {
00955         if (GUI::ToplevelWindow::keyUp(key)) return true;
00956 
00957         if (key.special == GUI::Key::Escape) {
00958             closeButton->executeAction();
00959             return true;
00960         }
00961 
00962         return false;
00963     }
00964 
00965 };
00966 
00967 class SaveDialog : public GUI::ToplevelWindow {
00968 protected:
00969     GUI::Input *name;
00970 public:
00971     SaveDialog(GUI::Screen *parent, int x, int y, const char *title) :
00972         ToplevelWindow(parent, x, y, 400, 100 + GUI::titlebar_y_stop, title) {
00973         new GUI::Label(this, 5, 10, "Enter filename for configuration file:");
00974         name = new GUI::Input(this, 5, 30, width - 10 - border_left - border_right);
00975         extern std::string capturedir;
00976         std::string fullpath,file;
00977         Cross::GetPlatformConfigName(file);
00978         const size_t last_slash_idx = capturedir.find_last_of("\\/");
00979         if (std::string::npos != last_slash_idx) {
00980             fullpath = capturedir.substr(0, last_slash_idx);
00981             fullpath += CROSS_FILESPLIT;
00982             fullpath += file;
00983         } else
00984             fullpath = "dosbox-x.conf";
00985         name->setText(fullpath.c_str());
00986         (new GUI::Button(this, 120, 60, "Cancel", 70))->addActionHandler(this);
00987         (new GUI::Button(this, 210, 60, "OK", 70))->addActionHandler(this);
00988     }
00989 
00990     void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00991         (void)b;//UNUSED
00992         // HACK: Attempting to cast a String to void causes "forming reference to void" errors when building with GCC 4.7
00993         (void)arg.size();//UNUSED
00994         if (arg == "OK") control->PrintConfig(name->getText());
00995         close();
00996         if(shortcut) running=false;
00997     }
00998 };
00999 
01000 class SaveLangDialog : public GUI::ToplevelWindow {
01001 protected:
01002     GUI::Input *name;
01003 public:
01004     SaveLangDialog(GUI::Screen *parent, int x, int y, const char *title) :
01005         ToplevelWindow(parent, x, y, 400, 100 + GUI::titlebar_y_stop, title) {
01006         new GUI::Label(this, 5, 10, "Enter filename for language file:");
01007         name = new GUI::Input(this, 5, 30, width - 10 - border_left - border_right);
01008         name->setText("messages.txt");
01009         (new GUI::Button(this, 120, 60, "Cancel", 70))->addActionHandler(this);
01010         (new GUI::Button(this, 210, 60, "OK", 70))->addActionHandler(this);
01011     }
01012 
01013     void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
01014         (void)b;//UNUSED
01015         if (arg == "OK") MSG_Write(name->getText());
01016         close();
01017         if(shortcut) running=false;
01018     }
01019 };
01020 
01021 // override Input field with one that responds to the Enter key as a keyboard-based cue to click "OK"
01022 class InputWithEnterKey : public GUI::Input {
01023 public:
01024                                         InputWithEnterKey(Window *parent, int x, int y, int w, int h = 0) : GUI::Input(parent,x,y,w,h) { };
01025 public:
01026     void                                set_trigger_target(GUI::ToplevelWindow *_who) { trigger_who = _who; };
01027 protected:
01028     GUI::ToplevelWindow*                trigger_who = NULL;
01029 public:
01030     std::string                         trigger_enter = "OK";
01031     std::string                         trigger_esc = "Cancel";
01032 public:
01033     virtual bool                        keyDown(const GUI::Key &key) {
01034         if (key.special == GUI::Key::Special::Enter) {
01035             if (trigger_who != NULL && !trigger_enter.empty())
01036                 trigger_who->actionExecuted(this, trigger_enter);
01037 
01038             return true;
01039         }
01040         else if (key.special == GUI::Key::Special::Escape) {
01041             if (trigger_who != NULL && !trigger_esc.empty())
01042                 trigger_who->actionExecuted(this, trigger_esc);
01043 
01044             return true;
01045         }
01046         else {
01047             return GUI::Input::keyDown(key);
01048         }
01049     }
01050 };
01051 
01052 class SetCycles : public GUI::ToplevelWindow {
01053 protected:
01054     InputWithEnterKey *name;
01055 public:
01056     SetCycles(GUI::Screen *parent, int x, int y, const char *title) :
01057         ToplevelWindow(parent, x, y, 400, 100 + GUI::titlebar_y_stop, title) {
01058         new GUI::Label(this, 5, 10, "Enter CPU cycles:");
01059 //      name = new GUI::Input(this, 5, 30, 350);
01060         name = new InputWithEnterKey(this, 5, 30, width - 10 - border_left - border_right);
01061         name->set_trigger_target(this);
01062         std::ostringstream str;
01063         str << "fixed " << CPU_CycleMax;
01064 
01065         std::string cycles=str.str();
01066         name->setText(cycles.c_str());
01067         (new GUI::Button(this, 120, 60, "Cancel", 70))->addActionHandler(this);
01068         (new GUI::Button(this, 210, 60, "OK", 70))->addActionHandler(this);
01069 
01070         name->raise(); /* make sure keyboard focus is on the text field, ready for the user */
01071         name->posToEnd(); /* position the cursor at the end where the user is most likely going to edit */
01072     }
01073 
01074     void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
01075         (void)b;//UNUSED
01076         if (arg == "OK") {
01077             Section* sec = control->GetSection("cpu");
01078             if (sec) {
01079                 std::string tmp("cycles=");
01080                 tmp.append((const char*)(name->getText()));
01081                 sec->HandleInputline(tmp);
01082             }
01083         }
01084         close();
01085         if(shortcut) running=false;
01086     }
01087 };
01088 
01089 class SetVsyncrate : public GUI::ToplevelWindow {
01090 protected:
01091     GUI::Input *name;
01092 public:
01093     SetVsyncrate(GUI::Screen *parent, int x, int y, const char *title) :
01094         ToplevelWindow(parent, x, y, 400, 150, title) {
01095         new GUI::Label(this, 5, 10, "Enter vertical syncrate (Hz):");
01096         name = new GUI::Input(this, 5, 30, 350);
01097         Section_prop * sec = static_cast<Section_prop *>(control->GetSection("vsync"));
01098         if (sec)
01099             name->setText(sec->Get_string("vsyncrate"));
01100         else
01101             name->setText("");
01102         (new GUI::Button(this, 120, 70, "Cancel", 70))->addActionHandler(this);
01103         (new GUI::Button(this, 210, 70, "OK", 70))->addActionHandler(this);
01104     }
01105 
01106     void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
01107         (void)b;//UNUSED
01108         Section_prop * sec = static_cast<Section_prop *>(control->GetSection("vsync"));
01109         if (arg == "OK") {
01110             if (sec) {
01111                 const char* well = name->getText();
01112                 std::string s(well, 20);
01113                 std::string tmp("vsyncrate=");
01114                 tmp.append(s);
01115                 sec->HandleInputline(tmp);
01116                 delete well;
01117             }
01118         }
01119         if (sec) LOG_MSG("GUI: Current Vertical Sync Rate: %s Hz", sec->Get_string("vsyncrate"));
01120         close();
01121         if(shortcut) running=false;
01122     }
01123 };
01124 
01125 class SetLocalSize : public GUI::ToplevelWindow {
01126 protected:
01127     GUI::Input *name;
01128 public:
01129     SetLocalSize(GUI::Screen *parent, int x, int y, const char *title) :
01130         ToplevelWindow(parent, x, y, 450, 150, title) {
01131             new GUI::Label(this, 5, 10, "Enter default local freesize (MB, min=0, max=1024):");
01132             name = new GUI::Input(this, 5, 30, 400);
01133             extern unsigned int hdd_defsize;
01134             unsigned int human_readable = 512u * 32u * hdd_defsize / 1024u / 1024u;
01135             char buffer[6];
01136             sprintf(buffer, "%u", human_readable);
01137             name->setText(buffer);
01138             (new GUI::Button(this, 120, 70, "Cancel", 70))->addActionHandler(this);
01139             (new GUI::Button(this, 210, 70, "OK", 70))->addActionHandler(this);
01140     }
01141 
01142     void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
01143         (void)b;//UNUSED
01144         if (arg == "OK") {
01145             extern unsigned int hdd_defsize;
01146             int human_readable = atoi(name->getText());
01147             if (human_readable < 0)
01148                 hdd_defsize = 0u;
01149             else if (human_readable > 1024)
01150                 hdd_defsize = 256000u;
01151             else
01152                 hdd_defsize = (unsigned int)human_readable * 1024u * 1024u / 512u / 32u;
01153             LOG_MSG("GUI: Current default freesize for local disk: %dMB", 512u * 32u * hdd_defsize / 1024u / 1024u);
01154         }
01155         close();
01156         if (shortcut) running = false;
01157     }
01158 };
01159 
01160 class ConfigurationWindow : public GUI::ToplevelWindow {
01161 public:
01162     GUI::Button *closeButton;
01163     ConfigurationWindow(GUI::Screen *parent, GUI::Size x, GUI::Size y, GUI::String& title) :
01164         GUI::ToplevelWindow(parent, (int)x, (int)y, 30/*initial*/, 30/*initial*/, title) {
01165         cfg_windows_active.clear();
01166 
01167         GUI::Menubar *bar = new GUI::Menubar(this, 0, 0, getWidth()/*initial*/);
01168         bar->addMenu("Configuration");
01169         bar->addItem(0,"Save...");
01170         bar->addItem(0,"Save Language File...");
01171         bar->addItem(0,"");
01172         bar->addItem(0,"Close");
01173         bar->addMenu("Settings");
01174         bar->addMenu("Help");
01175         if (!dos_kernel_disabled) {
01176             /* these do not work until shell help text is registerd */
01177             bar->addItem(2,"Introduction");
01178             bar->addItem(2,"Getting Started");
01179             bar->addItem(2,"CD-ROM Support");
01180             bar->addItem(2,"");
01181         }
01182         bar->addItem(2,"About");
01183         bar->addActionHandler(this);
01184 
01185         new GUI::Label(this, 10, 30, "Choose a settings group to configure:");
01186 
01187         Section *sec;
01188         int gridbtnwidth = 130;
01189         int gridbtnheight = 28;
01190         int gridbtnx = 12;
01191         int gridbtny = 50;
01192         int btnperrow = 4;
01193         int i = 0;
01194 
01195         std::function< std::pair<int,int>(const int) > gridfunc = [&/*access to locals here*/](const int i){
01196             return std::pair<int,int>(gridbtnx+(i%btnperrow)*gridbtnwidth, gridbtny+(i/btnperrow)*gridbtnheight);
01197         };
01198 
01199         while ((sec = control->GetSection(i))) {
01200             if (i != 0 && (i%15) == 0) bar->addItem(1, "|");
01201             std::string name = sec->GetName();
01202             name[0] = std::toupper(name[0]);
01203             const auto sz = gridfunc(i);
01204             GUI::Button *b = new GUI::Button(this, sz.first, sz.second, name, gridbtnwidth, gridbtnheight);
01205             b->addActionHandler(this);
01206             bar->addItem(1, name);
01207             i++;
01208         }
01209 
01210         const auto finalgridpos = gridfunc(i - 1);
01211         int closerow_y = finalgridpos.second + 12 + gridbtnheight;
01212 
01213         (closeButton = new GUI::Button(this, 240, closerow_y, "Close", 80))->addActionHandler(this);
01214 
01215         resize(gridbtnx + (gridbtnwidth * btnperrow) + 12 + border_left + border_right,
01216                closerow_y + closeButton->getHeight() + 12 + border_top + border_bottom);
01217 
01218         bar->resize(getWidth(),bar->getHeight());
01219     }
01220 
01221     ~ConfigurationWindow() { running = false; cfg_windows_active.clear(); }
01222 
01223     virtual bool keyDown(const GUI::Key &key) {
01224         if (GUI::ToplevelWindow::keyDown(key)) return true;
01225         return false;
01226     }
01227 
01228     virtual bool keyUp(const GUI::Key &key) {
01229         if (GUI::ToplevelWindow::keyUp(key)) return true;
01230 
01231         if (key.special == GUI::Key::Escape) {
01232             closeButton->executeAction();
01233             return true;
01234         }
01235 
01236         return false;
01237     }
01238 
01239     void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
01240         GUI::String sname = arg;
01241         sname.at(0) = (unsigned int)std::tolower((int)sname.at(0));
01242         Section *sec;
01243         if (arg == "Close" || arg == "Cancel" || arg == "Close") {
01244             running = false;
01245         } else if (sname == "autoexec") {
01246             auto lookup = cfg_windows_active.find(sname);
01247             if (lookup == cfg_windows_active.end()) {
01248                 Section_line *section = static_cast<Section_line *>(control->GetSection((const char *)sname));
01249                 auto *np = new AutoexecEditor(getScreen(), 50, 30, section);
01250                 cfg_windows_active[sname] = np;
01251                 np->cfg_sname = sname;
01252                 np->raise();
01253             }
01254             else {
01255                 lookup->second->raise();
01256             }
01257         } else if ((sec = control->GetSection((const char *)sname))) {
01258             auto lookup = cfg_windows_active.find(sname);
01259             if (lookup == cfg_windows_active.end()) {
01260                 Section_prop *section = static_cast<Section_prop *>(sec);
01261                 auto *np = new SectionEditor(getScreen(), 50, 30, section);
01262                 cfg_windows_active[sname] = np;
01263                 np->cfg_sname = sname;
01264                 np->raise();
01265             }
01266             else {
01267                 lookup->second->raise();
01268             }
01269         } else if (arg == "About") {
01270             const char *msg = PACKAGE_STRING " (C) 2002-" COPYRIGHT_END_YEAR " The DOSBox Team\nA fork of DOSBox 0.74 by TheGreatCodeholio\nBuild date: " UPDATED_STR "\n\nFor more info visit http://dosbox-x.com\nBased on DOSBox (http://dosbox.com)\n\n";
01271             new GUI::MessageBox2(getScreen(), 100, 150, 480, "About DOSBox-X", msg);
01272         } else if (arg == "Introduction") {
01273             new GUI::MessageBox2(getScreen(), 20, 50, 540, "Introduction", MSG_Get("PROGRAM_INTRO"));
01274         } else if (arg == "Getting Started") {
01275             std::string msg = MSG_Get("PROGRAM_INTRO_MOUNT_START");
01276 #ifdef WIN32
01277             msg += MSG_Get("PROGRAM_INTRO_MOUNT_WINDOWS");
01278 #else
01279             msg += MSG_Get("PROGRAM_INTRO_MOUNT_OTHER");
01280 #endif
01281             msg += MSG_Get("PROGRAM_INTRO_MOUNT_END");
01282 
01283             new GUI::MessageBox2(getScreen(), 0, 50, 680, std::string("Getting Started"), msg);
01284         } else if (arg == "CD-ROM Support") {
01285             new GUI::MessageBox2(getScreen(), 20, 50, 640, "CD-ROM Support", MSG_Get("PROGRAM_INTRO_CDROM"));
01286         } else if (arg == "Save...") {
01287             new SaveDialog(getScreen(), 90, 100, "Save Configuration...");
01288         } else if (arg == "Save Language File...") {
01289             new SaveLangDialog(getScreen(), 90, 100, "Save Language File...");
01290         } else {
01291             return ToplevelWindow::actionExecuted(b, arg);
01292         }
01293     }
01294 };
01295 
01296 /*********************************************************************************************************************/
01297 /* UI control functions */
01298 
01299 static void UI_Execute(GUI::ScreenSDL *screen) {
01300     SDL_Surface *sdlscreen;
01301     SDL_Event event;
01302     GUI::String configString = GUI::String("DOSBox-X Configuration");
01303 
01304     sdlscreen = screen->getSurface();
01305     auto *cfg_wnd = new ConfigurationWindow(screen, 30, 30, configString);
01306     cfg_wnd->raise();
01307 
01308     // event loop
01309     while (running) {
01310         while (SDL_PollEvent(&event)) {
01311             switch (event.type) {
01312 #if !defined(C_SDL2) && defined(_WIN32) && !defined(HX_DOS)
01313                 case SDL_SYSWMEVENT : {
01314                     switch ( event.syswm.msg->msg ) {
01315                         case WM_COMMAND:
01316 # if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01317                             if (GetMenu(GetHWND())) {
01318                                 if (guiMenu.mainMenuWM_COMMAND((unsigned int)LOWORD(event.syswm.msg->wParam))) return;
01319                             }
01320 # endif
01321                             break;
01322                     }
01323                 } break;
01324 #endif
01325                 default:
01326                     break;
01327             }
01328 
01329             if (!screen->event(event)) {
01330                 if (event.type == SDL_QUIT) running = false;
01331             }
01332         }
01333 
01334         //Selecting keyboard will create a new surface.
01335         screen->watchTime();
01336         sdlscreen = screen->getSurface();
01337 
01338         if (background)
01339             SDL_BlitSurface(background, NULL, sdlscreen, NULL);
01340         else
01341             SDL_FillRect(sdlscreen,0,0);
01342 
01343         screen->update(screen->getTime());
01344 
01345 #if defined(C_SDL2)
01346         SDL_Window* GFX_GetSDLWindow(void);
01347         SDL_UpdateWindowSurface(GFX_GetSDLWindow());
01348 #else
01349         SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
01350 #endif
01351 
01352         SDL_Delay(40);
01353     }
01354 }
01355 
01356 static void UI_Select(GUI::ScreenSDL *screen, int select) {
01357     SDL_Surface *sdlscreen = NULL;
01358     Section_line *section2 = NULL;
01359     Section_prop *section = NULL;
01360     Section *sec = NULL;
01361     SDL_Event event;
01362     GUI::String configString = GUI::String("DOSBox-X Configuration");
01363 
01364     sdlscreen = screen->getSurface();
01365     switch (select) {
01366         case 0:
01367             new GUI::MessageBox2(screen, 200, 150, 280, "", "");
01368             running=false;
01369             break;
01370         case 1:
01371             new SaveDialog(screen, 90, 100, "Save Configuration...");
01372             break;
01373         case 2: {
01374             sec = control->GetSection("sdl");
01375             section=static_cast<Section_prop *>(sec); 
01376             auto *p = new SectionEditor(screen,50,30,section);
01377             p->raise();
01378             } break;
01379         case 3:
01380             sec = control->GetSection("dosbox");
01381             section=static_cast<Section_prop *>(sec); 
01382             new SectionEditor(screen,50,30,section);
01383             break;
01384         case 4:
01385             sec = control->GetSection("mixer");
01386             section=static_cast<Section_prop *>(sec); 
01387             new SectionEditor(screen,50,30,section);
01388             break;
01389         case 5:
01390             sec = control->GetSection("serial");
01391             section=static_cast<Section_prop *>(sec); 
01392             new SectionEditor(screen,50,30,section);
01393             break;
01394         case 6:
01395             sec = control->GetSection("ne2000");
01396             section=static_cast<Section_prop *>(sec); 
01397             new SectionEditor(screen,50,30,section);
01398             break;
01399         case 7:
01400             section2 = static_cast<Section_line *>(control->GetSection("autoexec"));
01401             new AutoexecEditor(screen, 50, 30, section2);
01402             break;
01403         case 8:
01404             sec = control->GetSection("glide");
01405             section=static_cast<Section_prop *>(sec); 
01406             new SectionEditor(screen,50,30,section);
01407             break;
01408         case 9:
01409             new SaveLangDialog(screen, 90, 100, "Save Language File...");
01410             break;
01411         case 10: {
01412             auto *np = new ConfigurationWindow(screen, 30, 30, configString);
01413             np->raise();
01414             } break;
01415         case 11:
01416             sec = control->GetSection("parallel");
01417             section=static_cast<Section_prop *>(sec);
01418             new SectionEditor(screen,50,30,section);
01419             break;
01420         case 12:
01421             sec = control->GetSection("printer");
01422             section=static_cast<Section_prop *>(sec);
01423             new SectionEditor(screen,50,30,section);
01424             break;
01425         case 13:
01426             sec = control->GetSection("cpu");
01427             section=static_cast<Section_prop *>(sec);
01428             new SectionEditor(screen,50,30,section);
01429             break;
01430         case 14:
01431             sec = control->GetSection("dos");
01432             section=static_cast<Section_prop *>(sec);
01433             new SectionEditor(screen,50,30,section);
01434             break;
01435         case 15:
01436             sec = control->GetSection("midi");
01437             section=static_cast<Section_prop *>(sec);
01438             new SectionEditor(screen,50,30,section);
01439             break;
01440         case 16: {
01441             auto *np1 = new SetCycles(screen, 90, 100, "Set CPU Cycles...");
01442             np1->raise();
01443             } break;
01444         case 17: {
01445             auto *np2 = new SetVsyncrate(screen, 90, 100, "Set vertical syncrate...");
01446             np2->raise();
01447             } break;
01448         case 18: {
01449             auto *np3 = new SetLocalSize(screen, 90, 100, "Set Default Local Freesize...");
01450             np3->raise();
01451             } break;
01452         default:
01453             break;
01454     }
01455 
01456     // event loop
01457     while (running) {
01458         while (SDL_PollEvent(&event)) {
01459             switch (event.type) {
01460 #if !defined(C_SDL2) && defined(_WIN32) && !defined(HX_DOS)
01461                 case SDL_SYSWMEVENT : {
01462                     switch ( event.syswm.msg->msg ) {
01463                         case WM_COMMAND:
01464 # if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01465                             if (GetMenu(GetHWND())) {
01466                                 if (guiMenu.mainMenuWM_COMMAND((unsigned int)LOWORD(event.syswm.msg->wParam))) return;
01467                             }
01468 # endif
01469                             break;
01470                     }
01471                 } break;
01472 #endif
01473                 default:
01474                     break;
01475             }
01476 
01477             if (!screen->event(event)) {
01478                 if (event.type == SDL_QUIT) running = false;
01479             }
01480         }
01481 
01482         if (background)
01483             SDL_BlitSurface(background, NULL, sdlscreen, NULL);
01484         else
01485             SDL_FillRect(sdlscreen,0,0);
01486 
01487         screen->update(4);
01488 #if defined(C_SDL2)
01489         SDL_Window* GFX_GetSDLWindow(void);
01490         SDL_UpdateWindowSurface(GFX_GetSDLWindow());
01491 #else   
01492         SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
01493 #endif
01494         SDL_Delay(20);
01495     }
01496 }
01497 
01498 void GUI_Shortcut(int select) {
01499     if(!select || running) return;
01500 
01501     bool GFX_GetPreventFullscreen(void);
01502 
01503     /* Sorry, the UI screws up 3Dfx OpenGL emulation.
01504      * Remove this block when fixed. */
01505     if (GFX_GetPreventFullscreen()) {
01506         LOG_MSG("GUI is not available while 3Dfx OpenGL emulation is running");
01507         return;
01508     }
01509 
01510     shortcut=true;
01511     GUI::ScreenSDL *screen = UI_Startup(NULL);
01512     UI_Select(screen,select);
01513     UI_Shutdown(screen);
01514     shortcut=false;
01515     delete screen;
01516 }
01517 
01518 void GUI_Run(bool pressed) {
01519     if (pressed || running) return;
01520 
01521     bool GFX_GetPreventFullscreen(void);
01522 
01523     /* Sorry, the UI screws up 3Dfx OpenGL emulation.
01524      * Remove this block when fixed. */
01525     if (GFX_GetPreventFullscreen()) {
01526         LOG_MSG("GUI is not available while 3Dfx OpenGL emulation is running");
01527         return;
01528     }
01529 
01530     GUI::ScreenSDL *screen = UI_Startup(NULL);
01531     UI_Execute(screen);
01532     UI_Shutdown(screen);
01533     delete screen;
01534 }