GUI: Input remapping, part 1

This commit is contained in:
Jeffrey Pfau 2016-01-07 23:52:55 -08:00
parent 2254fc68c2
commit 331d92d363
12 changed files with 344 additions and 53 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2016 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -6,9 +6,14 @@
#include "gui-config.h"
#include "gba/gui/gui-runner.h"
#include "gba/gui/remap.h"
#include "util/gui/file-select.h"
#include "util/gui/menu.h"
#ifndef GUI_MAX_INPUTS
#define GUI_MAX_INPUTS 7
#endif
void GBAGUIShowConfig(struct GBAGUIRunner* runner, struct GUIMenuItem* extra, size_t nExtra) {
struct GUIMenu menu = {
.title = "Configure",
@ -22,8 +27,9 @@ void GBAGUIShowConfig(struct GBAGUIRunner* runner, struct GUIMenuItem* extra, si
.submenu = 0,
.state = 0,
.validStates = (const char*[]) {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 0
}
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
},
.nStates = 10
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Show framerate",
@ -31,8 +37,9 @@ void GBAGUIShowConfig(struct GBAGUIRunner* runner, struct GUIMenuItem* extra, si
.submenu = 0,
.state = false,
.validStates = (const char*[]) {
"Off", "On", 0
}
"Off", "On"
},
.nStates = 2
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Use BIOS if found",
@ -40,20 +47,38 @@ void GBAGUIShowConfig(struct GBAGUIRunner* runner, struct GUIMenuItem* extra, si
.submenu = 0,
.state = true,
.validStates = (const char*[]) {
"Off", "On", 0
}
"Off", "On"
},
.nStates = 2
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Select BIOS path",
.data = "bios",
};
size_t i;
const char* mapNames[GUI_MAX_INPUTS + 1];
if (runner->keySources) {
for (i = 0; runner->keySources[i].id && i < GUI_MAX_INPUTS; ++i) {
mapNames[i] = runner->keySources[i].name;
}
if (i == 1) {
// Don't display a name if there's only one input source
i = 0;
}
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Remap controls",
.data = "*REMAP",
.state = 0,
.validStates = i ? mapNames : 0,
.nStates = i
};
}
for (i = 0; i < nExtra; ++i) {
*GUIMenuItemListAppend(&menu.items) = extra[i];
}
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Save",
.data = "[SAVE]",
.data = "*SAVE",
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Cancel",
@ -76,7 +101,7 @@ void GBAGUIShowConfig(struct GBAGUIRunner* runner, struct GUIMenuItem* extra, si
if (reason != GUI_MENU_EXIT_ACCEPT || !item->data) {
break;
}
if (!strcmp(item->data, "[SAVE]")) {
if (!strcmp(item->data, "*SAVE")) {
if (biosPath[0]) {
GBAConfigSetValue(&runner->context.config, "bios", biosPath);
}
@ -90,6 +115,10 @@ void GBAGUIShowConfig(struct GBAGUIRunner* runner, struct GUIMenuItem* extra, si
GBAConfigSave(&runner->context.config);
break;
}
if (!strcmp(item->data, "*REMAP")) {
GBAGUIRemapKeys(&runner->params, &runner->context.inputMap, &runner->keySources[item->state]);
continue;
}
if (!strcmp(item->data, "bios")) {
// TODO: show box if failed
if (!GUISelectFile(&runner->params, biosPath, sizeof(biosPath), GBAIsBIOS)) {
@ -99,7 +128,7 @@ void GBAGUIShowConfig(struct GBAGUIRunner* runner, struct GUIMenuItem* extra, si
}
if (item->validStates) {
++item->state;
if (!item->validStates[item->state]) {
if (item->state >= item->nStates) {
item->state = 0;
}
}

View File

@ -116,6 +116,13 @@ void GBAGUIInit(struct GBAGUIRunner* runner, const char* port) {
if (runner->setup) {
runner->setup(runner);
}
if (runner->context.config.port && runner->keySources) {
size_t i;
for (i = 0; runner->keySources[i].id; ++i) {
GBAInputMapLoad(&runner->context.inputMap, runner->keySources[i].id, GBAConfigGetInput(&runner->context.config));
}
}
}
void GBAGUIDeinit(struct GBAGUIRunner* runner) {
@ -123,6 +130,12 @@ void GBAGUIDeinit(struct GBAGUIRunner* runner) {
runner->teardown(runner);
}
if (runner->context.config.port) {
if (runner->keySources) {
size_t i;
for (i = 0; runner->keySources[i].id; ++i) {
GBAInputMapSave(&runner->context.inputMap, runner->keySources[i].id, GBAConfigGetInput(&runner->context.config));
}
}
GBAConfigSave(&runner->context.config);
}
CircleBufferDeinit(&runner->fpsBuffer);

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2016 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -7,6 +7,7 @@
#define GUI_RUNNER_H
#include "gba/context/context.h"
#include "gba/gui/remap.h"
#include "util/circle-buffer.h"
#include "util/gui.h"
@ -39,6 +40,8 @@ struct GBAGUIRunner {
struct GUIMenuItem* configExtra;
size_t nConfigExtra;
struct GUIInputKeys* keySources;
float fps;
int64_t lastFpsCheck;
int32_t totalDelta;

62
src/gba/gui/remap.c Normal file
View File

@ -0,0 +1,62 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "remap.h"
#include "gba/input.h"
#include "util/gui.h"
#include "util/gui/menu.h"
void GBAGUIRemapKeys(struct GUIParams* params, struct GBAInputMap* map, const struct GUIInputKeys* keys) {
struct GUIMenu menu = {
.title = "Remap keys",
.index = 0,
.background = 0
};
GUIMenuItemListInit(&menu.items, 0);
const char* keyNames[keys->nKeys + 1];
memcpy(&keyNames[1], keys->keyNames, keys->nKeys * sizeof(keyNames[0]));
keyNames[0] = "Unmapped";
size_t i;
for (i = 0; i < GBA_KEY_MAX; ++i) {
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = GBAKeyNames[i],
.data = (void*) (GUI_INPUT_MAX + i),
.submenu = 0,
.state = GBAInputQueryBinding(map, keys->id, i) + 1,
.validStates = keyNames,
.nStates = keys->nKeys + 1
};
}
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Save",
.data = (void*) (GUI_INPUT_MAX + GBA_KEY_MAX + 2),
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Cancel",
.data = 0,
};
struct GUIMenuItem* item;
while (true) {
enum GUIMenuExitReason reason;
reason = GUIShowMenu(params, &menu, &item);
if (reason != GUI_MENU_EXIT_ACCEPT || !item->data) {
break;
}
if (item->data == (void*) (GUI_INPUT_MAX + GBA_KEY_MAX + 2)) {
for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) {
item = GUIMenuItemListGetPointer(&menu.items, i);
if (i < GBA_KEY_MAX) {
GBAInputBindKey(map, keys->id, item->state - 1, i);
}
}
break;
}
if (item->validStates) {
// TODO: Open remap menu
}
}
}

23
src/gba/gui/remap.h Normal file
View File

@ -0,0 +1,23 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GUI_REMAP_H
#define GUI_REMAP_H
#include "util/common.h"
struct GUIInputKeys {
const char* name;
uint32_t id;
const char* const* keyNames;
size_t nKeys;
};
struct GUIParams;
struct GBAInputMap;
void GBAGUIRemapKeys(struct GUIParams*, struct GBAInputMap*, const struct GUIInputKeys*);
#endif

View File

@ -30,6 +30,8 @@ static enum ScreenMode {
SM_MAX
} screenMode = SM_PA_TOP;
#define _3DS_INPUT 0x3344534B
#define AUDIO_SAMPLES 0x80
#define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 24)
@ -68,6 +70,10 @@ enum {
extern bool allocateRomBuffer(void);
static void _map3DSKey(struct GBAInputMap* map, int ctrKey, enum GBAKey key) {
GBAInputBindKey(map, _3DS_INPUT, __builtin_ctz(ctrKey), key);
}
static void _csndPlaySound(u32 flags, u32 sampleRate, float vol, void* left, void* right, u32 size)
{
u32 pleft = 0, pright = 0;
@ -185,6 +191,17 @@ static void _setup(struct GBAGUIRunner* runner) {
runner->context.gba->stream = &stream;
}
_map3DSKey(&runner->context.inputMap, KEY_A, GBA_KEY_A);
_map3DSKey(&runner->context.inputMap, KEY_B, GBA_KEY_B);
_map3DSKey(&runner->context.inputMap, KEY_START, GBA_KEY_START);
_map3DSKey(&runner->context.inputMap, KEY_SELECT, GBA_KEY_SELECT);
_map3DSKey(&runner->context.inputMap, KEY_UP, GBA_KEY_UP);
_map3DSKey(&runner->context.inputMap, KEY_DOWN, GBA_KEY_DOWN);
_map3DSKey(&runner->context.inputMap, KEY_LEFT, GBA_KEY_LEFT);
_map3DSKey(&runner->context.inputMap, KEY_RIGHT, GBA_KEY_RIGHT);
_map3DSKey(&runner->context.inputMap, KEY_L, GBA_KEY_L);
_map3DSKey(&runner->context.inputMap, KEY_R, GBA_KEY_R);
GBAVideoSoftwareRendererCreate(&renderer);
renderer.outputBuffer = linearMemAlign(256 * VIDEO_VERTICAL_PIXELS * 2, 0x80);
renderer.outputBufferStride = 256;
@ -343,9 +360,10 @@ static uint16_t _pollGameInput(struct GBAGUIRunner* runner) {
UNUSED(runner);
hidScanInput();
uint32_t activeKeys = hidKeysHeld() & 0xF00003FF;
activeKeys |= activeKeys >> 24;
return activeKeys;
uint32_t activeKeys = hidKeysHeld();
uint16_t keys = GBAInputMapKeyBits(&runner->context.inputMap, _3DS_INPUT, activeKeys, 0);
keys |= (activeKeys >> 24) & 0xF0;
return keys;
}
static void _incrementScreenMode(struct GBAGUIRunner* runner) {
@ -519,6 +537,26 @@ int main() {
GUI_PARAMS_TRAIL
},
.keySources = (struct GUIInputKeys[]) {
{
.name = "3DS Input",
.id = _3DS_INPUT,
.keyNames = (const char*[]) {
"A",
"B",
"Select",
"Start",
"D-Pad Right",
"D-Pad Left",
"D-Pad Up",
"D-Pad Down",
"R",
"L",
},
.nKeys = 10
},
{ .id = 0 }
},
.configExtra = (struct GUIMenuItem[]) {
{
.title = "Screen mode",
@ -532,8 +570,8 @@ int main() {
"Pixel-Accurate/Top",
"Aspect-Ratio Fit/Top",
"Stretched/Top",
0
}
},
.nStates = 6
}
},
.nConfigExtra = 1,

View File

@ -117,10 +117,36 @@ int main() {
"With Background",
"Without Background",
"Stretched",
0
}
},
.nStates = 3
}
},
.keySources = (struct GUIInputKeys[]) {
{
.name = "Vita Input",
.id = PSP2_INPUT,
.keyNames = (const char*[]) {
"Select",
0,
0,
"Start",
"Up",
"Right",
"Down",
"Left",
"L",
"R",
0, // L2?
0, // R2?
"Triangle",
"Circle",
"Cross",
"Square"
},
.nKeys = 16
},
{ .id = 0 }
},
.nConfigExtra = 1,
.setup = GBAPSP2Setup,
.teardown = GBAPSP2Teardown,

View File

@ -11,4 +11,6 @@
#define PSP2_HORIZONTAL_PIXELS 960
#define PSP2_VERTICAL_PIXELS 544
#define PSP2_INPUT 0x50535032
#endif

View File

@ -48,7 +48,6 @@ static struct GBASceRotationSource {
extern const uint8_t _binary_backdrop_png_start[];
static vita2d_texture* backdrop = 0;
#define PSP2_INPUT 0x50535032
#define PSP2_SAMPLES 64
#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 19)
@ -138,11 +137,6 @@ uint16_t GBAPSP2PollInput(struct GBAGUIRunner* runner) {
void GBAPSP2Setup(struct GBAGUIRunner* runner) {
scePowerSetArmClockFrequency(80);
struct GBAOptions opts = {
.useBios = true,
.idleOptimization = IDLE_LOOP_DETECT
};
GBAConfigLoadDefaults(&runner->context.config, &opts);
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_CROSS, GBA_KEY_A);
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_CIRCLE, GBA_KEY_B);
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_START, GBA_KEY_START);
@ -158,7 +152,6 @@ void GBAPSP2Setup(struct GBAGUIRunner* runner) {
GBAInputBindAxis(&runner->context.inputMap, PSP2_INPUT, 0, &desc);
desc = (struct GBAAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 };
GBAInputBindAxis(&runner->context.inputMap, PSP2_INPUT, 1, &desc);
GBAInputMapLoad(&runner->context.inputMap, PSP2_INPUT, GBAConfigGetInput(&runner->context.config));
tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
screenshot = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
@ -221,7 +214,6 @@ void GBAPSP2UnloadROM(struct GBAGUIRunner* runner) {
}
void GBAPSP2Teardown(struct GBAGUIRunner* runner) {
GBAInputMapSave(&runner->context.inputMap, PSP2_INPUT, GBAConfigGetInput(&runner->context.config));
vita2d_free_texture(tex);
vita2d_free_texture(screenshot);
}

View File

@ -63,7 +63,6 @@ static void _guiPrepare(void);
static void _guiFinish(void);
static void _setup(struct GBAGUIRunner* runner);
static void _teardown(struct GBAGUIRunner* runner);
static void _gameLoaded(struct GBAGUIRunner* runner);
static void _gameUnloaded(struct GBAGUIRunner* runner);
static void _unpaused(struct GBAGUIRunner* runner);
@ -215,6 +214,113 @@ int main() {
GUI_PARAMS_TRAIL
},
.keySources = (struct GUIInputKeys[]) {
{
.name = "GameCube Input (1)",
.id = GCN1_INPUT,
.keyNames = (const char*[]) {
"D-Pad Left",
"D-Pad Right",
"D-Pad Down",
"D-Pad Up",
"Z",
"R",
"L",
0,
"A",
"B",
"X",
"Y",
"Start"
},
.nKeys = 13
},
{
.name = "GameCube Input (2)",
.id = GCN2_INPUT,
.keyNames = (const char*[]) {
"D-Pad Left",
"D-Pad Right",
"D-Pad Down",
"D-Pad Up",
"Z",
"R",
"L",
0,
"A",
"B",
"X",
"Y",
"Start"
},
.nKeys = 13
},
{
.name = "Wii Remote Input",
.id = WIIMOTE_INPUT,
.keyNames = (const char*[]) {
"2",
"1",
"B",
"A",
"Minus",
0,
0,
"Home",
"Left",
"Right",
"Down",
"Up",
"Plus",
0,
0,
0,
"Z",
"C",
},
.nKeys = 18
},
{
.name = "Classic Controller Input",
.id = CLASSIC_INPUT,
.keyNames = (const char*[]) {
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
"Up",
"Left",
"ZR",
"X",
"A",
"Y",
"B",
"ZL",
0,
"R",
"Plus",
"Home",
"Minus",
"L",
"Down",
"Right",
},
.nKeys = 32
},
{ .id = 0 }
},
.configExtra = (struct GUIMenuItem[]) {
{
.title = "Screen mode",
@ -224,8 +330,8 @@ int main() {
.validStates = (const char*[]) {
"Pixel-Accurate",
"Stretched",
0
}
},
.nStates = 2
},
{
.title = "Filtering",
@ -235,13 +341,13 @@ int main() {
.validStates = (const char*[]) {
"Pixelated",
"Resampled",
0
}
},
.nStates = 2
}
},
.nConfigExtra = 2,
.setup = _setup,
.teardown = _teardown,
.teardown = 0,
.gameLoaded = _gameLoaded,
.gameUnloaded = _gameUnloaded,
.prepareForFrame = 0,
@ -407,11 +513,6 @@ void _setup(struct GBAGUIRunner* runner) {
runner->context.gba->rumble = &rumble;
runner->context.gba->rotationSource = &rotation;
struct GBAOptions opts = {
.useBios = true,
.idleOptimization = IDLE_LOOP_DETECT
};
GBAConfigLoadDefaults(&runner->context.config, &opts);
_mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
_mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
_mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
@ -452,10 +553,6 @@ void _setup(struct GBAGUIRunner* runner) {
desc = (struct GBAAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x40, -0x40 };
GBAInputBindAxis(&runner->context.inputMap, GCN1_INPUT, 1, &desc);
GBAInputBindAxis(&runner->context.inputMap, CLASSIC_INPUT, 1, &desc);
GBAInputMapLoad(&runner->context.inputMap, GCN1_INPUT, GBAConfigGetInput(&runner->context.config));
GBAInputMapLoad(&runner->context.inputMap, GCN2_INPUT, GBAConfigGetInput(&runner->context.config));
GBAInputMapLoad(&runner->context.inputMap, WIIMOTE_INPUT, GBAConfigGetInput(&runner->context.config));
GBAInputMapLoad(&runner->context.inputMap, CLASSIC_INPUT, GBAConfigGetInput(&runner->context.config));
GBAVideoSoftwareRendererCreate(&renderer);
renderer.outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
@ -471,13 +568,6 @@ void _setup(struct GBAGUIRunner* runner) {
#endif
}
void _teardown(struct GBAGUIRunner* runner) {
GBAInputMapSave(&runner->context.inputMap, GCN1_INPUT, GBAConfigGetInput(&runner->context.config));
GBAInputMapSave(&runner->context.inputMap, GCN2_INPUT, GBAConfigGetInput(&runner->context.config));
GBAInputMapSave(&runner->context.inputMap, WIIMOTE_INPUT, GBAConfigGetInput(&runner->context.config));
GBAInputMapSave(&runner->context.inputMap, CLASSIC_INPUT, GBAConfigGetInput(&runner->context.config));
}
void _gameUnloaded(struct GBAGUIRunner* runner) {
UNUSED(runner);
AUDIO_StopDMA();

View File

@ -38,7 +38,13 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, menu->index);
if (item->validStates) {
if (item->state > 0) {
--item->state;
unsigned oldState = item->state;
do {
--item->state;
} while (!item->validStates[item->state] && item->state > 0);
if (!item->validStates[item->state]) {
item->state = oldState;
}
}
} else if (menu->index >= pageSize) {
menu->index -= pageSize;
@ -49,8 +55,14 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
if (newInput & (1 << GUI_INPUT_RIGHT)) {
struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, menu->index);
if (item->validStates) {
if (item->validStates[item->state + 1]) {
++item->state;
if (item->state < item->nStates - 1) {
unsigned oldState = item->state;
do {
++item->state;
} while (!item->validStates[item->state] && item->state < item->nStates - 1);
if (!item->validStates[item->state]) {
item->state = oldState;
}
}
} else if (menu->index + pageSize < GUIMenuItemListSize(&menu->items)) {
menu->index += pageSize;
@ -120,7 +132,7 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
}
struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, i);
GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, color, "%c %s", bullet, item->title);
if (item->validStates) {
if (item->validStates && item->validStates[item->state]) {
GUIFontPrintf(params->font, params->width, y, GUI_TEXT_RIGHT, color, "%s ", item->validStates[item->state]);
}
y += lineHeight;

View File

@ -13,7 +13,8 @@ struct GUIMenuItem {
const char* title;
void* data;
unsigned state;
const char** validStates;
const char* const* validStates;
unsigned nStates;
struct GUIMenu* submenu;
};