Introduce F1 menu to reconfigure key bindings at runtime

This commit is contained in:
Jonas Kulla 2014-01-25 09:24:55 +01:00
parent af145c3a01
commit dd73db2e9d
17 changed files with 1837 additions and 136 deletions

View File

@ -135,6 +135,8 @@ set(MAIN_HEADERS
src/gl-util.h src/gl-util.h
src/util.h src/util.h
src/config.h src/config.h
src/settingsmenu.h
src/keybindings.h
src/tileatlas.h src/tileatlas.h
src/sharedstate.h src/sharedstate.h
src/al-util.h src/al-util.h
@ -180,6 +182,8 @@ set(MAIN_SOURCE
src/debuglogger.cpp src/debuglogger.cpp
src/etc.cpp src/etc.cpp
src/config.cpp src/config.cpp
src/settingsmenu.cpp
src/keybindings.cpp
src/tileatlas.cpp src/tileatlas.cpp
src/sharedstate.cpp src/sharedstate.cpp
src/gl-fun.cpp src/gl-fun.cpp

View File

@ -111,6 +111,19 @@
# allowSymlinks=false # 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' # Set the game window icon to 'path/to/icon.png'
# (default: none) # (default: none)
# #

View File

@ -114,6 +114,8 @@ HEADERS += \
src/gl-util.h \ src/gl-util.h \
src/util.h \ src/util.h \
src/config.h \ src/config.h \
src/settingsmenu.h \
src/keybindings.h \
src/tileatlas.h \ src/tileatlas.h \
src/sharedstate.h \ src/sharedstate.h \
src/al-util.h \ src/al-util.h \
@ -158,6 +160,8 @@ SOURCES += \
src/debuglogger.cpp \ src/debuglogger.cpp \
src/etc.cpp \ src/etc.cpp \
src/config.cpp \ src/config.cpp \
src/settingsmenu.cpp \
src/keybindings.cpp \
src/tileatlas.cpp \ src/tileatlas.cpp \
src/sharedstate.cpp \ src/sharedstate.cpp \
src/gl-fun.cpp \ src/gl-fun.cpp \

View File

@ -25,6 +25,8 @@
#include <boost/program_options/parsers.hpp> #include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp> #include <boost/program_options/variables_map.hpp>
#include <SDL_filesystem.h>
#include <fstream> #include <fstream>
#include <stdint.h> #include <stdint.h>
@ -118,6 +120,15 @@ static bool validUtf8(const char *string)
return true; 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; typedef std::vector<std::string> StringVec;
namespace po = boost::program_options; namespace po = boost::program_options;
@ -167,6 +178,8 @@ void Config::read(int argc, char *argv[])
PO_DESC(anyAltToggleFS, bool) \ PO_DESC(anyAltToggleFS, bool) \
PO_DESC(enableReset, bool) \ PO_DESC(enableReset, bool) \
PO_DESC(allowSymlinks, bool) \ PO_DESC(allowSymlinks, bool) \
PO_DESC(dataPathOrg, std::string) \
PO_DESC(dataPathApp, std::string) \
PO_DESC(iconPath, std::string) \ PO_DESC(iconPath, std::string) \
PO_DESC(titleLanguage, std::string) \ PO_DESC(titleLanguage, std::string) \
PO_DESC(midi.soundFont, std::string) \ PO_DESC(midi.soundFont, std::string) \
@ -243,6 +256,11 @@ void Config::read(int argc, char *argv[])
rgssVersion = clamp(rgssVersion, 0, 3); rgssVersion = clamp(rgssVersion, 0, 3);
SE.sourceCount = clamp(SE.sourceCount, 1, 64); 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) static std::string baseName(const std::string &path)

View File

@ -51,6 +51,9 @@ struct Config
bool allowSymlinks; bool allowSymlinks;
bool pathCache; bool pathCache;
std::string dataPathOrg;
std::string dataPathApp;
std::string iconPath; std::string iconPath;
std::string titleLanguage; std::string titleLanguage;
@ -82,6 +85,10 @@ struct Config
std::string title; std::string title;
} game; } game;
/* Internal */
std::string customDataPath;
std::string commonDataPath;
Config(); Config();
void read(int argc, char *argv[]); void read(int argc, char *argv[]);

View File

