DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/gui/sdl_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 "SDL.h"
00021 #include "menu.h"
00022 #include "../libs/gui_tk/gui_tk.h"
00023 
00024 #include "dosbox.h"
00025 #include "keyboard.h"
00026 #include "video.h"
00027 #include "render.h"
00028 #include "mapper.h"
00029 #include "setup.h"
00030 #include "control.h"
00031 #include "shell.h"
00032 #include "cpu.h"
00033 
00034 #include <iostream>
00035 #include <sstream>
00036 #include <stdexcept>
00037 #include <algorithm>
00038 #include <cctype>
00039 #include <assert.h>
00040 
00041 #include "SDL_syswm.h"
00042 
00043 /* helper class for command execution */
00044 class VirtualBatch : public BatchFile {
00045 public:
00046                                         VirtualBatch(DOS_Shell *host, const std::string& cmds);
00047         bool                            ReadLine(char *line);
00048 protected:
00049         std::istringstream              lines;
00050 };
00051 
00052 extern Bit8u                    int10_font_14[256 * 14];
00053 extern Program*                 first_shell;
00054 
00055 extern bool                     MSG_Write(const char *);
00056 extern void                     LoadMessageFile(const char * fname);
00057 extern void                     GFX_SetTitle(Bit32s cycles,Bits frameskip,Bits timing,bool paused);
00058 
00059 #if !defined(C_SDL2)
00060 static int                      cursor;
00061 static bool                     running;
00062 static int                      saved_bpp;
00063 static bool                     shell_idle;
00064 static int                      old_unicode;
00065 static bool                     mousetoggle;
00066 static bool                     shortcut=false;
00067 static SDL_Surface*             screenshot;
00068 static SDL_Surface*             background;
00069 #endif
00070 
00071 #if !defined(C_SDL2)
00072 /* Prepare screen for UI */
00073 void GUI_LoadFonts(void) {
00074         GUI::Font::addFont("default",new GUI::BitmapFont(int10_font_14,14,10));
00075 }
00076 
00077 static void getPixel(Bits x, Bits y, int &r, int &g, int &b, int shift)
00078 {
00079         if (x >= (Bits)render.src.width) x = (Bits)render.src.width-1;
00080         if (y >= (Bits)render.src.height) x = (Bits)render.src.height-1;
00081         if (x < 0) x = 0;
00082         if (y < 0) y = 0;
00083 
00084         Bit8u* src = (Bit8u *)&scalerSourceCache;
00085         Bit32u pixel;
00086         switch (render.scale.inMode) {
00087         case scalerMode8:
00088                 pixel = *((unsigned int)x+(Bit8u*)(src+(unsigned int)y*(unsigned int)render.scale.cachePitch));
00089                 r += (int)((unsigned int)render.pal.rgb[pixel].red >> (unsigned int)shift);
00090                 g += (int)((unsigned int)render.pal.rgb[pixel].green >> (unsigned int)shift);
00091                 b += (int)((unsigned int)render.pal.rgb[pixel].blue >> (unsigned int)shift);
00092                 break;
00093         case scalerMode15:
00094                 pixel = *((unsigned int)x+(Bit16u*)(src+(unsigned int)y*(unsigned int)render.scale.cachePitch));
00095                 r += (int)((pixel >> (7u+(unsigned int)shift)) & (0xf8u >> (unsigned int)shift));
00096                 g += (int)((pixel >> (2u+(unsigned int)shift)) & (0xf8u >> (unsigned int)shift));
00097                 b += (int)((pixel << (3u-(unsigned int)shift)) & (0xf8u >> (unsigned int)shift));
00098                 break;
00099         case scalerMode16:
00100                 pixel = *((unsigned int)x+(Bit16u*)(src+(unsigned int)y*(unsigned int)render.scale.cachePitch));
00101                 r += (int)((pixel >> (8u+(unsigned int)shift)) & (0xf8u >> shift));
00102                 g += (int)((pixel >> (3u+(unsigned int)shift)) & (0xfcu >> shift));
00103                 b += (int)((pixel << (3u-(unsigned int)shift)) & (0xf8u >> shift));
00104                 break;
00105         case scalerMode32:
00106                 pixel = *((unsigned int)x+(Bit32u*)(src+(unsigned int)y*(unsigned int)render.scale.cachePitch));
00107                 r += (int)((pixel >> (16u+(unsigned int)shift)) & (0xffu >> shift));
00108                 g += (int)((pixel >> ( 8u+(unsigned int)shift)) & (0xffu >> shift));
00109                 b += (int)((pixel >>      (unsigned int)shift)  & (0xffu >> shift));
00110                 break;
00111         }
00112 }
00113 
00114 extern bool dos_kernel_disabled;
00115 extern Bitu currentWindowWidth, currentWindowHeight;
00116 
00117 static GUI::ScreenSDL *UI_Startup(GUI::ScreenSDL *screen) {
00118         GFX_EndUpdate(0);
00119         GFX_SetTitle(-1,-1,-1,true);
00120         if(!screen) { //Coming from DOSBox. Clean up the keyboard buffer.
00121                 KEYBOARD_ClrBuffer();//Clear buffer
00122         }
00123         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
00124         SDL_Delay(20);
00125 
00126         LoadMessageFile(static_cast<Section_prop*>(control->GetSection("dosbox"))->Get_string("language"));
00127 
00128         // Comparable to the code of intro.com, but not the same! (the code of intro.com is called from within a com file)
00129         shell_idle = !dos_kernel_disabled && first_shell && (DOS_PSP(dos.psp()).GetSegment() == DOS_PSP(dos.psp()).GetParent());
00130 
00131         int w, h;
00132         bool fs;
00133         GFX_GetSize(w, h, fs);
00134         if (w <= 400) {
00135                 w *=2; h *=2;
00136         }
00137 
00138     if (!fs) {
00139         if (w < (int)currentWindowWidth)
00140             w = (int)currentWindowWidth;
00141         if (h < (int)currentWindowHeight)
00142             h = (int)currentWindowHeight;
00143     }
00144 
00145         old_unicode = SDL_EnableUNICODE(1);
00146         SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
00147         screenshot = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, GUI::Color::RedMask, GUI::Color::GreenMask, GUI::Color::BlueMask, 0);
00148 
00149         // create screenshot for fade effect
00150         unsigned int rs = screenshot->format->Rshift, gs = screenshot->format->Gshift, bs = screenshot->format->Bshift, am = GUI::Color::AlphaMask;
00151         for (unsigned int y = 0; (int)y < h; y++) {
00152                 Bit32u *bg = (Bit32u*)(y*(unsigned int)screenshot->pitch + (char*)screenshot->pixels);
00153                 for (unsigned int x = 0; (int)x < w; x++) {
00154                         int r = 0, g = 0, b = 0;
00155                         getPixel((int)(x*(unsigned int)render.src.width/(unsigned int)w),
00156                      (int)(y*(unsigned int)render.src.height/(unsigned int)h),
00157                      r, g, b, 0);
00158                         bg[x] = ((unsigned int)r << (unsigned int)rs) |
00159                     ((unsigned int)g << (unsigned int)gs) |
00160                     ((unsigned int)b << (unsigned int)bs);
00161                 }
00162         }
00163 
00164         background = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, GUI::Color::RedMask, GUI::Color::GreenMask, GUI::Color::BlueMask, GUI::Color::AlphaMask);
00165         // use a blurred and sepia-toned screenshot as menu background
00166         for (int y = 0; y < h; y++) {
00167                 Bit32u *bg = (Bit32u*)(y*background->pitch + (char*)background->pixels);
00168                 for (int x = 0; x < w; x++) {
00169                         int r = 0, g = 0, b = 0;
00170 #ifdef WIN32
00171                         getPixel(x    *(int)render.src.width/w, y    *(int)render.src.height/h, r, g, b, 0);
00172                         bg[x] = r << rs | g << gs | b << bs | am;
00173 #else
00174                         getPixel(x    *(int)render.src.width/w, y    *(int)render.src.height/h, r, g, b, 3); 
00175                         getPixel((x-1)*(int)render.src.width/w, y    *(int)render.src.height/h, r, g, b, 3); 
00176                         getPixel(x    *(int)render.src.width/w, (y-1)*(int)render.src.height/h, r, g, b, 3); 
00177                         getPixel((x-1)*(int)render.src.width/w, (y-1)*(int)render.src.height/h, r, g, b, 3); 
00178                         getPixel((x+1)*(int)render.src.width/w, y    *(int)render.src.height/h, r, g, b, 3); 
00179                         getPixel(x    *(int)render.src.width/w, (y+1)*(int)render.src.height/h, r, g, b, 3); 
00180                         getPixel((x+1)*(int)render.src.width/w, (y+1)*(int)render.src.height/h, r, g, b, 3); 
00181                         getPixel((x-1)*(int)render.src.width/w, (y+1)*(int)render.src.height/h, r, g, b, 3); 
00182                         int r1 = (int)((r * 393 + g * 769 + b * 189) / 1351); // 1351 -- tweak colors 
00183                         int g1 = (int)((r * 349 + g * 686 + b * 168) / 1503); // 1203 -- for a nice 
00184                         int b1 = (int)((r * 272 + g * 534 + b * 131) / 2340); // 2140 -- golden hue 
00185                         bg[x] = ((unsigned int)r1 << (unsigned int)rs) |
00186                     ((unsigned int)g1 << (unsigned int)gs) |
00187                     ((unsigned int)b1 << (unsigned int)bs) |
00188                      (unsigned int)am; 
00189 #endif
00190                 }
00191         }
00192 
00193         cursor = SDL_ShowCursor(SDL_QUERY);
00194         SDL_ShowCursor(SDL_ENABLE);
00195 
00196         mousetoggle = mouselocked;
00197         if (mouselocked) GFX_CaptureMouse();
00198 
00199         SDL_Surface* sdlscreen = SDL_SetVideoMode(w, h, 32, SDL_SWSURFACE|(fs?SDL_FULLSCREEN:0));
00200         if (sdlscreen == NULL) E_Exit("Could not initialize video mode %ix%ix32 for UI: %s", w, h, SDL_GetError());
00201 
00202         // fade out
00203         // Jonathan C: do it FASTER!
00204         SDL_Event event; 
00205         for (int i = 0xff; i > 0; i -= 0x30) { 
00206                 SDL_SetAlpha(screenshot, SDL_SRCALPHA, i); 
00207                 SDL_BlitSurface(background, NULL, sdlscreen, NULL); 
00208                 SDL_BlitSurface(screenshot, NULL, sdlscreen, NULL); 
00209                 SDL_UpdateRect(sdlscreen, 0, 0, 0, 0); 
00210                 while (SDL_PollEvent(&event)); 
00211                 SDL_Delay(40); 
00212         } 
00213  
00214         SDL_BlitSurface(background, NULL, sdlscreen, NULL);
00215         SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
00216 
00217         if (screen) screen->setSurface(sdlscreen);
00218         else screen = new GUI::ScreenSDL(sdlscreen);
00219 
00220         saved_bpp = render.src.bpp;
00221         render.src.bpp = 0;
00222         running = true;
00223         return screen;
00224 }
00225 
00226 /* Restore screen */
00227 static void UI_Shutdown(GUI::ScreenSDL *screen) {
00228         SDL_Surface *sdlscreen = screen->getSurface();
00229         render.src.bpp = (Bitu)saved_bpp;
00230 
00231         // fade in
00232         // Jonathan C: do it FASTER!
00233         SDL_Event event;
00234         for (unsigned int i = 0x00; i < 0xff; i += 0x30) {
00235                 SDL_SetAlpha(screenshot, SDL_SRCALPHA, i);
00236                 SDL_BlitSurface(background, NULL, sdlscreen, NULL);
00237                 SDL_BlitSurface(screenshot, NULL, sdlscreen, NULL);
00238                 SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
00239                 while (SDL_PollEvent(&event)) {};
00240                 SDL_Delay(40); 
00241         }
00242 
00243         // clean up
00244         if (mousetoggle) GFX_CaptureMouse();
00245         SDL_ShowCursor(cursor);
00246         SDL_FreeSurface(background);
00247         SDL_FreeSurface(screenshot);
00248         SDL_FreeSurface(sdlscreen);
00249         screen->setSurface(NULL);
00250 #ifdef WIN32
00251         void res_init(void);
00252         void change_output(int output);
00253         res_init();
00254         change_output(7);
00255 #else
00256 #if 1
00257         GFX_RestoreMode();
00258 #else
00259         GFX_ResetScreen();
00260 #endif
00261 #endif
00262         SDL_EnableUNICODE(old_unicode);
00263         SDL_EnableKeyRepeat(0,0);
00264         GFX_SetTitle(-1,-1,-1,false);
00265 
00266         void GFX_ForceRedrawScreen(void);
00267         GFX_ForceRedrawScreen();
00268 }
00269 
00270 static void UI_RunCommands(GUI::ScreenSDL *s, const std::string &cmds) {
00271         DOS_Shell temp;
00272         temp.call = true;
00273         UI_Shutdown(s);
00274         Bit16u n=1; Bit8u c='\n';
00275         DOS_WriteFile(STDOUT,&c,&n);
00276         temp.bf = new VirtualBatch(&temp, cmds);
00277         temp.RunInternal();
00278         temp.ShowPrompt();
00279         UI_Startup(s);
00280 }
00281 
00282 VirtualBatch::VirtualBatch(DOS_Shell *host, const std::string& cmds) : BatchFile(host, "CON", "", ""), lines(cmds) {
00283 }
00284 
00285 bool VirtualBatch::ReadLine(char *line) {
00286         std::string l;
00287 
00288         if (!std::getline(lines,l)) {
00289                 delete this;
00290                 return false;
00291         }
00292 
00293         strcpy(line,l.c_str());
00294         return true;
00295 }
00296 
00297 /* stringification and conversion from the c++ FAQ */
00298 class BadConversion : public std::runtime_error {
00299 public: BadConversion(const std::string& s) : std::runtime_error(s) { }
00300 };
00301 
00302 template<typename T> inline std::string stringify(const T& x, std::ios_base& ( *pf )(std::ios_base&) = NULL) {
00303         std::ostringstream o;
00304         if (pf) o << pf;
00305         if (!(o << x)) throw BadConversion(std::string("stringify(") + typeid(x).name() + ")");
00306         return o.str();
00307 }
00308 
00309 template<typename T> inline void convert(const std::string& s, T& x, bool failIfLeftoverChars = true, std::ios_base& ( *pf )(std::ios_base&) = NULL) {
00310         std::istringstream i(s);
00311         if (pf) i >> pf;
00312         char c;
00313         if (!(i >> x) || (failIfLeftoverChars && i.get(c))) throw BadConversion(s);
00314 }
00315 
00316 /*****************************************************************************************************************************************/
00317 /* UI classes */
00318 
00319 class PropertyEditor : public GUI::Window, public GUI::ActionEventSource_Callback {
00320 protected:
00321         Section_prop * section;
00322         Property *prop;
00323 public:
00324         PropertyEditor(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00325                 Window(parent, x, y, 240, 30), section(section), prop(prop) { }
00326 
00327         virtual bool prepare(std::string &buffer) = 0;
00328 
00329         void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00330         (void)b;//UNUSED
00331         (void)arg;//UNUSED
00332                 std::string line;
00333                 if (prepare(line)) {
00334                         prop->SetValue(GUI::String(line));
00335                 }
00336         }
00337 };
00338 
00339 class PropertyEditorBool : public PropertyEditor {
00340         GUI::Checkbox *input;
00341 public:
00342         PropertyEditorBool(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00343                 PropertyEditor(parent, x, y, section, prop) {
00344                 input = new GUI::Checkbox(this, 0, 3, prop->propname.c_str());
00345                 input->setChecked(static_cast<bool>(prop->GetValue()));
00346         }
00347 
00348         bool prepare(std::string &buffer) {
00349                 if (input->isChecked() == static_cast<bool>(prop->GetValue())) return false;
00350                 buffer.append(input->isChecked()?"true":"false");
00351                 return true;
00352         }
00353 };
00354 
00355 class PropertyEditorString : public PropertyEditor {
00356 protected:
00357         GUI::Input *input;
00358 public:
00359         PropertyEditorString(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00360                 PropertyEditor(parent, x, y, section, prop) {
00361                 new GUI::Label(this, 0, 5, prop->propname);
00362                 input = new GUI::Input(this, 130, 0, 110);
00363                 std::string temps = prop->GetValue().ToString();
00364                 input->setText(stringify(temps));
00365         }
00366 
00367         bool prepare(std::string &buffer) {
00368                 std::string temps = prop->GetValue().ToString();
00369                 if (input->getText() == GUI::String(temps)) return false;
00370                 buffer.append(static_cast<const std::string&>(input->getText()));
00371                 return true;
00372         }
00373 };
00374 
00375 class PropertyEditorFloat : public PropertyEditor {
00376 protected:
00377         GUI::Input *input;
00378 public:
00379         PropertyEditorFloat(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00380                 PropertyEditor(parent, x, y, section, prop) {
00381                 new GUI::Label(this, 0, 5, prop->propname);
00382                 input = new GUI::Input(this, 130, 0, 50);
00383                 input->setText(stringify((double)prop->GetValue()));
00384         }
00385 
00386         bool prepare(std::string &buffer) {
00387                 double val;
00388                 convert(input->getText(), val, false);
00389                 if (val == (double)prop->GetValue()) return false;
00390                 buffer.append(stringify(val));
00391                 return true;
00392         }
00393 };
00394 
00395 class PropertyEditorHex : public PropertyEditor {
00396 protected:
00397         GUI::Input *input;
00398 public:
00399         PropertyEditorHex(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00400                 PropertyEditor(parent, x, y, section, prop) {
00401                 new GUI::Label(this, 0, 5, prop->propname);
00402                 input = new GUI::Input(this, 130, 0, 50);
00403                 std::string temps = prop->GetValue().ToString();
00404                 input->setText(temps.c_str());
00405         }
00406 
00407         bool prepare(std::string &buffer) {
00408                 int val;
00409                 convert(input->getText(), val, false, std::hex);
00410                 if ((Hex)val ==  prop->GetValue()) return false;
00411                 buffer.append(stringify(val, std::hex));
00412                 return true;
00413         }
00414 };
00415 
00416 class PropertyEditorInt : public PropertyEditor {
00417 protected:
00418         GUI::Input *input;
00419 public:
00420         PropertyEditorInt(Window *parent, int x, int y, Section_prop *section, Property *prop) :
00421                 PropertyEditor(parent, x, y, section, prop) {
00422                 new GUI::Label(this, 0, 5, prop->propname);
00423                 input = new GUI::Input(this, 130, 0, 50);
00424                 //Maybe use ToString() of Value
00425                 input->setText(stringify(static_cast<int>(prop->GetValue())));
00426         };
00427 
00428         bool prepare(std::string &buffer) {
00429                 int val;
00430                 convert(input->getText(), val, false);
00431                 if (val == static_cast<int>(prop->GetValue())) return false;
00432                 buffer.append(stringify(val));
00433                 return true;
00434         };
00435 };
00436 
00437 class HelpWindow : public GUI::MessageBox2 {
00438 public:
00439         HelpWindow(GUI::Screen *parent, int x, int y, Section *section) :
00440                 MessageBox2(parent, x, y, 580, "", "") { // 740
00441                 if (section == NULL) {
00442                         LOG_MSG("BUG: HelpWindow constructor called with section == NULL\n");
00443                         return;
00444                 }
00445 
00446                 std::string title(section->GetName());
00447                 title.at(0) = std::toupper(title.at(0));
00448                 setTitle("Help for "+title);
00449 
00450                 Section_prop* sec = dynamic_cast<Section_prop*>(section);
00451                 if (sec) {
00452                         std::string msg;
00453                         Property *p;
00454                         int i = 0;
00455                         while ((p = sec->Get_prop(i++))) {
00456                                 msg += std::string("\033[34m")+p->propname+":\033[0m "+p->Get_help()+"\n";
00457                         }
00458                         msg.replace(msg.end()-1,msg.end(),"");
00459                         setText(msg);
00460                 } else {
00461                 std::string name = section->GetName();
00462                 std::transform(name.begin(), name.end(), name.begin(), (int(*)(int))std::toupper);
00463                 name += "_CONFIGFILE_HELP";
00464                 setText(MSG_Get(name.c_str()));
00465                 }
00466         };
00467 };
00468 
00469 class SectionEditor : public GUI::ToplevelWindow {
00470         Section_prop * section;
00471 public:
00472         SectionEditor(GUI::Screen *parent, int x, int y, Section_prop *section) :
00473                 ToplevelWindow(parent, x, y, 510, 300, ""), section(section) {
00474                 if (section == NULL) {
00475                         LOG_MSG("BUG: SectionEditor constructor called with section == NULL\n");
00476                         return;
00477                 }
00478                 std::string title(section->GetName());
00479                 title[0] = std::toupper(title[0]);
00480                 setTitle("Configuration for "+title);
00481                 new GUI::Label(this, 5, 10, "Settings:");
00482                 GUI::Button *b = new GUI::Button(this, 120, 220, "Cancel", 70);
00483                 b->addActionHandler(this);
00484                 b = new GUI::Button(this, 200, 220, "Help", 70);
00485                 b->addActionHandler(this);
00486                 b = new GUI::Button(this, 280, 220, "OK", 70);
00487 
00488                 int i = 0;
00489                 Property *prop;
00490                 while ((prop = section->Get_prop(i))) {
00491                         Prop_bool   *pbool   = dynamic_cast<Prop_bool*>(prop);
00492                         Prop_int    *pint    = dynamic_cast<Prop_int*>(prop);
00493                         Prop_double  *pdouble  = dynamic_cast<Prop_double*>(prop);
00494                         Prop_hex    *phex    = dynamic_cast<Prop_hex*>(prop);
00495                         Prop_string *pstring = dynamic_cast<Prop_string*>(prop);
00496                         Prop_multival* pmulti = dynamic_cast<Prop_multival*>(prop);
00497                         Prop_multival_remain* pmulti_remain = dynamic_cast<Prop_multival_remain*>(prop);
00498 
00499                         PropertyEditor *p;
00500                         if (pbool) p = new PropertyEditorBool(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
00501                         else if (phex) p = new PropertyEditorHex(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
00502                         else if (pint) p = new PropertyEditorInt(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
00503                         else if (pdouble) p = new PropertyEditorFloat(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
00504                         else if (pstring) p = new PropertyEditorString(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
00505                         else if (pmulti) p = new PropertyEditorString(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
00506                         else if (pmulti_remain) p = new PropertyEditorString(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
00507                         else { i++; continue; }
00508                         b->addActionHandler(p);
00509                         i++;
00510                 }
00511                 b->addActionHandler(this);
00512         }
00513         void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00514                 if (arg == "OK" || arg == "Cancel" || arg == "Close") { close(); if(shortcut) running=false; }
00515                 else if (arg == "Help") new HelpWindow(static_cast<GUI::Screen*>(parent), getX()-10, getY()-10, section);
00516                 else ToplevelWindow::actionExecuted(b, arg);
00517         }
00518 };
00519 
00520 class AutoexecEditor : public GUI::ToplevelWindow {
00521         Section_line * section;
00522         GUI::Input *content;
00523 public:
00524         AutoexecEditor(GUI::Screen *parent, int x, int y, Section_line *section) :
00525                 ToplevelWindow(parent, x, y, 450, 300, ""), section(section) {
00526                 if (section == NULL) {
00527                         LOG_MSG("BUG: AutoexecEditor constructor called with section == NULL\n");
00528                         return;
00529                 }
00530 
00531                 std::string title(section->GetName());
00532                 title[0] = std::toupper(title[0]);
00533                 setTitle("Edit "+title);
00534                 new GUI::Label(this, 5, 10, "Content:");
00535                 content = new GUI::Input(this, 5, 30, 420, 185);
00536                 content->setText(section->data);
00537                 if (first_shell) (new GUI::Button(this, 5, 220, "Append History"))->addActionHandler(this);
00538                 if (shell_idle) (new GUI::Button(this, 180, 220, "Execute Now"))->addActionHandler(this);
00539                 (new GUI::Button(this, 290, 220, "Cancel", 70))->addActionHandler(this);
00540                 (new GUI::Button(this, 360, 220, "OK", 70))->addActionHandler(this);
00541         }
00542 
00543         void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00544                 if (arg == "OK") section->data = *(std::string*)content->getText();
00545                 if (arg == "OK" || arg == "Cancel" || arg == "Close") { close(); if(shortcut) running=false; }
00546                 else if (arg == "Append Shell Commands") {
00547                         DOS_Shell *s = static_cast<DOS_Shell *>(first_shell);
00548                         std::list<std::string>::reverse_iterator i = s->l_history.rbegin();
00549                         std::string lines = *(std::string*)content->getText();
00550                         while (i != s->l_history.rend()) {
00551                                 lines += "\n";
00552                                 lines += *i;
00553                                 ++i;
00554                         }
00555                         content->setText(lines);
00556                 } else if (arg == "Execute Now") {
00557                         UI_RunCommands(dynamic_cast<GUI::ScreenSDL*>(getScreen()), content->getText());
00558                 } else ToplevelWindow::actionExecuted(b, arg);
00559         }
00560 };
00561 
00562 class SaveDialog : public GUI::ToplevelWindow {
00563 protected:
00564         GUI::Input *name;
00565 public:
00566         SaveDialog(GUI::Screen *parent, int x, int y, const char *title) :
00567                 ToplevelWindow(parent, x, y, 400, 150, title) {
00568                 new GUI::Label(this, 5, 10, "Enter filename for configuration file:");
00569                 name = new GUI::Input(this, 5, 30, 350);
00570                 extern std::string capturedir;
00571                 std::string fullpath,file;
00572                 Cross::GetPlatformConfigName(file);
00573                 const size_t last_slash_idx = capturedir.find_last_of("\\/");
00574                 if (std::string::npos != last_slash_idx) {
00575                         fullpath = capturedir.substr(0, last_slash_idx);
00576                         fullpath += CROSS_FILESPLIT;
00577                         fullpath += file;
00578                 } else
00579                         fullpath = "dosbox.conf";
00580                 name->setText(fullpath.c_str());
00581                 (new GUI::Button(this, 120, 70, "Cancel", 70))->addActionHandler(this);
00582                 (new GUI::Button(this, 210, 70, "OK", 70))->addActionHandler(this);
00583         }
00584 
00585         void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00586         (void)b;//UNUSED
00587         (void)arg;//UNUSED
00588                 if (arg == "OK") control->PrintConfig(name->getText());
00589                 close();
00590                 if(shortcut) running=false;
00591         }
00592 };
00593 
00594 class SaveLangDialog : public GUI::ToplevelWindow {
00595 protected:
00596         GUI::Input *name;
00597 public:
00598         SaveLangDialog(GUI::Screen *parent, int x, int y, const char *title) :
00599                 ToplevelWindow(parent, x, y, 400, 150, title) {
00600                 new GUI::Label(this, 5, 10, "Enter filename for language file:");
00601                 name = new GUI::Input(this, 5, 30, 350);
00602                 name->setText("messages.txt");
00603                 (new GUI::Button(this, 120, 70, "Cancel", 70))->addActionHandler(this);
00604                 (new GUI::Button(this, 210, 70, "OK", 70))->addActionHandler(this);
00605         }
00606 
00607         void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00608         (void)b;//UNUSED
00609                 if (arg == "OK") MSG_Write(name->getText());
00610                 close();
00611                 if(shortcut) running=false;
00612         }
00613 };
00614 
00615 // override Input field with one that responds to the Enter key as a keyboard-based cue to click "OK"
00616 class InputWithEnterKey : public GUI::Input {
00617 public:
00618                                                                                 InputWithEnterKey(Window *parent, int x, int y, int w, int h = 0) : GUI::Input(parent,x,y,w,h) { };
00619 public:
00620         void                                                            set_trigger_target(GUI::ToplevelWindow *_who) { trigger_who = _who; };
00621 protected:
00622         GUI::ToplevelWindow*                            trigger_who = NULL;
00623 public:
00624         std::string                                                     trigger_enter = "OK";
00625         std::string                                                     trigger_esc = "Cancel";
00626 public:
00627         virtual bool                                            keyDown(const GUI::Key &key) {
00628                 if (key.special == GUI::Key::Special::Enter) {
00629                         if (trigger_who != NULL && !trigger_enter.empty())
00630                                 trigger_who->actionExecuted(this, trigger_enter);
00631 
00632                         return true;
00633                 }
00634                 else if (key.special == GUI::Key::Special::Escape) {
00635                         if (trigger_who != NULL && !trigger_esc.empty())
00636                                 trigger_who->actionExecuted(this, trigger_esc);
00637 
00638                         return true;
00639                 }
00640                 else {
00641                         return GUI::Input::keyDown(key);
00642                 }
00643         }
00644 };
00645 
00646 class SetCycles : public GUI::ToplevelWindow {
00647 protected:
00648         InputWithEnterKey *name;
00649 public:
00650         SetCycles(GUI::Screen *parent, int x, int y, const char *title) :
00651                 ToplevelWindow(parent, x, y, 400, 150, title) {
00652                 new GUI::Label(this, 5, 10, "Enter CPU cycles:");
00653 //              name = new GUI::Input(this, 5, 30, 350);
00654                 name = new InputWithEnterKey(this, 5, 30, 350);
00655                 name->set_trigger_target(this);
00656                 std::ostringstream str;
00657                 str << "fixed " << CPU_CycleMax;
00658 
00659                 std::string cycles=str.str();
00660                 name->setText(cycles.c_str());
00661                 (new GUI::Button(this, 120, 70, "Cancel", 70))->addActionHandler(this);
00662                 (new GUI::Button(this, 210, 70, "OK", 70))->addActionHandler(this);
00663 
00664                 name->raise(); /* make sure keyboard focus is on the text field, ready for the user */
00665                 this->raise(); /* make sure THIS WINDOW has the keyboard focus */
00666 
00667                 name->posToEnd(); /* position the cursor at the end where the user is most likely going to edit */
00668         }
00669 
00670         void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00671         (void)b;//UNUSED
00672                 if (arg == "OK") {
00673                         Section* sec = control->GetSection("cpu");
00674                         if (sec) {
00675                                 std::string tmp("cycles=");
00676                                 tmp.append((const char*)(name->getText()));
00677                                 sec->HandleInputline(tmp);
00678                         }
00679                 }
00680                 close();
00681                 if(shortcut) running=false;
00682         }
00683 };
00684 
00685 class SetVsyncrate : public GUI::ToplevelWindow {
00686 protected:
00687         GUI::Input *name;
00688 public:
00689         SetVsyncrate(GUI::Screen *parent, int x, int y, const char *title) :
00690                 ToplevelWindow(parent, x, y, 400, 150, title) {
00691                 new GUI::Label(this, 5, 10, "Enter vertical syncrate (Hz):");
00692                 name = new GUI::Input(this, 5, 30, 350);
00693                 Section_prop * sec = static_cast<Section_prop *>(control->GetSection("vsync"));
00694                 if (sec)
00695                         name->setText(sec->Get_string("vsyncrate"));
00696                 else
00697                         name->setText("");
00698                 (new GUI::Button(this, 120, 70, "Cancel", 70))->addActionHandler(this);
00699                 (new GUI::Button(this, 210, 70, "OK", 70))->addActionHandler(this);
00700         }
00701 
00702         void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00703         (void)b;//UNUSED
00704                 Section_prop * sec = static_cast<Section_prop *>(control->GetSection("vsync"));
00705                 if (arg == "OK") {
00706                         if (sec) {
00707                                 const char* well = name->getText();
00708                                 std::string s(well, 20);
00709                                 std::string tmp("vsyncrate=");
00710                                 tmp.append(s);
00711                                 sec->HandleInputline(tmp);
00712                                 delete well;
00713                         }
00714                 }
00715                 LOG_MSG("GUI: Current Vertical Sync Rate: %s Hz", sec->Get_string("vsyncrate"));
00716                 close();
00717                 if(shortcut) running=false;
00718         }
00719 };
00720 
00721 class SetLocalSize : public GUI::ToplevelWindow {
00722 protected:
00723         GUI::Input *name;
00724 public:
00725         SetLocalSize(GUI::Screen *parent, int x, int y, const char *title) :
00726                 ToplevelWindow(parent, x, y, 450, 150, title) {
00727                         new GUI::Label(this, 5, 10, "Enter default local freesize (MB, min=0, max=1024):");
00728                         name = new GUI::Input(this, 5, 30, 400);
00729                         extern unsigned int hdd_defsize;
00730                         unsigned int human_readable = 512u * 32u * hdd_defsize / 1024u / 1024u;
00731                         char buffer[6];
00732                         sprintf(buffer, "%u", human_readable);
00733                         name->setText(buffer);
00734                         (new GUI::Button(this, 120, 70, "Cancel", 70))->addActionHandler(this);
00735                         (new GUI::Button(this, 210, 70, "OK", 70))->addActionHandler(this);
00736         }
00737 
00738         void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00739         (void)b;//UNUSED
00740                 if (arg == "OK") {
00741                         extern unsigned int hdd_defsize;
00742                         int human_readable = atoi(name->getText());
00743                         if (human_readable < 0)
00744                                 hdd_defsize = 0u;
00745                         else if (human_readable > 1024)
00746                                 hdd_defsize = 256000u;
00747                         else
00748                                 hdd_defsize = (unsigned int)human_readable * 1024u * 1024u / 512u / 32u;
00749                         LOG_MSG("GUI: Current default freesize for local disk: %dMB", 512u * 32u * hdd_defsize / 1024u / 1024u);
00750                 }
00751                 close();
00752                 if (shortcut) running = false;
00753         }
00754 };
00755 
00756 class ConfigurationWindow : public GUI::ToplevelWindow {
00757 public:
00758         ConfigurationWindow(GUI::Screen *parent, GUI::Size x, GUI::Size y, GUI::String title) :
00759                 GUI::ToplevelWindow(parent, (int)x, (int)y, 580, 380, title) {
00760 
00761                 (new GUI::Button(this, 240, 305, "Close", 80))->addActionHandler(this);
00762 
00763                 GUI::Menubar *bar = new GUI::Menubar(this, 0, 0, getWidth());
00764                 bar->addMenu("Configuration");
00765                 bar->addItem(0,"Save...");
00766                 bar->addItem(0,"Save Language File...");
00767                 bar->addItem(0,"");
00768                 bar->addItem(0,"Close");
00769                 bar->addMenu("Settings");
00770                 bar->addMenu("Help");
00771                 bar->addItem(2,"Introduction");
00772                 bar->addItem(2,"Getting Started");
00773                 bar->addItem(2,"CD-ROM Support");
00774                 bar->addItem(2,"Special Keys");
00775                 bar->addItem(2,"");
00776                 bar->addItem(2,"About");
00777                 bar->addActionHandler(this);
00778 
00779                 new GUI::Label(this, 10, 30, "Choose a settings group to configure:");
00780 
00781                 Section *sec;
00782                 int i = 0;
00783                 while ((sec = control->GetSection(i))) {
00784                         std::string name = sec->GetName();
00785                         name[0] = std::toupper(name[0]);
00786                         GUI::Button *b = new GUI::Button(this, 12+(i/7)*110, 50+(i%7)*35, name, 100);
00787                         b->addActionHandler(this);
00788                         bar->addItem(1, name);
00789                         i++;
00790                 }
00791 
00792                 if (first_shell) {
00793                         (new GUI::Button(this, 12+(i/7)*110, 50+(i%7)*35, "Keyboard", 100))->addActionHandler(this);
00794                         bar->addItem(1, "");
00795                         bar->addItem(1, "Keyboard");
00796                 }
00797         }
00798 
00799         ~ConfigurationWindow() { running = false; }
00800 
00801         void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
00802                 GUI::String sname = arg;
00803                 sname.at(0) = (unsigned int)std::tolower((int)sname.at(0));
00804                 Section *sec;
00805                 if (arg == "Close" || arg == "Cancel" || arg == "Close") {
00806                         running = false;
00807                 } else if (arg == "Keyboard") {
00808                         UI_Shutdown(dynamic_cast<GUI::ScreenSDL*>(getScreen()));
00809                         MAPPER_RunEvent(0);
00810                         UI_Startup(dynamic_cast<GUI::ScreenSDL*>(getScreen()));
00811                 } else if (sname == "autoexec") {
00812                         Section_line *section = static_cast<Section_line *>(control->GetSection((const char *)sname));
00813                         new AutoexecEditor(getScreen(), 50, 30, section);
00814                 } else if ((sec = control->GetSection((const char *)sname))) {
00815                         Section_prop *section = static_cast<Section_prop *>(sec);
00816                         new SectionEditor(getScreen(), 50, 30, section);
00817                 } else if (arg == "About") {
00818                         new GUI::MessageBox2(getScreen(), 200, 150, 280, "About DOSBox", "\nDOSBox-X\nAn emulator for old DOS Games\n\nCopyright 2002-2014\nThe DOSBox Team");
00819                 } else if (arg == "Introduction") {
00820                         new GUI::MessageBox2(getScreen(), 20, 50, 600, "Introduction", MSG_Get("PROGRAM_INTRO"));
00821                 } else if (arg == "Getting Started") {
00822                         std::string msg = MSG_Get("PROGRAM_INTRO_MOUNT_START");
00823 #ifdef WIN32
00824                         msg += MSG_Get("PROGRAM_INTRO_MOUNT_WINDOWS");
00825 #else
00826                         msg += MSG_Get("PROGRAM_INTRO_MOUNT_OTHER");
00827 #endif
00828                         msg += MSG_Get("PROGRAM_INTRO_MOUNT_END");
00829 
00830                         new GUI::MessageBox2(getScreen(), 20, 50, 600, std::string("Introduction"), msg);
00831                 } else if (arg == "CD-ROM Support") {
00832                         new GUI::MessageBox2(getScreen(), 20, 50, 600, "Introduction", MSG_Get("PROGRAM_INTRO_CDROM"));
00833                 } else if (arg == "Special Keys") {
00834                         new GUI::MessageBox2(getScreen(), 20, 50, 600, "Introduction", MSG_Get("PROGRAM_INTRO_SPECIAL"));
00835                 } else if (arg == "Save...") {
00836                         new SaveDialog(getScreen(), 90, 100, "Save Configuration...");
00837                 } else if (arg == "Save Language File...") {
00838                         new SaveLangDialog(getScreen(), 90, 100, "Save Language File...");
00839                 } else {
00840                         return ToplevelWindow::actionExecuted(b, arg);
00841                 }
00842         }
00843 };
00844 
00845 /*********************************************************************************************************************/
00846 /* UI control functions */
00847 
00848 static void UI_Execute(GUI::ScreenSDL *screen) {
00849         SDL_Surface *sdlscreen;
00850         SDL_Event event;
00851 
00852         sdlscreen = screen->getSurface();
00853         new ConfigurationWindow(screen, 30, 30, "DOSBox Configuration");
00854 
00855         // event loop
00856         while (running) {
00857                 while (SDL_PollEvent(&event)) {
00858                         if (!screen->event(event)) {
00859                                 if (event.type == SDL_QUIT) running = false;
00860                         }
00861                 }
00862 
00863                 //Selecting keyboard will create a new surface.
00864                 screen->watchTime();
00865                 sdlscreen = screen->getSurface();
00866                 SDL_BlitSurface(background, NULL, sdlscreen, NULL);
00867                 screen->update(screen->getTime());
00868                 SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
00869 
00870                 SDL_Delay(40);
00871         }
00872 }
00873 
00874 static void UI_Select(GUI::ScreenSDL *screen, int select) {
00875         SDL_Surface *sdlscreen = NULL;
00876         Section_line *section2 = NULL;
00877         Section_prop *section = NULL;
00878         Section *sec = NULL;
00879         SDL_Event event;
00880 
00881         sdlscreen = screen->getSurface();
00882         switch (select) {
00883                 case 0:
00884                         new GUI::MessageBox2(screen, 200, 150, 280, "", "");
00885                         running=false;
00886                         break;
00887                 case 1:
00888                         new SaveDialog(screen, 90, 100, "Save Configuration...");
00889                         break;
00890                 case 2:
00891                         sec = control->GetSection("sdl");
00892                         section=static_cast<Section_prop *>(sec); 
00893                         new SectionEditor(screen,50,30,section);
00894                         break;
00895                 case 3:
00896                         sec = control->GetSection("dosbox");
00897                         section=static_cast<Section_prop *>(sec); 
00898                         new SectionEditor(screen,50,30,section);
00899                         break;
00900                 case 4:
00901                         sec = control->GetSection("mixer");
00902                         section=static_cast<Section_prop *>(sec); 
00903                         new SectionEditor(screen,50,30,section);
00904                         break;
00905                 case 5:
00906                         sec = control->GetSection("serial");
00907                         section=static_cast<Section_prop *>(sec); 
00908                         new SectionEditor(screen,50,30,section);
00909                         break;
00910                 case 6:
00911                         sec = control->GetSection("ne2000");
00912                         section=static_cast<Section_prop *>(sec); 
00913                         new SectionEditor(screen,50,30,section);
00914                         break;
00915                 case 7:
00916                         section2 = static_cast<Section_line *>(control->GetSection("autoexec"));
00917                         new AutoexecEditor(screen, 50, 30, section2);
00918                         break;
00919                 case 8:
00920                         sec = control->GetSection("glide");
00921                         section=static_cast<Section_prop *>(sec); 
00922                         new SectionEditor(screen,50,30,section);
00923                         break;
00924                 case 9:
00925                         new SaveLangDialog(screen, 90, 100, "Save Language File...");
00926                         break;
00927                 case 10:
00928                         new ConfigurationWindow(screen, 30, 30, "DOSBox Configuration");
00929                         break;
00930                 case 11:
00931                         sec = control->GetSection("parallel");
00932                         section=static_cast<Section_prop *>(sec);
00933                         new SectionEditor(screen,50,30,section);
00934                         break;
00935                 case 12:
00936                         sec = control->GetSection("printer");
00937                         section=static_cast<Section_prop *>(sec);
00938                         new SectionEditor(screen,50,30,section);
00939                         break;
00940                 case 13:
00941                         sec = control->GetSection("cpu");
00942                         section=static_cast<Section_prop *>(sec);
00943                         new SectionEditor(screen,50,30,section);
00944                         break;
00945                 case 14:
00946                         sec = control->GetSection("dos");
00947                         section=static_cast<Section_prop *>(sec);
00948                         new SectionEditor(screen,50,30,section);
00949                         break;
00950                 case 15:
00951                         sec = control->GetSection("midi");
00952                         section=static_cast<Section_prop *>(sec);
00953                         new SectionEditor(screen,50,30,section);
00954                         break;
00955                 case 16:
00956                         new SetCycles(screen, 90, 100, "Set CPU Cycles...");
00957                         break;
00958                 case 17:
00959                         new SetVsyncrate(screen, 90, 100, "Set vertical syncrate...");
00960                         break;
00961                 case 18:
00962                         new SetLocalSize(screen, 90, 100, "Set Default Local Freesize...");
00963                         break;
00964                 default:
00965                         break;
00966         }
00967 
00968         // event loop
00969         while (running) {
00970                 while (SDL_PollEvent(&event)) {
00971                         if (!screen->event(event)) {
00972                                 if (event.type == SDL_QUIT) running = false;
00973                         }
00974                 }
00975                 SDL_BlitSurface(background, NULL, sdlscreen, NULL);
00976                 screen->update(4);
00977                 SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
00978                 SDL_Delay(20);
00979         }
00980 }
00981 
00982 void GUI_Shortcut(int select) {
00983         if(running) return;
00984 
00985     bool GFX_GetPreventFullscreen(void);
00986 
00987     /* Sorry, the UI screws up 3Dfx OpenGL emulation.
00988      * Remove this block when fixed. */
00989     if (GFX_GetPreventFullscreen()) {
00990         LOG_MSG("GUI is not available while 3Dfx OpenGL emulation is running");
00991         return;
00992     }
00993 
00994 #ifdef WIN32
00995         if(menu.maxwindow) ShowWindow(GetHWND(), SW_RESTORE);
00996 #endif
00997 
00998         shortcut=true;
00999         GUI::ScreenSDL *screen = UI_Startup(NULL);
01000         UI_Select(screen,select);
01001         UI_Shutdown(screen);
01002         shortcut=false;
01003         delete screen;
01004 }
01005 
01006 void GUI_Run(bool pressed) {
01007         if (pressed || running) return;
01008 
01009     bool GFX_GetPreventFullscreen(void);
01010 
01011     /* Sorry, the UI screws up 3Dfx OpenGL emulation.
01012      * Remove this block when fixed. */
01013     if (GFX_GetPreventFullscreen()) {
01014         LOG_MSG("GUI is not available while 3Dfx OpenGL emulation is running");
01015         return;
01016     }
01017 
01018         GUI::ScreenSDL *screen = UI_Startup(NULL);
01019         UI_Execute(screen);
01020         UI_Shutdown(screen);
01021         delete screen;
01022 }
01023 #endif /* !defined(C_SDL2) */