mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-05-07 18:36:09 +08:00
5983 lines
202 KiB
C++
5983 lines
202 KiB
C++
/*
|
|
* Copyright (C) 2002-2021 The DOSBox Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
#include <vector>
|
|
#include <list>
|
|
#include <chrono>
|
|
#include <thread>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
|
|
// for std::max
|
|
#include <algorithm>
|
|
#if defined(WIN32)
|
|
#define NOMINMAX
|
|
#endif
|
|
|
|
#include "SDL.h"
|
|
|
|
#include "dosbox.h"
|
|
#include "logging.h"
|
|
#include "menudef.h"
|
|
#include "video.h"
|
|
#include "keyboard.h"
|
|
#include "mouse.h"
|
|
#include "pic.h"
|
|
#include "control.h"
|
|
#include "joystick.h"
|
|
#include "util_math.h"
|
|
#include "keymap.h"
|
|
#include "support.h"
|
|
#include "mapper.h"
|
|
#include "render.h"
|
|
#include "setup.h"
|
|
#include "menu.h"
|
|
|
|
#include "SDL_syswm.h"
|
|
#include "sdlmain.h"
|
|
#include "shell.h"
|
|
#include "jfont.h"
|
|
|
|
#if C_EMSCRIPTEN
|
|
# include <emscripten.h>
|
|
#endif
|
|
|
|
#include <map>
|
|
#include <cctype>
|
|
|
|
#include <output/output_ttf.h>
|
|
|
|
#if defined(MACOSX)
|
|
#include <Carbon/Carbon.h>
|
|
#endif
|
|
|
|
#define BMOD_Mod1 0x0001
|
|
#define BMOD_Mod2 0x0002
|
|
#define BMOD_Mod3 0x0004
|
|
#define BMOD_Host 0x0008
|
|
|
|
#define BFLG_Hold 0x0001
|
|
#define BFLG_Hold_Temporary 0x0002 /* Emendelson alternate ctrl+alt host key combinations. Keep it SEPARATE so it does not disturb user changes to the mapper */
|
|
#define BFLG_Repeat 0x0004
|
|
|
|
|
|
#define MAXSTICKS 8
|
|
#define MAXACTIVE 16
|
|
#define MAXBUTTON 96
|
|
#define MAXBUTTON_CAP 16
|
|
#define MAXAXIS 8
|
|
#define MAXHAT 2
|
|
|
|
|
|
#define MAX_VJOY_BUTTONS 8
|
|
#define MAX_VJOY_HATS 16
|
|
#define MAX_VJOY_AXES 8
|
|
|
|
|
|
class CEvent;
|
|
class CKeyEvent;
|
|
class CHandlerEvent;
|
|
class CButton;
|
|
class CBind;
|
|
class CBindGroup;
|
|
class CJAxisBind;
|
|
class CJButtonBind;
|
|
class CJHatBind;
|
|
class CKeyBind;
|
|
class CKeyBindGroup;
|
|
class CStickBindGroup;
|
|
class CCaptionButton;
|
|
class CCheckButton;
|
|
class CBindButton;
|
|
class CModEvent;
|
|
|
|
enum {
|
|
CLR_BLACK = 0,
|
|
CLR_GREY = 1,
|
|
CLR_WHITE = 2,
|
|
CLR_RED = 3,
|
|
CLR_BLUE = 4,
|
|
CLR_GREEN = 5,
|
|
CLR_DARKGREEN = 6
|
|
};
|
|
|
|
enum BB_Types {
|
|
BB_Next,
|
|
BB_Add,
|
|
BB_Del,
|
|
BB_Save,
|
|
BB_Exit,
|
|
BB_Capture,
|
|
BB_Prevpage,
|
|
BB_Nextpage
|
|
};
|
|
|
|
enum BC_Types {
|
|
BC_Mod1,
|
|
BC_Mod2,
|
|
BC_Mod3,
|
|
BC_Host,
|
|
BC_Hold
|
|
};
|
|
|
|
typedef std::list<CBind *> CBindList;
|
|
typedef std::list<CEvent *>::iterator CEventList_it;
|
|
typedef std::list<CBind *>::iterator CBindList_it;
|
|
typedef std::vector<CButton *>::iterator CButton_it;
|
|
typedef std::vector<CEvent *>::iterator CEventVector_it;
|
|
typedef std::vector<CHandlerEvent *>::iterator CHandlerEventVector_it;
|
|
typedef std::vector<CBindGroup *>::iterator CBindGroup_it;
|
|
|
|
static struct {
|
|
bool button_pressed[MAX_VJOY_BUTTONS];
|
|
int16_t axis_pos[MAX_VJOY_AXES];
|
|
bool hat_pressed[MAX_VJOY_HATS];
|
|
} virtual_joysticks[2];
|
|
|
|
static struct {
|
|
CCaptionButton* event_title;
|
|
CCaptionButton* bind_title;
|
|
CCaptionButton* selected;
|
|
CCaptionButton* action;
|
|
CCaptionButton* dbg2;
|
|
CCaptionButton* dbg1;
|
|
CBindButton* save;
|
|
CBindButton* exit;
|
|
CBindButton* cap;
|
|
CBindButton* add;
|
|
CBindButton* del;
|
|
CBindButton* next;
|
|
CBindButton* prevpage;
|
|
CBindButton* nextpage;
|
|
CCaptionButton* pagestat;
|
|
CCheckButton *mod1, *mod2, *mod3, *host, *hold;
|
|
} bind_but;
|
|
|
|
struct KeyBlock {
|
|
const char* title;
|
|
const char* entry;
|
|
KBD_KEYS key;
|
|
};
|
|
|
|
#ifdef DOSBOXMENU_EXTERNALLY_MANAGED
|
|
static DOSBoxMenu mapperMenu;
|
|
#endif
|
|
|
|
extern unsigned int hostkeyalt, maincp;
|
|
extern uint8_t int10_font_14[256 * 14];
|
|
|
|
std::map<std::string,std::string> pending_string_binds;
|
|
|
|
static int mapper_esc_count = 0;
|
|
|
|
Bitu next_handler_xpos = 0;
|
|
Bitu next_handler_ypos = 0;
|
|
|
|
bool mapper_addhandler_create_buttons = false;
|
|
|
|
bool isJPkeyboard = false;
|
|
bool dotype = false;
|
|
|
|
//! \brief joystick autofire config option
|
|
bool autofire = false;
|
|
|
|
//! \brief log scancodes for debugging config option
|
|
bool log_keyboard_scan_codes = false;
|
|
|
|
//! \brief map of joystick 1 axes
|
|
int joy1axes[8];
|
|
|
|
//! \brief map of joystick 2 axes
|
|
int joy2axes[8];
|
|
|
|
static std::vector<CEvent *> events;
|
|
static std::vector<CButton *> buttons;
|
|
static std::vector<CBindGroup *> bindgroups;
|
|
static std::vector<CHandlerEvent *> handlergroup;
|
|
|
|
static CModEvent* mod_event[8] = {NULL};
|
|
|
|
static CKeyEvent* caps_lock_event = NULL;
|
|
static CKeyEvent* num_lock_event = NULL;
|
|
|
|
static std::map<std::string, size_t> name_to_events;
|
|
static std::map<std::string, std::string> event_map;
|
|
|
|
static SDL_Color map_pal[7] =
|
|
{
|
|
{0x00,0x00,0x00,0x00}, //0=black
|
|
{0x7f,0x7f,0x7f,0x00}, //1=grey
|
|
{0xff,0xff,0xff,0x00}, //2=white
|
|
{0xff,0x00,0x00,0x00}, //3=red
|
|
{0x10,0x30,0xff,0x00}, //4=blue
|
|
{0x00,0xff,0x20,0x00}, //5=green
|
|
{0x00,0x7f,0x10,0x00} //6=dark green
|
|
};
|
|
|
|
static KeyBlock combo_f[12] =
|
|
{
|
|
{"F1","f1",KBD_f1}, {"F2","f2",KBD_f2}, {"F3","f3",KBD_f3},
|
|
{"F4","f4",KBD_f4}, {"F5","f5",KBD_f5}, {"F6","f6",KBD_f6},
|
|
{"F7","f7",KBD_f7}, {"F8","f8",KBD_f8}, {"F9","f9",KBD_f9},
|
|
{"F10","f10",KBD_f10}, {"F11","f11",KBD_f11}, {"F12","f12",KBD_f12},
|
|
};
|
|
|
|
static KeyBlock combo_1[14] =
|
|
{
|
|
{"`~","grave",KBD_grave}, {"1!","1",KBD_1}, {"2@","2",KBD_2},
|
|
{"3#","3",KBD_3}, {"4$","4",KBD_4}, {"5%","5",KBD_5},
|
|
{"6^","6",KBD_6}, {"7&","7",KBD_7}, {"8*","8",KBD_8},
|
|
{"9(","9",KBD_9}, {"0)","0",KBD_0}, {"-_","minus",KBD_minus},
|
|
{"=+","equals",KBD_equals}, {"\x1B","bspace",KBD_backspace},
|
|
};
|
|
|
|
static KeyBlock combo_1_pc98[14] =
|
|
{
|
|
{"`~","grave",KBD_grave}, {"1!","1",KBD_1}, {"2\"","2",KBD_2},
|
|
{"3#","3",KBD_3}, {"4$","4",KBD_4}, {"5%","5",KBD_5},
|
|
{"6&","6",KBD_6}, {"7'","7",KBD_7}, {"8(","8",KBD_8},
|
|
{"9)","9",KBD_9}, {"0","0",KBD_0}, {"-=","minus",KBD_minus},
|
|
{"=+","equals",KBD_equals}, {"\x1B","bspace",KBD_backspace},
|
|
};
|
|
|
|
static KeyBlock combo_2[12] =
|
|
{
|
|
{"Q","q",KBD_q}, {"W","w",KBD_w}, {"E","e",KBD_e},
|
|
{"R","r",KBD_r}, {"T","t",KBD_t}, {"Y","y",KBD_y},
|
|
{"U","u",KBD_u}, {"I","i",KBD_i}, {"O","o",KBD_o},
|
|
{"P","p",KBD_p}, {"[{","lbracket",KBD_leftbracket},
|
|
{"]}","rbracket",KBD_rightbracket},
|
|
};
|
|
|
|
static KeyBlock combo_3[12] =
|
|
{
|
|
{"A","a",KBD_a}, {"S","s",KBD_s}, {"D","d",KBD_d},
|
|
{"F","f",KBD_f}, {"G","g",KBD_g}, {"H","h",KBD_h},
|
|
{"J","j",KBD_j}, {"K","k",KBD_k}, {"L","l",KBD_l},
|
|
{";:","semicolon",KBD_semicolon}, {"'\"","quote",KBD_quote},
|
|
{"\\|","backslash",KBD_backslash},
|
|
};
|
|
|
|
static KeyBlock combo_3_pc98[12] =
|
|
{
|
|
{"A","a",KBD_a}, {"S","s",KBD_s}, {"D","d",KBD_d},
|
|
{"F","f",KBD_f}, {"G","g",KBD_g}, {"H","h",KBD_h},
|
|
{"J","j",KBD_j}, {"K","k",KBD_k}, {"L","l",KBD_l},
|
|
{";:+","semicolon",KBD_semicolon}, {"'\"","quote",KBD_quote},
|
|
{"\\|","backslash",KBD_backslash},
|
|
};
|
|
|
|
static KeyBlock combo_4[11] =
|
|
{
|
|
{"<>","lessthan",KBD_extra_lt_gt},
|
|
{"Z","z",KBD_z}, {"X","x",KBD_x}, {"C","c",KBD_c},
|
|
{"V","v",KBD_v}, {"B","b",KBD_b}, {"N","n",KBD_n},
|
|
{"M","m",KBD_m}, {",<","comma",KBD_comma},
|
|
{".>","period",KBD_period}, {"/?","slash",KBD_slash},
|
|
};
|
|
|
|
static const char* LabelMod1 = "Mod1";
|
|
static const char* LabelMod2 = "Mod2";
|
|
static const char* LabelMod3 = "Mod3";
|
|
static const char* LabelHost = "Host";
|
|
static const char* LabelHold = "Hold";
|
|
|
|
static bool initjoy=true;
|
|
static int cpage=1, maxpage=1;
|
|
|
|
static void SetActiveEvent(CEvent * event);
|
|
static void SetActiveBind(CBind * _bind);
|
|
static void change_action_text(const char* text,uint8_t col);
|
|
static void DrawText(Bitu x,Bitu y,const char * text,uint8_t color,uint8_t bkcolor=CLR_BLACK);
|
|
static void MAPPER_SaveBinds(void);
|
|
|
|
CEvent* get_mapper_event_by_name(const std::string &x);
|
|
bool MAPPER_DemoOnly(void);
|
|
|
|
#if defined(USE_TTF)
|
|
void resetFontSize(void);
|
|
#endif
|
|
|
|
Bitu GUI_JoystickCount(void); // external
|
|
bool OpenGL_using(void);
|
|
bool GFX_GetPreventFullscreen(void); // external
|
|
void GFX_ForceRedrawScreen(void); // external
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
void DOSBox_SetSysMenu(void);
|
|
void WindowsTaskbarUpdatePreviewRegion(void);// external
|
|
void WindowsTaskbarResetPreviewRegion(void); // external
|
|
#endif
|
|
|
|
#if defined(MACOSX)
|
|
void macosx_reload_touchbar(void);
|
|
#endif
|
|
|
|
bool systemmessagebox(char const * aTitle, char const * aMessage, char const * aDialogType, char const * aIconType, int aDefaultButton);
|
|
|
|
//! \brief Base CEvent class for mapper events
|
|
class CEvent {
|
|
public:
|
|
//! \brief Type of CEvent class, if the code needs to use the specific type
|
|
//!
|
|
//! \description This is used by other parts of the mapper if it needs to retrieve
|
|
//! additional information that is only provided by the handler event class
|
|
enum event_type {
|
|
event_t=0,
|
|
handler_event_t,
|
|
mod_event_t
|
|
};
|
|
public:
|
|
//! \brief CEvent constructor
|
|
//!
|
|
//! \description This constructor takes a mapper entry name and event type.
|
|
//! Subclasses will call down to this constructor as well.
|
|
//! The handler event class will fill in the _type field to
|
|
//! identify itself.
|
|
CEvent(char const * const _entry,const enum event_type _type = event_t) {
|
|
safe_strncpy(entry,_entry,sizeof(entry));
|
|
|
|
{
|
|
if (name_to_events.find(entry) != name_to_events.end())
|
|
E_Exit("Mapper: Event \"%s\" already defined",entry);
|
|
}
|
|
|
|
name_to_events[entry] = events.size();
|
|
|
|
events.push_back(this);
|
|
bindlist.clear();
|
|
active=false;
|
|
activity=0;
|
|
current_value=0;
|
|
type = _type;
|
|
|
|
assert(get_mapper_event_by_name(entry) == this);
|
|
}
|
|
|
|
//! \brief Retrieve binding string for display in the menu
|
|
//!
|
|
//! \description Retrieve text string to show as the assigned mapper binding in a
|
|
//! menu item's displayable area so that the user knows what keyboard
|
|
//! input will trigger the shortcut.
|
|
virtual std::string GetBindMenuText(void);
|
|
|
|
//! \brief Update the menu item for the mapper shortcut with the latest text and keyboard shortcut
|
|
void update_menu_shortcut(void) {
|
|
if (!eventname.empty()) {
|
|
DOSBoxMenu::item& item = mainMenu.get_item(std::string("mapper_") + std::string(eventname));
|
|
std::string str = GetBindMenuText();
|
|
item.set_shortcut_text(str);
|
|
item.refresh_item(mainMenu);
|
|
// LOG_MSG("%s",str.c_str());
|
|
}
|
|
}
|
|
|
|
//! \brief Add binding to the bindlist
|
|
void AddBind(CBind * bind);
|
|
void ClearBinds();
|
|
|
|
virtual ~CEvent();
|
|
|
|
//! \brief Change whether the event is activated or not
|
|
virtual void Active(bool yesno) {
|
|
active = yesno;
|
|
}
|
|
|
|
//! \brief Activate the event, act on it
|
|
virtual void ActivateEvent(bool ev_trigger,bool skip_action)=0;
|
|
|
|
//! \brief Deactivate the event
|
|
virtual void DeActivateEvent(bool ev_trigger)=0;
|
|
|
|
//! \brief Deactivate all bindings
|
|
void DeActivateAll(void);
|
|
|
|
//! \brief Set the value of the event (such as joystick position)
|
|
void SetValue(Bits value){
|
|
current_value=value;
|
|
}
|
|
|
|
//! \brief Get the value of the event
|
|
Bits GetValue(void) {
|
|
return current_value;
|
|
}
|
|
|
|
//! \brief Retrieve the name of the event
|
|
char * GetName(void) { return entry; }
|
|
|
|
//! \brief Indicate whether the event is a trigger or continuous input
|
|
virtual bool IsTrigger(void)=0;
|
|
|
|
/// TODO
|
|
virtual void RebindRedraw(void) {}
|
|
|
|
//! \brief Event name
|
|
std::string eventname;
|
|
|
|
//! \brief event type
|
|
enum event_type type;
|
|
|
|
//! \brief Bind list to trigger on activation/deactivation
|
|
CBindList bindlist;
|
|
|
|
//! \brief Whether the event is active or not
|
|
bool active;
|
|
protected:
|
|
//! \brief Activity counter
|
|
Bitu activity;
|
|
|
|
//! \brief Mapper entry name
|
|
char entry[20];
|
|
|
|
//! \brief Current value of the event (such as joystick position)
|
|
Bits current_value;
|
|
};
|
|
|
|
//! \brief class for events which can be ON/OFF only: key presses, joystick buttons, joystick hat
|
|
class CTriggeredEvent : public CEvent {
|
|
public:
|
|
//! \brief Constructor, with event name
|
|
CTriggeredEvent(char const * const _entry) : CEvent(_entry) {}
|
|
|
|
// methods below this line have sufficient documentation inherited from the base class
|
|
|
|
~CTriggeredEvent() {}
|
|
|
|
bool IsTrigger(void) override {
|
|
return true;
|
|
}
|
|
|
|
void ActivateEvent(bool ev_trigger,bool skip_action) override {
|
|
if (current_value>25000) {
|
|
/* value exceeds boundary, trigger event if not active */
|
|
if (!activity && !skip_action) Active(true);
|
|
if (activity<32767) activity++;
|
|
} else {
|
|
if (activity>0) {
|
|
/* untrigger event if it is fully inactive */
|
|
DeActivateEvent(ev_trigger);
|
|
activity=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeActivateEvent(bool /*ev_trigger*/) override {
|
|
if (activity > 0) activity--;
|
|
if (!activity) Active(false);
|
|
}
|
|
};
|
|
|
|
//! \brief class for events which have a non-boolean state: joystick axis movement
|
|
class CContinuousEvent : public CEvent {
|
|
public:
|
|
//! \brief Constructor, with event name
|
|
CContinuousEvent(char const * const _entry) : CEvent(_entry) {}
|
|
|
|
// methods below this line have sufficient documentation inherited from the base class
|
|
|
|
~CContinuousEvent() {}
|
|
|
|
bool IsTrigger(void) override {
|
|
return false;
|
|
}
|
|
|
|
void ActivateEvent(bool ev_trigger,bool skip_action) override {
|
|
if (ev_trigger) {
|
|
activity++;
|
|
if (!skip_action) Active(true);
|
|
} else {
|
|
/* test if no trigger-activity is present, this cares especially
|
|
about activity of the opposite-direction joystick axis for example */
|
|
if (!GetActivityCount()) Active(true);
|
|
}
|
|
}
|
|
|
|
void DeActivateEvent(bool ev_trigger) override {
|
|
if (ev_trigger) {
|
|
if (activity>0) activity--;
|
|
if (activity==0) {
|
|
/* test if still some trigger-activity is present,
|
|
adjust the state in this case accordingly */
|
|
if (GetActivityCount()) RepostActivity();
|
|
else Active(false);
|
|
}
|
|
} else {
|
|
if (!GetActivityCount()) Active(false);
|
|
}
|
|
}
|
|
|
|
//! \brief Retrieve activity counter
|
|
virtual Bitu GetActivityCount(void) {
|
|
return activity;
|
|
}
|
|
|
|
//! \brief Re-post activity
|
|
virtual void RepostActivity(void) {}
|
|
};
|
|
|
|
//! \brief Base C++ class for a binding assigned in the mapper interface (or by default settings)
|
|
class CBind {
|
|
public:
|
|
//! \brief Bind class type, for runtime detection
|
|
enum CBindType {
|
|
bind_t=0,
|
|
keybind_t
|
|
};
|
|
public:
|
|
virtual ~CBind () {
|
|
list->remove(this);
|
|
}
|
|
|
|
//! \brief Constructor, to define the binding and type. This constructor adds the CBind object itself to the list
|
|
CBind(CBindList * _list,enum CBindType _type = bind_t) {
|
|
list=_list;
|
|
_list->push_back(this);
|
|
mods=flags=0;
|
|
event = nullptr;
|
|
active=holding=false;
|
|
type = _type;
|
|
value = 0;
|
|
}
|
|
|
|
//! \brief Get modifier text
|
|
virtual std::string GetModifierText(void);
|
|
|
|
//! \brief Get binding text, for display in the menu item
|
|
virtual std::string GetBindMenuText(void) {
|
|
return GetModifierText();
|
|
}
|
|
|
|
//! \brief Append modifier text to a string, for use in recording bindings to the mapper file
|
|
void AddFlags(char * buf) {
|
|
if (mods & BMOD_Mod1) strcat(buf," mod1");
|
|
if (mods & BMOD_Mod2) strcat(buf," mod2");
|
|
if (mods & BMOD_Mod3) strcat(buf," mod3");
|
|
if (mods & BMOD_Host) strcat(buf," host");
|
|
if (flags & BFLG_Hold) strcat(buf," hold");
|
|
}
|
|
|
|
//! \brief Read modifier flags from a string, for use in parsing bindings from the mapper file
|
|
void SetFlags(char * buf) {
|
|
char * word;
|
|
while (*(word=StripWord(buf))) {
|
|
if (!strcasecmp(word,"mod1")) mods|=BMOD_Mod1;
|
|
if (!strcasecmp(word,"mod2")) mods|=BMOD_Mod2;
|
|
if (!strcasecmp(word,"mod3")) mods|=BMOD_Mod3;
|
|
if (!strcasecmp(word,"host")) mods|=BMOD_Host;
|
|
if (!strcasecmp(word,"hold")) flags|=BFLG_Hold;
|
|
}
|
|
}
|
|
|
|
//! \brief Activate bindings
|
|
virtual void ActivateBind(Bits _value,bool ev_trigger,bool skip_action=false) {
|
|
if (event->IsTrigger()) {
|
|
/* use value-boundary for on/off events */
|
|
if (_value>25000) {
|
|
event->SetValue(_value);
|
|
if (active) return;
|
|
if (!holding) event->ActivateEvent(ev_trigger,skip_action);
|
|
active=true;
|
|
} else {
|
|
if (active) {
|
|
event->DeActivateEvent(ev_trigger);
|
|
active=false;
|
|
}
|
|
}
|
|
} else {
|
|
/* store value for possible later use in the activated event */
|
|
event->SetValue(_value);
|
|
event->ActivateEvent(ev_trigger,false);
|
|
}
|
|
}
|
|
|
|
//! \brief Deactivate bindings
|
|
void DeActivateBind(bool ev_trigger) {
|
|
if (event->IsTrigger()) {
|
|
if (!active) return;
|
|
active=false;
|
|
if (flags & (BFLG_Hold|BFLG_Hold_Temporary)) {
|
|
if (!holding) {
|
|
holding=true;
|
|
return;
|
|
} else {
|
|
holding=false;
|
|
}
|
|
}
|
|
event->DeActivateEvent(ev_trigger);
|
|
} else {
|
|
/* store value for possible later use in the activated event */
|
|
event->SetValue(0);
|
|
event->DeActivateEvent(ev_trigger);
|
|
}
|
|
}
|
|
|
|
//! \brief Get configuration name, for use in writing the mapper file
|
|
virtual void ConfigName(char * buf)=0;
|
|
|
|
//! \brief Get binding name, for display in the mapper UI
|
|
virtual void BindName(char * buf)=0;
|
|
|
|
//! \brief Modifiers (shift, ctrl, alt)
|
|
Bitu mods;
|
|
|
|
//! \brief Flags (hold)
|
|
Bitu flags;
|
|
|
|
//! \brief Binding value (TODO?)
|
|
int16_t value;
|
|
|
|
//! \brief Event object this binding is bound to (for visual UI purposes)
|
|
CEvent * event;
|
|
|
|
//! \brief List that this object is part of
|
|
CBindList * list;
|
|
|
|
//! \brief Active status
|
|
bool active;
|
|
|
|
//! \brief Holding status
|
|
bool holding;
|
|
|
|
//! \brief Binding type
|
|
enum CBindType type;
|
|
};
|
|
|
|
CEvent::~CEvent() {
|
|
CBindList_it it;
|
|
|
|
while ((it=bindlist.begin()) != bindlist.end()) {
|
|
delete (*it);
|
|
bindlist.erase(it);
|
|
}
|
|
}
|
|
|
|
void CEvent::AddBind(CBind * bind) {
|
|
bindlist.push_front(bind);
|
|
bind->event=this;
|
|
}
|
|
void CEvent::ClearBinds() {
|
|
for (CBind *bind : bindlist) {
|
|
delete bind;
|
|
}
|
|
bindlist.clear();
|
|
}
|
|
void CEvent::DeActivateAll(void) {
|
|
for (CBindList_it bit=bindlist.begin();bit!=bindlist.end();++bit) {
|
|
(*bit)->DeActivateBind(true);
|
|
}
|
|
}
|
|
|
|
class CBindGroup {
|
|
public:
|
|
CBindGroup() {
|
|
bindgroups.push_back(this);
|
|
}
|
|
virtual ~CBindGroup (void) { }
|
|
void ActivateBindList(CBindList * list,Bits value,bool ev_trigger);
|
|
void DeactivateBindList(CBindList * list,bool ev_trigger);
|
|
virtual CBind * CreateConfigBind(char *&buf)=0;
|
|
virtual CBind * CreateEventBind(SDL_Event * event)=0;
|
|
|
|
virtual bool CheckEvent(SDL_Event * event)=0;
|
|
virtual const char * ConfigStart(void)=0;
|
|
virtual const char * BindStart(void)=0;
|
|
|
|
protected:
|
|
|
|
};
|
|
|
|
void MAPPER_TriggerEvent(const CEvent *event, const bool deactivation_state) {
|
|
assert(event);
|
|
for (auto &bind : event->bindlist) {
|
|
bind->ActivateBind(32767, true, false);
|
|
bind->DeActivateBind(deactivation_state);
|
|
}
|
|
}
|
|
|
|
#if !defined(HX_DOS) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
|
|
/* TODO: This is fine, but it should not call MAPPER functions from a separate thread.
|
|
* These functions are not necessarily reentrant and can cause screw ups when
|
|
* called from multiple threads.
|
|
*
|
|
* Also the HX-DOS builds cannot use this code because the older MinGW lacks
|
|
* std::thread.
|
|
*
|
|
* Replace thread with PIC_AddEvent() to callback. */
|
|
class Typer {
|
|
public:
|
|
Typer() = default;
|
|
Typer(const Typer&) = delete; // prevent copy
|
|
Typer& operator=(const Typer&) = delete; // prevent assignment
|
|
~Typer() {
|
|
Stop();
|
|
}
|
|
void Start(std::vector<CEvent*> *ext_events,
|
|
std::vector<std::string> &ext_sequence,
|
|
const uint32_t wait_ms,
|
|
const uint32_t pace_ms,
|
|
bool choice) {
|
|
// Guard against empty inputs
|
|
if (!ext_events || ext_sequence.empty())
|
|
return;
|
|
Wait();
|
|
m_events = ext_events;
|
|
m_sequence = std::move(ext_sequence);
|
|
m_wait_ms = wait_ms;
|
|
m_pace_ms = pace_ms;
|
|
m_choice = choice;
|
|
m_stop_requested = false;
|
|
m_instance = std::thread(&Typer::Callback, this);
|
|
}
|
|
void Wait() {
|
|
if (m_instance.joinable())
|
|
m_instance.join();
|
|
}
|
|
void Stop() {
|
|
m_stop_requested = true;
|
|
Wait();
|
|
}
|
|
private:
|
|
CEvent *GetLShiftEvent()
|
|
{
|
|
static CEvent *lshift_event = nullptr;
|
|
for (auto &event : *m_events) {
|
|
if (std::string("key_lshift") == event->GetName()) {
|
|
lshift_event = event;
|
|
break;
|
|
}
|
|
}
|
|
assert(lshift_event);
|
|
return lshift_event;
|
|
}
|
|
void Callback() {
|
|
// quit before our initial wait time
|
|
if (m_stop_requested || (m_choice && !dotype))
|
|
return;
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(m_wait_ms));
|
|
for (const auto &button : m_sequence) {
|
|
bool found = false;
|
|
// comma adds an extra pause, similar to the pause used in a phone number
|
|
if (m_choice && !dotype)
|
|
return;
|
|
if (button == ",") {
|
|
found = true;
|
|
// quit before the pause
|
|
if (m_stop_requested)
|
|
return;
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(m_pace_ms));
|
|
// Otherwise trigger the matching button if we have one
|
|
} else {
|
|
const auto is_cap = button.length() == 1 && isupper(button[0]);
|
|
const auto maybe_lshift = is_cap ? GetLShiftEvent() : nullptr;
|
|
const std::string lbutton = is_cap ? std::string(1, tolower(button[0])) : button;
|
|
const std::string bind_name = "key_" + lbutton;
|
|
for (auto &event : *m_events) {
|
|
if (bind_name == event->GetName()) {
|
|
found = true;
|
|
if (maybe_lshift) maybe_lshift->Active(true);
|
|
event->Active(true);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
event->Active(false);
|
|
if (maybe_lshift) maybe_lshift->Active(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Terminate the sequence for safety reasons if we can't find a button.
|
|
* For example, we don't want DEAL becoming DEL, or 'rem' becoming 'rm'
|
|
*/
|
|
if (!found) {
|
|
LOG_MSG("MAPPER: Couldn't find a button named '%s', stopping.",
|
|
button.c_str());
|
|
return;
|
|
}
|
|
if (m_stop_requested || (m_choice && !dotype)) // quit before the pacing delay
|
|
return;
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(m_pace_ms));
|
|
}
|
|
}
|
|
std::thread m_instance;
|
|
std::vector<std::string> m_sequence;
|
|
std::vector<CEvent*> *m_events = nullptr;
|
|
uint32_t m_wait_ms = 0;
|
|
uint32_t m_pace_ms = 0;
|
|
bool m_choice = false;
|
|
bool m_stop_requested = false;
|
|
};
|
|
#endif
|
|
|
|
static struct CMapper {
|
|
#if defined(C_SDL2)
|
|
SDL_Window* window;
|
|
SDL_Rect draw_rect;
|
|
SDL_Surface* draw_surface_nonpaletted;
|
|
SDL_Surface* draw_surface;
|
|
#endif
|
|
SDL_Surface* surface = NULL;
|
|
bool exit = false;
|
|
CEvent* aevent = NULL; //Active Event
|
|
CBind* abind = NULL; //Active Bind
|
|
CBindList_it abindit; //Location of active bind in list
|
|
bool redraw = false;
|
|
bool addbind = false;
|
|
bool running = false;
|
|
Bitu mods = 0;
|
|
struct {
|
|
Bitu num_groups,num;
|
|
CStickBindGroup* stick[MAXSTICKS];
|
|
} sticks = {};
|
|
#if !defined(HX_DOS) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
|
|
Typer typist;
|
|
#endif
|
|
std::string filename;
|
|
} mapper;
|
|
|
|
#if defined(C_SDL2) /* SDL 2.x */
|
|
|
|
/* HACK */
|
|
typedef SDL_Scancode SDLKey;
|
|
|
|
#else /* !defined(C_SDL2) SDL 1.x */
|
|
|
|
#define MAX_SDLKEYS 323
|
|
|
|
static int usescancodes=-1;
|
|
static uint8_t scancode_map[MAX_SDLKEYS];
|
|
|
|
#define Z SDLK_UNKNOWN
|
|
|
|
#if defined (MACOSX)
|
|
static SDLKey sdlkey_map[]={
|
|
/* Main block printables */
|
|
/*00-05*/ Z, SDLK_s, SDLK_d, SDLK_f, SDLK_h, SDLK_g,
|
|
/*06-0B*/ SDLK_z, SDLK_x, SDLK_c, SDLK_v, SDLK_BACKQUOTE /* 'Section' key */, SDLK_b,
|
|
/*0C-11*/ SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_y, SDLK_t,
|
|
/*12-17*/ SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_6, SDLK_5,
|
|
/*18-1D*/ SDLK_EQUALS, SDLK_9, SDLK_7, SDLK_MINUS, SDLK_8, SDLK_0,
|
|
/*1E-21*/ SDLK_RIGHTBRACKET, SDLK_o, SDLK_u, SDLK_LEFTBRACKET,
|
|
/*22-23*/ SDLK_i, SDLK_p,
|
|
/*24-29*/ SDLK_RETURN, SDLK_l, SDLK_j, SDLK_QUOTE, SDLK_k, SDLK_SEMICOLON,
|
|
/*2A-2F*/ SDLK_BACKSLASH, SDLK_COMMA, SDLK_SLASH, SDLK_n, SDLK_m, SDLK_PERIOD,
|
|
|
|
/* Spaces, controls, modifiers (dosbox uses LMETA only for
|
|
* hotkeys, it's not really mapped to an emulated key) */
|
|
/*30-33*/ SDLK_TAB, SDLK_SPACE, SDLK_BACKQUOTE, SDLK_BACKSPACE,
|
|
/*34-37*/ Z, SDLK_ESCAPE, SDLK_RSUPER, SDLK_LSUPER,
|
|
/*38-3B*/ SDLK_LSHIFT, SDLK_CAPSLOCK, SDLK_LALT, SDLK_LCTRL,
|
|
/*3C-40*/ SDLK_RSHIFT, SDLK_RALT, SDLK_RCTRL, Z, SDLK_F17,
|
|
|
|
/* Keypad (KP_EQUALS not supported, NUMLOCK used on what is CLEAR
|
|
* in Mac OS X) */
|
|
/*41-46*/ SDLK_KP_PERIOD, Z, SDLK_KP_MULTIPLY, Z, SDLK_KP_PLUS, Z,
|
|
/*47-4A*/ SDLK_NUMLOCK /*==SDLK_CLEAR*/, Z, Z, Z,
|
|
/*4B-4D*/ SDLK_KP_DIVIDE, SDLK_KP_ENTER, Z,
|
|
/*4E-51*/ SDLK_KP_MINUS, SDLK_F18, SDLK_F19, SDLK_KP_EQUALS,
|
|
/*52-57*/ SDLK_KP0, SDLK_KP1, SDLK_KP2, SDLK_KP3, SDLK_KP4, SDLK_KP5,
|
|
/*58-5C*/ SDLK_KP6, SDLK_KP7, Z, SDLK_KP8, SDLK_KP9,
|
|
|
|
/*5D-5F*/ SDLK_JP_YEN, SDLK_JP_RO, SDLK_KP_COMMA,
|
|
|
|
/* Function keys and cursor blocks (F13 not supported, F14 =>
|
|
* PRINT[SCREEN], F15 => SCROLLOCK, F16 => PAUSE, HELP => INSERT) */
|
|
/*60-64*/ SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F3, SDLK_F8,
|
|
/*65-6A*/ SDLK_F9, SDLK_WORLD_17, SDLK_F11, SDLK_WORLD_18, SDLK_F13, SDLK_PAUSE /*==SDLK_F16*/,
|
|
/*6B-70*/ SDLK_PRINT /*==SDLK_F14*/, Z, SDLK_F10, Z, SDLK_F12, Z,
|
|
/*71-72*/ SDLK_SCROLLOCK /*==SDLK_F15*/, SDLK_INSERT /*==SDLK_HELP*/,
|
|
/*73-77*/ SDLK_HOME, SDLK_PAGEUP, SDLK_DELETE, SDLK_F4, SDLK_END,
|
|
/*78-7C*/ SDLK_F2, SDLK_PAGEDOWN, SDLK_F1, SDLK_LEFT, SDLK_RIGHT,
|
|
/*7D-7E*/ SDLK_DOWN, SDLK_UP,
|
|
|
|
/*7F-7F*/ Z,
|
|
|
|
/* 4 extra keys that don't really exist, but are needed for
|
|
* round-trip mapping (dosbox uses RMETA only for hotkeys, it's
|
|
* not really mapped to an emulated key) */
|
|
SDLK_RMETA, SDLK_RSHIFT, SDLK_RALT, SDLK_RCTRL
|
|
};
|
|
#define MAX_SCANCODES (0x80+4)
|
|
/* Make sure that the table above has the expected size. This
|
|
expression will raise a compiler error if the condition is false. */
|
|
typedef char assert_right_size [MAX_SCANCODES == (sizeof(sdlkey_map)/sizeof(sdlkey_map[0])) ? 1 : -1];
|
|
|
|
#elif defined (__linux__)
|
|
#define MAX_SCANCODES 0x90
|
|
#include "../../vs/sdl/include/SDL_keysym.h"
|
|
static SDLKey sdlkey_map[MAX_SCANCODES] = { // Convert hardware scancode (XKB = evdev + 8) to SDL virtual keycode
|
|
/* Refer to https://chromium.googlesource.com/chromium/src/+/lkgr/ui/events/keycodes/keyboard_code_conversion_x.cc */
|
|
SDLK_UNKNOWN,//0x00
|
|
Z,Z,Z,Z,Z,Z,Z,Z, //0x01-0x08 unknown
|
|
SDLK_ESCAPE,//0x09
|
|
/*0x0a-0x13 1-9,0*/
|
|
SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6, SDLK_7, SDLK_8, SDLK_9, SDLK_0,
|
|
SDLK_MINUS, //0x14
|
|
SDLK_EQUALS, //0x15
|
|
SDLK_BACKSPACE,//0x16
|
|
SDLK_TAB,//0x17
|
|
SDLK_q,//0x18
|
|
SDLK_w,//0x19
|
|
SDLK_e,//0x1a
|
|
SDLK_r,//0x1b
|
|
SDLK_t,//0x1c
|
|
SDLK_y,//0x1d
|
|
SDLK_u,//0x1e
|
|
SDLK_i,//0x1f
|
|
SDLK_o,//0x20
|
|
SDLK_p,//0x21
|
|
SDLK_LEFTBRACKET,//0x22 [ and {
|
|
SDLK_RIGHTBRACKET,//0x23 ] and }
|
|
SDLK_RETURN,//0x24 Enter
|
|
SDLK_LCTRL,//0x25 Left-Control
|
|
SDLK_a,//0x26
|
|
SDLK_s,//0x27
|
|
SDLK_d,//0x28
|
|
SDLK_f,//0x29
|
|
SDLK_g,//0x2a
|
|
SDLK_h,//0x2b
|
|
SDLK_j,//0x2c
|
|
SDLK_k,//0x2d
|
|
SDLK_l,//0x2e
|
|
SDLK_SEMICOLON,//0x2f ; and :
|
|
SDLK_QUOTE,//0x30 ' and "
|
|
SDLK_BACKQUOTE,//0x31 Grave Accent and Tilde
|
|
SDLK_LSHIFT,//0x32 left-shift
|
|
SDLK_BACKSLASH,//0x33 \ and |
|
|
SDLK_z,//0x34
|
|
SDLK_x,//0x35
|
|
SDLK_c,//0x36
|
|
SDLK_v,//0x37
|
|
SDLK_b,//0x38
|
|
SDLK_n,//0x39
|
|
SDLK_m,//0x3a
|
|
SDLK_COMMA,//0x3b , and <
|
|
SDLK_PERIOD,//0x3c . and >
|
|
SDLK_SLASH,//0x3d / and ?
|
|
SDLK_RSHIFT,//0x3e Right-Shift
|
|
SDLK_KP_MULTIPLY,//0x3f Keypad *
|
|
SDLK_LALT,//0x40 Left-Alt
|
|
SDLK_SPACE,//0x41 Spacebar
|
|
SDLK_CAPSLOCK,//0x42 CapsLock
|
|
/*0x43-0x4c F1-F10*/
|
|
SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10,
|
|
SDLK_NUMLOCK, //0x4d Keypad Num Lock and Clear
|
|
SDLK_SCROLLOCK, //0x4e Scroll-lock
|
|
SDLK_KP7, //0x4f Keypad 7 and Home
|
|
SDLK_KP8, //0x50 Keypad 8 and Up Arrow
|
|
SDLK_KP9, //0x51 Keypad 9 and PageUp
|
|
SDLK_KP_MINUS, //0x52 Keypad -
|
|
SDLK_KP4, //0x53 Keypad 4 Left-arrow
|
|
SDLK_KP5, //0x54 Keypad 5
|
|
SDLK_KP6, //0x55 Keypad 6 Right-arrow
|
|
SDLK_KP_PLUS, //0x56 Keypad +
|
|
SDLK_KP1, //0x57 Keypad 1 and End
|
|
SDLK_KP2, //0x58 Keypad 2 and Down Arrow
|
|
SDLK_KP3, //0x59 Keypad 3 and PageDn
|
|
SDLK_KP0, //0x5a Keypad 0 and Insert
|
|
SDLK_KP_PERIOD, //0x5b Keypad . and Delete
|
|
Z, //0x5c unknown
|
|
SDLK_WORLD_12, //0x5d Zenkaku/Hankaku
|
|
SDLK_LESS, //0x5e Non-US \ and |
|
|
SDLK_F11, //0x5f
|
|
SDLK_F12, //0x60
|
|
SDLK_JP_RO, //0x61
|
|
Z, Z, //0x62-0x63 unknown
|
|
SDLK_WORLD_14, //0x64 Henkan
|
|
SDLK_WORLD_15, //0x65 Hiragana/Katakana
|
|
SDLK_WORLD_13, //0x66 Muhenkan
|
|
Z,SDLK_KP_ENTER,SDLK_RCTRL,SDLK_KP_DIVIDE, //0x67-0x6a
|
|
Z, //SDLK_PRINTSCREEN, //0x6b
|
|
SDLK_RALT, //0x6c
|
|
Z, //0x6d unknown
|
|
SDLK_HOME, //0x6e
|
|
SDLK_UP, //0x6f
|
|
SDLK_PAGEUP,//0x70
|
|
SDLK_LEFT, //0x71
|
|
SDLK_RIGHT, //0x72
|
|
SDLK_END, //0x73
|
|
SDLK_DOWN, //0x74
|
|
SDLK_PAGEDOWN,//0x75
|
|
SDLK_INSERT,//0x76
|
|
SDLK_DELETE,//0x77
|
|
Z,Z,Z,Z,Z,Z,Z, //0x78-0x7e
|
|
SDLK_PAUSE, // 0x7F
|
|
/* 0x80: */
|
|
Z,Z, //0x80-0x81 unknown
|
|
Z, //0x82 Hanguel
|
|
Z, //0x83 Hanja
|
|
SDLK_JP_YEN,//0x84
|
|
SDLK_LSUPER, //0x85
|
|
SDLK_RSUPER, //0x86
|
|
SDLK_MENU, //0x87
|
|
Z,Z,Z,Z,Z,Z,Z,Z //0x88-0x8f unknown
|
|
/* 0x90-0x9f unknown */
|
|
//Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z
|
|
};
|
|
|
|
#else // !MACOSX && !Linux
|
|
#if defined(__FreeBSD__) || defined(OS2)
|
|
// Todo: recheck sdl mapping
|
|
#define SDLK_JP_RO (SDLKey)0x73
|
|
#define SDLK_JP_YEN (SDLKey)0x7d
|
|
#if defined(OS2)
|
|
#define SDLK_KP_COMMA SDLK_COMMA
|
|
#endif
|
|
#endif
|
|
#define MAX_SCANCODES 0xdf
|
|
static SDLKey sdlkey_map[MAX_SCANCODES] = {
|
|
/* Refer to http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf PS/2 Set 1 Make */
|
|
SDLK_UNKNOWN,//0x00
|
|
SDLK_ESCAPE,//0x01
|
|
/*0x02-0x0b 1-9,0*/
|
|
SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6, SDLK_7, SDLK_8, SDLK_9, SDLK_0,
|
|
/* 0x0c: */
|
|
SDLK_MINUS, //0x0c
|
|
SDLK_EQUALS, //0x0d
|
|
SDLK_BACKSPACE,//0x0e
|
|
SDLK_TAB,//0x0f
|
|
SDLK_q,//0x10
|
|
SDLK_w,//0x11
|
|
SDLK_e,//0x12
|
|
SDLK_r,//0x13
|
|
SDLK_t,//0x14
|
|
SDLK_y,//0x15
|
|
SDLK_u,//0x16
|
|
SDLK_i,//0x17
|
|
SDLK_o,//0x18
|
|
SDLK_p,//0x19
|
|
SDLK_LEFTBRACKET,//0x1a [ and {
|
|
SDLK_RIGHTBRACKET,//0x1b ] and }
|
|
SDLK_RETURN,//0x1c Enter
|
|
SDLK_LCTRL,//0x1d Left-Control
|
|
SDLK_a,//0x1e
|
|
SDLK_s,//0x1f
|
|
SDLK_d,//0x20
|
|
SDLK_f,//0x21
|
|
SDLK_g,//0x22
|
|
SDLK_h,//0x23
|
|
SDLK_j,//0x24
|
|
SDLK_k,//0x25
|
|
SDLK_l,//0x26
|
|
SDLK_SEMICOLON,//0x27 ; and :
|
|
SDLK_QUOTE,//0x28 ' and "
|
|
SDLK_BACKQUOTE,//0x29 Grave Accent and Tilde
|
|
SDLK_LSHIFT,//0x2a left-shift
|
|
SDLK_BACKSLASH,//0x2b \ and |
|
|
SDLK_z,//0x2c
|
|
SDLK_x,//0x2d
|
|
SDLK_c,//0x2e
|
|
SDLK_v,//0x2f
|
|
SDLK_b,//0x30
|
|
SDLK_n,//0x31
|
|
SDLK_m,//0x32
|
|
/* 0x33: */
|
|
SDLK_COMMA,//0x33 , and <
|
|
SDLK_PERIOD,//0x34 . and >
|
|
SDLK_SLASH,//0x35 / and ?
|
|
SDLK_RSHIFT,//0x36 Right-Shift
|
|
SDLK_KP_MULTIPLY,//0x37 Keypad *
|
|
SDLK_LALT,//0x38 Left-Alt
|
|
SDLK_SPACE,//0x39 Spacebar
|
|
SDLK_CAPSLOCK,//0x3a CapsLock
|
|
/*0x3b-0x44 F1-F10*/
|
|
SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10,
|
|
/* 0x45: */
|
|
SDLK_NUMLOCK, //0x45 Keypad Num Lock and Clear
|
|
SDLK_SCROLLOCK, //0x46 Scroll-lock and Break
|
|
SDLK_KP7, //0x47 Keypad 7 and Home
|
|
SDLK_KP8, //0x48 Keypad 8 and Up Arrow
|
|
SDLK_KP9, //0x49 Keypad 9 and PageUp
|
|
SDLK_KP_MINUS, //0x4a Keypad -
|
|
SDLK_KP4, //0x4b Keypad 4 Left-arrow
|
|
SDLK_KP5, //0x4c Keypad 5
|
|
SDLK_KP6, //0x4d Keypad 6 Right-arrow
|
|
SDLK_KP_PLUS, //0x4e Keypad +
|
|
SDLK_KP1, //0x4f Keypad 1 and End
|
|
SDLK_KP2, //0x50 Keypad 2 and Down Arrow
|
|
SDLK_KP3, //0x51 Keypad 3 and PageDn
|
|
SDLK_KP0, //0x52 Keypad 0 and Insert
|
|
SDLK_KP_PERIOD, //0x53 Keypad . and Delete
|
|
Z, //0x54
|
|
Z, //0x55
|
|
SDLK_LESS, //0x56 Non-US \ and |
|
|
SDLK_F11, //0x57
|
|
SDLK_F12, //0x58
|
|
SDLK_KP_EQUALS, //0x59 Keypad =
|
|
Z, Z, //0x5a-0x5b unknown
|
|
Z, //0x5c Keyboard International6
|
|
Z, Z, Z, //0x5d-0x5f unknown
|
|
/* 0x60: */
|
|
Z, Z, Z, Z, //0x60-0x63 unknown
|
|
/* 0x64-0x6e F13-F23 */
|
|
Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z,
|
|
Z, // 0x6F unknown
|
|
/* 0x70: */
|
|
SDLK_WORLD_15,//0x70 Keyboard International2 Hiragana/Katakana
|
|
Z, Z, //0x71-0x72 unknown
|
|
SDLK_JP_RO, //0x73 Keyboard International1 \ and _
|
|
Z, Z,//0x74-0x75 unknown
|
|
Z, //0x76 F24 or Keyboard LANG5
|
|
Z, //0x77 Keyboard Lang4
|
|
Z, //0x78 Keyboard Lang3
|
|
SDLK_WORLD_14, //0x79 Keyboard International4 Henkan
|
|
Z, //0x7a unknown
|
|
SDLK_WORLD_13, //0x7b Keyboard International5 Muhenkan
|
|
Z, //0x7c unknown
|
|
SDLK_JP_YEN, //0x7d Keyboard International3 \ and |
|
|
SDLK_KP_COMMA, //0x7e Keypad Comma
|
|
Z, //0x7f unknown
|
|
/* 0x80-0x8f unknown */
|
|
Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z,
|
|
/* 0x90: */
|
|
Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z,
|
|
/* 0xA0: */
|
|
Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z,
|
|
/* 0xB0: */
|
|
Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z,
|
|
/* 0xC0: */
|
|
Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z,
|
|
/* 0xD0: */
|
|
Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z, Z//,Z,Z,
|
|
/* 0xE0: */
|
|
//Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
|
|
/* 0xF0: */
|
|
//Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z//,Z,Z
|
|
|
|
};
|
|
#endif
|
|
|
|
#undef Z
|
|
|
|
unsigned int Linux_GetKeyboardLayout(void); // defined in sdlmain_linux.cpp
|
|
|
|
#if !defined(C_SDL2)
|
|
void setScanCode(Section_prop * section) {
|
|
usescancodes = -1;
|
|
const char *usesc = section->Get_string("usescancodes");
|
|
if (!strcasecmp(usesc, "true")||!strcmp(usesc, "1"))
|
|
usescancodes = 1;
|
|
else if (!strcasecmp(usesc, "false")||!strcmp(usesc, "0"))
|
|
usescancodes = 0;
|
|
#if defined(WIN32)
|
|
else {
|
|
WORD cur_kb_layout = LOWORD(GetKeyboardLayout(0));
|
|
if(cur_kb_layout == 1033) { /* Locale ID: en-us */
|
|
usescancodes = 0;
|
|
LOG_MSG("SDL_mapper: US keyboard detected, set usescancodes=false");
|
|
}
|
|
else {
|
|
usescancodes = 1;
|
|
LOG_MSG("SDL_mapper: non-US keyboard detected, set usescancodes=true");
|
|
}
|
|
}
|
|
#elif defined(__linux__)
|
|
else {
|
|
if(Linux_GetKeyboardLayout() == DKM_US) { /* Locale ID: en-us */
|
|
usescancodes = 0;
|
|
LOG_MSG("SDL_mapper: US keyboard detected, set usescancodes=false");
|
|
}
|
|
else {
|
|
usescancodes = 1;
|
|
LOG_MSG("SDL_mapper: non-US keyboard detected, set usescancodes=true");
|
|
}
|
|
}
|
|
#elif defined(MACOSX)
|
|
else {
|
|
char layout[128];
|
|
memset(layout, '\0', sizeof(layout));
|
|
TISInputSourceRef source = TISCopyCurrentKeyboardInputSource();
|
|
// get input source id - kTISPropertyInputSourceID
|
|
// get layout name - kTISPropertyLocalizedName
|
|
CFStringRef layoutID = CFStringRef(TISGetInputSourceProperty(source, kTISPropertyInputSourceID));
|
|
CFStringGetCString(layoutID, layout, sizeof(layout), kCFStringEncodingUTF8);
|
|
//LOG_MSG("SDL_mapper: %s\n", layout);
|
|
if(!strcasecmp(layout, "com.apple.keylayout.US")) { /* US keyboard layout */
|
|
usescancodes = 0;
|
|
LOG_MSG("SDL_mapper: US keyboard detected, set usescancodes=false");
|
|
}
|
|
else {
|
|
usescancodes = 1;
|
|
LOG_MSG("SDL_mapper: non-US keyboard detected, set usescancodes=true");
|
|
}
|
|
}
|
|
#endif //defined(MACOSX)
|
|
}
|
|
void loadScanCode();
|
|
const char* DOS_GetLoadedLayout(void);
|
|
bool load=false;
|
|
bool prev_ret;
|
|
#endif // !defined(C_SDL2)
|
|
|
|
bool useScanCode() {
|
|
#if defined(C_SDL2)
|
|
return false;
|
|
#else
|
|
if (usescancodes==1)
|
|
return true;
|
|
else if (!usescancodes)
|
|
return false;
|
|
else {
|
|
const char* layout_name = DOS_GetLoadedLayout();
|
|
bool ret = layout_name != NULL && !IS_PC98_ARCH;
|
|
if (!load)
|
|
prev_ret=ret;
|
|
else if (ret != prev_ret) {
|
|
prev_ret=ret;
|
|
loadScanCode();
|
|
GFX_LosingFocus();
|
|
MAPPER_Init();
|
|
load=true;
|
|
}
|
|
return ret;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
SDLKey MapSDLCode(Bitu skey) {
|
|
// LOG_MSG("MapSDLCode %d %X",skey,skey);
|
|
#if defined(C_SDL2)
|
|
return (SDLKey)skey;
|
|
#else
|
|
if (useScanCode()) {
|
|
if (skey <= SDLK_LAST) return (SDLKey)skey;
|
|
else return SDLK_UNKNOWN;
|
|
} else return (SDLKey)skey;
|
|
#endif
|
|
}
|
|
|
|
Bitu GetKeyCode(SDL_keysym keysym) {
|
|
// LOG_MSG("GetKeyCode %X %X %X",keysym.scancode,keysym.sym,keysym.mod);
|
|
if (useScanCode()) {
|
|
SDLKey key = SDLK_UNKNOWN;
|
|
|
|
#if defined (MACOSX)
|
|
// zero value makes the keyboard crazy
|
|
// Hack for 'a' key (QWERTY, QWERTZ), 'u' key (Turkish-F), 'q' key (AZERTY) on Mac
|
|
// (FIX ME if there are other keys returning scancode 0)
|
|
if((keysym.scancode == 0) &&
|
|
((keysym.sym == 'a') || (keysym.sym == 'u') || (keysym.sym == 'q'))) return 'a';
|
|
#endif
|
|
|
|
if (keysym.scancode==0
|
|
#if defined (MACOSX)
|
|
/* On Mac on US keyboards, scancode 0 is actually the 'a'
|
|
* key. For good measure exclude all printables from this
|
|
* condition. */
|
|
&& (keysym.sym < SDLK_SPACE || keysym.sym > SDLK_WORLD_95)
|
|
#endif
|
|
) {
|
|
/* try to retrieve key from keysym.sym as scancode is zero */
|
|
if (keysym.sym) {
|
|
key = (keysym.sym <= SDLK_LAST ? keysym.sym : SDLK_UNKNOWN);
|
|
}
|
|
}
|
|
else {
|
|
#if defined (WIN32)
|
|
#if(_WIN32_WINNT >= 0x0500)
|
|
if(keysym.win32_vk >= 0xa6 && keysym.win32_vk <= 0xb7) return SDLK_UNKNOWN; // Ignore all media keys
|
|
#endif
|
|
switch(keysym.scancode) {
|
|
case 0x46: // Scroll Lock
|
|
// LOG_MSG("Scroll_lock scancode=%x, vk_key=%x", keysym.scancode, keysym.win32_vk);
|
|
return (keysym.win32_vk == VK_CANCEL ? SDLK_BREAK : SDLK_SCROLLOCK);
|
|
case 0x35: // SLASH
|
|
if(keysym.sym != SDLK_KP_DIVIDE) {
|
|
return SDLK_SLASH; // Various characters are allocated to this key in European keyboards
|
|
}
|
|
case 0x1c: // ENTER
|
|
case 0x1d: // CONTROL
|
|
case 0x37: // PRINTSCREEN
|
|
case 0x38: // ALT
|
|
case 0x45: // PAUSE
|
|
case 0x47: // HOME
|
|
case 0x48: // cursor UP
|
|
case 0x49: // PAGE UP
|
|
case 0x4b: // cursor LEFT
|
|
case 0x4d: // cursor RIGHT
|
|
case 0x4f: // END
|
|
case 0x50: // cursor DOWN
|
|
case 0x51: // PAGE DOWN
|
|
case 0x52: // INSERT
|
|
case 0x53: // DELETE
|
|
case 0x5b: // Left Windows
|
|
case 0x5c: // Right Windows
|
|
case 0x5d: // App
|
|
return keysym.sym; //Hardware scancodes of these keys are not unique, so virtual keycodes are used instead
|
|
break;
|
|
default:
|
|
#elif defined (__linux__)
|
|
switch(keysym.scancode) { // Workaround for some of the keys return incorrect virtual keycodes
|
|
case 0x5E:
|
|
return SDLK_LESS; //DE Keyboard on Debian GNU/Linux 12 (bookworm) Debian 1:6.1.63-1+rpt1 (2023-11-24) aarch64 Linux 6.1.0-rpi7-rpi-v8
|
|
case 0x60:
|
|
return SDLK_F12;
|
|
case 0x61:
|
|
return SDLK_JP_RO; //Hack: JP Keyboards
|
|
case 0x64:
|
|
return SDLK_WORLD_14; //Hack: JP Keyboards
|
|
case 0x66:
|
|
return SDLK_WORLD_13; //Hack: JP Keyboards
|
|
case 0x68:
|
|
return SDLK_KP_ENTER;
|
|
case 0x69:
|
|
return SDLK_RCTRL;
|
|
case 0x6a:
|
|
return SDLK_KP_DIVIDE;
|
|
//case 0x6b:
|
|
// return SDLK_PRINTSCREEN;
|
|
case 0x6c:
|
|
return SDLK_RALT;
|
|
case 0x6e:
|
|
return SDLK_HOME;
|
|
case 0x6f:
|
|
return SDLK_UP;
|
|
case 0x77:
|
|
return SDLK_DELETE;
|
|
case 0x7f:
|
|
return SDLK_PAUSE;
|
|
default:
|
|
#endif
|
|
key = (keysym.scancode < MAX_SCANCODES ? sdlkey_map[keysym.scancode] : SDLK_UNKNOWN);
|
|
#if defined (WIN32) || defined (__linux__)
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
#if defined (WIN32)
|
|
/* Special handling for JP Keyboard */
|
|
if (key == SDLK_BACKQUOTE){
|
|
key = (isJPkeyboard ? SDLK_WORLD_12 : SDLK_BACKQUOTE); // Set to Hankaku key if JP Keyboard
|
|
}
|
|
#endif
|
|
return key;
|
|
} else {
|
|
#if defined (WIN32)
|
|
/* special handling of 102-key under windows */
|
|
if((keysym.sym == SDLK_BACKSLASH) && (keysym.scancode == 0x56)) return (Bitu)SDLK_LESS;
|
|
/* 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 */
|
|
/* NTS: SDL source on Win32 maps this key (VK_OEM_102) to SDLK_LESS */
|
|
else 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!
|
|
/* another hack, for the Yen \ pipe key on Japanese 106-keyboards.
|
|
sym == 0 if English layout, sym == 0x5C if Japanese layout */
|
|
else if(keysym.win32_vk == VK_CANCEL) return (Bitu)SDLK_BREAK;
|
|
#if(_WIN32_WINNT >= 0x0500)
|
|
else if(keysym.win32_vk >= 0xa6 && keysym.win32_vk <= 0xb7) return (Bitu)SDLK_UNKNOWN; // Ignore all media keys
|
|
#endif
|
|
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!
|
|
/* what is ~ ` on American keyboards is "Hankaku" on Japanese keyboards. Same scan code. */
|
|
if (keysym.scancode == 0x29) return (Bitu) (isJPkeyboard ? SDLK_WORLD_12 : SDLK_BACKQUOTE); //if JP106 keyboard Hankaku else Backquote(grave)
|
|
/* Muhenkan */
|
|
if (isJPkeyboard && keysym.sym == 0 && keysym.scancode == 0x7B) return (Bitu)SDLK_WORLD_13;
|
|
/* Henkan/Zenkouho */
|
|
if (isJPkeyboard && keysym.sym == 0 && keysym.scancode == 0x79) return (Bitu)SDLK_WORLD_14;
|
|
/* Hiragana/Katakana */
|
|
if (isJPkeyboard && keysym.sym == 0 && keysym.scancode == 0x70) return (Bitu)SDLK_WORLD_15;
|
|
#elif defined (__linux__)
|
|
if ((keysym.sym == SDLK_BACKSLASH) && (keysym.scancode == 0x84)) return (Bitu)SDLK_JP_YEN;
|
|
if ((keysym.sym == SDLK_BACKSLASH) && (keysym.scancode == 0x61)) return (Bitu)SDLK_JP_RO;
|
|
if ((keysym.sym == SDLK_UNKNOWN) && (keysym.scancode == 0x31)) return (Bitu)SDLK_WORLD_12;
|
|
#endif
|
|
if (keysym.sym) return (Bitu) (keysym.sym < SDLK_LAST ? keysym.sym : SDLK_UNKNOWN);
|
|
else return (Bitu)(keysym.scancode < MAX_SCANCODES ? sdlkey_map[(keysym.scancode)] : SDLK_UNKNOWN);
|
|
}
|
|
}
|
|
|
|
#endif /* !defined(C_SDL2) */
|
|
|
|
class CKeyBind : public CBind {
|
|
public:
|
|
CKeyBind(CBindList * _list,SDLKey _key) : CBind(_list, CBind::keybind_t) {
|
|
key = _key;
|
|
}
|
|
virtual ~CKeyBind() {}
|
|
|
|
std::string CamelCase(const char* input)
|
|
{
|
|
auto text = std::string(input);
|
|
|
|
auto caps = true;
|
|
|
|
for(auto& c : text)
|
|
{
|
|
if(std::isalpha(c))
|
|
{
|
|
if(caps)
|
|
{
|
|
c = std::toupper(c);
|
|
caps = false;
|
|
}
|
|
else
|
|
{
|
|
c = std::tolower(c);
|
|
}
|
|
}
|
|
else if(c == ' ')
|
|
{
|
|
caps = true;
|
|
}
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
virtual void BindName(char * buf) override {
|
|
#if defined(C_SDL2)
|
|
sprintf(buf,"Key %s",SDL_GetScancodeName(key));
|
|
#else
|
|
const char *r=SDL_GetKeyName(key);
|
|
if (!strcmp(r, "left super")) r = "left Windows";
|
|
else if (!strcmp(r, "right super")) r = "right Windows";
|
|
else if (!strcmp(r, "left meta")) r = "left Command";
|
|
else if (!strcmp(r, "right meta")) r = "right Command";
|
|
else if (!strcmp(r, "left ctrl")) r = "Left Ctrl";
|
|
else if (!strcmp(r, "right ctrl")) r = "Right Ctrl";
|
|
else if (!strcmp(r, "left alt")) r = "Left Alt";
|
|
else if (!strcmp(r, "right alt")) r = "Right Alt";
|
|
else if (!strcmp(r, "left shift")) r = "Left Shift";
|
|
else if (!strcmp(r, "right shift")) r = "Right Shift";
|
|
//LOG_MSG("Key %s", r);
|
|
const auto str = CamelCase(r);
|
|
sprintf(buf,"%s key",str.c_str());
|
|
#endif
|
|
}
|
|
virtual void ConfigName(char * buf) override {
|
|
#if defined(C_SDL2)
|
|
sprintf(buf,"key %d",key);
|
|
#else
|
|
sprintf(buf,"key %d",key);
|
|
#endif
|
|
}
|
|
virtual std::string GetBindMenuText(void) override {
|
|
const char *s;
|
|
std::string r,m;
|
|
|
|
#if defined(C_SDL2)
|
|
s = SDL_GetScancodeName(key);
|
|
if (s != NULL) r = s;
|
|
#else
|
|
s = SDL_GetKeyName(key);
|
|
if (s != NULL) {
|
|
r = s;
|
|
if (r.length()>0) {
|
|
r[0]=toupper(r[0]);
|
|
char *c=(char *)strstr(r.c_str(), " ctrl");
|
|
if (c==NULL) c=(char *)strstr(r.c_str(), " alt");
|
|
if (c==NULL) c=(char *)strstr(r.c_str(), " shift");
|
|
if (c!=NULL) *(c+1)=toupper(*(c+1));
|
|
else if (r=="Left super") r = "Left Windows";
|
|
else if (r=="Right super") r = "Right Windows";
|
|
else if (r=="Left meta") r = "Left Command";
|
|
else if (r=="Right meta") r = "Right Command";
|
|
}
|
|
}
|
|
#endif
|
|
|
|
m = event->type == CEvent::mod_event_t ? "" : GetModifierText();
|
|
if (!m.empty()) r = m + "+" + r;
|
|
|
|
return r;
|
|
}
|
|
public:
|
|
SDLKey key;
|
|
};
|
|
|
|
std::string CEvent::GetBindMenuText(void) {
|
|
std::string r="", s="", t="";
|
|
|
|
if (bindlist.empty())
|
|
return std::string();
|
|
|
|
bool first=true;
|
|
for (auto i=bindlist.begin();i!=bindlist.end();i++) {
|
|
CBind *b = *i;
|
|
if (b == NULL) continue;
|
|
if (b->type != CBind::keybind_t) continue;
|
|
|
|
CKeyBind *kb = reinterpret_cast<CKeyBind*>(b);
|
|
if (kb == NULL) continue;
|
|
|
|
t = kb->GetBindMenuText();
|
|
if (first) {
|
|
first=false;
|
|
r += t;
|
|
}
|
|
if (t!="Right Windows"&&t!="Left Windows"&&t!="Right Command"&&t!="Left Command"&&t!="Right Ctrl"&&t!="Left Ctrl"&&t!="Right Alt"&&t!="Left Alt"&&t!="Right Shift"&&t!="Left Shift") break;
|
|
s += t;
|
|
}
|
|
if (s=="Right WindowsLeft Windows"||s=="Left WindowsRight Windows") r="Windows";
|
|
else if (s=="Right CommandLeft Command"||s=="Left CommandRight Command") r="Command";
|
|
else if (s=="Right CtrlLeft Ctrl"||s=="Left CtrlRight Ctrl") r="Ctrl";
|
|
else if (s=="Right AltLeft Alt"||s=="Left AltRight Alt") r="Alt";
|
|
else if (s=="Right ShiftLeft Shift"||s=="Left ShiftRight Shift") r="Shift";
|
|
|
|
return r;
|
|
}
|
|
|
|
class CKeyBindGroup : public CBindGroup {
|
|
public:
|
|
CKeyBindGroup(Bitu _keys) : CBindGroup (){
|
|
lists=new CBindList[_keys];
|
|
for (Bitu i=0;i<_keys;i++) lists[i].clear();
|
|
keys=_keys;
|
|
configname="key";
|
|
}
|
|
virtual ~CKeyBindGroup() { delete[] lists; }
|
|
CBind * CreateConfigBind(char *& buf) override {
|
|
if (strncasecmp(buf,configname,strlen(configname))) return nullptr;
|
|
StripWord(buf);char * num=StripWord(buf);
|
|
Bitu code=(Bitu)ConvDecWord(num);
|
|
#if defined(C_SDL2)
|
|
CBind * bind=CreateKeyBind((SDL_Scancode)code);
|
|
#else
|
|
//if (useScanCode()) {
|
|
// if (code<MAX_SDLKEYS) code=scancode_map[code];
|
|
// else code=0;
|
|
//}
|
|
CBind * bind=CreateKeyBind((SDLKey)code);
|
|
#endif
|
|
return bind;
|
|
}
|
|
CBind * CreateEventBind(SDL_Event * event) override {
|
|
if (event->type!=SDL_KEYDOWN) return nullptr;
|
|
#if defined(C_SDL2)
|
|
SDL_Scancode key = event->key.keysym.scancode;
|
|
#if defined(WIN32)
|
|
if(key == SDL_SCANCODE_NONUSBACKSLASH) { // Special consideration for JP Keyboard
|
|
key = (isJPkeyboard ? SDL_SCANCODE_INTERNATIONAL1 : SDL_SCANCODE_NONUSBACKSLASH);
|
|
}
|
|
#endif
|
|
return CreateKeyBind(key);
|
|
#else
|
|
return CreateKeyBind((SDLKey)GetKeyCode(event->key.keysym));
|
|
#endif
|
|
};
|
|
bool CheckEvent(SDL_Event * event) override {
|
|
if (event->type!=SDL_KEYDOWN && event->type!=SDL_KEYUP) return false;
|
|
#if defined(C_SDL2)
|
|
Bitu key = event->key.keysym.scancode;
|
|
#if defined(WIN32)
|
|
if(key == SDL_SCANCODE_NONUSBACKSLASH) { // Special consideration for JP Keyboard
|
|
key = (isJPkeyboard ? SDL_SCANCODE_INTERNATIONAL1 : SDL_SCANCODE_NONUSBACKSLASH);
|
|
}
|
|
#endif
|
|
#else
|
|
Bitu key;
|
|
|
|
//key = (event->key.keysym.sym ? GetKeyCode(event->key.keysym) : sdlkey_map[(Bitu)(event->key.keysym.scancode)]);
|
|
key = GetKeyCode(event->key.keysym);
|
|
//assert(key < keys);
|
|
if(key >= keys) {
|
|
key = SDLK_UNKNOWN; // a test to avoid assertion failure (key < keys)
|
|
// LOG_MSG("assertion failed: key: %x [keysym.sym:%x keysym.scancode: %x]", key, event->key.keysym.sym, event->key.keysym.scancode);
|
|
}
|
|
#endif
|
|
// LOG_MSG("key type %i is %x [%x %x]",event->type,key,event->key.keysym.sym,event->key.keysym.scancode);
|
|
|
|
#if defined(WIN32)
|
|
/* HACK: When setting up the Japanese keyboard layout, I'm seeing some bizarre keyboard handling
|
|
from within Windows when pressing the ~ ` (grave) aka Hankaku key. I know it's not hardware
|
|
because when you switch back to English the key works normally as the tilde/grave key.
|
|
What I'm seeing is that pressing the key only sends the "down" event (even though you're
|
|
not holding the key down). When you press the key again, an "up" event is sent immediately
|
|
followed by a "down" event. This is confusing to the mapper, so we work around it here. */
|
|
|
|
#if defined(C_SDL2)
|
|
if (isJPkeyboard && key == 0x35 && event->key.keysym.sym == 0x60)
|
|
#else
|
|
//if (isJPkeyboard && event->key.keysym.scancode == 0x29 /*Hankaku*/ || (useScanCode() && key == 0x29 && event->key.keysym.sym == 0))
|
|
if (isJPkeyboard && (key == SDLK_BACKQUOTE || key == SDLK_WORLD_12))
|
|
#endif
|
|
{
|
|
if (event->type == SDL_KEYDOWN) {
|
|
// send down, then up (right?)
|
|
ActivateBindList(&lists[key], 0x7fff, true);
|
|
DeactivateBindList(&lists[key], true);
|
|
}
|
|
return 0; // ignore up event
|
|
}
|
|
#endif
|
|
|
|
#if !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
/* HACK: As requested on the issue tracker, on US keyboards, remap the Windows menu key
|
|
* to the "Ro" key.
|
|
*
|
|
* If the user has un-bound the Ro key, then allow the menu key to go through
|
|
* unmodified.
|
|
*
|
|
* TODO: I understand that later PC-9821 systems (post Windows 95, prior to the platform's
|
|
* demise) actually did have Windows and Windows Menu keys.
|
|
*
|
|
* I also understand from "Undocumented PC-98" that the actual keyboards for backwards
|
|
* compat reasons will not send these key codes unless a byte is sent to the keyboard
|
|
* on Windows 95 startup to tell the keyboard that it can send those keys.
|
|
*
|
|
* This would explain why if you boot a PC-9821 system into Windows 95 "Safe mode"
|
|
* the Windows key does not work.
|
|
*
|
|
* So later on, this remap should disable itself IF that byte is sent to signal
|
|
* Windows 95 is running and the Windows keys should be enabled. */
|
|
if (IS_PC98_ARCH && host_keyboard_layout != DKM_JPN && key == SDLK_MENU) {
|
|
CEvent *x = get_mapper_event_by_name("key_jp_ro");
|
|
bool RoMenuRemap = false;
|
|
|
|
if (x != NULL) {
|
|
if (!x->bindlist.empty()) {
|
|
RoMenuRemap = true;
|
|
|
|
/* but, if the Ro key has been bound to Menu, do not remap */
|
|
auto &i = x->bindlist.front();
|
|
if (i->type == CBind::keybind_t) {
|
|
auto ik = reinterpret_cast<CKeyBind*>(i);
|
|
RoMenuRemap = false;
|
|
if (ik->key == SDLK_JP_RO)
|
|
RoMenuRemap = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (RoMenuRemap) key = SDLK_JP_RO;
|
|
}
|
|
#endif
|
|
|
|
if (event->type==SDL_KEYDOWN) ActivateBindList(&lists[key],0x7fff,true);
|
|
else DeactivateBindList(&lists[key],true);
|
|
return 0;
|
|
}
|
|
CBind * CreateKeyBind(SDLKey _key) {
|
|
#if !defined(C_SDL2)
|
|
if (!useScanCode()) assert((Bitu)_key<keys);
|
|
#endif
|
|
return new CKeyBind(&lists[(Bitu)_key],_key);
|
|
}
|
|
private:
|
|
const char * ConfigStart(void) override {
|
|
return configname;
|
|
}
|
|
const char * BindStart(void) override {
|
|
return "Key";
|
|
}
|
|
protected:
|
|
const char * configname;
|
|
CBindList * lists;
|
|
Bitu keys;
|
|
};
|
|
|
|
class CJAxisBind : public CBind {
|
|
public:
|
|
CJAxisBind(CBindList * _list,CBindGroup * _group, Bitu _joystick, Bitu _axis,bool _positive) : CBind(_list){
|
|
group = _group;
|
|
axis = _axis;
|
|
positive = _positive;
|
|
joystick = _joystick;
|
|
}
|
|
virtual ~CJAxisBind() {}
|
|
virtual void ConfigName(char * buf) override {
|
|
sprintf(buf,"%s axis %d %d",group->ConfigStart(),(int)axis,positive ? 1 : 0);
|
|
}
|
|
virtual void BindName(char * buf) override {
|
|
sprintf(buf,"%s Axis %d%s",group->BindStart(),(int)axis,positive ? "+" : "-");
|
|
}
|
|
|
|
//! \brief Gets the joystick index for this instance.
|
|
Bitu GetJoystick() const { return joystick; };
|
|
|
|
//! \brief Gets the axis index for this instance.
|
|
Bitu GetAxis() const { return axis; }
|
|
|
|
//! \brief Gets the axis direction for this instance.
|
|
bool GetPositive() const { return positive; }
|
|
|
|
//! \brief Gets the deadzone for a joystick axis direction.
|
|
static int GetJoystickDeadzone(int joystick, int axis, bool positive)
|
|
{
|
|
auto section = control->GetSection("mapper");
|
|
auto prop = static_cast<Section_prop*>(section);
|
|
auto name = "joy" + std::to_string(joystick + 1) + "deadzone" + std::to_string(axis) + (positive ? "+" : "-");
|
|
auto value = prop->Get_double(name);
|
|
auto deadzone = static_cast<int>(value * 32767.0);
|
|
return deadzone;
|
|
}
|
|
|
|
void ActivateBind(Bits _value, bool ev_trigger, bool skip_action = false) override
|
|
{
|
|
/* Since codebase is flawed, we do a simple hack:
|
|
* If user-set deadzone exceeds hard-coded value of 25000 we just set it to 25001.
|
|
* Other code works as usual, CTriggeredEvent does not have to check if it handles a joy axis.
|
|
*/
|
|
|
|
// activate if we exceed user-defined deadzone
|
|
const auto deadzone = GetJoystickDeadzone((int)this->GetJoystick(), (int)this->GetAxis(), this->GetPositive());
|
|
|
|
if (_value > deadzone && event->IsTrigger())
|
|
_value = 25000 + 1;
|
|
|
|
CBind::ActivateBind(_value, ev_trigger, skip_action);
|
|
}
|
|
|
|
protected:
|
|
CBindGroup * group;
|
|
Bitu axis;
|
|
bool positive;
|
|
Bitu joystick;
|
|
};
|
|
|
|
class CJButtonBind : public CBind {
|
|
public:
|
|
CJButtonBind(CBindList * _list,CBindGroup * _group,Bitu _button) : CBind(_list) {
|
|
group = _group;
|
|
button=_button;
|
|
}
|
|
virtual ~CJButtonBind() {}
|
|
virtual void ConfigName(char * buf) override {
|
|
sprintf(buf,"%s button %d",group->ConfigStart(),(int)button);
|
|
}
|
|
virtual void BindName(char * buf) override {
|
|
sprintf(buf,"%s Button %d",group->BindStart(),(int)button);
|
|
}
|
|
protected:
|
|
CBindGroup * group;
|
|
Bitu button;
|
|
};
|
|
|
|
class CJHatBind : public CBind {
|
|
public:
|
|
CJHatBind(CBindList * _list,CBindGroup * _group,Bitu _hat,uint8_t _dir) : CBind(_list) {
|
|
group = _group;
|
|
hat = _hat;
|
|
dir = _dir;
|
|
/* allow only one hat position */
|
|
if (dir&SDL_HAT_UP) dir=SDL_HAT_UP;
|
|
else if (dir&SDL_HAT_RIGHT) dir=SDL_HAT_RIGHT;
|
|
else if (dir&SDL_HAT_DOWN) dir=SDL_HAT_DOWN;
|
|
else if (dir&SDL_HAT_LEFT) dir=SDL_HAT_LEFT;
|
|
else E_Exit("MAPPER:JOYSTICK:Invalid hat position");
|
|
}
|
|
virtual ~CJHatBind() {}
|
|
virtual void ConfigName(char * buf) override {
|
|
sprintf(buf,"%s hat %d %d",group->ConfigStart(),(int)hat,(int)dir);
|
|
}
|
|
virtual void BindName(char * buf) override {
|
|
sprintf(buf,"%s Hat %d %s",group->BindStart(),(int)hat,(dir==SDL_HAT_UP)?"up":
|
|
((dir==SDL_HAT_RIGHT)?"right":
|
|
((dir==SDL_HAT_DOWN)?"down":"left")));
|
|
}
|
|
protected:
|
|
CBindGroup * group;
|
|
Bitu hat;
|
|
uint8_t dir;
|
|
};
|
|
|
|
class CStickBindGroup : public CBindGroup {
|
|
public:
|
|
CStickBindGroup(Bitu _stick,Bitu _emustick,bool _dummy=false) : CBindGroup (){
|
|
stick=_stick; // the number of the physical device (SDL numbering|)
|
|
emustick=_emustick; // the number of the emulated device
|
|
sprintf(configname,"stick_%d",(int)emustick);
|
|
|
|
sdl_joystick=NULL;
|
|
axes=0; buttons=0; hats=0;
|
|
button_wrap=0;
|
|
button_cap=0; axes_cap=0; hats_cap=0;
|
|
emulated_buttons=0; emulated_axes=0; emulated_hats=0;
|
|
pos_axis_lists = neg_axis_lists = NULL; /* <- Initialize the pointers to NULL. The C++ compiler won't do it for us. */
|
|
button_lists = hat_lists = NULL;
|
|
|
|
is_dummy=_dummy;
|
|
if (_dummy) return;
|
|
|
|
// initialize binding lists and position data
|
|
pos_axis_lists=new CBindList[MAXAXIS];
|
|
neg_axis_lists=new CBindList[MAXAXIS];
|
|
button_lists=new CBindList[MAXBUTTON];
|
|
hat_lists=new CBindList[4];
|
|
Bitu i;
|
|
for (i=0; i<MAXBUTTON; i++) {
|
|
button_autofire[i]=0;
|
|
old_button_state[i]=0;
|
|
}
|
|
for(i=0;i<16;i++) old_hat_state[i]=0;
|
|
for (i=0; i<MAXAXIS; i++) {
|
|
old_pos_axis_state[i]=false;
|
|
old_neg_axis_state[i]=false;
|
|
}
|
|
|
|
// initialize emulated joystick state
|
|
emulated_axes=2;
|
|
emulated_buttons=2;
|
|
emulated_hats=0;
|
|
JOYSTICK_Enable(emustick,true);
|
|
|
|
sdl_joystick=SDL_JoystickOpen((int)_stick);
|
|
if (sdl_joystick==NULL) {
|
|
button_wrap=emulated_buttons;
|
|
return;
|
|
}
|
|
|
|
axes=(Bitu)SDL_JoystickNumAxes(sdl_joystick);
|
|
if (axes > MAXAXIS) axes = MAXAXIS;
|
|
axes_cap=emulated_axes;
|
|
if (axes_cap>axes) axes_cap=axes;
|
|
|
|
hats=(Bitu)SDL_JoystickNumHats(sdl_joystick);
|
|
if (hats > MAXHAT) hats = MAXHAT;
|
|
hats_cap=emulated_hats;
|
|
if (hats_cap>hats) hats_cap=hats;
|
|
|
|
buttons=(Bitu)SDL_JoystickNumButtons(sdl_joystick);
|
|
button_wrap=buttons;
|
|
button_cap=buttons;
|
|
if (button_wrapping_enabled) {
|
|
button_wrap=emulated_buttons;
|
|
if (buttons>MAXBUTTON_CAP) button_cap = MAXBUTTON_CAP;
|
|
}
|
|
if (button_wrap > MAXBUTTON) button_wrap = MAXBUTTON;
|
|
|
|
#if defined(C_SDL2)
|
|
LOG_MSG("Using joystick %s with %d axes, %d buttons and %d hat(s)",SDL_JoystickNameForIndex((int)stick),(int)axes,(int)buttons,(int)hats);
|
|
#else
|
|
LOG_MSG("Using joystick %s with %d axes, %d buttons and %d hat(s)",SDL_JoystickName((int)stick),(int)axes,(int)buttons,(int)hats);
|
|
#endif
|
|
|
|
// fetching these at every call simply freezes DOSBox at times so we do it once
|
|
// (game tested : Terminal Velocity @ joystick calibration page)
|
|
joy1dz1 = static_cast<float>(GetAxisDeadzone(0, 0));
|
|
joy1rs1 = static_cast<float>(GetAxisResponse(0, 0));
|
|
joy1dz2 = static_cast<float>(GetAxisDeadzone(0, 1));
|
|
joy1rs2 = static_cast<float>(GetAxisResponse(0, 1));
|
|
joy2dz1 = static_cast<float>(GetAxisDeadzone(1, 0));
|
|
joy2rs1 = static_cast<float>(GetAxisResponse(1, 0));
|
|
}
|
|
virtual ~CStickBindGroup() {
|
|
SDL_JoystickClose(sdl_joystick);
|
|
if (pos_axis_lists != NULL) delete[] pos_axis_lists;
|
|
if (neg_axis_lists != NULL) delete[] neg_axis_lists;
|
|
if (button_lists != NULL) delete[] button_lists;
|
|
if (hat_lists != NULL) delete[] hat_lists;
|
|
}
|
|
|
|
CBind * CreateConfigBind(char *& buf) override {
|
|
if (is_dummy) return nullptr;
|
|
if (strncasecmp(configname,buf,strlen(configname))) return nullptr;
|
|
StripWord(buf);char * type=StripWord(buf);
|
|
CBind * bind = nullptr;
|
|
if (!strcasecmp(type,"axis")) {
|
|
Bitu ax=(Bitu)ConvDecWord(StripWord(buf));
|
|
bool pos=ConvDecWord(StripWord(buf)) > 0;
|
|
bind=CreateAxisBind(ax,pos);
|
|
} else if (!strcasecmp(type,"button")) {
|
|
Bitu but=(Bitu)ConvDecWord(StripWord(buf));
|
|
bind=CreateButtonBind(but);
|
|
} else if (!strcasecmp(type,"hat")) {
|
|
Bitu hat=(Bitu)ConvDecWord(StripWord(buf));
|
|
uint8_t dir=(uint8_t)ConvDecWord(StripWord(buf));
|
|
bind=CreateHatBind(hat,dir);
|
|
}
|
|
return bind;
|
|
}
|
|
CBind * CreateEventBind(SDL_Event * event) override {
|
|
if (event->type==SDL_JOYAXISMOTION) {
|
|
if ((unsigned int)event->jaxis.which!=(unsigned int)stick) return nullptr;
|
|
#if defined (REDUCE_JOYSTICK_POLLING)
|
|
if (event->jaxis.axis>=axes) return nullptr;
|
|
#endif
|
|
if (abs(event->jaxis.value)<25000) return nullptr;
|
|
return CreateAxisBind(event->jaxis.axis,event->jaxis.value>0);
|
|
} else if (event->type==SDL_JOYBUTTONDOWN) {
|
|
if ((unsigned int)event->jbutton.which!=(unsigned int)stick) return nullptr;
|
|
#if defined (REDUCE_JOYSTICK_POLLING)
|
|
return CreateButtonBind(event->jbutton.button%button_wrap);
|
|
#else
|
|
return CreateButtonBind(event->jbutton.button);
|
|
#endif
|
|
} else if (event->type==SDL_JOYHATMOTION) {
|
|
if ((unsigned int)event->jhat.which!=(unsigned int)stick) return nullptr;
|
|
if (event->jhat.value==0) return nullptr;
|
|
if (event->jhat.value>(SDL_HAT_UP|SDL_HAT_RIGHT|SDL_HAT_DOWN|SDL_HAT_LEFT)) return nullptr;
|
|
return CreateHatBind(event->jhat.hat,event->jhat.value);
|
|
} else return nullptr;
|
|
}
|
|
|
|
bool CheckEvent(SDL_Event * event) override {
|
|
SDL_JoyAxisEvent * jaxis = NULL;
|
|
SDL_JoyButtonEvent * jbutton = NULL;
|
|
|
|
switch(event->type) {
|
|
case SDL_JOYAXISMOTION:
|
|
jaxis = &event->jaxis;
|
|
if((unsigned int)jaxis->which == (unsigned int)stick) {
|
|
if(jaxis->axis == 0)
|
|
JOYSTICK_Move_X(emustick,(float)(jaxis->value/32768.0));
|
|
else if(jaxis->axis == 1)
|
|
JOYSTICK_Move_Y(emustick,(float)(jaxis->value/32768.0));
|
|
}
|
|
break;
|
|
case SDL_JOYBUTTONDOWN:
|
|
case SDL_JOYBUTTONUP:
|
|
if (emulated_buttons != 0) {
|
|
jbutton = &event->jbutton;
|
|
bool state;
|
|
state=jbutton->type==SDL_JOYBUTTONDOWN;
|
|
Bitu but = jbutton->button % emulated_buttons;
|
|
if ((unsigned int)jbutton->which == (unsigned int)stick) {
|
|
JOYSTICK_Button(emustick, but, state);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
virtual void UpdateJoystick() {
|
|
if (is_dummy) return;
|
|
/* query SDL joystick and activate bindings */
|
|
ActivateJoystickBoundEvents();
|
|
|
|
bool button_pressed[MAXBUTTON];
|
|
Bitu i;
|
|
for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
|
|
for (i=0; i<MAX_VJOY_BUTTONS; i++) {
|
|
if (virtual_joysticks[emustick].button_pressed[i])
|
|
button_pressed[i % button_wrap]=true;
|
|
}
|
|
for (i=0; i<emulated_buttons; i++) {
|
|
if (autofire && (button_pressed[i]))
|
|
JOYSTICK_Button(emustick,i,(++button_autofire[i])&1);
|
|
else
|
|
JOYSTICK_Button(emustick,i,button_pressed[i]);
|
|
}
|
|
|
|
auto v = GetJoystickVector((int)emustick, 0, 0, 1);
|
|
JOYSTICK_Move_X(emustick, v.X);
|
|
JOYSTICK_Move_Y(emustick, v.Y);
|
|
}
|
|
|
|
void ActivateJoystickBoundEvents() {
|
|
if (GCC_UNLIKELY(sdl_joystick==NULL)) return;
|
|
|
|
Bitu i;
|
|
|
|
bool button_pressed[MAXBUTTON];
|
|
for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
|
|
/* read button states */
|
|
for (i=0; i<button_cap; i++) {
|
|
if (SDL_JoystickGetButton(sdl_joystick,(int)i))
|
|
button_pressed[i % button_wrap]=true;
|
|
}
|
|
for (i=0; i<button_wrap; i++) {
|
|
/* activate binding if button state has changed */
|
|
if (button_pressed[i]!=old_button_state[i]) {
|
|
if (button_pressed[i]) ActivateBindList(&button_lists[i],32767,true);
|
|
else DeactivateBindList(&button_lists[i],true);
|
|
old_button_state[i]=button_pressed[i];
|
|
}
|
|
}
|
|
|
|
int* axis_map = stick == 0 ? &joy1axes[0] : &joy2axes[0];
|
|
for (i=0; i<axes; i++) {
|
|
int i1 = axis_map[i];
|
|
Sint16 caxis_pos=SDL_JoystickGetAxis(sdl_joystick,i1);
|
|
/* activate bindings for joystick position */
|
|
if (caxis_pos>1) {
|
|
if (old_neg_axis_state[i]) {
|
|
DeactivateBindList(&neg_axis_lists[i],false);
|
|
old_neg_axis_state[i] = false;
|
|
}
|
|
ActivateBindList(&pos_axis_lists[i],caxis_pos,false);
|
|
old_pos_axis_state[i] = true;
|
|
} else if (caxis_pos<-1) {
|
|
if (old_pos_axis_state[i]) {
|
|
DeactivateBindList(&pos_axis_lists[i],false);
|
|
old_pos_axis_state[i] = false;
|
|
}
|
|
if (caxis_pos!=-32768) caxis_pos=(Sint16)abs(caxis_pos);
|
|
else caxis_pos=32767;
|
|
ActivateBindList(&neg_axis_lists[i],caxis_pos,false);
|
|
old_neg_axis_state[i] = true;
|
|
} else {
|
|
/* center */
|
|
if (old_pos_axis_state[i]) {
|
|
DeactivateBindList(&pos_axis_lists[i],false);
|
|
old_pos_axis_state[i] = false;
|
|
}
|
|
if (old_neg_axis_state[i]) {
|
|
DeactivateBindList(&neg_axis_lists[i],false);
|
|
old_neg_axis_state[i] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i=0; i<hats; i++) {
|
|
Uint8 chat_state=SDL_JoystickGetHat(sdl_joystick,(int)i);
|
|
|
|
/* activate binding if hat state has changed */
|
|
if ((chat_state & SDL_HAT_UP) != (old_hat_state[i] & SDL_HAT_UP)) {
|
|
if (chat_state & SDL_HAT_UP) ActivateBindList(&hat_lists[(i<<2)+0],32767,true);
|
|
else DeactivateBindList(&hat_lists[(i<<2)+0],true);
|
|
}
|
|
if ((chat_state & SDL_HAT_RIGHT) != (old_hat_state[i] & SDL_HAT_RIGHT)) {
|
|
if (chat_state & SDL_HAT_RIGHT) ActivateBindList(&hat_lists[(i<<2)+1],32767,true);
|
|
else DeactivateBindList(&hat_lists[(i<<2)+1],true);
|
|
}
|
|
if ((chat_state & SDL_HAT_DOWN) != (old_hat_state[i] & SDL_HAT_DOWN)) {
|
|
if (chat_state & SDL_HAT_DOWN) ActivateBindList(&hat_lists[(i<<2)+2],32767,true);
|
|
else DeactivateBindList(&hat_lists[(i<<2)+2],true);
|
|
}
|
|
if ((chat_state & SDL_HAT_LEFT) != (old_hat_state[i] & SDL_HAT_LEFT)) {
|
|
if (chat_state & SDL_HAT_LEFT) ActivateBindList(&hat_lists[(i<<2)+3],32767,true);
|
|
else DeactivateBindList(&hat_lists[(i<<2)+3],true);
|
|
}
|
|
old_hat_state[i]=chat_state;
|
|
}
|
|
}
|
|
|
|
private:
|
|
float joy1dz1 = 0, joy1rs1 = 0, joy1dz2 = 0, joy1rs2 = 0, joy2dz1 = 0, joy2rs1 = 0;
|
|
CBind * CreateAxisBind(Bitu axis,bool positive) {
|
|
if (axis<axes) {
|
|
if (positive) return new CJAxisBind(&pos_axis_lists[axis],this,stick,axis,positive);
|
|
else return new CJAxisBind(&neg_axis_lists[axis],this,stick,axis,positive);
|
|
}
|
|
return NULL;
|
|
}
|
|
CBind * CreateButtonBind(Bitu button) {
|
|
if (button<button_wrap)
|
|
return new CJButtonBind(&button_lists[button],this,button);
|
|
return NULL;
|
|
}
|
|
CBind * CreateHatBind(Bitu hat,uint8_t value) {
|
|
if (hat < hats_cap) return NULL;
|
|
Bitu hat_dir;
|
|
if (value&SDL_HAT_UP) hat_dir=0;
|
|
else if (value&SDL_HAT_RIGHT) hat_dir=1;
|
|
else if (value&SDL_HAT_DOWN) hat_dir=2;
|
|
else if (value&SDL_HAT_LEFT) hat_dir=3;
|
|
else return NULL;
|
|
return new CJHatBind(&hat_lists[(hat<<2)+hat_dir],this,hat,value);
|
|
}
|
|
const char * ConfigStart(void) override {
|
|
return configname;
|
|
}
|
|
const char * BindStart(void) override {
|
|
#if defined(C_SDL2)
|
|
if (sdl_joystick!=NULL) return SDL_JoystickNameForIndex((int)stick);
|
|
#else
|
|
if (sdl_joystick!=NULL) return SDL_JoystickName((int)stick);
|
|
#endif
|
|
else return "[missing joystick]";
|
|
}
|
|
|
|
static float GetAxisDeadzone(int joystick, int thumbStick)
|
|
{
|
|
auto section = control->GetSection("joystick");
|
|
auto prop = static_cast<Section_prop*>(section);
|
|
auto name = "joy" + std::to_string(joystick + 1) + "deadzone" + std::to_string(thumbStick + 1);
|
|
auto deadzone = static_cast<float>(prop->Get_double(name));
|
|
return deadzone;
|
|
}
|
|
|
|
static float GetAxisResponse(int joystick, int thumbStick)
|
|
{
|
|
auto section = control->GetSection("joystick");
|
|
auto prop = static_cast<Section_prop*>(section);
|
|
auto name = "joy" + std::to_string(joystick + 1) + "response" + std::to_string(thumbStick + 1);
|
|
auto response = static_cast<float>(prop->Get_double(name));
|
|
return response;
|
|
}
|
|
|
|
static void ProcessInput(int16_t x, int16_t y, float deadzone, DOSBox_Vector2& joy)
|
|
{
|
|
// http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html
|
|
|
|
joy = DOSBox_Vector2((x + 0.5f) / 32767.5f, (y + 0.5f) / 32767.5f);
|
|
|
|
float m = joy.magnitude();
|
|
DOSBox_Vector2 n = joy.normalized();
|
|
joy = m < deadzone ? DOSBox_Vector2() : n * ((m - deadzone) / (1.0f - deadzone));
|
|
|
|
DOSBox_Vector2 min = DOSBox_Vector2(-1.0f, -1.0f);
|
|
DOSBox_Vector2 max = DOSBox_Vector2(+1.0f, +1.0f);
|
|
joy = joy.clamp(min, max);
|
|
}
|
|
|
|
protected:
|
|
CBindList * pos_axis_lists;
|
|
CBindList * neg_axis_lists;
|
|
CBindList * button_lists;
|
|
CBindList * hat_lists;
|
|
Bitu stick,emustick,axes,buttons,hats,emulated_axes,emulated_buttons,emulated_hats;
|
|
Bitu button_wrap,button_cap,axes_cap,hats_cap;
|
|
SDL_Joystick * sdl_joystick;
|
|
char configname[10];
|
|
Bitu button_autofire[MAXBUTTON] = {};
|
|
bool old_button_state[MAXBUTTON] = {};
|
|
bool old_pos_axis_state[MAXAXIS] = {};
|
|
bool old_neg_axis_state[MAXAXIS] = {};
|
|
Uint8 old_hat_state[16] = {};
|
|
bool is_dummy;
|
|
|
|
DOSBox_Vector2 GetJoystickVector(int joystick, int thumbStick, int xAxis, int yAxis) const
|
|
{
|
|
int16_t x = virtual_joysticks[joystick].axis_pos[xAxis];
|
|
int16_t y = virtual_joysticks[joystick].axis_pos[yAxis];
|
|
float deadzone;
|
|
float response;
|
|
if (joystick == 0)
|
|
{
|
|
if (thumbStick == 0)
|
|
{
|
|
deadzone = joy1dz1;
|
|
response = joy1rs1;
|
|
}
|
|
else
|
|
{
|
|
deadzone = joy1dz2;
|
|
response = joy1rs2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
deadzone = joy2dz1;
|
|
response = joy2rs1;
|
|
}
|
|
DOSBox_Vector2 v;
|
|
ProcessInput(x, y, deadzone, v);
|
|
float x1 = (sgn(v.X) * pow(fabs(v.X), response));
|
|
float y1 = (sgn(v.Y) * pow(fabs(v.Y), response));
|
|
DOSBox_Vector2 v1(x1, y1);
|
|
return v1;
|
|
}
|
|
};
|
|
|
|
class C4AxisBindGroup : public CStickBindGroup {
|
|
public:
|
|
C4AxisBindGroup(Bitu _stick,Bitu _emustick) : CStickBindGroup (_stick,_emustick){
|
|
emulated_axes=4;
|
|
emulated_buttons=4;
|
|
if (button_wrapping_enabled) button_wrap=emulated_buttons;
|
|
|
|
axes_cap=emulated_axes;
|
|
if (axes_cap>axes) axes_cap=axes;
|
|
hats_cap=emulated_hats;
|
|
if (hats_cap>hats) hats_cap=hats;
|
|
|
|
JOYSTICK_Enable(1,true);
|
|
}
|
|
virtual ~C4AxisBindGroup() {}
|
|
|
|
bool CheckEvent(SDL_Event * event) override {
|
|
SDL_JoyAxisEvent * jaxis = NULL;
|
|
SDL_JoyButtonEvent * jbutton = NULL;
|
|
Bitu but = 0;
|
|
|
|
switch(event->type) {
|
|
case SDL_JOYAXISMOTION:
|
|
jaxis = &event->jaxis;
|
|
if((unsigned int)jaxis->which == (unsigned int)stick && jaxis->axis < 4) {
|
|
if(jaxis->axis & 1)
|
|
JOYSTICK_Move_Y(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
|
|
else
|
|
JOYSTICK_Move_X(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
|
|
}
|
|
break;
|
|
case SDL_JOYBUTTONDOWN:
|
|
case SDL_JOYBUTTONUP:
|
|
jbutton = &event->jbutton;
|
|
bool state;
|
|
state=jbutton->type==SDL_JOYBUTTONDOWN;
|
|
but = jbutton->button % emulated_buttons;
|
|
if ((unsigned int)jbutton->which == (unsigned int)stick) {
|
|
JOYSTICK_Button((but >> 1),(but & 1),state);
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void UpdateJoystick() override {
|
|
/* query SDL joystick and activate bindings */
|
|
ActivateJoystickBoundEvents();
|
|
|
|
bool button_pressed[MAXBUTTON];
|
|
Bitu i;
|
|
for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
|
|
for (i=0; i<MAX_VJOY_BUTTONS; i++) {
|
|
if (virtual_joysticks[0].button_pressed[i])
|
|
button_pressed[i % button_wrap]=true;
|
|
|
|
}
|
|
for (i=0; i<emulated_buttons; i++) {
|
|
if (autofire && (button_pressed[i]))
|
|
JOYSTICK_Button(i>>1,i&1,(++button_autofire[i])&1);
|
|
else
|
|
JOYSTICK_Button(i>>1,i&1,button_pressed[i]);
|
|
}
|
|
|
|
auto v1 = GetJoystickVector(0, 0, 0, 1);
|
|
auto v2 = GetJoystickVector(0, 1, 2, 3);
|
|
JOYSTICK_Move_X(0, v1.X);
|
|
JOYSTICK_Move_Y(0, v1.Y);
|
|
JOYSTICK_Move_X(1, v2.X);
|
|
JOYSTICK_Move_Y(1, v2.Y);
|
|
}
|
|
};
|
|
|
|
class CFCSBindGroup : public CStickBindGroup {
|
|
public:
|
|
CFCSBindGroup(Bitu _stick,Bitu _emustick) : CStickBindGroup (_stick,_emustick){
|
|
emulated_axes=4;
|
|
emulated_buttons=4;
|
|
old_hat_position=0;
|
|
emulated_hats=1;
|
|
if (button_wrapping_enabled) button_wrap=emulated_buttons;
|
|
|
|
axes_cap=emulated_axes;
|
|
if (axes_cap>axes) axes_cap=axes;
|
|
//hats_cap=emulated_hats;
|
|
//if (hats_cap>hats) hats_cap=hats;
|
|
|
|
JOYSTICK_Enable(1,true);
|
|
JOYSTICK_Move_Y(1,1.0);
|
|
}
|
|
virtual ~CFCSBindGroup() {}
|
|
|
|
bool CheckEvent(SDL_Event * event) override {
|
|
SDL_JoyAxisEvent * jaxis = NULL;
|
|
SDL_JoyButtonEvent * jbutton = NULL;
|
|
SDL_JoyHatEvent * jhat = NULL;
|
|
Bitu but = 0;
|
|
|
|
switch(event->type) {
|
|
case SDL_JOYAXISMOTION:
|
|
jaxis = &event->jaxis;
|
|
if((unsigned int)jaxis->which == (unsigned int)stick) {
|
|
if(jaxis->axis == 0)
|
|
JOYSTICK_Move_X(0,(float)(jaxis->value/32768.0));
|
|
else if(jaxis->axis == 1)
|
|
JOYSTICK_Move_Y(0,(float)(jaxis->value/32768.0));
|
|
else if(jaxis->axis == 2)
|
|
JOYSTICK_Move_X(1,(float)(jaxis->value/32768.0));
|
|
}
|
|
break;
|
|
case SDL_JOYHATMOTION:
|
|
jhat = &event->jhat;
|
|
if((unsigned int)jhat->which == (unsigned int)stick) DecodeHatPosition(jhat->value);
|
|
break;
|
|
case SDL_JOYBUTTONDOWN:
|
|
case SDL_JOYBUTTONUP:
|
|
jbutton = &event->jbutton;
|
|
bool state;
|
|
state=jbutton->type==SDL_JOYBUTTONDOWN;
|
|
but = jbutton->button % emulated_buttons;
|
|
if ((unsigned int)jbutton->which == (unsigned int)stick) {
|
|
JOYSTICK_Button((but >> 1),(but & 1),state);
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void UpdateJoystick() override {
|
|
/* query SDL joystick and activate bindings */
|
|
ActivateJoystickBoundEvents();
|
|
|
|
bool button_pressed[MAXBUTTON];
|
|
Bitu i;
|
|
for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
|
|
for (i=0; i<MAX_VJOY_BUTTONS; i++) {
|
|
if (virtual_joysticks[0].button_pressed[i])
|
|
button_pressed[i % button_wrap]=true;
|
|
|
|
}
|
|
for (i=0; i<emulated_buttons; i++) {
|
|
if (autofire && (button_pressed[i]))
|
|
JOYSTICK_Button(i>>1,i&1,(++button_autofire[i])&1);
|
|
else
|
|
JOYSTICK_Button(i>>1,i&1,button_pressed[i]);
|
|
}
|
|
|
|
auto v1 = GetJoystickVector(0, 0, 0, 1);
|
|
auto v2 = GetJoystickVector(0, 1, 2, 3);
|
|
JOYSTICK_Move_X(0, v1.X);
|
|
JOYSTICK_Move_Y(0, v1.Y);
|
|
JOYSTICK_Move_X(1, v2.X);
|
|
|
|
Uint8 hat_pos=0;
|
|
if (virtual_joysticks[0].hat_pressed[0]) hat_pos|=SDL_HAT_UP;
|
|
else if (virtual_joysticks[0].hat_pressed[2]) hat_pos|=SDL_HAT_DOWN;
|
|
if (virtual_joysticks[0].hat_pressed[3]) hat_pos|=SDL_HAT_LEFT;
|
|
else if (virtual_joysticks[0].hat_pressed[1]) hat_pos|=SDL_HAT_RIGHT;
|
|
|
|
if (hat_pos!=old_hat_position) {
|
|
DecodeHatPosition(hat_pos);
|
|
old_hat_position=hat_pos;
|
|
}
|
|
}
|
|
|
|
private:
|
|
Uint8 old_hat_position;
|
|
|
|
void DecodeHatPosition(Uint8 hat_pos) {
|
|
switch(hat_pos) {
|
|
case SDL_HAT_CENTERED:
|
|
JOYSTICK_Move_Y(1,1.0);
|
|
break;
|
|
case SDL_HAT_UP:
|
|
JOYSTICK_Move_Y(1,-1.0);
|
|
break;
|
|
case SDL_HAT_RIGHT:
|
|
JOYSTICK_Move_Y(1,-0.5);
|
|
break;
|
|
case SDL_HAT_DOWN:
|
|
JOYSTICK_Move_Y(1,0.0);
|
|
break;
|
|
case SDL_HAT_LEFT:
|
|
JOYSTICK_Move_Y(1,0.5);
|
|
break;
|
|
case SDL_HAT_LEFTUP:
|
|
if(JOYSTICK_GetMove_Y(1) < 0)
|
|
JOYSTICK_Move_Y(1,0.5);
|
|
else
|
|
JOYSTICK_Move_Y(1,-1.0);
|
|
break;
|
|
case SDL_HAT_RIGHTUP:
|
|
if(JOYSTICK_GetMove_Y(1) < -0.7)
|
|
JOYSTICK_Move_Y(1,-0.5);
|
|
else
|
|
JOYSTICK_Move_Y(1,-1.0);
|
|
break;
|
|
case SDL_HAT_RIGHTDOWN:
|
|
if(JOYSTICK_GetMove_Y(1) < -0.2)
|
|
JOYSTICK_Move_Y(1,0.0);
|
|
else
|
|
JOYSTICK_Move_Y(1,-0.5);
|
|
break;
|
|
case SDL_HAT_LEFTDOWN:
|
|
if(JOYSTICK_GetMove_Y(1) > 0.2)
|
|
JOYSTICK_Move_Y(1,0.0);
|
|
else
|
|
JOYSTICK_Move_Y(1,0.5);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
class CCHBindGroup : public CStickBindGroup {
|
|
public:
|
|
CCHBindGroup(Bitu _stick,Bitu _emustick) : CStickBindGroup (_stick,_emustick){
|
|
emulated_axes=4;
|
|
emulated_buttons=6;
|
|
emulated_hats=1;
|
|
if (button_wrapping_enabled) button_wrap=emulated_buttons;
|
|
|
|
axes_cap=emulated_axes;
|
|
if (axes_cap>axes) axes_cap=axes;
|
|
hats_cap=emulated_hats;
|
|
if (hats_cap>hats) hats_cap=hats;
|
|
|
|
JOYSTICK_Enable(1,true);
|
|
button_state=0;
|
|
}
|
|
virtual ~CCHBindGroup() {}
|
|
|
|
bool CheckEvent(SDL_Event * event) override {
|
|
SDL_JoyAxisEvent * jaxis = NULL;
|
|
SDL_JoyButtonEvent * jbutton = NULL;
|
|
SDL_JoyHatEvent * jhat = NULL;
|
|
Bitu but = 0;
|
|
static unsigned const button_magic[6]={0x02,0x04,0x10,0x100,0x20,0x200};
|
|
static unsigned const hat_magic[2][5]={{0x8888,0x8000,0x800,0x80,0x08},
|
|
{0x5440,0x4000,0x400,0x40,0x1000}};
|
|
switch(event->type) {
|
|
case SDL_JOYAXISMOTION:
|
|
jaxis = &event->jaxis;
|
|
if((unsigned int)jaxis->which == (unsigned int)stick && jaxis->axis < 4) {
|
|
if(jaxis->axis & 1)
|
|
JOYSTICK_Move_Y(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
|
|
else
|
|
JOYSTICK_Move_X(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
|
|
}
|
|
break;
|
|
case SDL_JOYHATMOTION:
|
|
jhat = &event->jhat;
|
|
if((unsigned int)jhat->which == (unsigned int)stick && jhat->hat < 2) {
|
|
if(jhat->value == SDL_HAT_CENTERED)
|
|
button_state&=~hat_magic[jhat->hat][0];
|
|
if(jhat->value & SDL_HAT_UP)
|
|
button_state|=hat_magic[jhat->hat][1];
|
|
if(jhat->value & SDL_HAT_RIGHT)
|
|
button_state|=hat_magic[jhat->hat][2];
|
|
if(jhat->value & SDL_HAT_DOWN)
|
|
button_state|=hat_magic[jhat->hat][3];
|
|
if(jhat->value & SDL_HAT_LEFT)
|
|
button_state|=hat_magic[jhat->hat][4];
|
|
}
|
|
break;
|
|
case SDL_JOYBUTTONDOWN:
|
|
jbutton = &event->jbutton;
|
|
but = jbutton->button % emulated_buttons;
|
|
if ((unsigned int)jbutton->which == (unsigned int)stick)
|
|
button_state|=button_magic[but];
|
|
break;
|
|
case SDL_JOYBUTTONUP:
|
|
jbutton = &event->jbutton;
|
|
but = jbutton->button % emulated_buttons;
|
|
if ((unsigned int)jbutton->which == (unsigned int)stick)
|
|
button_state&=~button_magic[but];
|
|
break;
|
|
}
|
|
|
|
unsigned i;
|
|
uint16_t j;
|
|
j=button_state;
|
|
for(i=0;i<16;i++) if (j & 1) break; else j>>=1;
|
|
JOYSTICK_Button(0,0,i&1);
|
|
JOYSTICK_Button(0,1,(i>>1)&1);
|
|
JOYSTICK_Button(1,0,(i>>2)&1);
|
|
JOYSTICK_Button(1,1,(i>>3)&1);
|
|
return false;
|
|
}
|
|
|
|
void UpdateJoystick() override {
|
|
static unsigned const button_priority[6]={7,11,13,14,5,6};
|
|
static unsigned const hat_priority[2][4]={{0,1,2,3},{8,9,10,12}};
|
|
|
|
/* query SDL joystick and activate bindings */
|
|
ActivateJoystickBoundEvents();
|
|
|
|
auto v1 = GetJoystickVector(0, 0, 0, 1);
|
|
auto v2 = GetJoystickVector(0, 1, 2, 3);
|
|
JOYSTICK_Move_X(0, v1.X);
|
|
JOYSTICK_Move_X(0, v1.Y);
|
|
JOYSTICK_Move_X(1, v2.X);
|
|
JOYSTICK_Move_X(1, v2.Y);
|
|
|
|
Bitu bt_state=15;
|
|
|
|
Bitu i;
|
|
for (i=0; i<(hats<2?hats:2); i++) {
|
|
Uint8 hat_pos=0;
|
|
if (virtual_joysticks[0].hat_pressed[(i<<2)+0]) hat_pos|=SDL_HAT_UP;
|
|
else if (virtual_joysticks[0].hat_pressed[(i<<2)+2]) hat_pos|=SDL_HAT_DOWN;
|
|
if (virtual_joysticks[0].hat_pressed[(i<<2)+3]) hat_pos|=SDL_HAT_LEFT;
|
|
else if (virtual_joysticks[0].hat_pressed[(i<<2)+1]) hat_pos|=SDL_HAT_RIGHT;
|
|
|
|
if (hat_pos & SDL_HAT_UP)
|
|
if (bt_state>hat_priority[i][0]) bt_state=hat_priority[i][0];
|
|
if (hat_pos & SDL_HAT_DOWN)
|
|
if (bt_state>hat_priority[i][1]) bt_state=hat_priority[i][1];
|
|
if (hat_pos & SDL_HAT_RIGHT)
|
|
if (bt_state>hat_priority[i][2]) bt_state=hat_priority[i][2];
|
|
if (hat_pos & SDL_HAT_LEFT)
|
|
if (bt_state>hat_priority[i][3]) bt_state=hat_priority[i][3];
|
|
}
|
|
|
|
bool button_pressed[MAXBUTTON];
|
|
for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
|
|
for (i=0; i<MAX_VJOY_BUTTONS; i++) {
|
|
if (virtual_joysticks[0].button_pressed[i])
|
|
button_pressed[i % button_wrap]=true;
|
|
|
|
}
|
|
for (i=0; i<6; i++) {
|
|
if ((button_pressed[i]) && (bt_state>button_priority[i]))
|
|
bt_state=button_priority[i];
|
|
}
|
|
|
|
if (bt_state>15) bt_state=15;
|
|
JOYSTICK_Button(0,0,(bt_state&8)==0);
|
|
JOYSTICK_Button(0,1,(bt_state&4)==0);
|
|
JOYSTICK_Button(1,0,(bt_state&2)==0);
|
|
JOYSTICK_Button(1,1,(bt_state&1)==0);
|
|
}
|
|
|
|
protected:
|
|
uint16_t button_state;
|
|
};
|
|
|
|
void CBindGroup::ActivateBindList(CBindList * list,Bits value,bool ev_trigger) {
|
|
assert(list);
|
|
Bitu validmod=0;
|
|
CBindList_it it;
|
|
for (it=list->begin();it!=list->end();++it) {
|
|
if (((*it)->mods & mapper.mods) == (*it)->mods) {
|
|
if (validmod<(*it)->mods) validmod=(*it)->mods;
|
|
}
|
|
}
|
|
for (it=list->begin();it!=list->end();++it) {
|
|
if ((*it)->mods==MMODHOST) {
|
|
if ((!hostkeyalt&&validmod==(*it)->mods)||(hostkeyalt==1&&(sdl.lctrlstate==SDL_KEYDOWN||sdl.rctrlstate==SDL_KEYDOWN)&&(sdl.laltstate==SDL_KEYDOWN||sdl.raltstate==SDL_KEYDOWN))||(hostkeyalt==2&&(sdl.lctrlstate==SDL_KEYDOWN||sdl.rctrlstate==SDL_KEYDOWN)&&(sdl.lshiftstate==SDL_KEYDOWN||sdl.rshiftstate==SDL_KEYDOWN))||(hostkeyalt==3&&(sdl.laltstate==SDL_KEYDOWN||sdl.raltstate==SDL_KEYDOWN)&&(sdl.lshiftstate==SDL_KEYDOWN||sdl.rshiftstate==SDL_KEYDOWN))) {
|
|
if (hostkeyalt != 0) /* only IF using an alternate host key */
|
|
(*it)->flags|=BFLG_Hold_Temporary;
|
|
|
|
(*it)->ActivateBind(value,ev_trigger);
|
|
}
|
|
} else if (validmod==(*it)->mods)
|
|
(*it)->ActivateBind(value,ev_trigger);
|
|
}
|
|
}
|
|
|
|
void CBindGroup::DeactivateBindList(CBindList * list,bool ev_trigger) {
|
|
assert(list);
|
|
CBindList_it it;
|
|
for (it=list->begin();it!=list->end();++it) {
|
|
(*it)->flags&=~BFLG_Hold_Temporary;
|
|
(*it)->DeActivateBind(ev_trigger);
|
|
}
|
|
}
|
|
|
|
class CButton {
|
|
public:
|
|
virtual ~CButton(){};
|
|
CButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy) {
|
|
x=_x;y=_y;dx=_dx;dy=_dy;
|
|
buttons.push_back(this);
|
|
bkcolor=CLR_BLACK;
|
|
color=CLR_WHITE;
|
|
enabled=true;
|
|
invert=false;
|
|
press=false;
|
|
page=1;
|
|
SetCanClick(true);
|
|
}
|
|
virtual void Draw(bool background, bool border) {
|
|
uint8_t bg;
|
|
|
|
if (!enabled) return;
|
|
|
|
if (!invert)
|
|
bg = press ? uint8_t(CLR_DARKGREEN) : bkcolor;
|
|
else
|
|
bg = color;
|
|
|
|
#if defined(C_SDL2)
|
|
uint8_t * point=((uint8_t *)mapper.draw_surface->pixels)+(y*mapper.draw_surface->w)+x;
|
|
#else
|
|
uint8_t * point=((uint8_t *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x;
|
|
#endif
|
|
for (Bitu lines=0;lines<dy;lines++) {
|
|
if (lines==0 || lines==(dy-1)) {
|
|
if (border)
|
|
{
|
|
for (Bitu cols=0;cols<dx;cols++)
|
|
{
|
|
*(point+cols)=color;
|
|
}
|
|
}
|
|
} else {
|
|
if (background)
|
|
{
|
|
for (Bitu cols=1;cols<(dx-1);cols++)
|
|
{
|
|
*(point+cols)=bg;
|
|
}
|
|
}
|
|
|
|
if (border)
|
|
{
|
|
*point=color;
|
|
*(point+dx-1)=color;
|
|
}
|
|
}
|
|
#if defined(C_SDL2)
|
|
point+=mapper.draw_surface->w;
|
|
#else
|
|
point+=mapper.surface->pitch;
|
|
#endif
|
|
}
|
|
}
|
|
virtual void Draw(void) {
|
|
Draw(true, true);
|
|
}
|
|
virtual bool OnTop(Bitu _x,Bitu _y) {
|
|
return ( enabled && (_x>=x) && (_x<x+dx) && (_y>=y) && (_y<y+dy));
|
|
}
|
|
virtual void BindColor(void) {}
|
|
|
|
virtual void Click(void)
|
|
{
|
|
if(clickable)
|
|
{
|
|
ClickImpl();
|
|
}
|
|
}
|
|
|
|
virtual void ClickImpl(void) {}
|
|
|
|
bool GetCanClick(void)
|
|
{
|
|
return clickable;
|
|
}
|
|
|
|
void SetCanClick(bool value)
|
|
{
|
|
clickable = value;
|
|
SetColor(value ? CLR_WHITE : CLR_GREY); // TODO don't hardcode
|
|
}
|
|
|
|
uint8_t Page(uint8_t p) {
|
|
if (p>0) page=p;
|
|
return page;
|
|
}
|
|
void Enable(bool yes) {
|
|
enabled=yes;
|
|
mapper.redraw=true;
|
|
}
|
|
void SetInvert(bool inv) {
|
|
invert=inv;
|
|
mapper.redraw=true;
|
|
}
|
|
void SetPress(bool p) {
|
|
press=p;
|
|
mapper.redraw=true;
|
|
}
|
|
virtual void RebindRedraw(void) {}
|
|
void SetColor(uint8_t _col) { color=_col; }
|
|
protected:
|
|
Bitu x,y,dx,dy;
|
|
uint8_t page;
|
|
uint8_t color;
|
|
uint8_t bkcolor;
|
|
bool press;
|
|
bool invert;
|
|
bool enabled;
|
|
bool clickable;
|
|
|
|
void DrawTextAuto(const char* text, bool centered, uint8_t foreground, uint8_t background)
|
|
{
|
|
const auto size = strlen(text);
|
|
const auto wide = dx / 8;
|
|
const auto data = dx > 0 && size > wide ? std::string(text, wide - 3) + std::string("...") : std::string(text);
|
|
|
|
if(centered)
|
|
{
|
|
const auto size = data.size();
|
|
const auto xPos = std::max(x, (Bitu) (x + dx / 2 - size * 8 / 2));
|
|
const auto yPos = std::max(y, y + dy / 2 - 14 / 2);
|
|
DrawText(1 + xPos, yPos, data.c_str(), foreground, background);
|
|
}
|
|
else
|
|
{
|
|
DrawText(x + 2, y + 2, data.c_str(), foreground, background);
|
|
}
|
|
}
|
|
};
|
|
|
|
static CButton *press_select = NULL;
|
|
|
|
class CTextButton : public CButton {
|
|
public:
|
|
CTextButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text) : CButton(_x,_y,_dx,_dy) {
|
|
if (strlen(_text)<100) strcpy(text, _text);
|
|
else {strncpy(text, _text, 99);text[99]=0;}
|
|
invertw=0;
|
|
}
|
|
~CTextButton() {}
|
|
void Draw(void) override {
|
|
uint8_t fg,bg;
|
|
|
|
if (!enabled) return;
|
|
|
|
if (!invert) {
|
|
fg = color;
|
|
bg = press ? uint8_t(CLR_DARKGREEN) : bkcolor;
|
|
}
|
|
else {
|
|
fg = bkcolor;
|
|
bg = color;
|
|
}
|
|
|
|
CButton::Draw();
|
|
|
|
DrawTextAuto(text, true, fg, bg); // TODO add the ability to center text for this class
|
|
|
|
#if defined(C_SDL2)
|
|
uint8_t * point=((uint8_t *)mapper.draw_surface->pixels)+(y*mapper.draw_surface->w)+x;
|
|
#else
|
|
uint8_t * point=((uint8_t *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x;
|
|
#endif
|
|
for (Bitu lines=0;lines<(dy-1);lines++) {
|
|
if (lines != 0) {
|
|
for (Bitu cols=1;cols<=invertw;cols++) {
|
|
if (*(point+cols) == color)
|
|
*(point+cols) = bkcolor;
|
|
else
|
|
*(point+cols) = color;
|
|
}
|
|
}
|
|
#if defined(C_SDL2)
|
|
point+=mapper.draw_surface->w;
|
|
#else
|
|
point+=mapper.surface->pitch;
|
|
#endif
|
|
// draw the borders a second time to prevent the text from overwriting them
|
|
CButton::Draw(false, true);
|
|
}
|
|
}
|
|
void SetText(const char *_text) {
|
|
if (strlen(_text)<100) strcpy(text, _text);
|
|
else {strncpy(text, _text, 99);text[99]=0;}
|
|
}
|
|
void SetPartialInvert(double a) {
|
|
if (a < 0) a = 0;
|
|
if (a > 1) a = 1;
|
|
invertw = (Bitu)floor((a * (dx - 2)) + 0.5);
|
|
if (invertw > (dx - 2)) invertw = dx - 2;
|
|
mapper.redraw=true;
|
|
}
|
|
protected:
|
|
char text[100];
|
|
Bitu invertw;
|
|
};
|
|
|
|
class CEventButton;
|
|
static CEventButton * hostbutton = NULL;
|
|
static CEventButton * last_clicked = NULL;
|
|
static std::vector<CEventButton *> ceventbuttons;
|
|
|
|
class CEventButton : public CTextButton {
|
|
public:
|
|
CEventButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,CEvent * _event)
|
|
: CTextButton(_x,_y,_dx,_dy,_text) {
|
|
event=_event;
|
|
}
|
|
~CEventButton() {}
|
|
void BindColor(void) override {
|
|
this->SetColor(event->bindlist.begin() == event->bindlist.end() ? CLR_GREY : CLR_WHITE);
|
|
}
|
|
void ClickImpl(void) override {
|
|
if (last_clicked) last_clicked->BindColor();
|
|
this->SetColor(event->bindlist.begin() == event->bindlist.end() ? CLR_DARKGREEN : CLR_GREEN);
|
|
SetActiveEvent(event);
|
|
last_clicked=this;
|
|
}
|
|
CEvent *GetEvent() {
|
|
return event;
|
|
}
|
|
void RebindRedraw(void) override {
|
|
Click();//HACK!
|
|
}
|
|
protected:
|
|
CEvent * event;
|
|
};
|
|
|
|
class CCaptionButton : public CButton {
|
|
public:
|
|
CCaptionButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy, bool centered) : CButton(_x,_y,_dx,_dy){
|
|
caption[0]=0;
|
|
center = centered;
|
|
}
|
|
virtual ~CCaptionButton() {}
|
|
void Change(const char * format,...) GCC_ATTRIBUTE(__format__(__printf__,2,3));
|
|
|
|
void Draw(void) override
|
|
{
|
|
if(!enabled)
|
|
return;
|
|
|
|
DrawTextAuto(caption, center, color, CLR_BLACK);
|
|
}
|
|
|
|
protected:
|
|
char caption[128];
|
|
private:
|
|
bool center;
|
|
};
|
|
|
|
void CCaptionButton::Change(const char * format,...) {
|
|
va_list msg;
|
|
va_start(msg,format);
|
|
vsprintf(caption,format,msg);
|
|
va_end(msg);
|
|
mapper.redraw=true;
|
|
}
|
|
|
|
void RedrawMapperBindButton(CEvent *ev), RedrawMapperEventButtons();
|
|
class CBindButton : public CTextButton {
|
|
public:
|
|
CBindButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,BB_Types _type)
|
|
: CTextButton(_x,_y,_dx,_dy,_text) {
|
|
type=_type;
|
|
}
|
|
~CBindButton() {}
|
|
void ClickImpl(void) override {
|
|
switch (type) {
|
|
case BB_Add:
|
|
mapper.addbind=true;
|
|
SetActiveBind(nullptr);
|
|
change_action_text(MSG_Get("PRESS_JOYSTICK_KEY"),CLR_RED);
|
|
break;
|
|
case BB_Del:
|
|
assert(mapper.aevent != NULL);
|
|
if (mapper.abindit!=mapper.aevent->bindlist.end()) {
|
|
delete (*mapper.abindit);
|
|
mapper.abindit=mapper.aevent->bindlist.erase(mapper.abindit);
|
|
if (mapper.abindit==mapper.aevent->bindlist.end())
|
|
mapper.abindit=mapper.aevent->bindlist.begin();
|
|
}
|
|
if (mapper.abindit!=mapper.aevent->bindlist.end()) SetActiveBind(*(mapper.abindit));
|
|
else SetActiveBind(nullptr);
|
|
RedrawMapperBindButton(mapper.aevent);
|
|
break;
|
|
case BB_Next:
|
|
assert(mapper.aevent != NULL);
|
|
if (mapper.abindit!=mapper.aevent->bindlist.end())
|
|
++mapper.abindit;
|
|
if (mapper.abindit==mapper.aevent->bindlist.end())
|
|
mapper.abindit=mapper.aevent->bindlist.begin();
|
|
SetActiveBind(*(mapper.abindit));
|
|
break;
|
|
case BB_Prevpage:
|
|
if (cpage<2) break;
|
|
cpage--;
|
|
RedrawMapperEventButtons();
|
|
break;
|
|
case BB_Nextpage:
|
|
if (cpage>=maxpage) break;
|
|
cpage++;
|
|
RedrawMapperEventButtons();
|
|
break;
|
|
case BB_Save:
|
|
MAPPER_SaveBinds();
|
|
break;
|
|
case BB_Exit:
|
|
mapper.exit=true;
|
|
break;
|
|
case BB_Capture:
|
|
GFX_CaptureMouse();
|
|
if (mouselocked) change_action_text(MSG_Get("CAPTURE_ENABLED"),CLR_WHITE);
|
|
break;
|
|
}
|
|
}
|
|
protected:
|
|
BB_Types type;
|
|
};
|
|
|
|
//! \brief Modifier trigger event, for modifier keys. This permits the user to change modifier key bindings.
|
|
class CModEvent : public CTriggeredEvent {
|
|
public:
|
|
//! \brief Constructor to provide entry name and the index of the modifier button
|
|
CModEvent(char const * const _entry,Bitu _wmod) : CTriggeredEvent(_entry), notify_button(NULL) {
|
|
wmod=_wmod;
|
|
type = wmod==4?event_t:mod_event_t;
|
|
}
|
|
|
|
~CModEvent() {}
|
|
|
|
void Active(bool yesno) override {
|
|
if (notify_button != NULL)
|
|
notify_button->SetInvert(yesno);
|
|
|
|
if (yesno) mapper.mods|=((Bitu)1u << (wmod-1u));
|
|
else mapper.mods&=~((Bitu)1u << (wmod-1u));
|
|
};
|
|
|
|
//! \brief Associate this object with a text button in the mapper UI
|
|
void notifybutton(CTextButton *n) {
|
|
notify_button = n;
|
|
}
|
|
|
|
void RebindRedraw(void) override {
|
|
if (notify_button != NULL)
|
|
notify_button->RebindRedraw();
|
|
}
|
|
|
|
//! \brief Mapper UI text button to indicate status by
|
|
CTextButton *notify_button;
|
|
protected:
|
|
//! \brief Modifier button index
|
|
Bitu wmod;
|
|
};
|
|
|
|
class CCheckButton : public CTextButton {
|
|
public:
|
|
CCheckButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,BC_Types _type)
|
|
: CTextButton(_x,_y,_dx,_dy,_text) {
|
|
type=_type;
|
|
}
|
|
~CCheckButton() {}
|
|
void Draw(void) override {
|
|
if (!enabled) return;
|
|
bool checked=false;
|
|
std::string str = "";
|
|
switch (type) {
|
|
case BC_Mod1:
|
|
checked=(mapper.abind->mods&BMOD_Mod1)>0;
|
|
str = checked && mod_event[1] != NULL ? mod_event[1]->GetBindMenuText() : LabelMod1;
|
|
strcpy(text, str.size()>8?LabelMod1:str.c_str());
|
|
break;
|
|
case BC_Mod2:
|
|
checked=(mapper.abind->mods&BMOD_Mod2)>0;
|
|
str = checked && mod_event[2] != NULL ? mod_event[2]->GetBindMenuText() : LabelMod2;
|
|
strcpy(text, str.size()>8?LabelMod2:str.c_str());
|
|
break;
|
|
case BC_Mod3:
|
|
checked=(mapper.abind->mods&BMOD_Mod3)>0;
|
|
str = checked && mod_event[3] != NULL ? mod_event[3]->GetBindMenuText() : LabelMod3;
|
|
strcpy(text, str.size()>8?LabelMod3:str.c_str());
|
|
break;
|
|
case BC_Host:
|
|
checked=(mapper.abind->mods&BMOD_Host)>0;
|
|
break;
|
|
case BC_Hold:
|
|
checked=(mapper.abind->flags&BFLG_Hold)>0;
|
|
break;
|
|
}
|
|
CTextButton::Draw();
|
|
if (checked) {
|
|
#if defined(C_SDL2)
|
|
uint8_t * point=((uint8_t *)mapper.draw_surface->pixels)+((y+2)*mapper.draw_surface->pitch)+x+dx-dy+2;
|
|
#else
|
|
uint8_t * point=((uint8_t *)mapper.surface->pixels)+((y+2)*mapper.surface->pitch)+x+dx-dy+2;
|
|
#endif
|
|
for (Bitu lines=0;lines<(dy-4);lines++) {
|
|
memset(point,color,dy-4);
|
|
#if defined(C_SDL2)
|
|
point+=mapper.draw_surface->w;
|
|
#else
|
|
point+=mapper.surface->pitch;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
void ClickImpl(void) override {
|
|
switch (type) {
|
|
case BC_Mod1:
|
|
mapper.abind->mods^=BMOD_Mod1;
|
|
break;
|
|
case BC_Mod2:
|
|
mapper.abind->mods^=BMOD_Mod2;
|
|
break;
|
|
case BC_Mod3:
|
|
mapper.abind->mods^=BMOD_Mod3;
|
|
break;
|
|
case BC_Host:
|
|
mapper.abind->mods^=BMOD_Host;
|
|
break;
|
|
case BC_Hold:
|
|
mapper.abind->flags^=BFLG_Hold;
|
|
break;
|
|
}
|
|
mapper.redraw=true;
|
|
}
|
|
protected:
|
|
BC_Types type;
|
|
};
|
|
|
|
//! \brief Keyboard key trigger event
|
|
class CKeyEvent : public CTriggeredEvent {
|
|
public:
|
|
//! \brief Constructor to specify mapper event, and KBD_* key enumeration constant
|
|
CKeyEvent(char const * const _entry,KBD_KEYS _key) : CTriggeredEvent(_entry), notify_button(NULL) {
|
|
key=_key;
|
|
}
|
|
|
|
~CKeyEvent() {}
|
|
|
|
void Active(bool yesno) override {
|
|
if (MAPPER_DemoOnly()) {
|
|
if (notify_button != NULL)
|
|
notify_button->SetInvert(yesno);
|
|
}
|
|
else {
|
|
KEYBOARD_AddKey(key,yesno);
|
|
}
|
|
|
|
active=yesno;
|
|
};
|
|
|
|
//! \brief Associate this object with a text button in the mapper UI
|
|
void notifybutton(CTextButton *n) {
|
|
notify_button = n;
|
|
}
|
|
|
|
void RebindRedraw(void) override {
|
|
if (notify_button != NULL)
|
|
notify_button->RebindRedraw();
|
|
}
|
|
|
|
//! \brief Text button in the mapper UI to indicate our status by
|
|
CTextButton *notify_button;
|
|
|
|
//! \brief KBD_* key enumeration value to transmit to keyboard emulation
|
|
KBD_KEYS key;
|
|
};
|
|
|
|
class CMouseButtonEvent : public CTriggeredEvent {
|
|
public:
|
|
CMouseButtonEvent(char const * const _entry,uint8_t _button) : CTriggeredEvent(_entry) {
|
|
button=_button;
|
|
notify_button=NULL;
|
|
}
|
|
void Active(bool yesno) override {
|
|
if (yesno)
|
|
Mouse_ButtonPressed(button);
|
|
else
|
|
Mouse_ButtonReleased(button);
|
|
}
|
|
//! \brief Associate this object with a text button in the mapper UI
|
|
void notifybutton(CTextButton *n) {
|
|
notify_button = n;
|
|
}
|
|
|
|
void RebindRedraw(void) override {
|
|
if (notify_button != NULL)
|
|
notify_button->RebindRedraw();
|
|
}
|
|
|
|
//! \brief Text button in the mapper UI to indicate our status by
|
|
CTextButton *notify_button;
|
|
|
|
uint8_t button;
|
|
};
|
|
|
|
//! \brief Joystick axis event handling for the mapper
|
|
class CJAxisEvent : public CContinuousEvent {
|
|
public:
|
|
//! \brief Constructor, to describe entry, joystick, axis, direction, and the opposing axis
|
|
CJAxisEvent(char const * const _entry,Bitu _stick,Bitu _axis,bool _positive,CJAxisEvent * _opposite_axis) : CContinuousEvent(_entry) {
|
|
notify_button=NULL;
|
|
stick=_stick;
|
|
axis=_axis;
|
|
positive=_positive;
|
|
opposite_axis=_opposite_axis;
|
|
if (_opposite_axis) {
|
|
_opposite_axis->SetOppositeAxis(this);
|
|
}
|
|
}
|
|
|
|
~CJAxisEvent() {}
|
|
|
|
void Active(bool /*moved*/) override {
|
|
if (notify_button != NULL)
|
|
notify_button->SetPartialInvert(GetValue()/32768.0);
|
|
|
|
virtual_joysticks[stick].axis_pos[axis]=(int16_t)(GetValue()*(positive?1:-1));
|
|
}
|
|
|
|
Bitu GetActivityCount(void) override {
|
|
return activity|opposite_axis->activity;
|
|
}
|
|
|
|
void RepostActivity(void) override {
|
|
/* caring for joystick movement into the opposite direction */
|
|
opposite_axis->Active(true);
|
|
}
|
|
|
|
//! \brief Associate this object with a text button in the mapper GUI so that joystick position can be displayed at all times
|
|
void notifybutton(CTextButton *n) {
|
|
notify_button = n;
|
|
}
|
|
|
|
void RebindRedraw(void) override {
|
|
if (notify_button != NULL)
|
|
notify_button->RebindRedraw();
|
|
}
|
|
|
|
//! \brief Text button to use to display joystick position
|
|
CTextButton *notify_button;
|
|
protected:
|
|
//! \brief Associate this object with the opposing joystick axis
|
|
void SetOppositeAxis(CJAxisEvent * _opposite_axis) {
|
|
opposite_axis=_opposite_axis;
|
|
}
|
|
|
|
//! \brief Joystick to follow
|
|
Bitu stick;
|
|
|
|
//! \brief Joystick axis to track
|
|
Bitu axis;
|
|
|
|
//! \brief Whether joystick axis is positive or negative
|
|
bool positive;
|
|
|
|
//! \brief Opposing joystick axis object
|
|
CJAxisEvent * opposite_axis;
|
|
};
|
|
|
|
//! \brief Joystick button trigger
|
|
class CJButtonEvent : public CTriggeredEvent {
|
|
public:
|
|
//! \brief Constructor, describing mapper event, joystick, and which button
|
|
CJButtonEvent(char const * const _entry,Bitu _stick,Bitu _button) : CTriggeredEvent(_entry) {
|
|
stick=_stick;
|
|
button=_button;
|
|
notify_button=NULL;
|
|
}
|
|
|
|
~CJButtonEvent() {}
|
|
|
|
void Active(bool pressed) override {
|
|
if (notify_button != NULL)
|
|
notify_button->SetInvert(pressed);
|
|
|
|
virtual_joysticks[stick].button_pressed[button]=pressed;
|
|
active=pressed;
|
|
}
|
|
|
|
//! \brief Associate this object with a text button in the mapper UI
|
|
void notifybutton(CTextButton *n) {
|
|
notify_button = n;
|
|
}
|
|
|
|
void RebindRedraw(void) override {
|
|
if (notify_button != NULL)
|
|
notify_button->RebindRedraw();
|
|
}
|
|
|
|
//! \brief Text button in the mapper UI to indicate our status by
|
|
CTextButton *notify_button;
|
|
protected:
|
|
//! \brief Which joystick
|
|
Bitu stick;
|
|
|
|
//! \brief Which button
|
|
Bitu button;
|
|
};
|
|
|
|
//! \brief Joystick hat event
|
|
class CJHatEvent : public CTriggeredEvent {
|
|
public:
|
|
//! \brief Constructor to describe mapper event, joystick, hat, and direction
|
|
CJHatEvent(char const * const _entry,Bitu _stick,Bitu _hat,Bitu _dir) : CTriggeredEvent(_entry) {
|
|
stick=_stick;
|
|
hat=_hat;
|
|
dir=_dir;
|
|
notify_button = NULL;
|
|
}
|
|
|
|
~CJHatEvent() {}
|
|
|
|
void Active(bool pressed) override {
|
|
if (notify_button != NULL)
|
|
notify_button->SetInvert(pressed);
|
|
virtual_joysticks[stick].hat_pressed[(hat<<2)+dir]=pressed;
|
|
}
|
|
void notifybutton(CTextButton *n)
|
|
{
|
|
notify_button = n;
|
|
}
|
|
|
|
void RebindRedraw(void) override {
|
|
if (notify_button != NULL)
|
|
notify_button->RebindRedraw();
|
|
}
|
|
|
|
CTextButton *notify_button;
|
|
protected:
|
|
//! \brief Which joystick
|
|
Bitu stick;
|
|
|
|
//! \brief Which hat
|
|
Bitu hat;
|
|
|
|
//! \brief Direction of hat
|
|
Bitu dir;
|
|
};
|
|
|
|
std::string CBind::GetModifierText(void) {
|
|
std::string r,t;
|
|
|
|
for (size_t m=4u/*Host key first*/;m >= 1u;m--) {
|
|
if ((mods & ((Bitu)1u << (m - 1u))) && mod_event[m] != NULL) {
|
|
t = mod_event[m]->GetBindMenuText();
|
|
if (!r.empty()) r += "+";
|
|
r += m==4?(hostkeyalt==1?"Ctrl+Alt":(hostkeyalt==2?"Ctrl+Shift":(hostkeyalt==3?"Alt+Shift":t))):t;
|
|
}
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
//! \brief Mapper shortcut event. Keyboard triggerable only.
|
|
class CHandlerEvent : public CTriggeredEvent {
|
|
public:
|
|
//! \brief Constructor, to specify the entry, handler (callback), key (according to MapKeys enumeration), and text to display for the shortcut in the mapper UO
|
|
CHandlerEvent(char const * const _entry,MAPPER_Handler * _handler,MapKeys _key,Bitu _mod,char const * const _buttonname) : CTriggeredEvent(_entry), notify_button(NULL) {
|
|
handler=_handler;
|
|
defmod=_mod;
|
|
defkey=_key;
|
|
if (strlen(_buttonname)<100) strcpy(buttonname, _buttonname);
|
|
else {strncpy(buttonname, _buttonname, 99);buttonname[99]=0;}
|
|
handlergroup.push_back(this);
|
|
type = handler_event_t;
|
|
}
|
|
|
|
~CHandlerEvent() {}
|
|
|
|
void RebindRedraw(void) override {
|
|
if (notify_button != NULL)
|
|
notify_button->RebindRedraw();
|
|
}
|
|
|
|
void Active(bool yesno) override {
|
|
if (MAPPER_DemoOnly()) {
|
|
if (notify_button != NULL)
|
|
notify_button->SetInvert(yesno);
|
|
}
|
|
else {
|
|
(*handler)(yesno);
|
|
}
|
|
|
|
active=yesno;
|
|
};
|
|
|
|
//! \brief Retrieve the button name (for display in the mapper UI)
|
|
const char * ButtonName() {
|
|
return buttonname;
|
|
}
|
|
|
|
void SetButtonName(const char *name) {
|
|
if (strlen(name)<100) strcpy(buttonname, name);
|
|
else {strncpy(buttonname, name, 99);buttonname[99]=0;}
|
|
}
|
|
|
|
//! \brief Generate a default binding from the MapKeys enumeration
|
|
#if defined(C_SDL2)
|
|
void MakeDefaultBind(char * buf) {
|
|
Bitu key=0;
|
|
switch (defkey) {
|
|
case MK_nothing: *buf = 0; return;
|
|
case MK_f1:case MK_f2:case MK_f3:case MK_f4:
|
|
case MK_f5:case MK_f6:case MK_f7:case MK_f8:
|
|
case MK_f9:case MK_f10:case MK_f11:case MK_f12:
|
|
key=SDL_SCANCODE_F1+(defkey-MK_f1);
|
|
break;
|
|
case MK_rightarrow:
|
|
key=SDL_SCANCODE_RIGHT;
|
|
break;
|
|
case MK_leftarrow:
|
|
key=SDL_SCANCODE_LEFT;
|
|
break;
|
|
case MK_uparrow:
|
|
key=SDL_SCANCODE_UP;
|
|
break;
|
|
case MK_downarrow:
|
|
key=SDL_SCANCODE_DOWN;
|
|
break;
|
|
case MK_return:
|
|
key=SDL_SCANCODE_RETURN;
|
|
break;
|
|
case MK_tab:
|
|
key=SDL_SCANCODE_TAB;
|
|
break;
|
|
case MK_slash:
|
|
key=SDL_SCANCODE_SLASH;
|
|
break;
|
|
case MK_backslash:
|
|
key=SDL_SCANCODE_BACKSLASH;
|
|
break;
|
|
case MK_space:
|
|
key=SDL_SCANCODE_SPACE;
|
|
break;
|
|
case MK_backspace:
|
|
key=SDL_SCANCODE_BACKSPACE;
|
|
break;
|
|
case MK_delete:
|
|
key=SDL_SCANCODE_DELETE;
|
|
break;
|
|
case MK_insert:
|
|
key=SDL_SCANCODE_INSERT;
|
|
break;
|
|
case MK_semicolon:
|
|
key=SDL_SCANCODE_SEMICOLON;
|
|
break;
|
|
case MK_quote:
|
|
key=SDL_SCANCODE_APOSTROPHE;
|
|
break;
|
|
case MK_grave:
|
|
key=SDL_SCANCODE_GRAVE;
|
|
break;
|
|
case MK_kpminus:
|
|
key=SDL_SCANCODE_KP_MINUS;
|
|
break;
|
|
case MK_kpplus:
|
|
key=SDL_SCANCODE_KP_PLUS;
|
|
break;
|
|
case MK_minus:
|
|
key=SDL_SCANCODE_MINUS;
|
|
break;
|
|
case MK_equals:
|
|
key=SDL_SCANCODE_EQUALS;
|
|
break;
|
|
case MK_scrolllock:
|
|
key=SDL_SCANCODE_SCROLLLOCK;
|
|
break;
|
|
case MK_pause:
|
|
key=SDL_SCANCODE_PAUSE;
|
|
break;
|
|
case MK_printscreen:
|
|
key=SDL_SCANCODE_PRINTSCREEN;
|
|
break;
|
|
case MK_home:
|
|
key=SDL_SCANCODE_HOME;
|
|
break;
|
|
case MK_end:
|
|
key=SDL_SCANCODE_END;
|
|
break;
|
|
case MK_pageup:
|
|
key=SDL_SCANCODE_PAGEUP;
|
|
break;
|
|
case MK_pagedown:
|
|
key=SDL_SCANCODE_PAGEDOWN;
|
|
break;
|
|
case MK_comma:
|
|
key=SDL_SCANCODE_COMMA;
|
|
break;
|
|
case MK_period:
|
|
key=SDL_SCANCODE_PERIOD;
|
|
break;
|
|
case MK_0:
|
|
key=SDL_SCANCODE_0;
|
|
break;
|
|
case MK_1:
|
|
key=SDL_SCANCODE_1;
|
|
break;
|
|
case MK_2:
|
|
key=SDL_SCANCODE_2;
|
|
break;
|
|
case MK_3:
|
|
key=SDL_SCANCODE_3;
|
|
break;
|
|
case MK_4:
|
|
key=SDL_SCANCODE_4;
|
|
break;
|
|
case MK_5:
|
|
key=SDL_SCANCODE_5;
|
|
break;
|
|
case MK_6:
|
|
key=SDL_SCANCODE_6;
|
|
break;
|
|
case MK_7:
|
|
key=SDL_SCANCODE_7;
|
|
break;
|
|
case MK_8:
|
|
key=SDL_SCANCODE_8;
|
|
break;
|
|
case MK_9:
|
|
key=SDL_SCANCODE_9;
|
|
break;
|
|
case MK_a:
|
|
key=SDL_SCANCODE_A;
|
|
break;
|
|
case MK_b:
|
|
key=SDL_SCANCODE_B;
|
|
break;
|
|
case MK_c:
|
|
key=SDL_SCANCODE_C;
|
|
break;
|
|
case MK_d:
|
|
key=SDL_SCANCODE_D;
|
|
break;
|
|
case MK_e:
|
|
key=SDL_SCANCODE_E;
|
|
break;
|
|
case MK_f:
|
|
key=SDL_SCANCODE_F;
|
|
break;
|
|
case MK_g:
|
|
key=SDL_SCANCODE_G;
|
|
break;
|
|
case MK_h:
|
|
key=SDL_SCANCODE_H;
|
|
break;
|
|
case MK_i:
|
|
key=SDL_SCANCODE_I;
|
|
break;
|
|
case MK_j:
|
|
key=SDL_SCANCODE_J;
|
|
break;
|
|
case MK_k:
|
|
key=SDL_SCANCODE_K;
|
|
break;
|
|
case MK_l:
|
|
key=SDL_SCANCODE_L;
|
|
break;
|
|
case MK_m:
|
|
key=SDL_SCANCODE_M;
|
|
break;
|
|
case MK_n:
|
|
key=SDL_SCANCODE_N;
|
|
break;
|
|
case MK_o:
|
|
key=SDL_SCANCODE_O;
|
|
break;
|
|
case MK_p:
|
|
key=SDL_SCANCODE_P;
|
|
break;
|
|
case MK_q:
|
|
key=SDL_SCANCODE_Q;
|
|
break;
|
|
case MK_r:
|
|
key=SDL_SCANCODE_R;
|
|
break;
|
|
case MK_s:
|
|
key=SDL_SCANCODE_S;
|
|
break;
|
|
case MK_t:
|
|
key=SDL_SCANCODE_T;
|
|
break;
|
|
case MK_u:
|
|
key=SDL_SCANCODE_U;
|
|
break;
|
|
case MK_v:
|
|
key=SDL_SCANCODE_V;
|
|
break;
|
|
case MK_w:
|
|
key=SDL_SCANCODE_W;
|
|
break;
|
|
case MK_x:
|
|
key=SDL_SCANCODE_X;
|
|
break;
|
|
case MK_y:
|
|
key=SDL_SCANCODE_Y;
|
|
break;
|
|
case MK_z:
|
|
key=SDL_SCANCODE_Z;
|
|
break;
|
|
case MK_escape:
|
|
key=SDL_SCANCODE_ESCAPE;
|
|
break;
|
|
case MK_lbracket:
|
|
key=SDL_SCANCODE_LEFTBRACKET;
|
|
break;
|
|
case MK_rbracket:
|
|
key=SDL_SCANCODE_RIGHTBRACKET;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
sprintf(buf,"%s \"key %d%s%s%s%s\"",
|
|
entry,
|
|
(int)key,
|
|
(defmod & 1) ? " mod1" : "",
|
|
(defmod & 2) ? " mod2" : "",
|
|
(defmod & 4) ? " mod3" : "",
|
|
(defmod & 8) ? " host" : ""
|
|
);
|
|
}
|
|
#else
|
|
void MakeDefaultBind(char * buf) {
|
|
Bitu key=0;
|
|
switch (defkey) {
|
|
case MK_nothing: *buf = 0; return;
|
|
case MK_f1:case MK_f2:case MK_f3:case MK_f4:
|
|
case MK_f5:case MK_f6:case MK_f7:case MK_f8:
|
|
case MK_f9:case MK_f10:case MK_f11:case MK_f12:
|
|
key=(Bitu)(SDLK_F1+(defkey-MK_f1));
|
|
break;
|
|
case MK_rightarrow:
|
|
key=SDLK_RIGHT;
|
|
break;
|
|
case MK_leftarrow:
|
|
key=SDLK_LEFT;
|
|
break;
|
|
case MK_uparrow:
|
|
key=SDLK_UP;
|
|
break;
|
|
case MK_downarrow:
|
|
key=SDLK_DOWN;
|
|
break;
|
|
case MK_return:
|
|
key=SDLK_RETURN;
|
|
break;
|
|
case MK_tab:
|
|
key=SDLK_TAB;
|
|
break;
|
|
case MK_slash:
|
|
key=SDLK_SLASH;
|
|
break;
|
|
case MK_backslash:
|
|
key=SDLK_BACKSLASH;
|
|
break;
|
|
case MK_space:
|
|
key=SDLK_SPACE;
|
|
break;
|
|
case MK_backspace:
|
|
key=SDLK_BACKSPACE;
|
|
break;
|
|
case MK_delete:
|
|
key=SDLK_DELETE;
|
|
break;
|
|
case MK_insert:
|
|
key=SDLK_INSERT;
|
|
break;
|
|
case MK_semicolon:
|
|
key=SDLK_SEMICOLON;
|
|
break;
|
|
case MK_quote:
|
|
key=SDLK_QUOTE;
|
|
break;
|
|
case MK_grave:
|
|
key=SDLK_BACKQUOTE;
|
|
break;
|
|
case MK_kpminus:
|
|
key=SDLK_KP_MINUS;
|
|
break;
|
|
case MK_kpplus:
|
|
key=SDLK_KP_PLUS;
|
|
break;
|
|
case MK_minus:
|
|
key=SDLK_MINUS;
|
|
break;
|
|
case MK_equals:
|
|
key=SDLK_EQUALS;
|
|
break;
|
|
case MK_scrolllock:
|
|
key=SDLK_SCROLLOCK;
|
|
break;
|
|
case MK_pause:
|
|
key=SDLK_PAUSE;
|
|
break;
|
|
case MK_printscreen:
|
|
key=SDLK_PRINT;
|
|
break;
|
|
case MK_home:
|
|
key=SDLK_HOME;
|
|
break;
|
|
case MK_end:
|
|
key=SDLK_END;
|
|
break;
|
|
case MK_pageup:
|
|
key=SDLK_PAGEUP;
|
|
break;
|
|
case MK_pagedown:
|
|
key=SDLK_PAGEDOWN;
|
|
break;
|
|
case MK_comma:
|
|
key=SDLK_COMMA;
|
|
break;
|
|
case MK_period:
|
|
key=SDLK_PERIOD;
|
|
break;
|
|
case MK_0:
|
|
key=SDLK_0;
|
|
break;
|
|
case MK_1:
|
|
key=SDLK_1;
|
|
break;
|
|
case MK_2:
|
|
key=SDLK_2;
|
|
break;
|
|
case MK_3:
|
|
key=SDLK_3;
|
|
break;
|
|
case MK_4:
|
|
key=SDLK_4;
|
|
break;
|
|
case MK_5:
|
|
key=SDLK_5;
|
|
break;
|
|
case MK_6:
|
|
key=SDLK_6;
|
|
break;
|
|
case MK_7:
|
|
key=SDLK_7;
|
|
break;
|
|
case MK_8:
|
|
key=SDLK_8;
|
|
break;
|
|
case MK_9:
|
|
key=SDLK_9;
|
|
break;
|
|
case MK_a:
|
|
key=SDLK_a;
|
|
break;
|
|
case MK_b:
|
|
key=SDLK_b;
|
|
break;
|
|
case MK_c:
|
|
key=SDLK_c;
|
|
break;
|
|
case MK_d:
|
|
key=SDLK_d;
|
|
break;
|
|
case MK_e:
|
|
key=SDLK_e;
|
|
break;
|
|
case MK_f:
|
|
key=SDLK_f;
|
|
break;
|
|
case MK_g:
|
|
key=SDLK_g;
|
|
break;
|
|
case MK_h:
|
|
key=SDLK_h;
|
|
break;
|
|
case MK_i:
|
|
key=SDLK_i;
|
|
break;
|
|
case MK_j:
|
|
key=SDLK_j;
|
|
break;
|
|
case MK_k:
|
|
key=SDLK_k;
|
|
break;
|
|
case MK_l:
|
|
key=SDLK_l;
|
|
break;
|
|
case MK_m:
|
|
key=SDLK_m;
|
|
break;
|
|
case MK_n:
|
|
key=SDLK_n;
|
|
break;
|
|
case MK_o:
|
|
key=SDLK_o;
|
|
break;
|
|
case MK_p:
|
|
key=SDLK_p;
|
|
break;
|
|
case MK_q:
|
|
key=SDLK_q;
|
|
break;
|
|
case MK_r:
|
|
key=SDLK_r;
|
|
break;
|
|
case MK_s:
|
|
key=SDLK_s;
|
|
break;
|
|
case MK_t:
|
|
key=SDLK_t;
|
|
break;
|
|
case MK_u:
|
|
key=SDLK_u;
|
|
break;
|
|
case MK_v:
|
|
key=SDLK_v;
|
|
break;
|
|
case MK_w:
|
|
key=SDLK_w;
|
|
break;
|
|
case MK_x:
|
|
key=SDLK_x;
|
|
break;
|
|
case MK_y:
|
|
key=SDLK_y;
|
|
break;
|
|
case MK_z:
|
|
key=SDLK_z;
|
|
break;
|
|
case MK_escape:
|
|
key=SDLK_ESCAPE;
|
|
break;
|
|
case MK_lbracket:
|
|
key=SDLK_LEFTBRACKET;
|
|
break;
|
|
case MK_rbracket:
|
|
key=SDLK_RIGHTBRACKET;
|
|
break;
|
|
default:
|
|
*buf = 0;
|
|
return;
|
|
}
|
|
sprintf(buf,"%s \"key %d%s%s%s%s\"",
|
|
entry,
|
|
(int)key,
|
|
defmod & 1 ? " mod1" : "",
|
|
defmod & 2 ? " mod2" : "",
|
|
defmod & 4 ? " mod3" : "",
|
|
defmod & 8 ? " host" : ""
|
|
);
|
|
}
|
|
#endif
|
|
//! \brief Associate this object with a text button in the mapper UI
|
|
void notifybutton(CTextButton *n) {
|
|
notify_button = n;
|
|
}
|
|
|
|
//! \brief Text button in the mapper UI to indicate status by
|
|
CTextButton *notify_button;
|
|
|
|
//! \brief Mapper handler shortcut
|
|
MAPPER_Handler * handler;
|
|
protected:
|
|
//! \brief MapKeys enumeration for keyboard shortcut
|
|
MapKeys defkey;
|
|
|
|
//! \brief Default modifiers
|
|
Bitu defmod;
|
|
public:
|
|
//! \brief Button name
|
|
char buttonname[100];
|
|
};
|
|
|
|
/* whether to run keystrokes through system but only to show how it comes out.
|
|
* otherwise, do full mapper processing. */
|
|
bool MAPPER_DemoOnly(void) {
|
|
return !mapper.exit;
|
|
}
|
|
|
|
CEvent *get_mapper_event_by_name(const std::string &x) {
|
|
auto i = name_to_events.find(x);
|
|
|
|
if (i != name_to_events.end()) {
|
|
if (i->second >= events.size())
|
|
E_Exit("Mapper: name to events contains out of range index for \"%s\"",x.c_str());
|
|
|
|
return events[i->second];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
unsigned char prvmc = 0;
|
|
extern bool font_14_init, loadlang;
|
|
extern uint8_t int10_font_14_init[256 * 14];
|
|
uint8_t *GetDbcs14Font(Bitu code, bool &is14);
|
|
bool isDBCSCP();
|
|
static void DrawText(Bitu x,Bitu y,const char * text,uint8_t color,uint8_t bkcolor/*=CLR_BLACK*/) {
|
|
#if defined(C_SDL2)
|
|
uint8_t * draw=((uint8_t *)mapper.draw_surface->pixels)+(y*mapper.draw_surface->w)+x;
|
|
#else
|
|
uint8_t * draw=((uint8_t *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x;
|
|
#endif
|
|
unsigned int cpbak = dos.loaded_codepage;
|
|
if (dos_kernel_disabled&&maincp) dos.loaded_codepage = maincp;
|
|
while (*text) {
|
|
unsigned char c = *text;
|
|
uint8_t * font;
|
|
bool is14 = false;
|
|
if (IS_PC98_ARCH || IS_JEGA_ARCH || isDBCSCP()) {
|
|
if (isKanji1(c) && prvmc == 0) {
|
|
prvmc = c;
|
|
text++;
|
|
continue;
|
|
} else if (isKanji2(c) && prvmc > 1) {
|
|
font = GetDbcs14Font(prvmc*0x100+c, is14);
|
|
prvmc = 1;
|
|
} else if (prvmc < 0x81)
|
|
prvmc = 0;
|
|
} else
|
|
prvmc = 0;
|
|
if (font_14_init&&dos.loaded_codepage&&dos.loaded_codepage!=437&&prvmc!=1)
|
|
font = &int10_font_14_init[c*14];
|
|
else if (prvmc!=1)
|
|
font = &int10_font_14[c*14];
|
|
Bitu i,j;
|
|
for (int k=0; k<(prvmc?2:1); k++) {
|
|
uint8_t * draw_line = draw;
|
|
for (i=0;i<14;i++) {
|
|
uint8_t map=*(font+(prvmc==1?(i*2+k):i));
|
|
for (j=0;j<8;j++) {
|
|
if (map & 0x80) *(draw_line+j)=color;
|
|
else *(draw_line+j)=bkcolor;
|
|
map<<=1;
|
|
}
|
|
#if defined(C_SDL2)
|
|
draw_line+=mapper.draw_surface->w;
|
|
#else
|
|
draw_line+=mapper.surface->pitch;
|
|
#endif
|
|
}
|
|
draw+=8;
|
|
}
|
|
text++;
|
|
prvmc = 0;
|
|
}
|
|
dos.loaded_codepage = cpbak;
|
|
prvmc = 0;
|
|
}
|
|
|
|
void RedrawMapperEventButtons() {
|
|
bind_but.prevpage->SetCanClick(cpage > 1);
|
|
bind_but.nextpage->SetCanClick(cpage < maxpage);
|
|
bind_but.pagestat->Change("%2u / %-2u", cpage, maxpage);
|
|
for (std::vector<CEventButton *>::iterator it = ceventbuttons.begin(); it != ceventbuttons.end(); ++it) {
|
|
CEventButton *button = (CEventButton *)*it;
|
|
button->Enable(button->Page(0)==cpage);
|
|
}
|
|
}
|
|
|
|
void MAPPER_TriggerEventByName(const std::string& name) {
|
|
CEvent *event = get_mapper_event_by_name(name);
|
|
if (event != NULL) {
|
|
if (event->type == CEvent::handler_event_t) {
|
|
CHandlerEvent *he = reinterpret_cast<CHandlerEvent*>(event);
|
|
if (he->handler != NULL) {
|
|
he->handler(true);
|
|
he->handler(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void change_action_text(const char* text,uint8_t col) {
|
|
bind_but.action->Change(text,"");
|
|
bind_but.action->SetColor(col);
|
|
}
|
|
|
|
|
|
static void SetActiveBind(CBind * _bind) {
|
|
mapper.abind=_bind;
|
|
if (_bind) {
|
|
bind_but.bind_title->Enable(true);
|
|
char buf[256];_bind->BindName(buf);
|
|
bind_but.bind_title->Change("INPUT: %s",buf);
|
|
bind_but.bind_title->SetColor(CLR_GREEN);
|
|
bind_but.del->Enable(true);
|
|
bind_but.next->Enable(true);
|
|
bind_but.mod1->Enable(true);
|
|
bind_but.mod2->Enable(true);
|
|
bind_but.mod3->Enable(true);
|
|
bind_but.host->Enable(true);
|
|
bind_but.hold->Enable(true);
|
|
} else {
|
|
bind_but.bind_title->Enable(false);
|
|
bind_but.del->Enable(false);
|
|
bind_but.next->Enable(false);
|
|
bind_but.mod1->Enable(false);
|
|
bind_but.mod2->Enable(false);
|
|
bind_but.mod3->Enable(false);
|
|
bind_but.host->Enable(false);
|
|
bind_but.hold->Enable(false);
|
|
}
|
|
}
|
|
|
|
static void SetActiveEvent(CEvent * event) {
|
|
mapper.aevent=event;
|
|
mapper.redraw=true;
|
|
mapper.addbind=false;
|
|
bind_but.event_title->Change("EVENT: %s",event ? event->GetName(): "none");
|
|
if (!event) {
|
|
change_action_text(MSG_Get("SELECT_EVENT"),CLR_WHITE);
|
|
bind_but.add->Enable(false);
|
|
SetActiveBind(nullptr);
|
|
} else {
|
|
change_action_text(MSG_Get("SELECT_DIFFERENT_EVENT"),CLR_WHITE);
|
|
mapper.abindit=event->bindlist.begin();
|
|
if (mapper.abindit!=event->bindlist.end()) {
|
|
SetActiveBind(*(mapper.abindit));
|
|
} else SetActiveBind(nullptr);
|
|
bind_but.add->Enable(true);
|
|
}
|
|
|
|
bind_but.cap->SetCanClick(event != nullptr);
|
|
}
|
|
|
|
#if defined(C_SDL2)
|
|
extern SDL_Window * GFX_SetSDLSurfaceWindow(uint16_t width, uint16_t height);
|
|
extern SDL_Rect GFX_GetSDLSurfaceSubwindowDims(uint16_t width, uint16_t height);
|
|
extern void GFX_UpdateDisplayDimensions(int width, int height);
|
|
#endif
|
|
|
|
static void DrawButtons(void) {
|
|
#if defined(C_SDL2)
|
|
SDL_FillRect(mapper.draw_surface, nullptr, 0);
|
|
#else
|
|
SDL_FillRect(mapper.surface,0,CLR_BLACK);
|
|
SDL_LockSurface(mapper.surface);
|
|
#endif
|
|
for (CButton_it but_it = buttons.begin();but_it!=buttons.end();++but_it) {
|
|
(*but_it)->Draw();
|
|
}
|
|
#if defined(C_SDL2)
|
|
// We can't just use SDL_BlitScaled (say for Android) in one step
|
|
SDL_BlitSurface(mapper.draw_surface, NULL, mapper.draw_surface_nonpaletted, NULL);
|
|
SDL_BlitScaled(mapper.draw_surface_nonpaletted, NULL, mapper.surface, &mapper.draw_rect);
|
|
// SDL_BlitSurface(mapper.draw_surface, NULL, mapper.surface, NULL);
|
|
SDL_UpdateWindowSurface(mapper.window);
|
|
#else
|
|
SDL_UnlockSurface(mapper.surface);
|
|
SDL_Flip(mapper.surface);
|
|
#endif
|
|
}
|
|
|
|
static CKeyEvent * AddKeyButtonEvent(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,char const * const entry,KBD_KEYS key) {
|
|
char buf[64];
|
|
strcpy(buf,"key_");
|
|
strcat(buf,entry);
|
|
CKeyEvent * event=new CKeyEvent(buf,key);
|
|
CEventButton *button=new CEventButton(x,y,dx,dy,title,event);
|
|
event->notifybutton(button);
|
|
return event;
|
|
}
|
|
|
|
static CMouseButtonEvent * AddMouseButtonEvent(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,char const * const entry,uint8_t key) {
|
|
char buf[64];
|
|
strcpy(buf,"mouse_");
|
|
strcat(buf,entry);
|
|
CMouseButtonEvent * event=new CMouseButtonEvent(buf,key);
|
|
CEventButton *button=new CEventButton(x,y,dx,dy,title,event);
|
|
event->notifybutton(button);
|
|
return event;
|
|
}
|
|
|
|
static CJAxisEvent * AddJAxisButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu stick,Bitu axis,bool positive,CJAxisEvent * opposite_axis) {
|
|
char buf[64];
|
|
sprintf(buf,"jaxis_%d_%d%s",(int)stick,(int)axis,positive ? "+" : "-");
|
|
CJAxisEvent * event=new CJAxisEvent(buf,stick,axis,positive,opposite_axis);
|
|
CEventButton *button=new CEventButton(x,y,dx,dy,title,event);
|
|
event->notifybutton(button);
|
|
return event;
|
|
}
|
|
static CJAxisEvent * AddJAxisButton_hidden(Bitu stick,Bitu axis,bool positive,CJAxisEvent * opposite_axis) {
|
|
char buf[64];
|
|
sprintf(buf,"jaxis_%d_%d%s",(int)stick,(int)axis,positive ? "+" : "-");
|
|
return new CJAxisEvent(buf,stick,axis,positive,opposite_axis);
|
|
}
|
|
|
|
static void AddJButtonButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu stick,Bitu button) {
|
|
char buf[64];
|
|
sprintf(buf,"jbutton_%d_%d",(int)stick,(int)button);
|
|
CJButtonEvent * event=new CJButtonEvent(buf,stick,button);
|
|
CEventButton *evbutton=new CEventButton(x,y,dx,dy,title,event);
|
|
event->notifybutton(evbutton);
|
|
}
|
|
static void AddJButtonButton_hidden(Bitu stick,Bitu button) {
|
|
char buf[64];
|
|
sprintf(buf,"jbutton_%d_%d",(int)stick,(int)button);
|
|
new CJButtonEvent(buf,stick,button);
|
|
}
|
|
|
|
static void AddJHatButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu _stick,Bitu _hat,Bitu _dir) {
|
|
char buf[64];
|
|
sprintf(buf,"jhat_%d_%d_%d",(int)_stick,(int)_hat,(int)_dir);
|
|
CJHatEvent * event=new CJHatEvent(buf,_stick,_hat,_dir);
|
|
CEventButton* evbutton = new CEventButton(x,y,dx,dy,title,event);
|
|
event->notifybutton(evbutton);
|
|
}
|
|
|
|
static void AddModButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu _mod) {
|
|
char buf[64];
|
|
|
|
if (_mod == 4)
|
|
sprintf(buf,"host");
|
|
else
|
|
sprintf(buf,"mod_%d",(int)_mod);
|
|
|
|
CModEvent * event=new CModEvent(buf,_mod);
|
|
CEventButton *button=new CEventButton(x,y,dx,dy,title,event);
|
|
event->notifybutton(button);
|
|
if (_mod == 4) {
|
|
button->Enable(hostkeyalt == 0);
|
|
hostbutton=button;
|
|
}
|
|
|
|
assert(_mod < 8);
|
|
mod_event[_mod] = event;
|
|
}
|
|
|
|
bool SortHandlers(CHandlerEvent* a, CHandlerEvent* b)
|
|
{
|
|
auto str1 = std::string(a->ButtonName());
|
|
auto str2 = std::string(b->ButtonName());
|
|
auto sort = str1.compare(str2) < 0;
|
|
return sort;
|
|
}
|
|
|
|
static void CreateLayout(void) {
|
|
|
|
#define DX 0
|
|
#define DY 10
|
|
#define MX 1
|
|
#define MY 1
|
|
#define BW 27
|
|
#define BH 18
|
|
#define CX (BW / 2)
|
|
#define CY (BH / 2)
|
|
#define BU(_X_) (BW * (_X_) + MX * ((_X_) - 1))
|
|
#define BV(_Y_) (BH * (_Y_) + MY * ((_Y_) - 1))
|
|
#define PX(_X_) (DX + (_X_) * (BW + MX))
|
|
#define PY(_Y_) (DY + (_Y_) * (BH + MY))
|
|
|
|
Bitu i;
|
|
|
|
#pragma region Keyboard
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 2
|
|
#define YO 0
|
|
|
|
if(!IS_PC98_ARCH)
|
|
{
|
|
AddKeyButtonEvent(PX(XO + 0), PY(YO), BU(1), BV(1), "F13", "f13", KBD_f13);
|
|
AddKeyButtonEvent(PX(XO + 1), PY(YO), BU(1), BV(1), "F14", "f14", KBD_f14);
|
|
AddKeyButtonEvent(PX(XO + 2), PY(YO), BU(1), BV(1), "F15", "f15", KBD_f15);
|
|
AddKeyButtonEvent(PX(XO + 3), PY(YO), BU(1), BV(1), "F16", "f16", KBD_f16);
|
|
AddKeyButtonEvent(PX(XO + 4), PY(YO), BU(1), BV(1), "F17", "f17", KBD_f17);
|
|
AddKeyButtonEvent(PX(XO + 5), PY(YO), BU(1), BV(1), "F18", "f18", KBD_f18);
|
|
AddKeyButtonEvent(PX(XO + 6), PY(YO), BU(1), BV(1), "F19", "f19", KBD_f19);
|
|
AddKeyButtonEvent(PX(XO + 7), PY(YO), BU(1), BV(1), "F20", "f20", KBD_f20);
|
|
AddKeyButtonEvent(PX(XO + 8), PY(YO), BU(1), BV(1), "F21", "f21", KBD_f21);
|
|
AddKeyButtonEvent(PX(XO + 9), PY(YO), BU(1), BV(1), "F22", "f22", KBD_f22);
|
|
AddKeyButtonEvent(PX(XO + 10), PY(YO), BU(1), BV(1), "F23", "f23", KBD_f23);
|
|
AddKeyButtonEvent(PX(XO + 11), PY(YO), BU(1), BV(1), "F24", "f24", KBD_f24);
|
|
}
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 0
|
|
#define YO 1
|
|
AddKeyButtonEvent(PX(XO), PY(YO), BU(2), BV(1), "Esc", "esc", KBD_esc);
|
|
|
|
for(i = 0; i < 12; i++)
|
|
{
|
|
AddKeyButtonEvent(PX(XO + 2 + i), PY(YO), BU(1), BV(1), combo_f[i].title, combo_f[i].entry, combo_f[i].key);
|
|
}
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 0
|
|
#define YO 2
|
|
|
|
for(i = 0; i < 14; i++)
|
|
{
|
|
const auto block = IS_PC98_ARCH ? combo_1_pc98 : combo_1;
|
|
const auto width = i == 0 || i == 13 ? 2 : 1;
|
|
AddKeyButtonEvent(PX(XO + (i > 0 ? i + 1 : i)), PY(YO), BU(width), BV(1), block[i].title, block[i].entry, block[i].key);
|
|
}
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 0
|
|
#define YO 3
|
|
|
|
AddKeyButtonEvent(PX(XO), PY(YO), BU(2), BV(1), "Tab", "tab", KBD_tab);
|
|
|
|
for(i = 0; i < 12; i++)
|
|
{
|
|
AddKeyButtonEvent(PX(XO + 2 + i), PY(YO), BU(1), BV(1), combo_2[i].title, combo_2[i].entry, combo_2[i].key);
|
|
}
|
|
|
|
AddKeyButtonEvent(PX(XO + 14), PY(YO), BU(2), BV(2), "Enter", "enter", KBD_enter);
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 0
|
|
#define YO 4
|
|
|
|
caps_lock_event = AddKeyButtonEvent(PX(XO), PY(YO), BU(2), BV(1), "Caps", "capslock", KBD_capslock);
|
|
|
|
for(i = 0; i < 12; i++)
|
|
{
|
|
const auto block = IS_PC98_ARCH ? combo_3_pc98 : combo_3;
|
|
AddKeyButtonEvent(PX(XO + 2 + i), PY(YO), BU(1), BV(1), block[i].title, block[i].entry, block[i].key);
|
|
}
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 0
|
|
#define YO 5
|
|
|
|
AddKeyButtonEvent(PX(XO), PY(YO), BU(2), BV(1), "Shift", "lshift", KBD_leftshift);
|
|
|
|
for(i = 0; i < 11; i++)
|
|
{
|
|
AddKeyButtonEvent(PX(XO + 2 + i), PY(YO), BU(1), BV(1), combo_4[i].title, combo_4[i].entry, combo_4[i].key);
|
|
}
|
|
|
|
AddKeyButtonEvent(PX(XO + 13), PY(YO), BU(3), BV(1), "Shift", "rshift", KBD_rightshift);
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 0
|
|
#define YO 6
|
|
|
|
AddKeyButtonEvent(PX(XO + 00), PY(YO), BU(2), BV(1), "Ctrl", "lctrl", KBD_leftctrl);
|
|
AddKeyButtonEvent(PX(XO + 02), PY(YO), BU(1), BV(1), "Win", "lwindows", KBD_lwindows);
|
|
AddKeyButtonEvent(PX(XO + 03), PY(YO), BU(1), BV(1), "Alt", "lalt", KBD_leftalt);
|
|
AddKeyButtonEvent(PX(XO + 04), PY(YO), BU(7), BV(1), "Space", "space", KBD_space);
|
|
AddKeyButtonEvent(PX(XO + 11), PY(YO), BU(1), BV(1), "Alt", "ralt", KBD_rightalt);
|
|
AddKeyButtonEvent(PX(XO + 12), PY(YO), BU(1), BV(1), "Win", "rwindows", KBD_rwindows);
|
|
AddKeyButtonEvent(PX(XO + 13), PY(YO), BU(1), BV(1), "App", "rwinmenu", KBD_rwinmenu);
|
|
AddKeyButtonEvent(PX(XO + 14), PY(YO), BU(2), BV(1), "Ctrl", "rctrl", KBD_rightctrl);
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 16
|
|
#define YO 0
|
|
|
|
AddKeyButtonEvent(PX(XO + 0), PY(YO + 0), BU(1), BV(1), "Prn", "printscreen", KBD_printscreen);
|
|
AddKeyButtonEvent(PX(XO + 1), PY(YO + 0), BU(1), BV(1), "Scr", "scrolllock", KBD_scrolllock);
|
|
AddKeyButtonEvent(PX(XO + 2), PY(YO + 0), BU(1), BV(1), "Brk", "pause", KBD_pause);
|
|
|
|
AddKeyButtonEvent(PX(XO + 0), PY(YO + 2), BU(1), BV(1), "Ins", "insert", KBD_insert);
|
|
AddKeyButtonEvent(PX(XO + 1), PY(YO + 2), BU(1), BV(1), "Hom", "home", KBD_home);
|
|
AddKeyButtonEvent(PX(XO + 2), PY(YO + 2), BU(1), BV(1), "Pg\x18", "pageup", KBD_pageup);
|
|
|
|
AddKeyButtonEvent(PX(XO + 0), PY(YO + 3), BU(1), BV(1), "Del", "delete", KBD_delete);
|
|
AddKeyButtonEvent(PX(XO + 1), PY(YO + 3), BU(1), BV(1), "End", "end", KBD_end);
|
|
AddKeyButtonEvent(PX(XO + 2), PY(YO + 3), BU(1), BV(1), "Pg\x19", "pagedown", KBD_pagedown);
|
|
|
|
AddKeyButtonEvent(PX(XO + 1), PY(YO + 5), BU(1), BV(1), "\x18", "up", KBD_up);
|
|
AddKeyButtonEvent(PX(XO + 0), PY(YO + 6), BU(1), BV(1), "\x1B", "left", KBD_left);
|
|
AddKeyButtonEvent(PX(XO + 1), PY(YO + 6), BU(1), BV(1), "\x19", "down", KBD_down);
|
|
AddKeyButtonEvent(PX(XO + 2), PY(YO + 6), BU(1), BV(1), "\x1A", "right", KBD_right);
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 19
|
|
#define YO 2
|
|
|
|
AddKeyButtonEvent(PX(XO + 3) - 3, PY(YO - 1), BU(1) - 1, BV(1), "KP=", "kp_equals", KBD_kpequals);
|
|
if(!IS_PC98_ARCH) {
|
|
AddKeyButtonEvent(PX(XO + 2) - 3, PY(YO - 1), BU(1) - 1, BV(1), "KP,", "kp_comma", KBD_kpcomma);
|
|
}
|
|
num_lock_event =
|
|
AddKeyButtonEvent(PX(XO + 0) - 0, PY(YO + 0), BU(1) - 1, BV(1), "Num", "numlock", KBD_numlock);
|
|
AddKeyButtonEvent(PX(XO + 1) - 1, PY(YO + 0), BU(1) - 1, BV(1), "/", "kp_divide", KBD_kpdivide);
|
|
AddKeyButtonEvent(PX(XO + 2) - 2, PY(YO + 0), BU(1) - 1, BV(1), "*", "kp_multiply", KBD_kpmultiply);
|
|
AddKeyButtonEvent(PX(XO + 3) - 3, PY(YO + 0), BU(1) - 1, BV(1), "-", "kp_minus", KBD_kpminus);
|
|
|
|
AddKeyButtonEvent(PX(XO + 0) - 0, PY(YO + 1), BU(1) - 1, BV(1), "7", "kp_7", KBD_kp7);
|
|
AddKeyButtonEvent(PX(XO + 1) - 1, PY(YO + 1), BU(1) - 1, BV(1), "8", "kp_8", KBD_kp8);
|
|
AddKeyButtonEvent(PX(XO + 2) - 2, PY(YO + 1), BU(1) - 1, BV(1), "9", "kp_9", KBD_kp9);
|
|
AddKeyButtonEvent(PX(XO + 3) - 3, PY(YO + 1), BU(1) - 1, BV(2), "+", "kp_plus", KBD_kpplus);
|
|
|
|
AddKeyButtonEvent(PX(XO + 0) - 0, PY(YO + 2), BU(1) - 1, BV(1), "4", "kp_4", KBD_kp4);
|
|
AddKeyButtonEvent(PX(XO + 1) - 1, PY(YO + 2), BU(1) - 1, BV(1), "5", "kp_5", KBD_kp5);
|
|
AddKeyButtonEvent(PX(XO + 2) - 2, PY(YO + 2), BU(1) - 1, BV(1), "6", "kp_6", KBD_kp6);
|
|
|
|
AddKeyButtonEvent(PX(XO + 0) - 0, PY(YO + 3), BU(1) - 1, BV(1), "1", "kp_1", KBD_kp1);
|
|
AddKeyButtonEvent(PX(XO + 1) - 1, PY(YO + 3), BU(1) - 1, BV(1), "2", "kp_2", KBD_kp2);
|
|
AddKeyButtonEvent(PX(XO + 2) - 2, PY(YO + 3), BU(1) - 1, BV(1), "3", "kp_3", KBD_kp3);
|
|
|
|
AddKeyButtonEvent(PX(XO + 3) - 3, PY(YO + 3), BU(1) - 1, BV(2), "Ent", "kp_enter", KBD_kpenter);
|
|
|
|
if(IS_PC98_ARCH) // TODO
|
|
{
|
|
AddKeyButtonEvent(PX(XO + 0) - 0, PY(YO + 4), BU(1) - 1, BV(1), "0", "kp_0", KBD_kp0);
|
|
AddKeyButtonEvent(PX(XO + 1) - 1, PY(YO + 4), BU(1) - 1, BV(1), ",", "kp_comma", KBD_kpcomma);
|
|
}
|
|
else
|
|
{
|
|
AddKeyButtonEvent(PX(XO + 0) - 0, PY(YO + 4), BU(2) - 2, BV(1), "0", "kp_0", KBD_kp0);
|
|
}
|
|
|
|
AddKeyButtonEvent(PX(XO + 2) - 2, PY(YO + 4), BU(1) - 1, BV(1), ".", "kp_period", KBD_kpperiod);
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Mouse
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 0
|
|
#define YO 9
|
|
new CTextButton(PX(XO + 0) + CX * 1, PY(YO - 1), BU(3), BV(1), "Mouse");
|
|
AddMouseButtonEvent(PX(XO + 0) + CX * 1, PY(YO), BU(1), BV(1), "L", "left", 0);
|
|
AddMouseButtonEvent(PX(XO + 1) + CX * 1, PY(YO), BU(1), BV(1), "M", "middle", 2);
|
|
AddMouseButtonEvent(PX(XO + 2) + CX * 1, PY(YO), BU(1), BV(1), "R", "right", 1);
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Joysticks
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 11
|
|
#define YO 8
|
|
|
|
switch(joytype)
|
|
{
|
|
case JOY_NONE:
|
|
(new CTextButton(PX(XO + 0), PY(YO + 0) - CY, BU(3), BV(1), "Disabled"))->SetCanClick(false);
|
|
(new CTextButton(PX(XO + 4), PY(YO + 0) - CY, BU(3), BV(1), "Disabled"))->SetCanClick(false);
|
|
(new CTextButton(PX(XO + 8), PY(YO + 0) - CY, BU(3), BV(1), "Disabled"))->SetCanClick(false);
|
|
break;
|
|
case JOY_2AXIS:
|
|
(new CTextButton(PX(XO + 0), PY(YO + 0) - CY, BU(3), BV(1), "Joystick 1"));
|
|
(new CTextButton(PX(XO + 4), PY(YO + 0) - CY, BU(3), BV(1), "Joystick 2"));
|
|
(new CTextButton(PX(XO + 8), PY(YO + 0) - CY, BU(3), BV(1), "Disabled"))->SetCanClick(false);
|
|
break;
|
|
case JOY_4AXIS:
|
|
case JOY_4AXIS_2:
|
|
(new CTextButton(PX(XO + 0), PY(YO + 0) - CY, BU(3), BV(1), "Axis 1/2"));
|
|
(new CTextButton(PX(XO + 4), PY(YO + 0) - CY, BU(3), BV(1), "Axis 3/4"));
|
|
(new CTextButton(PX(XO + 8), PY(YO + 0) - CY, BU(3), BV(1), "Disabled"))->SetCanClick(false);
|
|
break;
|
|
case JOY_FCS:
|
|
(new CTextButton(PX(XO + 0), PY(YO + 0) - CY, BU(3), BV(1), "Axis 1/2"));
|
|
(new CTextButton(PX(XO + 4), PY(YO + 0) - CY, BU(3), BV(1), "Axis 3"));
|
|
(new CTextButton(PX(XO + 8), PY(YO + 0) - CY, BU(3), BV(1), "Hat/D-pad"));
|
|
break;
|
|
case JOY_CH:
|
|
(new CTextButton(PX(XO + 0), PY(YO + 0) - CY, BU(3), BV(1), "Axis 1/2"));
|
|
(new CTextButton(PX(XO + 4), PY(YO + 0) - CY, BU(3), BV(1), "Axis 3/4"));
|
|
(new CTextButton(PX(XO + 8), PY(YO + 0) - CY, BU(3), BV(1), "Hat/D-pad"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
{
|
|
AddJButtonButton(PX(XO + 0), PY(YO + 1) - CY, BU(1), BV(1), "1", 0, 0);
|
|
AddJButtonButton(PX(XO + 2), PY(YO + 1) - CY, BU(1), BV(1), "2", 0, 1);
|
|
|
|
auto x1 = AddJAxisButton(PX(XO + 0), PY(YO + 2) - CY, BU(1), BV(1), "X-", 0, 0, false, nullptr);
|
|
auto x2 = AddJAxisButton(PX(XO + 2), PY(YO + 2) - CY, BU(1), BV(1), "X+", 0, 0, true, x1); (void)x2;//unused
|
|
auto y1 = AddJAxisButton(PX(XO + 1), PY(YO + 1) - CY, BU(1), BV(1), "Y-", 0, 1, false, nullptr);
|
|
auto y2 = AddJAxisButton(PX(XO + 1), PY(YO + 2) - CY, BU(1), BV(1), "Y+", 0, 1, true, y1); (void)y2;//unused
|
|
}
|
|
|
|
if(joytype == JOY_2AXIS)
|
|
{
|
|
AddJButtonButton(PX(XO + 4), PY(YO + 1) - CY, BU(1), BV(1), "1", 1, 0);
|
|
AddJButtonButton(PX(XO + 6), PY(YO + 1) - CY, BU(1), BV(1), "2", 1, 1);
|
|
AddJButtonButton_hidden(0, 2);
|
|
AddJButtonButton_hidden(0, 3);
|
|
|
|
auto x1 = AddJAxisButton(PX(XO + 4), PY(YO + 2) - CY, BW, BH, "X-", 1, 0, false, nullptr);
|
|
auto x2 = AddJAxisButton(PX(XO + 6), PY(YO + 2) - CY, BW, BH, "X+", 1, 0, true, x1); (void)x2;//unused
|
|
auto y1 = AddJAxisButton(PX(XO + 5), PY(YO + 1) - CY, BW, BH, "Y-", 1, 1, false, nullptr);
|
|
auto y2 = AddJAxisButton(PX(XO + 5), PY(YO + 2) - CY, BW, BH, "Y+", 1, 1, true, y1); (void)y2;//unused
|
|
auto z1 = AddJAxisButton_hidden(0, 2, false, nullptr);
|
|
auto z2 = AddJAxisButton_hidden(0, 3, false, nullptr);
|
|
|
|
AddJAxisButton_hidden(0, 2, true, z1);
|
|
AddJAxisButton_hidden(0, 3, true, z2);
|
|
}
|
|
else
|
|
{
|
|
AddJButtonButton(PX(XO + 4), PY(YO + 1) - CY, BU(1), BV(1), "3", 0, 2);
|
|
AddJButtonButton(PX(XO + 6), PY(YO + 1) - CY, BU(1), BV(1), "4", 0, 3);
|
|
AddJButtonButton_hidden(1, 0);
|
|
AddJButtonButton_hidden(1, 1);
|
|
|
|
auto x1 = AddJAxisButton(PX(XO + 4), PY(YO + 2) - CY, BU(1), BV(1), "X-", 0, 2, false, nullptr);
|
|
auto x2 = AddJAxisButton(PX(XO + 6), PY(YO + 2) - CY, BU(1), BV(1), "X+", 0, 2, true, x1); (void)x2;//unused
|
|
auto y1 = AddJAxisButton(PX(XO + 5), PY(YO + 1) - CY, BU(1), BV(1), "Y-", 0, 3, false, nullptr);
|
|
auto y2 = AddJAxisButton(PX(XO + 5), PY(YO + 2) - CY, BU(1), BV(1), "Y+", 0, 3, true, y1); (void)y2;//unused
|
|
auto z1 = AddJAxisButton_hidden(1, 0, false, nullptr);
|
|
auto z2 = AddJAxisButton_hidden(1, 1, false, nullptr);
|
|
|
|
AddJAxisButton_hidden(1, 0, true, z1);
|
|
AddJAxisButton_hidden(1, 1, true, z2);
|
|
}
|
|
|
|
if(joytype == JOY_CH)
|
|
{
|
|
AddJButtonButton(PX(XO + 8), PY(YO + 1) - CY, BU(1), BV(1), "5", 0, 4);
|
|
AddJButtonButton(PX(XO + 10), PY(YO + 1) - CY, BU(1), BV(1), "6", 0, 5);
|
|
}
|
|
else
|
|
{
|
|
AddJButtonButton_hidden(0, 4);
|
|
AddJButtonButton_hidden(0, 5);
|
|
}
|
|
|
|
AddJHatButton(PX(XO + 9), PY(YO + 1) - CY, BU(1), BV(1), "\x18", 0, 0, 0); // U
|
|
AddJHatButton(PX(XO + 8), PY(YO + 2) - CY, BU(1), BV(1), "\x1B", 0, 0, 3); // L
|
|
AddJHatButton(PX(XO + 9), PY(YO + 2) - CY, BU(1), BV(1), "\x19", 0, 0, 2); // D
|
|
AddJHatButton(PX(XO + 10), PY(YO + 2) - CY, BU(1), BV(1), "\x1A", 0, 0, 1); // R
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Japanese, Korean
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 0
|
|
#define YO 11
|
|
|
|
AddKeyButtonEvent(PX(XO + 0) + CX, PY(YO + 0), BU(3), BV(1), "Hankaku", "jp_hankaku", KBD_jp_hankaku);
|
|
AddKeyButtonEvent(PX(XO + 0) + CX, PY(YO + 1), BU(3), BV(1), "Muhenkan","jp_muhenkan",KBD_jp_muhenkan);
|
|
AddKeyButtonEvent(PX(XO + 0) + CX, PY(YO + 2), BU(3), BV(1), "Henkan", "jp_henkan", KBD_jp_henkan);
|
|
AddKeyButtonEvent(PX(XO + 0) + CX, PY(YO + 3), BU(3), BV(1), "Hiragana","jp_hiragana",KBD_jp_hiragana);
|
|
AddKeyButtonEvent(PX(XO + 0) + CX, PY(YO + 4), BU(3), BV(1), "Yen", "jp_yen", KBD_jp_yen);
|
|
AddKeyButtonEvent(PX(XO + 4) + CX, PY(YO + 0), BU(1), BV(1), "\\", "jp_bckslash",KBD_jp_backslash);
|
|
AddKeyButtonEvent(PX(XO + 5) + CX, PY(YO + 0), BU(1), BV(1), ":*", "colon", KBD_colon);
|
|
AddKeyButtonEvent(PX(XO + 6) + CX, PY(YO + 0), BU(1), BV(1), "^`", "caret", KBD_caret);
|
|
AddKeyButtonEvent(PX(XO + 7) + CX, PY(YO + 0), BU(1), BV(1), "@~", "atsign", KBD_atsign);
|
|
AddKeyButtonEvent(PX(XO + 4) + CX, PY(YO + 3), BU(3), BV(1), "Hancha", "kor_hancha", KBD_kor_hancha);
|
|
AddKeyButtonEvent(PX(XO + 4) + CX, PY(YO + 4), BU(3), BV(1), "Hanyong", "kor_hanyong",KBD_kor_hanyong);
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Modifiers & Bindings
|
|
|
|
AddModButton(PX(0) + CX, PY(17), BU(2), BV(1), LabelMod1, 1);
|
|
AddModButton(PX(2) + CX, PY(17), BU(2), BV(1), LabelMod2, 2);
|
|
AddModButton(PX(4) + CX, PY(17), BU(2), BV(1), LabelMod3, 3);
|
|
AddModButton(PX(6) + CX, PY(17), BU(3), BV(1), LabelHost, 4);
|
|
|
|
bind_but.event_title = new CCaptionButton(PX(0) + CX, PY(18), 0, 0, false);
|
|
bind_but.bind_title = new CCaptionButton(PX(0) + CX, PY(19), 0, 0, false);
|
|
|
|
bind_but.add = new CBindButton(PX(0) + CX, PY(20), BU(3), BV(1), MSG_Get("ADD"), BB_Add);
|
|
bind_but.del = new CBindButton(PX(3) + CX, PY(20), BU(3), BV(1), MSG_Get("DEL"), BB_Del);
|
|
bind_but.next = new CBindButton(PX(6) + CX, PY(20), BU(3), BV(1), MSG_Get("NEXT"), BB_Next);
|
|
|
|
bind_but.mod1 = new CCheckButton(PX(0) + CX, PY(21) + CY, BU(3), BV(1), LabelMod1, BC_Mod1);
|
|
bind_but.mod2 = new CCheckButton(PX(0) + CX, PY(22) + CY, BU(3), BV(1), LabelMod2, BC_Mod2);
|
|
bind_but.mod3 = new CCheckButton(PX(0) + CX, PY(23) + CY, BU(3), BV(1), LabelMod3, BC_Mod3);
|
|
bind_but.host = new CCheckButton(PX(3) + CX, PY(21) + CY, BU(3), BV(1), LabelHost, BC_Host);
|
|
bind_but.hold = new CCheckButton(PX(3) + CX, PY(22) + CY, BU(3), BV(1), LabelHold, BC_Hold);
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region PC-98 extra keys
|
|
|
|
if(IS_PC98_ARCH)
|
|
{
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 0
|
|
#define YO 0
|
|
|
|
AddKeyButtonEvent(PX(XO + 0), PY(YO), BU(2), BV(1), "STOP", "stop", KBD_stop);
|
|
AddKeyButtonEvent(PX(XO + 2), PY(YO), BU(2), BV(1), "HELP", "help", KBD_help);
|
|
AddKeyButtonEvent(PX(XO + 4), PY(YO), BU(2), BV(1), "COPY", "copy", KBD_copy);
|
|
AddKeyButtonEvent(PX(XO + 6), PY(YO), BU(2), BV(1), "KANA", "kana", KBD_kana);
|
|
AddKeyButtonEvent(PX(XO + 8), PY(YO), BU(2), BV(1), "NFER", "nfer", KBD_nfer);
|
|
AddKeyButtonEvent(PX(XO + 10), PY(YO), BU(2), BV(1), "XFER", "xfer", KBD_xfer);
|
|
AddKeyButtonEvent(PX(XO + 12), PY(YO), BU(2), BV(1), "RO/_", "jp_ro", KBD_jp_ro);
|
|
|
|
#undef XO
|
|
#undef YO
|
|
#define XO 3
|
|
#define YO 11
|
|
|
|
AddKeyButtonEvent(PX(XO + 1) + CX, PY(YO + 1), BU(1), BV(1), "VF1", "vf1", KBD_vf1);
|
|
AddKeyButtonEvent(PX(XO + 2) + CX, PY(YO + 1), BU(1), BV(1), "VF2", "vf2", KBD_vf2);
|
|
AddKeyButtonEvent(PX(XO + 3) + CX, PY(YO + 1), BU(1), BV(1), "VF3", "vf3", KBD_vf3);
|
|
AddKeyButtonEvent(PX(XO + 4) + CX, PY(YO + 1), BU(1), BV(1), "VF4", "vf4", KBD_vf4);
|
|
AddKeyButtonEvent(PX(XO + 5) + CX, PY(YO + 1), BU(1), BV(1), "VF5", "vf5", KBD_vf5);
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Bindables
|
|
|
|
#undef XO
|
|
#undef YO
|
|
|
|
Bitu xpos = 0;
|
|
Bitu ypos = 0;
|
|
uint8_t page = cpage;
|
|
|
|
ceventbuttons.clear();
|
|
|
|
std::sort(handlergroup.begin(), handlergroup.end(), SortHandlers);
|
|
|
|
for(CHandlerEventVector_it hit = handlergroup.begin(); hit != handlergroup.end(); ++hit)
|
|
{
|
|
maxpage = page;
|
|
|
|
auto button = new CEventButton(PX(10) + CX, PY(11 + ypos), BU(12), BV(1), (*hit)->ButtonName(), (*hit));
|
|
|
|
ceventbuttons.push_back(button);
|
|
(*hit)->notifybutton(button);
|
|
button->Enable(page == cpage);
|
|
button->Page(page);
|
|
|
|
ypos++;
|
|
|
|
if(ypos >= 7)
|
|
{
|
|
ypos = 0;
|
|
page++;
|
|
}
|
|
}
|
|
|
|
bind_but.prevpage =
|
|
new CBindButton(PX(11), PY(19) - CY - (CY / 2), BU(4), BV(1), "<-", BB_Prevpage);
|
|
|
|
bind_but.pagestat = new CCaptionButton(PX(14), PY(19) - CY - (CY / 2), BU(5), BV(1), true);
|
|
|
|
bind_but.nextpage =
|
|
new CBindButton(PX(18), PY(19) - CY - (CY / 2), BU(4), BV(1), "->", BB_Nextpage);
|
|
|
|
bind_but.pagestat->Change("%2u / %-2u", cpage, maxpage);
|
|
|
|
bind_but.prevpage->SetCanClick(false);
|
|
|
|
next_handler_xpos = xpos; // TODO is this useless, commenting it out changes nothing!?
|
|
next_handler_ypos = ypos; // TODO is this useless, commenting it out changes nothing!?
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Capture/Save/Exit
|
|
|
|
bind_but.cap = new CBindButton(PX(12), PY(20), BU(3), BV(1), MSG_Get("CAPTURE"), BB_Capture);
|
|
bind_but.save = new CBindButton(PX(15), PY(20), BU(3), BV(1), MSG_Get("SAVE"), BB_Save);
|
|
bind_but.exit = new CBindButton(PX(18), PY(20), BU(3), BV(1), MSG_Get("EXIT"), BB_Exit);
|
|
bind_but.cap->SetCanClick(false);
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Status/Info
|
|
|
|
// NOTE: screen budget is really tight down there, more than that and drawing crashes
|
|
|
|
bind_but.action = new CCaptionButton(PX(7), PY(22) - CY, BU(15), BV(1), false);
|
|
bind_but.dbg1 = new CCaptionButton(PX(7), PY(23) - CY, BU(16), BV(1), false);
|
|
bind_but.dbg2 = new CCaptionButton(PX(7), PY(24) - CY, BU(16), BV(1), false);
|
|
|
|
bind_but.dbg1->Change("%s", "");
|
|
bind_but.dbg2->Change("%s", "");
|
|
|
|
#pragma endregion
|
|
|
|
mapper_addhandler_create_buttons = true;
|
|
}
|
|
|
|
static void CreateStringBind(char * line,bool loading=false) {
|
|
std::string o_line = line;
|
|
|
|
line=trim(line);
|
|
if (*line == 0) return;
|
|
char * eventname=StripWord(line);
|
|
CEvent * event;
|
|
for (CEventVector_it ev_it=events.begin();ev_it!=events.end();++ev_it) {
|
|
if (!strcasecmp((*ev_it)->GetName(),eventname)) {
|
|
event=*ev_it;
|
|
goto foundevent;
|
|
}
|
|
}
|
|
|
|
if (loading) {
|
|
/* NTS: StripWord() updates line pointer after ASCIIZ snipping the event name */
|
|
pending_string_binds[eventname] = line; /* perhaps code will register it later (i.e. Herc pal change) */
|
|
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);
|
|
}
|
|
else {
|
|
LOG(LOG_MISC,LOG_WARN)("Can't find matching event for %s",eventname);
|
|
}
|
|
|
|
return ;
|
|
foundevent:
|
|
CBind * bind;
|
|
for (char * bindline=StripWord(line);*bindline;bindline=StripWord(line)) {
|
|
for (CBindGroup_it it=bindgroups.begin();it!=bindgroups.end();++it) {
|
|
bind=(*it)->CreateConfigBind(bindline);
|
|
if (bind) {
|
|
event->AddBind(bind);
|
|
bind->SetFlags(bindline);
|
|
event->update_menu_shortcut();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(C_SDL2)
|
|
|
|
static struct {
|
|
const char * eventend;
|
|
Bitu key;
|
|
} DefaultKeys[]={
|
|
|
|
{"f1",SDL_SCANCODE_F1}, {"f2",SDL_SCANCODE_F2}, {"f3",SDL_SCANCODE_F3}, {"f4",SDL_SCANCODE_F4},
|
|
{"f5",SDL_SCANCODE_F5}, {"f6",SDL_SCANCODE_F6}, {"f7",SDL_SCANCODE_F7}, {"f8",SDL_SCANCODE_F8},
|
|
{"f9",SDL_SCANCODE_F9}, {"f10",SDL_SCANCODE_F10}, {"f11",SDL_SCANCODE_F11}, {"f12",SDL_SCANCODE_F12},
|
|
|
|
{"1",SDL_SCANCODE_1}, {"2",SDL_SCANCODE_2}, {"3",SDL_SCANCODE_3}, {"4",SDL_SCANCODE_4},
|
|
{"5",SDL_SCANCODE_5}, {"6",SDL_SCANCODE_6}, {"7",SDL_SCANCODE_7}, {"8",SDL_SCANCODE_8},
|
|
{"9",SDL_SCANCODE_9}, {"0",SDL_SCANCODE_0},
|
|
|
|
{"a",SDL_SCANCODE_A}, {"b",SDL_SCANCODE_B}, {"c",SDL_SCANCODE_C}, {"d",SDL_SCANCODE_D},
|
|
{"e",SDL_SCANCODE_E}, {"f",SDL_SCANCODE_F}, {"g",SDL_SCANCODE_G}, {"h",SDL_SCANCODE_H},
|
|
{"i",SDL_SCANCODE_I}, {"j",SDL_SCANCODE_J}, {"k",SDL_SCANCODE_K}, {"l",SDL_SCANCODE_L},
|
|
{"m",SDL_SCANCODE_M}, {"n",SDL_SCANCODE_N}, {"o",SDL_SCANCODE_O}, {"p",SDL_SCANCODE_P},
|
|
{"q",SDL_SCANCODE_Q}, {"r",SDL_SCANCODE_R}, {"s",SDL_SCANCODE_S}, {"t",SDL_SCANCODE_T},
|
|
{"u",SDL_SCANCODE_U}, {"v",SDL_SCANCODE_V}, {"w",SDL_SCANCODE_W}, {"x",SDL_SCANCODE_X},
|
|
{"y",SDL_SCANCODE_Y}, {"z",SDL_SCANCODE_Z}, {"space",SDL_SCANCODE_SPACE},
|
|
{"esc",SDL_SCANCODE_ESCAPE}, {"equals",SDL_SCANCODE_EQUALS}, {"grave",SDL_SCANCODE_GRAVE},
|
|
{"tab",SDL_SCANCODE_TAB}, {"enter",SDL_SCANCODE_RETURN}, {"bspace",SDL_SCANCODE_BACKSPACE},
|
|
{"lbracket",SDL_SCANCODE_LEFTBRACKET}, {"rbracket",SDL_SCANCODE_RIGHTBRACKET},
|
|
{"minus",SDL_SCANCODE_MINUS}, {"capslock",SDL_SCANCODE_CAPSLOCK}, {"semicolon",SDL_SCANCODE_SEMICOLON},
|
|
{"quote", SDL_SCANCODE_APOSTROPHE}, {"backslash",SDL_SCANCODE_BACKSLASH}, {"lshift",SDL_SCANCODE_LSHIFT},
|
|
{"rshift",SDL_SCANCODE_RSHIFT}, {"lalt",SDL_SCANCODE_LALT}, {"ralt",SDL_SCANCODE_RALT},
|
|
{"lctrl",SDL_SCANCODE_LCTRL}, {"rctrl",SDL_SCANCODE_RCTRL}, {"comma",SDL_SCANCODE_COMMA},
|
|
{"period",SDL_SCANCODE_PERIOD}, {"slash",SDL_SCANCODE_SLASH}, {"printscreen",SDL_SCANCODE_PRINTSCREEN},
|
|
{"scrolllock",SDL_SCANCODE_SCROLLLOCK}, {"pause",SDL_SCANCODE_PAUSE}, {"pagedown",SDL_SCANCODE_PAGEDOWN},
|
|
{"pageup",SDL_SCANCODE_PAGEUP}, {"insert",SDL_SCANCODE_INSERT}, {"home",SDL_SCANCODE_HOME},
|
|
{"delete",SDL_SCANCODE_DELETE}, {"end",SDL_SCANCODE_END}, {"up",SDL_SCANCODE_UP},
|
|
{"left",SDL_SCANCODE_LEFT}, {"down",SDL_SCANCODE_DOWN}, {"right",SDL_SCANCODE_RIGHT},
|
|
{"kp_0",SDL_SCANCODE_KP_0}, {"kp_1",SDL_SCANCODE_KP_1}, {"kp_2",SDL_SCANCODE_KP_2}, {"kp_3",SDL_SCANCODE_KP_3},
|
|
{"kp_4",SDL_SCANCODE_KP_4}, {"kp_5",SDL_SCANCODE_KP_5}, {"kp_6",SDL_SCANCODE_KP_6}, {"kp_7",SDL_SCANCODE_KP_7},
|
|
{"kp_8",SDL_SCANCODE_KP_8}, {"kp_9",SDL_SCANCODE_KP_9}, {"numlock",SDL_SCANCODE_NUMLOCKCLEAR},
|
|
{"kp_divide",SDL_SCANCODE_KP_DIVIDE}, {"kp_multiply",SDL_SCANCODE_KP_MULTIPLY},
|
|
{"kp_minus",SDL_SCANCODE_KP_MINUS}, {"kp_plus",SDL_SCANCODE_KP_PLUS},
|
|
{"kp_period",SDL_SCANCODE_KP_PERIOD}, {"kp_enter",SDL_SCANCODE_KP_ENTER},
|
|
{"kp_equals",SDL_SCANCODE_KP_EQUALS}, {"kp_comma",SDL_SCANCODE_KP_COMMA},
|
|
|
|
/* Is that the extra backslash key ("less than" key) */
|
|
/* on some keyboards with the 102-keys layout?? */
|
|
{"lessthan",SDL_SCANCODE_NONUSBACKSLASH},
|
|
#if defined(MACOSX)
|
|
{"jp_bckslash",SDL_SCANCODE_INTERNATIONAL1},
|
|
{"jp_yen",SDL_SCANCODE_INTERNATIONAL3},
|
|
#elif (defined (WIN32) || defined (__linux__))
|
|
/* Special handling for JP Keyboards */
|
|
{"jp_bckslash",SDL_SCANCODE_INTERNATIONAL1}, // SDL2 returns same code as SDL_SCANCODE_NONUSBACKSLASH?
|
|
{"jp_yen",SDL_SCANCODE_INTERNATIONAL3},
|
|
{"jp_muhenkan", SDL_SCANCODE_INTERNATIONAL5},
|
|
{"jp_henkan", SDL_SCANCODE_INTERNATIONAL4},
|
|
{"jp_hiragana", SDL_SCANCODE_INTERNATIONAL2},
|
|
#endif //(defined (WIN32) || defined (__linux__))
|
|
#if 0
|
|
#ifdef SDL_DOSBOX_X_SPECIAL
|
|
/* hack for Japanese keyboards with \ and _ */
|
|
{"jp_bckslash",SDLK_JP_RO}, // Same difference
|
|
{"jp_ro",SDLK_JP_RO}, // DOSBox proprietary
|
|
/* hack for Japanese keyboards with Yen and | */
|
|
{"jp_yen",SDLK_JP_YEN },
|
|
#endif
|
|
/* more */
|
|
{"jp_hankaku", SDLK_WORLD_12 },
|
|
{"jp_muhenkan", SDLK_WORLD_13 },
|
|
{"jp_henkan", SDLK_WORLD_14 },
|
|
{"jp_hiragana", SDLK_WORLD_15 },
|
|
{"colon", SDLK_COLON },
|
|
{"caret", SDLK_CARET },
|
|
{"atsign", SDLK_AT },
|
|
#endif
|
|
{nullptr, 0}
|
|
};
|
|
#else
|
|
|
|
static struct {
|
|
const char * eventend;
|
|
Bitu key;
|
|
} DefaultKeys[]={
|
|
{"f1",SDLK_F1}, {"f2",SDLK_F2}, {"f3",SDLK_F3}, {"f4",SDLK_F4},
|
|
{"f5",SDLK_F5}, {"f6",SDLK_F6}, {"f7",SDLK_F7}, {"f8",SDLK_F8},
|
|
{"f9",SDLK_F9}, {"f10",SDLK_F10}, {"f11",SDLK_F11}, {"f12",SDLK_F12},
|
|
|
|
{"1",SDLK_1}, {"2",SDLK_2}, {"3",SDLK_3}, {"4",SDLK_4},
|
|
{"5",SDLK_5}, {"6",SDLK_6}, {"7",SDLK_7}, {"8",SDLK_8},
|
|
{"9",SDLK_9}, {"0",SDLK_0},
|
|
|
|
{"a",SDLK_a}, {"b",SDLK_b}, {"c",SDLK_c}, {"d",SDLK_d},
|
|
{"e",SDLK_e}, {"f",SDLK_f}, {"g",SDLK_g}, {"h",SDLK_h},
|
|
{"i",SDLK_i}, {"j",SDLK_j}, {"k",SDLK_k}, {"l",SDLK_l},
|
|
{"m",SDLK_m}, {"n",SDLK_n}, {"o",SDLK_o}, {"p",SDLK_p},
|
|
{"q",SDLK_q}, {"r",SDLK_r}, {"s",SDLK_s}, {"t",SDLK_t},
|
|
{"u",SDLK_u}, {"v",SDLK_v}, {"w",SDLK_w}, {"x",SDLK_x},
|
|
{"y",SDLK_y}, {"z",SDLK_z}, {"space",SDLK_SPACE},
|
|
{"esc",SDLK_ESCAPE}, {"equals",SDLK_EQUALS}, {"grave",SDLK_BACKQUOTE},
|
|
{"tab",SDLK_TAB}, {"enter",SDLK_RETURN}, {"bspace",SDLK_BACKSPACE},
|
|
{"lbracket",SDLK_LEFTBRACKET}, {"rbracket",SDLK_RIGHTBRACKET},
|
|
{"minus",SDLK_MINUS}, {"capslock",SDLK_CAPSLOCK}, {"semicolon",SDLK_SEMICOLON},
|
|
{"quote", SDLK_QUOTE}, {"backslash",SDLK_BACKSLASH}, {"lshift",SDLK_LSHIFT},
|
|
{"rshift",SDLK_RSHIFT}, {"lalt",SDLK_LALT}, {"ralt",SDLK_RALT},
|
|
{"lctrl",SDLK_LCTRL}, {"rctrl",SDLK_RCTRL}, {"comma",SDLK_COMMA},
|
|
{"period",SDLK_PERIOD}, {"slash",SDLK_SLASH},
|
|
|
|
{"printscreen",SDLK_PRINT},
|
|
{"scrolllock",SDLK_SCROLLOCK},
|
|
|
|
{"pause",SDLK_PAUSE}, {"pagedown",SDLK_PAGEDOWN},
|
|
{"pageup",SDLK_PAGEUP}, {"insert",SDLK_INSERT}, {"home",SDLK_HOME},
|
|
{"delete",SDLK_DELETE}, {"end",SDLK_END}, {"up",SDLK_UP},
|
|
{"left",SDLK_LEFT}, {"down",SDLK_DOWN}, {"right",SDLK_RIGHT},
|
|
|
|
{"kp_0",SDLK_KP0}, {"kp_1",SDLK_KP1}, {"kp_2",SDLK_KP2}, {"kp_3",SDLK_KP3},
|
|
{"kp_4",SDLK_KP4}, {"kp_5",SDLK_KP5}, {"kp_6",SDLK_KP6}, {"kp_7",SDLK_KP7},
|
|
{"kp_8",SDLK_KP8}, {"kp_9",SDLK_KP9},
|
|
{"numlock",SDLK_NUMLOCK},
|
|
|
|
{"kp_divide",SDLK_KP_DIVIDE}, {"kp_multiply",SDLK_KP_MULTIPLY},
|
|
{"kp_minus",SDLK_KP_MINUS}, {"kp_plus",SDLK_KP_PLUS},
|
|
{"kp_period",SDLK_KP_PERIOD}, {"kp_enter",SDLK_KP_ENTER},
|
|
|
|
/* NTS: IBM PC keyboards as far as I know do not have numeric keypad equals sign.
|
|
* This default assignment should allow Apple Mac users (who's keyboards DO have one)
|
|
* to use theirs as a normal equals sign. */
|
|
{"kp_equals",SDLK_KP_EQUALS},
|
|
{"kp_comma", SDLK_KP_COMMA},
|
|
|
|
#if defined(C_SDL2)
|
|
// TODO??
|
|
#else
|
|
/* Windows 95 keyboard stuff */
|
|
{"lwindows",SDLK_LSUPER},
|
|
{"rwindows",SDLK_RSUPER},
|
|
#endif
|
|
{"rwinmenu",SDLK_MENU},
|
|
|
|
#if defined (MACOSX)
|
|
/* Intl Mac keyboards in US layout actually put U+00A7 SECTION SIGN here */
|
|
{"lessthan",SDLK_WORLD_0},
|
|
#else
|
|
{"lessthan",SDLK_LESS},
|
|
#endif
|
|
|
|
#if defined(C_SDL2)
|
|
// TODO??
|
|
#else
|
|
#ifdef SDL_DOSBOX_X_SPECIAL
|
|
#if defined(MACOSX)
|
|
{"jp_bckslash",SDLK_JP_RO},
|
|
{"jp_yen",SDLK_JP_YEN },
|
|
#else
|
|
/* hack for Japanese keyboards with \ and _ */
|
|
{"jp_bckslash",SDLK_JP_RO}, // Same difference
|
|
//{"jp_ro",SDLK_JP_RO}, // DOSBox proprietary
|
|
/* hack for Japanese keyboards with Yen and | */
|
|
{"jp_yen",SDLK_JP_YEN },
|
|
#endif
|
|
#endif
|
|
/* more */
|
|
{"jp_hankaku", SDLK_WORLD_12 },
|
|
{"jp_muhenkan", SDLK_WORLD_13 },
|
|
{"jp_henkan", SDLK_WORLD_14 },
|
|
{"jp_hiragana", SDLK_WORLD_15 },
|
|
{"colon", SDLK_COLON },
|
|
{"caret", SDLK_CARET },
|
|
{"atsign", SDLK_AT },
|
|
#endif
|
|
|
|
{0,0}
|
|
};
|
|
|
|
#endif
|
|
|
|
static void ClearAllBinds(void) {
|
|
for (CEvent *event : events)
|
|
event->ClearBinds();
|
|
}
|
|
|
|
static void CreateDefaultBinds(void) {
|
|
ClearAllBinds();
|
|
char buffer[512];
|
|
Bitu i=0;
|
|
while (DefaultKeys[i].eventend) {
|
|
sprintf(buffer,"key_%s \"key %d\"",DefaultKeys[i].eventend,(int)DefaultKeys[i].key);
|
|
CreateStringBind(buffer);
|
|
i++;
|
|
}
|
|
|
|
#if defined(C_SDL2)
|
|
sprintf(buffer,"mod_1 \"key %d\"",SDL_SCANCODE_RCTRL);CreateStringBind(buffer);
|
|
sprintf(buffer,"mod_1 \"key %d\"",SDL_SCANCODE_LCTRL);CreateStringBind(buffer);
|
|
sprintf(buffer,"mod_2 \"key %d\"",SDL_SCANCODE_RALT);CreateStringBind(buffer);
|
|
sprintf(buffer,"mod_2 \"key %d\"",SDL_SCANCODE_LALT);CreateStringBind(buffer);
|
|
sprintf(buffer,"mod_3 \"key %d\"",SDL_SCANCODE_RSHIFT);CreateStringBind(buffer);
|
|
sprintf(buffer,"mod_3 \"key %d\"",SDL_SCANCODE_LSHIFT);CreateStringBind(buffer);
|
|
# 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 */
|
|
sprintf(buffer,"host \"key %d\"",SDL_SCANCODE_F11);CreateStringBind(buffer);
|
|
# else
|
|
sprintf(buffer,"host \"key %d\"",SDL_SCANCODE_F12);CreateStringBind(buffer);
|
|
# endif
|
|
#else
|
|
sprintf(buffer,"mod_1 \"key %d\"",SDLK_RCTRL);CreateStringBind(buffer);
|
|
sprintf(buffer,"mod_1 \"key %d\"",SDLK_LCTRL);CreateStringBind(buffer);
|
|
sprintf(buffer,"mod_2 \"key %d\"",SDLK_RALT);CreateStringBind(buffer);
|
|
sprintf(buffer,"mod_2 \"key %d\"",SDLK_LALT);CreateStringBind(buffer);
|
|
sprintf(buffer,"mod_3 \"key %d\"",SDLK_RSHIFT);CreateStringBind(buffer);
|
|
sprintf(buffer,"mod_3 \"key %d\"",SDLK_LSHIFT);CreateStringBind(buffer);
|
|
# 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 */
|
|
sprintf(buffer,"host \"key %d\"",SDLK_F11);CreateStringBind(buffer);
|
|
# else
|
|
sprintf(buffer,"host \"key %d\"",SDLK_F12);CreateStringBind(buffer);
|
|
# endif
|
|
#endif
|
|
|
|
for (CHandlerEventVector_it hit=handlergroup.begin();hit!=handlergroup.end();++hit) {
|
|
(*hit)->MakeDefaultBind(buffer);
|
|
CreateStringBind(buffer);
|
|
}
|
|
|
|
/* joystick1, buttons 1-6 */
|
|
sprintf(buffer,"jbutton_0_0 \"stick_0 button 0\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jbutton_0_1 \"stick_0 button 1\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jbutton_0_2 \"stick_0 button 2\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jbutton_0_3 \"stick_0 button 3\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jbutton_0_4 \"stick_0 button 4\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jbutton_0_5 \"stick_0 button 5\" ");CreateStringBind(buffer);
|
|
/* joystick2, buttons 1-2 */
|
|
sprintf(buffer,"jbutton_1_0 \"stick_1 button 0\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jbutton_1_1 \"stick_1 button 1\" ");CreateStringBind(buffer);
|
|
|
|
/* joystick1, axes 1-4 */
|
|
sprintf(buffer,"jaxis_0_0- \"stick_0 axis 0 0\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jaxis_0_0+ \"stick_0 axis 0 1\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jaxis_0_1- \"stick_0 axis 1 0\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jaxis_0_1+ \"stick_0 axis 1 1\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jaxis_0_2- \"stick_0 axis 2 0\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jaxis_0_2+ \"stick_0 axis 2 1\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jaxis_0_3- \"stick_0 axis 3 0\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jaxis_0_3+ \"stick_0 axis 3 1\" ");CreateStringBind(buffer);
|
|
/* joystick2, axes 1-2 */
|
|
sprintf(buffer,"jaxis_1_0- \"stick_1 axis 0 0\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jaxis_1_0+ \"stick_1 axis 0 1\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jaxis_1_1- \"stick_1 axis 1 0\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jaxis_1_1+ \"stick_1 axis 1 1\" ");CreateStringBind(buffer);
|
|
|
|
/* joystick1, hat */
|
|
sprintf(buffer,"jhat_0_0_0 \"stick_0 hat 0 1\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jhat_0_0_1 \"stick_0 hat 0 2\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jhat_0_0_2 \"stick_0 hat 0 4\" ");CreateStringBind(buffer);
|
|
sprintf(buffer,"jhat_0_0_3 \"stick_0 hat 0 8\" ");CreateStringBind(buffer);
|
|
}
|
|
|
|
void MAPPER_AddHandler(MAPPER_Handler * handler,MapKeys key,Bitu mods,char const * const eventname,char const * const buttonname,DOSBoxMenu::item **ret_menuitem) {
|
|
if (ret_menuitem != NULL)
|
|
*ret_menuitem = NULL;
|
|
|
|
char tempname[27];
|
|
strcpy(tempname,"hand_");
|
|
strcat(tempname,eventname);
|
|
|
|
//Check if it already exists=> if so return.
|
|
for(CHandlerEventVector_it it=handlergroup.begin();it!=handlergroup.end();++it) {
|
|
if(strcmp((*it)->buttonname,buttonname) == 0) {
|
|
if (ret_menuitem != NULL)
|
|
*ret_menuitem = &mainMenu.get_item(std::string("mapper_") + std::string(eventname));
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
CHandlerEvent *event = new CHandlerEvent(tempname,handler,key,mods,buttonname);
|
|
event->eventname = eventname;
|
|
|
|
/* The mapper now automatically makes menu items for mapper events */
|
|
DOSBoxMenu::item &menuitem = mainMenu.alloc_item(DOSBoxMenu::item_type_id, std::string("mapper_") + std::string(eventname));
|
|
menuitem.set_mapper_event(tempname);
|
|
|
|
if (ret_menuitem == NULL)
|
|
menuitem.set_text(buttonname);
|
|
else
|
|
*ret_menuitem = &menuitem;
|
|
|
|
if (mapper_addhandler_create_buttons) {
|
|
// and a button in the mapper UI
|
|
{
|
|
unsigned int columns = ((unsigned int)strlen(buttonname) + 9U) / 10U;
|
|
if ((next_handler_xpos+columns-1)>6) {
|
|
next_handler_xpos=3;next_handler_ypos++;
|
|
}
|
|
CEventButton *button=new CEventButton(PX(next_handler_xpos*3),PY(next_handler_ypos),BW*3*columns,BH,buttonname,event);
|
|
event->notifybutton(button);
|
|
next_handler_xpos += columns;
|
|
if (next_handler_xpos>6) {
|
|
next_handler_xpos=3;next_handler_ypos++;
|
|
}
|
|
}
|
|
|
|
// this event may have appeared in the user's mapper file, and been ignored.
|
|
// now is the time to register it.
|
|
{
|
|
auto i = pending_string_binds.find(tempname);
|
|
char tmp[512];
|
|
|
|
if (i != pending_string_binds.end()) {
|
|
LOG(LOG_MISC,LOG_WARN)("Found pending event for %s from user's file, applying now",tempname);
|
|
|
|
snprintf(tmp,sizeof(tmp),"%s %s",tempname,i->second.c_str());
|
|
|
|
CreateStringBind(tmp);
|
|
|
|
pending_string_binds.erase(i);
|
|
}
|
|
else {
|
|
/* use default binding.
|
|
* redundant? Yes! But, apparently necessary. */
|
|
event->MakeDefaultBind(tmp);
|
|
CreateStringBind(tmp);
|
|
}
|
|
|
|
// color of the button needs to reflect binding
|
|
event->notify_button->BindColor();
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
static void MAPPER_SaveBinds(void) {
|
|
std::string content="";
|
|
FILE * loadfile=fopen(mapper.filename.c_str(),"rt");
|
|
if (loadfile) {
|
|
char secname[512], linein[512], *line=linein;
|
|
strcpy(secname, "");
|
|
while (fgets(linein,512,loadfile)) {
|
|
line=trim(line);
|
|
if (strlen(line)>2 && line[0]=='[' && line[strlen(line)-1]==']') {
|
|
linein[strlen(line)-1] = 0;
|
|
strcpy(secname, line+1);
|
|
if (strcasecmp(secname, SDL_STRING)) content+=std::string(line)+"]\n";
|
|
} else if (strlen(secname)&&strcasecmp(secname, SDL_STRING))
|
|
content+=std::string(linein)+"\n";
|
|
}
|
|
fclose(loadfile);
|
|
}
|
|
|
|
FILE * savefile=fopen(mapper.filename.c_str(),"wt+");
|
|
if (!savefile) {
|
|
LOG_MSG("Can't open %s for saving the mappings",mapper.filename.c_str());
|
|
return;
|
|
}
|
|
fprintf(savefile,"[%s]\n",SDL_STRING);
|
|
char buf[128];
|
|
for (CEventVector_it event_it=events.begin();event_it!=events.end();++event_it) {
|
|
CEvent * event=*(event_it);
|
|
fprintf(savefile,"%s ",event->GetName());
|
|
for (CBindList_it bind_it=event->bindlist.begin();bind_it!=event->bindlist.end();++bind_it) {
|
|
CBind * bind=*(bind_it);
|
|
bind->ConfigName(buf);
|
|
bind->AddFlags(buf);
|
|
fprintf(savefile,"\"%s\" ",buf);
|
|
}
|
|
fprintf(savefile,"\n");
|
|
}
|
|
if (content.size()) {
|
|
fprintf(savefile,"\n");
|
|
std::istringstream f(content);
|
|
std::string line;
|
|
while (std::getline(f, line))
|
|
fprintf(savefile,"%s \n",line.c_str());
|
|
}
|
|
fclose(savefile);
|
|
#if defined(WIN32)
|
|
char path[MAX_PATH];
|
|
if (GetFullPathName(mapper.filename.c_str(), MAX_PATH, path, NULL)) LOG_MSG("Saved mapper file: %s", path);
|
|
#elif defined(HAVE_REALPATH)
|
|
char path[PATH_MAX];
|
|
if (realpath(mapper.filename.c_str(), path) != NULL) LOG_MSG("Saved mapper file: %s", path);
|
|
#endif
|
|
std::string str = mapper.filename.substr(mapper.filename.find_last_of("/\\") + 1);
|
|
std::string msg = MSG_Get("MAPPER_FILE_SAVED")+std::string(": ")+str;
|
|
change_action_text(msg.c_str(),CLR_WHITE);
|
|
}
|
|
|
|
static bool MAPPER_LoadBinds(void) {
|
|
FILE * loadfile=fopen(mapper.filename.c_str(),"rt");
|
|
if (!loadfile) return false;
|
|
ClearAllBinds();
|
|
bool othersec=false, hasbind=false;
|
|
char secname[512], linein[512], *line=linein;
|
|
strcpy(secname, "");
|
|
while (fgets(linein,512,loadfile)) {
|
|
line=trim(line);
|
|
if (strlen(line)>2 && line[0]=='[' && line[strlen(line)-1]==']') {
|
|
linein[strlen(line)-1] = 0;
|
|
strcpy(secname, line+1);
|
|
if (strcasecmp(secname, SDL_STRING)) othersec=true;
|
|
} else if (!strlen(secname)||!strcasecmp(secname, SDL_STRING)) {
|
|
hasbind=true;
|
|
CreateStringBind(linein,/*loading*/true);
|
|
}
|
|
}
|
|
fclose(loadfile);
|
|
if (othersec&&!hasbind) return false;
|
|
LOG(LOG_MISC,LOG_NORMAL)("MAPPER: Loading mapper settings from %s", mapper.filename.c_str());
|
|
return true;
|
|
}
|
|
|
|
void MAPPER_CheckEvent(SDL_Event * event) {
|
|
for (CBindGroup_it it=bindgroups.begin();it!=bindgroups.end();++it) {
|
|
if ((*it)->CheckEvent(event)) return;
|
|
}
|
|
|
|
if (log_keyboard_scan_codes) {
|
|
if (event->type == SDL_KEYDOWN || event->type == SDL_KEYUP)
|
|
LOG_MSG("MAPPER: SDL keyboard event (%s): scancode=0x%X sym=0x%X mod=0x%X",
|
|
event->type == SDL_KEYDOWN?"down":"up",
|
|
event->key.keysym.scancode,event->key.keysym.sym,event->key.keysym.mod);
|
|
}
|
|
}
|
|
|
|
void PressRelease(void) {
|
|
if (press_select != NULL) {
|
|
press_select->SetPress(false);
|
|
press_select = NULL;
|
|
}
|
|
}
|
|
|
|
void PressSelect(CButton *b) {
|
|
if (press_select != b)
|
|
PressRelease();
|
|
|
|
if (b != NULL) {
|
|
press_select = b;
|
|
press_select->SetPress(true);
|
|
}
|
|
}
|
|
|
|
void Mapper_MousePressEvent(SDL_Event &event) {
|
|
/* Check the press */
|
|
for (CButton_it but_it = buttons.begin();but_it!=buttons.end();++but_it) {
|
|
if ((*but_it)->OnTop(event.button.x,event.button.y)) {
|
|
PressSelect(*but_it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Mapper_MouseInputEvent(SDL_Event &event) {
|
|
PressRelease();
|
|
|
|
/* Check the press */
|
|
for (CButton_it but_it = buttons.begin();but_it!=buttons.end();++but_it) {
|
|
if ((*but_it)->OnTop(event.button.x,event.button.y)) {
|
|
(*but_it)->Click();
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(C_SDL2)
|
|
void Mapper_FingerPressEvent(SDL_Event &event) {
|
|
SDL_Event ev;
|
|
|
|
memset(&ev,0,sizeof(ev));
|
|
ev.type = SDL_MOUSEBUTTONDOWN;
|
|
|
|
/* NTS: Windows versions of SDL2 do normalize the coordinates */
|
|
ev.button.x = (Sint32)(event.tfinger.x * mapper.surface->w);
|
|
ev.button.y = (Sint32)(event.tfinger.y * mapper.surface->h);
|
|
|
|
Mapper_MousePressEvent(ev);
|
|
}
|
|
|
|
void Mapper_FingerInputEvent(SDL_Event &event) {
|
|
SDL_Event ev;
|
|
|
|
memset(&ev,0,sizeof(ev));
|
|
ev.type = SDL_MOUSEBUTTONUP;
|
|
|
|
/* NTS: Windows versions of SDL2 do normalize the coordinates */
|
|
ev.button.x = (Sint32)(event.tfinger.x * mapper.surface->w);
|
|
ev.button.y = (Sint32)(event.tfinger.y * mapper.surface->h);
|
|
|
|
Mapper_MouseInputEvent(ev);
|
|
}
|
|
#endif
|
|
|
|
void BIND_MappingEvents(void) {
|
|
SDL_Event event;
|
|
|
|
if (GUI_JoystickCount()>0) SDL_JoystickUpdate();
|
|
MAPPER_UpdateJoysticks();
|
|
|
|
#if C_EMSCRIPTEN
|
|
emscripten_sleep(0);
|
|
#endif
|
|
|
|
while (SDL_PollEvent(&event)) {
|
|
#if C_EMSCRIPTEN
|
|
emscripten_sleep(0);
|
|
#endif
|
|
|
|
switch (event.type) {
|
|
#if defined(_WIN32) && !defined(HX_DOS)
|
|
case SDL_SYSWMEVENT : {
|
|
switch ( event.syswm.msg->
|
|
#if defined(C_SDL2)
|
|
msg.win.
|
|
#endif
|
|
msg ) {
|
|
case WM_COMMAND:
|
|
# if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
if (GetMenu(GetHWND())) {
|
|
# if defined(C_SDL2)
|
|
if (mapperMenu.mainMenuWM_COMMAND((unsigned int)LOWORD(event.syswm.msg->msg.win.wParam))) return;
|
|
# else
|
|
if (mapperMenu.mainMenuWM_COMMAND((unsigned int)LOWORD(event.syswm.msg->wParam))) return;
|
|
# endif
|
|
}
|
|
# endif
|
|
break;
|
|
}
|
|
} break;
|
|
#endif
|
|
#if defined(C_SDL2) && !defined(IGNORE_TOUCHSCREEN)
|
|
case SDL_FINGERDOWN:
|
|
Mapper_FingerPressEvent(event);
|
|
break;
|
|
#endif
|
|
#if defined(C_SDL2) && !defined(IGNORE_TOUCHSCREEN)
|
|
case SDL_FINGERUP:
|
|
Mapper_FingerInputEvent(event);
|
|
break;
|
|
#endif
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
#if defined(C_SDL2)
|
|
if (event.button.which != SDL_TOUCH_MOUSEID) /* don't handle mouse events faked by touchscreen */
|
|
Mapper_MousePressEvent(event);
|
|
#else
|
|
Mapper_MousePressEvent(event);
|
|
#endif
|
|
break;
|
|
case SDL_MOUSEBUTTONUP:
|
|
#if defined(C_SDL2)
|
|
if (event.button.which != SDL_TOUCH_MOUSEID) /* don't handle mouse events faked by touchscreen */
|
|
Mapper_MouseInputEvent(event);
|
|
#else
|
|
Mapper_MouseInputEvent(event);
|
|
#endif
|
|
break;
|
|
case SDL_QUIT:
|
|
mapper.exit=true;
|
|
break;
|
|
case SDL_KEYDOWN: /* help the user determine keyboard problems by showing keyboard event, scan code, etc. */
|
|
case SDL_KEYUP:
|
|
{
|
|
static int event_count = 0;
|
|
#if defined(C_SDL2)
|
|
SDL_Keysym &s = event.key.keysym;
|
|
#else
|
|
SDL_keysym &s = event.key.keysym;
|
|
#endif
|
|
char tmp[256];
|
|
|
|
// ESC is your magic key out of capture
|
|
if (s.sym == SDLK_ESCAPE) {
|
|
if (mouselocked) {
|
|
GFX_CaptureMouse();
|
|
mapper_esc_count = 0;
|
|
change_action_text(MSG_Get("SELECT_EVENT"), CLR_WHITE);
|
|
}
|
|
else {
|
|
if (event.type == SDL_KEYUP) {
|
|
if (++mapper_esc_count == 3) {
|
|
void MAPPER_ReleaseAllKeys(void);
|
|
MAPPER_ReleaseAllKeys();
|
|
mapper.exit=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
mapper_esc_count = 0;
|
|
}
|
|
|
|
size_t tmpl;
|
|
#if defined(C_SDL2)
|
|
SDL_Scancode key = s.scancode;
|
|
#if defined(WIN32)
|
|
if(key == SDL_SCANCODE_NONUSBACKSLASH) { // Special consideration for JP Keyboard
|
|
key = (isJPkeyboard ? SDL_SCANCODE_INTERNATIONAL1 : SDL_SCANCODE_NONUSBACKSLASH);
|
|
}
|
|
#endif
|
|
tmpl = (size_t)sprintf(tmp,"%c%02x: scan=%d sym=%d mod=%xh name=%s",
|
|
(event.type == SDL_KEYDOWN ? 'D' : 'U'),
|
|
event_count&0xFF,
|
|
key,
|
|
s.sym,
|
|
s.mod,
|
|
SDL_GetScancodeName(key));
|
|
#else
|
|
tmpl = (size_t)sprintf(tmp,"%c%02x: scan=%u sym=%u mod=%xh u=%xh name=%s",
|
|
(event.type == SDL_KEYDOWN ? 'D' : 'U'),
|
|
event_count&0xFF,
|
|
s.scancode,
|
|
s.sym,
|
|
s.mod,
|
|
s.unicode,
|
|
SDL_GetKeyName((SDLKey)GetKeyCode(s)));
|
|
//(s.sym ? SDL_GetKeyName((SDLKey)MapSDLCode((Bitu)s.sym)) : SDL_GetKeyName((SDLKey)MapSDLCode((Bitu)sdlkey_map[(s.scancode ? s.scancode : event.key.keysym.scancode)]))));
|
|
#endif
|
|
while (tmpl < (440/8)) tmp[tmpl++] = ' ';
|
|
assert(tmpl < sizeof(tmp));
|
|
tmp[tmpl] = 0;
|
|
|
|
LOG(LOG_GUI,LOG_DEBUG)("Mapper keyboard event: %s",tmp);
|
|
bind_but.dbg1->Change("%s",tmp);
|
|
|
|
tmpl = 0;
|
|
#if defined(WIN32)
|
|
# if defined(C_SDL2)
|
|
# elif defined(SDL_DOSBOX_X_SPECIAL)
|
|
{
|
|
char nm[256];
|
|
|
|
nm[0] = 0;
|
|
#if !defined(HX_DOS) /* I assume HX DOS doesn't bother with keyboard scancode names */
|
|
GetKeyNameText((s.scancode ? s.scancode : event.key.keysym.scancode) << 16, nm, sizeof(nm)-1);
|
|
#endif
|
|
|
|
tmpl = sprintf(tmp, "Win32: VK=0x%x kn=%s",(unsigned int)s.win32_vk,nm);
|
|
}
|
|
# endif
|
|
#endif
|
|
#if defined(SDL_VIDEO_DRIVER_X11)
|
|
# if defined(C_SDL2)
|
|
# elif defined(SDL_DOSBOX_X_SPECIAL) && defined(LINUX)
|
|
{
|
|
char *LinuxX11_KeySymName(Uint32 x);
|
|
|
|
char *name;
|
|
|
|
name = LinuxX11_KeySymName(s.x11_sym);
|
|
tmpl = (size_t)sprintf(tmp,"X11: Sym=0x%x sn=%s",(unsigned int)s.x11_sym,name ? name : "");
|
|
}
|
|
# endif
|
|
#endif
|
|
while (tmpl < (250 / 8)) tmp[tmpl++] = ' ';
|
|
assert(tmpl < sizeof(tmp));
|
|
tmp[tmpl] = 0;
|
|
bind_but.dbg2->Change("%s", tmp);
|
|
|
|
event_count++;
|
|
}
|
|
/* fall through to mapper UI processing */
|
|
default:
|
|
if (mapper.addbind) {
|
|
for (CBindGroup_it it=bindgroups.begin();it!=bindgroups.end();++it) {
|
|
CBind * newbind=(*it)->CreateEventBind(&event);
|
|
if (!newbind) continue;
|
|
assert(mapper.aevent != NULL);
|
|
mapper.aevent->AddBind(newbind);
|
|
SetActiveEvent(mapper.aevent);
|
|
mapper.addbind=false;
|
|
mapper.aevent->update_menu_shortcut();
|
|
RedrawMapperBindButton(mapper.aevent);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MAPPER_CheckEvent(SDL_Event * event);
|
|
MAPPER_CheckEvent(&event);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void InitializeJoysticks(void) {
|
|
mapper.sticks.num=0;
|
|
mapper.sticks.num_groups=0;
|
|
if (joytype != JOY_NONE) {
|
|
mapper.sticks.num=(Bitu)SDL_NumJoysticks();
|
|
LOG(LOG_MISC,LOG_DEBUG)("Joystick type != none, SDL reports %u sticks",(unsigned int)mapper.sticks.num);
|
|
if (joytype==JOY_AUTO) {
|
|
// try to figure out what joystick type to select
|
|
// depending on the number of physically attached joysticks
|
|
if (mapper.sticks.num>1) {
|
|
// more than one joystick present; if all are acceptable use 2axis
|
|
// to allow emulation of two joysticks
|
|
bool first_usable=false;
|
|
SDL_Joystick* tmp_stick1=SDL_JoystickOpen(0);
|
|
if (tmp_stick1) {
|
|
if ((SDL_JoystickNumAxes(tmp_stick1)>1) || (SDL_JoystickNumButtons(tmp_stick1)>0)) {
|
|
first_usable=true;
|
|
}
|
|
SDL_JoystickClose(tmp_stick1);
|
|
}
|
|
bool second_usable=false;
|
|
SDL_Joystick* tmp_stick2=SDL_JoystickOpen(1);
|
|
if (tmp_stick2) {
|
|
if ((SDL_JoystickNumAxes(tmp_stick2)>1) || (SDL_JoystickNumButtons(tmp_stick2)>0)) {
|
|
second_usable=true;
|
|
}
|
|
SDL_JoystickClose(tmp_stick2);
|
|
}
|
|
// choose joystick type now that we know which physical joysticks are usable
|
|
if (first_usable) {
|
|
if (second_usable) {
|
|
joytype=JOY_2AXIS;
|
|
LOG_MSG("Two or more joysticks reported, initializing with 2axis");
|
|
} else {
|
|
joytype=JOY_4AXIS;
|
|
LOG_MSG("One joystick reported, initializing with 4axis");
|
|
}
|
|
} else if (second_usable) {
|
|
joytype=JOY_4AXIS_2;
|
|
LOG_MSG("One joystick reported, initializing with 4axis_2");
|
|
}
|
|
} else if (mapper.sticks.num) {
|
|
// one joystick present; if it is acceptable use 4axis
|
|
joytype=JOY_NONE;
|
|
SDL_Joystick* tmp_stick1=SDL_JoystickOpen(0);
|
|
if (tmp_stick1) {
|
|
if ((SDL_JoystickNumAxes(tmp_stick1)>0) || (SDL_JoystickNumButtons(tmp_stick1)>0)) {
|
|
joytype=JOY_4AXIS;
|
|
LOG_MSG("One joystick reported, initializing with 4axis");
|
|
}
|
|
}
|
|
} else {
|
|
joytype=JOY_NONE;
|
|
}
|
|
}
|
|
initjoy=false;
|
|
}
|
|
else {
|
|
LOG(LOG_MISC,LOG_DEBUG)("Joystick type none, not initializing");
|
|
}
|
|
}
|
|
|
|
static void CreateBindGroups(void) {
|
|
bindgroups.clear();
|
|
#if defined(C_SDL2)
|
|
new CKeyBindGroup(SDL_NUM_SCANCODES);
|
|
#else
|
|
new CKeyBindGroup(SDLK_LAST);
|
|
#endif
|
|
if (joytype != JOY_NONE) {
|
|
#if defined (REDUCE_JOYSTICK_POLLING)
|
|
// direct access to the SDL joystick, thus removed from the event handling
|
|
if (mapper.sticks.num) SDL_JoystickEventState(SDL_DISABLE);
|
|
#else
|
|
// enable joystick event handling
|
|
if (mapper.sticks.num) SDL_JoystickEventState(SDL_ENABLE);
|
|
else return;
|
|
#endif
|
|
uint8_t joyno=0;
|
|
switch (joytype) {
|
|
case JOY_NONE:
|
|
break;
|
|
case JOY_4AXIS:
|
|
mapper.sticks.stick[mapper.sticks.num_groups++]=new C4AxisBindGroup(joyno,joyno);
|
|
new CStickBindGroup(joyno+1U,joyno+1U,true);
|
|
break;
|
|
case JOY_4AXIS_2:
|
|
mapper.sticks.stick[mapper.sticks.num_groups++]=new C4AxisBindGroup(joyno+1U,joyno);
|
|
new CStickBindGroup(joyno,joyno+1U,true);
|
|
break;
|
|
case JOY_FCS:
|
|
mapper.sticks.stick[mapper.sticks.num_groups++]=new CFCSBindGroup(joyno,joyno);
|
|
new CStickBindGroup(joyno+1U,joyno+1U,true);
|
|
break;
|
|
case JOY_CH:
|
|
mapper.sticks.stick[mapper.sticks.num_groups++]=new CCHBindGroup(joyno,joyno);
|
|
new CStickBindGroup(joyno+1U,joyno+1U,true);
|
|
break;
|
|
case JOY_2AXIS:
|
|
default:
|
|
mapper.sticks.stick[mapper.sticks.num_groups++]=new CStickBindGroup(joyno,joyno);
|
|
if((joyno+1U) < mapper.sticks.num) {
|
|
mapper.sticks.stick[mapper.sticks.num_groups++]=new CStickBindGroup(joyno+1U,joyno+1U);
|
|
} else {
|
|
new CStickBindGroup(joyno+1U,joyno+1U,true);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined (REDUCE_JOYSTICK_POLLING)
|
|
void MAPPER_UpdateJoysticks(void) {
|
|
for (Bitu i=0; i<mapper.sticks.num_groups; i++) {
|
|
mapper.sticks.stick[i]->UpdateJoystick();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void MAPPER_LosingFocus(void) {
|
|
for (CEventVector_it evit=events.begin();evit!=events.end();++evit) {
|
|
if(*evit != caps_lock_event && *evit != num_lock_event)
|
|
(*evit)->DeActivateAll();
|
|
}
|
|
}
|
|
|
|
void MAPPER_ReleaseAllKeys(void) {
|
|
for (CEventVector_it evit=events.begin();evit!=events.end();++evit) {
|
|
if ((*evit)->active) {
|
|
LOG_MSG("Release");
|
|
(*evit)->Active(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MAPPER_RunEvent(Bitu /*val*/) {
|
|
if (hostbutton != NULL) hostbutton->Enable(hostkeyalt == 0);
|
|
KEYBOARD_ClrBuffer(); //Clear buffer
|
|
GFX_LosingFocus(); //Release any keys pressed (buffer gets filled again).
|
|
MAPPER_RunInternal();
|
|
}
|
|
|
|
void MAPPER_Run(bool pressed) {
|
|
if (pressed)
|
|
return;
|
|
PIC_AddEvent(MAPPER_RunEvent,0.0001f); //In case mapper deletes the key object that ran it
|
|
}
|
|
|
|
void update_all_shortcuts() {
|
|
for (auto &ev : events)
|
|
if (ev != NULL) ev->update_menu_shortcut();
|
|
}
|
|
|
|
void MAPPER_RunInternal() {
|
|
MAPPER_ReleaseAllKeys();
|
|
|
|
#ifdef DOSBOXMENU_EXTERNALLY_MANAGED
|
|
{
|
|
DOSBoxMenu::item &item = mapperMenu.get_item("MapperMenu");
|
|
item.set_text(mainMenu.get_item("mapper_mapper").get_text());
|
|
}
|
|
|
|
{
|
|
DOSBoxMenu::item &item = mapperMenu.get_item("ExitMapper");
|
|
item.set_text(MSG_Get("MAPPER_EDITOR_EXIT"));
|
|
}
|
|
|
|
{
|
|
DOSBoxMenu::item &item = mapperMenu.get_item("SaveMapper");
|
|
item.set_text(MSG_Get("SAVE_MAPPER_FILE"));
|
|
}
|
|
# if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU || DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU
|
|
if (loadlang) mapperMenu.unbuild();
|
|
# endif
|
|
mapperMenu.rebuild();
|
|
#endif
|
|
|
|
/* Sorry, the MAPPER screws up 3Dfx OpenGL emulation.
|
|
* Remove this block when fixed. */
|
|
if (GFX_GetPreventFullscreen()) {
|
|
systemmessagebox("Mapper Editor","Mapper Editor is not currently available.","ok", "info", 1);
|
|
LOG_MSG("Mapper Editor is not available while 3Dfx OpenGL emulation is running");
|
|
return;
|
|
}
|
|
|
|
mapper_esc_count = 0;
|
|
mapper.running = true;
|
|
|
|
#if defined(__WIN32__) && !defined(C_SDL2) && !defined(C_HX_DOS)
|
|
if(menu.maxwindow) ShowWindow(GetHWND(), SW_RESTORE);
|
|
#endif
|
|
int cursor = SDL_ShowCursor(SDL_QUERY);
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
bool mousetoggle=false;
|
|
if(mouselocked) {
|
|
mousetoggle=true;
|
|
GFX_CaptureMouse();
|
|
}
|
|
|
|
/* Be sure that there is no update in progress */
|
|
GFX_EndUpdate(nullptr);
|
|
#if defined(C_SDL2)
|
|
void GFX_SetResizeable(bool enable);
|
|
GFX_SetResizeable(false);
|
|
mapper.window = OpenGL_using() ? GFX_SetSDLWindowMode(640,480,SCREEN_OPENGL) : GFX_SetSDLSurfaceWindow(640,480);
|
|
if (mapper.window == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError());
|
|
mapper.surface=SDL_GetWindowSurface(mapper.window);
|
|
if (mapper.surface == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError());
|
|
mapper.draw_surface=SDL_CreateRGBSurface(0,640,480,8,0,0,0,0);
|
|
// Needed for SDL_BlitScaled
|
|
mapper.draw_surface_nonpaletted=SDL_CreateRGBSurface(0,640,480,32,0x0000ff00,0x00ff0000,0xff000000,0);
|
|
mapper.draw_rect=GFX_GetSDLSurfaceSubwindowDims(640,480);
|
|
// Sorry, but SDL_SetSurfacePalette requires a full palette.
|
|
SDL_Palette *sdl2_map_pal_ptr = SDL_AllocPalette(256);
|
|
SDL_SetPaletteColors(sdl2_map_pal_ptr, map_pal, 0, 7);
|
|
SDL_SetSurfacePalette(mapper.draw_surface, sdl2_map_pal_ptr);
|
|
if (last_clicked) {
|
|
last_clicked->SetColor(CLR_WHITE);
|
|
last_clicked=NULL;
|
|
}
|
|
#else
|
|
mapper.surface=SDL_SetVideoMode(640,480,8,0);
|
|
if (mapper.surface == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError());
|
|
|
|
/* Set some palette entries */
|
|
SDL_SetPalette(mapper.surface, SDL_LOGPAL|SDL_PHYSPAL, map_pal, 0, 7);
|
|
if (last_clicked) {
|
|
last_clicked->BindColor();
|
|
last_clicked=NULL;
|
|
}
|
|
#endif
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS) && !defined(_WIN32_WINDOWS)
|
|
WindowsTaskbarResetPreviewRegion();
|
|
#endif
|
|
|
|
#ifdef DOSBOXMENU_EXTERNALLY_MANAGED
|
|
DOSBox_SetMenu(mapperMenu);
|
|
#endif
|
|
ApplyPreventCap();
|
|
|
|
#if defined(MACOSX)
|
|
macosx_reload_touchbar();
|
|
#endif
|
|
|
|
/* Go in the event loop */
|
|
mapper.exit=false;
|
|
mapper.redraw=true;
|
|
SetActiveEvent(nullptr);
|
|
#if defined (REDUCE_JOYSTICK_POLLING)
|
|
SDL_JoystickEventState(SDL_ENABLE);
|
|
#endif
|
|
while (!mapper.exit) {
|
|
#if C_EMSCRIPTEN
|
|
emscripten_sleep(0);
|
|
#endif
|
|
|
|
if (mapper.redraw) {
|
|
mapper.redraw=false;
|
|
DrawButtons();
|
|
} else {
|
|
#if defined(C_SDL2)
|
|
// SDL_UpdateWindowSurface(mapper.window);
|
|
#endif
|
|
}
|
|
BIND_MappingEvents();
|
|
SDL_Delay(1);
|
|
}
|
|
#if defined(C_SDL2)
|
|
SDL_FreeSurface(mapper.draw_surface);
|
|
SDL_FreeSurface(mapper.draw_surface_nonpaletted);
|
|
SDL_FreePalette(sdl2_map_pal_ptr);
|
|
GFX_SetResizeable(true);
|
|
#elif C_DIRECT3D
|
|
bool Direct3D_using(void);
|
|
if (Direct3D_using()
|
|
# if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
&& !IS_VGA_ARCH && !IS_PC98_ARCH
|
|
# endif
|
|
) {
|
|
change_output(0);
|
|
change_output(6);
|
|
}
|
|
#endif
|
|
#if defined (REDUCE_JOYSTICK_POLLING)
|
|
SDL_JoystickEventState(SDL_DISABLE);
|
|
#endif
|
|
if((mousetoggle && !mouselocked) || (!mousetoggle && mouselocked)) GFX_CaptureMouse();
|
|
SDL_ShowCursor(cursor);
|
|
DOSBox_RefreshMenu();
|
|
if(!menu_gui) GFX_RestoreMode();
|
|
#if defined(__WIN32__) && !defined(HX_DOS) && !defined(_WIN32_WINDOWS)
|
|
if(GetAsyncKeyState(0x11)) {
|
|
INPUT ip;
|
|
|
|
// Set up a generic keyboard event.
|
|
ip.type = INPUT_KEYBOARD;
|
|
ip.ki.wScan = 0; // hardware scan code for key
|
|
ip.ki.time = 0;
|
|
ip.ki.dwExtraInfo = 0;
|
|
|
|
ip.ki.wVk = 0x11;
|
|
ip.ki.dwFlags = 0; // 0 for key press
|
|
SendInput(1, &ip, sizeof(INPUT));
|
|
|
|
// Release the "ctrl" key
|
|
ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
|
|
SendInput(1, &ip, sizeof(INPUT));
|
|
}
|
|
#endif
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS) && !defined(_WIN32_WINDOWS)
|
|
WindowsTaskbarUpdatePreviewRegion();
|
|
#endif
|
|
|
|
// KEYBOARD_ClrBuffer();
|
|
GFX_LosingFocus();
|
|
|
|
/* and then the menu items need to be updated */
|
|
update_all_shortcuts();
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
|
|
mainMenu.rebuild();
|
|
#endif
|
|
std::string mapper_keybind = mapper_event_keybind_string("host");
|
|
if (mapper_keybind.empty()) mapper_keybind = "unbound";
|
|
mainMenu.get_item("hostkey_mapper").check(hostkeyalt==0).set_text("Mapper-defined: "+mapper_keybind).refresh_item(mainMenu);
|
|
|
|
#if defined(USE_TTF)
|
|
if (!TTF_using() || ttf.inUse)
|
|
#endif
|
|
{
|
|
GFX_Stop();
|
|
if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackReset );
|
|
GFX_Start();
|
|
#if defined(USE_TTF)
|
|
if (ttf.inUse) resetFontSize();
|
|
#endif
|
|
}
|
|
|
|
mapper.running = false;
|
|
|
|
#if defined(MACOSX)
|
|
macosx_reload_touchbar();
|
|
#endif
|
|
|
|
#ifdef DOSBOXMENU_EXTERNALLY_MANAGED
|
|
DOSBox_SetMenu(mainMenu);
|
|
#endif
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
DOSBox_SetSysMenu();
|
|
#endif
|
|
}
|
|
|
|
bool MAPPER_IsRunning(void) {
|
|
return mapper.running;
|
|
}
|
|
|
|
void MAPPER_CheckKeyboardLayout() {
|
|
#if defined(WIN32)
|
|
WORD cur_kb_layout = LOWORD(GetKeyboardLayout(0));
|
|
|
|
isJPkeyboard = false;
|
|
|
|
if (cur_kb_layout == 1041/*JP106*/) {
|
|
isJPkeyboard = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool mapper_menu_exit(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
|
|
(void)menu;//UNUSED
|
|
(void)menuitem;//UNUSED
|
|
mapper.exit=true;
|
|
return true;
|
|
}
|
|
|
|
bool mapper_menu_save(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
|
|
(void)menu;//UNUSED
|
|
(void)menuitem;//UNUSED
|
|
MAPPER_SaveBinds();
|
|
return true;
|
|
}
|
|
|
|
std::vector<std::string> MAPPER_GetEventNames(const std::string &prefix) {
|
|
std::vector<std::string> key_names;
|
|
key_names.reserve(events.size());
|
|
for (auto & e : events) {
|
|
const std::string name = e->GetName();
|
|
const std::size_t found = name.find(prefix);
|
|
if (found != std::string::npos) {
|
|
const std::string key_name = name.substr(found + prefix.length());
|
|
key_names.push_back(key_name);
|
|
}
|
|
}
|
|
return key_names;
|
|
}
|
|
|
|
void MAPPER_AutoType(std::vector<std::string> &sequence, const uint32_t wait_ms, const uint32_t pace_ms, bool choice) {
|
|
#if !defined(HX_DOS) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
|
|
mapper.typist.Start(&events, sequence, wait_ms, pace_ms, choice);
|
|
#endif
|
|
}
|
|
|
|
void MAPPER_Init(void) {
|
|
LOG(LOG_MISC,LOG_DEBUG)("Initializing DOSBox-X mapper");
|
|
|
|
mapper.exit=true;
|
|
|
|
MAPPER_CheckKeyboardLayout();
|
|
if (initjoy) InitializeJoysticks();
|
|
if (buttons.empty()) CreateLayout();
|
|
if (bindgroups.empty()) CreateBindGroups();
|
|
if (!MAPPER_LoadBinds()) CreateDefaultBinds();
|
|
for (CButton_it but_it = buttons.begin(); but_it != buttons.end(); ++but_it) {
|
|
(*but_it)->BindColor();
|
|
}
|
|
if (SDL_GetModState()&KMOD_CAPS) {
|
|
for (CBindList_it bit=caps_lock_event->bindlist.begin();bit!=caps_lock_event->bindlist.end();++bit) {
|
|
#if SDL_VERSION_ATLEAST(1, 2, 14)
|
|
(*bit)->ActivateBind(32767,true,false);
|
|
(*bit)->DeActivateBind(false);
|
|
#else
|
|
(*bit)->ActivateBind(32767,true,true); //Skip the action itself as bios_keyboard.cpp handles the startup state.
|
|
#endif
|
|
}
|
|
}
|
|
if (SDL_GetModState()&KMOD_NUM) {
|
|
for (CBindList_it bit=num_lock_event->bindlist.begin();bit!=num_lock_event->bindlist.end();++bit) {
|
|
#if SDL_VERSION_ATLEAST(1, 2, 14)
|
|
(*bit)->ActivateBind(32767,true,false);
|
|
(*bit)->DeActivateBind(false);
|
|
#else
|
|
(*bit)->ActivateBind(32767,true,true);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* and then the menu items need to be updated */
|
|
update_all_shortcuts();
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
|
|
mainMenu.rebuild();
|
|
#endif
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
DOSBox_SetSysMenu();
|
|
#endif
|
|
}
|
|
|
|
std::string GetDOSBoxXPath(bool withexe=false);
|
|
void ResolvePath(std::string& in);
|
|
void ReloadMapper(Section_prop *section, bool init) {
|
|
if (!init&&control->opt_defaultmapper) return;
|
|
Prop_path* pp;
|
|
#if defined(C_SDL2)
|
|
pp = section->Get_path("mapperfile_sdl2");
|
|
#else
|
|
pp = section->Get_path("mapperfile_sdl1");
|
|
#endif
|
|
mapper.filename = pp->realpath;
|
|
if (mapper.filename=="") pp = section->Get_path("mapperfile");
|
|
mapper.filename = pp->realpath;
|
|
ResolvePath(mapper.filename);
|
|
bool appendmap=false;
|
|
FILE * loadfile=fopen(mapper.filename.c_str(),"rt");
|
|
if (!loadfile) {
|
|
loadfile=fopen((mapper.filename+".map").c_str(),"rt");
|
|
if (loadfile) appendmap=true;
|
|
}
|
|
if (!loadfile) {
|
|
std::string exepath=GetDOSBoxXPath(), config_path, res_path;
|
|
Cross::GetPlatformConfigDir(config_path), Cross::GetPlatformResDir(res_path);
|
|
if (mapper.filename.size() && exepath.size()) {
|
|
loadfile=fopen((exepath+mapper.filename).c_str(),"rt");
|
|
if (!loadfile) {
|
|
loadfile=fopen((exepath+mapper.filename+".map").c_str(),"rt");
|
|
if (loadfile) appendmap=true;
|
|
}
|
|
if (loadfile) {
|
|
mapper.filename = exepath+mapper.filename+(appendmap?".map":"");
|
|
fclose(loadfile);
|
|
if (control->opt_erasemapper) {
|
|
LOG_MSG("Erase mapper file: %s\n", mapper.filename.c_str());
|
|
unlink(mapper.filename.c_str());
|
|
}
|
|
}
|
|
}
|
|
if (!loadfile && mapper.filename.size() && config_path.size()) {
|
|
loadfile=fopen((config_path+mapper.filename).c_str(),"rt");
|
|
if (!loadfile) {
|
|
loadfile=fopen((config_path+mapper.filename+".map").c_str(),"rt");
|
|
if (loadfile) appendmap=true;
|
|
}
|
|
if (loadfile) {
|
|
mapper.filename = config_path+mapper.filename+(appendmap?".map":"");
|
|
fclose(loadfile);
|
|
if (control->opt_erasemapper) {
|
|
LOG_MSG("Erase mapper file: %s\n", mapper.filename.c_str());
|
|
unlink(mapper.filename.c_str());
|
|
}
|
|
}
|
|
}
|
|
if (!loadfile && mapper.filename.size() && res_path.size()) {
|
|
loadfile=fopen((res_path+mapper.filename).c_str(),"rt");
|
|
if (!loadfile) {
|
|
loadfile=fopen((res_path+mapper.filename+".map").c_str(),"rt");
|
|
if (loadfile) appendmap=true;
|
|
}
|
|
if (loadfile) {
|
|
mapper.filename = res_path+mapper.filename+(appendmap?".map":"");
|
|
fclose(loadfile);
|
|
if (control->opt_erasemapper) {
|
|
LOG_MSG("Erase mapper file: %s\n", mapper.filename.c_str());
|
|
unlink(mapper.filename.c_str());
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
mapper.filename = mapper.filename+(appendmap?".map":"");
|
|
fclose(loadfile);
|
|
if (control->opt_erasemapper) {
|
|
LOG_MSG("Erase mapper file: %s\n", mapper.filename.c_str());
|
|
unlink(mapper.filename.c_str());
|
|
}
|
|
}
|
|
if (init) {
|
|
GFX_LosingFocus(); //Release any keys pressed, or else they'll get stuck.
|
|
MAPPER_Init();
|
|
}
|
|
}
|
|
|
|
//Somehow including them at the top conflicts with something in setup.h
|
|
#ifdef C_X11_XKB
|
|
#include "SDL_syswm.h"
|
|
#include <X11/XKBlib.h>
|
|
#endif
|
|
|
|
#if !defined(C_SDL2)
|
|
void loadScanCode() {
|
|
|
|
load=false;
|
|
if (useScanCode()) {
|
|
|
|
/* Note: table has to be tested/updated for various OSs */
|
|
#if defined (MACOSX)
|
|
/* nothing */
|
|
#elif defined(HAIKU) || defined(RISCOS)
|
|
usescancodes = 0;
|
|
#elif defined(OS2)
|
|
sdlkey_map[0x61]=SDLK_UP;
|
|
sdlkey_map[0x66]=SDLK_DOWN;
|
|
sdlkey_map[0x63]=SDLK_LEFT;
|
|
sdlkey_map[0x64]=SDLK_RIGHT;
|
|
sdlkey_map[0x60]=SDLK_HOME;
|
|
sdlkey_map[0x65]=SDLK_END;
|
|
sdlkey_map[0x62]=SDLK_PAGEUP;
|
|
sdlkey_map[0x67]=SDLK_PAGEDOWN;
|
|
sdlkey_map[0x68]=SDLK_INSERT;
|
|
sdlkey_map[0x69]=SDLK_DELETE;
|
|
sdlkey_map[0x5C]=SDLK_KP_DIVIDE;
|
|
sdlkey_map[0x5A]=SDLK_KP_ENTER;
|
|
sdlkey_map[0x5B]=SDLK_RCTRL;
|
|
sdlkey_map[0x5F]=SDLK_PAUSE;
|
|
// sdlkey_map[0x00]=SDLK_PRINT;
|
|
sdlkey_map[0x5E]=SDLK_RALT;
|
|
sdlkey_map[0x40]=SDLK_KP5;
|
|
sdlkey_map[0x41]=SDLK_KP6;
|
|
#elif !defined (WIN32) /* => Linux & BSDs */
|
|
bool evdev_input = false;
|
|
#ifdef SDL_VIDEO_DRIVER_X11
|
|
//SDL needs to be compiled to use it, else the next makes no sense.
|
|
#ifdef C_X11_XKB
|
|
SDL_SysWMinfo info;
|
|
SDL_VERSION(&info.version);
|
|
if (SDL_GetWMInfo(&info)) {
|
|
XkbDescPtr desc = NULL;
|
|
if((desc = XkbGetMap(info.info.x11.display,XkbAllComponentsMask,XkbUseCoreKbd))) {
|
|
if(XkbGetNames(info.info.x11.display,XkbAllNamesMask,desc) == 0) {
|
|
const char* keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes);
|
|
// const char* geom = XGetAtomName(info.info.x11.display, desc->names->geometry);
|
|
if(keycodes) {
|
|
LOG(LOG_MISC,LOG_NORMAL)("keyboard type %s",keycodes);
|
|
if (strncmp(keycodes,"evdev",5) == 0) evdev_input = true;
|
|
}
|
|
XkbFreeNames(desc,XkbAllNamesMask,True);
|
|
}
|
|
XkbFreeClientMap(desc,0,True);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
if (evdev_input) {
|
|
sdlkey_map[0x67]=SDLK_UP;
|
|
sdlkey_map[0x6c]=SDLK_DOWN;
|
|
sdlkey_map[0x69]=SDLK_LEFT;
|
|
sdlkey_map[0x6a]=SDLK_RIGHT;
|
|
sdlkey_map[0x66]=SDLK_HOME;
|
|
sdlkey_map[0x6b]=SDLK_END;
|
|
sdlkey_map[0x68]=SDLK_PAGEUP;
|
|
sdlkey_map[0x6d]=SDLK_PAGEDOWN;
|
|
sdlkey_map[0x6e]=SDLK_INSERT;
|
|
sdlkey_map[0x6f]=SDLK_DELETE;
|
|
sdlkey_map[0x62]=SDLK_KP_DIVIDE;
|
|
sdlkey_map[0x60]=SDLK_KP_ENTER;
|
|
sdlkey_map[0x61]=SDLK_RCTRL;
|
|
sdlkey_map[0x77]=SDLK_PAUSE;
|
|
sdlkey_map[0x63]=SDLK_PRINT;
|
|
sdlkey_map[0x64]=SDLK_RALT;
|
|
|
|
//Win-keys
|
|
sdlkey_map[0x7d]=SDLK_LSUPER;
|
|
sdlkey_map[0x7e]=SDLK_RSUPER;
|
|
sdlkey_map[0x7f]=SDLK_MENU;
|
|
} else {
|
|
sdlkey_map[0x5a]=SDLK_UP;
|
|
sdlkey_map[0x60]=SDLK_DOWN;
|
|
sdlkey_map[0x5c]=SDLK_LEFT;
|
|
sdlkey_map[0x5e]=SDLK_RIGHT;
|
|
sdlkey_map[0x59]=SDLK_HOME;
|
|
sdlkey_map[0x5f]=SDLK_END;
|
|
sdlkey_map[0x5b]=SDLK_PAGEUP;
|
|
sdlkey_map[0x61]=SDLK_PAGEDOWN;
|
|
sdlkey_map[0x62]=SDLK_INSERT;
|
|
sdlkey_map[0x63]=SDLK_DELETE;
|
|
sdlkey_map[0x68]=SDLK_KP_DIVIDE;
|
|
sdlkey_map[0x64]=SDLK_KP_ENTER;
|
|
sdlkey_map[0x65]=SDLK_RCTRL;
|
|
sdlkey_map[0x66]=SDLK_PAUSE;
|
|
sdlkey_map[0x67]=SDLK_PRINT;
|
|
sdlkey_map[0x69]=SDLK_RALT;
|
|
}
|
|
#else //defined(WIN32) Is this really required?
|
|
sdlkey_map[0xc8]=SDLK_UP;
|
|
sdlkey_map[0xd0]=SDLK_DOWN;
|
|
sdlkey_map[0xcb]=SDLK_LEFT;
|
|
sdlkey_map[0xcd]=SDLK_RIGHT;
|
|
sdlkey_map[0xc7]=SDLK_HOME;
|
|
sdlkey_map[0xcf]=SDLK_END;
|
|
sdlkey_map[0xc9]=SDLK_PAGEUP;
|
|
sdlkey_map[0xd1]=SDLK_PAGEDOWN;
|
|
sdlkey_map[0xd2]=SDLK_INSERT;
|
|
sdlkey_map[0xd3]=SDLK_DELETE;
|
|
sdlkey_map[0xb5]=SDLK_KP_DIVIDE;
|
|
sdlkey_map[0x9c]=SDLK_KP_ENTER;
|
|
sdlkey_map[0x9d]=SDLK_RCTRL;
|
|
sdlkey_map[0xc5]=SDLK_PAUSE;
|
|
sdlkey_map[0xb7]=SDLK_PRINT;
|
|
sdlkey_map[0xb8]=SDLK_RALT;
|
|
|
|
//Win-keys
|
|
sdlkey_map[0xdb]=SDLK_LMETA;
|
|
sdlkey_map[0xdc]=SDLK_RMETA;
|
|
sdlkey_map[0xdd]=SDLK_MENU;
|
|
|
|
#endif
|
|
|
|
Bitu i;
|
|
for (i=0; i<MAX_SDLKEYS; i++) scancode_map[i]=0;
|
|
for (i=0; i<MAX_SCANCODES; i++) {
|
|
SDLKey key=sdlkey_map[i];
|
|
if (key<MAX_SDLKEYS) scancode_map[key]=(uint8_t)i;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void MAPPER_StartUp() {
|
|
Section_prop * section=static_cast<Section_prop *>(control->GetSection("sdl"));
|
|
mapper.sticks.num=0;
|
|
mapper.sticks.num_groups=0;
|
|
|
|
#ifdef DOSBOXMENU_EXTERNALLY_MANAGED
|
|
{
|
|
mapperMenu.alloc_item(DOSBoxMenu::separator_type_id,"_separator_");
|
|
}
|
|
|
|
{
|
|
DOSBoxMenu::item &item = mapperMenu.alloc_item(DOSBoxMenu::submenu_type_id,"MapperMenu");
|
|
item.set_text(mainMenu.get_item("mapper_mapper").get_text());
|
|
}
|
|
|
|
{
|
|
DOSBoxMenu::item &item = mapperMenu.alloc_item(DOSBoxMenu::item_type_id,"ExitMapper");
|
|
item.set_callback_function(mapper_menu_exit);
|
|
item.set_text(MSG_Get("MAPPER_EDITOR_EXIT"));
|
|
}
|
|
|
|
{
|
|
DOSBoxMenu::item &item = mapperMenu.alloc_item(DOSBoxMenu::item_type_id,"SaveMapper");
|
|
item.set_callback_function(mapper_menu_save);
|
|
item.set_text(MSG_Get("SAVE_MAPPER_FILE"));
|
|
}
|
|
|
|
mapperMenu.displaylist_clear(mapperMenu.display_list);
|
|
|
|
mapperMenu.displaylist_append(
|
|
mapperMenu.display_list,
|
|
mapperMenu.get_item_id_by_name("MapperMenu"));
|
|
|
|
{
|
|
mapperMenu.displaylist_append(
|
|
mapperMenu.get_item("MapperMenu").display_list, mapperMenu.get_item_id_by_name("ExitMapper"));
|
|
|
|
mapperMenu.displaylist_append(
|
|
mapperMenu.get_item("MapperMenu").display_list, mapperMenu.get_item_id_by_name("_separator_"));
|
|
|
|
mapperMenu.displaylist_append(
|
|
mapperMenu.get_item("MapperMenu").display_list, mapperMenu.get_item_id_by_name("SaveMapper"));
|
|
}
|
|
#endif
|
|
|
|
LOG(LOG_MISC,LOG_DEBUG)("MAPPER starting up");
|
|
|
|
memset(&virtual_joysticks,0,sizeof(virtual_joysticks));
|
|
|
|
#if !defined(C_SDL2)
|
|
setScanCode(section);
|
|
loadScanCode();
|
|
#endif
|
|
|
|
ReloadMapper(section, false);
|
|
#if !defined(C_SDL2)
|
|
load=true;
|
|
#endif
|
|
}
|
|
|
|
void MAPPER_Shutdown() {
|
|
for (size_t i=0;i < events.size();i++) {
|
|
if (events[i] != NULL) {
|
|
delete events[i];
|
|
events[i] = NULL;
|
|
}
|
|
}
|
|
name_to_events.clear();
|
|
events.clear();
|
|
|
|
for (size_t i=0;i < buttons.size();i++) {
|
|
if (buttons[i] != NULL) {
|
|
delete buttons[i];
|
|
buttons[i] = NULL;
|
|
}
|
|
}
|
|
buttons.clear();
|
|
|
|
for (size_t i=0;i < bindgroups.size();i++) {
|
|
if (bindgroups[i] != NULL) {
|
|
delete bindgroups[i];
|
|
bindgroups[i] = NULL;
|
|
}
|
|
}
|
|
bindgroups.clear();
|
|
|
|
for (size_t i=0;i < handlergroup.size();i++) {
|
|
if (handlergroup[i] != NULL) {
|
|
#if 0 /* FIXME: Is this list simply another copy of other pointers? Allowing this delete[] to commence triggers double-free warnings */
|
|
delete handlergroup[i];
|
|
#endif
|
|
handlergroup[i] = NULL;
|
|
}
|
|
}
|
|
handlergroup.clear();
|
|
initjoy=true;
|
|
}
|
|
|
|
void ext_signal_host_key(bool enable) {
|
|
CEvent *x = get_mapper_event_by_name("host");
|
|
if (x != NULL) {
|
|
if (enable) {
|
|
x->SetValue(32767);//HACK
|
|
x->ActivateEvent(true,false);
|
|
}
|
|
else {
|
|
x->SetValue(0);
|
|
x->DeActivateEvent(true);
|
|
}
|
|
}
|
|
else {
|
|
fprintf(stderr,"WARNING: No host mapper event\n");
|
|
}
|
|
}
|
|
|
|
void MapperCapCursorToggle(void) {
|
|
MAPPER_TriggerEventByName("hand_capmouse");
|
|
}
|
|
|
|
void RedrawMapperBindButton(CEvent *ev) {
|
|
if (ev != NULL) ev->RebindRedraw();
|
|
}
|
|
|
|
std::string mapper_event_keybind_string(const std::string &x) {
|
|
CEvent *ev = get_mapper_event_by_name(x);
|
|
if (ev != NULL) return ev->GetBindMenuText();
|
|
return std::string();
|
|
}
|
|
|
|
std::string get_mapper_shortcut(const char *name) {
|
|
for (CHandlerEventVector_it it=handlergroup.begin();it!=handlergroup.end();++it)
|
|
if ((*it)!=NULL&&!strcmp(name, (*it)->eventname.c_str()))
|
|
return (*it)->GetBindMenuText();
|
|
return "";
|
|
}
|
|
|
|
std::map<std::string,std::string> get_event_map() {
|
|
event_map.clear();
|
|
for (CHandlerEventVector_it it=handlergroup.begin();it!=handlergroup.end();++it)
|
|
event_map.insert(std::pair<std::string,std::string>((*it)->eventname,(*it)->ButtonName()));
|
|
return event_map;
|
|
}
|
|
|
|
void update_bindbutton_text() {
|
|
if (bind_but.prevpage) bind_but.prevpage->SetText("<-");
|
|
if (bind_but.nextpage) bind_but.nextpage->SetText("->");
|
|
if (bind_but.add) bind_but.add->SetText(MSG_Get("ADD"));
|
|
if (bind_but.del) bind_but.del->SetText(MSG_Get("DEL"));
|
|
if (bind_but.next) bind_but.next->SetText(MSG_Get("NEXT"));
|
|
if (bind_but.save) bind_but.save->SetText(MSG_Get("SAVE"));
|
|
if (bind_but.exit) bind_but.exit->SetText(MSG_Get("EXIT"));
|
|
if (bind_but.cap) bind_but.cap->SetText(MSG_Get("CAPTURE"));
|
|
}
|
|
|
|
void set_eventbutton_text(const char *eventname, const char *buttonname) {
|
|
for (CHandlerEventVector_it it=handlergroup.begin();it!=handlergroup.end();++it)
|
|
if ((*it)!=NULL&&!strcmp(eventname, (*it)->eventname.c_str())) {
|
|
(*it)->SetButtonName(buttonname);
|
|
break;
|
|
}
|
|
for (std::vector<CEventButton *>::iterator it = ceventbuttons.begin(); it != ceventbuttons.end(); ++it) {
|
|
CEventButton *button = (CEventButton *)*it;
|
|
if (button!=NULL&&!strcmp(eventname, button->GetEvent()->eventname.c_str())) {
|
|
button->SetText(buttonname);
|
|
break;
|
|
}
|
|
}
|
|
}
|