@ -29,6 +29,7 @@
#include "sharedstate.h" #include "sharedstate.h"
#include "graphics.h" #include "graphics.h"
#include "settingsmenu.h"
#include "debugwriter.h" #include "debugwriter.h"
#include <string.h> #include <string.h>
@ -37,7 +38,7 @@ bool EventThread::keyStates[] = { false };
EventThread::JoyState EventThread::joyState = EventThread::JoyState EventThread::joyState =
{ {
0, 0, { false } { 0 }, { false }
}; };
EventThread::MouseState EventThread::mouseState = EventThread::MouseState EventThread::mouseState =
@ -108,6 +109,8 @@ void EventThread::process(RGSSThreadData &rtData)
bool resetting = false; bool resetting = false;
SettingsMenu *sMenu = 0;
while (true) while (true)
{ {
if (!SDL_WaitEvent(&event)) if (!SDL_WaitEvent(&event))
@ -116,6 +119,19 @@ void EventThread::process(RGSSThreadData &rtData)
break; break;
} }
if (sMenu && sMenu->onEvent(event))
{
if (sMenu->destroyReq())
{
delete sMenu;
sMenu = 0;
updateCursorState(cursorInWindow && windowFocused);
}
continue;
}
switch (event.type) switch (event.type)
{ {
case SDL_WINDOWEVENT : case SDL_WINDOWEVENT :
@ -129,14 +145,14 @@ void EventThread::process(RGSSThreadData &rtData)
case SDL_WINDOWEVENT_ENTER : case SDL_WINDOWEVENT_ENTER :
cursorInWindow = true; cursorInWindow = true;
mouseState.inWindow = true; mouseState.inWindow = true;
updateCursorState(cursorInWindow && windowFocused); updateCursorState(cursorInWindow && windowFocused && !sMenu);
break; break;
case SDL_WINDOWEVENT_LEAVE : case SDL_WINDOWEVENT_LEAVE :
cursorInWindow = false; cursorInWindow = false;
mouseState.inWindow = false; mouseState.inWindow = false;
updateCursorState(cursorInWindow && windowFocused); updateCursorState(cursorInWindow && windowFocused && !sMenu);
break; break;
@ -147,13 +163,13 @@ void EventThread::process(RGSSThreadData &rtData)
case SDL_WINDOWEVENT_FOCUS_GAINED : case SDL_WINDOWEVENT_FOCUS_GAINED :
windowFocused = true; windowFocused = true;
updateCursorState(cursorInWindow && windowFocused); updateCursorState(cursorInWindow && windowFocused && !sMenu);
break; break;
case SDL_WINDOWEVENT_FOCUS_LOST : case SDL_WINDOWEVENT_FOCUS_LOST :
windowFocused = false; windowFocused = false;
updateCursorState(cursorInWindow && windowFocused); updateCursorState(cursorInWindow && windowFocused && !sMenu);
resetInputStates(); resetInputStates();
break; break;
@ -181,6 +197,17 @@ void EventThread::process(RGSSThreadData &rtData)
break; 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 (event.key.keysym.scancode == SDL_SCANCODE_F2)
{ {
if (!fps.displaying) if (!fps.displaying)
@ -248,11 +275,7 @@ void EventThread::process(RGSSThreadData &rtData)
break; break;
case SDL_JOYAXISMOTION : case SDL_JOYAXISMOTION :
if (event.jaxis.axis == 0) joyState.axis[event.jaxis.axis] = event.jaxis.value;
joyState.xAxis = event.jaxis.value;
else
joyState.yAxis = event.jaxis.value;
break; break;
case SDL_JOYDEVICEADDED : case SDL_JOYDEVICEADDED :
@ -333,6 +356,8 @@ void EventThread::process(RGSSThreadData &rtData)
if (SDL_JoystickGetAttached(js)) if (SDL_JoystickGetAttached(js))
SDL_JoystickClose(js); SDL_JoystickClose(js);
delete sMenu;
} }
void EventThread::cleanup() void EventThread::cleanup()

View File

@ -25,6 +25,7 @@
#include "config.h" #include "config.h"
#include "etc-internal.h" #include "etc-internal.h"
#include "sdl-util.h" #include "sdl-util.h"
#include "keybindings.h"
#include <SDL_scancode.h> #include <SDL_scancode.h>
#include <SDL_joystick.h> #include <SDL_joystick.h>
@ -45,10 +46,8 @@ public:
struct JoyState struct JoyState
{ {
int xAxis; int axis[256];
int yAxis; bool buttons[256];
bool buttons[16];
}; };
static JoyState joyState; 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 struct RGSSThreadData
{ {
/* Main thread sets this to request RGSS thread to terminate */ /* Main thread sets this to request RGSS thread to terminate */
@ -171,6 +219,7 @@ struct RGSSThreadData
EventThread *ethread; EventThread *ethread;
WindowSizeNotify windowSizeMsg; WindowSizeNotify windowSizeMsg;
BindingNotify bindingUpdateMsg;
const char *argv0; const char *argv0;

View File

@ -195,6 +195,13 @@ bool SharedFontState::fontPresent(std::string family)
return !(set.regular.empty() && set.other.empty()); 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 struct FontPrivate
{ {

View File

@ -49,6 +49,8 @@ public:
bool fontPresent(std::string family); bool fontPresent(std::string family);
static _TTF_Font *openBundled(int size);
private: private:
SharedFontStatePrivate *p; SharedFontStatePrivate *p;
}; };

View File

@ -22,6 +22,7 @@
#include "input.h" #include "input.h"
#include "sharedstate.h" #include "sharedstate.h"
#include "eventthread.h" #include "eventthread.h"
#include "keybindings.h"
#include "exception.h" #include "exception.h"
#include "util.h" #include "util.h"
@ -30,6 +31,7 @@
#include <vector> #include <vector>
#include <string.h> #include <string.h>
#include <assert.h>
#define BUTTON_CODE_COUNT 24 #define BUTTON_CODE_COUNT 24
@ -52,12 +54,6 @@ struct KbBindingData
Input::ButtonCode target; Input::ButtonCode target;
}; };
struct JsBindingData
{
int source;
Input::ButtonCode target;
};
struct Binding struct Binding
{ {
Binding(Input::ButtonCode target = Input::None) Binding(Input::ButtonCode target = Input::None)
@ -82,6 +78,15 @@ struct KbBinding : public Binding
bool sourceActive() const 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]; return EventThread::keyStates[source];
} }
@ -100,11 +105,6 @@ struct JsButtonBinding : public Binding
{ {
JsButtonBinding() {} JsButtonBinding() {}
JsButtonBinding(const JsBindingData &data)
: Binding(data.target),
source(data.source)
{}
bool sourceActive() const bool sourceActive() const
{ {
return EventThread::joyState.buttons[source]; return EventThread::joyState.buttons[source];
@ -115,7 +115,7 @@ struct JsButtonBinding : public Binding
return true; return true;
} }
int source; uint8_t source;
}; };
/* Joystick axis binding */ /* Joystick axis binding */
@ -123,17 +123,22 @@ struct JsAxisBinding : public Binding
{ {
JsAxisBinding() {} JsAxisBinding() {}
JsAxisBinding(int *source, JsAxisBinding(uint8_t source,
int compareValue, AxisDir dir,
Input::ButtonCode target) Input::ButtonCode target)
: Binding(target), : Binding(target),
source(source), source(source),
compareValue(compareValue) dir(dir)
{} {}
bool sourceActive() const 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 bool sourceRepeatable() const
@ -141,8 +146,8 @@ struct JsAxisBinding : public Binding
return true; return true;
} }
int *source; uint8_t source;
int compareValue; AxisDir dir;
}; };
/* Mouse button binding */ /* Mouse button binding */
@ -172,10 +177,6 @@ struct MsBinding : public Binding
/* Not rebindable */ /* Not rebindable */
static const KbBindingData staticKbBindings[] = 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_LSHIFT, Input::Shift },
{ SDL_SCANCODE_RSHIFT, Input::Shift }, { SDL_SCANCODE_RSHIFT, Input::Shift },
{ SDL_SCANCODE_LCTRL, Input::Ctrl }, { SDL_SCANCODE_LCTRL, Input::Ctrl },
@ -191,61 +192,6 @@ static const KbBindingData staticKbBindings[] =
static elementsN(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 /* Maps ButtonCode enum values to indices
* in the button state array */ * in the button state array */
static const int mapToIndex[] = static const int mapToIndex[] =
@ -292,6 +238,7 @@ static const Input::ButtonCode otherDirs[4][3] =
struct InputPrivate struct InputPrivate
{ {
std::vector<KbBinding> kbStatBindings;
std::vector<KbBinding> kbBindings; std::vector<KbBinding> kbBindings;
std::vector<JsAxisBinding> jsABindings; std::vector<JsAxisBinding> jsABindings;
std::vector<JsButtonBinding> jsBBindings; std::vector<JsButtonBinding> jsBBindings;
@ -320,12 +267,14 @@ struct InputPrivate
} dir8Data; } dir8Data;
InputPrivate() InputPrivate(const RGSSThreadData &rtData)
{ {
initKbBindings(); initStaticKbBindings();
initJsBindings();
initMsBindings(); initMsBindings();
/* Main thread should have these posted by now */
checkBindingChange(rtData);
states = stateArray; states = stateArray;
statesOld = stateArray + BUTTON_CODE_COUNT; statesOld = stateArray + BUTTON_CODE_COUNT;
@ -378,51 +327,90 @@ struct InputPrivate
memset(states, 0, size); memset(states, 0, size);
} }
void initKbBindings() void checkBindingChange(const RGSSThreadData &rtData)
{ {
kbBindings.clear(); BDescVec d;
for (size_t i = 0; i < staticKbBindingsN; ++i) if (!rtData.bindingUpdateMsg.poll(d))
kbBindings.push_back(KbBinding(staticKbBindings[i])); return;
for (size_t i = 0; i < defaultKbBindingsN; ++i) applyBindingDesc(d);
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]);
} }
void initJsBindings() template<class B>
void appendBindings(std::vector<B> &bind)
{ {
/* Create axis bindings */ for (size_t i = 0; i < bind.size(); ++i)
jsABindings.resize(4); bindings.push_back(&bind[i]);
}
size_t i = 0; void applyBindingDesc(const BDescVec &d)
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.xAxis, 0x7FFF, Input::Right); {
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.xAxis, -0x8000, Input::Left); kbBindings.clear();
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.yAxis, 0x7FFF, Input::Down); jsABindings.clear();
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.yAxis, -0x8000, Input::Up); jsBBindings.clear();
/* Create button bindings */ for (size_t i = 0; i < d.size(); ++i)
jsBBindings.resize(defaultJsBindingsN); {
const BindingDesc &desc = d[i];
const SourceDesc &src = desc.src;
for (size_t i = 0; i < defaultJsBindingsN; ++i) if (desc.target == Input::None)
jsBBindings[i] = JsButtonBinding(defaultJsBindings[i]); continue;
/* Add to binging array */ switch (desc.src.type)
for (size_t i = 0; i < jsABindings.size(); ++i) {
bindings.push_back(&jsABindings[i]); 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) break;
bindings.push_back(&jsBBindings[i]); }
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() void initMsBindings()
@ -433,10 +421,6 @@ struct InputPrivate
msBindings[i++] = MsBinding(SDL_BUTTON_LEFT, Input::MouseLeft); msBindings[i++] = MsBinding(SDL_BUTTON_LEFT, Input::MouseLeft);
msBindings[i++] = MsBinding(SDL_BUTTON_MIDDLE, Input::MouseMiddle); msBindings[i++] = MsBinding(SDL_BUTTON_MIDDLE, Input::MouseMiddle);
msBindings[i++] = MsBinding(SDL_BUTTON_RIGHT, Input::MouseRight); 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) 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() void Input::update()
{ {
shState->checkShutdown(); shState->checkShutdown();
p->checkBindingChange(shState->rtData());
p->swapBuffers(); p->swapBuffers();
p->clearBuffer(); p->clearBuffer();

View File

@ -23,6 +23,7 @@
#define INPUT_H #define INPUT_H
struct InputPrivate; struct InputPrivate;
struct RGSSThreadData;
class Input class Input
{ {
@ -59,7 +60,7 @@ public:
int mouseY(); int mouseY();
private: private:
Input(); Input(const RGSSThreadData &rtData);
~Input(); ~Input();
friend struct SharedStatePrivate; friend struct SharedStatePrivate;

311
src/keybindings.cpp Normal file
View File

@ -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);
}

108
src/keybindings.h Normal file
View File

@ -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

View File

@ -238,7 +238,7 @@ int main(int argc, char *argv[])
SDL_SetHint("SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS", "0"); SDL_SetHint("SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS", "0");
SDL_Window *win; SDL_Window *win;
Uint32 winFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN; Uint32 winFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_INPUT_FOCUS;
if (conf.winResizable) if (conf.winResizable)
winFlags |= SDL_WINDOW_RESIZABLE; winFlags |= SDL_WINDOW_RESIZABLE;
@ -268,6 +268,9 @@ int main(int argc, char *argv[])
EventThread eventThread; EventThread eventThread;
RGSSThreadData rtData(&eventThread, argv[0], win, conf); RGSSThreadData rtData(&eventThread, argv[0], win, conf);
/* Load and post key bindings */
rtData.bindingUpdateMsg.post(loadBindings(conf));
/* Start RGSS thread */ /* Start RGSS thread */
SDL_Thread *rgssThread = SDL_Thread *rgssThread =
SDL_CreateThread(rgssThreadFun, "rgss", &rtData); SDL_CreateThread(rgssThreadFun, "rgss", &rtData);
@ -310,6 +313,11 @@ int main(int argc, char *argv[])
/* Clean up any remainin events */ /* Clean up any remainin events */
eventThread.cleanup(); eventThread.cleanup();
/* Store key bindings */
BDescVec keyBinds;
rtData.bindingUpdateMsg.get(keyBinds);
storeBindings(keyBinds, rtData.config);
Debug() << "Shutting down."; Debug() << "Shutting down.";
SDL_DestroyWindow(win); SDL_DestroyWindow(win);

1112
src/settingsmenu.cpp Normal file

File diff suppressed because it is too large Load Diff

46
src/settingsmenu.h Normal file
View File

@ -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

View File

@ -106,6 +106,7 @@ struct SharedStatePrivate
config(threadData->config), config(threadData->config),
midiState(threadData->config), midiState(threadData->config),
graphics(threadData), graphics(threadData),
input(*threadData),
audio(threadData->config), audio(threadData->config),
fontState(threadData->config), fontState(threadData->config),
stampCounter(0) stampCounter(0)