Introduce F1 menu to reconfigure key bindings at runtime
This commit is contained in:
parent
af145c3a01
commit
dd73db2e9d
|
@ -135,6 +135,8 @@ set(MAIN_HEADERS
|
|||
src/gl-util.h
|
||||
src/util.h
|
||||
src/config.h
|
||||
src/settingsmenu.h
|
||||
src/keybindings.h
|
||||
src/tileatlas.h
|
||||
src/sharedstate.h
|
||||
src/al-util.h
|
||||
|
@ -180,6 +182,8 @@ set(MAIN_SOURCE
|
|||
src/debuglogger.cpp
|
||||
src/etc.cpp
|
||||
src/config.cpp
|
||||
src/settingsmenu.cpp
|
||||
src/keybindings.cpp
|
||||
src/tileatlas.cpp
|
||||
src/sharedstate.cpp
|
||||
src/gl-fun.cpp
|
||||
|
|
|
@ -111,6 +111,19 @@
|
|||
# allowSymlinks=false
|
||||
|
||||
|
||||
# Organisation / company and application / game
|
||||
# name to build the directory path where mkxp
|
||||
# will store game specific data (eg. key bindings).
|
||||
# If not specified, mkxp will save to a common
|
||||
# directory shared by all games. Note that these
|
||||
# are TWO individual config entries, and both need
|
||||
# to be defined for this to take effect.
|
||||
# (default: none)
|
||||
#
|
||||
# dataPathOrg=mycompany
|
||||
# dataPathApp=mygame
|
||||
|
||||
|
||||
# Set the game window icon to 'path/to/icon.png'
|
||||
# (default: none)
|
||||
#
|
||||
|
|
4
mkxp.pro
4
mkxp.pro
|
@ -114,6 +114,8 @@ HEADERS += \
|
|||
src/gl-util.h \
|
||||
src/util.h \
|
||||
src/config.h \
|
||||
src/settingsmenu.h \
|
||||
src/keybindings.h \
|
||||
src/tileatlas.h \
|
||||
src/sharedstate.h \
|
||||
src/al-util.h \
|
||||
|
@ -158,6 +160,8 @@ SOURCES += \
|
|||
src/debuglogger.cpp \
|
||||
src/etc.cpp \
|
||||
src/config.cpp \
|
||||
src/settingsmenu.cpp \
|
||||
src/keybindings.cpp \
|
||||
src/tileatlas.cpp \
|
||||
src/sharedstate.cpp \
|
||||
src/gl-fun.cpp \
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
#include <SDL_filesystem.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -118,6 +120,15 @@ static bool validUtf8(const char *string)
|
|||
return true;
|
||||
}
|
||||
|
||||
static std::string prefPath(const char *org, const char *app)
|
||||
{
|
||||
char *path = SDL_GetPrefPath(org, app);
|
||||
std::string str(path);
|
||||
SDL_free(path);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
typedef std::vector<std::string> StringVec;
|
||||
namespace po = boost::program_options;
|
||||
|
||||
|
@ -167,6 +178,8 @@ void Config::read(int argc, char *argv[])
|
|||
PO_DESC(anyAltToggleFS, bool) \
|
||||
PO_DESC(enableReset, bool) \
|
||||
PO_DESC(allowSymlinks, bool) \
|
||||
PO_DESC(dataPathOrg, std::string) \
|
||||
PO_DESC(dataPathApp, std::string) \
|
||||
PO_DESC(iconPath, std::string) \
|
||||
PO_DESC(titleLanguage, std::string) \
|
||||
PO_DESC(midi.soundFont, std::string) \
|
||||
|
@ -243,6 +256,11 @@ void Config::read(int argc, char *argv[])
|
|||
rgssVersion = clamp(rgssVersion, 0, 3);
|
||||
|
||||
SE.sourceCount = clamp(SE.sourceCount, 1, 64);
|
||||
|
||||
if (!dataPathOrg.empty() && !dataPathApp.empty())
|
||||
customDataPath = prefPath(dataPathOrg.c_str(), dataPathApp.c_str());
|
||||
|
||||
commonDataPath = prefPath(".", "mkxp");
|
||||
}
|
||||
|
||||
static std::string baseName(const std::string &path)
|
||||
|
|
|
@ -51,6 +51,9 @@ struct Config
|
|||
bool allowSymlinks;
|
||||
bool pathCache;
|
||||
|
||||
std::string dataPathOrg;
|
||||
std::string dataPathApp;
|
||||
|
||||
std::string iconPath;
|
||||
std::string titleLanguage;
|
||||
|
||||
|
@ -82,6 +85,10 @@ struct Config
|
|||
std::string title;
|
||||
} game;
|
||||
|
||||
/* Internal */
|
||||
std::string customDataPath;
|
||||
std::string commonDataPath;
|
||||
|
||||
Config();
|
||||
|
||||
void read(int argc, char *argv[]);
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "sharedstate.h"
|
||||
#include "graphics.h"
|
||||
#include "settingsmenu.h"
|
||||
#include "debugwriter.h"
|
||||
|
||||
#include <string.h>
|
||||
|
@ -37,7 +38,7 @@ bool EventThread::keyStates[] = { false };
|
|||
|
||||
EventThread::JoyState EventThread::joyState =
|
||||
{
|
||||
0, 0, { false }
|
||||
{ 0 }, { false }
|
||||
};
|
||||
|
||||
EventThread::MouseState EventThread::mouseState =
|
||||
|
@ -108,6 +109,8 @@ void EventThread::process(RGSSThreadData &rtData)
|
|||
|
||||
bool resetting = false;
|
||||
|
||||
SettingsMenu *sMenu = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!SDL_WaitEvent(&event))
|
||||
|
@ -116,6 +119,19 @@ void EventThread::process(RGSSThreadData &rtData)
|
|||
break;
|
||||
}
|
||||
|
||||
if (sMenu && sMenu->onEvent(event))
|
||||
{
|
||||
if (sMenu->destroyReq())
|
||||
{
|
||||
delete sMenu;
|
||||
sMenu = 0;
|
||||
|
||||
updateCursorState(cursorInWindow && windowFocused);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_WINDOWEVENT :
|
||||
|
@ -129,14 +145,14 @@ void EventThread::process(RGSSThreadData &rtData)
|
|||
case SDL_WINDOWEVENT_ENTER :
|
||||
cursorInWindow = true;
|
||||
mouseState.inWindow = true;
|
||||
updateCursorState(cursorInWindow && windowFocused);
|
||||
updateCursorState(cursorInWindow && windowFocused && !sMenu);
|
||||
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_LEAVE :
|
||||
cursorInWindow = false;
|
||||
mouseState.inWindow = false;
|
||||
updateCursorState(cursorInWindow && windowFocused);
|
||||
updateCursorState(cursorInWindow && windowFocused && !sMenu);
|
||||
|
||||
break;
|
||||
|
||||
|
@ -147,13 +163,13 @@ void EventThread::process(RGSSThreadData &rtData)
|
|||
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED :
|
||||
windowFocused = true;
|
||||
updateCursorState(cursorInWindow && windowFocused);
|
||||
updateCursorState(cursorInWindow && windowFocused && !sMenu);
|
||||
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST :
|
||||
windowFocused = false;
|
||||
updateCursorState(cursorInWindow && windowFocused);
|
||||
updateCursorState(cursorInWindow && windowFocused && !sMenu);
|
||||
resetInputStates();
|
||||
|
||||
break;
|
||||
|
@ -181,6 +197,17 @@ void EventThread::process(RGSSThreadData &rtData)
|
|||
break;
|
||||
}
|
||||
|
||||
if (event.key.keysym.scancode == SDL_SCANCODE_F1)
|
||||
{
|
||||
if (!sMenu)
|
||||
{
|
||||
sMenu = new SettingsMenu(rtData);
|
||||
updateCursorState(false);
|
||||
}
|
||||
|
||||
sMenu->raise();
|
||||
}
|
||||
|
||||
if (event.key.keysym.scancode == SDL_SCANCODE_F2)
|
||||
{
|
||||
if (!fps.displaying)
|
||||
|
@ -248,11 +275,7 @@ void EventThread::process(RGSSThreadData &rtData)
|
|||
break;
|
||||
|
||||
case SDL_JOYAXISMOTION :
|
||||
if (event.jaxis.axis == 0)
|
||||
joyState.xAxis = event.jaxis.value;
|
||||
else
|
||||
joyState.yAxis = event.jaxis.value;
|
||||
|
||||
joyState.axis[event.jaxis.axis] = event.jaxis.value;
|
||||
break;
|
||||
|
||||
case SDL_JOYDEVICEADDED :
|
||||
|
@ -333,6 +356,8 @@ void EventThread::process(RGSSThreadData &rtData)
|
|||
|
||||
if (SDL_JoystickGetAttached(js))
|
||||
SDL_JoystickClose(js);
|
||||
|
||||
delete sMenu;
|
||||
}
|
||||
|
||||
void EventThread::cleanup()
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "config.h"
|
||||
#include "etc-internal.h"
|
||||
#include "sdl-util.h"
|
||||
#include "keybindings.h"
|
||||
|
||||
#include <SDL_scancode.h>
|
||||
#include <SDL_joystick.h>
|
||||
|
@ -45,10 +46,8 @@ public:
|
|||
|
||||
struct JoyState
|
||||
{
|
||||
int xAxis;
|
||||
int yAxis;
|
||||
|
||||
bool buttons[16];
|
||||
int axis[256];
|
||||
bool buttons[256];
|
||||
};
|
||||
|
||||
static JoyState joyState;
|
||||
|
@ -155,6 +154,55 @@ struct WindowSizeNotify
|
|||
}
|
||||
};
|
||||
|
||||
struct BindingNotify
|
||||
{
|
||||
BindingNotify()
|
||||
{
|
||||
mut = SDL_CreateMutex();
|
||||
}
|
||||
~BindingNotify()
|
||||
{
|
||||
SDL_DestroyMutex(mut);
|
||||
}
|
||||
|
||||
bool poll(BDescVec &out) const
|
||||
{
|
||||
if (!changed)
|
||||
return false;
|
||||
|
||||
SDL_LockMutex(mut);
|
||||
|
||||
out = data;
|
||||
changed.clear();
|
||||
|
||||
SDL_UnlockMutex(mut);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void get(BDescVec &out) const
|
||||
{
|
||||
SDL_LockMutex(mut);
|
||||
out = data;
|
||||
SDL_UnlockMutex(mut);
|
||||
}
|
||||
|
||||
void post(const BDescVec &d)
|
||||
{
|
||||
SDL_LockMutex(mut);
|
||||
|
||||
changed.set();
|
||||
data = d;
|
||||
|
||||
SDL_UnlockMutex(mut);
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_mutex *mut;
|
||||
BDescVec data;
|
||||
mutable AtomicFlag changed;
|
||||
};
|
||||
|
||||
struct RGSSThreadData
|
||||
{
|
||||
/* Main thread sets this to request RGSS thread to terminate */
|
||||
|
@ -171,6 +219,7 @@ struct RGSSThreadData
|
|||
|
||||
EventThread *ethread;
|
||||
WindowSizeNotify windowSizeMsg;
|
||||
BindingNotify bindingUpdateMsg;
|
||||
|
||||
const char *argv0;
|
||||
|
||||
|
|
|
@ -195,6 +195,13 @@ bool SharedFontState::fontPresent(std::string family)
|
|||
return !(set.regular.empty() && set.other.empty());
|
||||
}
|
||||
|
||||
_TTF_Font *SharedFontState::openBundled(int size)
|
||||
{
|
||||
SDL_RWops *ops = openBundledFont();
|
||||
|
||||
return TTF_OpenFontRW(ops, 1, size);
|
||||
}
|
||||
|
||||
|
||||
struct FontPrivate
|
||||
{
|
||||
|
|
|
@ -49,6 +49,8 @@ public:
|
|||
|
||||
bool fontPresent(std::string family);
|
||||
|
||||
static _TTF_Font *openBundled(int size);
|
||||
|
||||
private:
|
||||
SharedFontStatePrivate *p;
|
||||
};
|
||||
|
|
225
src/input.cpp
225
src/input.cpp
|
@ -22,6 +22,7 @@
|
|||
#include "input.h"
|
||||
#include "sharedstate.h"
|
||||
#include "eventthread.h"
|
||||
#include "keybindings.h"
|
||||
#include "exception.h"
|
||||
#include "util.h"
|
||||
|
||||
|
@ -30,6 +31,7 @@
|
|||
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define BUTTON_CODE_COUNT 24
|
||||
|
||||
|
@ -52,12 +54,6 @@ struct KbBindingData
|
|||
Input::ButtonCode target;
|
||||
};
|
||||
|
||||
struct JsBindingData
|
||||
{
|
||||
int source;
|
||||
Input::ButtonCode target;
|
||||
};
|
||||
|
||||
struct Binding
|
||||
{
|
||||
Binding(Input::ButtonCode target = Input::None)
|
||||
|
@ -82,6 +78,15 @@ struct KbBinding : public Binding
|
|||
|
||||
bool sourceActive() const
|
||||
{
|
||||
/* Special case aliases */
|
||||
if (source == SDL_SCANCODE_LSHIFT)
|
||||
return EventThread::keyStates[source]
|
||||
|| EventThread::keyStates[SDL_SCANCODE_RSHIFT];
|
||||
|
||||
if (source == SDL_SCANCODE_RETURN)
|
||||
return EventThread::keyStates[source]
|
||||
|| EventThread::keyStates[SDL_SCANCODE_KP_ENTER];
|
||||
|
||||
return EventThread::keyStates[source];
|
||||
}
|
||||
|
||||
|
@ -100,11 +105,6 @@ struct JsButtonBinding : public Binding
|
|||
{
|
||||
JsButtonBinding() {}
|
||||
|
||||
JsButtonBinding(const JsBindingData &data)
|
||||
: Binding(data.target),
|
||||
source(data.source)
|
||||
{}
|
||||
|
||||
bool sourceActive() const
|
||||
{
|
||||
return EventThread::joyState.buttons[source];
|
||||
|
@ -115,7 +115,7 @@ struct JsButtonBinding : public Binding
|
|||
return true;
|
||||
}
|
||||
|
||||
int source;
|
||||
uint8_t source;
|
||||
};
|
||||
|
||||
/* Joystick axis binding */
|
||||
|
@ -123,17 +123,22 @@ struct JsAxisBinding : public Binding
|
|||
{
|
||||
JsAxisBinding() {}
|
||||
|
||||
JsAxisBinding(int *source,
|
||||
int compareValue,
|
||||
JsAxisBinding(uint8_t source,
|
||||
AxisDir dir,
|
||||
Input::ButtonCode target)
|
||||
: Binding(target),
|
||||
source(source),
|
||||
compareValue(compareValue)
|
||||
dir(dir)
|
||||
{}
|
||||
|
||||
bool sourceActive() const
|
||||
{
|
||||
return (*source == compareValue);
|
||||
int val = EventThread::joyState.axis[source];
|
||||
|
||||
if (dir == Negative)
|
||||
return val < -JAXIS_THRESHOLD;
|
||||
else /* dir == Positive */
|
||||
return val > JAXIS_THRESHOLD;
|
||||
}
|
||||
|
||||
bool sourceRepeatable() const
|
||||
|
@ -141,8 +146,8 @@ struct JsAxisBinding : public Binding
|
|||
return true;
|
||||
}
|
||||
|
||||
int *source;
|
||||
int compareValue;
|
||||
uint8_t source;
|
||||
AxisDir dir;
|
||||
};
|
||||
|
||||
/* Mouse button binding */
|
||||
|
@ -172,10 +177,6 @@ struct MsBinding : public Binding
|
|||
/* Not rebindable */
|
||||
static const KbBindingData staticKbBindings[] =
|
||||
{
|
||||
{ SDL_SCANCODE_LEFT, Input::Left },
|
||||
{ SDL_SCANCODE_RIGHT, Input::Right },
|
||||
{ SDL_SCANCODE_UP, Input::Up },
|
||||
{ SDL_SCANCODE_DOWN, Input::Down },
|
||||
{ SDL_SCANCODE_LSHIFT, Input::Shift },
|
||||
{ SDL_SCANCODE_RSHIFT, Input::Shift },
|
||||
{ SDL_SCANCODE_LCTRL, Input::Ctrl },
|
||||
|
@ -191,61 +192,6 @@ static const KbBindingData staticKbBindings[] =
|
|||
|
||||
static elementsN(staticKbBindings);
|
||||
|
||||
/* Rebindable */
|
||||
static const KbBindingData defaultKbBindings[] =
|
||||
{
|
||||
{ SDL_SCANCODE_SPACE, Input::C },
|
||||
{ SDL_SCANCODE_RETURN, Input::C },
|
||||
{ SDL_SCANCODE_KP_ENTER, Input::C }, /* Treated as alias of RETURN */
|
||||
{ SDL_SCANCODE_ESCAPE, Input::B },
|
||||
{ SDL_SCANCODE_KP_0, Input::B },
|
||||
{ SDL_SCANCODE_LSHIFT, Input::A },
|
||||
{ SDL_SCANCODE_RSHIFT, Input::A },
|
||||
{ SDL_SCANCODE_X, Input::B },
|
||||
{ SDL_SCANCODE_B, Input::None },
|
||||
{ SDL_SCANCODE_D, Input::Z },
|
||||
{ SDL_SCANCODE_Q, Input::L },
|
||||
{ SDL_SCANCODE_W, Input::R },
|
||||
{ SDL_SCANCODE_V, Input::None },
|
||||
{ SDL_SCANCODE_A, Input::X },
|
||||
{ SDL_SCANCODE_S, Input::Y }
|
||||
};
|
||||
|
||||
/* RGSS1 */
|
||||
static const KbBindingData defaultKbBindings1[] =
|
||||
{
|
||||
{ SDL_SCANCODE_Z, Input::A },
|
||||
{ SDL_SCANCODE_C, Input::C },
|
||||
};
|
||||
|
||||
/* RGSS2 and higher */
|
||||
static const KbBindingData defaultKbBindings2[] =
|
||||
{
|
||||
{ SDL_SCANCODE_Z, Input::C },
|
||||
{ SDL_SCANCODE_C, Input::None },
|
||||
};
|
||||
|
||||
static elementsN(defaultKbBindings);
|
||||
static elementsN(defaultKbBindings1);
|
||||
static elementsN(defaultKbBindings2);
|
||||
|
||||
/* Rebindable */
|
||||
static const JsBindingData defaultJsBindings[] =
|
||||
{
|
||||
{ 0, Input::A },
|
||||
{ 1, Input::B },
|
||||
{ 2, Input::C },
|
||||
{ 3, Input::X },
|
||||
{ 4, Input::Y },
|
||||
{ 5, Input::Z },
|
||||
{ 6, Input::L },
|
||||
{ 7, Input::R },
|
||||
{ 8, Input::None },
|
||||
{ 9, Input::None }
|
||||
};
|
||||
|
||||
static elementsN(defaultJsBindings);
|
||||
|
||||
/* Maps ButtonCode enum values to indices
|
||||
* in the button state array */
|
||||
static const int mapToIndex[] =
|
||||
|
@ -292,6 +238,7 @@ static const Input::ButtonCode otherDirs[4][3] =
|
|||
|
||||
struct InputPrivate
|
||||
{
|
||||
std::vector<KbBinding> kbStatBindings;
|
||||
std::vector<KbBinding> kbBindings;
|
||||
std::vector<JsAxisBinding> jsABindings;
|
||||
std::vector<JsButtonBinding> jsBBindings;
|
||||
|
@ -320,12 +267,14 @@ struct InputPrivate
|
|||
} dir8Data;
|
||||
|
||||
|
||||
InputPrivate()
|
||||
InputPrivate(const RGSSThreadData &rtData)
|
||||
{
|
||||
initKbBindings();
|
||||
initJsBindings();
|
||||
initStaticKbBindings();
|
||||
initMsBindings();
|
||||
|
||||
/* Main thread should have these posted by now */
|
||||
checkBindingChange(rtData);
|
||||
|
||||
states = stateArray;
|
||||
statesOld = stateArray + BUTTON_CODE_COUNT;
|
||||
|
||||
|
@ -378,51 +327,90 @@ struct InputPrivate
|
|||
memset(states, 0, size);
|
||||
}
|
||||
|
||||
void initKbBindings()
|
||||
void checkBindingChange(const RGSSThreadData &rtData)
|
||||
{
|
||||
kbBindings.clear();
|
||||
BDescVec d;
|
||||
|
||||
for (size_t i = 0; i < staticKbBindingsN; ++i)
|
||||
kbBindings.push_back(KbBinding(staticKbBindings[i]));
|
||||
if (!rtData.bindingUpdateMsg.poll(d))
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < defaultKbBindingsN; ++i)
|
||||
kbBindings.push_back(KbBinding(defaultKbBindings[i]));
|
||||
|
||||
if (rgssVer == 1)
|
||||
for (size_t i = 0; i < defaultKbBindings1N; ++i)
|
||||
kbBindings.push_back(KbBinding(defaultKbBindings1[i]));
|
||||
else
|
||||
for (size_t i = 0; i < defaultKbBindings2N; ++i)
|
||||
kbBindings.push_back(KbBinding(defaultKbBindings2[i]));
|
||||
|
||||
/* Add to binging array */
|
||||
for (size_t i = 0; i < kbBindings.size(); ++i)
|
||||
bindings.push_back(&kbBindings[i]);
|
||||
applyBindingDesc(d);
|
||||
}
|
||||
|
||||
void initJsBindings()
|
||||
template<class B>
|
||||
void appendBindings(std::vector<B> &bind)
|
||||
{
|
||||
/* Create axis bindings */
|
||||
jsABindings.resize(4);
|
||||
for (size_t i = 0; i < bind.size(); ++i)
|
||||
bindings.push_back(&bind[i]);
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.xAxis, 0x7FFF, Input::Right);
|
||||
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.xAxis, -0x8000, Input::Left);
|
||||
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.yAxis, 0x7FFF, Input::Down);
|
||||
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.yAxis, -0x8000, Input::Up);
|
||||
void applyBindingDesc(const BDescVec &d)
|
||||
{
|
||||
kbBindings.clear();
|
||||
jsABindings.clear();
|
||||
jsBBindings.clear();
|
||||
|
||||
/* Create button bindings */
|
||||
jsBBindings.resize(defaultJsBindingsN);
|
||||
for (size_t i = 0; i < d.size(); ++i)
|
||||
{
|
||||
const BindingDesc &desc = d[i];
|
||||
const SourceDesc &src = desc.src;
|
||||
|
||||
for (size_t i = 0; i < defaultJsBindingsN; ++i)
|
||||
jsBBindings[i] = JsButtonBinding(defaultJsBindings[i]);
|
||||
if (desc.target == Input::None)
|
||||
continue;
|
||||
|
||||
/* Add to binging array */
|
||||
for (size_t i = 0; i < jsABindings.size(); ++i)
|
||||
bindings.push_back(&jsABindings[i]);
|
||||
switch (desc.src.type)
|
||||
{
|
||||
case Invalid :
|
||||
break;
|
||||
case Key :
|
||||
{
|
||||
KbBinding bind;
|
||||
bind.source = src.d.scan;
|
||||
bind.target = desc.target;
|
||||
kbBindings.push_back(bind);
|
||||
|
||||
for (size_t i = 0; i < jsBBindings.size(); ++i)
|
||||
bindings.push_back(&jsBBindings[i]);
|
||||
break;
|
||||
}
|
||||
case JAxis :
|
||||
{
|
||||
JsAxisBinding bind;
|
||||
bind.source = src.d.ja.axis;
|
||||
bind.dir = src.d.ja.dir;
|
||||
bind.target = desc.target;
|
||||
jsABindings.push_back(bind);
|
||||
|
||||
break;
|
||||
}
|
||||
case JButton :
|
||||
{
|
||||
JsButtonBinding bind;
|
||||
bind.source = src.d.jb;
|
||||
bind.target = desc.target;
|
||||
jsBBindings.push_back(bind);
|
||||
|
||||
break;
|
||||
}
|
||||
default :
|
||||
assert(!"unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
bindings.clear();
|
||||
|
||||
appendBindings(kbStatBindings);
|
||||
appendBindings(msBindings);
|
||||
|
||||
appendBindings(kbBindings);
|
||||
appendBindings(jsABindings);
|
||||
appendBindings(jsBBindings);
|
||||
}
|
||||
|
||||
void initStaticKbBindings()
|
||||
{
|
||||
kbStatBindings.clear();
|
||||
|
||||
for (size_t i = 0; i < staticKbBindingsN; ++i)
|
||||
kbStatBindings.push_back(KbBinding(staticKbBindings[i]));
|
||||
}
|
||||
|
||||
void initMsBindings()
|
||||
|
@ -433,10 +421,6 @@ struct InputPrivate
|
|||
msBindings[i++] = MsBinding(SDL_BUTTON_LEFT, Input::MouseLeft);
|
||||
msBindings[i++] = MsBinding(SDL_BUTTON_MIDDLE, Input::MouseMiddle);
|
||||
msBindings[i++] = MsBinding(SDL_BUTTON_RIGHT, Input::MouseRight);
|
||||
|
||||
/* Add to binding array */
|
||||
for (size_t i = 0; i < msBindings.size(); ++i)
|
||||
bindings.push_back(&msBindings[i]);
|
||||
}
|
||||
|
||||
void pollBindings(Input::ButtonCode &repeatCand)
|
||||
|
@ -564,14 +548,15 @@ struct InputPrivate
|
|||
};
|
||||
|
||||
|
||||
Input::Input()
|
||||
Input::Input(const RGSSThreadData &rtData)
|
||||
{
|
||||
p = new InputPrivate;
|
||||
p = new InputPrivate(rtData);
|
||||
}
|
||||
|
||||
void Input::update()
|
||||
{
|
||||
shState->checkShutdown();
|
||||
p->checkBindingChange(shState->rtData());
|
||||
|
||||
p->swapBuffers();
|
||||
p->clearBuffer();
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#define INPUT_H
|
||||
|
||||
struct InputPrivate;
|
||||
struct RGSSThreadData;
|
||||
|
||||
class Input
|
||||
{
|
||||
|
@ -59,7 +60,7 @@ public:
|
|||
int mouseY();
|
||||
|
||||
private:
|
||||
Input();
|
||||
Input(const RGSSThreadData &rtData);
|
||||
~Input();
|
||||
|
||||
friend struct SharedStatePrivate;
|
||||
|
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
** keybindings.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp 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.
|
||||
**
|
||||
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "keybindings.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
struct KbBindingData
|
||||
{
|
||||
SDL_Scancode source;
|
||||
Input::ButtonCode target;
|
||||
|
||||
void add(BDescVec &d) const
|
||||
{
|
||||
SourceDesc src;
|
||||
src.type = Key;
|
||||
src.d.scan = source;
|
||||
|
||||
BindingDesc desc;
|
||||
desc.src = src;
|
||||
desc.target = target;
|
||||
|
||||
d.push_back(desc);
|
||||
}
|
||||
};
|
||||
|
||||
struct JsBindingData
|
||||
{
|
||||
int source;
|
||||
Input::ButtonCode target;
|
||||
|
||||
void add(BDescVec &d) const
|
||||
{
|
||||
SourceDesc src;
|
||||
src.type = JButton;
|
||||
src.d.jb = source;
|
||||
|
||||
BindingDesc desc;
|
||||
desc.src = src;
|
||||
desc.target = target;
|
||||
|
||||
d.push_back(desc);
|
||||
}
|
||||
};
|
||||
|
||||
/* Common */
|
||||
static const KbBindingData defaultKbBindings[] =
|
||||
{
|
||||
{ SDL_SCANCODE_LEFT, Input::Left },
|
||||
{ SDL_SCANCODE_RIGHT, Input::Right },
|
||||
{ SDL_SCANCODE_UP, Input::Up },
|
||||
{ SDL_SCANCODE_DOWN, Input::Down },
|
||||
{ SDL_SCANCODE_SPACE, Input::C },
|
||||
{ SDL_SCANCODE_RETURN, Input::C },
|
||||
{ SDL_SCANCODE_ESCAPE, Input::B },
|
||||
{ SDL_SCANCODE_KP_0, Input::B },
|
||||
{ SDL_SCANCODE_LSHIFT, Input::A },
|
||||
{ SDL_SCANCODE_X, Input::B },
|
||||
{ SDL_SCANCODE_D, Input::Z },
|
||||
{ SDL_SCANCODE_Q, Input::L },
|
||||
{ SDL_SCANCODE_W, Input::R },
|
||||
{ SDL_SCANCODE_A, Input::X },
|
||||
{ SDL_SCANCODE_S, Input::Y }
|
||||
};
|
||||
|
||||
/* RGSS1 */
|
||||
static const KbBindingData defaultKbBindings1[] =
|
||||
{
|
||||
{ SDL_SCANCODE_Z, Input::A },
|
||||
{ SDL_SCANCODE_C, Input::C },
|
||||
};
|
||||
|
||||
/* RGSS2 and higher */
|
||||
static const KbBindingData defaultKbBindings2[] =
|
||||
{
|
||||
{ SDL_SCANCODE_Z, Input::C }
|
||||
};
|
||||
|
||||
static elementsN(defaultKbBindings);
|
||||
static elementsN(defaultKbBindings1);
|
||||
static elementsN(defaultKbBindings2);
|
||||
|
||||
static const JsBindingData defaultJsBindings[] =
|
||||
{
|
||||
{ 0, Input::A },
|
||||
{ 1, Input::B },
|
||||
{ 2, Input::C },
|
||||
{ 3, Input::X },
|
||||
{ 4, Input::Y },
|
||||
{ 5, Input::Z },
|
||||
{ 6, Input::L },
|
||||
{ 7, Input::R }
|
||||
};
|
||||
|
||||
static elementsN(defaultJsBindings);
|
||||
|
||||
static void addAxisBinding(BDescVec &d, uint8_t axis, AxisDir dir, Input::ButtonCode target)
|
||||
{
|
||||
SourceDesc src;
|
||||
src.type = JAxis;
|
||||
src.d.ja.axis = axis;
|
||||
src.d.ja.dir = dir;
|
||||
|
||||
BindingDesc desc;
|
||||
desc.src = src;
|
||||
desc.target = target;
|
||||
|
||||
d.push_back(desc);
|
||||
}
|
||||
|
||||
BDescVec genDefaultBindings(const Config &conf)
|
||||
{
|
||||
BDescVec d;
|
||||
|
||||
for (size_t i = 0; i < defaultKbBindingsN; ++i)
|
||||
defaultKbBindings[i].add(d);
|
||||
|
||||
if (conf.rgssVersion == 1)
|
||||
for (size_t i = 0; i < defaultKbBindings1N; ++i)
|
||||
defaultKbBindings1[i].add(d);
|
||||
else
|
||||
for (size_t i = 0; i < defaultKbBindings2N; ++i)
|
||||
defaultKbBindings2[i].add(d);
|
||||
|
||||
for (size_t i = 0; i < defaultJsBindingsN; ++i)
|
||||
defaultJsBindings[i].add(d);
|
||||
|
||||
addAxisBinding(d, 0, Negative, Input::Left );
|
||||
addAxisBinding(d, 0, Positive, Input::Right);
|
||||
addAxisBinding(d, 1, Negative, Input::Up );
|
||||
addAxisBinding(d, 1, Positive, Input::Down );
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
#define FORMAT_VER 1
|
||||
|
||||
struct Header
|
||||
{
|
||||
uint32_t formVer;
|
||||
uint32_t rgssVer;
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
static void buildPath(const std::string &dir, uint32_t rgssVersion,
|
||||
char *out, size_t outSize)
|
||||
{
|
||||
snprintf(out, outSize, "%s/keybindings.mkxp%u", dir.c_str(), rgssVersion);
|
||||
}
|
||||
|
||||
static bool writeBindings(const BDescVec &d, const std::string &dir,
|
||||
uint32_t rgssVersion)
|
||||
{
|
||||
if (dir.empty())
|
||||
return false;
|
||||
|
||||
char path[1024];
|
||||
buildPath(dir, rgssVersion, path, sizeof(path));
|
||||
|
||||
FILE *f = fopen(path, "w");
|
||||
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
Header hd;
|
||||
hd.formVer = FORMAT_VER;
|
||||
hd.rgssVer = rgssVersion;
|
||||
hd.count = d.size();
|
||||
|
||||
if (fwrite(&hd, sizeof(hd), 1, f) < 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fwrite(&d[0], sizeof(d[0]), hd.count, f) < hd.count)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void storeBindings(const BDescVec &d, const Config &conf)
|
||||
{
|
||||
if (writeBindings(d, conf.customDataPath, conf.rgssVersion))
|
||||
return;
|
||||
|
||||
writeBindings(d, conf.commonDataPath, conf.rgssVersion);
|
||||
}
|
||||
|
||||
#define READ(ptr, size, n, f) if (fread(ptr, size, n, f) < n) return false
|
||||
|
||||
static bool verifyDesc(const BindingDesc &desc)
|
||||
{
|
||||
const Input::ButtonCode codes[] =
|
||||
{
|
||||
Input::None,
|
||||
Input::Down, Input::Left, Input::Right, Input::Up,
|
||||
Input::A, Input::B, Input::C,
|
||||
Input::X, Input::Y, Input::Z,
|
||||
Input::L, Input::R,
|
||||
Input::Shift, Input::Ctrl, Input::Alt,
|
||||
Input::F5, Input::F6, Input::F7, Input::F8, Input::F9
|
||||
};
|
||||
|
||||
elementsN(codes);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < codesN; ++i)
|
||||
if (desc.target == codes[i])
|
||||
break;
|
||||
|
||||
if (i == codesN)
|
||||
return false;
|
||||
|
||||
const SourceDesc &src = desc.src;
|
||||
|
||||
switch (src.type)
|
||||
{
|
||||
case Invalid:
|
||||
return true;
|
||||
case Key:
|
||||
return src.d.scan < SDL_NUM_SCANCODES;
|
||||
case JButton:
|
||||
return true;
|
||||
case JAxis:
|
||||
return src.d.ja.dir == Negative || src.d.ja.dir == Positive;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool readBindings(BDescVec &out, const std::string &dir,
|
||||
uint32_t rgssVersion)
|
||||
{
|
||||
if (dir.empty())
|
||||
return false;
|
||||
|
||||
char path[1024];
|
||||
buildPath(dir, rgssVersion, path, sizeof(path));
|
||||
|
||||
FILE *f = fopen(path, "r");
|
||||
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
Header hd;
|
||||
if (fread(&hd, sizeof(hd), 1, f) < 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hd.formVer != FORMAT_VER)
|
||||
return false;
|
||||
if (hd.rgssVer != rgssVersion)
|
||||
return false;
|
||||
/* Arbitrary max value */
|
||||
if (hd.count > 1024)
|
||||
return false;
|
||||
|
||||
out.resize(hd.count);
|
||||
if (fread(&out[0], sizeof(out[0]), hd.count, f) < hd.count)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < hd.count; ++i)
|
||||
if (!verifyDesc(out[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BDescVec loadBindings(const Config &conf)
|
||||
{
|
||||
BDescVec d;
|
||||
|
||||
if (readBindings(d, conf.customDataPath, conf.rgssVersion))
|
||||
return d;
|
||||
|
||||
if (readBindings(d, conf.commonDataPath, conf.rgssVersion))
|
||||
return d;
|
||||
|
||||
return genDefaultBindings(conf);
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
** keybindings.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp 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.
|
||||
**
|
||||
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEYBINDINGS_H
|
||||
#define KEYBINDINGS_H
|
||||
|
||||
#include "input.h"
|
||||
|
||||
#include <SDL_scancode.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
|
||||
enum AxisDir
|
||||
{
|
||||
Negative,
|
||||
Positive
|
||||
};
|
||||
|
||||
enum SourceType
|
||||
{
|
||||
Invalid,
|
||||
Key,
|
||||
JButton,
|
||||
JAxis
|
||||
};
|
||||
|
||||
struct SourceDesc
|
||||
{
|
||||
SourceType type;
|
||||
|
||||
union Data
|
||||
{
|
||||
/* Keyboard scancode */
|
||||
SDL_Scancode scan;
|
||||
/* Joystick button index */
|
||||
uint8_t jb;
|
||||
struct
|
||||
{
|
||||
/* Joystick axis index */
|
||||
uint8_t axis;
|
||||
/* Joystick axis direction */
|
||||
AxisDir dir;
|
||||
} ja;
|
||||
} d;
|
||||
|
||||
bool operator==(const SourceDesc &o) const
|
||||
{
|
||||
if (type != o.type)
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Invalid:
|
||||
return true;
|
||||
case Key:
|
||||
return d.scan == o.d.scan;
|
||||
case JButton:
|
||||
return d.jb == o.d.jb;
|
||||
case JAxis:
|
||||
return (d.ja.axis == o.d.ja.axis) && (d.ja.dir == o.d.ja.dir);
|
||||
default:
|
||||
assert(!"unreachable");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator!=(const SourceDesc &o) const
|
||||
{
|
||||
return !(*this == o);
|
||||
}
|
||||
};
|
||||
|
||||
#define JAXIS_THRESHOLD 0x4000
|
||||
|
||||
struct BindingDesc
|
||||
{
|
||||
SourceDesc src;
|
||||
Input::ButtonCode target;
|
||||
};
|
||||
|
||||
typedef std::vector<BindingDesc> BDescVec;
|
||||
struct Config;
|
||||
|
||||
BDescVec genDefaultBindings(const Config &conf);
|
||||
|
||||
void storeBindings(const BDescVec &d, const Config &conf);
|
||||
BDescVec loadBindings(const Config &conf);
|
||||
|
||||
#endif // KEYBINDINGS_H
|
10
src/main.cpp
10
src/main.cpp
|
@ -238,7 +238,7 @@ int main(int argc, char *argv[])
|
|||
SDL_SetHint("SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS", "0");
|
||||
|
||||
SDL_Window *win;
|
||||
Uint32 winFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
|
||||
Uint32 winFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_INPUT_FOCUS;
|
||||
|
||||
if (conf.winResizable)
|
||||
winFlags |= SDL_WINDOW_RESIZABLE;
|
||||
|
@ -268,6 +268,9 @@ int main(int argc, char *argv[])
|
|||
EventThread eventThread;
|
||||
RGSSThreadData rtData(&eventThread, argv[0], win, conf);
|
||||
|
||||
/* Load and post key bindings */
|
||||
rtData.bindingUpdateMsg.post(loadBindings(conf));
|
||||
|
||||
/* Start RGSS thread */
|
||||
SDL_Thread *rgssThread =
|
||||
SDL_CreateThread(rgssThreadFun, "rgss", &rtData);
|
||||
|
@ -310,6 +313,11 @@ int main(int argc, char *argv[])
|
|||
/* Clean up any remainin events */
|
||||
eventThread.cleanup();
|
||||
|
||||
/* Store key bindings */
|
||||
BDescVec keyBinds;
|
||||
rtData.bindingUpdateMsg.get(keyBinds);
|
||||
storeBindings(keyBinds, rtData.config);
|
||||
|
||||
Debug() << "Shutting down.";
|
||||
|
||||
SDL_DestroyWindow(win);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
** settingsmenu.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp 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.
|
||||
**
|
||||
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SETTINGSMENU_H
|
||||
#define SETTINGSMENU_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct SettingsMenuPrivate;
|
||||
struct RGSSThreadData;
|
||||
union SDL_Event;
|
||||
|
||||
class SettingsMenu
|
||||
{
|
||||
public:
|
||||
SettingsMenu(RGSSThreadData &rtData);
|
||||
~SettingsMenu();
|
||||
|
||||
/* Returns true if the event was consumed */
|
||||
bool onEvent(const SDL_Event &event);
|
||||
void raise();
|
||||
bool destroyReq() const;
|
||||
|
||||
private:
|
||||
SettingsMenuPrivate *p;
|
||||
};
|
||||
|
||||
#endif // SETTINGSMENU_H
|
|
@ -106,6 +106,7 @@ struct SharedStatePrivate
|
|||
config(threadData->config),
|
||||
midiState(threadData->config),
|
||||
graphics(threadData),
|
||||
input(*threadData),
|
||||
audio(threadData->config),
|
||||
fontState(threadData->config),
|
||||
stampCounter(0)
|
||||
|
|
Loading…
Reference in New Issue