DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/gui/sdl_mapper.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <vector>
00021 #include <list>
00022 #include <chrono>
00023 #include <thread>
00024 #include <string.h>
00025 #include <ctype.h>
00026 #include <stdarg.h>
00027 #include <stdio.h>
00028 #include <assert.h>
00029 
00030 
00031 #include "SDL.h"
00032 
00033 #include "dosbox.h"
00034 #include "video.h"
00035 #include "keyboard.h"
00036 #include "mouse.h"
00037 #include "pic.h"
00038 #include "control.h"
00039 #include "joystick.h"
00040 #include "util_math.h"
00041 #include "keymap.h"
00042 #include "support.h"
00043 #include "mapper.h"
00044 #include "setup.h"
00045 #include "menu.h"
00046 
00047 #include "SDL_syswm.h"
00048 
00049 #if C_EMSCRIPTEN
00050 # include <emscripten.h>
00051 #endif
00052 
00053 #include <map>
00054 
00055 #define BMOD_Mod1               0x0001
00056 #define BMOD_Mod2               0x0002
00057 #define BMOD_Mod3               0x0004
00058 #define BMOD_Host               0x0008
00059 
00060 #define BFLG_Hold               0x0001
00061 #define BFLG_Repeat             0x0004
00062 
00063 
00064 #define MAXSTICKS               8
00065 #define MAXACTIVE               16
00066 #define MAXBUTTON               32
00067 #define MAXBUTTON_CAP           16
00068 #define MAXAXIS                 8
00069 #define MAXHAT                  2
00070 
00071 
00072 #define MAX_VJOY_BUTTONS        8
00073 #define MAX_VJOY_HATS           16
00074 #define MAX_VJOY_AXES           8
00075 
00076 
00077 class CEvent;
00078 class CKeyEvent;
00079 class CHandlerEvent;
00080 class CButton;
00081 class CBind;
00082 class CBindGroup;
00083 class CJAxisBind;
00084 class CJButtonBind;
00085 class CJHatBind;
00086 class CKeyBind;
00087 class CKeyBindGroup;
00088 class CStickBindGroup;
00089 class CCaptionButton;
00090 class CCheckButton;
00091 class CBindButton;
00092 class CModEvent;
00093 
00094 enum {
00095     CLR_BLACK = 0,
00096     CLR_GREY = 1,
00097     CLR_WHITE = 2,
00098     CLR_RED = 3,
00099     CLR_BLUE = 4,
00100     CLR_GREEN = 5,
00101     CLR_DARKGREEN = 6
00102 };
00103 
00104 enum BB_Types {
00105     BB_Next,
00106     BB_Add,
00107     BB_Del,
00108     BB_Save,
00109     BB_Exit,
00110     BB_Capture
00111 };
00112 
00113 enum BC_Types {
00114     BC_Mod1,
00115     BC_Mod2,
00116     BC_Mod3,
00117     BC_Host,
00118     BC_Hold
00119 };
00120 
00121 typedef std::list<CBind *>                      CBindList;
00122 typedef std::list<CEvent *>::iterator           CEventList_it;
00123 typedef std::list<CBind *>::iterator            CBindList_it;
00124 typedef std::vector<CButton *>::iterator        CButton_it;
00125 typedef std::vector<CEvent *>::iterator         CEventVector_it;
00126 typedef std::vector<CHandlerEvent *>::iterator  CHandlerEventVector_it;
00127 typedef std::vector<CBindGroup *>::iterator     CBindGroup_it;
00128 
00129 static struct {
00130     bool                                        button_pressed[MAX_VJOY_BUTTONS];
00131     Bit16s                                      axis_pos[MAX_VJOY_AXES];
00132     bool                                        hat_pressed[MAX_VJOY_HATS];
00133 } virtual_joysticks[2];
00134 
00135 static struct {
00136     CCaptionButton*                             event_title;
00137     CCaptionButton*                             bind_title;
00138     CCaptionButton*                             selected;
00139     CCaptionButton*                             action;
00140     CCaptionButton*                             dbg2;
00141     CCaptionButton*                             dbg;
00142     CBindButton*                                save;
00143     CBindButton*                                exit;   
00144     CBindButton*                                cap;
00145     CBindButton*                                add;
00146     CBindButton*                                del;
00147     CBindButton*                                next;
00148     CCheckButton                                *mod1, *mod2, *mod3, *host, *hold;
00149 } bind_but;
00150 
00151 struct KeyBlock {
00152     const char*                                 title;
00153     const char*                                 entry;
00154     KBD_KEYS                                    key;
00155 };
00156 
00157 #ifdef DOSBOXMENU_EXTERNALLY_MANAGED
00158 static DOSBoxMenu                               mapperMenu;
00159 #endif
00160 
00161 extern Bit8u                                    int10_font_14[256 * 14];
00162 
00163 std::map<std::string,std::string>               pending_string_binds;
00164 
00165 static int                                      mapper_esc_count = 0;
00166 
00167 Bitu                                            next_handler_xpos = 0;
00168 Bitu                                            next_handler_ypos = 0;
00169 
00170 bool                                            mapper_addhandler_create_buttons = false;
00171 
00172 bool                                            isJPkeyboard = false;
00173 
00175 bool                                            autofire = false;
00176 
00178 bool                                            log_keyboard_scan_codes = false;
00179 
00181 int                                             joy1axes[8];
00182 
00184 int                                             joy2axes[8];
00185 
00186 static std::vector<CEvent *>                    events;
00187 static std::vector<CButton *>                   buttons;
00188 static std::vector<CBindGroup *>                bindgroups;
00189 static std::vector<CHandlerEvent *>             handlergroup;
00190 
00191 static CModEvent*                               mod_event[8] = {NULL};
00192 
00193 static CKeyEvent*                               caps_lock_event = NULL;
00194 static CKeyEvent*                               num_lock_event = NULL;
00195 
00196 static std::map<std::string, size_t>            name_to_events;
00197 
00198 static SDL_Color                                map_pal[7] =
00199 {
00200     {0x00,0x00,0x00,0x00},          //0=black
00201     {0x7f,0x7f,0x7f,0x00},          //1=grey
00202     {0xff,0xff,0xff,0x00},          //2=white
00203     {0xff,0x00,0x00,0x00},          //3=red
00204     {0x10,0x30,0xff,0x00},          //4=blue
00205     {0x00,0xff,0x20,0x00},          //5=green
00206     {0x00,0x7f,0x10,0x00}           //6=dark green
00207 };
00208 
00209 static KeyBlock combo_f[12] =
00210 {
00211     {"F1","f1",KBD_f1},     {"F2","f2",KBD_f2},     {"F3","f3",KBD_f3},
00212     {"F4","f4",KBD_f4},     {"F5","f5",KBD_f5},     {"F6","f6",KBD_f6},
00213     {"F7","f7",KBD_f7},     {"F8","f8",KBD_f8},     {"F9","f9",KBD_f9},
00214     {"F10","f10",KBD_f10},  {"F11","f11",KBD_f11},  {"F12","f12",KBD_f12},
00215 };
00216 
00217 static KeyBlock combo_1[14] =
00218 {
00219     {"`~","grave",KBD_grave},   {"1!","1",KBD_1},   {"2@","2",KBD_2},
00220     {"3#","3",KBD_3},           {"4$","4",KBD_4},   {"5%","5",KBD_5},
00221     {"6^","6",KBD_6},           {"7&","7",KBD_7},   {"8*","8",KBD_8},
00222     {"9(","9",KBD_9},           {"0)","0",KBD_0},   {"-_","minus",KBD_minus},
00223     {"=+","equals",KBD_equals}, {"\x1B","bspace",KBD_backspace},
00224 };
00225 
00226 static KeyBlock combo_1_pc98[14] =
00227 {
00228     {"`~","grave",KBD_grave},   {"1!","1",KBD_1},   {"2\"","2",KBD_2},
00229     {"3#","3",KBD_3},           {"4$","4",KBD_4},   {"5%","5",KBD_5},
00230     {"6&","6",KBD_6},           {"7'","7",KBD_7},   {"8(","8",KBD_8},
00231     {"9)","9",KBD_9},           {"0","0",KBD_0},    {"-=","minus",KBD_minus},
00232     {"=+","equals",KBD_equals}, {"\x1B","bspace",KBD_backspace},
00233 };
00234 
00235 static KeyBlock combo_2[12] =
00236 {
00237     {"Q","q",KBD_q},            {"W","w",KBD_w},    {"E","e",KBD_e},
00238     {"R","r",KBD_r},            {"T","t",KBD_t},    {"Y","y",KBD_y},
00239     {"U","u",KBD_u},            {"I","i",KBD_i},    {"O","o",KBD_o},
00240     {"P","p",KBD_p},            {"[{","lbracket",KBD_leftbracket},
00241     {"]}","rbracket",KBD_rightbracket},
00242 };
00243 
00244 static KeyBlock combo_3[12] =
00245 {
00246     {"A","a",KBD_a},            {"S","s",KBD_s},    {"D","d",KBD_d},
00247     {"F","f",KBD_f},            {"G","g",KBD_g},    {"H","h",KBD_h},
00248     {"J","j",KBD_j},            {"K","k",KBD_k},    {"L","l",KBD_l},
00249     {";:","semicolon",KBD_semicolon},               {"'\"","quote",KBD_quote},
00250     {"\\|","backslash",KBD_backslash},
00251 };
00252 
00253 static KeyBlock combo_3_pc98[12] =
00254 {
00255     {"A","a",KBD_a},            {"S","s",KBD_s},    {"D","d",KBD_d},
00256     {"F","f",KBD_f},            {"G","g",KBD_g},    {"H","h",KBD_h},
00257     {"J","j",KBD_j},            {"K","k",KBD_k},    {"L","l",KBD_l},
00258     {";:+","semicolon",KBD_semicolon},              {"'\"","quote",KBD_quote},
00259     {"\\|","backslash",KBD_backslash},
00260 };
00261 
00262 static KeyBlock combo_4[11] =
00263 {
00264     {"<>","lessthan",KBD_extra_lt_gt},
00265     {"Z","z",KBD_z},            {"X","x",KBD_x},    {"C","c",KBD_c},
00266     {"V","v",KBD_v},            {"B","b",KBD_b},    {"N","n",KBD_n},
00267     {"M","m",KBD_m},            {",<","comma",KBD_comma},
00268     {".>","period",KBD_period},                     {"/?","slash",KBD_slash},
00269 };
00270 
00271 static void                                     SetActiveEvent(CEvent * event);
00272 static void                                     SetActiveBind(CBind * _bind);
00273 static void                                     change_action_text(const char* text,Bit8u col);
00274 static void                                     DrawText(Bitu x,Bitu y,const char * text,Bit8u color,Bit8u bkcolor=CLR_BLACK);
00275 static void                                     MAPPER_SaveBinds(void);
00276 
00277 CEvent*                                         get_mapper_event_by_name(const std::string &x);
00278 bool                                            MAPPER_DemoOnly(void);
00279 
00280 Bitu                                            GUI_JoystickCount(void);                // external
00281 bool                                            GFX_GetPreventFullscreen(void);         // external
00282 void                                            GFX_ForceRedrawScreen(void);            // external
00283 #if defined(WIN32) && !defined(HX_DOS)
00284 void                                            WindowsTaskbarUpdatePreviewRegion(void);// external
00285 void                                            WindowsTaskbarResetPreviewRegion(void); // external
00286 #endif
00287 
00289 class CEvent {
00290 public:
00295     enum event_type {
00296         event_t=0,
00297         handler_event_t
00298     };
00299 public:
00306     CEvent(char const * const _entry,const enum event_type _type = event_t) {
00307         safe_strncpy(entry,_entry,sizeof(entry));
00308 
00309         {
00310             if (name_to_events.find(entry) != name_to_events.end())
00311                 E_Exit("Mapper: Event \"%s\" already defined",entry);
00312         }
00313 
00314         name_to_events[entry] = events.size();
00315 
00316         events.push_back(this);
00317         bindlist.clear();
00318         active=false;
00319         activity=0;
00320         current_value=0;
00321         type = _type;
00322 
00323         assert(get_mapper_event_by_name(entry) == this);
00324     }
00325 
00331     virtual std::string GetBindMenuText(void);
00332 
00334     void update_menu_shortcut(void) {
00335         if (!eventname.empty()) {
00336             DOSBoxMenu::item& item = mainMenu.get_item(std::string("mapper_") + std::string(eventname));
00337             std::string str = GetBindMenuText();
00338             item.set_shortcut_text(str);
00339             item.refresh_item(mainMenu);
00340 //            LOG_MSG("%s",str.c_str());
00341         }
00342     }
00343 
00345     void AddBind(CBind * bind);
00346         void ClearBinds();
00347 
00348     virtual ~CEvent();
00349 
00351     virtual void Active(bool yesno) {
00352         active = yesno;
00353     }
00354 
00356     virtual void ActivateEvent(bool ev_trigger,bool skip_action)=0;
00357 
00359     virtual void DeActivateEvent(bool ev_trigger)=0;
00360 
00362     void DeActivateAll(void);
00363 
00365     void SetValue(Bits value){
00366         current_value=value;
00367     }
00368 
00370     Bits GetValue(void) {
00371         return current_value;
00372     }
00373 
00375     char * GetName(void) { return entry; }
00376 
00378     virtual bool IsTrigger(void)=0;
00379 
00381     virtual void RebindRedraw(void) {}
00382 
00384     std::string eventname;
00385 
00387     enum event_type type;
00388 
00390     CBindList bindlist;
00391 
00393     bool active;
00394 protected:
00396     Bitu activity;
00397 
00399     char entry[16];
00400 
00402     Bits current_value;
00403 };
00404 
00406 class CTriggeredEvent : public CEvent {
00407 public:
00409     CTriggeredEvent(char const * const _entry) : CEvent(_entry) {}
00410 
00411     // methods below this line have sufficient documentation inherited from the base class
00412 
00413     virtual ~CTriggeredEvent() {}
00414 
00415     virtual bool IsTrigger(void) {
00416         return true;
00417     }
00418 
00419     virtual void ActivateEvent(bool ev_trigger,bool skip_action) {
00420         if (current_value>25000) {
00421             /* value exceeds boundary, trigger event if not active */
00422             if (!activity && !skip_action) Active(true);
00423             if (activity<32767) activity++;
00424         } else {
00425             if (activity>0) {
00426                 /* untrigger event if it is fully inactive */
00427                 DeActivateEvent(ev_trigger);
00428                 activity=0;
00429             }
00430         }
00431     }
00432 
00433     virtual void DeActivateEvent(bool /*ev_trigger*/) {
00434         if (activity > 0) activity--;
00435         if (!activity) Active(false);
00436     }
00437 };
00438 
00440 class CContinuousEvent : public CEvent {
00441 public:
00443     CContinuousEvent(char const * const _entry) : CEvent(_entry) {}
00444 
00445     // methods below this line have sufficient documentation inherited from the base class
00446 
00447     virtual ~CContinuousEvent() {}
00448 
00449     virtual bool IsTrigger(void) {
00450         return false;
00451     }
00452 
00453     virtual void ActivateEvent(bool ev_trigger,bool skip_action) {
00454         if (ev_trigger) {
00455             activity++;
00456             if (!skip_action) Active(true);
00457         } else {
00458             /* test if no trigger-activity is present, this cares especially
00459                about activity of the opposite-direction joystick axis for example */
00460             if (!GetActivityCount()) Active(true);
00461         }
00462     }
00463 
00464     virtual void DeActivateEvent(bool ev_trigger) {
00465         if (ev_trigger) {
00466             if (activity>0) activity--;
00467             if (activity==0) {
00468                 /* test if still some trigger-activity is present,
00469                    adjust the state in this case accordingly */
00470                 if (GetActivityCount()) RepostActivity();
00471                 else Active(false);
00472             }
00473         } else {
00474             if (!GetActivityCount()) Active(false);
00475         }
00476     }
00477 
00479     virtual Bitu GetActivityCount(void) {
00480         return activity;
00481     }
00482 
00484     virtual void RepostActivity(void) {}
00485 };
00486 
00488 class CBind {
00489 public:
00491     enum CBindType {
00492         bind_t=0,
00493         keybind_t
00494     };
00495 public:
00496     virtual ~CBind () {
00497         list->remove(this);
00498     }
00499 
00501     CBind(CBindList * _list,enum CBindType _type = bind_t) {
00502         list=_list;
00503         _list->push_back(this);
00504         mods=flags=0;
00505         event=0;
00506         active=holding=false;
00507         type = _type;
00508         value = 0;
00509     }
00510 
00512     virtual std::string GetModifierText(void);
00513 
00515     virtual std::string GetBindMenuText(void) {
00516         return GetModifierText();
00517     }
00518 
00520     void AddFlags(char * buf) {
00521         if (mods & BMOD_Mod1) strcat(buf," mod1");
00522         if (mods & BMOD_Mod2) strcat(buf," mod2");
00523         if (mods & BMOD_Mod3) strcat(buf," mod3");
00524         if (mods & BMOD_Host) strcat(buf," host");
00525         if (flags & BFLG_Hold) strcat(buf," hold");
00526     }
00527 
00529     void SetFlags(char * buf) {
00530         char * word;
00531         while (*(word=StripWord(buf))) {
00532             if (!strcasecmp(word,"mod1")) mods|=BMOD_Mod1;
00533             if (!strcasecmp(word,"mod2")) mods|=BMOD_Mod2;
00534             if (!strcasecmp(word,"mod3")) mods|=BMOD_Mod3;
00535             if (!strcasecmp(word,"host")) mods|=BMOD_Host;
00536             if (!strcasecmp(word,"hold")) flags|=BFLG_Hold;
00537         }
00538     }
00539 
00541     virtual void ActivateBind(Bits _value,bool ev_trigger,bool skip_action=false) {
00542         if (event->IsTrigger()) {
00543             /* use value-boundary for on/off events */
00544             if (_value>25000) {
00545                 event->SetValue(_value);
00546                 if (active) return;
00547                 if (!holding) event->ActivateEvent(ev_trigger,skip_action);
00548                 active=true;
00549             } else {
00550                 if (active) {
00551                     event->DeActivateEvent(ev_trigger);
00552                     active=false;
00553                 }
00554             }
00555         } else {
00556             /* store value for possible later use in the activated event */
00557             event->SetValue(_value);
00558             event->ActivateEvent(ev_trigger,false);
00559         }
00560     }
00561 
00563     void DeActivateBind(bool ev_trigger) {
00564         if (event->IsTrigger()) {
00565             if (!active) return;
00566             active=false;
00567             if (flags & BFLG_Hold) {
00568                 if (!holding) {
00569                     holding=true;
00570                     return;
00571                 } else {
00572                     holding=false;
00573                 }
00574             }
00575             event->DeActivateEvent(ev_trigger);
00576         } else {
00577             /* store value for possible later use in the activated event */
00578             event->SetValue(0);
00579             event->DeActivateEvent(ev_trigger);
00580         }
00581     }
00582 
00584     virtual void ConfigName(char * buf)=0;
00585 
00587     virtual void BindName(char * buf)=0;
00588 
00590     Bitu mods;
00591 
00593     Bitu flags;
00594 
00596     Bit16s value;
00597 
00599     CEvent * event;
00600 
00602     CBindList * list;
00603 
00605     bool active;
00606 
00608     bool holding;
00609 
00611     enum CBindType type;
00612 };
00613 
00614 CEvent::~CEvent() {
00615     CBindList_it it;
00616 
00617     while ((it=bindlist.begin()) != bindlist.end()) {
00618         delete (*it);
00619         bindlist.erase(it);
00620     }
00621 }
00622 
00623 void CEvent::AddBind(CBind * bind) {
00624     bindlist.push_front(bind);
00625     bind->event=this;
00626 }
00627 void CEvent::ClearBinds() {
00628         for (CBind *bind : bindlist) {
00629                 delete bind;
00630         }
00631         bindlist.clear();
00632 }
00633 void CEvent::DeActivateAll(void) {
00634     for (CBindList_it bit=bindlist.begin();bit!=bindlist.end();++bit) {
00635         (*bit)->DeActivateBind(true);
00636     }
00637 }
00638 
00639 class CBindGroup {
00640 public:
00641     CBindGroup() {
00642         bindgroups.push_back(this);
00643     }
00644     virtual ~CBindGroup (void) { }
00645     void ActivateBindList(CBindList * list,Bits value,bool ev_trigger);
00646     void DeactivateBindList(CBindList * list,bool ev_trigger);
00647     virtual CBind * CreateConfigBind(char *&buf)=0;
00648     virtual CBind * CreateEventBind(SDL_Event * event)=0;
00649 
00650     virtual bool CheckEvent(SDL_Event * event)=0;
00651     virtual const char * ConfigStart(void)=0;
00652     virtual const char * BindStart(void)=0;
00653 
00654 protected:
00655 
00656 };
00657 
00658 void MAPPER_TriggerEvent(const CEvent *event, const bool deactivation_state) {
00659         assert(event);
00660         for (auto &bind : event->bindlist) {
00661                 bind->ActivateBind(32767, true, false);
00662                 bind->DeActivateBind(deactivation_state);
00663         }
00664 }
00665 
00666 #if !defined(HX_DOS)
00667 /* TODO: This is fine, but it should not call MAPPER functions from a separate thread.
00668  *       These functions are not necessarily reentrant and can cause screw ups when
00669  *       called from multiple threads.
00670  *
00671  *       Also the HX-DOS builds cannot use this code because the older MinGW lacks
00672  *       std::thread.
00673  *
00674  *       Replace thread with PIC_AddEvent() to callback. */
00675 class Typer {
00676         public:
00677                 Typer() = default;
00678                 Typer(const Typer&) = delete; // prevent copy
00679                 Typer& operator=(const Typer&) = delete; // prevent assignment
00680                 ~Typer() {
00681                         Stop();
00682                 }
00683                 void Start(std::vector<CEvent*>     *ext_events,
00684                            std::vector<std::string> &ext_sequence,
00685                    const uint32_t           wait_ms,
00686                    const uint32_t           pace_ms) {
00687                         // Guard against empty inputs
00688                         if (!ext_events || ext_sequence.empty())
00689                                 return;
00690                         Wait();
00691                         m_events = ext_events;
00692                         m_sequence = std::move(ext_sequence);
00693                         m_wait_ms = wait_ms;
00694                         m_pace_ms = pace_ms;
00695                         m_stop_requested = false;
00696                         m_instance = std::thread(&Typer::Callback, this);
00697                 }
00698                 void Wait() {
00699                         if (m_instance.joinable())
00700                                 m_instance.join();
00701                 }
00702                 void Stop() {
00703                         m_stop_requested = true;
00704                         Wait();
00705                 }
00706         private:
00707                 void Callback() {
00708                         // quit before our initial wait time
00709                         if (m_stop_requested)
00710                                 return;
00711                         std::this_thread::sleep_for(std::chrono::milliseconds(m_wait_ms));
00712                         for (const auto &button : m_sequence) {
00713                                 bool found = false;
00714                                 // comma adds an extra pause, similar to the pause used in a phone number
00715                                 if (button == ",") {
00716                                         found = true;
00717                                          // quit before the pause
00718                                         if (m_stop_requested)
00719                                                 return;
00720                                         std::this_thread::sleep_for(std::chrono::milliseconds(m_pace_ms));
00721                                 // Otherwise trigger the matching button if we have one
00722                                 } else {
00723                                         const std::string bind_name = "key_" + button;
00724                                         for (auto &event : *m_events) {
00725                                                 if (bind_name == event->GetName()) {
00726                                                         found = true;
00727                                                         MAPPER_TriggerEvent(event, true);
00728                                                         break;
00729                                                 }
00730                                         }
00731                                 }
00732                                 /*
00733                                 *  Terminate the sequence for safety reasons if we can't find a button.
00734                                 *  For example, we don't wan't DEAL becoming DEL, or 'rem' becoming 'rm'
00735                                 */
00736                                 if (!found) {
00737                                         LOG_MSG("MAPPER: Couldn't find a button named '%s', stopping.",
00738                                                         button.c_str());
00739                                         return;
00740                                 }
00741                                 if (m_stop_requested) // quit before the pacing delay
00742                                         return;
00743                                 std::this_thread::sleep_for(std::chrono::milliseconds(m_pace_ms));
00744                         }
00745                 }
00746                 std::thread              m_instance;
00747                 std::vector<std::string> m_sequence;
00748                 std::vector<CEvent*>     *m_events = nullptr;
00749                 uint32_t                 m_wait_ms = 0;
00750                 uint32_t                 m_pace_ms = 0;
00751                 bool                     m_stop_requested = false;
00752 };
00753 #endif
00754 
00755 static struct CMapper {
00756 #if defined(C_SDL2)
00757     SDL_Window*                                 window;
00758     SDL_Rect                                    draw_rect;
00759     SDL_Surface*                                draw_surface_nonpaletted;
00760     SDL_Surface*                                draw_surface;
00761 #endif
00762     SDL_Surface*                                surface = NULL;
00763     bool                                        exit = false;
00764     CEvent*                                     aevent = NULL;                     //Active Event
00765     CBind*                                      abind = NULL;                      //Active Bind
00766     CBindList_it                                abindit;                    //Location of active bind in list
00767     bool                                        redraw = false;
00768     bool                                        addbind = false;
00769     bool                                        running = false;
00770     Bitu                                        mods = 0;
00771     struct {
00772         Bitu                                    num_groups,num;
00773         CStickBindGroup*                        stick[MAXSTICKS];
00774     } sticks = {};
00775 #if !defined(HX_DOS)
00776         Typer                                                                           typist;
00777 #endif
00778     std::string                                 filename;
00779 } mapper;
00780 
00781 #if defined(C_SDL2) /* SDL 2.x */
00782 
00783 /* HACK */
00784 typedef SDL_Scancode SDLKey;
00785 
00786 #else /* !defined(C_SDL2) SDL 1.x */
00787 
00788 #define MAX_SDLKEYS 323
00789 
00790 static bool usescancodes;
00791 static Bit8u scancode_map[MAX_SDLKEYS];
00792 
00793 #define Z SDLK_UNKNOWN
00794 
00795 #if defined (MACOSX)
00796 static SDLKey sdlkey_map[]={
00797     /* Main block printables */
00798     /*00-05*/ Z, SDLK_s, SDLK_d, SDLK_f, SDLK_h, SDLK_g,
00799     /*06-0B*/ SDLK_z, SDLK_x, SDLK_c, SDLK_v, SDLK_WORLD_0, SDLK_b,
00800     /*0C-11*/ SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_y, SDLK_t, 
00801     /*12-17*/ SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_6, SDLK_5, 
00802     /*18-1D*/ SDLK_EQUALS, SDLK_9, SDLK_7, SDLK_MINUS, SDLK_8, SDLK_0, 
00803     /*1E-21*/ SDLK_RIGHTBRACKET, SDLK_o, SDLK_u, SDLK_LEFTBRACKET, 
00804     /*22-23*/ SDLK_i, SDLK_p,
00805     /*24-29*/ SDLK_RETURN, SDLK_l, SDLK_j, SDLK_QUOTE, SDLK_k, SDLK_SEMICOLON, 
00806     /*2A-29*/ SDLK_BACKSLASH, SDLK_COMMA, SDLK_SLASH, SDLK_n, SDLK_m, 
00807     /*2F-2F*/ SDLK_PERIOD,
00808 
00809     /* Spaces, controls, modifiers (dosbox uses LMETA only for
00810      * hotkeys, it's not really mapped to an emulated key) */
00811     /*30-33*/ SDLK_TAB, SDLK_SPACE, SDLK_BACKQUOTE, SDLK_BACKSPACE,
00812     /*34-37*/ Z, SDLK_ESCAPE, Z, SDLK_LMETA,
00813     /*38-3B*/ SDLK_LSHIFT, SDLK_CAPSLOCK, SDLK_LALT, SDLK_LCTRL,
00814 
00815     /*3C-40*/ Z, Z, Z, Z, Z,
00816 
00817     /* Keypad (KP_EQUALS not supported, NUMLOCK used on what is CLEAR
00818      * in Mac OS X) */
00819     /*41-46*/ SDLK_KP_PERIOD, Z, SDLK_KP_MULTIPLY, Z, SDLK_KP_PLUS, Z,
00820     /*47-4A*/ SDLK_NUMLOCK /*==SDLK_CLEAR*/, Z, Z, Z,
00821     /*4B-4D*/ SDLK_KP_DIVIDE, SDLK_KP_ENTER, Z,
00822     /*4E-51*/ SDLK_KP_MINUS, Z, Z, SDLK_KP_EQUALS,
00823     /*52-57*/ SDLK_KP0, SDLK_KP1, SDLK_KP2, SDLK_KP3, SDLK_KP4, SDLK_KP5, 
00824     /*58-5C*/ SDLK_KP6, SDLK_KP7, Z, SDLK_KP8, SDLK_KP9, 
00825 
00826     /*5D-5F*/ Z, Z, SDLK_a,
00827     
00828     /* Function keys and cursor blocks (F13 not supported, F14 =>
00829      * PRINT[SCREEN], F15 => SCROLLOCK, F16 => PAUSE, HELP => INSERT) */
00830     /*60-64*/ SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F3, SDLK_F8,
00831     /*65-6A*/ SDLK_F9, Z, SDLK_F11, Z, SDLK_F13, SDLK_PAUSE /*==SDLK_F16*/,
00832     /*6B-70*/ SDLK_PRINT /*==SDLK_F14*/, Z, SDLK_F10, Z, SDLK_F12, Z,
00833     /*71-72*/ SDLK_SCROLLOCK /*==SDLK_F15*/, SDLK_INSERT /*==SDLK_HELP*/, 
00834     /*73-77*/ SDLK_HOME, SDLK_PAGEUP, SDLK_DELETE, SDLK_F4, SDLK_END,
00835     /*78-7C*/ SDLK_F2, SDLK_PAGEDOWN, SDLK_F1, SDLK_LEFT, SDLK_RIGHT,
00836     /*7D-7E*/ SDLK_DOWN, SDLK_UP,
00837 
00838     /*7F-7F*/ Z,
00839 
00840     /* 4 extra keys that don't really exist, but are needed for
00841      * round-trip mapping (dosbox uses RMETA only for hotkeys, it's
00842      * not really mapped to an emulated key) */
00843     SDLK_RMETA, SDLK_RSHIFT, SDLK_RALT, SDLK_RCTRL
00844 };
00845 #define MAX_SCANCODES (0x80+4)
00846 /* Make sure that the table above has the expected size.  This
00847    expression will raise a compiler error if the condition is false.  */
00848 typedef char assert_right_size [MAX_SCANCODES == (sizeof(sdlkey_map)/sizeof(sdlkey_map[0])) ? 1 : -1];
00849 
00850 #else // !MACOSX
00851 
00852 #define MAX_SCANCODES 0xdf
00853 static SDLKey sdlkey_map[MAX_SCANCODES]={SDLK_UNKNOWN,SDLK_ESCAPE,
00854     SDLK_1,SDLK_2,SDLK_3,SDLK_4,SDLK_5,SDLK_6,SDLK_7,SDLK_8,SDLK_9,SDLK_0,
00855     /* 0x0c: */
00856     SDLK_MINUS,SDLK_EQUALS,SDLK_BACKSPACE,SDLK_TAB,
00857     SDLK_q,SDLK_w,SDLK_e,SDLK_r,SDLK_t,SDLK_y,SDLK_u,SDLK_i,SDLK_o,SDLK_p,
00858     SDLK_LEFTBRACKET,SDLK_RIGHTBRACKET,SDLK_RETURN,SDLK_LCTRL,
00859     SDLK_a,SDLK_s,SDLK_d,SDLK_f,SDLK_g,SDLK_h,SDLK_j,SDLK_k,SDLK_l,
00860     SDLK_SEMICOLON,SDLK_QUOTE,SDLK_BACKQUOTE,SDLK_LSHIFT,SDLK_BACKSLASH,
00861     SDLK_z,SDLK_x,SDLK_c,SDLK_v,SDLK_b,SDLK_n,SDLK_m,
00862     /* 0x33: */
00863     SDLK_COMMA,SDLK_PERIOD,SDLK_SLASH,SDLK_RSHIFT,SDLK_KP_MULTIPLY,
00864     SDLK_LALT,SDLK_SPACE,SDLK_CAPSLOCK,
00865     SDLK_F1,SDLK_F2,SDLK_F3,SDLK_F4,SDLK_F5,SDLK_F6,SDLK_F7,SDLK_F8,SDLK_F9,SDLK_F10,
00866     /* 0x45: */
00867     SDLK_NUMLOCK,SDLK_SCROLLOCK,
00868     SDLK_KP7,SDLK_KP8,SDLK_KP9,SDLK_KP_MINUS,SDLK_KP4,SDLK_KP5,SDLK_KP6,SDLK_KP_PLUS,
00869     SDLK_KP1,SDLK_KP2,SDLK_KP3,SDLK_KP0,SDLK_KP_PERIOD,
00870     SDLK_UNKNOWN,SDLK_UNKNOWN,
00871     SDLK_LESS,SDLK_F11,SDLK_F12, Z, Z, Z, Z, Z, Z, Z,
00872     /* 0x60: */
00873     Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
00874     /* 0x70: */
00875     Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
00876     /* 0x80: */
00877     Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
00878     /* 0x90: */
00879     Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
00880     /* 0xA0: */
00881     Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
00882     /* 0xB0: */
00883     Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
00884     /* 0xC0: */
00885     Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
00886     /* 0xD0: */
00887     Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z//,Z,Z,
00888     /* 0xE0: */
00889     //Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
00890     /* 0xF0: */
00891 //  Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z//,Z,Z
00892 
00893 };
00894 #endif
00895 
00896 #undef Z
00897 
00898 
00899 SDLKey MapSDLCode(Bitu skey) {
00900 //  LOG_MSG("MapSDLCode %d %X",skey,skey);
00901     if (usescancodes) {
00902         if (skey<MAX_SCANCODES) return sdlkey_map[skey];
00903         else return SDLK_UNKNOWN;
00904     } else return (SDLKey)skey;
00905 }
00906 
00907 Bitu GetKeyCode(SDL_keysym keysym) {
00908 //  LOG_MSG("GetKeyCode %X %X %X",keysym.scancode,keysym.sym,keysym.mod);
00909     if (usescancodes) {
00910         Bitu key=(Bitu)keysym.scancode;
00911 
00912 #if defined (MACOSX)
00913         if ((keysym.scancode == 0) && (keysym.sym == 'a')) key = 0x5f;  // zero value makes the keyboar crazy
00914 #endif
00915 
00916         if (key==0
00917 #if defined (MACOSX)
00918             /* On Mac on US keyboards, scancode 0 is actually the 'a'
00919              * key.  For good measure exclude all printables from this
00920              * condition. */
00921             && (keysym.sym < SDLK_SPACE || keysym.sym > SDLK_WORLD_95)
00922 #endif
00923             ) {
00924             /* try to retrieve key from symbolic key as scancode is zero */
00925             if (keysym.sym<MAX_SDLKEYS) key=scancode_map[(Bitu)keysym.sym];
00926         } 
00927 #if !defined (WIN32) && !defined (MACOSX) && !defined(OS2)
00928         /* Linux adds 8 to all scancodes */
00929         else key-=8;
00930 #endif
00931 #if defined (WIN32)
00932         switch (key) {
00933             case 0x1c:  // ENTER
00934             case 0x1d:  // CONTROL
00935             case 0x35:  // SLASH
00936             case 0x37:  // PRINTSCREEN
00937             case 0x38:  // ALT
00938             case 0x45:  // PAUSE
00939             case 0x47:  // HOME
00940             case 0x48:  // cursor UP
00941             case 0x49:  // PAGE UP
00942             case 0x4b:  // cursor LEFT
00943             case 0x4d:  // cursor RIGHT
00944             case 0x4f:  // END
00945             case 0x50:  // cursor DOWN
00946             case 0x51:  // PAGE DOWN
00947             case 0x52:  // INSERT
00948             case 0x53:  // DELETE
00949                 if (GFX_SDLUsingWinDIB()) key=scancode_map[(Bitu)keysym.sym];
00950                 break;
00951         }
00952 #endif
00953         return key;
00954     } else {
00955 #if defined (WIN32)
00956         /* special handling of 102-key under windows */
00957         if ((keysym.sym==SDLK_BACKSLASH) && (keysym.scancode==0x56)) return (Bitu)SDLK_LESS;
00958         /* special case of the \ _ key on Japanese 106-keyboards. seems to be the same code whether or not you've told Windows it's a 106-key */
00959         /* NTS: SDL source on Win32 maps this key (VK_OEM_102) to SDLK_LESS */
00960         if (isJPkeyboard && (keysym.sym == 0) && (keysym.scancode == 0x73)) return (Bitu)SDLK_WORLD_10; //FIXME: There's no SDLK code for that key! Re-use one of the world keys!
00961         /* another hack, for the Yen \ pipe key on Japanese 106-keyboards.
00962            sym == 0 if English layout, sym == 0x5C if Japanese layout */
00963         if (isJPkeyboard && (keysym.sym == 0 || keysym.sym == 0x5C) && (keysym.scancode == 0x7D)) return (Bitu)SDLK_WORLD_11; //FIXME: There's no SDLK code for that key! Re-use one of the world keys!
00964         /* what is ~ ` on American keyboards is "Hankaku" on Japanese keyboards. Same scan code. */
00965         if (isJPkeyboard && keysym.sym == SDLK_BACKQUOTE) return (int)SDLK_WORLD_12;
00966         /* Muhenkan */
00967         if (isJPkeyboard && keysym.sym == 0 && keysym.scancode == 0x7B) return (int)SDLK_WORLD_13;
00968         /* Henkan/Zenkouho */
00969         if (isJPkeyboard && keysym.sym == 0 && keysym.scancode == 0x79) return (int)SDLK_WORLD_14;
00970         /* Hiragana/Katakana */
00971         if (isJPkeyboard && keysym.sym == 0 && keysym.scancode == 0x70) return (int)SDLK_WORLD_15;
00972 #endif
00973         return (Bitu)keysym.sym;
00974     }
00975 }
00976 
00977 #endif /* !defined(C_SDL2) */
00978 
00979 class CKeyBind : public CBind {
00980 public:
00981     CKeyBind(CBindList * _list,SDLKey _key) : CBind(_list, CBind::keybind_t) {
00982         key = _key;
00983     }
00984     virtual ~CKeyBind() {}
00985     virtual void BindName(char * buf) override {
00986 #if defined(C_SDL2)
00987         sprintf(buf,"Key %s",SDL_GetScancodeName(key));
00988 #else
00989         sprintf(buf,"Key %s",SDL_GetKeyName(MapSDLCode((Bitu)key)));
00990 #endif
00991     }
00992     virtual void ConfigName(char * buf) override {
00993 #if defined(C_SDL2)
00994         sprintf(buf,"key %d",key);
00995 #else
00996         sprintf(buf,"key %d",MapSDLCode((Bitu)key));
00997 #endif
00998     }
00999     virtual std::string GetBindMenuText(void) override {
01000         const char *s;
01001         std::string r,m;
01002 
01003 #if defined(C_SDL2)
01004         s = SDL_GetScancodeName(key);
01005         if (s != NULL) r = s;
01006 #else
01007         s = SDL_GetKeyName(MapSDLCode((Bitu)key));
01008                 if (s != NULL) {
01009                         r = s;
01010                         if (r.length()>0) {
01011                                 r[0]=toupper(r[0]);
01012                                 char *c=(char *)strstr(r.c_str(), " ctrl");
01013                                 if (c==NULL) c=(char *)strstr(r.c_str(), " alt");
01014                                 if (c==NULL) c=(char *)strstr(r.c_str(), " shift");
01015                                 if (c!=NULL) *(c+1)=toupper(*(c+1));
01016                         }
01017                 }
01018 #endif
01019 
01020         m = GetModifierText();
01021         if (!m.empty()) r = m + "+" + r;
01022 
01023         return r;
01024     }
01025 public:
01026     SDLKey key;
01027 };
01028 
01029 std::string CEvent::GetBindMenuText(void) {
01030     std::string r;
01031 
01032     if (bindlist.empty())
01033         return std::string();
01034 
01035     for (auto i=bindlist.begin();i!=bindlist.end();i++) {
01036         CBind *b = *i;
01037         if (b == NULL) continue;
01038         if (b->type != CBind::keybind_t) continue;
01039 
01040         CKeyBind *kb = reinterpret_cast<CKeyBind*>(b);
01041         if (kb == NULL) continue;
01042 
01043         r += kb->GetBindMenuText();
01044         break;
01045     }
01046 
01047     return r;
01048 }
01049 
01050 class CKeyBindGroup : public  CBindGroup {
01051 public:
01052     CKeyBindGroup(Bitu _keys) : CBindGroup (){
01053         lists=new CBindList[_keys];
01054         for (Bitu i=0;i<_keys;i++) lists[i].clear();
01055         keys=_keys;
01056         configname="key";
01057     }
01058     virtual ~CKeyBindGroup() { delete[] lists; }
01059     CBind * CreateConfigBind(char *& buf) {
01060         if (strncasecmp(buf,configname,strlen(configname))) return 0;
01061         StripWord(buf);char * num=StripWord(buf);
01062         Bitu code=(Bitu)ConvDecWord(num);
01063 #if defined(C_SDL2)
01064         CBind * bind=CreateKeyBind((SDL_Scancode)code);
01065 #else
01066         if (usescancodes) {
01067             if (code<MAX_SDLKEYS) code=scancode_map[code];
01068             else code=0;
01069         }
01070         CBind * bind=CreateKeyBind((SDLKey)code);
01071 #endif
01072         return bind;
01073     }
01074     CBind * CreateEventBind(SDL_Event * event) {
01075         if (event->type!=SDL_KEYDOWN) return 0;
01076 #if defined(C_SDL2)
01077         return CreateKeyBind(event->key.keysym.scancode);
01078 #else
01079         return CreateKeyBind((SDLKey)GetKeyCode(event->key.keysym));
01080 #endif
01081     };
01082     bool CheckEvent(SDL_Event * event) {
01083         if (event->type!=SDL_KEYDOWN && event->type!=SDL_KEYUP) return false;
01084 #if defined(C_SDL2)
01085         Bitu key = event->key.keysym.scancode;
01086 #else
01087         Bitu key=GetKeyCode(event->key.keysym);
01088         assert(Bitu(event->key.keysym.sym)<keys);
01089 #endif
01090 //      LOG_MSG("key type %i is %x [%x %x]",event->type,key,event->key.keysym.sym,event->key.keysym.scancode);
01091 
01092 #if defined(WIN32) && !defined(C_SDL2)
01093         /* HACK: When setting up the Japanese keyboard layout, I'm seeing some bizarre keyboard handling
01094                  from within Windows when pressing the ~ ` (grave) aka Hankaku key. I know it's not hardware
01095                  because when you switch back to English the key works normally as the tilde/grave key.
01096                  What I'm seeing is that pressing the key only sends the "down" event (even though you're
01097                  not holding the key down). When you press the key again, an "up" event is sent immediately
01098                  followed by a "down" event. This is confusing to the mapper, so we work around it here. */
01099         if (isJPkeyboard && key == SDLK_WORLD_12/*Hankaku*/) {
01100             if (event->type == SDL_KEYDOWN) {
01101                 // send down, then up (right?)
01102                 ActivateBindList(&lists[key], 0x7fff, true);
01103                 DeactivateBindList(&lists[key], true);
01104             }
01105 
01106             return 0; // ignore up event
01107         }
01108 #endif
01109 
01110 #if !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
01111         /* HACK: As requested on the issue tracker, on US keyboards, remap the Windows menu key
01112          *       to the "Ro" key.
01113          *
01114          *       If the user has un-bound the Ro key, then allow the menu key to go through
01115          *       unmodified.
01116          *
01117          * TODO: I understand that later PC-9821 systems (post Windows 95, prior to the platform's
01118          *       demise) actually did have Windows and Windows Menu keys.
01119          *
01120          *       I also understand from "Undocumented PC-98" that the actual keyboards for backwards
01121          *       compat reasons will not send these key codes unless a byte is sent to the keyboard
01122          *       on Windows 95 startup to tell the keyboard that it can send those keys.
01123          *
01124          *       This would explain why if you boot a PC-9821 system into Windows 95 "Safe mode"
01125          *       the Windows key does not work.
01126          *
01127          *       So later on, this remap should disable itself IF that byte is sent to signal
01128          *       Windows 95 is running and the Windows keys should be enabled. */
01129         if (IS_PC98_ARCH && host_keyboard_layout != DKM_JPN && key == SDLK_MENU) {
01130             CEvent *x = get_mapper_event_by_name("key_jp_ro");
01131             bool RoMenuRemap = false;
01132 
01133             if (x != NULL) {
01134                 if (!x->bindlist.empty()) {
01135                     RoMenuRemap = true;
01136 
01137                     /* but, if the Ro key has been bound to Menu, do not remap */
01138                     auto &i = x->bindlist.front();
01139                     if (i->type == CBind::keybind_t) {
01140                         auto ik = reinterpret_cast<CKeyBind*>(i);
01141                         RoMenuRemap = false;
01142                         if (ik->key == SDLK_JP_RO)
01143                             RoMenuRemap = true;
01144                     }
01145                 }
01146             }
01147 
01148             if (RoMenuRemap) key = SDLK_JP_RO;
01149         }
01150 #endif
01151 
01152         if (event->type==SDL_KEYDOWN) ActivateBindList(&lists[key],0x7fff,true);
01153         else DeactivateBindList(&lists[key],true);
01154         return 0;
01155     }
01156     CBind * CreateKeyBind(SDLKey _key) {
01157 #if !defined(C_SDL2)
01158         if (!usescancodes) assert((Bitu)_key<keys);
01159 #endif
01160         return new CKeyBind(&lists[(Bitu)_key],_key);
01161     }
01162 private:
01163     const char * ConfigStart(void) {
01164         return configname;
01165     }
01166     const char * BindStart(void) {
01167         return "Key";
01168     }
01169 protected:
01170     const char * configname;
01171     CBindList * lists;
01172     Bitu keys;
01173 };
01174 
01175 class CJAxisBind : public CBind {
01176 public:
01177     CJAxisBind(CBindList * _list,CBindGroup * _group, Bitu _joystick, Bitu _axis,bool _positive) : CBind(_list){
01178         group = _group;
01179         axis = _axis;
01180         positive = _positive;
01181         joystick = _joystick;
01182     }
01183     virtual ~CJAxisBind() {}
01184     virtual void ConfigName(char * buf) override {
01185         sprintf(buf,"%s axis %d %d",group->ConfigStart(),(int)axis,positive ? 1 : 0);
01186     }
01187     virtual void BindName(char * buf) override {
01188         sprintf(buf,"%s Axis %d%s",group->BindStart(),(int)axis,positive ? "+" : "-");
01189     }
01190 
01192     Bitu GetJoystick() const { return joystick; };
01193 
01195     Bitu GetAxis() const { return axis; }
01196 
01198     bool GetPositive() const { return positive; }
01199 
01201     static int GetJoystickDeadzone(int joystick, int axis, bool positive)
01202     {
01203         auto section = control->GetSection("mapper");
01204         auto prop = static_cast<Section_prop*>(section);
01205         auto name = "joy" + std::to_string(joystick + 1) + "deadzone" + std::to_string(axis) + (positive ? "+" : "-");
01206         auto value = prop->Get_double(name);
01207         auto deadzone = static_cast<int>(value * 32767.0);
01208         return deadzone;
01209     }
01210 
01211     void ActivateBind(Bits _value, bool ev_trigger, bool skip_action = false) override
01212     {
01213         /* Since codebase is flawed, we do a simple hack:
01214          * If user-set deadzone exceeds hard-coded value of 25000 we just set it to 25001.
01215          * Other code works as usual, CTriggeredEvent does not have to check if it handles a joy axis.
01216          */
01217 
01218         // activate if we exceed user-defined deadzone
01219         const auto deadzone = GetJoystickDeadzone((int)this->GetJoystick(), (int)this->GetAxis(), this->GetPositive());
01220 
01221         if (_value > deadzone && event->IsTrigger()) 
01222             _value = 25000 + 1;
01223 
01224         CBind::ActivateBind(_value, ev_trigger, skip_action);
01225     }
01226 
01227 protected:
01228     CBindGroup * group;
01229     Bitu axis;
01230     bool positive;
01231     Bitu joystick;
01232 };
01233 
01234 class CJButtonBind : public CBind {
01235 public:
01236     CJButtonBind(CBindList * _list,CBindGroup * _group,Bitu _button) : CBind(_list) {
01237         group = _group;
01238         button=_button;
01239     }
01240     virtual ~CJButtonBind() {}
01241     virtual void ConfigName(char * buf) override {
01242         sprintf(buf,"%s button %d",group->ConfigStart(),(int)button);
01243     }
01244     virtual void BindName(char * buf) override {
01245         sprintf(buf,"%s Button %d",group->BindStart(),(int)button);
01246     }
01247 protected:
01248     CBindGroup * group;
01249     Bitu button;
01250 };
01251 
01252 class CJHatBind : public CBind {
01253 public:
01254     CJHatBind(CBindList * _list,CBindGroup * _group,Bitu _hat,Bit8u _dir) : CBind(_list) {
01255         group = _group;
01256         hat   = _hat;
01257         dir   = _dir;
01258         /* allow only one hat position */
01259         if (dir&SDL_HAT_UP) dir=SDL_HAT_UP;
01260         else if (dir&SDL_HAT_RIGHT) dir=SDL_HAT_RIGHT;
01261         else if (dir&SDL_HAT_DOWN) dir=SDL_HAT_DOWN;
01262         else if (dir&SDL_HAT_LEFT) dir=SDL_HAT_LEFT;
01263         else E_Exit("MAPPER:JOYSTICK:Invalid hat position");
01264     }
01265     virtual ~CJHatBind() {}
01266     virtual void ConfigName(char * buf) override {
01267         sprintf(buf,"%s hat %d %d",group->ConfigStart(),(int)hat,(int)dir);
01268     }
01269     virtual void BindName(char * buf) override {
01270         sprintf(buf,"%s Hat %d %s",group->BindStart(),(int)hat,(dir==SDL_HAT_UP)?"up":
01271                                                         ((dir==SDL_HAT_RIGHT)?"right":
01272                                                         ((dir==SDL_HAT_DOWN)?"down":"left")));
01273     }
01274 protected:
01275     CBindGroup * group;
01276     Bitu hat;
01277     Bit8u dir;
01278 };
01279 
01280 class CStickBindGroup : public  CBindGroup {
01281 public:
01282     CStickBindGroup(Bitu _stick,Bitu _emustick,bool _dummy=false) : CBindGroup (){
01283         stick=_stick;       // the number of the physical device (SDL numbering|)
01284         emustick=_emustick; // the number of the emulated device
01285         sprintf(configname,"stick_%d",(int)emustick);
01286 
01287         sdl_joystick=NULL;
01288         axes=0; buttons=0; hats=0;
01289         button_wrap=0;
01290         button_cap=0; axes_cap=0; hats_cap=0;
01291         emulated_buttons=0; emulated_axes=0; emulated_hats=0;
01292         pos_axis_lists = neg_axis_lists = NULL; /* <- Initialize the pointers to NULL. The C++ compiler won't do it for us. */
01293         button_lists = hat_lists = NULL;
01294 
01295         is_dummy=_dummy;
01296         if (_dummy) return;
01297 
01298         // initialize binding lists and position data
01299         pos_axis_lists=new CBindList[MAXAXIS];
01300         neg_axis_lists=new CBindList[MAXAXIS];
01301         button_lists=new CBindList[MAXBUTTON];
01302         hat_lists=new CBindList[4];
01303         Bitu i;
01304         for (i=0; i<MAXBUTTON; i++) {
01305             button_autofire[i]=0;
01306             old_button_state[i]=0;
01307         }
01308         for(i=0;i<16;i++) old_hat_state[i]=0;
01309         for (i=0; i<MAXAXIS; i++) {
01310             old_pos_axis_state[i]=false;
01311             old_neg_axis_state[i]=false;
01312         }
01313 
01314         // initialize emulated joystick state
01315         emulated_axes=2;
01316         emulated_buttons=2;
01317         emulated_hats=0;
01318         JOYSTICK_Enable(emustick,true);
01319 
01320         sdl_joystick=SDL_JoystickOpen((int)_stick);
01321         if (sdl_joystick==NULL) {
01322             button_wrap=emulated_buttons;
01323             return;
01324         }
01325 
01326         axes=(Bitu)SDL_JoystickNumAxes(sdl_joystick);
01327         if (axes > MAXAXIS) axes = MAXAXIS;
01328         axes_cap=emulated_axes;
01329         if (axes_cap>axes) axes_cap=axes;
01330 
01331         hats=(Bitu)SDL_JoystickNumHats(sdl_joystick);
01332         if (hats > MAXHAT) hats = MAXHAT;
01333         hats_cap=emulated_hats;
01334         if (hats_cap>hats) hats_cap=hats;
01335 
01336         buttons=(Bitu)SDL_JoystickNumButtons(sdl_joystick);
01337         button_wrap=buttons;
01338         button_cap=buttons;
01339         if (button_wrapping_enabled) {
01340             button_wrap=emulated_buttons;
01341             if (buttons>MAXBUTTON_CAP) button_cap = MAXBUTTON_CAP;
01342         }
01343         if (button_wrap > MAXBUTTON) button_wrap = MAXBUTTON;
01344 
01345 #if defined(C_SDL2)
01346         LOG_MSG("Using joystick %s with %d axes, %d buttons and %d hat(s)",SDL_JoystickNameForIndex((int)stick),(int)axes,(int)buttons,(int)hats);
01347 #else
01348         LOG_MSG("Using joystick %s with %d axes, %d buttons and %d hat(s)",SDL_JoystickName((int)stick),(int)axes,(int)buttons,(int)hats);
01349 #endif
01350 
01351         // fetching these at every call simply freezes DOSBox at times so we do it once
01352         // (game tested : Terminal Velocity @ joystick calibration page)
01353         joy1dz1 = static_cast<float>(GetAxisDeadzone(0, 0));
01354         joy1rs1 = static_cast<float>(GetAxisResponse(0, 0));
01355         joy1dz2 = static_cast<float>(GetAxisDeadzone(0, 1));
01356         joy1rs2 = static_cast<float>(GetAxisResponse(0, 1));
01357         joy2dz1 = static_cast<float>(GetAxisDeadzone(1, 0));
01358         joy2rs1 = static_cast<float>(GetAxisResponse(1, 0));
01359     }
01360     virtual ~CStickBindGroup() {
01361         SDL_JoystickClose(sdl_joystick);
01362         if (pos_axis_lists != NULL) delete[] pos_axis_lists;
01363         if (neg_axis_lists != NULL) delete[] neg_axis_lists;
01364         if (button_lists != NULL) delete[] button_lists;
01365         if (hat_lists != NULL) delete[] hat_lists;
01366     }
01367 
01368     CBind * CreateConfigBind(char *& buf) {
01369         if (strncasecmp(configname,buf,strlen(configname))) return 0;
01370         StripWord(buf);char * type=StripWord(buf);
01371         CBind * bind=0;
01372         if (!strcasecmp(type,"axis")) {
01373             Bitu ax=(Bitu)ConvDecWord(StripWord(buf));
01374             bool pos=ConvDecWord(StripWord(buf)) > 0;
01375             bind=CreateAxisBind(ax,pos);
01376         } else if (!strcasecmp(type,"button")) {
01377             Bitu but=(Bitu)ConvDecWord(StripWord(buf));           
01378             bind=CreateButtonBind(but);
01379         } else if (!strcasecmp(type,"hat")) {
01380             Bitu hat=(Bitu)ConvDecWord(StripWord(buf));           
01381             Bit8u dir=(Bit8u)ConvDecWord(StripWord(buf));           
01382             bind=CreateHatBind(hat,dir);
01383         }
01384         return bind;
01385     }
01386     CBind * CreateEventBind(SDL_Event * event) {
01387         if (event->type==SDL_JOYAXISMOTION) {
01388             if ((unsigned int)event->jaxis.which!=(unsigned int)stick) return 0;
01389 #if defined (REDUCE_JOYSTICK_POLLING)
01390             if (event->jaxis.axis>=axes) return 0;
01391 #endif
01392             if (abs(event->jaxis.value)<25000) return 0;
01393             return CreateAxisBind(event->jaxis.axis,event->jaxis.value>0);
01394         } else if (event->type==SDL_JOYBUTTONDOWN) {
01395             if ((unsigned int)event->jbutton.which!=(unsigned int)stick) return 0;
01396 #if defined (REDUCE_JOYSTICK_POLLING)
01397             return CreateButtonBind(event->jbutton.button%button_wrap);
01398 #else
01399             return CreateButtonBind(event->jbutton.button);
01400 #endif
01401         } else if (event->type==SDL_JOYHATMOTION) {
01402             if ((unsigned int)event->jhat.which!=(unsigned int)stick) return 0;
01403             if (event->jhat.value==0) return 0;
01404             if (event->jhat.value>(SDL_HAT_UP|SDL_HAT_RIGHT|SDL_HAT_DOWN|SDL_HAT_LEFT)) return 0;
01405             return CreateHatBind(event->jhat.hat,event->jhat.value);
01406         } else return 0;
01407     }
01408 
01409     virtual bool CheckEvent(SDL_Event * event) {
01410         SDL_JoyAxisEvent * jaxis = NULL;
01411         SDL_JoyButtonEvent * jbutton = NULL;
01412 
01413         switch(event->type) {
01414             case SDL_JOYAXISMOTION:
01415                 jaxis = &event->jaxis;
01416                 if((unsigned int)jaxis->which == (unsigned int)stick) {
01417                     if(jaxis->axis == 0)
01418                         JOYSTICK_Move_X(emustick,(float)(jaxis->value/32768.0));
01419                     else if(jaxis->axis == 1)
01420                         JOYSTICK_Move_Y(emustick,(float)(jaxis->value/32768.0));
01421                 }
01422                 break;
01423             case SDL_JOYBUTTONDOWN:
01424             case SDL_JOYBUTTONUP:
01425                 if (emulated_buttons != 0) {
01426                     jbutton = &event->jbutton;
01427                     bool state;
01428                     state=jbutton->type==SDL_JOYBUTTONDOWN;
01429                     Bitu but = jbutton->button % emulated_buttons;
01430                     if ((unsigned int)jbutton->which == (unsigned int)stick) {
01431                         JOYSTICK_Button(emustick, but, state);
01432                     }
01433                 }
01434                 break;
01435         }
01436         return false;
01437     }
01438 
01439     virtual void UpdateJoystick() {
01440         if (is_dummy) return;
01441         /* query SDL joystick and activate bindings */
01442         ActivateJoystickBoundEvents();
01443 
01444         bool button_pressed[MAXBUTTON];
01445         Bitu i;
01446         for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
01447         for (i=0; i<MAX_VJOY_BUTTONS; i++) {
01448             if (virtual_joysticks[emustick].button_pressed[i])
01449                 button_pressed[i % button_wrap]=true;
01450         }
01451         for (i=0; i<emulated_buttons; i++) {
01452             if (autofire && (button_pressed[i]))
01453                 JOYSTICK_Button(emustick,i,(++button_autofire[i])&1);
01454             else
01455                 JOYSTICK_Button(emustick,i,button_pressed[i]);
01456         }
01457 
01458         auto v = GetJoystickVector((int)emustick, 0, 0, 1);
01459         JOYSTICK_Move_X(emustick, v.X);
01460         JOYSTICK_Move_Y(emustick, v.Y);
01461     }
01462 
01463     void ActivateJoystickBoundEvents() {
01464         if (GCC_UNLIKELY(sdl_joystick==NULL)) return;
01465 
01466         Bitu i;
01467 
01468         bool button_pressed[MAXBUTTON];
01469         for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
01470         /* read button states */
01471         for (i=0; i<button_cap; i++) {
01472             if (SDL_JoystickGetButton(sdl_joystick,(int)i))
01473                 button_pressed[i % button_wrap]=true;
01474         }
01475         for (i=0; i<button_wrap; i++) {
01476             /* activate binding if button state has changed */
01477             if (button_pressed[i]!=old_button_state[i]) {
01478                 if (button_pressed[i]) ActivateBindList(&button_lists[i],32767,true);
01479                 else DeactivateBindList(&button_lists[i],true);
01480                 old_button_state[i]=button_pressed[i];
01481             }
01482         }
01483         
01484         int* axis_map = stick == 0 ? &joy1axes[0] : &joy2axes[0];
01485         for (i=0; i<axes; i++) {
01486             int i1 = axis_map[i];
01487             Sint16 caxis_pos=SDL_JoystickGetAxis(sdl_joystick,i1);
01488             /* activate bindings for joystick position */
01489             if (caxis_pos>1) {
01490                 if (old_neg_axis_state[i]) {
01491                     DeactivateBindList(&neg_axis_lists[i],false);
01492                     old_neg_axis_state[i] = false;
01493                 }
01494                 ActivateBindList(&pos_axis_lists[i],caxis_pos,false);
01495                 old_pos_axis_state[i] = true;
01496             } else if (caxis_pos<-1) {
01497                 if (old_pos_axis_state[i]) {
01498                     DeactivateBindList(&pos_axis_lists[i],false);
01499                     old_pos_axis_state[i] = false;
01500                 }
01501                 if (caxis_pos!=-32768) caxis_pos=(Sint16)abs(caxis_pos);
01502                 else caxis_pos=32767;
01503                 ActivateBindList(&neg_axis_lists[i],caxis_pos,false);
01504                 old_neg_axis_state[i] = true;
01505             } else {
01506                 /* center */
01507                 if (old_pos_axis_state[i]) {
01508                     DeactivateBindList(&pos_axis_lists[i],false);
01509                     old_pos_axis_state[i] = false;
01510                 }
01511                 if (old_neg_axis_state[i]) {
01512                     DeactivateBindList(&neg_axis_lists[i],false);
01513                     old_neg_axis_state[i] = false;
01514                 }
01515             }
01516         }
01517 
01518         for (i=0; i<hats; i++) {
01519             Uint8 chat_state=SDL_JoystickGetHat(sdl_joystick,(int)i);
01520 
01521             /* activate binding if hat state has changed */
01522             if ((chat_state & SDL_HAT_UP) != (old_hat_state[i] & SDL_HAT_UP)) {
01523                 if (chat_state & SDL_HAT_UP) ActivateBindList(&hat_lists[(i<<2)+0],32767,true);
01524                 else DeactivateBindList(&hat_lists[(i<<2)+0],true);
01525             }
01526             if ((chat_state & SDL_HAT_RIGHT) != (old_hat_state[i] & SDL_HAT_RIGHT)) {
01527                 if (chat_state & SDL_HAT_RIGHT) ActivateBindList(&hat_lists[(i<<2)+1],32767,true);
01528                 else DeactivateBindList(&hat_lists[(i<<2)+1],true);
01529             }
01530             if ((chat_state & SDL_HAT_DOWN) != (old_hat_state[i] & SDL_HAT_DOWN)) {
01531                 if (chat_state & SDL_HAT_DOWN) ActivateBindList(&hat_lists[(i<<2)+2],32767,true);
01532                 else DeactivateBindList(&hat_lists[(i<<2)+2],true);
01533             }
01534             if ((chat_state & SDL_HAT_LEFT) != (old_hat_state[i] & SDL_HAT_LEFT)) {
01535                 if (chat_state & SDL_HAT_LEFT) ActivateBindList(&hat_lists[(i<<2)+3],32767,true);
01536                 else DeactivateBindList(&hat_lists[(i<<2)+3],true);
01537             }
01538             old_hat_state[i]=chat_state;
01539         }
01540     }
01541 
01542 private:
01543     float joy1dz1 = 0, joy1rs1 = 0, joy1dz2 = 0, joy1rs2 = 0, joy2dz1 = 0, joy2rs1 = 0;
01544     CBind * CreateAxisBind(Bitu axis,bool positive) {
01545         if (axis<axes) {
01546             if (positive) return new CJAxisBind(&pos_axis_lists[axis],this,stick,axis,positive);
01547             else return new CJAxisBind(&neg_axis_lists[axis],this,stick,axis,positive);
01548         }
01549         return NULL;
01550     }
01551     CBind * CreateButtonBind(Bitu button) {
01552         if (button<button_wrap) 
01553             return new CJButtonBind(&button_lists[button],this,button);
01554         return NULL;
01555     }
01556     CBind * CreateHatBind(Bitu hat,Bit8u value) {
01557         Bitu hat_dir;
01558         if (value&SDL_HAT_UP) hat_dir=0;
01559         else if (value&SDL_HAT_RIGHT) hat_dir=1;
01560         else if (value&SDL_HAT_DOWN) hat_dir=2;
01561         else if (value&SDL_HAT_LEFT) hat_dir=3;
01562         else return NULL;
01563         return new CJHatBind(&hat_lists[(hat<<2)+hat_dir],this,hat,value);
01564     }
01565     const char * ConfigStart(void) {
01566         return configname;
01567     }
01568     const char * BindStart(void) {
01569 #if defined(C_SDL2)
01570         if (sdl_joystick!=NULL) return SDL_JoystickNameForIndex((int)stick);
01571 #else
01572         if (sdl_joystick!=NULL) return SDL_JoystickName((int)stick);
01573 #endif
01574         else return "[missing joystick]";
01575     }
01576 
01577     static float GetAxisDeadzone(int joystick, int thumbStick)
01578     {
01579         auto section = control->GetSection("joystick");
01580         auto prop = static_cast<Section_prop*>(section);
01581         auto name = "joy" + std::to_string(joystick + 1) + "deadzone" + std::to_string(thumbStick + 1);
01582         auto deadzone = static_cast<float>(prop->Get_double(name));
01583         return deadzone;
01584     }
01585     
01586     static float GetAxisResponse(int joystick, int thumbStick)
01587     {
01588         auto section = control->GetSection("joystick");
01589         auto prop = static_cast<Section_prop*>(section);
01590         auto name = "joy" + std::to_string(joystick + 1) + "response" + std::to_string(thumbStick + 1);
01591         auto response = static_cast<float>(prop->Get_double(name));
01592         return response;
01593     }
01594 
01595     static void ProcessInput(Bit16s x, Bit16s y, float deadzone, DOSBox_Vector2& joy)
01596     {
01597         // http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html
01598 
01599         joy = DOSBox_Vector2((x + 0.5f) / 32767.5f, (y + 0.5f) / 32767.5f);
01600 
01601         float m = joy.magnitude();
01602         DOSBox_Vector2 n = joy.normalized();
01603         joy = m < deadzone ? DOSBox_Vector2() : n * ((m - deadzone) / (1.0f - deadzone));
01604 
01605         DOSBox_Vector2 min = DOSBox_Vector2(-1.0f, -1.0f);
01606         DOSBox_Vector2 max = DOSBox_Vector2(+1.0f, +1.0f);
01607         joy = joy.clamp(min, max);
01608     }
01609 
01610 protected:
01611     CBindList * pos_axis_lists;
01612     CBindList * neg_axis_lists;
01613     CBindList * button_lists;
01614     CBindList * hat_lists;
01615     Bitu stick,emustick,axes,buttons,hats,emulated_axes,emulated_buttons,emulated_hats;
01616     Bitu button_wrap,button_cap,axes_cap,hats_cap;
01617     SDL_Joystick * sdl_joystick;
01618     char configname[10];
01619     Bitu button_autofire[MAXBUTTON] = {};
01620     bool old_button_state[MAXBUTTON] = {};
01621     bool old_pos_axis_state[MAXAXIS] = {};
01622     bool old_neg_axis_state[MAXAXIS] = {};
01623     Uint8 old_hat_state[16] = {};
01624     bool is_dummy;
01625 
01626     DOSBox_Vector2 GetJoystickVector(int joystick, int thumbStick, int xAxis, int yAxis) const
01627     {
01628         Bit16s x = virtual_joysticks[joystick].axis_pos[xAxis];
01629         Bit16s y = virtual_joysticks[joystick].axis_pos[yAxis];
01630         float deadzone;
01631         float response;
01632         if (joystick == 0)
01633         {
01634             if (thumbStick == 0)
01635             {
01636                 deadzone = joy1dz1;
01637                 response = joy1rs1;
01638             }
01639             else
01640             {
01641                 deadzone = joy1dz2;
01642                 response = joy1rs2;
01643             }
01644         }
01645         else
01646         {
01647             deadzone = joy2dz1;
01648             response = joy2rs1;
01649         }
01650         DOSBox_Vector2 v;
01651         ProcessInput(x, y, deadzone, v);
01652         float x1 = (sgn(v.X) * fabs(pow(v.X, response)));
01653         float y1 = (sgn(v.Y) * fabs(pow(v.Y, response)));
01654         DOSBox_Vector2 v1(x1, y1);
01655         return v1;
01656     }
01657 };
01658 
01659 class C4AxisBindGroup : public  CStickBindGroup {
01660 public:
01661     C4AxisBindGroup(Bitu _stick,Bitu _emustick) : CStickBindGroup (_stick,_emustick){
01662         emulated_axes=4;
01663         emulated_buttons=4;
01664         if (button_wrapping_enabled) button_wrap=emulated_buttons;
01665 
01666         axes_cap=emulated_axes;
01667         if (axes_cap>axes) axes_cap=axes;
01668         hats_cap=emulated_hats;
01669         if (hats_cap>hats) hats_cap=hats;
01670 
01671         JOYSTICK_Enable(1,true);
01672     }
01673     virtual ~C4AxisBindGroup() {}
01674 
01675     bool CheckEvent(SDL_Event * event) {
01676         SDL_JoyAxisEvent * jaxis = NULL;
01677         SDL_JoyButtonEvent * jbutton = NULL;
01678         Bitu but = 0;
01679 
01680         switch(event->type) {
01681             case SDL_JOYAXISMOTION:
01682                 jaxis = &event->jaxis;
01683                 if((unsigned int)jaxis->which == (unsigned int)stick && jaxis->axis < 4) {
01684                     if(jaxis->axis & 1)
01685                         JOYSTICK_Move_Y(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
01686                     else
01687                         JOYSTICK_Move_X(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
01688                 }
01689                 break;
01690             case SDL_JOYBUTTONDOWN:
01691             case SDL_JOYBUTTONUP:
01692                 jbutton = &event->jbutton;
01693                 bool state;
01694                 state=jbutton->type==SDL_JOYBUTTONDOWN;
01695                 but = jbutton->button % emulated_buttons;
01696                 if ((unsigned int)jbutton->which == (unsigned int)stick) {
01697                     JOYSTICK_Button((but >> 1),(but & 1),state);
01698                 }
01699                 break;
01700         }
01701         return false;
01702     }
01703 
01704     virtual void UpdateJoystick() {
01705         /* query SDL joystick and activate bindings */
01706         ActivateJoystickBoundEvents();
01707 
01708         bool button_pressed[MAXBUTTON];
01709         Bitu i;
01710         for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
01711         for (i=0; i<MAX_VJOY_BUTTONS; i++) {
01712             if (virtual_joysticks[0].button_pressed[i])
01713                 button_pressed[i % button_wrap]=true;
01714             
01715         }
01716         for (i=0; i<emulated_buttons; i++) {
01717             if (autofire && (button_pressed[i]))
01718                 JOYSTICK_Button(i>>1,i&1,(++button_autofire[i])&1);
01719             else
01720                 JOYSTICK_Button(i>>1,i&1,button_pressed[i]);
01721         }
01722 
01723         auto v1 = GetJoystickVector(0, 0, 0, 1);
01724         auto v2 = GetJoystickVector(0, 1, 2, 3);
01725         JOYSTICK_Move_X(0, v1.X);
01726         JOYSTICK_Move_Y(0, v1.Y);
01727         JOYSTICK_Move_X(1, v2.X);
01728         JOYSTICK_Move_Y(1, v2.Y);
01729     }
01730 };
01731 
01732 class CFCSBindGroup : public  CStickBindGroup {
01733 public:
01734     CFCSBindGroup(Bitu _stick,Bitu _emustick) : CStickBindGroup (_stick,_emustick){
01735         emulated_axes=4;
01736         emulated_buttons=4;
01737         old_hat_position=0;
01738         emulated_hats=1;
01739         if (button_wrapping_enabled) button_wrap=emulated_buttons;
01740 
01741         axes_cap=emulated_axes;
01742         if (axes_cap>axes) axes_cap=axes;
01743         hats_cap=emulated_hats;
01744         if (hats_cap>hats) hats_cap=hats;
01745 
01746         JOYSTICK_Enable(1,true);
01747         JOYSTICK_Move_Y(1,1.0);
01748     }
01749     virtual ~CFCSBindGroup() {}
01750 
01751     bool CheckEvent(SDL_Event * event) {
01752         SDL_JoyAxisEvent * jaxis = NULL;
01753         SDL_JoyButtonEvent * jbutton = NULL;
01754         SDL_JoyHatEvent * jhat = NULL;
01755         Bitu but = 0;
01756 
01757         switch(event->type) {
01758             case SDL_JOYAXISMOTION:
01759                 jaxis = &event->jaxis;
01760                 if((unsigned int)jaxis->which == (unsigned int)stick) {
01761                     if(jaxis->axis == 0)
01762                         JOYSTICK_Move_X(0,(float)(jaxis->value/32768.0));
01763                     else if(jaxis->axis == 1)
01764                         JOYSTICK_Move_Y(0,(float)(jaxis->value/32768.0));
01765                     else if(jaxis->axis == 2)
01766                         JOYSTICK_Move_X(1,(float)(jaxis->value/32768.0));
01767                 }
01768                 break;
01769             case SDL_JOYHATMOTION:
01770                 jhat = &event->jhat;
01771                 if((unsigned int)jhat->which == (unsigned int)stick) DecodeHatPosition(jhat->value);
01772                 break;
01773             case SDL_JOYBUTTONDOWN:
01774             case SDL_JOYBUTTONUP:
01775                 jbutton = &event->jbutton;
01776                 bool state;
01777                 state=jbutton->type==SDL_JOYBUTTONDOWN;
01778                 but = jbutton->button % emulated_buttons;
01779                 if ((unsigned int)jbutton->which == (unsigned int)stick) {
01780                         JOYSTICK_Button((but >> 1),(but & 1),state);
01781                 }
01782                 break;
01783         }
01784         return false;
01785     }
01786 
01787     virtual void UpdateJoystick() {
01788         /* query SDL joystick and activate bindings */
01789         ActivateJoystickBoundEvents();
01790 
01791         bool button_pressed[MAXBUTTON];
01792         Bitu i;
01793         for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
01794         for (i=0; i<MAX_VJOY_BUTTONS; i++) {
01795             if (virtual_joysticks[0].button_pressed[i])
01796                 button_pressed[i % button_wrap]=true;
01797             
01798         }
01799         for (i=0; i<emulated_buttons; i++) {
01800             if (autofire && (button_pressed[i]))
01801                 JOYSTICK_Button(i>>1,i&1,(++button_autofire[i])&1);
01802             else
01803                 JOYSTICK_Button(i>>1,i&1,button_pressed[i]);
01804         }
01805 
01806         auto v1 = GetJoystickVector(0, 0, 0, 1);
01807         auto v2 = GetJoystickVector(0, 1, 2, 3);
01808         JOYSTICK_Move_X(0, v1.X);
01809         JOYSTICK_Move_Y(0, v1.Y);
01810         JOYSTICK_Move_X(1, v2.X);
01811 
01812         Uint8 hat_pos=0;
01813         if (virtual_joysticks[0].hat_pressed[0]) hat_pos|=SDL_HAT_UP;
01814         else if (virtual_joysticks[0].hat_pressed[2]) hat_pos|=SDL_HAT_DOWN;
01815         if (virtual_joysticks[0].hat_pressed[3]) hat_pos|=SDL_HAT_LEFT;
01816         else if (virtual_joysticks[0].hat_pressed[1]) hat_pos|=SDL_HAT_RIGHT;
01817 
01818         if (hat_pos!=old_hat_position) {
01819             DecodeHatPosition(hat_pos);
01820             old_hat_position=hat_pos;
01821         }
01822     }
01823 
01824 private:
01825     Uint8 old_hat_position;
01826 
01827     void DecodeHatPosition(Uint8 hat_pos) {
01828         switch(hat_pos) {
01829             case SDL_HAT_CENTERED:
01830                 JOYSTICK_Move_Y(1,1.0);
01831                 break;
01832             case SDL_HAT_UP:
01833                 JOYSTICK_Move_Y(1,-1.0);
01834                 break;
01835             case SDL_HAT_RIGHT:
01836                 JOYSTICK_Move_Y(1,-0.5);
01837                 break;
01838             case SDL_HAT_DOWN:
01839                 JOYSTICK_Move_Y(1,0.0);
01840                 break;
01841             case SDL_HAT_LEFT:
01842                 JOYSTICK_Move_Y(1,0.5);
01843                 break;
01844             case SDL_HAT_LEFTUP:
01845                 if(JOYSTICK_GetMove_Y(1) < 0)
01846                     JOYSTICK_Move_Y(1,0.5);
01847                 else
01848                     JOYSTICK_Move_Y(1,-1.0);
01849                 break;
01850             case SDL_HAT_RIGHTUP:
01851                 if(JOYSTICK_GetMove_Y(1) < -0.7)
01852                     JOYSTICK_Move_Y(1,-0.5);
01853                 else
01854                     JOYSTICK_Move_Y(1,-1.0);
01855                 break;
01856             case SDL_HAT_RIGHTDOWN:
01857                 if(JOYSTICK_GetMove_Y(1) < -0.2)
01858                     JOYSTICK_Move_Y(1,0.0);
01859                 else
01860                     JOYSTICK_Move_Y(1,-0.5);
01861                 break;
01862             case SDL_HAT_LEFTDOWN:
01863                 if(JOYSTICK_GetMove_Y(1) > 0.2)
01864                     JOYSTICK_Move_Y(1,0.0);
01865                 else
01866                     JOYSTICK_Move_Y(1,0.5);
01867                 break;
01868         }
01869     }
01870 };
01871 
01872 class CCHBindGroup : public  CStickBindGroup {
01873 public:
01874     CCHBindGroup(Bitu _stick,Bitu _emustick) : CStickBindGroup (_stick,_emustick){
01875         emulated_axes=4;
01876         emulated_buttons=6;
01877         emulated_hats=1;
01878         if (button_wrapping_enabled) button_wrap=emulated_buttons;
01879 
01880         axes_cap=emulated_axes;
01881         if (axes_cap>axes) axes_cap=axes;
01882         hats_cap=emulated_hats;
01883         if (hats_cap>hats) hats_cap=hats;
01884 
01885         JOYSTICK_Enable(1,true);
01886         button_state=0;
01887     }
01888     virtual ~CCHBindGroup() {}
01889 
01890     bool CheckEvent(SDL_Event * event) {
01891         SDL_JoyAxisEvent * jaxis = NULL;
01892         SDL_JoyButtonEvent * jbutton = NULL;
01893         SDL_JoyHatEvent * jhat = NULL;
01894         Bitu but = 0;
01895         static unsigned const button_magic[6]={0x02,0x04,0x10,0x100,0x20,0x200};
01896         static unsigned const hat_magic[2][5]={{0x8888,0x8000,0x800,0x80,0x08},
01897                                {0x5440,0x4000,0x400,0x40,0x1000}};
01898         switch(event->type) {
01899             case SDL_JOYAXISMOTION:
01900                 jaxis = &event->jaxis;
01901                 if((unsigned int)jaxis->which == (unsigned int)stick && jaxis->axis < 4) {
01902                     if(jaxis->axis & 1)
01903                         JOYSTICK_Move_Y(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
01904                     else
01905                         JOYSTICK_Move_X(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
01906                 }
01907                 break;
01908             case SDL_JOYHATMOTION:
01909                 jhat = &event->jhat;
01910                 if((unsigned int)jhat->which == (unsigned int)stick && jhat->hat < 2) {
01911                     if(jhat->value == SDL_HAT_CENTERED)
01912                         button_state&=~hat_magic[jhat->hat][0];
01913                     if(jhat->value & SDL_HAT_UP)
01914                         button_state|=hat_magic[jhat->hat][1];
01915                     if(jhat->value & SDL_HAT_RIGHT)
01916                         button_state|=hat_magic[jhat->hat][2];
01917                     if(jhat->value & SDL_HAT_DOWN)
01918                         button_state|=hat_magic[jhat->hat][3];
01919                     if(jhat->value & SDL_HAT_LEFT)
01920                         button_state|=hat_magic[jhat->hat][4];
01921                 }
01922                 break;
01923             case SDL_JOYBUTTONDOWN:
01924                 jbutton = &event->jbutton;
01925                 but = jbutton->button % emulated_buttons;
01926                 if ((unsigned int)jbutton->which == (unsigned int)stick)
01927                     button_state|=button_magic[but];
01928                 break;
01929             case SDL_JOYBUTTONUP:
01930                 jbutton = &event->jbutton;
01931                 but = jbutton->button % emulated_buttons;
01932                 if ((unsigned int)jbutton->which == (unsigned int)stick)
01933                     button_state&=~button_magic[but];
01934                 break;
01935         }
01936 
01937         unsigned i;
01938         Bit16u j;
01939         j=button_state;
01940         for(i=0;i<16;i++) if (j & 1) break; else j>>=1;
01941         JOYSTICK_Button(0,0,i&1);
01942         JOYSTICK_Button(0,1,(i>>1)&1);
01943         JOYSTICK_Button(1,0,(i>>2)&1);
01944         JOYSTICK_Button(1,1,(i>>3)&1);
01945         return false;
01946     }
01947 
01948     void UpdateJoystick() {
01949         static unsigned const button_priority[6]={7,11,13,14,5,6};
01950         static unsigned const hat_priority[2][4]={{0,1,2,3},{8,9,10,12}};
01951 
01952         /* query SDL joystick and activate bindings */
01953         ActivateJoystickBoundEvents();
01954 
01955         auto v1 = GetJoystickVector(0, 0, 0, 1);
01956         auto v2 = GetJoystickVector(0, 1, 2, 3);
01957         JOYSTICK_Move_X(0, v1.X);
01958         JOYSTICK_Move_X(0, v1.Y);
01959         JOYSTICK_Move_X(1, v2.X);
01960         JOYSTICK_Move_X(1, v2.Y);
01961 
01962         Bitu bt_state=15;
01963 
01964         Bitu i;
01965         for (i=0; i<(hats<2?hats:2); i++) {
01966             Uint8 hat_pos=0;
01967             if (virtual_joysticks[0].hat_pressed[(i<<2)+0]) hat_pos|=SDL_HAT_UP;
01968             else if (virtual_joysticks[0].hat_pressed[(i<<2)+2]) hat_pos|=SDL_HAT_DOWN;
01969             if (virtual_joysticks[0].hat_pressed[(i<<2)+3]) hat_pos|=SDL_HAT_LEFT;
01970             else if (virtual_joysticks[0].hat_pressed[(i<<2)+1]) hat_pos|=SDL_HAT_RIGHT;
01971 
01972             if (hat_pos & SDL_HAT_UP)
01973                 if (bt_state>hat_priority[i][0]) bt_state=hat_priority[i][0];
01974             if (hat_pos & SDL_HAT_DOWN)
01975                 if (bt_state>hat_priority[i][1]) bt_state=hat_priority[i][1];
01976             if (hat_pos & SDL_HAT_RIGHT)
01977                 if (bt_state>hat_priority[i][2]) bt_state=hat_priority[i][2];
01978             if (hat_pos & SDL_HAT_LEFT)
01979                 if (bt_state>hat_priority[i][3]) bt_state=hat_priority[i][3];
01980         }
01981 
01982         bool button_pressed[MAXBUTTON];
01983         for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
01984         for (i=0; i<MAX_VJOY_BUTTONS; i++) {
01985             if (virtual_joysticks[0].button_pressed[i])
01986                 button_pressed[i % button_wrap]=true;
01987             
01988         }
01989         for (i=0; i<6; i++) {
01990             if ((button_pressed[i]) && (bt_state>button_priority[i]))
01991                 bt_state=button_priority[i];
01992         }
01993 
01994         if (bt_state>15) bt_state=15;
01995         JOYSTICK_Button(0,0,(bt_state&8)==0);
01996         JOYSTICK_Button(0,1,(bt_state&4)==0);
01997         JOYSTICK_Button(1,0,(bt_state&2)==0);
01998         JOYSTICK_Button(1,1,(bt_state&1)==0);
01999     }
02000 
02001 protected:
02002     Bit16u button_state;
02003 };
02004 
02005 void CBindGroup::ActivateBindList(CBindList * list,Bits value,bool ev_trigger) {
02006         assert(list);
02007     Bitu validmod=0;
02008     CBindList_it it;
02009     for (it=list->begin();it!=list->end();++it) {
02010         if (((*it)->mods & mapper.mods) == (*it)->mods) {
02011             if (validmod<(*it)->mods) validmod=(*it)->mods;
02012         }
02013     }
02014     for (it=list->begin();it!=list->end();++it) {
02015         if (validmod==(*it)->mods) (*it)->ActivateBind(value,ev_trigger);
02016     }
02017 }
02018 
02019 void CBindGroup::DeactivateBindList(CBindList * list,bool ev_trigger) {
02020         assert(list);
02021     CBindList_it it;
02022     for (it=list->begin();it!=list->end();++it) {
02023         (*it)->DeActivateBind(ev_trigger);
02024     }
02025 }
02026 
02027 class CButton {
02028 public:
02029     virtual ~CButton(){};
02030     CButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy) {
02031         x=_x;y=_y;dx=_dx;dy=_dy;
02032         buttons.push_back(this);
02033         bkcolor=CLR_BLACK;
02034         color=CLR_WHITE;
02035         enabled=true;
02036         invert=false;
02037         press=false;
02038     }
02039     virtual void Draw(void) {
02040         Bit8u bg;
02041 
02042         if (!enabled) return;
02043 
02044         if (!invert)
02045             bg = press ? Bit8u(CLR_DARKGREEN) : bkcolor;
02046         else
02047             bg = color;
02048 
02049 #if defined(C_SDL2)
02050         Bit8u * point=((Bit8u *)mapper.draw_surface->pixels)+(y*mapper.draw_surface->w)+x;
02051 #else
02052         Bit8u * point=((Bit8u *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x;
02053 #endif
02054         for (Bitu lines=0;lines<dy;lines++)  {
02055             if (lines==0 || lines==(dy-1)) {
02056                 for (Bitu cols=0;cols<dx;cols++) *(point+cols)=color;
02057             } else {
02058                 for (Bitu cols=1;cols<(dx-1);cols++) *(point+cols)=bg;
02059                 *point=color;*(point+dx-1)=color;
02060             }
02061 #if defined(C_SDL2)
02062             point+=mapper.draw_surface->w;
02063 #else
02064             point+=mapper.surface->pitch;
02065 #endif
02066         }
02067     }
02068     virtual bool OnTop(Bitu _x,Bitu _y) {
02069         return ( enabled && (_x>=x) && (_x<x+dx) && (_y>=y) && (_y<y+dy));
02070     }
02071     virtual void BindColor(void) {}
02072     virtual void Click(void) {}
02073     void Enable(bool yes) { 
02074         enabled=yes; 
02075         mapper.redraw=true;
02076     }
02077     void SetInvert(bool inv) {
02078         invert=inv;
02079         mapper.redraw=true;
02080     }
02081     void SetPress(bool p) {
02082         press=p;
02083         mapper.redraw=true;
02084     }
02085     virtual void RebindRedraw(void) {}
02086     void SetColor(Bit8u _col) { color=_col; }
02087 protected:
02088     Bitu x,y,dx,dy;
02089     Bit8u color;
02090     Bit8u bkcolor;
02091     bool press;
02092     bool invert;
02093     bool enabled;
02094 };
02095 
02096 static CButton *press_select = NULL;
02097 
02098 class CTextButton : public CButton {
02099 public:
02100     CTextButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text) : CButton(_x,_y,_dx,_dy) { text=_text; invertw=0; }
02101     virtual ~CTextButton() {}
02102     void Draw(void) {
02103         Bit8u fg,bg;
02104 
02105         if (!enabled) return;
02106 
02107         if (!invert) {
02108             fg = color;
02109             bg = press ? Bit8u(CLR_DARKGREEN) : bkcolor;
02110         }
02111         else {
02112             fg = bkcolor;
02113             bg = color;
02114         }
02115 
02116         CButton::Draw();
02117         DrawText(x+2,y+2,text,fg,bg);
02118 
02119 #if defined(C_SDL2)
02120         Bit8u * point=((Bit8u *)mapper.draw_surface->pixels)+(y*mapper.draw_surface->w)+x;
02121 #else
02122         Bit8u * point=((Bit8u *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x;
02123 #endif
02124         for (Bitu lines=0;lines<(dy-1);lines++) {
02125             if (lines != 0) {
02126                 for (Bitu cols=1;cols<=invertw;cols++) {
02127                     if (*(point+cols) == color)
02128                         *(point+cols) = bkcolor;
02129                     else
02130                         *(point+cols) = color;
02131                 }
02132             }
02133 #if defined(C_SDL2)
02134             point+=mapper.draw_surface->w;
02135 #else
02136             point+=mapper.surface->pitch;
02137 #endif
02138         }
02139     }
02140     void SetText(const char *_text) {
02141         text = _text;
02142     }
02143     void SetPartialInvert(double a) {
02144         if (a < 0) a = 0;
02145         if (a > 1) a = 1;
02146         invertw = (Bitu)floor((a * (dx - 2)) + 0.5);
02147         if (invertw > (dx - 2)) invertw = dx - 2;
02148         mapper.redraw=true;
02149     }
02150 protected:
02151     const char * text;
02152     Bitu invertw;
02153 };
02154 
02155 class CEventButton;
02156 static CEventButton * last_clicked = NULL;
02157 
02158 class CEventButton : public CTextButton {
02159 public:
02160     CEventButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,CEvent * _event) 
02161     : CTextButton(_x,_y,_dx,_dy,_text)  { 
02162         event=_event;   
02163     }
02164     virtual ~CEventButton() {}
02165     void BindColor(void) {
02166         this->SetColor(event->bindlist.begin() == event->bindlist.end() ? CLR_GREY : CLR_WHITE);
02167     }
02168     void Click(void) {
02169         if (last_clicked) last_clicked->BindColor();
02170         this->SetColor(event->bindlist.begin() == event->bindlist.end() ? CLR_DARKGREEN : CLR_GREEN);
02171         SetActiveEvent(event);
02172         last_clicked=this;
02173     }
02174     void RebindRedraw(void) {
02175         Click();//HACK!
02176     }
02177 protected:
02178     CEvent * event;
02179 };
02180 
02181 class CCaptionButton : public CButton {
02182 public:
02183     CCaptionButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy) : CButton(_x,_y,_dx,_dy){
02184         caption[0]=0;
02185     }
02186     virtual ~CCaptionButton() {}
02187     void Change(const char * format,...) GCC_ATTRIBUTE(__format__(__printf__,2,3));
02188 
02189     void Draw(void) {
02190         if (!enabled) return;
02191         DrawText(x+2,y+2,caption,color);
02192     }
02193 protected:
02194     char caption[128];
02195 };
02196 
02197 void CCaptionButton::Change(const char * format,...) {
02198     va_list msg;
02199     va_start(msg,format);
02200     vsprintf(caption,format,msg);
02201     va_end(msg);
02202     mapper.redraw=true;
02203 }       
02204 
02205 void RedrawMapperBindButton(CEvent *ev);
02206 
02207 class CBindButton : public CTextButton {
02208 public: 
02209     CBindButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,BB_Types _type) 
02210     : CTextButton(_x,_y,_dx,_dy,_text)  { 
02211         type=_type;
02212     }
02213     virtual ~CBindButton() {}
02214     void Click(void) {
02215         switch (type) {
02216         case BB_Add: 
02217             mapper.addbind=true;
02218             SetActiveBind(0);
02219             change_action_text("Press a key/joystick button or move the joystick.",CLR_RED);
02220             break;
02221         case BB_Del:
02222             assert(mapper.aevent != NULL);
02223             if (mapper.abindit!=mapper.aevent->bindlist.end())  {
02224                 delete (*mapper.abindit);
02225                 mapper.abindit=mapper.aevent->bindlist.erase(mapper.abindit);
02226                 if (mapper.abindit==mapper.aevent->bindlist.end()) 
02227                     mapper.abindit=mapper.aevent->bindlist.begin();
02228             }
02229             if (mapper.abindit!=mapper.aevent->bindlist.end()) SetActiveBind(*(mapper.abindit));
02230             else SetActiveBind(0);
02231             RedrawMapperBindButton(mapper.aevent);
02232             break;
02233         case BB_Next:
02234             assert(mapper.aevent != NULL);
02235             if (mapper.abindit!=mapper.aevent->bindlist.end()) 
02236                 ++mapper.abindit;
02237             if (mapper.abindit==mapper.aevent->bindlist.end()) 
02238                 mapper.abindit=mapper.aevent->bindlist.begin();
02239             SetActiveBind(*(mapper.abindit));
02240             break;
02241         case BB_Save:
02242             MAPPER_SaveBinds();
02243             break;
02244         case BB_Exit:   
02245             mapper.exit=true;
02246             break;
02247         case BB_Capture:
02248             GFX_CaptureMouse();
02249             if (mouselocked) change_action_text("Capture enabled. Hit ESC to release capture.",CLR_WHITE);
02250             break;
02251         }
02252     }
02253 protected:
02254     BB_Types type;
02255 };
02256 
02257 class CCheckButton : public CTextButton {
02258 public: 
02259     CCheckButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,BC_Types _type) 
02260     : CTextButton(_x,_y,_dx,_dy,_text)  { 
02261         type=_type;
02262     }
02263     virtual ~CCheckButton() {}
02264     void Draw(void) {
02265         if (!enabled) return;
02266         bool checked=false;
02267         switch (type) {
02268         case BC_Mod1:
02269             checked=(mapper.abind->mods&BMOD_Mod1)>0;
02270             break;
02271         case BC_Mod2:
02272             checked=(mapper.abind->mods&BMOD_Mod2)>0;
02273             break;
02274         case BC_Mod3:
02275             checked=(mapper.abind->mods&BMOD_Mod3)>0;
02276             break;
02277         case BC_Host:
02278             checked=(mapper.abind->mods&BMOD_Host)>0;
02279             break;
02280         case BC_Hold:
02281             checked=(mapper.abind->flags&BFLG_Hold)>0;
02282             break;
02283         }
02284         CTextButton::Draw();
02285         if (checked) {
02286 #if defined(C_SDL2)
02287             Bit8u * point=((Bit8u *)mapper.draw_surface->pixels)+((y+2)*mapper.draw_surface->pitch)+x+dx-dy+2;
02288 #else
02289             Bit8u * point=((Bit8u *)mapper.surface->pixels)+((y+2)*mapper.surface->pitch)+x+dx-dy+2;
02290 #endif
02291             for (Bitu lines=0;lines<(dy-4);lines++)  {
02292                 memset(point,color,dy-4);
02293 #if defined(C_SDL2)
02294                 point+=mapper.draw_surface->w;
02295 #else
02296                 point+=mapper.surface->pitch;
02297 #endif
02298             }
02299         }
02300     }
02301     void Click(void) {
02302         switch (type) {
02303         case BC_Mod1:
02304             mapper.abind->mods^=BMOD_Mod1;
02305             break;
02306         case BC_Mod2:
02307             mapper.abind->mods^=BMOD_Mod2;
02308             break;
02309         case BC_Mod3:
02310             mapper.abind->mods^=BMOD_Mod3;
02311             break;
02312         case BC_Host:
02313             mapper.abind->mods^=BMOD_Host;
02314             break;
02315         case BC_Hold:
02316             mapper.abind->flags^=BFLG_Hold;
02317             break;
02318         }
02319         mapper.redraw=true;
02320     }
02321 protected:
02322     BC_Types type;
02323 };
02324 
02326 class CKeyEvent : public CTriggeredEvent {
02327 public:
02329     CKeyEvent(char const * const _entry,KBD_KEYS _key) : CTriggeredEvent(_entry), notify_button(NULL) {
02330         key=_key;
02331     }
02332 
02333     virtual ~CKeyEvent() {}
02334 
02335     virtual void Active(bool yesno) {
02336         if (MAPPER_DemoOnly()) {
02337             if (notify_button != NULL)
02338                 notify_button->SetInvert(yesno);
02339         }
02340         else {
02341             KEYBOARD_AddKey(key,yesno);
02342         }
02343 
02344         active=yesno;
02345     };
02346 
02348     void notifybutton(CTextButton *n) {
02349         notify_button = n;
02350     }
02351 
02352     virtual void RebindRedraw(void) {
02353         if (notify_button != NULL)
02354             notify_button->RebindRedraw();
02355     }
02356 
02358     CTextButton *notify_button;
02359 
02361     KBD_KEYS key;
02362 };
02363 
02364 class CMouseButtonEvent : public CTriggeredEvent {
02365 public:
02366         CMouseButtonEvent(char const * const _entry,Bit8u _button) : CTriggeredEvent(_entry) {
02367                 button=_button;
02368         notify_button=NULL;
02369         }
02370         void Active(bool yesno) {
02371                 if (yesno)
02372                         Mouse_ButtonPressed(button);
02373                 else
02374                         Mouse_ButtonReleased(button);
02375         }
02377     void notifybutton(CTextButton *n) {
02378         notify_button = n;
02379     }
02380 
02381     virtual void RebindRedraw(void) {
02382         if (notify_button != NULL)
02383             notify_button->RebindRedraw();
02384     }
02385 
02387     CTextButton *notify_button;
02388 
02389         Bit8u button;
02390 };
02391 
02393 class CJAxisEvent : public CContinuousEvent {
02394 public:
02396     CJAxisEvent(char const * const _entry,Bitu _stick,Bitu _axis,bool _positive,CJAxisEvent * _opposite_axis) : CContinuousEvent(_entry) {
02397         notify_button=NULL;
02398         stick=_stick;
02399         axis=_axis;
02400         positive=_positive;
02401         opposite_axis=_opposite_axis;
02402         if (_opposite_axis) {
02403             _opposite_axis->SetOppositeAxis(this);
02404         }
02405     }
02406 
02407     virtual ~CJAxisEvent() {}
02408 
02409     virtual void Active(bool /*moved*/) {
02410         if (notify_button != NULL)
02411             notify_button->SetPartialInvert(GetValue()/32768.0);
02412 
02413         virtual_joysticks[stick].axis_pos[axis]=(Bit16s)(GetValue()*(positive?1:-1));
02414     }
02415 
02416     virtual Bitu GetActivityCount(void) {
02417         return activity|opposite_axis->activity;
02418     }
02419 
02420     virtual void RepostActivity(void) {
02421         /* caring for joystick movement into the opposite direction */
02422         opposite_axis->Active(true);
02423     }
02424 
02426     void notifybutton(CTextButton *n) {
02427         notify_button = n;
02428     }
02429 
02430     virtual void RebindRedraw(void) {
02431         if (notify_button != NULL)
02432             notify_button->RebindRedraw();
02433     }
02434 
02436     CTextButton *notify_button;
02437 protected:
02439     void SetOppositeAxis(CJAxisEvent * _opposite_axis) {
02440         opposite_axis=_opposite_axis;
02441     }
02442 
02444     Bitu stick;
02445 
02447     Bitu axis;
02448 
02450     bool positive;
02451 
02453     CJAxisEvent * opposite_axis;
02454 };
02455 
02457 class CJButtonEvent : public CTriggeredEvent {
02458 public:
02460     CJButtonEvent(char const * const _entry,Bitu _stick,Bitu _button) : CTriggeredEvent(_entry) {
02461         stick=_stick;
02462         button=_button;
02463         notify_button=NULL;
02464     }
02465 
02466     virtual ~CJButtonEvent() {}
02467     
02468     virtual void Active(bool pressed) {
02469         if (notify_button != NULL)
02470             notify_button->SetInvert(pressed);
02471 
02472         virtual_joysticks[stick].button_pressed[button]=pressed;
02473         active=pressed;
02474     }
02475     
02477     void notifybutton(CTextButton *n) {
02478         notify_button = n;
02479     }
02480 
02481     virtual void RebindRedraw(void) {
02482         if (notify_button != NULL)
02483             notify_button->RebindRedraw();
02484     }
02485 
02487     CTextButton *notify_button;
02488 protected:
02490     Bitu stick;
02491 
02493     Bitu button;
02494 };
02495 
02497 class CJHatEvent : public CTriggeredEvent {
02498 public:
02500     CJHatEvent(char const * const _entry,Bitu _stick,Bitu _hat,Bitu _dir) : CTriggeredEvent(_entry) {
02501         stick=_stick;
02502         hat=_hat;
02503         dir=_dir;
02504         notify_button = NULL;
02505     }
02506 
02507     virtual ~CJHatEvent() {}
02508 
02509     virtual void Active(bool pressed) {
02510         if (notify_button != NULL)
02511             notify_button->SetInvert(pressed);
02512         virtual_joysticks[stick].hat_pressed[(hat<<2)+dir]=pressed;
02513     }
02514     void notifybutton(CTextButton *n)
02515     {
02516         notify_button = n;
02517     }
02518 
02519     virtual void RebindRedraw(void) {
02520         if (notify_button != NULL)
02521             notify_button->RebindRedraw();
02522     }
02523 
02524     CTextButton *notify_button;
02525 protected:
02527     Bitu stick;
02528 
02530     Bitu hat;
02531 
02533     Bitu dir;
02534 };
02535 
02537 class CModEvent : public CTriggeredEvent {
02538 public:
02540     CModEvent(char const * const _entry,Bitu _wmod) : CTriggeredEvent(_entry), notify_button(NULL) {
02541         wmod=_wmod;
02542     }
02543 
02544     virtual ~CModEvent() {}
02545 
02546     virtual void Active(bool yesno) {
02547         if (notify_button != NULL)
02548             notify_button->SetInvert(yesno);
02549 
02550         if (yesno) mapper.mods|=((Bitu)1u << (wmod-1u));
02551         else mapper.mods&=~((Bitu)1u << (wmod-1u));
02552     };
02553 
02555     void notifybutton(CTextButton *n) {
02556         notify_button = n;
02557     }
02558 
02559     virtual void RebindRedraw(void) {
02560         if (notify_button != NULL)
02561             notify_button->RebindRedraw();
02562     }
02563 
02565     CTextButton *notify_button;
02566 protected:
02568     Bitu wmod;
02569 };
02570 
02571 std::string CBind::GetModifierText(void) {
02572     std::string r,t;
02573 
02574     for (size_t m=4u/*Host key first*/;m >= 1u;m--) {
02575         if ((mods & ((Bitu)1u << (m - 1u))) && mod_event[m] != NULL) {
02576             t = mod_event[m]->GetBindMenuText();
02577             if (!r.empty()) r += "+";
02578             r += t;
02579         }
02580     }
02581 
02582     return r;
02583 }
02584 
02586 class CHandlerEvent : public CTriggeredEvent {
02587 public:
02589     CHandlerEvent(char const * const _entry,MAPPER_Handler * _handler,MapKeys _key,Bitu _mod,char const * const _buttonname) : CTriggeredEvent(_entry), notify_button(NULL) {
02590         handler=_handler;
02591         defmod=_mod;
02592         defkey=_key;
02593         buttonname=_buttonname;
02594         handlergroup.push_back(this);
02595         type = handler_event_t;
02596     }
02597 
02598     virtual ~CHandlerEvent() {}
02599 
02600     virtual void RebindRedraw(void) {
02601         if (notify_button != NULL)
02602             notify_button->RebindRedraw();
02603     }
02604 
02605     virtual void Active(bool yesno) {
02606         if (MAPPER_DemoOnly()) {
02607             if (notify_button != NULL)
02608                 notify_button->SetInvert(yesno);
02609         }
02610         else {
02611             (*handler)(yesno);
02612         }
02613 
02614         active=yesno;
02615     };
02616 
02618     const char * ButtonName(void) {
02619         return buttonname;
02620     }
02621 
02623 #if defined(C_SDL2)
02624     void MakeDefaultBind(char * buf) {
02625         Bitu key=0;
02626         switch (defkey) {
02627         case MK_nothing: *buf = 0; return;
02628         case MK_f1:case MK_f2:case MK_f3:case MK_f4:
02629         case MK_f5:case MK_f6:case MK_f7:case MK_f8:
02630         case MK_f9:case MK_f10:case MK_f11:case MK_f12: 
02631             key=SDL_SCANCODE_F1+(defkey-MK_f1);
02632             break;
02633         case MK_rightarrow:
02634             key=SDL_SCANCODE_RIGHT;
02635             break;
02636         case MK_leftarrow:
02637             key=SDL_SCANCODE_LEFT;
02638             break;
02639         case MK_return:
02640             key=SDL_SCANCODE_RETURN;
02641             break;
02642         case MK_kpminus:
02643             key=SDL_SCANCODE_KP_MINUS;
02644             break;
02645         case MK_kpplus:
02646             key=SDL_SCANCODE_KP_PLUS;
02647             break;
02648         case MK_minus:
02649             key=SDL_SCANCODE_MINUS;
02650             break;
02651         case MK_equals:
02652             key=SDL_SCANCODE_EQUALS;
02653             break;
02654         case MK_scrolllock:
02655             key=SDL_SCANCODE_SCROLLLOCK;
02656             break;
02657         case MK_pause:
02658             key=SDL_SCANCODE_PAUSE;
02659             break;
02660         case MK_printscreen:
02661             key=SDL_SCANCODE_PRINTSCREEN;
02662             break;
02663         case MK_home: 
02664             key=SDL_SCANCODE_HOME;
02665             break;
02666         case MK_1:
02667             key=SDL_SCANCODE_1;
02668             break;
02669         case MK_2:
02670             key=SDL_SCANCODE_2;
02671             break;
02672         case MK_3:
02673             key=SDL_SCANCODE_3;
02674             break;
02675         case MK_4:
02676             key=SDL_SCANCODE_4;
02677             break;
02678         case MK_c:
02679             key=SDL_SCANCODE_C;
02680             break;
02681         case MK_d:
02682             key=SDL_SCANCODE_D;
02683             break;
02684         case MK_f:
02685             key=SDL_SCANCODE_F;
02686             break;
02687         case MK_m:
02688             key=SDL_SCANCODE_M;
02689             break;
02690         case MK_r:
02691             key=SDL_SCANCODE_R;
02692             break;
02693         case MK_s:
02694             key=SDL_SCANCODE_S;
02695             break;
02696         case MK_v:
02697             key=SDL_SCANCODE_V;
02698             break;
02699         case MK_w:
02700             key=SDL_SCANCODE_W;
02701             break;
02702         case MK_escape:
02703             key=SDL_SCANCODE_ESCAPE;
02704             break;
02705         case MK_lbracket:
02706             key=SDL_SCANCODE_LEFTBRACKET;
02707             break;
02708         case MK_rbracket:
02709             key=SDL_SCANCODE_RIGHTBRACKET;
02710             break;
02711         default:
02712             break;
02713         }
02714         sprintf(buf,"%s \"key %d%s%s%s%s\"",
02715             entry,
02716             (int)key,
02717             (defmod & 1) ? " mod1" : "",
02718             (defmod & 2) ? " mod2" : "",
02719             (defmod & 4) ? " mod3" : "",
02720             (defmod & 8) ? " host" : ""
02721         );
02722     }
02723 #else
02724     void MakeDefaultBind(char * buf) {
02725         Bitu key=0;
02726         switch (defkey) {
02727         case MK_nothing: *buf = 0; return;
02728         case MK_f1:case MK_f2:case MK_f3:case MK_f4:
02729         case MK_f5:case MK_f6:case MK_f7:case MK_f8:
02730         case MK_f9:case MK_f10:case MK_f11:case MK_f12: 
02731             key=(Bitu)(SDLK_F1+(defkey-MK_f1));
02732             break;
02733         case MK_rightarrow:
02734             key=SDLK_RIGHT;
02735             break;
02736         case MK_leftarrow:
02737             key=SDLK_LEFT;
02738             break;
02739         case MK_return:
02740             key=SDLK_RETURN;
02741             break;
02742         case MK_kpminus:
02743             key=SDLK_KP_MINUS;
02744             break;
02745         case MK_kpplus:
02746             key=SDLK_KP_PLUS;
02747             break;
02748         case MK_minus:
02749             key=SDLK_MINUS;
02750             break;
02751         case MK_equals:
02752             key=SDLK_EQUALS;
02753             break;
02754         case MK_scrolllock:
02755 #if defined(C_SDL2)
02756             key=SDLK_SCROLLLOCK;
02757 #else
02758             key=SDLK_SCROLLOCK;
02759 #endif
02760             break;
02761         case MK_pause:
02762             key=SDLK_PAUSE;
02763             break;
02764         case MK_printscreen:
02765 #if defined(C_SDL2)
02766             key=SDLK_PRINTSCREEN;
02767 #else
02768             key=SDLK_PRINT;
02769 #endif
02770             break;
02771         case MK_home: 
02772             key=SDLK_HOME; 
02773             break;
02774         case MK_1:
02775             key=SDLK_1;
02776             break;
02777         case MK_2:
02778             key=SDLK_2;
02779             break;
02780         case MK_3:
02781             key=SDLK_3;
02782             break;
02783         case MK_4:
02784             key=SDLK_4;
02785             break;
02786         case MK_c:
02787             key=SDLK_c;
02788             break;
02789         case MK_d:
02790             key=SDLK_d;
02791             break;
02792         case MK_f:
02793             key=SDLK_f;
02794             break;
02795         case MK_m:
02796             key=SDLK_m;
02797             break;
02798         case MK_r:
02799             key=SDLK_r;
02800             break;
02801         case MK_s:
02802             key=SDLK_s;
02803             break;
02804         case MK_v:
02805             key=SDLK_v;
02806             break;
02807         case MK_w:
02808             key=SDLK_w;
02809             break;
02810         case MK_escape:
02811             key=SDLK_ESCAPE;
02812             break;
02813         case MK_lbracket:
02814             key=SDLK_LEFTBRACKET;
02815             break;
02816         case MK_rbracket:
02817             key=SDLK_RIGHTBRACKET;
02818             break;
02819         default:
02820             *buf = 0;
02821             return;
02822         }
02823         sprintf(buf,"%s \"key %d%s%s%s%s\"",
02824             entry,
02825             (int)key,
02826             defmod & 1 ? " mod1" : "",
02827             defmod & 2 ? " mod2" : "",
02828             defmod & 4 ? " mod3" : "",
02829             defmod & 8 ? " host" : ""
02830         );
02831     }
02832 #endif
02833 
02834     void notifybutton(CTextButton *n) {
02835         notify_button = n;
02836     }
02837 
02839     CTextButton *notify_button;
02840 
02842     MAPPER_Handler * handler;
02843 protected:
02845     MapKeys defkey;
02846 
02848     Bitu defmod;
02849 public:
02851     const char * buttonname;
02852 };
02853 
02854 /* whether to run keystrokes through system but only to show how it comes out.
02855  * otherwise, do full mapper processing. */
02856 bool MAPPER_DemoOnly(void) {
02857     return !mapper.exit;
02858 }
02859 
02860 CEvent *get_mapper_event_by_name(const std::string &x) {
02861     auto i = name_to_events.find(x);
02862 
02863     if (i != name_to_events.end()) {
02864         if (i->second >= events.size())
02865             E_Exit("Mapper: name to events contains out of range index for \"%s\"",x.c_str());
02866 
02867         return events[i->second];
02868     }
02869 
02870     return NULL;
02871 }
02872 
02873 static void DrawText(Bitu x,Bitu y,const char * text,Bit8u color,Bit8u bkcolor/*=CLR_BLACK*/) {
02874 #if defined(C_SDL2)
02875     Bit8u * draw=((Bit8u *)mapper.draw_surface->pixels)+(y*mapper.draw_surface->w)+x;
02876 #else
02877     Bit8u * draw=((Bit8u *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x;
02878 #endif
02879     while (*text) {
02880         Bit8u * font=&int10_font_14[(*text)*14];
02881         Bitu i,j;Bit8u * draw_line=draw;
02882         for (i=0;i<14;i++) {
02883             Bit8u map=*font++;
02884             for (j=0;j<8;j++) {
02885                 if (map & 0x80) *(draw_line+j)=color;
02886                 else *(draw_line+j)=bkcolor;
02887                 map<<=1;
02888             }
02889 #if defined(C_SDL2)
02890             draw_line+=mapper.draw_surface->w;
02891 #else
02892             draw_line+=mapper.surface->pitch;
02893 #endif
02894         }
02895         text++;draw+=8;
02896     }
02897 }
02898 
02899 
02900 void MAPPER_TriggerEventByName(const std::string& name) {
02901     CEvent *event = get_mapper_event_by_name(name);
02902     if (event != NULL) {
02903         if (event->type == CEvent::handler_event_t) {
02904             CHandlerEvent *he = reinterpret_cast<CHandlerEvent*>(event);
02905             if (he->handler != NULL) {
02906                 he->handler(true);
02907                 he->handler(false);
02908             }
02909         }
02910     }
02911 }
02912 
02913 static void change_action_text(const char* text,Bit8u col) {
02914     bind_but.action->Change(text,"");
02915     bind_but.action->SetColor(col);
02916 }
02917 
02918 
02919 static void SetActiveBind(CBind * _bind) {
02920     mapper.abind=_bind;
02921     if (_bind) {
02922         bind_but.bind_title->Enable(true);
02923         char buf[256];_bind->BindName(buf);
02924         bind_but.bind_title->Change("BIND:%s",buf);
02925         bind_but.del->Enable(true);
02926         bind_but.next->Enable(true);
02927         bind_but.mod1->Enable(true);
02928         bind_but.mod2->Enable(true);
02929         bind_but.mod3->Enable(true);
02930         bind_but.host->Enable(true);
02931         bind_but.hold->Enable(true);
02932     } else {
02933         bind_but.bind_title->Enable(false);
02934         bind_but.del->Enable(false);
02935         bind_but.next->Enable(false);
02936         bind_but.mod1->Enable(false);
02937         bind_but.mod2->Enable(false);
02938         bind_but.mod3->Enable(false);
02939         bind_but.host->Enable(false);
02940         bind_but.hold->Enable(false);
02941     }
02942 }
02943 
02944 static void SetActiveEvent(CEvent * event) {
02945     mapper.aevent=event;
02946     mapper.redraw=true;
02947     mapper.addbind=false;
02948     bind_but.event_title->Change("EVENT:%s",event ? event->GetName(): "none");
02949     if (!event) {
02950         change_action_text("Select an event to change.",CLR_WHITE);
02951         bind_but.add->Enable(false);
02952         SetActiveBind(0);
02953     } else {
02954         change_action_text("Select a different event or hit the Add/Del/Next buttons.",CLR_WHITE);
02955         mapper.abindit=event->bindlist.begin();
02956         if (mapper.abindit!=event->bindlist.end()) {
02957             SetActiveBind(*(mapper.abindit));
02958         } else SetActiveBind(0);
02959         bind_but.add->Enable(true);
02960     }
02961 }
02962 
02963 #if defined(C_SDL2)
02964 extern SDL_Window * GFX_SetSDLSurfaceWindow(Bit16u width, Bit16u height);
02965 extern SDL_Rect GFX_GetSDLSurfaceSubwindowDims(Bit16u width, Bit16u height);
02966 extern void GFX_UpdateDisplayDimensions(int width, int height);
02967 #endif
02968 
02969 static void DrawButtons(void) {
02970 #if defined(C_SDL2)
02971     SDL_FillRect(mapper.draw_surface,0,0);
02972 #else
02973     SDL_FillRect(mapper.surface,0,CLR_BLACK);
02974     SDL_LockSurface(mapper.surface);
02975 #endif
02976     for (CButton_it but_it = buttons.begin();but_it!=buttons.end();++but_it) {
02977         (*but_it)->Draw();
02978     }
02979 #if defined(C_SDL2)
02980     // We can't just use SDL_BlitScaled (say for Android) in one step
02981     SDL_BlitSurface(mapper.draw_surface, NULL, mapper.draw_surface_nonpaletted, NULL);
02982     SDL_BlitScaled(mapper.draw_surface_nonpaletted, NULL, mapper.surface, &mapper.draw_rect);
02983 //    SDL_BlitSurface(mapper.draw_surface, NULL, mapper.surface, NULL);
02984     SDL_UpdateWindowSurface(mapper.window);
02985 #else
02986     SDL_UnlockSurface(mapper.surface);
02987     SDL_Flip(mapper.surface);
02988 #endif
02989 }
02990 
02991 static CKeyEvent * AddKeyButtonEvent(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,char const * const entry,KBD_KEYS key) {
02992     char buf[64];
02993     strcpy(buf,"key_");
02994     strcat(buf,entry);
02995     CKeyEvent * event=new CKeyEvent(buf,key);
02996     CEventButton *button=new CEventButton(x,y,dx,dy,title,event);
02997     event->notifybutton(button);
02998     return event;
02999 }
03000 
03001 static CMouseButtonEvent * AddMouseButtonEvent(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,char const * const entry,Bit8u key) {
03002         char buf[64];
03003         strcpy(buf,"mouse_");
03004         strcat(buf,entry);
03005         CMouseButtonEvent * event=new CMouseButtonEvent(buf,key);
03006         CEventButton *button=new CEventButton(x,y,dx,dy,title,event);
03007     event->notifybutton(button);
03008         return event;
03009 }
03010 
03011 static CJAxisEvent * AddJAxisButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu stick,Bitu axis,bool positive,CJAxisEvent * opposite_axis) {
03012     char buf[64];
03013     sprintf(buf,"jaxis_%d_%d%s",(int)stick,(int)axis,positive ? "+" : "-");
03014     CJAxisEvent * event=new CJAxisEvent(buf,stick,axis,positive,opposite_axis);
03015     CEventButton *button=new CEventButton(x,y,dx,dy,title,event);
03016     event->notifybutton(button);
03017     return event;
03018 }
03019 static CJAxisEvent * AddJAxisButton_hidden(Bitu stick,Bitu axis,bool positive,CJAxisEvent * opposite_axis) {
03020     char buf[64];
03021     sprintf(buf,"jaxis_%d_%d%s",(int)stick,(int)axis,positive ? "+" : "-");
03022     return new CJAxisEvent(buf,stick,axis,positive,opposite_axis);
03023 }
03024 
03025 static void AddJButtonButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu stick,Bitu button) {
03026     char buf[64];
03027     sprintf(buf,"jbutton_%d_%d",(int)stick,(int)button);
03028     CJButtonEvent * event=new CJButtonEvent(buf,stick,button);
03029     CEventButton *evbutton=new CEventButton(x,y,dx,dy,title,event);
03030     event->notifybutton(evbutton);
03031 }
03032 static void AddJButtonButton_hidden(Bitu stick,Bitu button) {
03033     char buf[64];
03034     sprintf(buf,"jbutton_%d_%d",(int)stick,(int)button);
03035     new CJButtonEvent(buf,stick,button);
03036 }
03037 
03038 static void AddJHatButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu _stick,Bitu _hat,Bitu _dir) {
03039     char buf[64];
03040     sprintf(buf,"jhat_%d_%d_%d",(int)_stick,(int)_hat,(int)_dir);
03041     CJHatEvent * event=new CJHatEvent(buf,_stick,_hat,_dir);
03042     CEventButton* evbutton = new CEventButton(x,y,dx,dy,title,event);
03043     event->notifybutton(evbutton);
03044 }
03045 
03046 static void AddModButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu _mod) {
03047     char buf[64];
03048 
03049     if (_mod == 4)
03050         sprintf(buf,"host");
03051     else
03052         sprintf(buf,"mod_%d",(int)_mod);
03053 
03054     CModEvent * event=new CModEvent(buf,_mod);
03055     CEventButton *button=new CEventButton(x,y,dx,dy,title,event);
03056     event->notifybutton(button);
03057 
03058     assert(_mod < 8);
03059     mod_event[_mod] = event;
03060 }
03061 
03062 static void CreateLayout(void) {
03063     Bitu i;
03064     /* Create the buttons for the Keyboard */
03065 #define BW 28
03066 #define BH 18
03067 #define DX 5
03068 #define PX(_X_) ((_X_)*BW + DX)
03069 #define PY(_Y_) (10+(_Y_)*BH)
03070     AddKeyButtonEvent(PX(0),PY(0),BW,BH,"ESC","esc",KBD_esc);
03071     for (i=0;i<12;i++) AddKeyButtonEvent(PX(2+i),PY(0),BW,BH,combo_f[i].title,combo_f[i].entry,combo_f[i].key);
03072 
03073     if (IS_PC98_ARCH) {
03074         for (i=0;i<14;i++) AddKeyButtonEvent(PX(  i),PY(1),BW,BH,combo_1_pc98[i].title,combo_1_pc98[i].entry,combo_1_pc98[i].key);
03075     }
03076     else {
03077         for (i=0;i<14;i++) AddKeyButtonEvent(PX(  i),PY(1),BW,BH,combo_1[i].title,combo_1[i].entry,combo_1[i].key);
03078     }
03079 
03080     AddKeyButtonEvent(PX(0),PY(2),BW*2,BH,"TAB","tab",KBD_tab);
03081     for (i=0;i<12;i++) AddKeyButtonEvent(PX(2+i),PY(2),BW,BH,combo_2[i].title,combo_2[i].entry,combo_2[i].key);
03082 
03083     AddKeyButtonEvent(PX(14),PY(2),BW*2,BH*2,"ENTER","enter",KBD_enter);
03084     
03085     caps_lock_event=AddKeyButtonEvent(PX(0),PY(3),BW*2,BH,"CLCK","capslock",KBD_capslock);
03086 
03087     if (IS_PC98_ARCH) {
03088         for (i=0;i<12;i++) AddKeyButtonEvent(PX(2+i),PY(3),BW,BH,combo_3_pc98[i].title,combo_3_pc98[i].entry,combo_3_pc98[i].key);
03089     }
03090     else {
03091         for (i=0;i<12;i++) AddKeyButtonEvent(PX(2+i),PY(3),BW,BH,combo_3[i].title,combo_3[i].entry,combo_3[i].key);
03092     }
03093 
03094     AddKeyButtonEvent(PX(0),PY(4),BW*2,BH,"SHIFT","lshift",KBD_leftshift);
03095     for (i=0;i<11;i++) AddKeyButtonEvent(PX(2+i),PY(4),BW,BH,combo_4[i].title,combo_4[i].entry,combo_4[i].key);
03096     AddKeyButtonEvent(PX(13),PY(4),BW*3,BH,"SHIFT","rshift",KBD_rightshift);
03097 
03098     /* Last Row */
03099     AddKeyButtonEvent(PX(0) ,PY(5),BW*2,BH,"CTRL","lctrl",KBD_leftctrl);
03100     AddKeyButtonEvent(PX(2) ,PY(5),BW*1,BH,"WIN","lwindows",KBD_lwindows);
03101     AddKeyButtonEvent(PX(3) ,PY(5),BW*1,BH,"ALT","lalt",KBD_leftalt);
03102     AddKeyButtonEvent(PX(4) ,PY(5),BW*7,BH,"SPACE","space",KBD_space);
03103     AddKeyButtonEvent(PX(11),PY(5),BW*1,BH,"ALT","ralt",KBD_rightalt);
03104     AddKeyButtonEvent(PX(12),PY(5),BW*1,BH,"WIN","rwindows",KBD_rwindows);
03105     AddKeyButtonEvent(PX(13),PY(5),BW*1,BH,"WMN","rwinmenu",KBD_rwinmenu);
03106     AddKeyButtonEvent(PX(14),PY(5),BW*2,BH,"CTRL","rctrl",KBD_rightctrl);
03107 
03108     /* Arrow Keys */
03109 #define XO 18
03110 #define YO 0
03111 
03112     AddKeyButtonEvent(PX(XO+0),PY(YO),BW,BH,"PRT","printscreen",KBD_printscreen);
03113     AddKeyButtonEvent(PX(XO+1),PY(YO),BW,BH,"SCL","scrolllock",KBD_scrolllock);
03114     AddKeyButtonEvent(PX(XO+2),PY(YO),BW,BH,"PAU","pause",KBD_pause);
03115     AddKeyButtonEvent(PX(XO+0),PY(YO+1),BW,BH,"INS","insert",KBD_insert);
03116     AddKeyButtonEvent(PX(XO+1),PY(YO+1),BW,BH,"HOM","home",KBD_home);
03117     AddKeyButtonEvent(PX(XO+2),PY(YO+1),BW,BH,"PUP","pageup",KBD_pageup);
03118     AddKeyButtonEvent(PX(XO+0),PY(YO+2),BW,BH,"DEL","delete",KBD_delete);
03119     AddKeyButtonEvent(PX(XO+1),PY(YO+2),BW,BH,"END","end",KBD_end);
03120     AddKeyButtonEvent(PX(XO+2),PY(YO+2),BW,BH,"PDN","pagedown",KBD_pagedown);
03121     AddKeyButtonEvent(PX(XO-4),PY(YO),BW,BH,"NEQ","kp_equals",KBD_kpequals);
03122     AddKeyButtonEvent(PX(XO-2),PY(YO),BW,BH,"\x18 U","up",KBD_up);
03123     AddKeyButtonEvent(PX(XO-3),PY(YO+1),BW,BH,"\x1B L","left",KBD_left);
03124     AddKeyButtonEvent(PX(XO-2),PY(YO+1),BW,BH,"\x19 D","down",KBD_down);
03125     AddKeyButtonEvent(PX(XO-1),PY(YO+1),BW,BH,"\x1A R","right",KBD_right);
03126 #undef XO
03127 #undef YO
03128 #define XO 18
03129 #define YO 5
03130         /* Mouse Buttons */
03131         new CTextButton(PX(XO+0),PY(YO-1),3*BW,20,"Mouse keys");
03132         AddMouseButtonEvent(PX(XO+0),PY(YO),BW,BH,"L","left",0);
03133         AddMouseButtonEvent(PX(XO+1),PY(YO),BW,BH,"M","middle",2);
03134         AddMouseButtonEvent(PX(XO+2),PY(YO),BW,BH,"R","right",1);
03135 #undef XO
03136 #undef YO
03137 #define XO 0
03138 #define YO 7
03139     /* Numeric KeyPad */
03140     num_lock_event=AddKeyButtonEvent(PX(XO),PY(YO),BW,BH,"NUM","numlock",KBD_numlock);
03141     AddKeyButtonEvent(PX(XO+1),PY(YO),BW,BH,"/","kp_divide",KBD_kpdivide);
03142     AddKeyButtonEvent(PX(XO+2),PY(YO),BW,BH,"*","kp_multiply",KBD_kpmultiply);
03143     AddKeyButtonEvent(PX(XO+3),PY(YO),BW,BH,"-","kp_minus",KBD_kpminus);
03144     AddKeyButtonEvent(PX(XO+0),PY(YO+1),BW,BH,"7","kp_7",KBD_kp7);
03145     AddKeyButtonEvent(PX(XO+1),PY(YO+1),BW,BH,"8","kp_8",KBD_kp8);
03146     AddKeyButtonEvent(PX(XO+2),PY(YO+1),BW,BH,"9","kp_9",KBD_kp9);
03147     AddKeyButtonEvent(PX(XO+3),PY(YO+1),BW,BH*2,"+","kp_plus",KBD_kpplus);
03148     AddKeyButtonEvent(PX(XO),PY(YO+2),BW,BH,"4","kp_4",KBD_kp4);
03149     AddKeyButtonEvent(PX(XO+1),PY(YO+2),BW,BH,"5","kp_5",KBD_kp5);
03150     AddKeyButtonEvent(PX(XO+2),PY(YO+2),BW,BH,"6","kp_6",KBD_kp6);
03151     AddKeyButtonEvent(PX(XO+0),PY(YO+3),BW,BH,"1","kp_1",KBD_kp1);
03152     AddKeyButtonEvent(PX(XO+1),PY(YO+3),BW,BH,"2","kp_2",KBD_kp2);
03153     AddKeyButtonEvent(PX(XO+2),PY(YO+3),BW,BH,"3","kp_3",KBD_kp3);
03154     AddKeyButtonEvent(PX(XO+3),PY(YO+3),BW,BH*2,"ENT","kp_enter",KBD_kpenter);
03155     if (IS_PC98_ARCH) {
03156         AddKeyButtonEvent(PX(XO+0),PY(YO+4),BW*1,BH,"0","kp_0",KBD_kp0);
03157         AddKeyButtonEvent(PX(XO+1),PY(YO+4),BW*1,BH,",","kp_comma",KBD_kpcomma);
03158     }
03159     else {
03160         AddKeyButtonEvent(PX(XO+0),PY(YO+4),BW*2,BH,"0","kp_0",KBD_kp0);
03161     }
03162     AddKeyButtonEvent(PX(XO+2),PY(YO+4),BW,BH,".","kp_period",KBD_kpperiod);
03163 #undef XO
03164 #undef YO
03165 #define XO 5
03166 #define YO 7
03167     if (IS_PC98_ARCH) {
03168         /* PC-98 extra keys */
03169         AddKeyButtonEvent(PX(XO+0),PY(YO+0),BW*2,BH,"STOP","stop",KBD_stop);
03170         AddKeyButtonEvent(PX(XO+2),PY(YO+0),BW*2,BH,"HELP","help",KBD_help);
03171 
03172         AddKeyButtonEvent(PX(XO+0),PY(YO+1),BW*2,BH,"COPY","copy",KBD_copy);
03173         AddKeyButtonEvent(PX(XO+2),PY(YO+1),BW*2,BH,"KANA","kana",KBD_kana);
03174 
03175         AddKeyButtonEvent(PX(XO+0),PY(YO+2),BW*2,BH,"NFER","nfer",KBD_nfer);
03176         AddKeyButtonEvent(PX(XO+2),PY(YO+2),BW*2,BH,"XFER","xfer",KBD_xfer);
03177 
03178         AddKeyButtonEvent(PX(XO+2),PY(YO+3),BW*2,BH,"Ro / _","jp_ro",KBD_jp_ro);
03179 
03180         AddKeyButtonEvent(PX(XO+0),PY(YO+3),BW*1,BH,"VF1","vf1",KBD_vf1);
03181         AddKeyButtonEvent(PX(XO+1),PY(YO+3),BW*1,BH,"VF2","vf2",KBD_vf2);
03182         AddKeyButtonEvent(PX(XO+0),PY(YO+4),BW*1,BH,"VF3","vf3",KBD_vf3);
03183         AddKeyButtonEvent(PX(XO+1),PY(YO+4),BW*1,BH,"VF4","vf4",KBD_vf4);
03184         AddKeyButtonEvent(PX(XO+2),PY(YO+4),BW*1,BH,"VF5","vf5",KBD_vf5);
03185     }
03186     else {
03187         /* F13-F24 block */
03188         AddKeyButtonEvent(PX(XO+0),PY(YO+0),BW,BH,"F13","f13",KBD_f13);
03189         AddKeyButtonEvent(PX(XO+1),PY(YO+0),BW,BH,"F14","f14",KBD_f14);
03190         AddKeyButtonEvent(PX(XO+2),PY(YO+0),BW,BH,"F15","f15",KBD_f15);
03191         AddKeyButtonEvent(PX(XO+3),PY(YO+0),BW,BH,"F16","f16",KBD_f16);
03192         AddKeyButtonEvent(PX(XO+0),PY(YO+1),BW,BH,"F17","f17",KBD_f17);
03193         AddKeyButtonEvent(PX(XO+1),PY(YO+1),BW,BH,"F18","f18",KBD_f18);
03194         AddKeyButtonEvent(PX(XO+2),PY(YO+1),BW,BH,"F19","f19",KBD_f19);
03195         AddKeyButtonEvent(PX(XO+3),PY(YO+1),BW,BH,"F20","f20",KBD_f20);
03196         AddKeyButtonEvent(PX(XO+0),PY(YO+2),BW,BH,"F21","f21",KBD_f21);
03197         AddKeyButtonEvent(PX(XO+1),PY(YO+2),BW,BH,"F22","f22",KBD_f22);
03198         AddKeyButtonEvent(PX(XO+2),PY(YO+2),BW,BH,"F23","f23",KBD_f23);
03199         AddKeyButtonEvent(PX(XO+3),PY(YO+2),BW,BH,"F24","f24",KBD_f24);
03200     }
03201 #undef XO
03202 #undef YO
03203 #define XO 0
03204 #define YO 13
03205     /* Japanese keys */
03206     AddKeyButtonEvent(PX(XO+0),PY(YO+0),BW*3,BH,"HANKAKU", "jp_hankaku", KBD_jp_hankaku);
03207     AddKeyButtonEvent(PX(XO+0),PY(YO+1),BW*3,BH,"MUHENKAN","jp_muhenkan",KBD_jp_muhenkan);
03208     AddKeyButtonEvent(PX(XO+0),PY(YO+2),BW*3,BH,"HENKAN",  "jp_henkan",  KBD_jp_henkan);
03209     AddKeyButtonEvent(PX(XO+3),PY(YO+0),BW*3,BH,"HIRAGANA","jp_hiragana",KBD_jp_hiragana);
03210     AddKeyButtonEvent(PX(XO+6),PY(YO+0),BW*1,BH,"YEN",     "jp_yen",     KBD_jp_yen);
03211     AddKeyButtonEvent(PX(XO+6),PY(YO+1),BW*1,BH,"\\",      "jp_bckslash",KBD_jp_backslash);
03212     AddKeyButtonEvent(PX(XO+6),PY(YO+2),BW*1,BH,":*",      "colon",      KBD_colon);
03213     AddKeyButtonEvent(PX(XO+7),PY(YO+0),BW*1,BH,"^`",      "caret",      KBD_caret);
03214     AddKeyButtonEvent(PX(XO+7),PY(YO+1),BW*1,BH,"@~",      "atsign",     KBD_atsign);
03215     /* Korean */
03216     AddKeyButtonEvent(PX(XO+3),PY(YO+1),BW*3,BH,"HANCHA",  "kor_hancha", KBD_kor_hancha);
03217     AddKeyButtonEvent(PX(XO+3),PY(YO+2),BW*3,BH,"HANYONG", "kor_hanyong",KBD_kor_hanyong);
03218 #undef XO
03219 #undef YO
03220 #define XO 10
03221 #define YO 8
03222     /* Joystick Buttons/Texts */
03223     /* Buttons 1+2 of 1st Joystick */
03224     AddJButtonButton(PX(XO),PY(YO),BW,BH,"1" ,0,0);
03225     AddJButtonButton(PX(XO+2),PY(YO),BW,BH,"2" ,0,1);
03226     /* Axes 1+2 (X+Y) of 1st Joystick */
03227     CJAxisEvent * cjaxis=AddJAxisButton(PX(XO+1),PY(YO),BW,BH,"Y-",0,1,false,NULL);
03228     AddJAxisButton  (PX(XO+1),PY(YO+1),BW,BH,"Y+",0,1,true,cjaxis);
03229     cjaxis=AddJAxisButton  (PX(XO),PY(YO+1),BW,BH,"X-",0,0,false,NULL);
03230     AddJAxisButton  (PX(XO+2),PY(YO+1),BW,BH,"X+",0,0,true,cjaxis);
03231 
03232     if (joytype==JOY_2AXIS) {
03233         /* Buttons 1+2 of 2nd Joystick */
03234         AddJButtonButton(PX(XO+4),PY(YO),BW,BH,"1" ,1,0);
03235         AddJButtonButton(PX(XO+4+2),PY(YO),BW,BH,"2" ,1,1);
03236         /* Buttons 3+4 of 1st Joystick, not accessible */
03237         AddJButtonButton_hidden(0,2);
03238         AddJButtonButton_hidden(0,3);
03239 
03240         /* Axes 1+2 (X+Y) of 2nd Joystick */
03241         cjaxis= AddJAxisButton(PX(XO+4),PY(YO+1),BW,BH,"X-",1,0,false,NULL);
03242                 AddJAxisButton(PX(XO+4+2),PY(YO+1),BW,BH,"X+",1,0,true,cjaxis);
03243         cjaxis= AddJAxisButton(PX(XO+4+1),PY(YO+0),BW,BH,"Y-",1,1,false,NULL);
03244                 AddJAxisButton(PX(XO+4+1),PY(YO+1),BW,BH,"Y+",1,1,true,cjaxis);
03245         /* Axes 3+4 (X+Y) of 1st Joystick, not accessible */
03246         cjaxis= AddJAxisButton_hidden(0,2,false,NULL);
03247                 AddJAxisButton_hidden(0,2,true,cjaxis);
03248         cjaxis= AddJAxisButton_hidden(0,3,false,NULL);
03249                 AddJAxisButton_hidden(0,3,true,cjaxis);
03250     } else {
03251         /* Buttons 3+4 of 1st Joystick */
03252         AddJButtonButton(PX(XO+4),PY(YO),BW,BH,"3" ,0,2);
03253         AddJButtonButton(PX(XO+4+2),PY(YO),BW,BH,"4" ,0,3);
03254         /* Buttons 1+2 of 2nd Joystick, not accessible */
03255         AddJButtonButton_hidden(1,0);
03256         AddJButtonButton_hidden(1,1);
03257 
03258         /* Axes 3+4 (X+Y) of 1st Joystick */
03259         cjaxis= AddJAxisButton(PX(XO+4),PY(YO+1),BW,BH,"X-",0,2,false,NULL);
03260                 AddJAxisButton(PX(XO+4+2),PY(YO+1),BW,BH,"X+",0,2,true,cjaxis);
03261         cjaxis= AddJAxisButton(PX(XO+4+1),PY(YO+0),BW,BH,"Y-",0,3,false,NULL);
03262                 AddJAxisButton(PX(XO+4+1),PY(YO+1),BW,BH,"Y+",0,3,true,cjaxis);
03263         /* Axes 1+2 (X+Y) of 2nd Joystick , not accessible*/
03264         cjaxis= AddJAxisButton_hidden(1,0,false,NULL);
03265                 AddJAxisButton_hidden(1,0,true,cjaxis);
03266         cjaxis= AddJAxisButton_hidden(1,1,false,NULL);
03267                 AddJAxisButton_hidden(1,1,true,cjaxis);
03268     }
03269 
03270     if (joytype==JOY_CH) {
03271         /* Buttons 5+6 of 1st Joystick */
03272         AddJButtonButton(PX(XO+8),PY(YO),BW,BH,"5" ,0,4);
03273         AddJButtonButton(PX(XO+8+2),PY(YO),BW,BH,"6" ,0,5);
03274     } else {
03275         /* Buttons 5+6 of 1st Joystick, not accessible */
03276         AddJButtonButton_hidden(0,4);
03277         AddJButtonButton_hidden(0,5);
03278     }
03279 
03280     /* Hat directions up, left, down, right */
03281     AddJHatButton(PX(XO+8+1),PY(YO),BW,BH,"UP",0,0,0);
03282     AddJHatButton(PX(XO+8+0),PY(YO+1),BW,BH,"LFT",0,0,3);
03283     AddJHatButton(PX(XO+8+1),PY(YO+1),BW,BH,"DWN",0,0,2);
03284     AddJHatButton(PX(XO+8+2),PY(YO+1),BW,BH,"RGT",0,0,1);
03285 
03286     /* Labels for the joystick */
03287     CTextButton* btn;
03288     if (joytype ==JOY_2AXIS) {
03289         new CTextButton(PX(XO+0),PY(YO-1),3*BW,BH,"Joystick 1");
03290         new CTextButton(PX(XO+4),PY(YO-1),3*BW,BH,"Joystick 2");
03291         btn = new CTextButton(PX(XO + 8), PY(YO - 1), 3 * BW, BH, "Disabled");
03292         btn->SetColor(CLR_GREY);
03293     } else if(joytype ==JOY_4AXIS || joytype == JOY_4AXIS_2) {
03294         new CTextButton(PX(XO+0),PY(YO-1),3*BW,BH,"Axis 1/2");
03295         new CTextButton(PX(XO+4),PY(YO-1),3*BW,BH,"Axis 3/4");
03296         btn = new CTextButton(PX(XO + 8), PY(YO - 1), 3 * BW, BH, "Disabled");
03297         btn->SetColor(CLR_GREY);
03298     } else if(joytype == JOY_CH) {
03299         new CTextButton(PX(XO+0),PY(YO-1),3*BW,BH,"Axis 1/2");
03300         new CTextButton(PX(XO+4),PY(YO-1),3*BW,BH,"Axis 3/4");
03301         new CTextButton(PX(XO+8),PY(YO-1),3*BW,BH,"Hat/D-pad");
03302     } else if ( joytype==JOY_FCS) {
03303         new CTextButton(PX(XO+0),PY(YO-1),3*BW,BH,"Axis 1/2");
03304         new CTextButton(PX(XO+4),PY(YO-1),3*BW,BH,"Axis 3");
03305         new CTextButton(PX(XO+8),PY(YO-1),3*BW,BH,"Hat/D-pad");
03306     } else if(joytype == JOY_NONE) {
03307         btn = new CTextButton(PX(XO + 0), PY(YO - 1), 3 * BW, BH, "Disabled");
03308         btn->SetColor(CLR_GREY);
03309         btn = new CTextButton(PX(XO + 4), PY(YO - 1), 3 * BW, BH, "Disabled");
03310         btn->SetColor(CLR_GREY);
03311         btn = new CTextButton(PX(XO + 8), PY(YO - 1), 3 * BW, BH, "Disabled");
03312         btn->SetColor(CLR_GREY);
03313     }
03314 #undef XO
03315 #undef YO
03316    
03317     /* The modifier buttons */
03318     AddModButton(PX(0),PY(17),50,BH,"Mod1",1);
03319     AddModButton(PX(2),PY(17),50,BH,"Mod2",2);
03320     AddModButton(PX(4),PY(17),50,BH,"Mod3",3);
03321     AddModButton(PX(6),PY(17),50,BH,"Host",4);
03322     /* Create Handler buttons */
03323     Bitu xpos=3;Bitu ypos=11;
03324     for (CHandlerEventVector_it hit=handlergroup.begin();hit!=handlergroup.end();++hit) {
03325         unsigned int columns = ((unsigned int)strlen((*hit)->ButtonName()) + 9U) / 10U;
03326         if ((xpos+columns-1)>6) {
03327             xpos=3;ypos++;
03328         }
03329         CEventButton *button=new CEventButton(PX(xpos*3),PY(ypos),BW*3*columns,BH,(*hit)->ButtonName(),(*hit));
03330         (*hit)->notifybutton(button);
03331         xpos += columns;
03332         if (xpos>6) {
03333             xpos=3;ypos++;
03334         }
03335     }
03336     next_handler_xpos = xpos;
03337     next_handler_ypos = ypos;
03338     /* Create some text buttons */
03339 //  new CTextButton(PX(6),0,124,BH,"Keyboard Layout");
03340 //  new CTextButton(PX(17),0,124,BH,"Joystick Layout");
03341 
03342     bind_but.action=new CCaptionButton(180,426,0,0);
03343 
03344     bind_but.event_title=new CCaptionButton(0,350,0,0);
03345     bind_but.bind_title=new CCaptionButton(0,365,0,0);
03346 
03347     /* Create binding support buttons */
03348     
03349     bind_but.mod1=new CCheckButton(20,410,60,BH, "mod1",BC_Mod1);
03350     bind_but.mod2=new CCheckButton(20,432,60,BH, "mod2",BC_Mod2);
03351     bind_but.mod3=new CCheckButton(20,454,60,BH, "mod3",BC_Mod3);
03352     bind_but.host=new CCheckButton(100,410,60,BH,"host",BC_Host);
03353     bind_but.hold=new CCheckButton(100,432,60,BH,"hold",BC_Hold);
03354 
03355     bind_but.add=new CBindButton(20,384,50,BH,"Add",BB_Add);
03356     bind_but.del=new CBindButton(70,384,50,BH,"Del",BB_Del);
03357     bind_but.next=new CBindButton(120,384,50,BH,"Next",BB_Next);
03358 
03359     bind_but.save=new CBindButton(180,444,50,BH,"Save",BB_Save);
03360     bind_but.exit=new CBindButton(230,444,50,BH,"Exit",BB_Exit);
03361     bind_but.cap=new CBindButton(280,444,50,BH,"Capt",BB_Capture);
03362 
03363     bind_but.dbg = new CCaptionButton(180, 462, 460, 20); // right below the Save button
03364     bind_but.dbg->Change("(event debug)");
03365 
03366     bind_but.dbg2 = new CCaptionButton(330, 444, 310, 20); // right next to the Save button
03367     bind_but.dbg2->Change("%s", "");
03368 
03369     bind_but.bind_title->Change("Bind Title");
03370 
03371     mapper_addhandler_create_buttons = true;
03372 }
03373 
03374 static void CreateStringBind(char * line,bool loading=false) {
03375     std::string o_line = line;
03376 
03377     line=trim(line);
03378     if (*line == 0) return;
03379     char * eventname=StripWord(line);
03380     CEvent * event;
03381     for (CEventVector_it ev_it=events.begin();ev_it!=events.end();++ev_it) {
03382         if (!strcasecmp((*ev_it)->GetName(),eventname)) {
03383             event=*ev_it;
03384             goto foundevent;
03385         }
03386     }
03387 
03388     if (loading) {
03389         /* NTS: StripWord() updates line pointer after ASCIIZ snipping the event name */
03390         pending_string_binds[eventname] = line; /* perhaps code will register it later (i.e. Herc pal change) */
03391         LOG(LOG_MISC,LOG_WARN)("Can't find matching event for %s = %s yet. It may exist later when registered elsewhere in this emulator.",eventname,line);
03392     }
03393     else {
03394         LOG(LOG_MISC,LOG_WARN)("Can't find matching event for %s",eventname);
03395     }
03396 
03397     return ;
03398 foundevent:
03399     CBind * bind;
03400     for (char * bindline=StripWord(line);*bindline;bindline=StripWord(line)) {
03401         for (CBindGroup_it it=bindgroups.begin();it!=bindgroups.end();++it) {
03402             bind=(*it)->CreateConfigBind(bindline);
03403             if (bind) {
03404                 event->AddBind(bind);
03405                 bind->SetFlags(bindline);
03406                 event->update_menu_shortcut();
03407                 break;
03408             }
03409         }
03410     }
03411 }
03412 
03413 #if defined(C_SDL2)
03414 
03415 static struct {
03416     const char * eventend;
03417     Bitu key;
03418 } DefaultKeys[]={
03419 
03420     {"f1",SDL_SCANCODE_F1},     {"f2",SDL_SCANCODE_F2},     {"f3",SDL_SCANCODE_F3},     {"f4",SDL_SCANCODE_F4},
03421     {"f5",SDL_SCANCODE_F5},     {"f6",SDL_SCANCODE_F6},     {"f7",SDL_SCANCODE_F7},     {"f8",SDL_SCANCODE_F8},
03422     {"f9",SDL_SCANCODE_F9},     {"f10",SDL_SCANCODE_F10},   {"f11",SDL_SCANCODE_F11},   {"f12",SDL_SCANCODE_F12},
03423 
03424     {"1",SDL_SCANCODE_1},       {"2",SDL_SCANCODE_2},       {"3",SDL_SCANCODE_3},       {"4",SDL_SCANCODE_4},
03425     {"5",SDL_SCANCODE_5},       {"6",SDL_SCANCODE_6},       {"7",SDL_SCANCODE_7},       {"8",SDL_SCANCODE_8},
03426     {"9",SDL_SCANCODE_9},       {"0",SDL_SCANCODE_0},
03427 
03428     {"a",SDL_SCANCODE_A},       {"b",SDL_SCANCODE_B},       {"c",SDL_SCANCODE_C},       {"d",SDL_SCANCODE_D},
03429     {"e",SDL_SCANCODE_E},       {"f",SDL_SCANCODE_F},       {"g",SDL_SCANCODE_G},       {"h",SDL_SCANCODE_H},
03430     {"i",SDL_SCANCODE_I},       {"j",SDL_SCANCODE_J},       {"k",SDL_SCANCODE_K},       {"l",SDL_SCANCODE_L},
03431     {"m",SDL_SCANCODE_M},       {"n",SDL_SCANCODE_N},       {"o",SDL_SCANCODE_O},       {"p",SDL_SCANCODE_P},
03432     {"q",SDL_SCANCODE_Q},       {"r",SDL_SCANCODE_R},       {"s",SDL_SCANCODE_S},       {"t",SDL_SCANCODE_T},
03433     {"u",SDL_SCANCODE_U},       {"v",SDL_SCANCODE_V},       {"w",SDL_SCANCODE_W},       {"x",SDL_SCANCODE_X},
03434     {"y",SDL_SCANCODE_Y},       {"z",SDL_SCANCODE_Z},       {"space",SDL_SCANCODE_SPACE},
03435     {"esc",SDL_SCANCODE_ESCAPE},    {"equals",SDL_SCANCODE_EQUALS},     {"grave",SDL_SCANCODE_GRAVE},
03436     {"tab",SDL_SCANCODE_TAB},       {"enter",SDL_SCANCODE_RETURN},      {"bspace",SDL_SCANCODE_BACKSPACE},
03437     {"lbracket",SDL_SCANCODE_LEFTBRACKET},                      {"rbracket",SDL_SCANCODE_RIGHTBRACKET},
03438     {"minus",SDL_SCANCODE_MINUS},   {"capslock",SDL_SCANCODE_CAPSLOCK}, {"semicolon",SDL_SCANCODE_SEMICOLON},
03439     {"quote", SDL_SCANCODE_APOSTROPHE}, {"backslash",SDL_SCANCODE_BACKSLASH},   {"lshift",SDL_SCANCODE_LSHIFT},
03440     {"rshift",SDL_SCANCODE_RSHIFT}, {"lalt",SDL_SCANCODE_LALT},         {"ralt",SDL_SCANCODE_RALT},
03441     {"lctrl",SDL_SCANCODE_LCTRL},   {"rctrl",SDL_SCANCODE_RCTRL},       {"comma",SDL_SCANCODE_COMMA},
03442     {"period",SDL_SCANCODE_PERIOD}, {"slash",SDL_SCANCODE_SLASH},       {"printscreen",SDL_SCANCODE_PRINTSCREEN},
03443     {"scrolllock",SDL_SCANCODE_SCROLLLOCK}, {"pause",SDL_SCANCODE_PAUSE},       {"pagedown",SDL_SCANCODE_PAGEDOWN},
03444     {"pageup",SDL_SCANCODE_PAGEUP}, {"insert",SDL_SCANCODE_INSERT},     {"home",SDL_SCANCODE_HOME},
03445     {"delete",SDL_SCANCODE_DELETE}, {"end",SDL_SCANCODE_END},           {"up",SDL_SCANCODE_UP},
03446     {"left",SDL_SCANCODE_LEFT},     {"down",SDL_SCANCODE_DOWN},         {"right",SDL_SCANCODE_RIGHT},
03447     {"kp_0",SDL_SCANCODE_KP_0}, {"kp_1",SDL_SCANCODE_KP_1}, {"kp_2",SDL_SCANCODE_KP_2}, {"kp_3",SDL_SCANCODE_KP_3},
03448     {"kp_4",SDL_SCANCODE_KP_4}, {"kp_5",SDL_SCANCODE_KP_5}, {"kp_6",SDL_SCANCODE_KP_6}, {"kp_7",SDL_SCANCODE_KP_7},
03449     {"kp_8",SDL_SCANCODE_KP_8}, {"kp_9",SDL_SCANCODE_KP_9}, {"numlock",SDL_SCANCODE_NUMLOCKCLEAR},
03450     {"kp_divide",SDL_SCANCODE_KP_DIVIDE},   {"kp_multiply",SDL_SCANCODE_KP_MULTIPLY},
03451     {"kp_minus",SDL_SCANCODE_KP_MINUS},     {"kp_plus",SDL_SCANCODE_KP_PLUS},
03452     {"kp_period",SDL_SCANCODE_KP_PERIOD},   {"kp_enter",SDL_SCANCODE_KP_ENTER},
03453 
03454     /* Is that the extra backslash key ("less than" key) */
03455     /* on some keyboards with the 102-keys layout??      */
03456     {"lessthan",SDL_SCANCODE_NONUSBACKSLASH},
03457     {0,0}
03458 };
03459 
03460 #else
03461 
03462 static struct {
03463     const char * eventend;
03464     Bitu key;
03465 } DefaultKeys[]={
03466     {"f1",SDLK_F1},     {"f2",SDLK_F2},     {"f3",SDLK_F3},     {"f4",SDLK_F4},
03467     {"f5",SDLK_F5},     {"f6",SDLK_F6},     {"f7",SDLK_F7},     {"f8",SDLK_F8},
03468     {"f9",SDLK_F9},     {"f10",SDLK_F10},   {"f11",SDLK_F11},   {"f12",SDLK_F12},
03469 
03470     {"1",SDLK_1},       {"2",SDLK_2},       {"3",SDLK_3},       {"4",SDLK_4},
03471     {"5",SDLK_5},       {"6",SDLK_6},       {"7",SDLK_7},       {"8",SDLK_8},
03472     {"9",SDLK_9},       {"0",SDLK_0},
03473 
03474     {"a",SDLK_a},       {"b",SDLK_b},       {"c",SDLK_c},       {"d",SDLK_d},
03475     {"e",SDLK_e},       {"f",SDLK_f},       {"g",SDLK_g},       {"h",SDLK_h},
03476     {"i",SDLK_i},       {"j",SDLK_j},       {"k",SDLK_k},       {"l",SDLK_l},
03477     {"m",SDLK_m},       {"n",SDLK_n},       {"o",SDLK_o},       {"p",SDLK_p},
03478     {"q",SDLK_q},       {"r",SDLK_r},       {"s",SDLK_s},       {"t",SDLK_t},
03479     {"u",SDLK_u},       {"v",SDLK_v},       {"w",SDLK_w},       {"x",SDLK_x},
03480     {"y",SDLK_y},       {"z",SDLK_z},       {"space",SDLK_SPACE},
03481     {"esc",SDLK_ESCAPE},    {"equals",SDLK_EQUALS},     {"grave",SDLK_BACKQUOTE},
03482     {"tab",SDLK_TAB},       {"enter",SDLK_RETURN},      {"bspace",SDLK_BACKSPACE},
03483     {"lbracket",SDLK_LEFTBRACKET},                      {"rbracket",SDLK_RIGHTBRACKET},
03484     {"minus",SDLK_MINUS},   {"capslock",SDLK_CAPSLOCK}, {"semicolon",SDLK_SEMICOLON},
03485     {"quote", SDLK_QUOTE},  {"backslash",SDLK_BACKSLASH},   {"lshift",SDLK_LSHIFT},
03486     {"rshift",SDLK_RSHIFT}, {"lalt",SDLK_LALT},         {"ralt",SDLK_RALT},
03487     {"lctrl",SDLK_LCTRL},   {"rctrl",SDLK_RCTRL},       {"comma",SDLK_COMMA},
03488     {"period",SDLK_PERIOD}, {"slash",SDLK_SLASH},
03489 
03490 #if defined(C_SDL2)
03491     {"printscreen",SDLK_PRINTSCREEN},
03492     {"scrolllock",SDLK_SCROLLLOCK},
03493 #else
03494     {"printscreen",SDLK_PRINT},
03495     {"scrolllock",SDLK_SCROLLOCK},
03496 #endif
03497 
03498     {"pause",SDLK_PAUSE},       {"pagedown",SDLK_PAGEDOWN},
03499     {"pageup",SDLK_PAGEUP}, {"insert",SDLK_INSERT},     {"home",SDLK_HOME},
03500     {"delete",SDLK_DELETE}, {"end",SDLK_END},           {"up",SDLK_UP},
03501     {"left",SDLK_LEFT},     {"down",SDLK_DOWN},         {"right",SDLK_RIGHT},
03502 
03503 #if defined(C_SDL2)
03504     {"kp_0",SDLK_KP_0}, {"kp_1",SDLK_KP_1}, {"kp_2",SDLK_KP_2}, {"kp_3",SDLK_KP_3},
03505     {"kp_4",SDLK_KP_4}, {"kp_5",SDLK_KP_5}, {"kp_6",SDLK_KP_6}, {"kp_7",SDLK_KP_7},
03506     {"kp_8",SDLK_KP_8}, {"kp_9",SDLK_KP_9},
03507     {"numlock",SDLK_NUMLOCKCLEAR},
03508 #else
03509     {"kp_0",SDLK_KP0},  {"kp_1",SDLK_KP1},  {"kp_2",SDLK_KP2},  {"kp_3",SDLK_KP3},
03510     {"kp_4",SDLK_KP4},  {"kp_5",SDLK_KP5},  {"kp_6",SDLK_KP6},  {"kp_7",SDLK_KP7},
03511     {"kp_8",SDLK_KP8},  {"kp_9",SDLK_KP9},
03512     {"numlock",SDLK_NUMLOCK},
03513 #endif
03514 
03515     {"kp_divide",SDLK_KP_DIVIDE},   {"kp_multiply",SDLK_KP_MULTIPLY},
03516     {"kp_minus",SDLK_KP_MINUS},     {"kp_plus",SDLK_KP_PLUS},
03517     {"kp_period",SDLK_KP_PERIOD},   {"kp_enter",SDLK_KP_ENTER},
03518 
03519     /* NTS: IBM PC keyboards as far as I know do not have numeric keypad equals sign.
03520      *      This default assignment should allow Apple Mac users (who's keyboards DO have one)
03521      *      to use theirs as a normal equals sign. */
03522     {"kp_equals",SDLK_KP_EQUALS},
03523 
03524 #if defined(C_SDL2)
03525     // TODO??
03526 #else
03527     /* Windows 95 keyboard stuff */
03528     {"lwindows",SDLK_LSUPER},
03529     {"rwindows",SDLK_RSUPER},
03530 #endif
03531     {"rwinmenu",SDLK_MENU},
03532 
03533 #if defined (MACOSX)
03534     /* Intl Mac keyboards in US layout actually put U+00A7 SECTION SIGN here */
03535     {"lessthan",SDLK_WORLD_0},
03536 #else
03537     {"lessthan",SDLK_LESS},
03538 #endif
03539 
03540 #if defined(C_SDL2)
03541     // TODO??
03542 #else
03543 #ifdef SDL_DOSBOX_X_SPECIAL
03544     /* hack for Japanese keyboards with \ and _ */
03545     {"jp_bckslash",SDLK_JP_RO}, // Same difference
03546     {"jp_ro",SDLK_JP_RO}, // DOSBox proprietary
03547     /* hack for Japanese keyboards with Yen and | */
03548     {"jp_yen",SDLK_JP_YEN },
03549 #endif
03550     /* more */
03551     {"jp_hankaku", SDLK_WORLD_12 },
03552     {"jp_muhenkan", SDLK_WORLD_13 },
03553     {"jp_henkan", SDLK_WORLD_14 },
03554     {"jp_hiragana", SDLK_WORLD_15 },
03555     {"colon", SDLK_COLON },
03556     {"caret", SDLK_CARET },
03557     {"atsign", SDLK_AT },
03558 #endif
03559 
03560     {0,0}
03561 };
03562 
03563 #endif
03564 
03565 static void ClearAllBinds(void) {
03566         for (CEvent *event : events)
03567                 event->ClearBinds();
03568 }
03569 
03570 static void CreateDefaultBinds(void) {
03571         ClearAllBinds();
03572     char buffer[512];
03573     Bitu i=0;
03574     while (DefaultKeys[i].eventend) {
03575         sprintf(buffer,"key_%s \"key %d\"",DefaultKeys[i].eventend,(int)DefaultKeys[i].key);
03576         CreateStringBind(buffer);
03577         i++;
03578     }
03579 
03580 #if defined(C_SDL2)
03581     sprintf(buffer,"mod_1 \"key %d\"",SDL_SCANCODE_RCTRL);CreateStringBind(buffer);
03582     sprintf(buffer,"mod_1 \"key %d\"",SDL_SCANCODE_LCTRL);CreateStringBind(buffer);
03583     sprintf(buffer,"mod_2 \"key %d\"",SDL_SCANCODE_RALT);CreateStringBind(buffer);
03584     sprintf(buffer,"mod_2 \"key %d\"",SDL_SCANCODE_LALT);CreateStringBind(buffer);
03585     sprintf(buffer,"mod_3 \"key %d\"",SDL_SCANCODE_RSHIFT);CreateStringBind(buffer);
03586     sprintf(buffer,"mod_3 \"key %d\"",SDL_SCANCODE_LSHIFT);CreateStringBind(buffer);
03587 # if defined(WIN32) && !defined(C_HX_DOS) /* F12 is not a good modifier key in Windows: https://stackoverflow.com/questions/18997754/how-to-disable-f12-to-debug-application-in-visual-studio-2012 */
03588     sprintf(buffer,"host \"key %d\"",SDL_SCANCODE_F11);CreateStringBind(buffer);
03589 # else
03590     sprintf(buffer,"host \"key %d\"",SDL_SCANCODE_F12);CreateStringBind(buffer);
03591 # endif
03592 #else
03593     sprintf(buffer,"mod_1 \"key %d\"",SDLK_RCTRL);CreateStringBind(buffer);
03594     sprintf(buffer,"mod_1 \"key %d\"",SDLK_LCTRL);CreateStringBind(buffer);
03595     sprintf(buffer,"mod_2 \"key %d\"",SDLK_RALT);CreateStringBind(buffer);
03596     sprintf(buffer,"mod_2 \"key %d\"",SDLK_LALT);CreateStringBind(buffer);
03597     sprintf(buffer,"mod_3 \"key %d\"",SDLK_RSHIFT);CreateStringBind(buffer);
03598     sprintf(buffer,"mod_3 \"key %d\"",SDLK_LSHIFT);CreateStringBind(buffer);
03599 # if defined(WIN32) && !defined(C_HX_DOS) /* F12 is not a good modifier key in Windows: https://stackoverflow.com/questions/18997754/how-to-disable-f12-to-debug-application-in-visual-studio-2012 */
03600     sprintf(buffer,"host \"key %d\"",SDLK_F11);CreateStringBind(buffer);
03601 # else
03602     sprintf(buffer,"host \"key %d\"",SDLK_F12);CreateStringBind(buffer);
03603 # endif
03604 #endif
03605 
03606     for (CHandlerEventVector_it hit=handlergroup.begin();hit!=handlergroup.end();++hit) {
03607         (*hit)->MakeDefaultBind(buffer);
03608         CreateStringBind(buffer);
03609     }
03610 
03611     /* joystick1, buttons 1-6 */
03612     sprintf(buffer,"jbutton_0_0 \"stick_0 button 0\" ");CreateStringBind(buffer);
03613     sprintf(buffer,"jbutton_0_1 \"stick_0 button 1\" ");CreateStringBind(buffer);
03614     sprintf(buffer,"jbutton_0_2 \"stick_0 button 2\" ");CreateStringBind(buffer);
03615     sprintf(buffer,"jbutton_0_3 \"stick_0 button 3\" ");CreateStringBind(buffer);
03616     sprintf(buffer,"jbutton_0_4 \"stick_0 button 4\" ");CreateStringBind(buffer);
03617     sprintf(buffer,"jbutton_0_5 \"stick_0 button 5\" ");CreateStringBind(buffer);
03618     /* joystick2, buttons 1-2 */
03619     sprintf(buffer,"jbutton_1_0 \"stick_1 button 0\" ");CreateStringBind(buffer);
03620     sprintf(buffer,"jbutton_1_1 \"stick_1 button 1\" ");CreateStringBind(buffer);
03621 
03622     /* joystick1, axes 1-4 */
03623     sprintf(buffer,"jaxis_0_0- \"stick_0 axis 0 0\" ");CreateStringBind(buffer);
03624     sprintf(buffer,"jaxis_0_0+ \"stick_0 axis 0 1\" ");CreateStringBind(buffer);
03625     sprintf(buffer,"jaxis_0_1- \"stick_0 axis 1 0\" ");CreateStringBind(buffer);
03626     sprintf(buffer,"jaxis_0_1+ \"stick_0 axis 1 1\" ");CreateStringBind(buffer);
03627     sprintf(buffer,"jaxis_0_2- \"stick_0 axis 2 0\" ");CreateStringBind(buffer);
03628     sprintf(buffer,"jaxis_0_2+ \"stick_0 axis 2 1\" ");CreateStringBind(buffer);
03629     sprintf(buffer,"jaxis_0_3- \"stick_0 axis 3 0\" ");CreateStringBind(buffer);
03630     sprintf(buffer,"jaxis_0_3+ \"stick_0 axis 3 1\" ");CreateStringBind(buffer);
03631     /* joystick2, axes 1-2 */
03632     sprintf(buffer,"jaxis_1_0- \"stick_1 axis 0 0\" ");CreateStringBind(buffer);
03633     sprintf(buffer,"jaxis_1_0+ \"stick_1 axis 0 1\" ");CreateStringBind(buffer);
03634     sprintf(buffer,"jaxis_1_1- \"stick_1 axis 1 0\" ");CreateStringBind(buffer);
03635     sprintf(buffer,"jaxis_1_1+ \"stick_1 axis 1 1\" ");CreateStringBind(buffer);
03636 
03637     /* joystick1, hat */
03638     sprintf(buffer,"jhat_0_0_0 \"stick_0 hat 0 1\" ");CreateStringBind(buffer);
03639     sprintf(buffer,"jhat_0_0_1 \"stick_0 hat 0 2\" ");CreateStringBind(buffer);
03640     sprintf(buffer,"jhat_0_0_2 \"stick_0 hat 0 4\" ");CreateStringBind(buffer);
03641     sprintf(buffer,"jhat_0_0_3 \"stick_0 hat 0 8\" ");CreateStringBind(buffer);
03642 }
03643 
03644 void MAPPER_AddHandler(MAPPER_Handler * handler,MapKeys key,Bitu mods,char const * const eventname,char const * const buttonname,DOSBoxMenu::item **ret_menuitem) {
03645     if (ret_menuitem != NULL)
03646         *ret_menuitem = NULL;
03647 
03648     char tempname[27];
03649     strcpy(tempname,"hand_");
03650     strcat(tempname,eventname);
03651 
03652     //Check if it already exists=> if so return.
03653     for(CHandlerEventVector_it it=handlergroup.begin();it!=handlergroup.end();++it) {
03654         if(strcmp((*it)->buttonname,buttonname) == 0) {
03655             if (ret_menuitem != NULL)
03656                 *ret_menuitem = &mainMenu.get_item(std::string("mapper_") + std::string(eventname));
03657 
03658             return;
03659         }
03660     }
03661 
03662     CHandlerEvent *event = new CHandlerEvent(tempname,handler,key,mods,buttonname);
03663     event->eventname = eventname;
03664 
03665     /* The mapper now automatically makes menu items for mapper events */
03666     DOSBoxMenu::item &menuitem = mainMenu.alloc_item(DOSBoxMenu::item_type_id, std::string("mapper_") + std::string(eventname));
03667     menuitem.set_mapper_event(tempname);
03668 
03669     if (ret_menuitem == NULL)
03670         menuitem.set_text(buttonname);
03671     else
03672         *ret_menuitem = &menuitem;
03673 
03674     if (mapper_addhandler_create_buttons) {
03675         // and a button in the mapper UI
03676         {
03677             unsigned int columns = ((unsigned int)strlen(buttonname) + 9U) / 10U;
03678             if ((next_handler_xpos+columns-1)>6) {
03679                 next_handler_xpos=3;next_handler_ypos++;
03680             }
03681             CEventButton *button=new CEventButton(PX(next_handler_xpos*3),PY(next_handler_ypos),BW*3*columns,BH,buttonname,event);
03682             event->notifybutton(button);
03683             next_handler_xpos += columns;
03684             if (next_handler_xpos>6) {
03685                 next_handler_xpos=3;next_handler_ypos++;
03686             }
03687         }
03688 
03689         // this event may have appeared in the user's mapper file, and been ignored.
03690         // now is the time to register it.
03691         {
03692             auto i = pending_string_binds.find(tempname);
03693             char tmp[512];
03694 
03695             if (i != pending_string_binds.end()) {
03696                 LOG(LOG_MISC,LOG_WARN)("Found pending event for %s from user's file, applying now",tempname);
03697 
03698                 snprintf(tmp,sizeof(tmp),"%s %s",tempname,i->second.c_str());
03699 
03700                 CreateStringBind(tmp);
03701 
03702                 pending_string_binds.erase(i);
03703             }
03704             else {
03705                 /* use default binding.
03706                  * redundant? Yes! But, apparently necessary. */
03707                 event->MakeDefaultBind(tmp);
03708                 CreateStringBind(tmp);
03709             }
03710 
03711             // color of the button needs to reflect binding
03712             event->notify_button->BindColor();
03713         }
03714     }
03715 
03716     return ;
03717 }
03718 
03719 static void MAPPER_SaveBinds(void) {
03720     FILE * savefile=fopen(mapper.filename.c_str(),"wt+");
03721     if (!savefile) {
03722         LOG_MSG("Can't open %s for saving the mappings",mapper.filename.c_str());
03723         return;
03724     }
03725     char buf[128];
03726     for (CEventVector_it event_it=events.begin();event_it!=events.end();++event_it) {
03727         CEvent * event=*(event_it);
03728         fprintf(savefile,"%s ",event->GetName());
03729         for (CBindList_it bind_it=event->bindlist.begin();bind_it!=event->bindlist.end();++bind_it) {
03730             CBind * bind=*(bind_it);
03731             bind->ConfigName(buf);
03732             bind->AddFlags(buf);
03733             fprintf(savefile,"\"%s\" ",buf);
03734         }
03735         fprintf(savefile,"\n");
03736     }
03737     fclose(savefile);
03738     change_action_text("Mapper file saved.",CLR_WHITE);
03739 }
03740 
03741 static bool MAPPER_LoadBinds(void) {
03742     FILE * loadfile=fopen(mapper.filename.c_str(),"rt");
03743     if (!loadfile) return false;
03744         ClearAllBinds();
03745     char linein[512];
03746     while (fgets(linein,512,loadfile)) {
03747         CreateStringBind(linein,/*loading*/true);
03748     }
03749     fclose(loadfile);
03750     LOG(LOG_MISC,LOG_NORMAL)("MAPPER: Loading mapper settings from %s", mapper.filename.c_str());
03751     return true;
03752 }
03753 
03754 void MAPPER_CheckEvent(SDL_Event * event) {
03755     for (CBindGroup_it it=bindgroups.begin();it!=bindgroups.end();++it) {
03756         if ((*it)->CheckEvent(event)) return;
03757     }
03758 
03759     if (log_keyboard_scan_codes) {
03760         if (event->type == SDL_KEYDOWN || event->type == SDL_KEYUP)
03761             LOG_MSG("MAPPER: SDL keyboard event (%s): scancode=0x%X sym=0x%X mod=0x%X",
03762                 event->type == SDL_KEYDOWN?"down":"up",
03763                 event->key.keysym.scancode,event->key.keysym.sym,event->key.keysym.mod);
03764     }
03765 }
03766 
03767 void PressRelease(void) {
03768     if (press_select != NULL) {
03769         press_select->SetPress(false);
03770         press_select = NULL;
03771     }
03772 }
03773 
03774 void PressSelect(CButton *b) {
03775     if (press_select != b)
03776         PressRelease();
03777 
03778     if (b != NULL) {
03779         press_select = b;
03780         press_select->SetPress(true);
03781     }
03782 }
03783 
03784 void Mapper_MousePressEvent(SDL_Event &event) {
03785     /* Check the press */
03786     for (CButton_it but_it = buttons.begin();but_it!=buttons.end();++but_it) {
03787         if ((*but_it)->OnTop(event.button.x,event.button.y)) {
03788             PressSelect(*but_it);
03789             break;
03790         }
03791     }
03792 }
03793 
03794 void Mapper_MouseInputEvent(SDL_Event &event) {
03795     PressRelease();
03796 
03797     /* Check the press */
03798     for (CButton_it but_it = buttons.begin();but_it!=buttons.end();++but_it) {
03799         if ((*but_it)->OnTop(event.button.x,event.button.y)) {
03800             (*but_it)->Click();
03801         }
03802     }
03803 }
03804 
03805 #if defined(C_SDL2)
03806 void Mapper_FingerPressEvent(SDL_Event &event) {
03807     SDL_Event ev;
03808 
03809     memset(&ev,0,sizeof(ev));
03810     ev.type = SDL_MOUSEBUTTONDOWN;
03811 
03812     /* NTS: Windows versions of SDL2 do normalize the coordinates */
03813     ev.button.x = (Sint32)(event.tfinger.x * mapper.surface->w);
03814     ev.button.y = (Sint32)(event.tfinger.y * mapper.surface->h);
03815 
03816     Mapper_MousePressEvent(ev);
03817 }
03818 
03819 void Mapper_FingerInputEvent(SDL_Event &event) {
03820     SDL_Event ev;
03821 
03822     memset(&ev,0,sizeof(ev));
03823     ev.type = SDL_MOUSEBUTTONUP;
03824 
03825     /* NTS: Windows versions of SDL2 do normalize the coordinates */
03826     ev.button.x = (Sint32)(event.tfinger.x * mapper.surface->w);
03827     ev.button.y = (Sint32)(event.tfinger.y * mapper.surface->h);
03828 
03829     Mapper_MouseInputEvent(ev);
03830 }
03831 #endif
03832 
03833 void BIND_MappingEvents(void) {
03834     SDL_Event event;
03835 
03836     if (GUI_JoystickCount()>0) SDL_JoystickUpdate();
03837     MAPPER_UpdateJoysticks();
03838 
03839 #if C_EMSCRIPTEN
03840     emscripten_sleep_with_yield(0);
03841 #endif
03842 
03843     while (SDL_PollEvent(&event)) {
03844 #if C_EMSCRIPTEN
03845         emscripten_sleep_with_yield(0);
03846 #endif
03847 
03848         switch (event.type) {
03849 #if !defined(C_SDL2) && defined(_WIN32) && !defined(HX_DOS)
03850         case SDL_SYSWMEVENT : {
03851             switch ( event.syswm.msg->msg ) {
03852                 case WM_COMMAND:
03853 # if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
03854                     if (GetMenu(GetHWND())) {
03855                         if (mapperMenu.mainMenuWM_COMMAND((unsigned int)LOWORD(event.syswm.msg->wParam))) return;
03856                     }
03857 # endif
03858                     break;
03859             }
03860         } break;
03861 #endif
03862 #if defined(C_SDL2) && !defined(IGNORE_TOUCHSCREEN)
03863         case SDL_FINGERDOWN:
03864             Mapper_FingerPressEvent(event);
03865             break;
03866 #endif
03867 #if defined(C_SDL2) && !defined(IGNORE_TOUCHSCREEN)
03868         case SDL_FINGERUP:
03869             Mapper_FingerInputEvent(event);
03870             break;
03871 #endif
03872         case SDL_MOUSEBUTTONDOWN:
03873 #if defined(C_SDL2)
03874             if (event.button.which != SDL_TOUCH_MOUSEID) /* don't handle mouse events faked by touchscreen */
03875                 Mapper_MousePressEvent(event);
03876 #else
03877             Mapper_MousePressEvent(event);
03878 #endif
03879             break;
03880         case SDL_MOUSEBUTTONUP:
03881 #if defined(C_SDL2)
03882             if (event.button.which != SDL_TOUCH_MOUSEID) /* don't handle mouse events faked by touchscreen */
03883                 Mapper_MouseInputEvent(event);
03884 #else
03885             Mapper_MouseInputEvent(event);
03886 #endif
03887             break;
03888         case SDL_QUIT:
03889             mapper.exit=true;
03890             break;
03891         case SDL_KEYDOWN: /* help the user determine keyboard problems by showing keyboard event, scan code, etc. */
03892         case SDL_KEYUP:
03893             {
03894                 static int event_count = 0;
03895 #if defined(C_SDL2)
03896                 SDL_Keysym &s = event.key.keysym;
03897 #else
03898                 SDL_keysym &s = event.key.keysym;
03899 #endif
03900                 char tmp[256];
03901 
03902                 // ESC is your magic key out of capture
03903                 if (s.sym == SDLK_ESCAPE) {
03904                     if (mouselocked) {
03905                         GFX_CaptureMouse();
03906                         mapper_esc_count = 0;
03907                     }
03908                     else {
03909                         if (event.type == SDL_KEYUP) {
03910                             if (++mapper_esc_count == 3) {
03911                                 void MAPPER_ReleaseAllKeys(void);
03912                                 MAPPER_ReleaseAllKeys();
03913                                 mapper.exit=true;
03914                             }
03915                         }
03916                     }
03917                 }
03918                 else {
03919                     mapper_esc_count = 0;
03920                 }
03921 
03922                 size_t tmpl;
03923 #if defined(C_SDL2)
03924                 tmpl = (size_t)sprintf(tmp,"%c%02x: scan=%d sym=%d mod=%xh name=%s",
03925                     (event.type == SDL_KEYDOWN ? 'D' : 'U'),
03926                     event_count&0xFF,
03927                     s.scancode,
03928                     s.sym,
03929                     s.mod,
03930                     SDL_GetScancodeName(s.scancode));
03931 #else
03932                 tmpl = (size_t)sprintf(tmp,"%c%02x: scan=%u sym=%u mod=%xh u=%xh name=%s",
03933                     (event.type == SDL_KEYDOWN ? 'D' : 'U'),
03934                     event_count&0xFF,
03935                     s.scancode,
03936                     s.sym,
03937                     s.mod,
03938                     s.unicode,
03939                     SDL_GetKeyName((SDLKey)MapSDLCode((Bitu)s.sym)));
03940 #endif
03941                 while (tmpl < (440/8)) tmp[tmpl++] = ' ';
03942                 assert(tmpl < sizeof(tmp));
03943                 tmp[tmpl] = 0;
03944 
03945                 LOG(LOG_GUI,LOG_DEBUG)("Mapper keyboard event: %s",tmp);
03946                 bind_but.dbg->Change("%s",tmp);
03947 
03948                 tmpl = 0;
03949 #if defined(WIN32)
03950 # if defined(C_SDL2)
03951 # else
03952                 {
03953                     char nm[256];
03954 
03955                     nm[0] = 0;
03956 #if !defined(HX_DOS) /* I assume HX DOS doesn't bother with keyboard scancode names */
03957                     GetKeyNameText(s.scancode << 16,nm,sizeof(nm)-1);
03958 #endif
03959 
03960                     tmpl = sprintf(tmp, "Win32: VK=0x%x kn=%s",(unsigned int)s.win32_vk,nm);
03961                 }
03962 # endif
03963 #endif
03964 #if defined(SDL_VIDEO_DRIVER_X11)
03965 # if defined(C_SDL2)
03966 # else
03967                 {
03968                     char *LinuxX11_KeySymName(Uint32 x);
03969 
03970                     char *name;
03971 
03972                     name = LinuxX11_KeySymName(s.x11_sym);
03973                     tmpl = (size_t)sprintf(tmp,"X11: Sym=0x%x sn=%s",(unsigned int)s.x11_sym,name ? name : "");
03974                 }
03975 # endif
03976 #endif
03977                 while (tmpl < (310 / 8)) tmp[tmpl++] = ' ';
03978                 assert(tmpl < sizeof(tmp));
03979                 tmp[tmpl] = 0;
03980                 bind_but.dbg2->Change("%s", tmp);
03981 
03982                 event_count++;
03983             }
03984             /* fall through to mapper UI processing */
03985         default:
03986             if (mapper.addbind) {
03987                 for (CBindGroup_it it=bindgroups.begin();it!=bindgroups.end();++it) {
03988                     CBind * newbind=(*it)->CreateEventBind(&event);
03989                     if (!newbind) continue;
03990                     assert(mapper.aevent != NULL);
03991                     mapper.aevent->AddBind(newbind);
03992                     SetActiveEvent(mapper.aevent);
03993                     mapper.addbind=false;
03994                     mapper.aevent->update_menu_shortcut();
03995                     RedrawMapperBindButton(mapper.aevent);
03996                     break;
03997                 }
03998             }
03999 
04000             void MAPPER_CheckEvent(SDL_Event * event);
04001             MAPPER_CheckEvent(&event);
04002         }
04003     }
04004 }
04005 
04006 static void InitializeJoysticks(void) {
04007     mapper.sticks.num=0;
04008     mapper.sticks.num_groups=0;
04009     if (joytype != JOY_NONE) {
04010         mapper.sticks.num=(Bitu)SDL_NumJoysticks();
04011         LOG(LOG_MISC,LOG_DEBUG)("Joystick type != none, SDL reports %u sticks",(unsigned int)mapper.sticks.num);
04012         if (joytype==JOY_AUTO) {
04013             // try to figure out what joystick type to select
04014             // depending on the number of physically attached joysticks
04015             if (mapper.sticks.num>1) {
04016                 // more than one joystick present; if all are acceptable use 2axis
04017                 // to allow emulation of two joysticks
04018                 bool first_usable=false;
04019                 SDL_Joystick* tmp_stick1=SDL_JoystickOpen(0);
04020                 if (tmp_stick1) {
04021                     if ((SDL_JoystickNumAxes(tmp_stick1)>1) || (SDL_JoystickNumButtons(tmp_stick1)>0)) {
04022                         first_usable=true;
04023                     }
04024                     SDL_JoystickClose(tmp_stick1);
04025                 }
04026                 bool second_usable=false;
04027                 SDL_Joystick* tmp_stick2=SDL_JoystickOpen(1);
04028                 if (tmp_stick2) {
04029                     if ((SDL_JoystickNumAxes(tmp_stick2)>1) || (SDL_JoystickNumButtons(tmp_stick2)>0)) {
04030                         second_usable=true;
04031                     }
04032                     SDL_JoystickClose(tmp_stick2);
04033                 }
04034                 // choose joystick type now that we know which physical joysticks are usable
04035                 if (first_usable) {
04036                     if (second_usable) {
04037                         joytype=JOY_2AXIS;
04038                         LOG_MSG("Two or more joysticks reported, initializing with 2axis");
04039                     } else {
04040                         joytype=JOY_4AXIS;
04041                         LOG_MSG("One joystick reported, initializing with 4axis");
04042                     }
04043                 } else if (second_usable) {
04044                     joytype=JOY_4AXIS_2;
04045                     LOG_MSG("One joystick reported, initializing with 4axis_2");
04046                 }
04047             } else if (mapper.sticks.num) {
04048                 // one joystick present; if it is acceptable use 4axis
04049                 joytype=JOY_NONE;
04050                 SDL_Joystick* tmp_stick1=SDL_JoystickOpen(0);
04051                 if (tmp_stick1) {
04052                     if ((SDL_JoystickNumAxes(tmp_stick1)>0) || (SDL_JoystickNumButtons(tmp_stick1)>0)) {
04053                         joytype=JOY_4AXIS;
04054                         LOG_MSG("One joystick reported, initializing with 4axis");
04055                     }
04056                 }
04057             } else {
04058                 joytype=JOY_NONE;
04059             }
04060         }
04061     }
04062     else {
04063         LOG(LOG_MISC,LOG_DEBUG)("Joystick type none, not initializing");
04064     }
04065 }
04066 
04067 static void CreateBindGroups(void) {
04068     bindgroups.clear();
04069 #if defined(C_SDL2)
04070     new CKeyBindGroup(SDL_NUM_SCANCODES);
04071 #else
04072     new CKeyBindGroup(SDLK_LAST);
04073 #endif
04074     if (joytype != JOY_NONE) {
04075 #if defined (REDUCE_JOYSTICK_POLLING)
04076         // direct access to the SDL joystick, thus removed from the event handling
04077         if (mapper.sticks.num) SDL_JoystickEventState(SDL_DISABLE);
04078 #else
04079         // enable joystick event handling
04080         if (mapper.sticks.num) SDL_JoystickEventState(SDL_ENABLE);
04081         else return;
04082 #endif
04083         Bit8u joyno=0;
04084         switch (joytype) {
04085         case JOY_NONE:
04086             break;
04087         case JOY_4AXIS:
04088             mapper.sticks.stick[mapper.sticks.num_groups++]=new C4AxisBindGroup(joyno,joyno);
04089             new CStickBindGroup(joyno+1U,joyno+1U,true);
04090             break;
04091         case JOY_4AXIS_2:
04092             mapper.sticks.stick[mapper.sticks.num_groups++]=new C4AxisBindGroup(joyno+1U,joyno);
04093             new CStickBindGroup(joyno,joyno+1U,true);
04094             break;
04095         case JOY_FCS:
04096             mapper.sticks.stick[mapper.sticks.num_groups++]=new CFCSBindGroup(joyno,joyno);
04097             new CStickBindGroup(joyno+1U,joyno+1U,true);
04098             break;
04099         case JOY_CH:
04100             mapper.sticks.stick[mapper.sticks.num_groups++]=new CCHBindGroup(joyno,joyno);
04101             new CStickBindGroup(joyno+1U,joyno+1U,true);
04102             break;
04103         case JOY_2AXIS:
04104         default:
04105             mapper.sticks.stick[mapper.sticks.num_groups++]=new CStickBindGroup(joyno,joyno);
04106             if((joyno+1U) < mapper.sticks.num) {
04107                 mapper.sticks.stick[mapper.sticks.num_groups++]=new CStickBindGroup(joyno+1U,joyno+1U);
04108             } else {
04109                 new CStickBindGroup(joyno+1U,joyno+1U,true);
04110             }
04111             break;
04112         }
04113     }
04114 }
04115 
04116 #if defined (REDUCE_JOYSTICK_POLLING)
04117 void MAPPER_UpdateJoysticks(void) {
04118     for (Bitu i=0; i<mapper.sticks.num_groups; i++) {
04119         mapper.sticks.stick[i]->UpdateJoystick();
04120     }
04121 }
04122 #endif
04123 
04124 void MAPPER_LosingFocus(void) {
04125     for (CEventVector_it evit=events.begin();evit!=events.end();++evit) {
04126         if(*evit != caps_lock_event && *evit != num_lock_event)
04127             (*evit)->DeActivateAll();
04128     }
04129 }
04130 
04131 void MAPPER_ReleaseAllKeys(void) {
04132     for (CEventVector_it evit=events.begin();evit!=events.end();++evit) {
04133         if ((*evit)->active) {
04134             LOG_MSG("Release");
04135             (*evit)->Active(false);
04136         }
04137     }
04138 }
04139 
04140 void MAPPER_RunEvent(Bitu /*val*/) {
04141     KEYBOARD_ClrBuffer();   //Clear buffer
04142     GFX_LosingFocus();      //Release any keys pressed (buffer gets filled again).
04143     MAPPER_RunInternal();
04144 }
04145 
04146 void MAPPER_Run(bool pressed) {
04147     if (pressed)
04148         return;
04149     PIC_AddEvent(MAPPER_RunEvent,0.0001f);  //In case mapper deletes the key object that ran it
04150 }
04151 
04152 void MAPPER_RunInternal() {
04153     MAPPER_ReleaseAllKeys();
04154 
04155 #ifdef DOSBOXMENU_EXTERNALLY_MANAGED
04156     mapperMenu.rebuild();
04157 #endif
04158 
04159     /* Sorry, the MAPPER screws up 3Dfx OpenGL emulation.
04160      * Remove this block when fixed. */
04161     if (GFX_GetPreventFullscreen()) {
04162         LOG_MSG("MAPPER ui is not available while 3Dfx OpenGL emulation is running");
04163         return;
04164     }
04165 
04166     mapper_esc_count = 0;
04167     mapper.running = true;
04168 
04169 #if defined(__WIN32__) && !defined(C_SDL2) && !defined(C_HX_DOS)
04170     if(menu.maxwindow) ShowWindow(GetHWND(), SW_RESTORE);
04171 #endif
04172     int cursor = SDL_ShowCursor(SDL_QUERY);
04173     SDL_ShowCursor(SDL_ENABLE);
04174     bool mousetoggle=false;
04175     if(mouselocked) {
04176         mousetoggle=true;
04177         GFX_CaptureMouse();
04178     }
04179 
04180     /* Be sure that there is no update in progress */
04181     GFX_EndUpdate( 0 );
04182 #if defined(C_SDL2)
04183     void GFX_SetResizeable(bool enable);
04184     GFX_SetResizeable(false);
04185     mapper.window=GFX_SetSDLSurfaceWindow(640,480);
04186     if (mapper.window == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError());
04187     mapper.surface=SDL_GetWindowSurface(mapper.window);
04188     if (mapper.surface == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError());
04189     mapper.draw_surface=SDL_CreateRGBSurface(0,640,480,8,0,0,0,0);
04190     // Needed for SDL_BlitScaled
04191     mapper.draw_surface_nonpaletted=SDL_CreateRGBSurface(0,640,480,32,0x0000ff00,0x00ff0000,0xff000000,0);
04192     mapper.draw_rect=GFX_GetSDLSurfaceSubwindowDims(640,480);
04193     // Sorry, but SDL_SetSurfacePalette requires a full palette.
04194     SDL_Palette *sdl2_map_pal_ptr = SDL_AllocPalette(256);
04195     SDL_SetPaletteColors(sdl2_map_pal_ptr, map_pal, 0, 7);
04196     SDL_SetSurfacePalette(mapper.draw_surface, sdl2_map_pal_ptr);
04197     if (last_clicked) {
04198         last_clicked->SetColor(CLR_WHITE);
04199         last_clicked=NULL;
04200     }
04201 #else
04202     mapper.surface=SDL_SetVideoMode(640,480,8,0);
04203     if (mapper.surface == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError());
04204 
04205     /* Set some palette entries */
04206     SDL_SetPalette(mapper.surface, SDL_LOGPAL|SDL_PHYSPAL, map_pal, 0, 7);
04207     if (last_clicked) {
04208         last_clicked->BindColor();
04209         last_clicked=NULL;
04210     }
04211 #endif
04212 
04213 #if defined(WIN32) && !defined(HX_DOS)
04214     WindowsTaskbarResetPreviewRegion();
04215 #endif
04216 
04217 #ifdef DOSBOXMENU_EXTERNALLY_MANAGED
04218     DOSBox_SetMenu(mapperMenu);
04219 #endif
04220 
04221 #if defined(MACOSX)
04222     void osx_reload_touchbar(void);
04223     osx_reload_touchbar();
04224 #endif
04225 
04226     /* Go in the event loop */
04227     mapper.exit=false;  
04228     mapper.redraw=true;
04229     SetActiveEvent(0);
04230 #if defined (REDUCE_JOYSTICK_POLLING)
04231     SDL_JoystickEventState(SDL_ENABLE);
04232 #endif
04233     while (!mapper.exit) {
04234 #if C_EMSCRIPTEN
04235         emscripten_sleep_with_yield(0);
04236 #endif
04237 
04238         if (mapper.redraw) {
04239             mapper.redraw=false;        
04240             DrawButtons();
04241         } else {
04242 #if defined(C_SDL2)
04243 //            SDL_UpdateWindowSurface(mapper.window);
04244 #endif
04245         }
04246         BIND_MappingEvents();
04247         SDL_Delay(1);
04248     }
04249 #if defined(C_SDL2)
04250     SDL_FreeSurface(mapper.draw_surface);
04251     SDL_FreeSurface(mapper.draw_surface_nonpaletted);
04252     SDL_FreePalette(sdl2_map_pal_ptr);
04253     GFX_SetResizeable(true);
04254 #endif
04255 #if defined (REDUCE_JOYSTICK_POLLING)
04256     SDL_JoystickEventState(SDL_DISABLE);
04257 #endif
04258     if((mousetoggle && !mouselocked) || (!mousetoggle && mouselocked)) GFX_CaptureMouse();
04259     SDL_ShowCursor(cursor);
04260 #if !defined(C_SDL2)
04261     DOSBox_RefreshMenu();
04262 #endif
04263     if(!menu_gui) GFX_RestoreMode();
04264 #if defined(__WIN32__) && !defined(HX_DOS)
04265     if(GetAsyncKeyState(0x11)) {
04266         INPUT ip;
04267 
04268         // Set up a generic keyboard event.
04269         ip.type = INPUT_KEYBOARD;
04270         ip.ki.wScan = 0; // hardware scan code for key
04271         ip.ki.time = 0;
04272         ip.ki.dwExtraInfo = 0;
04273 
04274         ip.ki.wVk = 0x11;
04275         ip.ki.dwFlags = 0; // 0 for key press
04276         SendInput(1, &ip, sizeof(INPUT));
04277 
04278         // Release the "ctrl" key
04279         ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
04280         SendInput(1, &ip, sizeof(INPUT));
04281     }
04282 #endif
04283 
04284 #if defined(WIN32) && !defined(HX_DOS)
04285     WindowsTaskbarUpdatePreviewRegion();
04286 #endif
04287 
04288 //  KEYBOARD_ClrBuffer();
04289     GFX_LosingFocus();
04290 
04291     /* and then the menu items need to be updated */
04292     for (auto &ev : events) {
04293         if (ev != NULL) ev->update_menu_shortcut();
04294     }
04295 
04296 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
04297     mainMenu.rebuild();
04298 #endif
04299 
04300     GFX_ForceRedrawScreen();
04301 
04302     mapper.running = false;
04303 
04304 #if defined(MACOSX)
04305     void osx_reload_touchbar(void);
04306     osx_reload_touchbar();
04307 #endif
04308 
04309 #ifdef DOSBOXMENU_EXTERNALLY_MANAGED
04310     DOSBox_SetMenu(mainMenu);
04311 #endif
04312 }
04313 
04314 bool MAPPER_IsRunning(void) {
04315     return mapper.running;
04316 }
04317 
04318 void MAPPER_CheckKeyboardLayout() {
04319 #if defined(WIN32)
04320     WORD cur_kb_layout = LOWORD(GetKeyboardLayout(0));
04321 
04322     isJPkeyboard = false;
04323 
04324     if (cur_kb_layout == 1041/*JP106*/) {
04325         isJPkeyboard = true;
04326     }
04327 #endif
04328 }
04329 
04330 bool mapper_menu_exit(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
04331     (void)menu;//UNUSED
04332     (void)menuitem;//UNUSED
04333     mapper.exit=true;
04334     return true;
04335 }
04336 
04337 bool mapper_menu_save(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
04338     (void)menu;//UNUSED
04339     (void)menuitem;//UNUSED
04340     MAPPER_SaveBinds();
04341     return true;
04342 }
04343 
04344 std::vector<std::string> MAPPER_GetEventNames(const std::string &prefix) {
04345         std::vector<std::string> key_names;
04346         key_names.reserve(events.size());
04347         for (auto & e : events) {
04348                 const std::string name = e->GetName();
04349                 const std::size_t found = name.find(prefix);
04350                 if (found != std::string::npos) {
04351                         const std::string key_name = name.substr(found + prefix.length());
04352                         key_names.push_back(key_name);
04353                 }
04354         }
04355         return key_names;
04356 }
04357 
04358 void MAPPER_AutoType(std::vector<std::string> &sequence,
04359                      const uint32_t wait_ms,
04360                      const uint32_t pace_ms) {
04361 #if !defined(HX_DOS)
04362         mapper.typist.Start(&events, sequence, wait_ms, pace_ms);
04363 #endif
04364 }
04365 
04366 void MAPPER_Init(void) {
04367     LOG(LOG_MISC,LOG_DEBUG)("Initializing DOSBox-X mapper");
04368 
04369     mapper.exit=true;
04370 
04371     MAPPER_CheckKeyboardLayout();
04372     InitializeJoysticks();
04373     if (buttons.empty()) CreateLayout();
04374     if (bindgroups.empty()) CreateBindGroups();
04375     if (!MAPPER_LoadBinds()) CreateDefaultBinds();
04376     for (CButton_it but_it = buttons.begin(); but_it != buttons.end(); ++but_it) {
04377         (*but_it)->BindColor();
04378     }
04379     if (SDL_GetModState()&KMOD_CAPS) {
04380         for (CBindList_it bit=caps_lock_event->bindlist.begin();bit!=caps_lock_event->bindlist.end();++bit) {
04381 #if SDL_VERSION_ATLEAST(1, 2, 14)
04382             (*bit)->ActivateBind(32767,true,false);
04383             (*bit)->DeActivateBind(false);
04384 #else
04385             (*bit)->ActivateBind(32767,true,true); //Skip the action itself as bios_keyboard.cpp handles the startup state.
04386 #endif
04387         }
04388     }
04389     if (SDL_GetModState()&KMOD_NUM) {
04390         for (CBindList_it bit=num_lock_event->bindlist.begin();bit!=num_lock_event->bindlist.end();++bit) {
04391 #if SDL_VERSION_ATLEAST(1, 2, 14)
04392             (*bit)->ActivateBind(32767,true,false);
04393             (*bit)->DeActivateBind(false);
04394 #else
04395             (*bit)->ActivateBind(32767,true,true);
04396 #endif
04397         }
04398     }
04399 
04400     /* and then the menu items need to be updated */
04401     for (auto &ev : events) {
04402         if (ev != NULL) ev->update_menu_shortcut();
04403     }
04404 }
04405 
04406 void ReloadMapper(Section_prop *section, bool init) {
04407     Prop_path* pp;
04408 #if defined(C_SDL2)
04409         pp = section->Get_path("mapperfile_sdl2");
04410     mapper.filename = pp->realpath;
04411         if (mapper.filename=="") pp = section->Get_path("mapperfile");
04412 #else
04413     pp = section->Get_path("mapperfile");
04414 #endif
04415     mapper.filename = pp->realpath;
04416         if (init) {
04417                 GFX_LosingFocus(); //Release any keys pressed, or else they'll get stuck.
04418                 MAPPER_Init();
04419         }
04420 }
04421 
04422 //Somehow including them at the top conflicts with something in setup.h
04423 #ifdef C_X11_XKB
04424 #include "SDL_syswm.h"
04425 #include <X11/XKBlib.h>
04426 #endif
04427 void MAPPER_StartUp() {
04428     Section_prop * section=static_cast<Section_prop *>(control->GetSection("sdl"));
04429     mapper.sticks.num=0;
04430     mapper.sticks.num_groups=0;
04431 
04432 #ifdef DOSBOXMENU_EXTERNALLY_MANAGED
04433     {
04434         mapperMenu.alloc_item(DOSBoxMenu::separator_type_id,"_separator_");
04435     }
04436 
04437     {
04438         DOSBoxMenu::item &item = mapperMenu.alloc_item(DOSBoxMenu::submenu_type_id,"MapperMenu");
04439         item.set_text("Mapper");
04440     }
04441 
04442     {
04443         DOSBoxMenu::item &item = mapperMenu.alloc_item(DOSBoxMenu::item_type_id,"ExitMapper");
04444         item.set_callback_function(mapper_menu_exit);
04445         item.set_text("Exit mapper");
04446     }
04447 
04448     {
04449         DOSBoxMenu::item &item = mapperMenu.alloc_item(DOSBoxMenu::item_type_id,"SaveMapper");
04450         item.set_callback_function(mapper_menu_save);
04451         item.set_text("Save mapper file");
04452     }
04453 
04454     mapperMenu.displaylist_clear(mapperMenu.display_list);
04455 
04456     mapperMenu.displaylist_append(
04457         mapperMenu.display_list,
04458         mapperMenu.get_item_id_by_name("MapperMenu"));
04459 
04460     {
04461         mapperMenu.displaylist_append(
04462             mapperMenu.get_item("MapperMenu").display_list, mapperMenu.get_item_id_by_name("ExitMapper"));
04463 
04464         mapperMenu.displaylist_append(
04465             mapperMenu.get_item("MapperMenu").display_list, mapperMenu.get_item_id_by_name("_separator_"));
04466 
04467         mapperMenu.displaylist_append(
04468             mapperMenu.get_item("MapperMenu").display_list, mapperMenu.get_item_id_by_name("SaveMapper"));
04469     }
04470 #endif
04471 
04472     LOG(LOG_MISC,LOG_DEBUG)("MAPPER starting up");
04473 
04474     memset(&virtual_joysticks,0,sizeof(virtual_joysticks));
04475 
04476 #if !defined(C_SDL2)
04477     usescancodes = false;
04478 
04479     if (section->Get_bool("usescancodes")) {
04480         usescancodes=true;
04481 
04482         /* Note: table has to be tested/updated for various OSs */
04483 #if defined (MACOSX)
04484         /* nothing */
04485 #elif defined(HAIKU) || defined(RISCOS)
04486         usescancodes = false;
04487 #elif defined(OS2)
04488         sdlkey_map[0x61]=SDLK_UP;
04489         sdlkey_map[0x66]=SDLK_DOWN;
04490         sdlkey_map[0x63]=SDLK_LEFT;
04491         sdlkey_map[0x64]=SDLK_RIGHT;
04492         sdlkey_map[0x60]=SDLK_HOME;
04493         sdlkey_map[0x65]=SDLK_END;
04494         sdlkey_map[0x62]=SDLK_PAGEUP;
04495         sdlkey_map[0x67]=SDLK_PAGEDOWN;
04496         sdlkey_map[0x68]=SDLK_INSERT;
04497         sdlkey_map[0x69]=SDLK_DELETE;
04498         sdlkey_map[0x5C]=SDLK_KP_DIVIDE;
04499         sdlkey_map[0x5A]=SDLK_KP_ENTER;
04500         sdlkey_map[0x5B]=SDLK_RCTRL;
04501         sdlkey_map[0x5F]=SDLK_PAUSE;
04502 //      sdlkey_map[0x00]=SDLK_PRINT;
04503         sdlkey_map[0x5E]=SDLK_RALT;
04504         sdlkey_map[0x40]=SDLK_KP5;
04505         sdlkey_map[0x41]=SDLK_KP6;
04506 #elif !defined (WIN32) /* => Linux & BSDs */
04507         bool evdev_input = false;
04508 #ifdef SDL_VIDEO_DRIVER_X11
04509 //SDL needs to be compiled to use it, else the next makes no sense.
04510 #ifdef C_X11_XKB
04511         SDL_SysWMinfo info;
04512         SDL_VERSION(&info.version);
04513         if (SDL_GetWMInfo(&info)) {
04514             XkbDescPtr desc = NULL;
04515             if((desc = XkbGetMap(info.info.x11.display,XkbAllComponentsMask,XkbUseCoreKbd))) {
04516                 if(XkbGetNames(info.info.x11.display,XkbAllNamesMask,desc) == 0) {
04517                     const char* keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes);
04518 //                  const char* geom = XGetAtomName(info.info.x11.display, desc->names->geometry);
04519                     if(keycodes) {
04520                         LOG(LOG_MISC,LOG_NORMAL)("keyboard type %s",keycodes);
04521                         if (strncmp(keycodes,"evdev",5) == 0) evdev_input = true;
04522                     }
04523                     XkbFreeNames(desc,XkbAllNamesMask,True);
04524                 }
04525             XkbFreeClientMap(desc,0,True);
04526             }
04527         }
04528 #endif
04529 #endif
04530         if (evdev_input) {
04531             sdlkey_map[0x67]=SDLK_UP;
04532             sdlkey_map[0x6c]=SDLK_DOWN;
04533             sdlkey_map[0x69]=SDLK_LEFT;
04534             sdlkey_map[0x6a]=SDLK_RIGHT;
04535             sdlkey_map[0x66]=SDLK_HOME;
04536             sdlkey_map[0x6b]=SDLK_END;
04537             sdlkey_map[0x68]=SDLK_PAGEUP;
04538             sdlkey_map[0x6d]=SDLK_PAGEDOWN;
04539             sdlkey_map[0x6e]=SDLK_INSERT;
04540             sdlkey_map[0x6f]=SDLK_DELETE;
04541             sdlkey_map[0x62]=SDLK_KP_DIVIDE;
04542             sdlkey_map[0x60]=SDLK_KP_ENTER;
04543             sdlkey_map[0x61]=SDLK_RCTRL;
04544             sdlkey_map[0x77]=SDLK_PAUSE;
04545             sdlkey_map[0x63]=SDLK_PRINT;
04546             sdlkey_map[0x64]=SDLK_RALT;
04547 
04548             //Win-keys
04549             sdlkey_map[0x7d]=SDLK_LSUPER;
04550             sdlkey_map[0x7e]=SDLK_RSUPER;
04551             sdlkey_map[0x7f]=SDLK_MENU;
04552         } else {
04553             sdlkey_map[0x5a]=SDLK_UP;
04554             sdlkey_map[0x60]=SDLK_DOWN;
04555             sdlkey_map[0x5c]=SDLK_LEFT;
04556             sdlkey_map[0x5e]=SDLK_RIGHT;
04557             sdlkey_map[0x59]=SDLK_HOME;
04558             sdlkey_map[0x5f]=SDLK_END;
04559             sdlkey_map[0x5b]=SDLK_PAGEUP;
04560             sdlkey_map[0x61]=SDLK_PAGEDOWN;
04561             sdlkey_map[0x62]=SDLK_INSERT;
04562             sdlkey_map[0x63]=SDLK_DELETE;
04563             sdlkey_map[0x68]=SDLK_KP_DIVIDE;
04564             sdlkey_map[0x64]=SDLK_KP_ENTER;
04565             sdlkey_map[0x65]=SDLK_RCTRL;
04566             sdlkey_map[0x66]=SDLK_PAUSE;
04567             sdlkey_map[0x67]=SDLK_PRINT;
04568             sdlkey_map[0x69]=SDLK_RALT;
04569         }
04570 #else
04571         sdlkey_map[0xc8]=SDLK_UP;
04572         sdlkey_map[0xd0]=SDLK_DOWN;
04573         sdlkey_map[0xcb]=SDLK_LEFT;
04574         sdlkey_map[0xcd]=SDLK_RIGHT;
04575         sdlkey_map[0xc7]=SDLK_HOME;
04576         sdlkey_map[0xcf]=SDLK_END;
04577         sdlkey_map[0xc9]=SDLK_PAGEUP;
04578         sdlkey_map[0xd1]=SDLK_PAGEDOWN;
04579         sdlkey_map[0xd2]=SDLK_INSERT;
04580         sdlkey_map[0xd3]=SDLK_DELETE;
04581         sdlkey_map[0xb5]=SDLK_KP_DIVIDE;
04582         sdlkey_map[0x9c]=SDLK_KP_ENTER;
04583         sdlkey_map[0x9d]=SDLK_RCTRL;
04584         sdlkey_map[0xc5]=SDLK_PAUSE;
04585         sdlkey_map[0xb7]=SDLK_PRINT;
04586         sdlkey_map[0xb8]=SDLK_RALT;
04587 
04588         //Win-keys
04589         sdlkey_map[0xdb]=SDLK_LMETA;
04590         sdlkey_map[0xdc]=SDLK_RMETA;
04591         sdlkey_map[0xdd]=SDLK_MENU;
04592 
04593 #endif
04594 
04595         Bitu i;
04596         for (i=0; i<MAX_SDLKEYS; i++) scancode_map[i]=0;
04597         for (i=0; i<MAX_SCANCODES; i++) {
04598             SDLKey key=sdlkey_map[i];
04599             if (key<MAX_SDLKEYS) scancode_map[key]=(Bit8u)i;
04600         }
04601     }
04602 #endif
04603         ReloadMapper(section, false);
04604 
04605     {
04606         DOSBoxMenu::item *itemp = NULL;
04607 
04608         MAPPER_AddHandler(&MAPPER_Run,MK_m,MMODHOST,"mapper","Mapper",&itemp);
04609         itemp->set_accelerator(DOSBoxMenu::accelerator('m'));
04610         itemp->set_description("Bring up the mapper UI");
04611         itemp->set_text("Mapper editor");
04612     }
04613 }
04614 
04615 void MAPPER_Shutdown() {
04616     for (size_t i=0;i < events.size();i++) {
04617         if (events[i] != NULL) {
04618             delete events[i];
04619             events[i] = NULL;
04620         }
04621     }
04622     name_to_events.clear();
04623     events.clear();
04624 
04625     for (size_t i=0;i < buttons.size();i++) {
04626         if (buttons[i] != NULL) {
04627             delete buttons[i];
04628             buttons[i] = NULL;
04629         }
04630     }
04631     buttons.clear();
04632 
04633     for (size_t i=0;i < bindgroups.size();i++) {
04634         if (bindgroups[i] != NULL) {
04635             delete bindgroups[i];
04636             bindgroups[i] = NULL;
04637         }
04638     }
04639     bindgroups.clear();
04640 
04641     for (size_t i=0;i < handlergroup.size();i++) {
04642         if (handlergroup[i] != NULL) {
04643 #if 0 /* FIXME: Is this list simply another copy of other pointers? Allowing this delete[] to commence triggers double-free warnings */
04644             delete handlergroup[i];
04645 #endif
04646             handlergroup[i] = NULL;
04647         }
04648     }
04649     handlergroup.clear();
04650 }
04651 
04652 void ext_signal_host_key(bool enable) {
04653     CEvent *x = get_mapper_event_by_name("host");
04654     if (x != NULL) {
04655         if (enable) {
04656             x->SetValue(32767);//HACK
04657             x->ActivateEvent(true,false);
04658         }
04659         else {
04660             x->SetValue(0);
04661             x->DeActivateEvent(true);
04662         }
04663     }
04664     else {
04665         fprintf(stderr,"WARNING: No host mapper event\n");
04666     }
04667 }
04668 
04669 void MapperCapCursorToggle(void) {
04670     MAPPER_TriggerEventByName("hand_capmouse");
04671 }
04672 
04673 void RedrawMapperBindButton(CEvent *ev) {
04674     if (ev != NULL) ev->RebindRedraw();
04675 }
04676 
04677 std::string mapper_event_keybind_string(const std::string &x) {
04678     CEvent *ev = get_mapper_event_by_name(x);
04679     if (ev != NULL) return ev->GetBindMenuText();
04680     return std::string();
04681 }
04682