mirror of https://github.com/bsnes-emu/bsnes.git
Update to v082r16 release.
byuu says: Binary output is once again called bsnes. No versioning on the title without a system cartridge loaded. Still saving config files to .config/batch for now. Finally fixed NES APU frame IRQ clearing on $4015 reads. Added mouse button/axis binding through buttons on the input capture window. Added advanced settings window with driver selection and focus policy settings. Will show your default driver properly if none are selected now, unlike old bsnes. That exposed a small bug where phoenix isn't removing widgets on Layout::remove, worked around it for now by hiding the panels. Damn, sick of working on phoenix. Added all missing input controllers, which can all now be mapped, and bound them to the main menu, and added NES support for selecting "no connected controller." Added mouse capture and the requisite tools menu option for it. Added WindowManager class that keeps track of both position and size now (eg full geometry), so now you can resize your windows and save the settings, unlike old bsnes. WindowManager has more stringent geometry checks. The *client area* (not the window border) can't be below 0,0 or above the width/height of three 30" monitors. If you have 4+ 30" monitors, then fuck you :P settings.cfg is now also saved, captures all currently available settings. Right now, there's only one path for the file browser to remember. I will probably make this per-system later. FileBrowser has been made a bit more friendly. The bottom left tells you what type of files the list is filtered by (so you see "*.sfc" for SNES), and the bottom right has an open button that can enter folders or load files. Added video shader support. Fixed nall/dsp variadic-channel support, was only outputting the left channel.
This commit is contained in:
parent
382ae1e61e
commit
5c2d16828c
|
@ -89,7 +89,7 @@ void DSP::read(signed channel[]) {
|
|||
adjustBalance();
|
||||
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
channel[c] = clamp(settings.precision, output.read(0) * settings.intensity);
|
||||
channel[c] = clamp(settings.precision, output.read(c) * settings.intensity);
|
||||
}
|
||||
output.rdoffset++;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
nes_objects := nes-interface nes-system nes-scheduler
|
||||
nes_objects := nes-interface nes-system nes-scheduler nes-input
|
||||
nes_objects += nes-mapper nes-cartridge nes-memory
|
||||
nes_objects += nes-cpu nes-apu nes-ppu
|
||||
nes_objects += nes-cheat
|
||||
|
@ -7,6 +7,7 @@ objects += $(nes_objects)
|
|||
obj/nes-interface.o: $(nes)/interface/interface.cpp $(call rwildcard,$(nes)/interface/)
|
||||
obj/nes-system.o: $(nes)/system/system.cpp $(call rwildcard,$(nes)/system/)
|
||||
obj/nes-scheduler.o: $(nes)/scheduler/scheduler.cpp $(call rwildcard,$(nes)/scheduler/)
|
||||
obj/nes-input.o: $(nes)/input/input.cpp $(call rwildcard,$(nes)/input/)
|
||||
obj/nes-mapper.o: $(nes)/mapper/mapper.cpp $(call rwildcard,$(nes)/mapper/)
|
||||
obj/nes-cartridge.o: $(nes)/cartridge/cartridge.cpp $(call rwildcard,$(nes)/cartridge/)
|
||||
obj/nes-memory.o: $(nes)/memory/memory.cpp $(call rwildcard,$(nes)/memory/)
|
||||
|
|
|
@ -139,7 +139,7 @@ void APU::reset() {
|
|||
frame.divider = 1;
|
||||
|
||||
enabled_channels = 0;
|
||||
//cpu.set_alu_irq_line(frame.irq_pending || dmc.irq_pending);
|
||||
set_irq_line();
|
||||
}
|
||||
|
||||
uint8 APU::read(uint16 addr) {
|
||||
|
@ -152,6 +152,10 @@ uint8 APU::read(uint16 addr) {
|
|||
result |= dmc.length_counter ? 0x10 : 0;
|
||||
result |= frame.irq_pending ? 0x40 : 0;
|
||||
result |= dmc.irq_pending ? 0x80 : 0;
|
||||
|
||||
frame.irq_pending = false;
|
||||
set_irq_line();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -105,13 +105,15 @@ void CPU::ram_write(uint16 addr, uint8 data) {
|
|||
|
||||
uint8 CPU::read(uint16 addr) {
|
||||
if(addr == 0x4016) {
|
||||
if(status.controller_port0 >= 8) return (mdr() & 0xc0) | 1;
|
||||
return interface->inputPoll(0, 0u, status.controller_port0++);
|
||||
return (mdr() & 0xc0) | input.data(0);
|
||||
// if(status.controller_port0 >= 8) return (mdr() & 0xc0) | 1;
|
||||
// return interface->inputPoll(0, 0u, status.controller_port0++);
|
||||
}
|
||||
|
||||
if(addr == 0x4017) {
|
||||
if(status.controller_port1 >= 8) return (mdr() & 0xc0) | 1;
|
||||
return interface->inputPoll(1, 0u, status.controller_port1++);
|
||||
return (mdr() & 0xc0) | input.data(1);
|
||||
// if(status.controller_port1 >= 8) return (mdr() & 0xc0) | 1;
|
||||
// return interface->inputPoll(1, 0u, status.controller_port1++);
|
||||
}
|
||||
|
||||
return apu.read(addr);
|
||||
|
@ -124,11 +126,12 @@ void CPU::write(uint16 addr, uint8 data) {
|
|||
}
|
||||
|
||||
if(addr == 0x4016) {
|
||||
status.controller_latch = data & 0x01;
|
||||
if(status.controller_latch) {
|
||||
status.controller_port0 = 0;
|
||||
status.controller_port1 = 0;
|
||||
}
|
||||
input.latch(data & 0x01);
|
||||
// status.controller_latch = data & 0x01;
|
||||
// if(status.controller_latch) {
|
||||
// status.controller_port0 = 0;
|
||||
// status.controller_port1 = 0;
|
||||
// }
|
||||
}
|
||||
|
||||
return apu.write(addr, data);
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
#include <nes/nes.hpp>
|
||||
|
||||
namespace NES {
|
||||
|
||||
Input input;
|
||||
|
||||
void Input::latch(bool data) {
|
||||
latchdata = data;
|
||||
|
||||
if(latchdata == 1) {
|
||||
counter1 = 0;
|
||||
counter2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Input::data(bool port) {
|
||||
bool result = 0;
|
||||
|
||||
if(port == 0) {
|
||||
if(port1 == Device::Joypad) {
|
||||
if(counter1 >= 8) return 1;
|
||||
result = interface->inputPoll(0, 0u, counter1);
|
||||
if(latchdata == 0) counter1++;
|
||||
}
|
||||
}
|
||||
|
||||
if(port == 1) {
|
||||
if(port2 == Device::Joypad) {
|
||||
if(counter2 >= 8) return 1;
|
||||
result = interface->inputPoll(1, 0u, counter2);
|
||||
if(latchdata == 0) counter2++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Input::connect(bool port, Device device) {
|
||||
if(port == 0) port1 = device, counter1 = 0;
|
||||
if(port == 1) port2 = device, counter2 = 0;
|
||||
}
|
||||
|
||||
void Input::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Input::reset() {
|
||||
latchdata = 0;
|
||||
counter1 = 0;
|
||||
counter2 = 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
struct Input {
|
||||
enum class Device : unsigned {
|
||||
None,
|
||||
Joypad,
|
||||
};
|
||||
|
||||
void latch(bool data);
|
||||
bool data(bool port);
|
||||
void connect(bool port, Device device);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
Device port1;
|
||||
Device port2;
|
||||
|
||||
bool latchdata;
|
||||
unsigned counter1;
|
||||
unsigned counter2;
|
||||
};
|
||||
|
||||
extern Input input;
|
|
@ -19,6 +19,10 @@ void Interface::initialize(Interface *derived_interface) {
|
|||
system.init();
|
||||
}
|
||||
|
||||
void Interface::connect(bool port, Input::Device device) {
|
||||
input.connect(port, device);
|
||||
}
|
||||
|
||||
bool Interface::cartridgeLoaded() {
|
||||
return cartridge.loaded();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ struct Interface {
|
|||
virtual void audioSample(int16_t sample);
|
||||
virtual int16_t inputPoll(bool port, unsigned device, unsigned id);
|
||||
|
||||
virtual void connect(bool port, Input::Device device);
|
||||
|
||||
virtual void initialize(Interface*);
|
||||
|
||||
virtual bool cartridgeLoaded();
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
namespace NES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bnes";
|
||||
static const char Version[] = "000.11";
|
||||
static const char Version[] = "000.12";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,9 +98,9 @@ namespace NES {
|
|||
inline Processor() : thread(0) {}
|
||||
};
|
||||
|
||||
#include <nes/interface/interface.hpp>
|
||||
#include <nes/system/system.hpp>
|
||||
#include <nes/scheduler/scheduler.hpp>
|
||||
#include <nes/input/input.hpp>
|
||||
#include <nes/mapper/mapper.hpp>
|
||||
#include <nes/cartridge/cartridge.hpp>
|
||||
#include <nes/memory/memory.hpp>
|
||||
|
@ -108,6 +108,7 @@ namespace NES {
|
|||
#include <nes/apu/apu.hpp>
|
||||
#include <nes/ppu/ppu.hpp>
|
||||
#include <nes/cheat/cheat.hpp>
|
||||
#include <nes/interface/interface.hpp>
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,6 +13,7 @@ void System::power() {
|
|||
cpu.power();
|
||||
apu.power();
|
||||
ppu.power();
|
||||
input.reset();
|
||||
scheduler.power();
|
||||
}
|
||||
|
||||
|
@ -21,11 +22,14 @@ void System::reset() {
|
|||
cpu.reset();
|
||||
apu.reset();
|
||||
ppu.reset();
|
||||
input.reset();
|
||||
scheduler.reset();
|
||||
}
|
||||
|
||||
void System::init() {
|
||||
assert(interface != 0);
|
||||
input.connect(0, Input::Device::Joypad);
|
||||
input.connect(1, Input::Device::None);
|
||||
}
|
||||
|
||||
void System::term() {
|
||||
|
|
|
@ -184,6 +184,10 @@ bool Window::focused() {
|
|||
return p.focused();
|
||||
}
|
||||
|
||||
bool Window::fullScreen() {
|
||||
return state.fullScreen;
|
||||
}
|
||||
|
||||
Geometry Window::geometry() {
|
||||
return p.geometry();
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ struct Window : private nall::base_from_member<pWindow&>, Object {
|
|||
Geometry frameGeometry();
|
||||
Geometry frameMargin();
|
||||
bool focused();
|
||||
bool fullScreen();
|
||||
Geometry geometry();
|
||||
void ignore();
|
||||
void remove(Layout &layout);
|
||||
|
|
|
@ -19,6 +19,10 @@ void Interface::initialize(Interface *derived_interface) {
|
|||
system.init();
|
||||
}
|
||||
|
||||
void Interface::connect(bool port, Input::Device device) {
|
||||
input.connect(port, device);
|
||||
}
|
||||
|
||||
bool Interface::cartridgeLoaded() {
|
||||
return cartridge.loaded();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ public:
|
|||
|
||||
virtual void initialize(Interface*);
|
||||
|
||||
virtual void connect(bool port, Input::Device device);
|
||||
|
||||
virtual bool cartridgeLoaded();
|
||||
virtual void loadCartridge(const string &xml, const uint8_t *data, unsigned size);
|
||||
virtual void unloadCartridge();
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
namespace SNES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "082.15";
|
||||
static const char Version[] = "082.16";
|
||||
static const unsigned SerializerVersion = 22;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
include $(nes)/Makefile
|
||||
include $(snes)/Makefile
|
||||
include $(gameboy)/Makefile
|
||||
name := batch
|
||||
name := bsnes
|
||||
|
||||
ui_objects := ui-main ui-config ui-interface ui-input ui-utility
|
||||
ui_objects += ui-general ui-settings ui-tools
|
||||
ui_objects += ui-window ui-general ui-settings ui-tools
|
||||
ui_objects += phoenix ruby
|
||||
ui_objects += $(if $(call streq,$(platform),win),resource)
|
||||
|
||||
|
@ -72,6 +72,7 @@ obj/ui-config.o: $(ui)/config/config.cpp $(call rwildcard,$(ui)/)
|
|||
obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-window.o: $(ui)/window/window.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-tools.o: $(ui)/tools/tools.cpp $(call rwildcard,$(ui)/)
|
||||
|
|
|
@ -23,12 +23,14 @@ using namespace ruby;
|
|||
#include "interface/interface.hpp"
|
||||
#include "input/input.hpp"
|
||||
#include "utility/utility.hpp"
|
||||
#include "window/window.hpp"
|
||||
#include "general/general.hpp"
|
||||
#include "settings/settings.hpp"
|
||||
#include "tools/tools.hpp"
|
||||
|
||||
struct Application {
|
||||
bool quit;
|
||||
bool autopause;
|
||||
|
||||
string realpath;
|
||||
string userpath;
|
||||
|
|
|
@ -2,7 +2,30 @@
|
|||
Config *config = 0;
|
||||
|
||||
Config::Config() {
|
||||
attach(video.driver = "", "Video::Driver");
|
||||
attach(video.shader = "", "Video::Shader");
|
||||
attach(video.synchronize = true, "Video::Synchronize");
|
||||
attach(video.smooth = false, "Video::Smooth");
|
||||
|
||||
attach(audio.driver = "", "Audio::Driver");
|
||||
attach(audio.synchronize = true, "Audio::Synchronize");
|
||||
attach(audio.mute = false, "Audio::Mute");
|
||||
|
||||
attach(input.driver = "", "Input::Driver");
|
||||
attach(input.focusPolicy = 1, "Input::FocusPolicy");
|
||||
|
||||
attach(path.last = application->realpath, "Path::Recent");
|
||||
|
||||
attach(nes.controllerPort1Device = 1, "NES::Controller::Port1");
|
||||
attach(nes.controllerPort2Device = 0, "NES::Controller::Port2");
|
||||
|
||||
attach(snes.controllerPort1Device = 1, "SNES::Controller::Port1");
|
||||
attach(snes.controllerPort2Device = 0, "SNES::Controller::Port2");
|
||||
|
||||
load(string{ application->userpath, "settings.cfg" });
|
||||
save(string{ application->userpath, "settings.cfg" });
|
||||
}
|
||||
|
||||
Config::~Config() {
|
||||
save(string{ application->userpath, "settings.cfg" });
|
||||
}
|
||||
|
|
|
@ -1,4 +1,36 @@
|
|||
struct Config : public configuration {
|
||||
struct Video {
|
||||
string driver;
|
||||
string shader;
|
||||
bool synchronize;
|
||||
bool smooth;
|
||||
} video;
|
||||
|
||||
struct Audio {
|
||||
string driver;
|
||||
bool synchronize;
|
||||
bool mute;
|
||||
} audio;
|
||||
|
||||
struct Input {
|
||||
string driver;
|
||||
unsigned focusPolicy;
|
||||
} input;
|
||||
|
||||
struct Path {
|
||||
string last;
|
||||
} path;
|
||||
|
||||
struct NES {
|
||||
unsigned controllerPort1Device;
|
||||
unsigned controllerPort2Device;
|
||||
} nes;
|
||||
|
||||
struct SNES {
|
||||
unsigned controllerPort1Device;
|
||||
unsigned controllerPort2Device;
|
||||
} snes;
|
||||
|
||||
Config();
|
||||
~Config();
|
||||
};
|
||||
|
|
|
@ -2,19 +2,22 @@ FileBrowser *fileBrowser = 0;
|
|||
|
||||
FileBrowser::FileBrowser() {
|
||||
setGeometry({ 128, 128, 640, 400 });
|
||||
setWidgetFont(application->normalFont);
|
||||
|
||||
pathBrowse.setText("Browse ...");
|
||||
pathUp.setText("..");
|
||||
windowManager->append(this, "FileBrowser");
|
||||
|
||||
layout.setMargin(5);
|
||||
pathBrowse.setText("Browse ...");
|
||||
pathUp.setText("..");
|
||||
openButton.setText("Open");
|
||||
|
||||
append(layout);
|
||||
layout.append(pathLayout, ~0, 0, 5);
|
||||
pathLayout.append(pathEdit, ~0, 0, 5);
|
||||
pathLayout.append(pathBrowse, 0, 0, 5);
|
||||
pathLayout.append(pathUp, 0, 0);
|
||||
layout.append(fileList, ~0, ~0);
|
||||
layout.append(fileList, ~0, ~0, 5);
|
||||
layout.append(controlLayout, ~0, 0);
|
||||
controlLayout.append(filterLabel, ~0, 0, 5);
|
||||
controlLayout.append(openButton, 80, 0);
|
||||
|
||||
pathEdit.onActivate = [&] {
|
||||
string path = pathEdit.text();
|
||||
|
@ -36,12 +39,9 @@ FileBrowser::FileBrowser() {
|
|||
setPath(path);
|
||||
};
|
||||
|
||||
fileList.onActivate = { &FileBrowser::fileListActivate, this };
|
||||
fileList.onActivate = openButton.onTick = { &FileBrowser::fileListActivate, this };
|
||||
|
||||
char path[PATH_MAX];
|
||||
auto unused = getcwd(path);
|
||||
strcpy(path, "/media/sdb1/root/nes_images/");
|
||||
setPath(path);
|
||||
setPath(config->path.last);
|
||||
}
|
||||
|
||||
void FileBrowser::open(const string &title, const lstring &filterList, function<void (string)> callback) {
|
||||
|
@ -50,10 +50,17 @@ void FileBrowser::open(const string &title, const lstring &filterList, function<
|
|||
|
||||
setTitle(title);
|
||||
setPath(activePath);
|
||||
|
||||
string filterText = "Files of type: ";
|
||||
foreach(filter, filterList) filterText.append(filter, ", ");
|
||||
filterText.trim<1>(", ");
|
||||
filterLabel.setText(filterText);
|
||||
|
||||
setVisible();
|
||||
}
|
||||
|
||||
void FileBrowser::setPath(const string &path) {
|
||||
config->path.last = path;
|
||||
activePath = path;
|
||||
pathEdit.setText(activePath);
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
struct FileBrowser : Window {
|
||||
VerticalLayout layout;
|
||||
HorizontalLayout pathLayout;
|
||||
LineEdit pathEdit;
|
||||
Button pathBrowse;
|
||||
Button pathUp;
|
||||
LineEdit pathEdit;
|
||||
Button pathBrowse;
|
||||
Button pathUp;
|
||||
ListView fileList;
|
||||
lstring fileNameList;
|
||||
HorizontalLayout controlLayout;
|
||||
Label filterLabel;
|
||||
Button openButton;
|
||||
|
||||
void open(const string &title, const lstring &filterList, function<void (string)> callback);
|
||||
|
||||
|
@ -14,6 +16,7 @@ struct FileBrowser : Window {
|
|||
private:
|
||||
string activePath;
|
||||
lstring filterList;
|
||||
lstring fileNameList;
|
||||
function<void (string)> callback;
|
||||
|
||||
void setPath(const string &path);
|
||||
|
|
|
@ -4,6 +4,7 @@ MainWindow::MainWindow() {
|
|||
setTitle(application->title);
|
||||
setGeometry({ 256, 256, 512, 480 });
|
||||
setBackgroundColor({ 0, 0, 0 });
|
||||
windowManager->append(this, "MainWindow");
|
||||
|
||||
cartridgeMenu.setText("Cartridge");
|
||||
cartridgeLoadSNES.setText("Load SNES Cartridge ...");
|
||||
|
@ -13,25 +14,57 @@ MainWindow::MainWindow() {
|
|||
nesMenu.setText("NES");
|
||||
nesPower.setText("Power Cycle");
|
||||
nesReset.setText("Reset");
|
||||
nesPort1.setText("Controller Port 1");
|
||||
nesPort1Device[0].setText("None");
|
||||
nesPort1Device[1].setText("Gamepad");
|
||||
RadioItem::group(nesPort1Device[0], nesPort1Device[1]);
|
||||
nesPort1Device[config->nes.controllerPort1Device].setChecked();
|
||||
nesPort2.setText("Controller Port 2");
|
||||
nesPort2Device[0].setText("None");
|
||||
nesPort2Device[1].setText("Gamepad");
|
||||
RadioItem::group(nesPort2Device[0], nesPort2Device[1]);
|
||||
nesPort2Device[config->nes.controllerPort2Device].setChecked();
|
||||
nesCartridgeUnload.setText("Unload Cartridge");
|
||||
|
||||
snesMenu.setText("SNES");
|
||||
snesPower.setText("Power Cycle");
|
||||
snesReset.setText("Reset");
|
||||
snesCartridgeUnload.setText("Unload Cartridge");
|
||||
snesPort1.setText("Controller Port 1");
|
||||
snesPort1Device[0].setText("None");
|
||||
snesPort1Device[1].setText("Gamepad");
|
||||
snesPort1Device[2].setText("Multitap");
|
||||
snesPort1Device[3].setText("Mouse");
|
||||
RadioItem::group(snesPort1Device[0], snesPort1Device[1], snesPort1Device[2], snesPort1Device[3]);
|
||||
snesPort1Device[config->snes.controllerPort1Device].setChecked();
|
||||
snesPort2.setText("Controller Port 2");
|
||||
snesPort2Device[0].setText("None");
|
||||
snesPort2Device[1].setText("Gamepad");
|
||||
snesPort2Device[2].setText("Multitap");
|
||||
snesPort2Device[3].setText("Mouse");
|
||||
snesPort2Device[4].setText("Super Scope");
|
||||
snesPort2Device[5].setText("Justifier");
|
||||
snesPort2Device[6].setText("Dual Justifiers");
|
||||
snesPort2Device[7].setText("Serial Cable");
|
||||
RadioItem::group(snesPort2Device[0], snesPort2Device[1], snesPort2Device[2], snesPort2Device[3],
|
||||
snesPort2Device[4], snesPort2Device[5], snesPort2Device[6], snesPort2Device[7]);
|
||||
snesPort2Device[config->snes.controllerPort2Device].setChecked();
|
||||
snesCartridgeUnload.setText("Unload Cartridge");
|
||||
|
||||
gameBoyMenu.setText("Game Boy");
|
||||
gameBoyPower.setText("Power Cycle");
|
||||
gameBoyCartridgeUnload.setText("Unload Cartridge");
|
||||
|
||||
settingsMenu.setText("Settings");
|
||||
settingsVideoShaders.setText("Video Shader");
|
||||
setupVideoShaders();
|
||||
settingsSynchronizeVideo.setText("Synchronize Video");
|
||||
settingsSynchronizeVideo.setChecked();
|
||||
settingsSynchronizeVideo.setChecked(config->video.synchronize);
|
||||
settingsSynchronizeAudio.setText("Synchronize Audio");
|
||||
settingsSynchronizeAudio.setChecked();
|
||||
settingsSynchronizeAudio.setChecked(config->audio.synchronize);
|
||||
settingsSmoothVideo.setText("Smooth Video Output");
|
||||
settingsSmoothVideo.setChecked(config->video.smooth);
|
||||
settingsMuteAudio.setText("Mute Audio");
|
||||
settingsMuteAudio.setChecked(config->audio.mute);
|
||||
settingsConfiguration.setText("Configuration ...");
|
||||
|
||||
toolsMenu.setText("Tools");
|
||||
|
@ -47,6 +80,7 @@ MainWindow::MainWindow() {
|
|||
toolsStateLoad3.setText("Slot 3");
|
||||
toolsStateLoad4.setText("Slot 4");
|
||||
toolsStateLoad5.setText("Slot 5");
|
||||
toolsCaptureMouse.setText("Capture Mouse");
|
||||
toolsShrinkWindow.setText("Shrink Window");
|
||||
toolsCheatEditor.setText("Cheat Editor ...");
|
||||
toolsStateManager.setText("State Manager ...");
|
||||
|
@ -63,17 +97,36 @@ MainWindow::MainWindow() {
|
|||
append(nesMenu);
|
||||
nesMenu.append(nesPower);
|
||||
nesMenu.append(nesReset);
|
||||
nesMenu.append(nesSeparator);
|
||||
nesMenu.append(nesSeparator1);
|
||||
nesMenu.append(nesPort1);
|
||||
nesPort1.append(nesPort1Device[0]);
|
||||
nesPort1.append(nesPort1Device[1]);
|
||||
nesMenu.append(nesPort2);
|
||||
nesPort2.append(nesPort2Device[0]);
|
||||
nesPort2.append(nesPort2Device[1]);
|
||||
nesMenu.append(nesSeparator2);
|
||||
nesMenu.append(nesCartridgeUnload);
|
||||
|
||||
append(snesMenu);
|
||||
snesMenu.append(snesPower);
|
||||
snesMenu.append(snesReset);
|
||||
snesMenu.append(snesSeparator1);
|
||||
snesMenu.append(snesCartridgeUnload);
|
||||
snesMenu.append(snesSeparator2);
|
||||
snesMenu.append(snesPort1);
|
||||
snesPort1.append(snesPort1Device[0]);
|
||||
snesPort1.append(snesPort1Device[1]);
|
||||
snesPort1.append(snesPort1Device[2]);
|
||||
snesPort1.append(snesPort1Device[3]);
|
||||
snesMenu.append(snesPort2);
|
||||
snesPort2.append(snesPort2Device[0]);
|
||||
snesPort2.append(snesPort2Device[1]);
|
||||
snesPort2.append(snesPort2Device[2]);
|
||||
snesPort2.append(snesPort2Device[3]);
|
||||
snesPort2.append(snesPort2Device[4]);
|
||||
snesPort2.append(snesPort2Device[5]);
|
||||
snesPort2.append(snesPort2Device[6]);
|
||||
snesPort2.append(snesPort2Device[7]);
|
||||
snesMenu.append(snesSeparator2);
|
||||
snesMenu.append(snesCartridgeUnload);
|
||||
|
||||
append(gameBoyMenu);
|
||||
gameBoyMenu.append(gameBoyPower);
|
||||
|
@ -81,10 +134,15 @@ MainWindow::MainWindow() {
|
|||
gameBoyMenu.append(gameBoyCartridgeUnload);
|
||||
|
||||
append(settingsMenu);
|
||||
settingsMenu.append(settingsVideoShaders);
|
||||
for(unsigned n = 0; n < videoShaderCount; n++)
|
||||
settingsVideoShaders.append(settingsVideoShader[n]);
|
||||
settingsMenu.append(settingsSeparator1);
|
||||
settingsMenu.append(settingsSynchronizeVideo);
|
||||
settingsMenu.append(settingsSynchronizeAudio);
|
||||
settingsMenu.append(settingsSmoothVideo);
|
||||
settingsMenu.append(settingsMuteAudio);
|
||||
settingsMenu.append(settingsSeparator);
|
||||
settingsMenu.append(settingsSeparator2);
|
||||
settingsMenu.append(settingsConfiguration);
|
||||
|
||||
append(toolsMenu);
|
||||
|
@ -100,20 +158,21 @@ MainWindow::MainWindow() {
|
|||
toolsStateLoad.append(toolsStateLoad3);
|
||||
toolsStateLoad.append(toolsStateLoad4);
|
||||
toolsStateLoad.append(toolsStateLoad5);
|
||||
toolsMenu.append(toolsSeparator);
|
||||
toolsMenu.append(toolsSeparator1);
|
||||
toolsMenu.append(toolsCaptureMouse);
|
||||
toolsMenu.append(toolsShrinkWindow);
|
||||
toolsMenu.append(toolsSeparator2);
|
||||
toolsMenu.append(toolsCheatEditor);
|
||||
toolsMenu.append(toolsStateManager);
|
||||
toolsMenu.append(toolsSeparator3);
|
||||
toolsMenu.append(toolsTest);
|
||||
|
||||
append(helpMenu);
|
||||
helpMenu.append(helpAbout);
|
||||
|
||||
setMenuFont(application->normalFont);
|
||||
setMenuVisible();
|
||||
|
||||
setStatusText("No cartridge loaded");
|
||||
setStatusFont(application->boldFont);
|
||||
setStatusVisible();
|
||||
|
||||
layout.append(viewport, { 0, 0, 512, 480 });
|
||||
|
@ -142,25 +201,55 @@ MainWindow::MainWindow() {
|
|||
|
||||
nesPower.onTick = { &Interface::power, interface };
|
||||
nesReset.onTick = { &Interface::reset, interface };
|
||||
|
||||
nesPort1Device[0].onTick = [&] { interface->setController(0, 0); };
|
||||
nesPort1Device[1].onTick = [&] { interface->setController(0, 1); };
|
||||
|
||||
nesPort2Device[0].onTick = [&] { interface->setController(1, 0); };
|
||||
nesPort2Device[1].onTick = [&] { interface->setController(1, 1); };
|
||||
|
||||
nesCartridgeUnload.onTick = { &Interface::unloadCartridge, interface };
|
||||
|
||||
snesPower.onTick = { &Interface::power, interface };
|
||||
snesReset.onTick = { &Interface::reset, interface };
|
||||
|
||||
snesPort1Device[0].onTick = [&] { interface->setController(0, 0); };
|
||||
snesPort1Device[1].onTick = [&] { interface->setController(0, 1); };
|
||||
snesPort1Device[2].onTick = [&] { interface->setController(0, 2); };
|
||||
snesPort1Device[3].onTick = [&] { interface->setController(0, 3); };
|
||||
|
||||
snesPort2Device[0].onTick = [&] { interface->setController(1, 0); };
|
||||
snesPort2Device[1].onTick = [&] { interface->setController(1, 1); };
|
||||
snesPort2Device[2].onTick = [&] { interface->setController(1, 2); };
|
||||
snesPort2Device[3].onTick = [&] { interface->setController(1, 3); };
|
||||
snesPort2Device[4].onTick = [&] { interface->setController(1, 4); };
|
||||
snesPort2Device[5].onTick = [&] { interface->setController(1, 5); };
|
||||
snesPort2Device[6].onTick = [&] { interface->setController(1, 6); };
|
||||
snesPort2Device[7].onTick = [&] { interface->setController(1, 7); };
|
||||
|
||||
snesCartridgeUnload.onTick = { &Interface::unloadCartridge, interface };
|
||||
|
||||
gameBoyPower.onTick = { &Interface::power, interface };
|
||||
gameBoyCartridgeUnload.onTick = { &Interface::unloadCartridge, interface };
|
||||
|
||||
settingsSynchronizeVideo.onTick = [&] {
|
||||
video.set(Video::Synchronize, settingsSynchronizeVideo.checked());
|
||||
config->video.synchronize = settingsSynchronizeVideo.checked();
|
||||
video.set(Video::Synchronize, config->video.synchronize);
|
||||
};
|
||||
|
||||
settingsSynchronizeAudio.onTick = [&] {
|
||||
audio.set(Audio::Synchronize, settingsSynchronizeAudio.checked());
|
||||
config->audio.synchronize = settingsSynchronizeAudio.checked();
|
||||
audio.set(Audio::Synchronize, config->audio.synchronize);
|
||||
};
|
||||
|
||||
settingsSmoothVideo.onTick = [&] {
|
||||
config->video.smooth = settingsSmoothVideo.checked();
|
||||
video.set(Video::Filter, config->video.smooth == false ? 0u : 1u);
|
||||
};
|
||||
|
||||
settingsMuteAudio.onTick = [&] {
|
||||
dspaudio.setVolume(settingsMuteAudio.checked() ? 0.0 : 1.0);
|
||||
config->audio.mute = settingsMuteAudio.checked();
|
||||
dspaudio.setVolume(config->audio.mute == false ? 1.0 : 0.0);
|
||||
};
|
||||
|
||||
settingsConfiguration.onTick = [&] { settingsWindow->setVisible(); };
|
||||
|
@ -177,6 +266,7 @@ MainWindow::MainWindow() {
|
|||
toolsStateLoad4.onTick = [&] { interface->loadState({ interface->baseName, "-4.bst" }); };
|
||||
toolsStateLoad5.onTick = [&] { interface->loadState({ interface->baseName, "-5.bst" }); };
|
||||
|
||||
toolsCaptureMouse.onTick = [&] { input.acquire(); };
|
||||
toolsShrinkWindow.onTick = [&] { utility->resizeMainWindow(true); };
|
||||
|
||||
toolsCheatEditor.onTick = [&] { cheatEditor->setVisible(); };
|
||||
|
@ -206,3 +296,34 @@ void MainWindow::synchronize() {
|
|||
toolsStateLoad.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setupVideoShaders() {
|
||||
lstring files = directory::files({ application->userpath, "shaders/" }, { "*.", config->video.driver, ".shader" });
|
||||
videoShaderCount = 1 + files.size();
|
||||
|
||||
reference_array<RadioItem&> group;
|
||||
unsigned active = 0;
|
||||
|
||||
settingsVideoShader = new RadioItem[videoShaderCount];
|
||||
for(unsigned n = 0; n < videoShaderCount; n++) {
|
||||
string name;
|
||||
if(n == 0) {
|
||||
name = "None";
|
||||
videoShaderName.append("");
|
||||
} else {
|
||||
name = files[n - 1];
|
||||
videoShaderName.append({ application->userpath, "shaders/", name });
|
||||
if(auto position = name.position(string{ ".", config->video.driver, ".shader" })) name[position()] = 0;
|
||||
}
|
||||
if(config->video.shader == videoShaderName[n]) active = n;
|
||||
settingsVideoShader[n].setText(name);
|
||||
settingsVideoShader[n].onTick = [&, n] {
|
||||
config->video.shader = videoShaderName[n];
|
||||
utility->bindVideoShader();
|
||||
};
|
||||
}
|
||||
|
||||
for(unsigned n = 0; n < videoShaderCount; n++) group.append(settingsVideoShader[n]);
|
||||
RadioItem::group(group);
|
||||
settingsVideoShader[active].setChecked();
|
||||
}
|
||||
|
|
|
@ -10,17 +10,24 @@ struct MainWindow : Window {
|
|||
Menu nesMenu;
|
||||
Item nesPower;
|
||||
Item nesReset;
|
||||
Separator nesSeparator;
|
||||
Separator nesSeparator1;
|
||||
Menu nesPort1;
|
||||
RadioItem nesPort1Device[2];
|
||||
Menu nesPort2;
|
||||
RadioItem nesPort2Device[2];
|
||||
Separator nesSeparator2;
|
||||
Item nesCartridgeUnload;
|
||||
|
||||
Menu snesMenu;
|
||||
Item snesPower;
|
||||
Item snesReset;
|
||||
Separator snesSeparator1;
|
||||
Item snesCartridgeUnload;
|
||||
Separator snesSeparator2;
|
||||
Menu snesPort1;
|
||||
RadioItem snesPort1Device[4];
|
||||
Menu snesPort2;
|
||||
RadioItem snesPort2Device[8];
|
||||
Separator snesSeparator2;
|
||||
Item snesCartridgeUnload;
|
||||
|
||||
Menu gameBoyMenu;
|
||||
Item gameBoyPower;
|
||||
|
@ -28,10 +35,14 @@ struct MainWindow : Window {
|
|||
Item gameBoyCartridgeUnload;
|
||||
|
||||
Menu settingsMenu;
|
||||
Menu settingsVideoShaders;
|
||||
RadioItem *settingsVideoShader;
|
||||
Separator settingsSeparator1;
|
||||
CheckItem settingsSynchronizeVideo;
|
||||
CheckItem settingsSynchronizeAudio;
|
||||
CheckItem settingsSmoothVideo;
|
||||
CheckItem settingsMuteAudio;
|
||||
Separator settingsSeparator;
|
||||
Separator settingsSeparator2;
|
||||
Item settingsConfiguration;
|
||||
|
||||
Menu toolsMenu;
|
||||
|
@ -47,10 +58,13 @@ struct MainWindow : Window {
|
|||
Item toolsStateLoad3;
|
||||
Item toolsStateLoad4;
|
||||
Item toolsStateLoad5;
|
||||
Separator toolsSeparator;
|
||||
Separator toolsSeparator1;
|
||||
Item toolsCaptureMouse;
|
||||
Item toolsShrinkWindow;
|
||||
Separator toolsSeparator2;
|
||||
Item toolsCheatEditor;
|
||||
Item toolsStateManager;
|
||||
Separator toolsSeparator3;
|
||||
CheckItem toolsTest;
|
||||
|
||||
Menu helpMenu;
|
||||
|
@ -58,6 +72,12 @@ struct MainWindow : Window {
|
|||
|
||||
void synchronize();
|
||||
MainWindow();
|
||||
|
||||
private:
|
||||
unsigned videoShaderCount;
|
||||
lstring videoShaderName;
|
||||
|
||||
void setupVideoShaders();
|
||||
};
|
||||
|
||||
extern MainWindow *mainWindow;
|
||||
|
|
|
@ -12,6 +12,8 @@ void AbstractInput::attach(const string &primaryName, const string &secondaryNam
|
|||
}
|
||||
|
||||
void AbstractInput::bind() {
|
||||
if(mapping == "") type = Type::Button, mapping = "None";
|
||||
|
||||
if(mapping.endswith(".Up")) type = Type::HatUp;
|
||||
else if(mapping.endswith(".Down")) type = Type::HatDown;
|
||||
else if(mapping.endswith(".Left")) type = Type::HatLeft;
|
||||
|
@ -19,7 +21,8 @@ void AbstractInput::bind() {
|
|||
else if(mapping.endswith(".Lo")) type = Type::AxisLo;
|
||||
else if(mapping.endswith(".Hi")) type = Type::AxisHi;
|
||||
else if(mapping.beginswith("JP") && mapping.position("Axis")) type = Type::Axis;
|
||||
else if(mapping.beginswith("MS") && mapping.endswith("axis")) type = Type::Axis;
|
||||
else if(mapping.beginswith("MS") && mapping.endswith("axis")) type = Type::MouseAxis;
|
||||
else if(mapping.beginswith("MS")) type = Type::MouseButton;
|
||||
else type = Type::Button;
|
||||
|
||||
string decode = mapping;
|
||||
|
@ -28,6 +31,7 @@ void AbstractInput::bind() {
|
|||
}
|
||||
|
||||
int16_t AbstractInput::poll() {
|
||||
if(config->input.focusPolicy == 1 && mainWindow->focused() == false) return 0;
|
||||
return inputManager->scancode[inputManager->activeScancode][scancode];
|
||||
}
|
||||
|
||||
|
@ -35,18 +39,11 @@ int16_t AbstractInput::poll() {
|
|||
|
||||
bool AnalogInput::bind(int16_t scancode, int16_t value) {
|
||||
string encode = Scancode::encode(scancode);
|
||||
Type type = Type::Button;
|
||||
|
||||
if(Mouse::isAnyAxis(scancode)) {
|
||||
for(unsigned n = 0; n < Mouse::Count; n++) {
|
||||
if(scancode == mouse(n)[Mouse::Xaxis]) { encode.append(".Xaxis"); goto bind; }
|
||||
if(scancode == mouse(n)[Mouse::Yaxis]) { encode.append(".Yaxis"); goto bind; }
|
||||
if(scancode == mouse(n)[Mouse::Zaxis]) { encode.append(".Zaxis"); goto bind; }
|
||||
}
|
||||
}
|
||||
|
||||
if(Joypad::isAnyAxis(scancode)) {
|
||||
goto bind;
|
||||
}
|
||||
if(scancode == Scancode::None) goto bind;
|
||||
if(Mouse::isAnyAxis(scancode)) { type = Type::MouseAxis; goto bind; }
|
||||
if(Joypad::isAnyAxis(scancode)) { type = Type::Axis; goto bind; }
|
||||
|
||||
return false;
|
||||
|
||||
|
@ -54,18 +51,36 @@ bind:
|
|||
mapping = encode;
|
||||
this->scancode = scancode;
|
||||
this->type = Type::Axis;
|
||||
return true;
|
||||
}
|
||||
|
||||
int16_t AnalogInput::poll() {
|
||||
int16_t value = AbstractInput::poll();
|
||||
switch(type) {
|
||||
case Type::MouseAxis: return input.acquired() ? value : 0;
|
||||
case Type::Axis: return value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
bool DigitalInput::bind(int16_t scancode, int16_t value) {
|
||||
string encode = Scancode::encode(scancode);
|
||||
Type type;
|
||||
Type type = Type::Button;
|
||||
|
||||
if(scancode == Scancode::None) goto bind;
|
||||
|
||||
if(Keyboard::isAnyKey(scancode) || Keyboard::isAnyModifier(scancode) || Joypad::isAnyButton(scancode)) {
|
||||
if(value == 0) return false;
|
||||
type = Type::Button;
|
||||
goto bind;
|
||||
goto bind;
|
||||
}
|
||||
|
||||
if(Mouse::isAnyButton(scancode)) {
|
||||
if(value == 0) return false;
|
||||
type = Type::MouseButton;
|
||||
goto bind;
|
||||
}
|
||||
|
||||
if(Joypad::isAnyHat(scancode)) {
|
||||
|
@ -92,13 +107,14 @@ bind:
|
|||
int16_t DigitalInput::poll() {
|
||||
int16_t value = AbstractInput::poll();
|
||||
switch(type) {
|
||||
case Type::Button: return (bool)(value);
|
||||
case Type::HatUp: return (bool)(value & Joypad::HatUp);
|
||||
case Type::HatDown: return (bool)(value & Joypad::HatDown);
|
||||
case Type::HatLeft: return (bool)(value & Joypad::HatLeft);
|
||||
case Type::HatRight: return (bool)(value & Joypad::HatRight);
|
||||
case Type::AxisLo: return (bool)(value < -16384);
|
||||
case Type::AxisHi: return (bool)(value > +16384);
|
||||
case Type::Button: return (bool)(value);
|
||||
case Type::MouseButton: return (bool)(value & input.acquired());
|
||||
case Type::HatUp: return (bool)(value & Joypad::HatUp);
|
||||
case Type::HatDown: return (bool)(value & Joypad::HatDown);
|
||||
case Type::HatLeft: return (bool)(value & Joypad::HatLeft);
|
||||
case Type::HatRight: return (bool)(value & Joypad::HatRight);
|
||||
case Type::AxisLo: return (bool)(value < -16384);
|
||||
case Type::AxisHi: return (bool)(value > +16384);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -157,10 +173,14 @@ void InputManager::scan() {
|
|||
|
||||
for(unsigned n = 0; n < Scancode::Limit; n++) {
|
||||
if(scancode[!activeScancode][n] != scancode[activeScancode][n]) {
|
||||
inputSettings->inputEvent(n, scancode[activeScancode][n]);
|
||||
userInterface.inputEvent(n, scancode[activeScancode][n]);
|
||||
if(settingsWindow->focused()) inputSettings->inputEvent(n, scancode[activeScancode][n]);
|
||||
if(mainWindow->focused()) userInterface.inputEvent(n, scancode[activeScancode][n]);
|
||||
}
|
||||
}
|
||||
|
||||
if(scancode[activeScancode][keyboard(0)[Keyboard::Escape]]) {
|
||||
if(mainWindow->fullScreen() == false && input.acquired()) input.unacquire();
|
||||
}
|
||||
}
|
||||
|
||||
InputManager::InputManager() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
struct AbstractInput {
|
||||
enum class Type : unsigned { Button, Axis, HatUp, HatDown, HatLeft, HatRight, AxisLo, AxisHi } type;
|
||||
enum class Type : unsigned { Button, MouseButton, MouseAxis, HatUp, HatDown, HatLeft, HatRight, Axis, AxisLo, AxisHi } type;
|
||||
string name;
|
||||
string mapping;
|
||||
unsigned scancode;
|
||||
|
@ -12,6 +12,7 @@ struct AbstractInput {
|
|||
|
||||
struct AnalogInput : AbstractInput {
|
||||
bool bind(int16_t scancode, int16_t value);
|
||||
int16_t poll();
|
||||
};
|
||||
|
||||
struct DigitalInput : AbstractInput {
|
||||
|
|
|
@ -16,34 +16,27 @@ int16_t SnesGamepad::poll(unsigned n) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
SnesGamepad::SnesGamepad() {
|
||||
name = "Gamepad";
|
||||
SnesGamepad::SnesGamepad(const string &name, bool defaultBindings) {
|
||||
this->name = name;
|
||||
|
||||
up.name = "Up";
|
||||
down.name = "Down";
|
||||
left.name = "Left";
|
||||
right.name = "Right";
|
||||
b.name = "B";
|
||||
a.name = "A";
|
||||
y.name = "Y";
|
||||
x.name = "X";
|
||||
l.name = "L";
|
||||
r.name = "R";
|
||||
select.name = "Select";
|
||||
start.name = "Start";
|
||||
up.name = "Up", down.name = "Down", left.name = "Left", right.name = "Right";
|
||||
b.name = "B", a.name = "A", y.name = "Y", x.name = "X";
|
||||
l.name = "L", r.name = "R", select.name = "Select", start.name = "Start";
|
||||
|
||||
up.mapping = "KB0::Up";
|
||||
down.mapping = "KB0::Down";
|
||||
left.mapping = "KB0::Left";
|
||||
right.mapping = "KB0::Right";
|
||||
b.mapping = "KB0::Z";
|
||||
a.mapping = "KB0::X";
|
||||
y.mapping = "KB0::A";
|
||||
x.mapping = "KB0::S";
|
||||
l.mapping = "KB0::D";
|
||||
r.mapping = "KB0::C";
|
||||
select.mapping = "KB0::Apostrophe";
|
||||
start.mapping = "KB0::Return";
|
||||
if(defaultBindings) {
|
||||
up.mapping = "KB0::Up";
|
||||
down.mapping = "KB0::Down";
|
||||
left.mapping = "KB0::Left";
|
||||
right.mapping = "KB0::Right";
|
||||
b.mapping = "KB0::Z";
|
||||
a.mapping = "KB0::X";
|
||||
y.mapping = "KB0::A";
|
||||
x.mapping = "KB0::S";
|
||||
l.mapping = "KB0::D";
|
||||
r.mapping = "KB0::C";
|
||||
select.mapping = "KB0::Apostrophe";
|
||||
start.mapping = "KB0::Return";
|
||||
}
|
||||
|
||||
append(up); append(down); append(left); append(right);
|
||||
append(b); append(a); append(y); append(x);
|
||||
|
@ -52,16 +45,137 @@ SnesGamepad::SnesGamepad() {
|
|||
|
||||
//
|
||||
|
||||
SnesPort1Input::SnesPort1Input() {
|
||||
name = "Controller Port 1";
|
||||
append(gamepad);
|
||||
int16_t SnesMouse::poll(unsigned n) {
|
||||
switch((SNES::Input::MouseID)n) {
|
||||
case SNES::Input::MouseID::X: return xaxis.poll();
|
||||
case SNES::Input::MouseID::Y: return yaxis.poll();
|
||||
case SNES::Input::MouseID::Left: return left.poll();
|
||||
case SNES::Input::MouseID::Right: return right.poll();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SnesMouse::SnesMouse(const string &name, bool defaultBindings) {
|
||||
this->name = name;
|
||||
|
||||
xaxis.name = "X-axis", yaxis.name = "Y-axis";
|
||||
left.name = "Left button", right.name = "Right button";
|
||||
|
||||
if(defaultBindings) {
|
||||
xaxis.mapping = "MS0::Xaxis";
|
||||
yaxis.mapping = "MS0::Yaxis";
|
||||
left.mapping = "MS0::Button0";
|
||||
right.mapping = "MS0::Button2";
|
||||
}
|
||||
|
||||
append(xaxis); append(yaxis);
|
||||
append(left); append(right);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
SnesPort2Input::SnesPort2Input() {
|
||||
int16_t SnesSuperScope::poll(unsigned n) {
|
||||
switch((SNES::Input::SuperScopeID)n) {
|
||||
case SNES::Input::SuperScopeID::X: return xaxis.poll();
|
||||
case SNES::Input::SuperScopeID::Y: return yaxis.poll();
|
||||
case SNES::Input::SuperScopeID::Trigger: return trigger.poll();
|
||||
case SNES::Input::SuperScopeID::Cursor: return cursor.poll();
|
||||
case SNES::Input::SuperScopeID::Turbo: return turbo.poll();
|
||||
case SNES::Input::SuperScopeID::Pause: return pause.poll();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SnesSuperScope::SnesSuperScope(const string &name, bool defaultBindings) {
|
||||
this->name = name;
|
||||
|
||||
xaxis.name = "X-axis", yaxis.name = "Y-axis";
|
||||
trigger.name = "Trigger", cursor.name = "Cursor", turbo.name = "Turbo", pause.name = "Pause";
|
||||
|
||||
if(defaultBindings) {
|
||||
xaxis.mapping = "MS0::Xaxis";
|
||||
yaxis.mapping = "MS0::Yaxis";
|
||||
trigger.mapping = "MS0::Button0";
|
||||
cursor.mapping = "MS0::Button2";
|
||||
turbo.mapping = "KB0::T";
|
||||
pause.mapping = "KB0::P";
|
||||
}
|
||||
|
||||
append(xaxis); append(yaxis);
|
||||
append(trigger); append(cursor); append(turbo); append(pause);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
int16_t SnesJustifier::poll(unsigned n) {
|
||||
switch((SNES::Input::JustifierID)n) {
|
||||
case SNES::Input::JustifierID::X: return xaxis.poll();
|
||||
case SNES::Input::JustifierID::Y: return yaxis.poll();
|
||||
case SNES::Input::JustifierID::Trigger: return trigger.poll();
|
||||
case SNES::Input::JustifierID::Start: return start.poll();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SnesJustifier::SnesJustifier(const string &name, bool defaultBindings) {
|
||||
this->name = name;
|
||||
|
||||
xaxis.name = "X-axis", yaxis.name = "Y-axis";
|
||||
trigger.name = "Trigger", start.name = "Start";
|
||||
|
||||
if(defaultBindings) {
|
||||
xaxis.mapping = "MS0::Xaxis";
|
||||
yaxis.mapping = "MS0::Yaxis";
|
||||
trigger.mapping = "MS0::Button0";
|
||||
start.mapping = "MS0::Button2";
|
||||
}
|
||||
|
||||
append(xaxis), append(yaxis);
|
||||
append(trigger), append(start);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
SnesPort1Input::SnesPort1Input():
|
||||
gamepad("Gamepad", true),
|
||||
multitap1("Multitap - Port 1", false),
|
||||
multitap2("Multitap - Port 2", false),
|
||||
multitap3("Multitap - Port 3", false),
|
||||
multitap4("Multitap - Port 4", false),
|
||||
mouse("Mouse", true)
|
||||
{
|
||||
name = "Controller Port 1";
|
||||
append(gamepad);
|
||||
append(multitap1);
|
||||
append(multitap2);
|
||||
append(multitap3);
|
||||
append(multitap4);
|
||||
append(mouse);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
SnesPort2Input::SnesPort2Input():
|
||||
gamepad("Gamepad", false),
|
||||
multitap1("Multitap - Port 1", false),
|
||||
multitap2("Multitap - Port 2", false),
|
||||
multitap3("Multitap - Port 3", false),
|
||||
multitap4("Multitap - Port 4", false),
|
||||
mouse("Mouse", true),
|
||||
superScope("Super Scope", true),
|
||||
justifier1("Justifier - Port 1", true),
|
||||
justifier2("Justifier - Port 2", false)
|
||||
{
|
||||
name = "Controller Port 2";
|
||||
append(gamepad);
|
||||
append(multitap1);
|
||||
append(multitap2);
|
||||
append(multitap3);
|
||||
append(multitap4);
|
||||
append(mouse);
|
||||
append(superScope);
|
||||
append(justifier1);
|
||||
append(justifier2);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -4,17 +4,54 @@ struct SnesGamepad : TertiaryInput {
|
|||
DigitalInput l, r, select, start;
|
||||
|
||||
int16_t poll(unsigned n);
|
||||
SnesGamepad();
|
||||
SnesGamepad(const string &name, bool defaultBindings);
|
||||
};
|
||||
|
||||
struct SnesMouse : TertiaryInput {
|
||||
AnalogInput xaxis, yaxis;
|
||||
DigitalInput left, right;
|
||||
|
||||
int16_t poll(unsigned n);
|
||||
SnesMouse(const string &name, bool defaultBindings);
|
||||
};
|
||||
|
||||
struct SnesSuperScope : TertiaryInput {
|
||||
AnalogInput xaxis, yaxis;
|
||||
DigitalInput trigger, cursor, turbo, pause;
|
||||
|
||||
int16_t poll(unsigned n);
|
||||
SnesSuperScope(const string &name, bool defaultBindings);
|
||||
};
|
||||
|
||||
struct SnesJustifier : TertiaryInput {
|
||||
AnalogInput xaxis, yaxis;
|
||||
DigitalInput trigger, start;
|
||||
|
||||
int16_t poll(unsigned n);
|
||||
SnesJustifier(const string &name, bool defaultBindings);
|
||||
};
|
||||
|
||||
struct SnesPort1Input : SecondaryInput {
|
||||
SnesGamepad gamepad;
|
||||
SnesGamepad multitap1;
|
||||
SnesGamepad multitap2;
|
||||
SnesGamepad multitap3;
|
||||
SnesGamepad multitap4;
|
||||
SnesMouse mouse;
|
||||
|
||||
SnesPort1Input();
|
||||
};
|
||||
|
||||
struct SnesPort2Input : SecondaryInput {
|
||||
SnesGamepad gamepad;
|
||||
SnesGamepad multitap1;
|
||||
SnesGamepad multitap2;
|
||||
SnesGamepad multitap3;
|
||||
SnesGamepad multitap4;
|
||||
SnesMouse mouse;
|
||||
SnesSuperScope superScope;
|
||||
SnesJustifier justifier1;
|
||||
SnesJustifier justifier2;
|
||||
|
||||
SnesPort2Input();
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ bool InterfaceGameBoy::loadState(const string &filename) {
|
|||
//
|
||||
|
||||
void InterfaceGameBoy::videoRefresh(const uint8_t *data) {
|
||||
interface->video_refresh();
|
||||
interface->videoRefresh();
|
||||
|
||||
uint32_t *output;
|
||||
unsigned outpitch;
|
||||
|
|
|
@ -4,6 +4,27 @@
|
|||
#include "gameboy.cpp"
|
||||
Interface *interface = 0;
|
||||
|
||||
void Interface::bindControllers() {
|
||||
switch(mode()) {
|
||||
case Mode::NES:
|
||||
nes.setController(0, config->nes.controllerPort1Device);
|
||||
nes.setController(1, config->nes.controllerPort2Device);
|
||||
break;
|
||||
|
||||
case Mode::SNES:
|
||||
snes.setController(0, config->snes.controllerPort1Device);
|
||||
snes.setController(1, config->snes.controllerPort2Device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::setController(unsigned port, unsigned device) {
|
||||
switch(mode()) {
|
||||
case Mode::NES: return nes.setController(port, device);
|
||||
case Mode::SNES: return snes.setController(port, device);
|
||||
}
|
||||
}
|
||||
|
||||
bool Interface::loaded() {
|
||||
switch(mode()) {
|
||||
case Mode::NES: return nes.cartridgeLoaded();
|
||||
|
@ -22,6 +43,7 @@ bool Interface::loadCartridge(const string &filename) {
|
|||
if(filename.endswith(".gb" )) result = loadCartridgeGameBoy(filename);
|
||||
if(filename.endswith(".gbc")) result = loadCartridgeGameBoy(filename);
|
||||
if(result == true) {
|
||||
bindControllers();
|
||||
cheatEditor->load({ baseName, ".cht" });
|
||||
stateManager->load({ baseName, ".bsa" }, 0u);
|
||||
}
|
||||
|
@ -148,10 +170,7 @@ Interface::Interface() {
|
|||
|
||||
//internal
|
||||
|
||||
void Interface::input_poll() {
|
||||
}
|
||||
|
||||
void Interface::video_refresh() {
|
||||
void Interface::videoRefresh() {
|
||||
static unsigned frameCounter = 0;
|
||||
static time_t previous, current;
|
||||
frameCounter++;
|
||||
|
|
|
@ -6,6 +6,9 @@ struct Interface : property<Interface> {
|
|||
enum class Mode : unsigned { None, SNES, NES, GameBoy };
|
||||
readonly<Mode> mode;
|
||||
|
||||
void bindControllers();
|
||||
void setController(unsigned port, unsigned device);
|
||||
|
||||
bool loaded();
|
||||
|
||||
bool loadCartridge(const string &filename);
|
||||
|
@ -31,8 +34,7 @@ struct Interface : property<Interface> {
|
|||
|
||||
Interface();
|
||||
|
||||
void input_poll();
|
||||
void video_refresh();
|
||||
void videoRefresh();
|
||||
|
||||
string baseName; // = "/path/to/cartridge" (no extension)
|
||||
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
void InterfaceNES::setController(bool port, unsigned device) {
|
||||
if(port == 0) config->nes.controllerPort1Device = device;
|
||||
if(port == 1) config->nes.controllerPort2Device = device;
|
||||
|
||||
if(port == 0) switch(device) {
|
||||
case 0: return connect(0, NES::Input::Device::None);
|
||||
case 1: return connect(0, NES::Input::Device::Joypad);
|
||||
}
|
||||
|
||||
if(port == 1) switch(device) {
|
||||
case 0: return connect(1, NES::Input::Device::None);
|
||||
case 1: return connect(1, NES::Input::Device::Joypad);
|
||||
}
|
||||
}
|
||||
|
||||
bool InterfaceNES::loadCartridge(const string &filename) {
|
||||
filemap fp;
|
||||
if(fp.open(filename, filemap::mode::read) == false) return false;
|
||||
|
@ -17,7 +32,7 @@ void InterfaceNES::unloadCartridge() {
|
|||
//
|
||||
|
||||
void InterfaceNES::videoRefresh(const uint16_t *data) {
|
||||
interface->video_refresh();
|
||||
interface->videoRefresh();
|
||||
|
||||
uint32_t *output;
|
||||
unsigned outpitch;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
struct InterfaceNES : NES::Interface {
|
||||
void setController(bool port, unsigned device);
|
||||
|
||||
bool loadCartridge(const string &filename);
|
||||
void unloadCartridge();
|
||||
|
||||
|
|
|
@ -1,3 +1,26 @@
|
|||
void InterfaceSNES::setController(bool port, unsigned device) {
|
||||
if(port == 0) config->snes.controllerPort1Device = device;
|
||||
if(port == 1) config->snes.controllerPort2Device = device;
|
||||
|
||||
if(port == 0) switch(device) {
|
||||
case 0: return connect(0, SNES::Input::Device::None);
|
||||
case 1: return connect(0, SNES::Input::Device::Joypad);
|
||||
case 2: return connect(0, SNES::Input::Device::Multitap);
|
||||
case 3: return connect(0, SNES::Input::Device::Mouse);
|
||||
}
|
||||
|
||||
if(port == 1) switch(device) {
|
||||
case 0: return connect(1, SNES::Input::Device::None);
|
||||
case 1: return connect(1, SNES::Input::Device::Joypad);
|
||||
case 2: return connect(1, SNES::Input::Device::Multitap);
|
||||
case 3: return connect(1, SNES::Input::Device::Mouse);
|
||||
case 4: return connect(1, SNES::Input::Device::SuperScope);
|
||||
case 5: return connect(1, SNES::Input::Device::Justifier);
|
||||
case 6: return connect(1, SNES::Input::Device::Justifiers);
|
||||
case 7: return connect(1, SNES::Input::Device::Serial);
|
||||
}
|
||||
}
|
||||
|
||||
bool InterfaceSNES::loadCartridge(const string &filename) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
|
@ -33,7 +56,7 @@ bool InterfaceSNES::loadState(const string &filename) {
|
|||
//
|
||||
|
||||
void InterfaceSNES::videoRefresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {
|
||||
interface->video_refresh();
|
||||
interface->videoRefresh();
|
||||
|
||||
unsigned width = hires ? 512 : 256;
|
||||
unsigned height = 0 ? 224 : 239;
|
||||
|
@ -78,7 +101,34 @@ void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) {
|
|||
}
|
||||
|
||||
int16_t InterfaceSNES::inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
|
||||
if(port == 0 && device == SNES::Input::Device::Joypad) return inputManager->snes.port1.gamepad.poll(id);
|
||||
if(port == 0) {
|
||||
if(device == SNES::Input::Device::Joypad) return inputManager->snes.port1.gamepad.poll(id);
|
||||
if(device == SNES::Input::Device::Multitap) {
|
||||
if(index == 0) return inputManager->snes.port1.multitap1.poll(id);
|
||||
if(index == 1) return inputManager->snes.port1.multitap1.poll(id);
|
||||
if(index == 2) return inputManager->snes.port1.multitap1.poll(id);
|
||||
if(index == 3) return inputManager->snes.port1.multitap1.poll(id);
|
||||
}
|
||||
if(device == SNES::Input::Device::Mouse) return inputManager->snes.port1.mouse.poll(id);
|
||||
}
|
||||
|
||||
if(port == 1) {
|
||||
if(device == SNES::Input::Device::Joypad) return inputManager->snes.port2.gamepad.poll(id);
|
||||
if(device == SNES::Input::Device::Multitap) {
|
||||
if(index == 0) return inputManager->snes.port2.multitap1.poll(id);
|
||||
if(index == 1) return inputManager->snes.port2.multitap1.poll(id);
|
||||
if(index == 2) return inputManager->snes.port2.multitap1.poll(id);
|
||||
if(index == 3) return inputManager->snes.port2.multitap1.poll(id);
|
||||
}
|
||||
if(device == SNES::Input::Device::Mouse) return inputManager->snes.port2.mouse.poll(id);
|
||||
if(device == SNES::Input::Device::SuperScope) return inputManager->snes.port2.superScope.poll(id);
|
||||
if(device == SNES::Input::Device::Justifier) return inputManager->snes.port2.justifier1.poll(id);
|
||||
if(device == SNES::Input::Device::Justifiers) {
|
||||
if(index == 0) return inputManager->snes.port2.justifier1.poll(id);
|
||||
if(index == 1) return inputManager->snes.port2.justifier2.poll(id);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
struct InterfaceSNES : SNES::Interface {
|
||||
void setController(bool port, unsigned device);
|
||||
|
||||
bool loadCartridge(const string &filename);
|
||||
void unloadCartridge();
|
||||
|
||||
|
|
|
@ -6,7 +6,10 @@ nall::DSP dspaudio;
|
|||
void Application::run() {
|
||||
inputManager->scan();
|
||||
|
||||
if(interface->loaded() == false) {
|
||||
autopause = (mainWindow->focused() == false && config->input.focusPolicy == 2);
|
||||
|
||||
if(interface->loaded() == false || autopause) {
|
||||
audio.clear();
|
||||
usleep(20 * 1000);
|
||||
return;
|
||||
}
|
||||
|
@ -34,48 +37,50 @@ Application::Application(int argc, char **argv) : quit(false) {
|
|||
inputManager = new InputManager;
|
||||
utility = new Utility;
|
||||
|
||||
title = "batch";
|
||||
title = "bsnes";
|
||||
|
||||
#if defined(PLATFORM_WIN)
|
||||
string videoDriver = "Direct3D", audioDriver = "XAudio2", inputDriver = "RawInput";
|
||||
normalFont = "Tahoma, 8";
|
||||
boldFont = "Tahoma, 8, Bold";
|
||||
titleFont = "Tahoma, 16, Bold";
|
||||
#else
|
||||
string videoDriver = "OpenGL", audioDriver = "PulseAudio", inputDriver = "SDL";
|
||||
normalFont = "Sans, 8";
|
||||
boldFont = "Sans, 8, Bold";
|
||||
titleFont = "Sans, 16, Bold";
|
||||
#endif
|
||||
|
||||
windowManager = new WindowManager;
|
||||
mainWindow = new MainWindow;
|
||||
fileBrowser = new FileBrowser;
|
||||
settingsWindow = new SettingsWindow;
|
||||
cheatEditor = new CheatEditor;
|
||||
stateManager = new StateManager;
|
||||
windowManager->loadGeometry();
|
||||
|
||||
utility->setMode(Interface::Mode::None);
|
||||
mainWindow->setVisible();
|
||||
|
||||
video.driver(videoDriver);
|
||||
video.driver(config->video.driver);
|
||||
video.set(Video::Handle, mainWindow->viewport.handle());
|
||||
video.set(Video::Synchronize, true);
|
||||
video.set(Video::Filter, 0u);
|
||||
video.set(Video::Synchronize, config->video.synchronize);
|
||||
video.set(Video::Filter, config->video.smooth == false ? 0u : 1u);
|
||||
video.init();
|
||||
utility->bindVideoShader();
|
||||
|
||||
audio.driver(audioDriver);
|
||||
audio.driver(config->audio.driver);
|
||||
audio.set(Audio::Handle, mainWindow->viewport.handle());
|
||||
audio.set(Audio::Synchronize, true);
|
||||
audio.set(Audio::Synchronize, config->audio.synchronize);
|
||||
audio.set(Audio::Latency, 60u);
|
||||
audio.set(Audio::Frequency, 48000u);
|
||||
audio.init();
|
||||
|
||||
dspaudio.setPrecision(16);
|
||||
dspaudio.setVolume(1.0);
|
||||
dspaudio.setVolume(config->audio.mute == false ? 1.0 : 0.0);
|
||||
dspaudio.setBalance(0.0);
|
||||
dspaudio.setResampler(DSP::Resampler::Average);
|
||||
dspaudio.setResamplerFrequency(48000.0);
|
||||
|
||||
input.driver(inputDriver);
|
||||
input.driver(config->input.driver);
|
||||
input.set(Input::Handle, mainWindow->viewport.handle());
|
||||
input.init();
|
||||
|
||||
|
@ -87,6 +92,7 @@ Application::Application(int argc, char **argv) : quit(false) {
|
|||
}
|
||||
|
||||
interface->unloadCartridge();
|
||||
windowManager->saveGeometry();
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
|
@ -95,6 +101,7 @@ Application::~Application() {
|
|||
delete settingsWindow;
|
||||
delete fileBrowser;
|
||||
delete mainWindow;
|
||||
delete windowManager;
|
||||
delete utility;
|
||||
delete inputManager;
|
||||
delete interface;
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
AdvancedSettings *advancedSettings = 0;
|
||||
|
||||
AdvancedSettings::AdvancedSettings() {
|
||||
title.setFont(application->titleFont);
|
||||
title.setText("Advanced Settings");
|
||||
driverLabel.setFont(application->boldFont);
|
||||
driverLabel.setText("Driver selection: (changes require restart to take effect)");
|
||||
videoLabel.setText("Video:");
|
||||
audioLabel.setText("Audio:");
|
||||
inputLabel.setText("Input:");
|
||||
focusPolicyLabel.setFont(application->boldFont);
|
||||
focusPolicyLabel.setText("When emulation window does not have focus:");
|
||||
focusPolicy[0].setText("Allow input");
|
||||
focusPolicy[1].setText("Ignore input");
|
||||
focusPolicy[2].setText("Pause emulation");
|
||||
RadioBox::group(focusPolicy[0], focusPolicy[1], focusPolicy[2]);
|
||||
focusPolicy[config->input.focusPolicy].setChecked();
|
||||
|
||||
lstring list;
|
||||
|
||||
list.split(";", video.driver_list());
|
||||
for(unsigned n = 0; n < list.size(); n++) {
|
||||
videoDriver.append(list[n]);
|
||||
if(list[n] == config->video.driver) videoDriver.setSelection(n);
|
||||
if(list[n] == video.default_driver() && config->video.driver == "") videoDriver.setSelection(n);
|
||||
}
|
||||
|
||||
list.split(";", audio.driver_list());
|
||||
for(unsigned n = 0; n < list.size(); n++) {
|
||||
audioDriver.append(list[n]);
|
||||
if(list[n] == config->audio.driver) audioDriver.setSelection(n);
|
||||
if(list[n] == audio.default_driver() && config->audio.driver == "") audioDriver.setSelection(n);
|
||||
}
|
||||
|
||||
list.split(";", input.driver_list());
|
||||
for(unsigned n = 0; n < list.size(); n++) {
|
||||
inputDriver.append(list[n]);
|
||||
if(list[n] == config->input.driver) inputDriver.setSelection(n);
|
||||
if(list[n] == input.default_driver() && config->input.driver == "") inputDriver.setSelection(n);
|
||||
}
|
||||
|
||||
append(title, ~0, 0, 5);
|
||||
append(driverLabel, ~0, 0, 5);
|
||||
append(driverLayout, ~0, 0, 5);
|
||||
driverLayout.append(videoLabel, 0, 0, 5);
|
||||
driverLayout.append(videoDriver, ~0, 0, 5);
|
||||
driverLayout.append(audioLabel, 0, 0, 5);
|
||||
driverLayout.append(audioDriver, ~0, 0, 5);
|
||||
driverLayout.append(inputLabel, 0, 0, 5);
|
||||
driverLayout.append(inputDriver, ~0, 0);
|
||||
append(focusPolicyLabel, ~0, 0, 5);
|
||||
append(focusPolicyLayout, ~0, 0, 5);
|
||||
focusPolicyLayout.append(focusPolicy[0], ~0, 0, 5);
|
||||
focusPolicyLayout.append(focusPolicy[1], ~0, 0, 5);
|
||||
focusPolicyLayout.append(focusPolicy[2], ~0, 0);
|
||||
|
||||
videoDriver.onChange = [&] {
|
||||
lstring list;
|
||||
list.split(";", video.driver_list());
|
||||
config->video.driver = list[videoDriver.selection()];
|
||||
};
|
||||
|
||||
audioDriver.onChange = [&] {
|
||||
lstring list;
|
||||
list.split(";", audio.driver_list());
|
||||
config->audio.driver = list[audioDriver.selection()];
|
||||
};
|
||||
|
||||
inputDriver.onChange = [&] {
|
||||
lstring list;
|
||||
list.split(";", input.driver_list());
|
||||
config->input.driver = list[inputDriver.selection()];
|
||||
};
|
||||
|
||||
focusPolicy[0].onTick = [&] { config->input.focusPolicy = 0; };
|
||||
focusPolicy[1].onTick = [&] { config->input.focusPolicy = 1; };
|
||||
focusPolicy[2].onTick = [&] { config->input.focusPolicy = 2; };
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
struct AdvancedSettings : VerticalLayout {
|
||||
Label title;
|
||||
Label driverLabel;
|
||||
HorizontalLayout driverLayout;
|
||||
Label videoLabel;
|
||||
ComboBox videoDriver;
|
||||
Label audioLabel;
|
||||
ComboBox audioDriver;
|
||||
Label inputLabel;
|
||||
ComboBox inputDriver;
|
||||
Label focusPolicyLabel;
|
||||
HorizontalLayout focusPolicyLayout;
|
||||
RadioBox focusPolicy[3];
|
||||
|
||||
AdvancedSettings();
|
||||
};
|
||||
|
||||
extern AdvancedSettings *advancedSettings;
|
|
@ -5,6 +5,9 @@ InputSettings::InputSettings() : activeInput(0) {
|
|||
title.setText("Input Settings");
|
||||
inputList.setHeaderText("Name", "Mapping");
|
||||
inputList.setHeaderVisible();
|
||||
assignPrimary.setVisible(false);
|
||||
assignSecondary.setVisible(false);
|
||||
assignTertiary.setVisible(false);
|
||||
clearButton.setText("Clear");
|
||||
|
||||
for(unsigned n = 0; n < inputManager->inputList.size(); n++) {
|
||||
|
@ -19,6 +22,9 @@ InputSettings::InputSettings() : activeInput(0) {
|
|||
selectionLayout.append(tertiary, ~0, 0);
|
||||
append(inputList, ~0, ~0, 5);
|
||||
append(controlLayout, ~0, 0);
|
||||
controlLayout.append(assignPrimary, 100, 0, 5);
|
||||
controlLayout.append(assignSecondary, 100, 0, 5);
|
||||
controlLayout.append(assignTertiary, 100, 0, 5);
|
||||
controlLayout.append(spacer, ~0, 0);
|
||||
controlLayout.append(clearButton, 80, 0);
|
||||
|
||||
|
@ -26,7 +32,11 @@ InputSettings::InputSettings() : activeInput(0) {
|
|||
secondary.onChange = { &InputSettings::secondaryChange, this };
|
||||
tertiary.onChange = { &InputSettings::tertiaryChange, this };
|
||||
inputList.onChange = { &InputSettings::synchronize, this };
|
||||
inputList.onActivate = { &InputSettings::assignInputBegin, this };
|
||||
inputList.onActivate = { &InputSettings::assignInput, this };
|
||||
assignPrimary.onTick = [&] { assignMouseInput(0); };
|
||||
assignSecondary.onTick = [&] { assignMouseInput(1); };
|
||||
assignTertiary.onTick = [&] { assignMouseInput(2); };
|
||||
clearButton.onTick = { &InputSettings::clearInput, this };
|
||||
|
||||
synchronize();
|
||||
}
|
||||
|
@ -67,22 +77,70 @@ void InputSettings::tertiaryChange() {
|
|||
inputList.autoSizeColumns();
|
||||
}
|
||||
|
||||
void InputSettings::assignInputBegin() {
|
||||
void InputSettings::assignInput() {
|
||||
PrimaryInput &pinput = inputManager->inputList[primary.selection()];
|
||||
SecondaryInput &sinput = pinput[secondary.selection()];
|
||||
TertiaryInput &tinput = sinput[tertiary.selection()];
|
||||
activeInput = &tinput[inputList.selection()];
|
||||
|
||||
settingsWindow->layout.setEnabled(false);
|
||||
settingsWindow->setStatusText({ "Set asssignment for [", tinput.name, "::", activeInput->name, "] ..." });
|
||||
settingsWindow->panelList.setEnabled(false);
|
||||
primary.setEnabled(false);
|
||||
secondary.setEnabled(false);
|
||||
tertiary.setEnabled(false);
|
||||
inputList.setEnabled(false);
|
||||
|
||||
if(dynamic_cast<AnalogInput*>(activeInput)) {
|
||||
assignPrimary.setText("Mouse X-axis");
|
||||
assignSecondary.setText("Mouse Y-axis");
|
||||
assignPrimary.setVisible(true);
|
||||
assignSecondary.setVisible(true);
|
||||
}
|
||||
|
||||
if(dynamic_cast<DigitalInput*>(activeInput)) {
|
||||
assignPrimary.setText("Mouse Left");
|
||||
assignSecondary.setText("Mouse Middle");
|
||||
assignTertiary.setText("Mouse Right");
|
||||
assignPrimary.setVisible(true);
|
||||
assignSecondary.setVisible(true);
|
||||
assignTertiary.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void InputSettings::inputEvent(int16_t scancode, int16_t value) {
|
||||
void InputSettings::assignMouseInput(unsigned n) {
|
||||
if(activeInput == 0) return;
|
||||
|
||||
if(dynamic_cast<AnalogInput*>(activeInput)) {
|
||||
return inputEvent(mouse(0).axis(n), 1u, true);
|
||||
}
|
||||
|
||||
if(dynamic_cast<DigitalInput*>(activeInput)) {
|
||||
return inputEvent(mouse(0).button(n), 1u, true);
|
||||
}
|
||||
}
|
||||
|
||||
void InputSettings::clearInput() {
|
||||
PrimaryInput &pinput = inputManager->inputList[primary.selection()];
|
||||
SecondaryInput &sinput = pinput[secondary.selection()];
|
||||
TertiaryInput &tinput = sinput[tertiary.selection()];
|
||||
activeInput = &tinput[inputList.selection()];
|
||||
inputEvent(Scancode::None, 1u);
|
||||
}
|
||||
|
||||
void InputSettings::inputEvent(int16_t scancode, int16_t value, bool allowMouseInput) {
|
||||
if(activeInput == 0) return;
|
||||
if(allowMouseInput == false && (Mouse::isAnyButton(scancode) || Mouse::isAnyAxis(scancode))) return;
|
||||
if(activeInput->bind(scancode, value) == false) return;
|
||||
|
||||
activeInput = 0;
|
||||
tertiaryChange();
|
||||
settingsWindow->setStatusText("");
|
||||
settingsWindow->layout.setEnabled(true);
|
||||
settingsWindow->panelList.setEnabled(true);
|
||||
primary.setEnabled(true);
|
||||
secondary.setEnabled(true);
|
||||
tertiary.setEnabled(true);
|
||||
inputList.setEnabled(true);
|
||||
assignPrimary.setVisible(false);
|
||||
assignSecondary.setVisible(false);
|
||||
assignTertiary.setVisible(false);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
struct InputSettings : VerticalLayout {
|
||||
Label title;
|
||||
HorizontalLayout selectionLayout;
|
||||
ComboBox primary;
|
||||
ComboBox secondary;
|
||||
ComboBox tertiary;
|
||||
ComboBox primary;
|
||||
ComboBox secondary;
|
||||
ComboBox tertiary;
|
||||
ListView inputList;
|
||||
HorizontalLayout controlLayout;
|
||||
Widget spacer;
|
||||
Button clearButton;
|
||||
Button assignPrimary;
|
||||
Button assignSecondary;
|
||||
Button assignTertiary;
|
||||
Widget spacer;
|
||||
Button clearButton;
|
||||
|
||||
InputSettings();
|
||||
|
||||
|
@ -15,8 +18,10 @@ struct InputSettings : VerticalLayout {
|
|||
void primaryChange();
|
||||
void secondaryChange();
|
||||
void tertiaryChange();
|
||||
void assignInputBegin();
|
||||
void inputEvent(int16_t scancode, int16_t value);
|
||||
void assignInput();
|
||||
void assignMouseInput(unsigned);
|
||||
void clearInput();
|
||||
void inputEvent(int16_t scancode, int16_t value, bool allowMouseInput = false);
|
||||
|
||||
private:
|
||||
AbstractInput *activeInput;
|
||||
|
|
|
@ -1,24 +1,51 @@
|
|||
#include "../base.hpp"
|
||||
#include "input.cpp"
|
||||
#include "advanced.cpp"
|
||||
SettingsWindow *settingsWindow = 0;
|
||||
|
||||
SettingsWindow::SettingsWindow() {
|
||||
setTitle("Configuration Settings");
|
||||
setGeometry({ 128, 128, 640, 360 });
|
||||
setStatusFont(application->boldFont);
|
||||
setStatusVisible();
|
||||
windowManager->append(this, "SettingsWindow");
|
||||
|
||||
panelList.append("Input");
|
||||
panelList.append("Advanced");
|
||||
|
||||
inputSettings = new InputSettings;
|
||||
advancedSettings = new AdvancedSettings;
|
||||
|
||||
append(layout);
|
||||
layout.setMargin(5);
|
||||
layout.append(panelList, 120, ~0, 5);
|
||||
|
||||
layout.append(*inputSettings, ~0, ~0);
|
||||
panelList.onChange = [&] { setPanel(panelList.selection()); };
|
||||
|
||||
setPanel(0);
|
||||
}
|
||||
|
||||
SettingsWindow::~SettingsWindow() {
|
||||
delete advancedSettings;
|
||||
delete inputSettings;
|
||||
}
|
||||
|
||||
void SettingsWindow::setPanel(unsigned n) {
|
||||
//TODO: removing layouts isn't working right, so for now we are hiding them on toggle
|
||||
|
||||
layout.remove(*inputSettings);
|
||||
layout.remove(*advancedSettings);
|
||||
|
||||
inputSettings->setVisible(false);
|
||||
advancedSettings->setVisible(false);
|
||||
|
||||
switch(n) {
|
||||
case 0:
|
||||
layout.append(*inputSettings, ~0, ~0);
|
||||
inputSettings->setVisible();
|
||||
break;
|
||||
case 1:
|
||||
layout.append(*advancedSettings, ~0, ~0);
|
||||
advancedSettings->setVisible();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#include "input.hpp"
|
||||
#include "advanced.hpp"
|
||||
|
||||
struct SettingsWindow : Window {
|
||||
HorizontalLayout layout;
|
||||
ListView panelList;
|
||||
|
||||
void setPanel(unsigned);
|
||||
|
||||
SettingsWindow();
|
||||
~SettingsWindow();
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ CheatEditor *cheatEditor = 0;
|
|||
CheatEditor::CheatEditor() {
|
||||
setTitle("Cheat Editor");
|
||||
setGeometry({ 128, 128, 600, 360 });
|
||||
windowManager->append(this, "CheatEditor");
|
||||
|
||||
cheatList.setHeaderText("Slot", "Code", "Description");
|
||||
cheatList.setHeaderVisible();
|
||||
|
|
|
@ -3,6 +3,7 @@ StateManager *stateManager = 0;
|
|||
StateManager::StateManager() {
|
||||
setTitle("State Manager");
|
||||
setGeometry({ 128, 128, 600, 360 });
|
||||
windowManager->append(this, "StateManager");
|
||||
|
||||
stateList.setHeaderText("Slot", "Description");
|
||||
stateList.setHeaderVisible();
|
||||
|
|
|
@ -95,3 +95,9 @@ void Utility::toggleFullScreen() {
|
|||
fullScreen ^= 1;
|
||||
resizeMainWindow();
|
||||
}
|
||||
|
||||
void Utility::bindVideoShader() {
|
||||
string data;
|
||||
data.readfile(config->video.shader);
|
||||
video.set(Video::Shader, (const char*)data);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ struct Utility {
|
|||
void resizeMainWindow(bool shrink = false);
|
||||
void shrinkMainWindow();
|
||||
void toggleFullScreen();
|
||||
void bindVideoShader();
|
||||
};
|
||||
|
||||
extern Utility *utility;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
#include "../base.hpp"
|
||||
WindowManager *windowManager = 0;
|
||||
|
||||
void WindowManager::append(Window *window, const string &name) {
|
||||
windowList.append({ window, name, geometry(window->geometry()) });
|
||||
|
||||
window->setMenuFont(application->normalFont);
|
||||
window->setWidgetFont(application->normalFont);
|
||||
window->setStatusFont(application->boldFont);
|
||||
}
|
||||
|
||||
string WindowManager::geometry(const Geometry &geometry) {
|
||||
return { geometry.x, ",", geometry.y, ",", geometry.width, ",", geometry.height };
|
||||
}
|
||||
|
||||
Geometry WindowManager::geometry(const string &geometry) {
|
||||
lstring part;
|
||||
part.split(",", geometry);
|
||||
Geometry geom = {
|
||||
(signed)integer(part[0]), (signed)integer(part[1]),
|
||||
(unsigned)decimal(part[2]), (unsigned)decimal(part[3])
|
||||
};
|
||||
geom.x = max(0, min(7680, geom.x));
|
||||
geom.y = max(0, min(4800, geom.y));
|
||||
geom.width = min(7680, geom.width );
|
||||
geom.height = min(4800, geom.height);
|
||||
return geom;
|
||||
}
|
||||
|
||||
void WindowManager::loadGeometry() {
|
||||
foreach(window, windowList) {
|
||||
config.attach(window.geometry, window.name);
|
||||
}
|
||||
config.load(string{ application->userpath, "geometry.cfg" });
|
||||
config.save(string{ application->userpath, "geometry.cfg" });
|
||||
foreach(window, windowList) {
|
||||
window.window->setGeometry(geometry(window.geometry));
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::saveGeometry() {
|
||||
foreach(window, windowList) {
|
||||
window.geometry = geometry(window.window->geometry());
|
||||
}
|
||||
config.save(string{ application->userpath, "geometry.cfg" });
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
struct WindowManager {
|
||||
struct WindowList {
|
||||
Window *window;
|
||||
string name;
|
||||
string geometry;
|
||||
};
|
||||
linear_vector<WindowList> windowList;
|
||||
|
||||
void append(Window *window, const string &name);
|
||||
|
||||
string geometry(const Geometry &geometry);
|
||||
Geometry geometry(const string &geometry);
|
||||
|
||||
void loadGeometry();
|
||||
void saveGeometry();
|
||||
|
||||
private:
|
||||
configuration config;
|
||||
};
|
||||
|
||||
extern WindowManager *windowManager;
|
Loading…
Reference in New Issue