DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/gui/menu.cpp
00001 /*
00002  *  Copyright (C) 2002-2013  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 #include "../dos/drives.h"
00020 #include "control.h"
00021 #include "cpu.h"
00022 #include "render.h"
00023 #include "menu.h"
00024 #include "SDL.h"
00025 #include "SDL_syswm.h"
00026 #include "bios_disk.h"
00027 #include "ide.h" // for ide support
00028 #include "mapper.h"
00029 #include "keyboard.h"
00030 #include "timer.h"
00031 #include "inout.h"
00032 
00033 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* Mac OS X menu handle */
00034 void sdl_hax_nsMenuAddApplicationMenu(void *nsMenu);
00035 void *sdl_hax_nsMenuItemFromTag(void *nsMenu, unsigned int tag);
00036 void sdl_hax_nsMenuItemUpdateFromItem(void *nsMenuItem, DOSBoxMenu::item &item);
00037 void sdl_hax_nsMenuItemSetTag(void *nsMenuItem, unsigned int id);
00038 void sdl_hax_nsMenuItemSetSubmenu(void *nsMenuItem,void *nsMenu);
00039 void sdl_hax_nsMenuAddItem(void *nsMenu,void *nsMenuItem);
00040 void* sdl_hax_nsMenuAllocSeparator(void);
00041 void* sdl_hax_nsMenuAlloc(const char *initWithText);
00042 void sdl_hax_nsMenuRelease(void *nsMenu);
00043 void* sdl_hax_nsMenuItemAlloc(const char *initWithText);
00044 void sdl_hax_nsMenuItemRelease(void *nsMenuItem);
00045 #endif
00046 
00047 const DOSBoxMenu::mapper_event_t DOSBoxMenu::unassigned_mapper_event; /* empty std::string */
00048 
00049 DOSBoxMenu::DOSBoxMenu() {
00050 }
00051 
00052 DOSBoxMenu::~DOSBoxMenu() {
00053     unbuild();
00054     clear_all_menu_items();
00055 }
00056 
00057 DOSBoxMenu::displaylist::displaylist() {
00058 }
00059 
00060 DOSBoxMenu::displaylist::~displaylist() {
00061 }
00062         
00063 bool DOSBoxMenu::item_exists(const std::string &name) {
00064     auto i = name_map.find(name);
00065 
00066     if (i == name_map.end())
00067        return false;
00068 
00069     return item_exists(i->second);
00070 }
00071 
00072 bool DOSBoxMenu::item_exists(const item_handle_t i) {
00073     if (i == unassigned_item_handle)
00074         return false;
00075     else if (i >= master_list.size())
00076         return false;
00077 
00078     item &ret = master_list[(size_t)i];
00079 
00080     if (!ret.status.allocated || ret.master_id == unassigned_item_handle)
00081         return false;
00082     else if (ret.master_id != i)
00083         return false;
00084 
00085     return true;
00086 }
00087 
00088 DOSBoxMenu::item_handle_t DOSBoxMenu::get_item_id_by_name(const std::string &name) {
00089     auto i = name_map.find(name);
00090 
00091     if (i == name_map.end())
00092         return unassigned_item_handle;
00093 
00094     return i->second;
00095 }
00096 
00097 DOSBoxMenu::item& DOSBoxMenu::get_item(const std::string &name) {
00098     item_handle_t handle = get_item_id_by_name(name);
00099 
00100     if (handle == unassigned_item_handle)
00101         E_Exit("DOSBoxMenu::get_item() No such item '%s'",name.c_str());
00102 
00103     return get_item(handle);
00104 }
00105 
00106 DOSBoxMenu::item& DOSBoxMenu::get_item(const item_handle_t i) {
00107     if (i == unassigned_item_handle)
00108         E_Exit("DOSBoxMenu::get_item() attempt to get unassigned handle");
00109     else if (i >= master_list.size())
00110         E_Exit("DOSBoxMenu::get_item() attempt to get out of range handle");
00111 
00112     item &ret = master_list[(size_t)i];
00113 
00114     if (!ret.status.allocated || ret.master_id == unassigned_item_handle)
00115         E_Exit("DOSBoxMenu::get_item() attempt to read unallocated item");
00116     else if (ret.master_id != i)
00117         E_Exit("DOSBoxMenu::get_item() ID mismatch");
00118 
00119     return ret;
00120 }
00121 
00122 DOSBoxMenu::item& DOSBoxMenu::alloc_item(const enum item_type_t type,const std::string &name) {
00123     if (type >= MAX_id)
00124         E_Exit("DOSBoxMenu::alloc_item() illegal menu type value");
00125 
00126     if (name_map.find(name) != name_map.end())
00127         E_Exit("DOSBoxMenu::alloc_item() name '%s' already taken",name.c_str());
00128 
00129     while (master_list_alloc < master_list.size()) {
00130         if (!master_list[master_list_alloc].status.allocated) {
00131             name_map[name] = master_list_alloc;
00132             return master_list[master_list_alloc].allocate(master_list_alloc,type,name);
00133         }
00134 
00135         master_list_alloc++;
00136     }
00137 
00138     if (master_list_alloc >= master_list_limit)
00139         E_Exit("DOSBoxMenu::alloc_item() no slots are free");
00140 
00141     size_t newsize = master_list.size() + (master_list.size() / 2);
00142     if (newsize < 64) newsize = 64;
00143     if (newsize > master_list_limit) newsize = master_list_limit;
00144     master_list.resize(newsize);
00145 
00146     assert(master_list_alloc < master_list.size());
00147 
00148     name_map[name] = master_list_alloc;
00149     return master_list[master_list_alloc].allocate(master_list_alloc,type,name);
00150 }
00151 
00152 void DOSBoxMenu::delete_item(const item_handle_t i) {
00153     if (i == unassigned_item_handle)
00154         E_Exit("DOSBoxMenu::delete_item() attempt to get unassigned handle");
00155     else if (i >= master_list.size())
00156         E_Exit("DOSBoxMenu::delete_item() attempt to get out of range handle");
00157 
00158     {
00159         auto it = name_map.find(master_list[i].name);
00160         if (it != name_map.end()) {
00161             if (it->second != i) E_Exit("DOSBoxMenu::delete_item() master_id mismatch");
00162             name_map.erase(it);
00163         }
00164     }
00165 
00166     master_list[i].deallocate();
00167     master_list_alloc = i;
00168 }
00169 
00170 const char *DOSBoxMenu::TypeToString(const enum item_type_t type) {
00171     switch (type) {
00172         case item_type_id:              return "Item";
00173         case submenu_type_id:           return "Submenu";
00174         case separator_type_id:         return "Separator";
00175         case vseparator_type_id:        return "VSeparator";
00176         default:                        break;
00177     };
00178 
00179     return "";
00180 }
00181 
00182 void DOSBoxMenu::dump_log_displaylist(DOSBoxMenu::displaylist &ls, unsigned int indent) {
00183     std::string prep;
00184 
00185     for (unsigned int i=0;i < indent;i++)
00186         prep += "+ ";
00187 
00188     for (auto &id : ls.disp_list) {
00189         DOSBoxMenu::item &item = get_item(id);
00190 
00191         if (!item.is_allocated()) {
00192             LOG_MSG("%s (NOT ALLOCATED!!!)",prep.c_str());
00193             continue;
00194         }
00195 
00196         LOG_MSG("%sid=%u type=\"%s\" name=\"%s\" text=\"%s\"",
00197             prep.c_str(),
00198             (unsigned int)item.master_id,
00199             TypeToString(item.type),
00200             item.name.c_str(),
00201             item.text.c_str());
00202 
00203         if (item.type == submenu_type_id)
00204             dump_log_displaylist(item.display_list, indent+1);
00205     }
00206 }
00207 
00208 void DOSBoxMenu::dump_log_debug(void) {
00209     LOG_MSG("Menu dump log (%p)",(void*)this);
00210     LOG_MSG("---- Master list ----");
00211     for (auto &id : master_list) {
00212         if (id.is_allocated()) {
00213             LOG_MSG("+ id=%u type=\"%s\" name=\"%s\" text=\"%s\" shortcut=\"%s\" desc=\"%s\"",
00214                 (unsigned int)id.master_id,
00215                 TypeToString(id.type),
00216                 id.name.c_str(),
00217                 id.text.c_str(),
00218                 id.shortcut_text.c_str(),
00219                 id.description.c_str());
00220 
00221             if (!id.get_mapper_event().empty())
00222                 LOG_MSG("+ + mapper_event=\"%s\"",id.get_mapper_event().c_str());
00223         }
00224     }
00225     LOG_MSG("---- display list ----");
00226     dump_log_displaylist(display_list, 1);
00227 }
00228 
00229 void DOSBoxMenu::clear_all_menu_items(void) {
00230     for (auto &id : master_list) {
00231         if (id.is_allocated())
00232             id.deallocate();
00233     }
00234     master_list_alloc = 0;
00235     master_list.clear();
00236     name_map.clear();
00237 }
00238 
00239 DOSBoxMenu::item::item() {
00240 }
00241 
00242 DOSBoxMenu::item::~item() {
00243 }
00244 
00245 DOSBoxMenu::item &DOSBoxMenu::item::allocate(const item_handle_t id,const enum item_type_t new_type,const std::string &new_name) {
00246     if (master_id != unassigned_item_handle || status.allocated)
00247         E_Exit("DOSBoxMenu::item::allocate() called on item already allocated");
00248 
00249     status.allocated = 1;
00250     name = new_name;
00251     type = new_type;
00252     master_id = id;
00253     return *this;
00254 }
00255 
00256 void DOSBoxMenu::item::deallocate(void) {
00257     if (master_id == unassigned_item_handle || !status.allocated)
00258         E_Exit("DOSBoxMenu::item::deallocate() called on item already deallocated");
00259 
00260     master_id = unassigned_item_handle;
00261     status.allocated = 0;
00262     status.changed = 1;
00263     shortcut_text.clear();
00264     description.clear();
00265     text.clear();
00266     name.clear();
00267 }
00268 
00269 void DOSBoxMenu::displaylist_append(displaylist &ls,const DOSBoxMenu::item_handle_t item_id) {
00270     DOSBoxMenu::item &item = get_item(item_id);
00271 
00272     if (item.status.in_use)
00273         E_Exit("DOSBoxMenu::displaylist_append() item already in use");
00274 
00275     ls.disp_list.push_back(item.master_id);
00276     item.status.in_use = true;
00277     ls.order_changed = true;
00278 }
00279 
00280 void DOSBoxMenu::displaylist_clear(DOSBoxMenu::displaylist &ls) {
00281     for (auto &id : ls.disp_list) {
00282         if (id != DOSBoxMenu::unassigned_item_handle) {
00283             id = DOSBoxMenu::unassigned_item_handle;
00284         }
00285     }
00286 
00287     ls.disp_list.clear();
00288     ls.items_changed = true;
00289     ls.order_changed = true;
00290 }
00291 
00292 void DOSBoxMenu::rebuild(void) {
00293 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU /* Windows menu handle */
00294     if (winMenu == NULL) {
00295         if (!winMenuInit())
00296             return;
00297     }
00298 #endif
00299 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* Mac OS X menu handle */
00300     if (nsMenu == NULL) {
00301         if (!nsMenuInit())
00302             return;
00303     }
00304 #endif
00305 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
00306     layoutMenu();
00307 #endif
00308 }
00309 
00310 void DOSBoxMenu::unbuild(void) {
00311 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU /* Windows menu handle */
00312     winMenuDestroy();
00313 #endif
00314 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* Mac OS X menu handle */
00315     nsMenuDestroy();
00316 #endif
00317 }
00318 
00319 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* Mac OS X menu handle */
00320 bool DOSBoxMenu::mainMenuAction(unsigned int id) {
00321     if (id < nsMenuMinimumID) return false;
00322     id -= nsMenuMinimumID;
00323 
00324     if (id >= master_list.size()) return false;
00325 
00326     item &item = master_list[id];
00327     if (!item.status.allocated || item.master_id == unassigned_item_handle) return false;
00328 
00329     dispatchItemCommand(item);
00330     return true;
00331 }
00332 
00333 void DOSBoxMenu::item::nsAppendMenu(void* parent_nsMenu) {
00334     if (type == separator_type_id) {
00335         sdl_hax_nsMenuAddItem(parent_nsMenu, sdl_hax_nsMenuAllocSeparator());
00336     }
00337     else if (type == vseparator_type_id) {
00338         sdl_hax_nsMenuAddItem(parent_nsMenu, sdl_hax_nsMenuAllocSeparator());
00339     }
00340     else if (type == submenu_type_id) {
00341         if (nsMenu != NULL) {
00342                 // NTS: You have to make a menu ITEM who's submenu is the submenu object
00343                 nsMenuItem = sdl_hax_nsMenuItemAlloc(text.c_str());
00344 
00345                 sdl_hax_nsMenuItemSetTag(nsMenuItem, master_id + nsMenuMinimumID);
00346                 sdl_hax_nsMenuItemSetSubmenu(nsMenuItem, nsMenu);
00347                 sdl_hax_nsMenuAddItem(parent_nsMenu, nsMenuItem);
00348                 sdl_hax_nsMenuItemUpdateFromItem(nsMenuItem, *this);
00349                 sdl_hax_nsMenuItemRelease(nsMenuItem);
00350         }
00351     }
00352     else if (type == item_type_id) {
00353         nsMenuItem = sdl_hax_nsMenuItemAlloc(text.c_str());
00354 
00355         sdl_hax_nsMenuItemSetTag(nsMenuItem, master_id + nsMenuMinimumID);
00356         sdl_hax_nsMenuAddItem(parent_nsMenu, nsMenuItem);
00357         sdl_hax_nsMenuItemUpdateFromItem(nsMenuItem, *this);
00358         sdl_hax_nsMenuItemRelease(nsMenuItem);
00359     }
00360 }
00361 
00362 bool DOSBoxMenu::nsMenuSubInit(DOSBoxMenu::item &p_item) {
00363     if (p_item.nsMenu == NULL) {
00364         p_item.nsMenu = sdl_hax_nsMenuAlloc(p_item.get_text().c_str());
00365         if (p_item.nsMenu != NULL) {
00366             for (auto id : p_item.display_list.disp_list) {
00367                 DOSBoxMenu::item &item = get_item(id);
00368 
00369                 /* if a submenu, make the submenu */
00370                 if (item.type == submenu_type_id) {
00371                     item.parent_id = p_item.master_id;
00372                     nsMenuSubInit(item);
00373                 }
00374 
00375                 item.nsAppendMenu(p_item.nsMenu);
00376             }
00377         }
00378     }
00379 
00380     return true;
00381 }
00382 
00383 bool DOSBoxMenu::nsMenuInit(void) {
00384     if (nsMenu == NULL) {
00385         if ((nsMenu = sdl_hax_nsMenuAlloc("")) == NULL)
00386             return false;
00387 
00388         /* For whatever reason, Mac OS X will always make the first top level menu
00389            the Application menu and will put the name of the app there NO MATTER WHAT */
00390         sdl_hax_nsMenuAddApplicationMenu(nsMenu);
00391 
00392         /* top level */
00393         for (auto id : display_list.disp_list) {
00394             DOSBoxMenu::item &item = get_item(id);
00395 
00396             /* if a submenu, make the submenu */
00397             if (item.type == submenu_type_id) {
00398                 item.parent_id = unassigned_item_handle;
00399                 nsMenuSubInit(item);
00400             }
00401 
00402             item.nsAppendMenu(nsMenu);
00403         }
00404 
00405         /* release our handle on the nsMenus. Mac OS X will keep them alive with it's
00406            reference until the menu is destroyed at which point all items and submenus
00407            will be automatically destroyed */
00408         for (auto &id : master_list) {
00409                 if (id.nsMenu != NULL) {
00410                     sdl_hax_nsMenuRelease(id.nsMenu);
00411                     id.nsMenu = NULL;
00412                 }
00413         }
00414     }
00415 
00416     return true;
00417 }
00418 
00419 void DOSBoxMenu::nsMenuDestroy(void) {
00420     if (nsMenu != NULL) {
00421         sdl_hax_nsMenuRelease(nsMenu);
00422         nsMenu = NULL;
00423     }
00424 }
00425 
00426 void* DOSBoxMenu::getNsMenu(void) const {
00427         return nsMenu;
00428 }
00429 #endif
00430 
00431 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU /* Windows menu handle */
00432 std::string DOSBoxMenu::item::winConstructMenuText(void) {
00433     std::string r;
00434 
00435     /* copy text, converting '&' to '&&' for Windows.
00436      * TODO: Use accelerator to place '&' for underline */
00437     for (auto i=text.begin();i!=text.end();i++) {
00438         char c = *i;
00439 
00440         if (c == '&') {
00441             r += "&&";
00442         }
00443         else {
00444             r += c;
00445         }
00446     }
00447 
00448     /* then the shortcut text */
00449     if (!shortcut_text.empty()) {
00450         r += "\t";
00451 
00452         for (auto i=shortcut_text.begin();i!=shortcut_text.end();i++) {
00453             char c = *i;
00454 
00455             if (c == '&') {
00456                 r += "&&";
00457             }
00458             else {
00459                 r += c;
00460             }
00461         }
00462     }
00463 
00464     return r;
00465 }
00466 
00467 void DOSBoxMenu::item::winAppendMenu(HMENU handle) {
00468     if (type == separator_type_id) {
00469         AppendMenu(handle, MF_SEPARATOR, 0, NULL);
00470     }
00471     else if (type == vseparator_type_id) {
00472         AppendMenu(handle, MF_MENUBREAK, 0, NULL);
00473     }
00474     else if (type == submenu_type_id) {
00475         if (winMenu != NULL)
00476             AppendMenu(handle, MF_POPUP | MF_STRING, (uintptr_t)winMenu, winConstructMenuText().c_str());
00477     }
00478     else if (type == item_type_id) {
00479         unsigned int attr = MF_STRING;
00480 
00481         attr |= (status.checked) ? MF_CHECKED : MF_UNCHECKED;
00482         attr |= (status.enabled) ? MF_ENABLED : (MF_DISABLED | MF_GRAYED);
00483 
00484         AppendMenu(handle, attr, (uintptr_t)(master_id + winMenuMinimumID), winConstructMenuText().c_str());
00485     }
00486 }
00487 
00488 bool DOSBoxMenu::winMenuSubInit(DOSBoxMenu::item &p_item) {
00489     if (p_item.winMenu == NULL) {
00490         p_item.winMenu = CreatePopupMenu();
00491         if (p_item.winMenu != NULL) {
00492             for (auto id : p_item.display_list.disp_list) {
00493                 DOSBoxMenu::item &item = get_item(id);
00494 
00495                 /* if a submenu, make the submenu */
00496                 if (item.type == submenu_type_id) {
00497                     item.parent_id = p_item.master_id;
00498                     winMenuSubInit(item);
00499                 }
00500 
00501                 item.winAppendMenu(p_item.winMenu);
00502             }
00503         }
00504     }
00505 
00506     return true;
00507 }
00508 
00509 bool DOSBoxMenu::winMenuInit(void) {
00510     if (winMenu == NULL) {
00511         winMenu = CreateMenu();
00512         if (winMenu == NULL) return false;
00513 
00514         /* top level */
00515         for (auto id : display_list.disp_list) {
00516             DOSBoxMenu::item &item = get_item(id);
00517 
00518             /* if a submenu, make the submenu */
00519             if (item.type == submenu_type_id) {
00520                 item.parent_id = unassigned_item_handle;
00521                 winMenuSubInit(item);
00522             }
00523 
00524             item.winAppendMenu(winMenu);
00525         }
00526     }
00527 
00528     return true;
00529 }
00530 
00531 void DOSBoxMenu::winMenuDestroy(void) {
00532     if (winMenu != NULL) {
00533         /* go through all menu items, and clear the menu handle */
00534         for (auto &id : master_list)
00535             id.winMenu = NULL;
00536 
00537         /* destroy the menu.
00538          * By MSDN docs it destroys submenus automatically */
00539         DestroyMenu(winMenu);
00540         winMenu = NULL;
00541     }
00542 }
00543 
00544 HMENU DOSBoxMenu::getWinMenu(void) const {
00545     return winMenu;
00546 }
00547 
00548 /* call this from WM_COMMAND */
00549 bool DOSBoxMenu::mainMenuWM_COMMAND(unsigned int id) {
00550     if (id < winMenuMinimumID) return false;
00551     id -= winMenuMinimumID;
00552 
00553     if (id >= master_list.size()) return false;
00554 
00555     item &item = master_list[id];
00556     if (!item.status.allocated || item.master_id == unassigned_item_handle) return false;
00557 
00558     dispatchItemCommand(item);
00559     return true;
00560 }
00561 #endif
00562 
00563 void MAPPER_TriggerEventByName(const std::string name);
00564 
00565 void DOSBoxMenu::item::refresh_item(DOSBoxMenu &menu) {
00566     (void)menu;//POSSIBLY UNUSED
00567 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU /* Windows menu handle */
00568     if (menu.winMenu != NULL && status.in_use && status.changed) {
00569         HMENU phandle = NULL;
00570 
00571         if (parent_id != unassigned_item_handle)
00572             phandle = menu.get_item(parent_id).winMenu;
00573         else
00574             phandle = menu.winMenu;
00575 
00576                 if (phandle != NULL) {
00577                         if (type == separator_type_id) {
00578                                 /* none */
00579                         }
00580                         else if (type == vseparator_type_id) {
00581                                 /* none */
00582                         }
00583                         else if (type == submenu_type_id) {
00584                                 /* TODO: Can't change by ID, have to change by position */
00585                         }
00586                         else if (type == item_type_id) {
00587                                 unsigned int attr = MF_STRING;
00588 
00589                                 attr |= (status.checked) ? MF_CHECKED : MF_UNCHECKED;
00590                                 attr |= (status.enabled) ? MF_ENABLED : (MF_DISABLED | MF_GRAYED);
00591 
00592                                 ModifyMenu(phandle, (uintptr_t)(master_id + winMenuMinimumID),
00593                                         attr | MF_BYCOMMAND, (uintptr_t)(master_id + winMenuMinimumID),
00594                                         winConstructMenuText().c_str());
00595                         }
00596                 }
00597     }
00598 
00599     status.changed = false;
00600 #endif
00601 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* Mac OS X menu handle */
00602     if (nsMenuItem != NULL)
00603             sdl_hax_nsMenuItemUpdateFromItem(nsMenuItem, *this);
00604 #endif
00605 }
00606 
00607 void DOSBoxMenu::dispatchItemCommand(item &item) {
00608     if (item.callback_func)
00609         item.callback_func(this,&item);
00610 
00611     if (item.mapper_event != unassigned_mapper_event)
00612         MAPPER_TriggerEventByName(item.mapper_event);
00613 }
00614 
00615 /* this is THE menu */
00616 DOSBoxMenu mainMenu;
00617 
00618 /* top level menu ("") */
00619 static const char *def_menu__toplevel[] = {
00620     "MainMenu",
00621     "CpuMenu",
00622     "VideoMenu",
00623     "SoundMenu",
00624     "DOSMenu",
00625 #if !defined(C_EMSCRIPTEN)
00626     "CaptureMenu",
00627 #endif
00628     NULL
00629 };
00630 
00631 /* main menu ("MainMenu") */
00632 static const char *def_menu_main[] = {
00633     "mapper_mapper",
00634     "mapper_gui",
00635         "--",
00636     "MainSendKey",
00637         "--",
00638 #if !defined(C_EMSCRIPTEN)
00639         "wait_on_error",
00640 #endif
00641     "showdetails",
00642 #if C_DEBUG
00643         "--",
00644         "mapper_debugger",
00645 #endif
00646 #if !defined(MACOSX) && !defined(LINUX) && !defined(HX_DOS) && !defined(C_EMSCRIPTEN)
00647     "show_console",
00648 #endif
00649     "--",
00650     "mapper_capmouse",
00651         "auto_lock_mouse",
00652 #if !defined(C_EMSCRIPTEN)//FIXME: Reset causes problems with Emscripten
00653         "--",
00654         "mapper_pause",
00655         "mapper_pauseints",
00656 #endif
00657 #if !defined(C_EMSCRIPTEN)//FIXME: Reset causes problems with Emscripten
00658     "--",
00659     "mapper_reset",
00660 #endif
00661 #if !defined(C_EMSCRIPTEN)//FIXME: Shutdown causes problems with Emscripten
00662         "--",
00663 #endif
00664 #if !defined(HX_DOS) && !defined(C_EMSCRIPTEN)
00665         "mapper_restart",
00666     "--",
00667 #endif
00668 #if !defined(C_EMSCRIPTEN)//FIXME: Shutdown causes problems with Emscripten
00669     "mapper_shutdown",
00670 #endif
00671     NULL
00672 };
00673 
00674 /* main -> send key menu ("MenuSendKey") */
00675 static const char *def_menu_main_sendkey[] = {
00676     "sendkey_ctrlesc",
00677     "sendkey_alttab",
00678     "sendkey_winlogo",
00679     "sendkey_winmenu",
00680     "--",
00681     "sendkey_cad",
00682     NULL
00683 };
00684 
00685 /* cpu -> core menu ("CpuCoreMenu") */
00686 static const char *def_menu_cpu_core[] = {
00687     "mapper_cycauto",
00688     "--",
00689     "mapper_normal",
00690 #if !defined(C_EMSCRIPTEN)//FIXME: Shutdown causes problems with Emscripten
00691     "mapper_full",
00692     "mapper_simple",
00693 #endif
00694 #if (C_DYNAMIC_X86)
00695     "mapper_dynamic",
00696 #endif
00697     NULL
00698 };
00699 
00700 /* cpu -> type menu ("CpuTypeMenu") */
00701 static const char *def_menu_cpu_type[] = {
00702     "cputype_auto",
00703     "--",
00704     "cputype_8086",
00705     "cputype_8086_prefetch",
00706     "cputype_80186",
00707     "cputype_80186_prefetch",
00708     "cputype_286",
00709     "cputype_286_prefetch",
00710     "cputype_386",
00711     "cputype_386_prefetch",
00712     "cputype_486old",
00713     "cputype_486old_prefetch",
00714     "cputype_486",
00715     "cputype_486_prefetch",
00716     "cputype_pentium",
00717     "cputype_pentium_mmx",
00718     "cputype_ppro_slow",
00719     NULL
00720 };
00721 
00722 /* cpu menu ("CpuMenu") */
00723 static const char *def_menu_cpu[] = {
00724     "mapper_speedlock2", /* NTS: "mapper_speedlock" doesn't work for a menu item because it requires holding the key */
00725     "--",
00726     "mapper_cycleup",
00727     "mapper_cycledown",
00728 #if !defined(C_SDL2)
00729         "mapper_editcycles",
00730 #endif
00731     "--",
00732     "CpuCoreMenu",
00733     "CpuTypeMenu",
00734     NULL
00735 };
00736 
00737 /* video frameskip menu ("VideoFrameskipMenu") */
00738 static const char *def_menu_video_frameskip[] = {
00739     "frameskip_0",
00740     "frameskip_1",
00741     "frameskip_2",
00742     "frameskip_3",
00743     "frameskip_4",
00744     "frameskip_5",
00745     "frameskip_6",
00746     "frameskip_7",
00747     "frameskip_8",
00748     "frameskip_9",
00749     "frameskip_10",
00750     NULL
00751 };
00752 
00753 /* video scaler menu ("VideoScalerMenu") */
00754 static const char *def_menu_video_scaler[] = {
00755     NULL
00756 };
00757 
00758 /* video output menu ("VideoOutputMenu") */
00759 static const char *def_menu_video_output[] = {
00760     "output_surface",
00761 #if !defined(C_SDL2) && !defined(HX_DOS)
00762 # if (HAVE_D3D9_H) && defined(WIN32)
00763     "output_direct3d",
00764 # endif
00765 # if (C_OPENGL)
00766     "output_opengl",
00767     "output_openglnb",
00768 # endif
00769 #endif
00770     NULL
00771 };
00772 
00773 /* video vsync menu ("VideoVsyncMenu") */
00774 static const char *def_menu_video_vsync[] = {
00775 #if !defined(C_SDL2)
00776     "vsync_on",
00777     "vsync_force",
00778     "vsync_host",
00779     "vsync_off",
00780     "--",
00781     "vsync_set_syncrate",
00782 #endif
00783     NULL
00784 };
00785 
00786 /* video overscan menu ("VideoOverscanMenu") */
00787 static const char *def_menu_video_overscan[] = {
00788     "overscan_0",
00789     "overscan_1",
00790     "overscan_2",
00791     "overscan_3",
00792     "overscan_4",
00793     "overscan_5",
00794     "overscan_6",
00795     "overscan_7",
00796     "overscan_8",
00797     "overscan_9",
00798     "overscan_10",
00799     NULL
00800 };
00801 
00802 /* video output menu ("VideoCompatMenu") */
00803 static const char *def_menu_video_compat[] = {
00804     "vga_9widetext",
00805     "doublescan",
00806     NULL
00807 };
00808 
00809 /* video output menu ("VideoPC98Menu") */
00810 static const char *def_menu_video_pc98[] = {
00811     "pc98_5mhz_gdc",
00812     "pc98_allow_200scanline",
00813     "pc98_allow_4partitions",
00814     "--",
00815     "pc98_enable_egc",
00816     "pc98_enable_grcg",
00817     "pc98_enable_analog",
00818     "pc98_enable_188user",
00819     "--",
00820     "pc98_clear_text",
00821     "pc98_clear_graphics",
00822     NULL
00823 };
00824 
00825 /* video menu ("VideoMenu") */
00826 static const char *def_menu_video[] = {
00827 #if !defined(C_SDL2)
00828         "mapper_aspratio",
00829         "--",
00830 #endif
00831 #if !defined(C_SDL2) && !defined(HX_DOS)
00832         "mapper_fullscr",
00833         "--",
00834 #endif
00835 #if !defined(C_SDL2) && !defined(HX_DOS)
00836     "alwaysontop",
00837 #endif
00838 #if !defined(C_SDL2)
00839     "doublebuf",
00840         "--",
00841 #endif
00842 #ifndef MACOSX
00843     "mapper_togmenu",
00844 # if !defined(C_SDL2) && !defined(HX_DOS)
00845         "--",
00846 # endif
00847 #endif
00848 #if !defined(C_SDL2) && !defined(HX_DOS)
00849         "mapper_resetsize",
00850 #endif
00851         "--",
00852         "VideoFrameskipMenu",
00853         "--",
00854     "scaler_forced",
00855     "VideoScalerMenu",
00856     "VideoOutputMenu",
00857 #if !defined(C_SDL2)
00858     "VideoVsyncMenu",
00859 #endif
00860     "VideoOverscanMenu",
00861     "VideoCompatMenu",
00862     "VideoPC98Menu",
00863     NULL
00864 };
00865 
00866 /* DOS menu ("DOSMenu") */
00867 static const char *def_menu_dos[] = {
00868     "DOSMouseMenu",
00869     "--",
00870     "DOSPC98Menu",
00871     NULL
00872 };
00873 
00874 /* DOS mouse menu ("DOSMouseMenu") */
00875 static const char *def_menu_dos_mouse[] = {
00876     "dos_mouse_enable_int33",
00877     "dos_mouse_y_axis_reverse",
00878 #if !defined(C_SDL2)
00879     "--",
00880     "dos_mouse_sensitivity",
00881 #endif
00882     NULL
00883 };
00884 
00885 /* DOS pc-98 menu ("DOSPC98Menu") */
00886 static const char *def_menu_dos_pc98[] = {
00887     "dos_pc98_pit_4mhz",
00888     "dos_pc98_pit_5mhz",
00889     NULL
00890 };
00891 
00892 /* sound menu ("SoundMenu") */
00893 static const char *def_menu_sound[] = {
00894     "mapper_volup",
00895     "mapper_voldown",
00896     "--",
00897     "mixer_mute",
00898     "mixer_swapstereo",
00899     NULL
00900 };
00901 
00902 /* capture menu ("CaptureMenu") */
00903 static const char *def_menu_capture[] = {
00904 #if defined(C_SSHOT)
00905     "mapper_scrshot",
00906     "--",
00907 #endif
00908 #if !defined(C_EMSCRIPTEN)
00909 # if (C_SSHOT)
00910     "CaptureFormatMenu",
00911     "--",
00912 # endif
00913     "mapper_video",
00914     "mapper_recwave",
00915     "mapper_recmtwave",
00916     "mapper_caprawopl",
00917     "mapper_caprawmidi",
00918 #endif
00919     NULL
00920 };
00921 
00922 #if !defined(C_EMSCRIPTEN)
00923 # if (C_SSHOT)
00924 /* capture format menu ("CaptureFormatMenu") */
00925 static const char *def_menu_capture_format[] = {
00926     "capture_fmt_avi_zmbv",
00927 #  if (C_AVCODEC)
00928     "capture_fmt_mpegts_h264",
00929 #  endif
00930     NULL
00931 };
00932 # endif
00933 #endif
00934 
00935 static DOSBoxMenu::item_handle_t separator_alloc = 0;
00936 static std::vector<DOSBoxMenu::item_handle_t> separators;
00937 
00938 static std::string separator_id(const DOSBoxMenu::item_handle_t r) {
00939     char tmp[32];
00940 
00941     sprintf(tmp,"%u",(unsigned int)r);
00942     return std::string("_separator_") + std::string(tmp);
00943 }
00944 
00945 static DOSBoxMenu::item_handle_t separator_get(const DOSBoxMenu::item_type_t t=DOSBoxMenu::separator_type_id) {
00946     assert(separator_alloc <= separators.size());
00947     if (separator_alloc == separators.size()) {
00948         DOSBoxMenu::item &nitem = mainMenu.alloc_item(t, separator_id(separator_alloc));
00949         separators.push_back(nitem.get_master_id());
00950     }
00951 
00952     assert(separator_alloc < separators.size());
00953     mainMenu.get_item(separators[separator_alloc]).set_type(t);
00954     return separators[separator_alloc++];
00955 }
00956 
00957 void ConstructSubMenu(DOSBoxMenu::item_handle_t item_id, const char * const * list) {
00958     for (size_t i=0;list[i] != NULL;i++) {
00959         const char *ref = list[i];
00960 
00961         /* NTS: This code calls mainMenu.get_item(item_id) every iteration.
00962          *      
00963          *      This seemingly inefficient method of populating the display
00964          *      list is REQUIRED because DOSBoxMenu::item& is a reference
00965          *      to a std::vector, and the reference becomes invalid when
00966          *      the vector reallocates to accomodate more entries.
00967          *
00968          *      Holding onto one reference for the entire loop risks a
00969          *      segfault (use after free) bug if the vector should reallocate
00970          *      in separator_get() -> alloc_item()
00971          *
00972          *      Since get_item(item_id) is literally just a constant time
00973          *      array lookup, this is not very inefficient at all. */
00974 
00975         if (!strcmp(ref,"--")) {
00976             mainMenu.displaylist_append(
00977                 mainMenu.get_item(item_id).display_list, separator_get(DOSBoxMenu::separator_type_id));
00978         }
00979         else if (mainMenu.item_exists(ref)) {
00980             mainMenu.displaylist_append(
00981                 mainMenu.get_item(item_id).display_list, mainMenu.get_item_id_by_name(ref));
00982         }
00983     }
00984 }
00985 
00986 extern const char *scaler_menu_opts[][2];
00987 
00988 void ConstructMenu(void) {
00989     mainMenu.displaylist_clear(mainMenu.display_list);
00990     separator_alloc = 0;
00991 
00992     /* top level */
00993     for (size_t i=0;def_menu__toplevel[i] != NULL;i++)
00994         mainMenu.displaylist_append(
00995             mainMenu.display_list,
00996             mainMenu.get_item_id_by_name(def_menu__toplevel[i]));
00997 
00998     /* main menu */
00999     ConstructSubMenu(mainMenu.get_item("MainMenu").get_master_id(), def_menu_main);
01000 
01001     /* main sendkey menu */
01002     ConstructSubMenu(mainMenu.get_item("MainSendKey").get_master_id(), def_menu_main_sendkey);
01003 
01004     /* cpu menu */
01005     ConstructSubMenu(mainMenu.get_item("CpuMenu").get_master_id(), def_menu_cpu);
01006 
01007     /* cpu core menu */
01008     ConstructSubMenu(mainMenu.get_item("CpuCoreMenu").get_master_id(), def_menu_cpu_core);
01009 
01010     /* cpu type menu */
01011     ConstructSubMenu(mainMenu.get_item("CpuTypeMenu").get_master_id(), def_menu_cpu_type);
01012 
01013     /* video menu */
01014     ConstructSubMenu(mainMenu.get_item("VideoMenu").get_master_id(), def_menu_video);
01015 
01016     /* video frameskip menu */
01017     ConstructSubMenu(mainMenu.get_item("VideoFrameskipMenu").get_master_id(), def_menu_video_frameskip);
01018 
01019     /* video scaler menu */
01020     ConstructSubMenu(mainMenu.get_item("VideoScalerMenu").get_master_id(), def_menu_video_scaler);
01021     {
01022         size_t count=0;
01023 
01024         for (size_t i=0;scaler_menu_opts[i][0] != NULL;i++) {
01025             const std::string name = std::string("scaler_set_") + scaler_menu_opts[i][0];
01026 
01027             if (mainMenu.item_exists(name)) {
01028                 mainMenu.displaylist_append(
01029                     mainMenu.get_item("VideoScalerMenu").display_list,
01030                     mainMenu.get_item_id_by_name(name));
01031 
01032 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
01033                 if ((count % 15) == 14) {
01034                     mainMenu.displaylist_append(
01035                         mainMenu.get_item("VideoScalerMenu").display_list,
01036                         separator_get(DOSBoxMenu::vseparator_type_id));
01037                 }
01038 #endif
01039 
01040                 count++;
01041             }
01042         }
01043     }
01044 
01045     /* video output menu */
01046     ConstructSubMenu(mainMenu.get_item("VideoOutputMenu").get_master_id(), def_menu_video_output);
01047 
01048     /* video vsync menu */
01049     ConstructSubMenu(mainMenu.get_item("VideoVsyncMenu").get_master_id(), def_menu_video_vsync);
01050 
01051     /* video overscan menu */
01052     ConstructSubMenu(mainMenu.get_item("VideoOverscanMenu").get_master_id(), def_menu_video_overscan);
01053 
01054     /* video compat menu */
01055     ConstructSubMenu(mainMenu.get_item("VideoCompatMenu").get_master_id(), def_menu_video_compat);
01056 
01057     /* video PC-98 menu */
01058     ConstructSubMenu(mainMenu.get_item("VideoPC98Menu").get_master_id(), def_menu_video_pc98);
01059 
01060     /* sound menu */
01061     ConstructSubMenu(mainMenu.get_item("SoundMenu").get_master_id(), def_menu_sound);
01062 
01063     /* DOS menu */
01064     ConstructSubMenu(mainMenu.get_item("DOSMenu").get_master_id(), def_menu_dos);
01065 
01066     /* DOS mouse menu */
01067     ConstructSubMenu(mainMenu.get_item("DOSMouseMenu").get_master_id(), def_menu_dos_mouse);
01068 
01069     /* DOS PC-98 menu */
01070     ConstructSubMenu(mainMenu.get_item("DOSPC98Menu").get_master_id(), def_menu_dos_pc98);
01071 
01072 #if !defined(C_EMSCRIPTEN)
01073     /* capture menu */
01074     ConstructSubMenu(mainMenu.get_item("CaptureMenu").get_master_id(), def_menu_capture);
01075 #endif
01076 
01077 #if !defined(C_EMSCRIPTEN)
01078 # if (C_SSHOT)
01079     /* capture format menu */
01080     ConstructSubMenu(mainMenu.get_item("CaptureFormatMenu").get_master_id(), def_menu_capture_format);
01081 # endif
01082 #endif
01083 }
01084 
01085 bool MENU_SetBool(std::string secname, std::string value) {
01086         Section_prop * sec = static_cast<Section_prop *>(control->GetSection(secname));
01087         if(sec) SetVal(secname, value, sec->Get_bool(value) ? "false" : "true");
01088         return sec->Get_bool(value);
01089 }
01090 
01091 void RENDER_CallBack( GFX_CallBackFunctions_t function );
01092 
01093 // Sets the scaler 'forced' flag.
01094 void SetScaleForced(bool forced)
01095 {
01096         render.scale.forced = forced;
01097         RENDER_CallBack(GFX_CallBackReset);
01098     mainMenu.get_item("scaler_forced").check(render.scale.forced).refresh_item(mainMenu);
01099 }
01100 
01101 // Sets the scaler to use.
01102 void SetScaler(scalerOperation_t op, Bitu size, std::string prefix)
01103 {
01104         auto value = prefix + (render.scale.forced ? " forced" : "");
01105         SetVal("render", "scaler", value);
01106         render.scale.size = size;
01107         render.scale.op = op;
01108         RENDER_CallBack(GFX_CallBackReset);
01109 }
01110 
01111 extern int NonUserResizeCounter;
01112 
01113 extern bool dos_kernel_disabled;
01114 extern bool dos_shell_running_program;
01115 
01116 bool GFX_GetPreventFullscreen(void);
01117 void DOSBox_ShowConsole();
01118 
01119 #if !defined(C_SDL2)
01120 void GUI_ResetResize(bool pressed);
01121 #endif
01122 
01123 std::string MSCDEX_Output(int num) {
01124         std::string MSCDEX_MSG = "GUI: MSCDEX ";
01125         std::string MSCDEX_MSG_Failure = "Failure: ";
01126         switch (num) {
01127         case 0: return MSCDEX_MSG + "installed";
01128         case 1: return MSCDEX_MSG + MSCDEX_MSG_Failure + "Drive-letters of multiple CDRom-drives have to be continuous.";
01129         case 2: return MSCDEX_MSG + MSCDEX_MSG_Failure + "Not yet supported.";
01130         case 3: return MSCDEX_MSG + MSCDEX_MSG_Failure + "Path not valid.";
01131         case 4: return MSCDEX_MSG + MSCDEX_MSG_Failure + "Too many CDRom-drives (max: 5). MSCDEX Installation failed";
01132         case 5: return MSCDEX_MSG + "Mounted subdirectory: limited support.";
01133         case 6: return MSCDEX_MSG + MSCDEX_MSG_Failure + "Unknown error";
01134         default: return 0;
01135         }
01136 }
01137 
01138 static std::string not_recommended = "Mounting C:\\ is NOT recommended.\nDo you want to continue?";
01139 
01140 void SetVal(const std::string secname, std::string preval, const std::string val) {
01141         if(preval=="keyboardlayout" && !dos_kernel_disabled) {
01142                 DOS_MCB mcb(dos.psp()-1);
01143                 static char name[9];
01144                 mcb.GetFileName(name);
01145                 if (strlen(name)) {
01146                         LOG_MSG("GUI: Exit %s running in DOSBox, and then try again.",name);
01147                         return;
01148                 }
01149         }
01150         Section* sec = control->GetSection(secname);
01151         if(sec) {
01152                 std::string real_val=preval+"="+val;
01153                 sec->HandleInputline(real_val);
01154         }
01155 }
01156 
01157 MENU_Block menu;
01158 
01159 unsigned int hdd_defsize=16000;
01160 char hdd_size[20]="";
01161 
01162 bool DOSBox_isMenuVisible(void) {
01163     return menu.toggle;
01164 }
01165 
01166 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01167 extern "C" void SDL1_hax_SetMenu(HMENU menu);
01168 extern HMENU MainMenu;
01169 #endif
01170 
01171 void DOSBox_SetMenu(void) {
01172 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
01173     /* FIXME: SDL menu is NOT AVAILABLE if OpenGL surface is used */
01174     {
01175         menu.toggle=true;
01176         mainMenu.showMenu();
01177         mainMenu.setRedraw();
01178         GFX_ResetScreen();
01179     }
01180 #endif
01181 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* TODO: Move to menu.cpp DOSBox_SetMenu() and add setmenu(NULL) to DOSBox_NoMenu() @emendelson request showmenu=false */
01182     void sdl_hax_macosx_setmenu(void *nsMenu);
01183     sdl_hax_macosx_setmenu(mainMenu.getNsMenu());
01184 #endif
01185 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01186         if(!menu.gui) return;
01187 
01188     if (MainMenu == NULL) return;
01189 
01190         LOG(LOG_MISC,LOG_DEBUG)("Win32: loading and attaching menu resource to DOSBox's window");
01191 
01192         menu.toggle=true;
01193     NonUserResizeCounter=1;
01194         SDL1_hax_SetMenu(MainMenu);
01195         mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
01196 
01197         Reflect_Menu();
01198 
01199         if(menu.startup) {
01200                 RENDER_CallBack( GFX_CallBackReset );
01201         }
01202 
01203     void DOSBox_SetSysMenu(void);
01204     DOSBox_SetSysMenu();
01205 #endif
01206 }
01207 
01208 void DOSBox_NoMenu(void) {
01209 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
01210     /* FIXME: SDL menu is NOT AVAILABLE if OpenGL surface is used */
01211     {
01212         menu.toggle=false;
01213         mainMenu.showMenu(false);
01214         mainMenu.setRedraw();
01215         GFX_ResetScreen();
01216     }
01217 #endif
01218 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU
01219     void sdl_hax_macosx_setmenu(void *nsMenu);
01220     sdl_hax_macosx_setmenu(NULL);
01221 #endif
01222 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01223         if(!menu.gui) return;
01224         menu.toggle=false;
01225     NonUserResizeCounter=1;
01226         SDL1_hax_SetMenu(NULL);
01227         mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
01228         RENDER_CallBack( GFX_CallBackReset );
01229 
01230     void DOSBox_SetSysMenu(void);
01231     DOSBox_SetSysMenu();
01232 #endif
01233 }
01234 
01235 void ToggleMenu(bool pressed) {
01236     bool GFX_GetPreventFullscreen(void);
01237 
01238     /* prevent removing the menu in 3Dfx mode */
01239     if (GFX_GetPreventFullscreen())
01240         return;
01241 
01242     menu.resizeusing=true;
01243         int width, height; bool fullscreen;
01244         void GFX_GetSize(int &width, int &height, bool &fullscreen);
01245         GFX_GetSize(width, height, fullscreen);
01246     if(!menu.gui || !pressed || fullscreen) return;
01247         if(!menu.toggle) {
01248                 menu.toggle=true;
01249                 DOSBox_SetMenu();
01250         } else {
01251                 menu.toggle=false;
01252                 DOSBox_NoMenu();
01253         }
01254 
01255     void DOSBox_SetSysMenu(void);
01256         DOSBox_SetSysMenu();
01257 }
01258 
01259 #if !(defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS))
01260 bool OpenGL_using(void);
01261 
01262 int Reflect_Menu(void) {
01263     return 0;
01264 }
01265 
01266 void DOSBox_RefreshMenu(void) {
01267 }
01268 
01269 void DOSBox_CheckOS(int &id, int &major, int &minor) {
01270     id=major=minor=0;
01271 }
01272 #endif
01273 
01274 #if defined(WIN32)
01275 # if defined(HX_DOS) || !defined(C_SDL2)
01276 HWND GetHWND(void) {
01277         SDL_SysWMinfo wmi;
01278         SDL_VERSION(&wmi.version);
01279 
01280         if(!SDL_GetWMInfo(&wmi)) {
01281                 return NULL;
01282         }
01283         return wmi.window;
01284 }
01285 
01286 HWND GetSurfaceHWND(void) {
01287         SDL_SysWMinfo wmi;
01288         SDL_VERSION(&wmi.version);
01289 
01290         if (!SDL_GetWMInfo(&wmi)) {
01291                 return NULL;
01292         }
01293         return wmi.child_window;
01294 }
01295 # endif
01296 #endif
01297 
01298 #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
01299 #include <shlobj.h>
01300 
01301 extern void RENDER_CallBack( GFX_CallBackFunctions_t function );
01302 
01303 void GetDefaultSize(void) {
01304         char sizetemp[20]="512,32,32765,";
01305         char sizetemp2[20]="";
01306         sprintf(sizetemp2,"%d",hdd_defsize);
01307         strcat(sizetemp,sizetemp2);
01308         sprintf(hdd_size,sizetemp);
01309 }
01310 
01311 void SearchFolder( char path[MAX_PATH], char drive, std::string drive_type ) {
01312         WIN32_FIND_DATA FindFileData;
01313         HANDLE hFind;
01314 
01315         hFind = FindFirstFile ( "*.*", &FindFileData );
01316 
01317         if ( hFind != INVALID_HANDLE_VALUE ) MountDrive_2(drive,path,drive_type);
01318         FindClose ( hFind );
01319 }
01320 
01321 void BrowseFolder( char drive , std::string drive_type ) {
01322 #if !defined(HX_DOS)
01323         if (Drives[drive-'A']) {
01324                 LOG_MSG("Unmount drive %c first, and then try again.",drive);
01325                 return;
01326         }
01327         std::string title = "Select a drive/directory to mount";
01328         char path[MAX_PATH];
01329         BROWSEINFO bi = { 0 };
01330         if(drive_type=="CDROM")
01331                 bi.lpszTitle = ( title + " CD-ROM\nMounting a directory as CD-ROM gives an limited support" ).c_str();
01332         else if(drive_type=="FLOPPY")
01333                 bi.lpszTitle = ( title + " as Floppy" ).c_str();
01334         else if(drive_type=="LOCAL")
01335                 bi.lpszTitle = ( title + " as Local").c_str();
01336         else
01337                 bi.lpszTitle = (title.c_str());
01338         LPITEMIDLIST pidl = SHBrowseForFolder ( &bi );
01339 
01340         if ( pidl != 0 ) {
01341                 SHGetPathFromIDList ( pidl, path );
01342 //              SetCurrentDirectory ( path );
01343                 SearchFolder( path , drive, drive_type );
01344                 IMalloc * imalloc = 0;
01345                 if ( SUCCEEDED( SHGetMalloc ( &imalloc )) ) {
01346                         imalloc->Free ( pidl );
01347                         imalloc->Release ( );
01348                 }
01349         }
01350 #endif
01351 }
01352 
01353 void mem_conf(std::string memtype, int option) {
01354         std::string tmp;
01355         Section* sec = control->GetSection("dos");
01356         Section_prop * section=static_cast<Section_prop *>(sec); 
01357         if (!option) {
01358                 tmp = section->Get_bool(memtype) ? "false" : "true";
01359         } else {
01360                 switch (option) {
01361                         case 1: tmp = "true"; break;
01362                         case 2: tmp = "false"; break;
01363                         case 3: tmp = "emsboard"; break;
01364                         case 4: tmp = "emm386"; break;
01365                         default: return;
01366                 }
01367         }
01368         if(sec) {
01369                 memtype += "=" + tmp;
01370                 sec->HandleInputline(memtype);
01371         }
01372 }
01373 
01374 void UnMount(int i_drive) {
01375         if (dos_kernel_disabled)
01376                 return;
01377 
01378         i_drive = toupper(i_drive);
01379         if(i_drive-'A' == DOS_GetDefaultDrive()) {
01380                 DOS_MCB mcb(dos.psp()-1);
01381                 static char name[9];
01382                 mcb.GetFileName(name);
01383                 if (!strlen(name)) goto umount;
01384                 LOG_MSG("GUI:Drive %c is being used. Aborted.",i_drive);
01385                 return;
01386         };
01387 umount:
01388         if (i_drive-'A' < DOS_DRIVES && i_drive-'A' >= 0 && Drives[i_drive-'A']) {
01389                 switch (DriveManager::UnmountDrive(i_drive-'A')) {
01390                 case 0:
01391                         Drives[i_drive-'A'] = 0;
01392                         if(i_drive-'A' == DOS_GetDefaultDrive()) 
01393                                 DOS_SetDrive(toupper('Z') - 'A');
01394                         LOG_MSG("GUI:Drive %c has succesfully been removed.",i_drive); break;
01395                 case 1:
01396                         LOG_MSG("GUI:Virtual Drives can not be unMOUNTed."); break;
01397                 case 2:
01398                         LOG_MSG(MSCDEX_Output(1).c_str()); break;
01399                 }
01400         }
01401 }
01402 
01403 void MountDrive_2(char drive, const char drive2[DOS_PATHLENGTH], std::string drive_type) {
01404         (void)drive_type;
01405         (void)drive2;
01406         (void)drive;
01407 }
01408 
01409 void MountDrive(char drive, const char drive2[DOS_PATHLENGTH]) {
01410         (void)drive2;
01411         (void)drive;
01412 }
01413 
01414 void Mount_Img_Floppy(char drive, std::string realpath) {
01415         (void)realpath;
01416         (void)drive;
01417 }
01418 
01419 void Mount_Img_HDD(char drive, std::string realpath) {
01420         (void)realpath;
01421         (void)drive;
01422 }
01423 
01424 void Mount_Img(char drive, std::string realpath) {
01425         (void)realpath;
01426         (void)drive;
01427 }
01428 
01429 void DOSBox_SetSysMenu(void) {
01430 #if !defined(HX_DOS)
01431         MENUITEMINFO mii;
01432         HMENU sysmenu;
01433 
01434         sysmenu = GetSystemMenu(GetHWND(), TRUE); // revert, so we can reapply menu items
01435         sysmenu = GetSystemMenu(GetHWND(), FALSE);
01436         if (sysmenu == NULL) return;
01437 
01438         AppendMenu(sysmenu, MF_SEPARATOR, -1, "");
01439 
01440         {
01441                 const char *msg = "Show menu &bar";
01442 
01443                 memset(&mii, 0, sizeof(mii));
01444                 mii.cbSize = sizeof(mii);
01445                 mii.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
01446                 mii.fState = (menu.toggle ? MFS_CHECKED : 0) | (GFX_GetPreventFullscreen() ? MFS_DISABLED : MFS_ENABLED);
01447                 mii.wID = ID_WIN_SYSMENU_TOGGLEMENU;
01448                 mii.dwTypeData = (LPTSTR)(msg);
01449                 mii.cch = (UINT)(strlen(msg)+1);
01450 
01451                 InsertMenuItem(sysmenu, GetMenuItemCount(sysmenu), TRUE, &mii);
01452         }
01453 #endif
01454 }
01455 
01456 void DOSBox_CheckOS(int &id, int &major, int &minor) {
01457         OSVERSIONINFO osi;
01458         ZeroMemory(&osi, sizeof(OSVERSIONINFO));
01459         osi.dwOSVersionInfoSize = sizeof(osi);
01460         GetVersionEx(&osi);
01461         id=osi.dwPlatformId;
01462         if(id==1) { major=0; minor=0; return; }
01463         major=osi.dwMajorVersion;
01464         minor=osi.dwMinorVersion;
01465 }
01466 
01467 bool DOSBox_Kor(void) {
01468     if(menu.compatible) return false;
01469     char Buffer[30];
01470     GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, Buffer, sizeof(Buffer));
01471     return (!strcmp(Buffer,"KOR") ? true : false);
01472 }
01473 
01474 void DOSBox_RefreshMenu(void) {
01475 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01476     int width, height; bool fullscreen;
01477     void GFX_GetSize(int &width, int &height, bool &fullscreen);
01478     GFX_GetSize(width,height,fullscreen);
01479     void SDL_Prepare(void);
01480     SDL_Prepare();
01481     if(!menu.gui) return;
01482 
01483     bool GFX_GetPreventFullscreen(void);
01484 
01485     /* prevent removing the menu in 3Dfx mode */
01486     if (GFX_GetPreventFullscreen())
01487         return;
01488 
01489     if(fullscreen) {
01490         NonUserResizeCounter=1;
01491         SetMenu(GetHWND(), NULL);
01492         DrawMenuBar(GetHWND());
01493         return;
01494     }
01495         DOSBox_SetSysMenu();
01496         if(menu.toggle)
01497                 DOSBox_SetMenu();
01498         else
01499                 DOSBox_NoMenu();
01500 #endif
01501 }
01502 
01503 void DOSBox_RefreshMenu2(void) {
01504 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01505         if(!menu.gui) return;
01506    int width, height; bool fullscreen;
01507    void GFX_GetSize(int &width, int &height, bool &fullscreen);
01508    GFX_GetSize(width,height,fullscreen);
01509     void SDL_Prepare(void);
01510     SDL_Prepare();
01511     if(!menu.gui) return;
01512 
01513     if(fullscreen) {
01514         NonUserResizeCounter=1;
01515         return;
01516     }
01517         if(menu.toggle) {
01518                 menu.toggle=true;
01519         NonUserResizeCounter=1;
01520         SDL1_hax_SetMenu(MainMenu);
01521         } else {
01522                 menu.toggle=false;
01523         NonUserResizeCounter=1;
01524                 SDL1_hax_SetMenu(NULL);
01525         }
01526 
01527     void DOSBox_SetSysMenu(void);
01528     DOSBox_SetSysMenu();
01529 #endif
01530 }
01531 
01532 void MENU_Check_Drive(HMENU handle, int cdrom, int floppy, int local, int image, int automount, int umount, char drive) {
01533 #if !defined(HX_DOS)
01534         std::string full_drive(1, drive);
01535         Section_prop * sec = static_cast<Section_prop *>(control->GetSection("dos"));
01536         full_drive += ":\\";
01537         EnableMenuItem(handle, cdrom, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01538         EnableMenuItem(handle, floppy, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01539         EnableMenuItem(handle, local, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01540         EnableMenuItem(handle, image, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01541         if(sec) EnableMenuItem(handle, automount, AUTOMOUNT(full_drive.c_str(), drive) && !menu.boot && sec->Get_bool("automount") ? MF_ENABLED : MF_GRAYED);
01542         EnableMenuItem(handle, umount, (!Drives[drive - 'A']) || menu.boot ? MF_GRAYED : MF_ENABLED);
01543 #endif
01544 }
01545 
01546 void MENU_KeyDelayRate(int delay, int rate) {
01547         IO_Write(0x60,0xf3); IO_Write(0x60,(Bit8u)(((delay-1)<<5)|(32-rate)));
01548         LOG_MSG("GUI: Keyboard rate %d, delay %d", rate, delay);
01549 }
01550 
01551 extern "C" void (*SDL1_hax_INITMENU_cb)();
01552 void reflectmenu_INITMENU_cb();
01553 
01554 bool GFX_GetPreventFullscreen(void);
01555 
01556 int Reflect_Menu(void) {
01557 #if !defined(HX_DOS)
01558     SDL1_hax_INITMENU_cb = reflectmenu_INITMENU_cb;
01559 #endif
01560     return 1;
01561 }
01562 
01563 void reflectmenu_INITMENU_cb() {
01564         /* WARNING: SDL calls this from Parent Window Thread!
01565                     This executes in the context of the Parent Window Thread, NOT the main thread!
01566                                 As stupid as that seems, this is the only way the Parent Window Thread can make
01567                                 sure to keep Windows waiting while we take our time to reset the checkmarks in
01568                                 the menus before the menu is displayed. */
01569         Reflect_Menu();
01570 }
01571 
01572 void MSG_WM_COMMAND_handle(SDL_SysWMmsg &Message) {
01573     bool GFX_GetPreventFullscreen(void);
01574 
01575     if (!menu.gui || GetSetSDLValue(1, "desktop.fullscreen", 0)) return;
01576     if (!GetMenu(GetHWND())) return;
01577     if (Message.msg != WM_COMMAND) return;
01578 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01579         if (mainMenu.mainMenuWM_COMMAND((unsigned int)Message.wParam)) return;
01580 #endif
01581 }
01582 #else
01583 void DOSBox_SetSysMenu(void) {
01584 }
01585 #endif
01586 
01587 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
01588 void DOSBoxMenu::item::showItem(DOSBoxMenu &menu,bool show) {
01589     (void)menu;//UNUSED
01590     if (itemVisible != show) {
01591         itemVisible = show;
01592         needRedraw = true;
01593     }
01594     else {
01595     }
01596 }
01597 
01598 DOSBoxMenu::item &DOSBoxMenu::item::setHilight(DOSBoxMenu &menu,bool hi) {
01599     (void)menu;//UNUSED
01600     if (itemHilight != hi) {
01601         itemHilight = hi;
01602         needRedraw = true;
01603     }
01604 
01605     return *this;
01606 }
01607 
01608 DOSBoxMenu::item &DOSBoxMenu::item::setHover(DOSBoxMenu &menu,bool ho) {
01609     (void)menu;//UNUSED
01610     if (itemHover != ho) {
01611         itemHover = ho;
01612         needRedraw = true;
01613     }
01614 
01615     return *this;
01616 }
01617 
01618 void DOSBoxMenu::item::removeFocus(DOSBoxMenu &menu) {
01619     if (menu.menuUserAttentionAt == master_id) {
01620         menu.menuUserAttentionAt = unassigned_item_handle;
01621         setHilight(menu,false);
01622     }
01623 }
01624 
01625 void DOSBoxMenu::item::removeHover(DOSBoxMenu &menu) {
01626     if (menu.menuUserHoverAt == master_id) {
01627         menu.menuUserHoverAt = unassigned_item_handle;
01628         setHover(menu,false);
01629     }
01630 }
01631 
01632 void DOSBoxMenu::showMenu(bool show) {
01633     if (menuVisible != show) {
01634         menuVisible = show;
01635         needRedraw = true;
01636         removeFocus();
01637         updateRect();
01638     }
01639 }
01640 
01641 void DOSBoxMenu::removeFocus(void) {
01642     if (menuUserAttentionAt != unassigned_item_handle) {
01643         for (auto &id : master_list) {
01644             id.removeFocus(*this);
01645             id.showItem(*this,false);
01646         }
01647         menuUserAttentionAt = unassigned_item_handle;
01648         needRedraw = true;
01649     }
01650 }
01651 
01652 void DOSBoxMenu::setScale(size_t s) {
01653     if (s == 0) s = 1;
01654     if (s > 2) s = 2;
01655 
01656     if (fontCharScale != s) {
01657         fontCharScale = s;
01658         menuBarHeight = menuBarHeightBase * fontCharScale;
01659         fontCharWidth = fontCharWidthBase * fontCharScale;
01660         fontCharHeight = fontCharHeightBase * fontCharScale;
01661         updateRect();
01662         layoutMenu();
01663     }
01664 }
01665 
01666 void DOSBoxMenu::removeHover(void) {
01667     if (menuUserHoverAt != unassigned_item_handle) {
01668         get_item(menuUserHoverAt).removeHover(*this);
01669         menuUserHoverAt = unassigned_item_handle;
01670         needRedraw = true;
01671     }
01672 }
01673 
01674 void DOSBoxMenu::updateRect(void) {
01675     menuBox.x = 0;
01676     menuBox.y = 0;
01677     menuBox.w = menuVisible ? (unsigned int)screenWidth : 0;
01678     menuBox.h = menuVisible ? (unsigned int)menuBarHeight : 0;
01679 #if 0
01680     LOG_MSG("SDL menuBox w=%d h=%d",menuBox.w,menuBox.h);
01681 #endif
01682 }
01683 
01684 void DOSBoxMenu::layoutMenu(void) {
01685     int x, y;
01686 
01687     x = menuBox.x;
01688     y = menuBox.y;
01689 
01690     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++) {
01691         DOSBoxMenu::item &item = get_item(*i);
01692 
01693         item.placeItem(*this, x, y, /*toplevel*/true);
01694         x += item.screenBox.w;
01695     }
01696 
01697     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++)
01698         get_item(*i).placeItemFinal(*this, /*finalwidth*/x - menuBox.x, /*toplevel*/true);
01699 
01700     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++)
01701         get_item(*i).layoutSubmenu(*this, /*toplevel*/true);
01702 
01703 #if 0
01704     LOG_MSG("Layout complete");
01705 #endif
01706 }
01707 
01708 void DOSBoxMenu::item::layoutSubmenu(DOSBoxMenu &menu, bool isTopLevel) {
01709     int x, y, minx, maxx;
01710 
01711     x = screenBox.x;
01712     y = screenBox.y;
01713 
01714     if (isTopLevel) {
01715         y += textBox.h;
01716     }
01717     else {
01718         x += screenBox.w + 2/*popup border*/;
01719     }
01720 
01721     popupBox.x = x;
01722     popupBox.y = y;
01723 
01724     minx = x;
01725     maxx = x;
01726 
01727     auto arr_follow=display_list.disp_list.begin();
01728     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++) {
01729         DOSBoxMenu::item &item = menu.get_item(*i);
01730 
01731         if (item.get_type() == DOSBoxMenu::vseparator_type_id) {
01732             for (;arr_follow < i;arr_follow++)
01733                 menu.get_item(*arr_follow).placeItemFinal(menu, /*finalwidth*/maxx - minx, /*toplevel*/false);
01734 
01735             x = maxx;
01736 
01737             item.screenBox.x = x;
01738             item.screenBox.y = popupBox.y;
01739             item.screenBox.w = (unsigned int)((4 * menu.fontCharScale) + 1);
01740             item.screenBox.h = y - popupBox.y;
01741 
01742             minx = maxx = x = item.screenBox.x + item.screenBox.w;
01743             y = popupBox.y;
01744         }
01745         else {
01746             item.placeItem(menu, x, y, /*toplevel*/false);
01747             y += item.screenBox.h;
01748 
01749             if (maxx < (item.screenBox.x + item.screenBox.w))
01750                 maxx = (item.screenBox.x + item.screenBox.w);
01751         }
01752     }
01753 
01754     for (;arr_follow < display_list.disp_list.end();arr_follow++)
01755         menu.get_item(*arr_follow).placeItemFinal(menu, /*finalwidth*/maxx - minx, /*toplevel*/false);
01756 
01757     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++) {
01758         DOSBoxMenu::item &item = menu.get_item(*i);
01759         int my = item.screenBox.y + item.screenBox.h;
01760         if (y < my) y = my;
01761     }
01762 
01763     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++)
01764         menu.get_item(*i).layoutSubmenu(menu, /*toplevel*/false);
01765 
01766     popupBox.w = maxx - popupBox.x;
01767     popupBox.h = y - popupBox.y;
01768 
01769     /* 1 pixel border, top */
01770     if (!isTopLevel) {
01771         borderTop = true;
01772         popupBox.y -= 1;
01773         popupBox.h += 1;
01774     }
01775     else {
01776         borderTop = false;
01777     }
01778     /* 1 pixel border, left */
01779     popupBox.x -= 1;
01780     popupBox.w += 1;
01781     /* 1 pixel border, right */
01782     popupBox.w += 1;
01783     /* 1 pixel border, bottom */
01784     popupBox.h += 1;
01785 }
01786 
01787 void DOSBoxMenu::item::placeItemFinal(DOSBoxMenu &menu,int finalwidth,bool isTopLevel) {
01788     if (type < separator_type_id) {
01789         int x = 0,rx = 0;
01790 
01791         if (!isTopLevel) {
01792             screenBox.w = finalwidth;
01793         }
01794 
01795         /* from the left */
01796         checkBox.x = x;
01797         x += checkBox.w;
01798 
01799         textBox.x = x;
01800         x += textBox.w;
01801 
01802         /* from the right */
01803         rx = screenBox.w;
01804 
01805         rx -= (int)menu.fontCharWidth;
01806 
01807         rx -= shortBox.w;
01808         shortBox.x = rx;
01809 
01810         if (!isTopLevel) {
01811             screenBox.w = finalwidth;
01812         }
01813 
01814         /* check */
01815         if (x > rx) LOG_MSG("placeItemFinal warning: text and shorttext overlap by %d pixels",x-rx);
01816     }
01817     else if (type == separator_type_id) {
01818         if (!isTopLevel) {
01819             screenBox.w = finalwidth;
01820         }
01821     }
01822 
01823 #if 0
01824     LOG_MSG("Item id=%u name=\"%s\" placed at x,y,w,h=%d,%d,%d,%d. text:x,y,w,h=%d,%d,%d,%d",
01825         master_id,name.c_str(),
01826         screenBox.x,screenBox.y,
01827         screenBox.w,screenBox.h,
01828         textBox.x,textBox.y,
01829         textBox.w,textBox.h);
01830 #endif
01831 
01832     boxInit = true;
01833 }
01834 
01835 void DOSBoxMenu::item::placeItem(DOSBoxMenu &menu,int x,int y,bool isTopLevel) {
01836     if (type < separator_type_id) {
01837         screenBox.x = x;
01838         screenBox.y = y;
01839         screenBox.w = 0;
01840         screenBox.h = (unsigned int)menu.fontCharHeight;
01841 
01842         checkBox.x = 0;
01843         checkBox.y = 0;
01844         checkBox.w = (unsigned int)menu.fontCharWidth;
01845         checkBox.h = (unsigned int)menu.fontCharHeight;
01846         screenBox.w += (unsigned int)checkBox.w;
01847 
01848         textBox.x = 0;
01849         textBox.y = 0;
01850         textBox.w = (unsigned int)menu.fontCharWidth * (unsigned int)text.length();
01851         textBox.h = (unsigned int)menu.fontCharHeight;
01852         screenBox.w += (unsigned int)textBox.w;
01853 
01854         shortBox.x = 0;
01855         shortBox.y = 0;
01856         shortBox.w = 0;
01857         shortBox.h = 0;
01858         if (!isTopLevel && !shortcut_text.empty()) {
01859             screenBox.w += (unsigned int)menu.fontCharWidth;
01860             shortBox.w += (unsigned int)menu.fontCharWidth * (unsigned int)shortcut_text.length();
01861             shortBox.h = (unsigned int)menu.fontCharHeight;
01862             screenBox.w += (unsigned int)shortBox.w;
01863         }
01864 
01865         if (!isTopLevel && type == submenu_type_id)
01866             screenBox.w += (unsigned int)menu.fontCharWidth;
01867 
01868         screenBox.w += (unsigned int)menu.fontCharWidth;
01869     }
01870     else {
01871         screenBox.x = x;
01872         screenBox.y = y;
01873         screenBox.w = (unsigned int)menu.fontCharWidth * 2;
01874         screenBox.h = (unsigned int)((4 * menu.fontCharScale) + 1);
01875 
01876         checkBox.x = 0;
01877         checkBox.y = 0;
01878         checkBox.w = 0;
01879         checkBox.h = 0;
01880 
01881         textBox.x = 0;
01882         textBox.y = 0;
01883         textBox.w = 0;
01884         textBox.h = 0;
01885 
01886         shortBox.x = 0;
01887         shortBox.y = 0;
01888         shortBox.w = 0;
01889         shortBox.h = 0;
01890     }
01891 }
01892 #endif
01893