UI: Added global input options (deadzone, display inputs on screen, etc.)

This commit is contained in:
Sour 2022-03-04 22:08:29 -05:00
parent 9f6841eea5
commit aa6a9699fb
44 changed files with 601 additions and 285 deletions

View file

@ -122,6 +122,7 @@
<ClInclude Include="Shared\Interfaces\stdafx.h" />
<ClInclude Include="Shared\KeyDefinitions.h" />
<ClInclude Include="Shared\Movies\stdafx.h" />
<ClInclude Include="Shared\RenderedFrame.h" />
<ClInclude Include="Shared\RomInfo.h" />
<ClInclude Include="Shared\stdafx.h" />
<ClInclude Include="NES\NesDefaultVideoFilter.h" />

View file

@ -819,6 +819,7 @@
<ClInclude Include="NES\Debugger\DummyNesCpu.h" />
<ClInclude Include="Gameboy\Debugger\DummyGbCpu.h" />
<ClInclude Include="Debugger\DebuggerFeatures.h" />
<ClInclude Include="Shared\RenderedFrame.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="SNES\SnesCpu.cpp">

View file

@ -8,6 +8,8 @@
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Shared/RewindManager.h"
#include "Shared/Interfaces/IControlManager.h"
#include "Shared/RenderedFrame.h"
#include "Shared/Video/VideoDecoder.h"
#include "Shared/NotificationManager.h"
#include "Shared/MessageManager.h"
@ -650,7 +652,7 @@ void GbPpu::SendFrame()
}
_isFirstFrame = false;
RenderedFrame frame(_currentBuffer, GbConstants::ScreenWidth, GbConstants::ScreenHeight, 1.0, _state.FrameCount);
RenderedFrame frame(_currentBuffer, GbConstants::ScreenWidth, GbConstants::ScreenHeight, 1.0, _state.FrameCount, _gameboy->GetControlManager()->GetPortStates());
#ifdef LIBRETRO
_emu->GetVideoDecoder()->UpdateFrame(frame, true, false);
#else

View file

@ -3,6 +3,7 @@
#include "Shared/BaseControlDevice.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Shared/InputHud.h"
#include "Utilities/Serializer.h"
class GbController : public BaseControlDevice
@ -57,4 +58,23 @@ public:
void WriteRam(uint16_t addr, uint8_t value) override
{
}
void DrawController(InputHud& hud)
{
hud.DrawOutline(35, 14);
hud.DrawButton(5, 3, 3, 3, IsPressed(Buttons::Up));
hud.DrawButton(5, 9, 3, 3, IsPressed(Buttons::Down));
hud.DrawButton(2, 6, 3, 3, IsPressed(Buttons::Left));
hud.DrawButton(8, 6, 3, 3, IsPressed(Buttons::Right));
hud.DrawButton(5, 6, 3, 3, false);
hud.DrawButton(30, 7, 3, 3, IsPressed(Buttons::A));
hud.DrawButton(25, 7, 3, 3, IsPressed(Buttons::B));
hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select));
hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start));
hud.DrawNumber(_port + 1, 16, 2);
}
};

View file

@ -3,6 +3,7 @@
#include "Shared/BaseControlDevice.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Shared/InputHud.h"
#include "Utilities/Serializer.h"
class NesController : public BaseControlDevice
@ -132,4 +133,23 @@ public:
{
StrobeProcessWrite(value);
}
void DrawController(InputHud& hud)
{
hud.DrawOutline(35, 14);
hud.DrawButton(5, 3, 3, 3, IsPressed(Buttons::Up));
hud.DrawButton(5, 9, 3, 3, IsPressed(Buttons::Down));
hud.DrawButton(2, 6, 3, 3, IsPressed(Buttons::Left));
hud.DrawButton(8, 6, 3, 3, IsPressed(Buttons::Right));
hud.DrawButton(5, 6, 3, 3, false);
hud.DrawButton(30, 7, 3, 3, IsPressed(Buttons::A));
hud.DrawButton(25, 7, 3, 3, IsPressed(Buttons::B));
hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select));
hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start));
hud.DrawNumber(_port + 1, 16, 2);
}
};

View file

@ -20,6 +20,7 @@
#include "Shared/Video/VideoDecoder.h"
#include "Shared/RewindManager.h"
#include "Shared/NotificationManager.h"
#include "Shared/RenderedFrame.h"
#include "MemoryOperationType.h"
#include "EventType.h"
@ -1071,7 +1072,7 @@ template<class T> void NesPpu<T>::SendFrame()
_emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer);
}
RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount);
RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount, _console->GetControlManager()->GetPortStates());
frame.Data = frameData; //HD packs
#ifdef LIBRETRO
@ -1097,7 +1098,7 @@ template<class T> void NesPpu<T>::SendFrameVsDualSystem()
NesConfig& cfg = _settings->GetNesConfig();
bool forRewind = _emu->GetRewindManager()->IsRewinding();
RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount);
RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount, _console->GetControlManager()->GetPortStates());
if(cfg.VsDualVideoOutput == VsDualOutputOption::MainSystemOnly && _console->IsVsMainConsole()) {
_emu->GetVideoDecoder()->UpdateFrame(frame, forRewind, forRewind);
@ -1119,7 +1120,7 @@ template<class T> void NesPpu<T>::SendFrameVsDualSystem()
in2 += NesConstants::ScreenWidth;
}
RenderedFrame mergedFrame(mergedBuffer, NesConstants::ScreenWidth*2, NesConstants::ScreenHeight, 1.0, _frameCount);
RenderedFrame mergedFrame(mergedBuffer, NesConstants::ScreenWidth*2, NesConstants::ScreenHeight, 1.0, _frameCount, _console->GetControlManager()->GetPortStates());
_emu->GetVideoDecoder()->UpdateFrame(mergedFrame, true, forRewind);
delete[] mergedBuffer;
}

View file

@ -3,7 +3,9 @@
#include "SNES/Input/SnesController.h"
#include "SNES/SnesConsole.h"
#include "SNES/InternalRegisters.h"
#include "Shared/InputHud.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
string Multitap::GetKeyNames()
{
@ -149,3 +151,43 @@ void Multitap::WriteRam(uint16_t addr, uint8_t value)
StrobeProcessWrite(value);
}
}
void Multitap::DrawController(InputHud& hud)
{
InputConfig& cfg = _emu->GetSettings()->GetInputConfig();
for(int j = 0; j < 4; j++) {
int port = j == 0 ? _port : (j + 1);
if(cfg.DisplayInputPort[port]) {
DrawController(hud, port);
}
}
}
void Multitap::DrawController(InputHud& hud, int port)
{
int offset = port * Multitap::ButtonCount;
hud.DrawOutline(35, 14);
hud.DrawButton(5, 3, 3, 3, IsPressed(Buttons::Up + offset));
hud.DrawButton(5, 9, 3, 3, IsPressed(Buttons::Down + offset));
hud.DrawButton(2, 6, 3, 3, IsPressed(Buttons::Left + offset));
hud.DrawButton(8, 6, 3, 3, IsPressed(Buttons::Right + offset));
hud.DrawButton(5, 6, 3, 3, false);
hud.DrawButton(27, 3, 3, 3, IsPressed(Buttons::X + offset));
hud.DrawButton(27, 9, 3, 3, IsPressed(Buttons::B + offset));
hud.DrawButton(30, 6, 3, 3, IsPressed(Buttons::A + offset));
hud.DrawButton(24, 6, 3, 3, IsPressed(Buttons::Y + offset));
hud.DrawButton(4, 0, 5, 2, IsPressed(Buttons::L + offset));
hud.DrawButton(26, 0, 5, 2, IsPressed(Buttons::R + offset));
hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select + offset));
hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start + offset));
hud.DrawNumber(port + 1, 16, 2);
hud.EndDrawController();
}

View file

@ -6,6 +6,7 @@
class InternalRegisters;
class SnesController;
class SnesConsole;
class InputHud;
class Multitap : public BaseControlDevice
{
@ -18,6 +19,8 @@ private:
uint16_t _stateBuffer[4] = {};
InternalRegisters *_internalRegs = nullptr;
void DrawController(InputHud& hud, int port);
protected:
string GetKeyNames() override;
void InternalSetStateFromInput() override;
@ -33,4 +36,6 @@ public:
uint8_t ReadRam(uint16_t addr) override;
void WriteRam(uint16_t addr, uint8_t value) override;
void DrawController(InputHud& hud) override;
};

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "SNES/Input/SnesController.h"
#include "Shared/Emulator.h"
#include "Shared/InputHud.h"
SnesController::SnesController(Emulator* emu, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::SnesController, port, keyMappings)
{
@ -96,3 +97,27 @@ void SnesController::WriteRam(uint16_t addr, uint8_t value)
{
StrobeProcessWrite(value);
}
void SnesController::DrawController(InputHud& hud)
{
hud.DrawOutline(35, 14);
hud.DrawButton(5, 3, 3, 3, IsPressed(Buttons::Up));
hud.DrawButton(5, 9, 3, 3, IsPressed(Buttons::Down));
hud.DrawButton(2, 6, 3, 3, IsPressed(Buttons::Left));
hud.DrawButton(8, 6, 3, 3, IsPressed(Buttons::Right));
hud.DrawButton(5, 6, 3, 3, false);
hud.DrawButton(27, 3, 3, 3, IsPressed(Buttons::X));
hud.DrawButton(27, 9, 3, 3, IsPressed(Buttons::B));
hud.DrawButton(30, 6, 3, 3, IsPressed(Buttons::A));
hud.DrawButton(24, 6, 3, 3, IsPressed(Buttons::Y));
hud.DrawButton(4, 0, 5, 2, IsPressed(Buttons::L));
hud.DrawButton(26, 0, 5, 2, IsPressed(Buttons::R));
hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select));
hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start));
hud.DrawNumber(_port + 1, 16, 2);
}

View file

@ -23,4 +23,6 @@ public:
uint8_t ReadRam(uint16_t addr) override;
void WriteRam(uint16_t addr, uint8_t value) override;
void DrawController(InputHud& hud) override;
};

View file

@ -3,6 +3,7 @@
#include "Shared/BaseControlDevice.h"
#include "Shared/Interfaces/IKeyManager.h"
#include "Shared/KeyManager.h"
#include "Shared/InputHud.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Utilities/Serializer.h"
@ -85,4 +86,14 @@ public:
_stateBuffer = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4;
}
void DrawController(InputHud& hud)
{
hud.DrawOutline(11, 14);
hud.DrawButton(1, 1, 4, 5, IsPressed(Buttons::Left));
hud.DrawButton(6, 1, 4, 5, IsPressed(Buttons::Right));
hud.DrawNumber(_port + 1, 4, 7);
}
};

View file

@ -12,6 +12,7 @@
#include "Shared/Video/VideoDecoder.h"
#include "Shared/Video/VideoRenderer.h"
#include "Shared/NotificationManager.h"
#include "Shared/RenderedFrame.h"
#include "Shared/MessageManager.h"
#include "EventType.h"
#include "Shared/RewindManager.h"
@ -1476,7 +1477,7 @@ void SnesPpu::SendFrame()
bool isRewinding = _emu->GetRewindManager()->IsRewinding();
RenderedFrame frame(_currentBuffer, width, height, _useHighResOutput ? 0.5 : 1.0, _frameCount);
RenderedFrame frame(_currentBuffer, width, height, _useHighResOutput ? 0.5 : 1.0, _frameCount, _console->GetControlManager()->GetPortStates());
#ifdef LIBRETRO
_emu->GetVideoDecoder()->UpdateFrame(frame, true, isRewinding);
#else

View file

@ -7,6 +7,7 @@
#include "Utilities/ISerializable.h"
class Emulator;
class InputHud;
class BaseControlDevice : public ISerializable
{
@ -81,6 +82,8 @@ public:
void SetRawState(ControlDeviceState state);
ControlDeviceState GetRawState();
virtual void DrawController(InputHud& hud) {}
virtual uint8_t ReadRam(uint16_t addr) = 0;
virtual void WriteRam(uint16_t addr, uint8_t value) = 0;

View file

@ -66,7 +66,7 @@ vector<ControllerData> BaseControlManager::GetPortStates()
auto lock = _deviceLock.AcquireSafe();
vector<ControllerData> states;
for(int i = 0; i < 2; i++) {
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
shared_ptr<BaseControlDevice> device = GetControlDevice(i);
if(device) {
states.push_back({ device->GetControllerType(), device->GetRawState() });

View file

@ -82,7 +82,7 @@ void EmuSettings::SetInputConfig(InputConfig config)
}*/
}
InputConfig EmuSettings::GetInputConfig()
InputConfig& EmuSettings::GetInputConfig()
{
return _input;
}

View file

@ -53,7 +53,7 @@ public:
AudioConfig GetAudioConfig();
void SetInputConfig(InputConfig config);
InputConfig GetInputConfig();
InputConfig& GetInputConfig();
void SetEmulationConfig(EmulationConfig config);
EmulationConfig GetEmulationConfig();

View file

@ -1,180 +1,181 @@
#include "stdafx.h"
#include "InputHud.h"
#include "Shared/InputHud.h"
#include "Shared/Emulator.h"
#include "Shared/BaseControlManager.h"
#include "Shared/Interfaces/IControlManager.h"
#include "Shared/BaseControlDevice.h"
#include "Shared/EmuSettings.h"
#include "Shared/Video/DebugHud.h"
static constexpr int color[2] = { 0x00111111, 0x00FFFFFF };
InputHud::InputHud(Emulator* emu)
InputHud::InputHud(Emulator* emu, DebugHud* hud)
{
_emu = emu;
_hud = hud;
}
void InputHud::DrawController(int port, ControlDeviceState state, int x, int y, int frameNumber)
void InputHud::DrawButton(int x, int y, int width, int height, bool pressed)
{
//TODO
/*SnesController controller(_console, 0, KeyMappingSet());
controller.SetRawState(state);
shared_ptr<DebugHud> hud = _console->GetDebugHud();
hud->DrawRectangle(0 + x, 0 + y, 35, 14, 0x80CCCCCC, true, 1, frameNumber);
hud->DrawRectangle(0 + x, 0 + y, 35, 14, color[0], false, 1, frameNumber);
hud->DrawRectangle(5 + x, 3 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::Up)], true, 1, frameNumber);
hud->DrawRectangle(5 + x, 9 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::Down)], true, 1, frameNumber);
hud->DrawRectangle(2 + x, 6 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::Left)], true, 1, frameNumber);
hud->DrawRectangle(8 + x, 6 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::Right)], true, 1, frameNumber);
hud->DrawRectangle(5 + x, 6 + y, 3, 3, color[0], true, 1, frameNumber);
hud->DrawRectangle(27 + x, 3 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::X)], true, 1, frameNumber);
hud->DrawRectangle(27 + x, 9 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::B)], true, 1, frameNumber);
hud->DrawRectangle(30 + x, 6 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::A)], true, 1, frameNumber);
hud->DrawRectangle(24 + x, 6 + y, 3, 3, color[controller.IsPressed(SnesController::Buttons::Y)], true, 1, frameNumber);
hud->DrawRectangle(4 + x, 0 + y, 5, 2, color[controller.IsPressed(SnesController::Buttons::L)], true, 1, frameNumber);
hud->DrawRectangle(26 + x, 0 + y, 5, 2, color[controller.IsPressed(SnesController::Buttons::R)], true, 1, frameNumber);
hud->DrawRectangle(13 + x, 9 + y, 4, 2, color[controller.IsPressed(SnesController::Buttons::Select)], true, 1, frameNumber);
hud->DrawRectangle(18 + x, 9 + y, 4, 2, color[controller.IsPressed(SnesController::Buttons::Start)], true, 1, frameNumber);
switch(port) {
case 0:
//1
hud->DrawLine(17 + x, 2 + y, 17 + x, 6 + y, color[0], 1, frameNumber);
hud->DrawLine(16 + x, 6 + y, 18 + x, 6 + y, color[0], 1, frameNumber);
hud->DrawPixel(16 + x, 3 + y, color[0], 1, frameNumber);
break;
_hud->DrawRectangle(_xOffset + x, _yOffset + y, width, height, color[pressed], true, 1);
}
void InputHud::DrawNumber(int number, int x, int y)
{
switch(number) {
case 1:
//2
hud->DrawLine(16 + x, 2 + y, 18 + x, 2 + y, color[0], 1, frameNumber);
hud->DrawPixel(18 + x, 3 + y, color[0], 1, frameNumber);
hud->DrawLine(16 + x, 4 + y, 18 + x, 4 + y, color[0], 1, frameNumber);
hud->DrawPixel(16 + x, 5 + y, color[0], 1, frameNumber);
hud->DrawLine(16 + x, 6 + y, 18 + x, 6 + y, color[0], 1, frameNumber);
_hud->DrawLine(x+1 + _xOffset, y + _yOffset, x+1 + _xOffset, 4 + y + _yOffset, color[0], 1);
_hud->DrawLine(x + _xOffset, 4 + y + _yOffset, x+2 + _xOffset, 4 + y + _yOffset, color[0], 1);
_hud->DrawPixel(x + _xOffset, 1 + y + _yOffset, color[0], 1);
break;
case 2:
//3
hud->DrawLine(16 + x, 2 + y, 18 + x, 2 + y, color[0], 1, frameNumber);
hud->DrawPixel(18 + x, 3 + y, color[0], 1, frameNumber);
hud->DrawLine(16 + x, 4 + y, 18 + x, 4 + y, color[0], 1, frameNumber);
hud->DrawPixel(18 + x, 5 + y, color[0], 1, frameNumber);
hud->DrawLine(16 + x, 6 + y, 18 + x, 6 + y, color[0], 1, frameNumber);
_hud->DrawLine(x + _xOffset, y + _yOffset, x+2 + _xOffset, y + _yOffset, color[0], 1);
_hud->DrawPixel(x+2 + _xOffset, 1 + y + _yOffset, color[0], 1);
_hud->DrawLine(x + _xOffset, 2 + y + _yOffset, x+2 + _xOffset, 2 + y + _yOffset, color[0], 1);
_hud->DrawPixel(x + _xOffset, 3 + y + _yOffset, color[0], 1);
_hud->DrawLine(x + _xOffset, 4 + y + _yOffset, x+2 + _xOffset, 4 + y + _yOffset, color[0], 1);
break;
case 3:
//4
hud->DrawLine(16 + x, 2 + y, 16 + x, 4 + y, color[0], 1, frameNumber);
hud->DrawLine(18 + x, 2 + y, 18 + x, 6 + y, color[0], 1, frameNumber);
hud->DrawLine(16 + x, 4 + y, 18 + x, 4 + y, color[0], 1, frameNumber);
_hud->DrawLine(x + _xOffset, y + _yOffset, x+2 + _xOffset, y + _yOffset, color[0], 1);
_hud->DrawPixel(x+2 + _xOffset, 1 + y + _yOffset, color[0], 1);
_hud->DrawLine(x + _xOffset, 2 + y + _yOffset, x+2 + _xOffset, 2 + y + _yOffset, color[0], 1);
_hud->DrawPixel(x+2 + _xOffset, 3 + y + _yOffset, color[0], 1);
_hud->DrawLine(x + _xOffset, 4 + y + _yOffset, x+2 + _xOffset, 4 + y + _yOffset, color[0], 1);
break;
case 4:
//5
hud->DrawLine(16 + x, 2 + y, 18 + x, 2 + y, color[0], 1, frameNumber);
hud->DrawPixel(16 + x, 3 + y, color[0], 1, frameNumber);
hud->DrawLine(16 + x, 4 + y, 18 + x, 4 + y, color[0], 1, frameNumber);
hud->DrawPixel(18 + x, 5 + y, color[0], 1, frameNumber);
hud->DrawLine(16 + x, 6 + y, 18 + x, 6 + y, color[0], 1, frameNumber);
_hud->DrawLine(x + _xOffset, y + _yOffset, x + _xOffset, 2 + y + _yOffset, color[0], 1);
_hud->DrawLine(x+2 + _xOffset, y + _yOffset, x+2 + _xOffset, 4 + y + _yOffset, color[0], 1);
_hud->DrawLine(x + _xOffset, 2 + y + _yOffset, x+2 + _xOffset, 2 + y + _yOffset, color[0], 1);
break;
}*/
case 5:
_hud->DrawLine(x + _xOffset, y + _yOffset, x+2 + _xOffset, y + _yOffset, color[0], 1);
_hud->DrawPixel(x + _xOffset, 1 + y + _yOffset, color[0], 1);
_hud->DrawLine(x + _xOffset, 2 + y + _yOffset, x+2 + _xOffset, 2 + y + _yOffset, color[0], 1);
_hud->DrawPixel(x+2 + _xOffset, 3 + y + _yOffset, color[0], 1);
_hud->DrawLine(x + _xOffset, 4 + y + _yOffset, x+2 + _xOffset, 4 + y + _yOffset, color[0], 1);
break;
default:
break;
}
}
void InputHud::DrawControllers(OverscanDimensions overscan, int frameNumber)
void InputHud::DrawOutline(int width, int height)
{
//TODO
/*
vector<ControllerData> controllerData = _console->GetControlManager()->GetPortStates();
InputConfig cfg = _console->GetSettings()->GetInputConfig();
int xStart;
int yStart;
int xOffset = cfg.DisplayInputHorizontally ? 38 : 0;
int yOffset = cfg.DisplayInputHorizontally ? 0 : 16;
InputConfig& cfg = _emu->GetSettings()->GetInputConfig();
switch(cfg.DisplayInputPosition) {
default:
case InputDisplayPosition::TopLeft:
xStart = overscan.Left + 3;
yStart = overscan.Top + 3;
break;
case InputDisplayPosition::TopRight:
xStart = 256 - overscan.Right - 38;
yStart = overscan.Top + 3;
xOffset = -xOffset;
_xOffset -= width + 1;
break;
case InputDisplayPosition::BottomLeft:
xStart = overscan.Left + 3;
yStart = 240 - overscan.Bottom - 18;
yOffset = -yOffset;
_yOffset -= height + 1;
break;
case InputDisplayPosition::BottomRight:
xStart = 256 - overscan.Right - 38;
yStart = 240 - overscan.Bottom - 18;
xOffset = -xOffset;
yOffset = -yOffset;
_yOffset -= height + 1;
_xOffset -= width + 1;
break;
}
for(int i = 0; i < (int)controllerData.size(); i++) {
if(controllerData[i].Type == ControllerType::SnesController) {
if(cfg.DisplayInputPort[i]) {
DrawController(i, controllerData[i].State, xStart, yStart, frameNumber);
xStart += xOffset;
yStart += yOffset;
}
} else if(controllerData[i].Type == ControllerType::Multitap) {
uint64_t rawData = 0;
for(int j = (int)controllerData[i].State.State.size() - 1; j >= 0; j--) {
rawData <<= 8;
rawData |= controllerData[i].State.State[j];
}
ControlDeviceState controllers[4] = {};
for(int j = 0; j < 4; j++) {
controllers[j].State.push_back(rawData & 0xFF);
controllers[j].State.push_back((rawData >> 8) & 0x0F);
rawData >>= 12;
}
if(cfg.DisplayInputPort[i]) {
DrawController(i, controllers[0], xStart, yStart, frameNumber);
xStart += xOffset;
yStart += yOffset;
}
for(int j = 1; j < 4; j++) {
if(cfg.DisplayInputPort[j + 1]) {
DrawController(j + 1, controllers[j], xStart, yStart, frameNumber);
xStart += xOffset;
yStart += yOffset;
}
}
} else if(controllerData[i].Type == ControllerType::SuperScope) {
if(cfg.DisplayInputPort[i]) {
SuperScope scope(_console, 0, KeyMappingSet());
scope.SetRawState(controllerData[i].State);
MousePosition pos = scope.GetCoordinates();
shared_ptr<DebugHud> hud = _console->GetDebugHud();
hud->DrawRectangle(pos.X - 1, pos.Y - 1, 3, 3, 0x00111111, true, 1, frameNumber);
hud->DrawRectangle(pos.X - 1, pos.Y - 1, 3, 3, 0x80CCCCCC, false, 1, frameNumber);
}
} else if(controllerData[i].Type == ControllerType::SnesMouse) {
if(cfg.DisplayInputPort[i]) {
SnesMouse mouse(_console, 0);
mouse.SetRawState(controllerData[i].State);
shared_ptr<DebugHud> hud = _console->GetDebugHud();
hud->DrawRectangle(xStart + 12, yStart, 11, 14, 0x00AAAAAA, true, 1, frameNumber);
hud->DrawRectangle(xStart + 12, yStart, 11, 14, color[0], false, 1, frameNumber);
hud->DrawRectangle(xStart + 13, yStart + 1, 4, 5, color[mouse.IsPressed(SnesMouse::Buttons::Left)], true, 1, frameNumber);
hud->DrawRectangle(xStart + 18, yStart + 1, 4, 5, color[mouse.IsPressed(SnesMouse::Buttons::Right)], true, 1, frameNumber);
xStart += xOffset;
yStart += yOffset;
}
}
}*/
_hud->DrawRectangle(_xOffset, _yOffset, width, height, 0x80CCCCCC, true, 1);
_hud->DrawRectangle(_xOffset, _yOffset, width, height, color[0], false, 1);
_outlineWidth = width;
_outlineHeight = height;
}
void InputHud::DrawController(ControllerType controllerType, int port, ControlDeviceState state)
{
shared_ptr<BaseControlDevice> controller = ((BaseControlManager*)_emu->GetControlManager())->CreateControllerDevice(controllerType, port);
if(!controller) {
return;
}
controller->SetRawState(state);
controller->DrawController(*this);
EndDrawController();
}
void InputHud::EndDrawController()
{
if(_outlineHeight > 0 && _outlineWidth > 0) {
InputConfig& cfg = _emu->GetSettings()->GetInputConfig();
switch(cfg.DisplayInputPosition) {
default:
case InputDisplayPosition::TopLeft:
if(cfg.DisplayInputHorizontally) {
_xOffset += _outlineWidth + 1;
} else {
_yOffset += _outlineHeight + 1;
}
break;
case InputDisplayPosition::TopRight:
if(!cfg.DisplayInputHorizontally) {
_xOffset += _outlineWidth + 1;
_yOffset += _outlineHeight + 1;
}
break;
case InputDisplayPosition::BottomLeft:
if(cfg.DisplayInputHorizontally) {
_xOffset += _outlineWidth + 1;
_yOffset += _outlineHeight + 1;
}
break;
case InputDisplayPosition::BottomRight:
if(cfg.DisplayInputHorizontally) {
_yOffset += _outlineHeight + 1;
} else {
_xOffset += _outlineWidth + 1;
}
break;
}
_outlineWidth = 0;
_outlineHeight = 0;
}
}
void InputHud::DrawControllers(FrameInfo size, vector<ControllerData> controllerData)
{
InputConfig& cfg = _emu->GetSettings()->GetInputConfig();
switch(cfg.DisplayInputPosition) {
default:
case InputDisplayPosition::TopLeft:
_xOffset = 2;
_yOffset = 2;
break;
case InputDisplayPosition::TopRight:
_xOffset = size.Width - 1;
_yOffset = 2;
break;
case InputDisplayPosition::BottomLeft:
_xOffset = 2;
_yOffset = size.Height - 1;
break;
case InputDisplayPosition::BottomRight:
_xOffset = size.Width - 1;
_yOffset = size.Height - 1;
break;
}
for(int i = 0; i < (int)controllerData.size(); i++) {
if(controllerData[i].Type != ControllerType::None) {
DrawController(controllerData[i].Type, i, controllerData[i].State);
}
}
}

View file

@ -4,16 +4,28 @@
#include "ControlDeviceState.h"
class Emulator;
class DebugHud;
class InputHud
{
private:
Emulator* _emu;
DebugHud* _hud;
void DrawController(int port, ControlDeviceState state, int x, int y, int frameNumber);
int _xOffset = 0;
int _yOffset = 0;
int _outlineWidth = 0;
int _outlineHeight = 0;
void DrawController(ControllerType type, int port, ControlDeviceState state);
public:
InputHud(Emulator *emu);
InputHud(Emulator *emu, DebugHud* hud);
void DrawControllers(OverscanDimensions overscan, int frameNumber);
void DrawOutline(int width, int height);
void DrawButton(int x, int y, int width, int height, bool pressed);
void DrawNumber(int number, int x, int y);
void EndDrawController();
void DrawControllers(FrameInfo size, vector<ControllerData> controllerData);
};

View file

@ -4,6 +4,7 @@
#include "IInputProvider.h"
enum class ControllerType;
struct ControllerData;
class IControlManager
{
@ -20,6 +21,8 @@ public:
virtual shared_ptr<BaseControlDevice> CreateControllerDevice(ControllerType type, uint8_t port) = 0;
virtual bool HasControlDevice(ControllerType type) = 0;
virtual vector<ControllerData> GetPortStates() = 0;
virtual void SetPollCounter(uint32_t pollCounter) = 0;
virtual uint32_t GetPollCounter() = 0;

View file

@ -3,11 +3,13 @@
#include "stdafx.h"
#include "Shared/SettingTypes.h"
struct RenderedFrame;
class IRenderingDevice
{
public:
virtual ~IRenderingDevice() {}
virtual void UpdateFrame(RenderedFrame frame) = 0;
virtual void UpdateFrame(RenderedFrame& frame) = 0;
virtual void Render(uint32_t* hudBuffer, uint32_t width, uint32_t height) = 0;
virtual void Reset() = 0;
virtual void SetFullscreenMode(bool fullscreen, void* windowHandle, uint32_t monitorWidth, uint32_t monitorHeight) = 0;

View file

@ -0,0 +1,38 @@
#pragma once
#include "stdafx.h"
#include "Shared/SettingTypes.h"
#include "Shared/ControlDeviceState.h"
struct RenderedFrame
{
void* FrameBuffer = nullptr;
void* Data = nullptr; //Used by HD packs
uint32_t Width = 256;
uint32_t Height = 240;
double Scale = 1.0;
uint32_t FrameNumber = 0;
vector<ControllerData> InputData;
RenderedFrame()
{}
RenderedFrame(void* buffer, uint32_t width, uint32_t height, double scale = 1.0, uint32_t frameNumber = 0) :
FrameBuffer(buffer),
Width(width),
Height(height),
Scale(scale),
FrameNumber(frameNumber),
Data(nullptr),
InputData({})
{}
RenderedFrame(void* buffer, uint32_t width, uint32_t height, double scale, uint32_t frameNumber, vector<ControllerData> inputData) :
FrameBuffer(buffer),
Width(width),
Height(height),
Scale(scale),
FrameNumber(frameNumber),
Data(nullptr),
InputData(inputData)
{}
};

View file

@ -6,6 +6,7 @@
#include "Shared/Video/VideoRenderer.h"
#include "Shared/Audio/SoundMixer.h"
#include "Shared/BaseControlDevice.h"
#include "Shared/RenderedFrame.h"
#include "Shared/Interfaces/IControlManager.h"
RewindManager::RewindManager(Emulator* emu)
@ -220,7 +221,7 @@ void RewindManager::ProcessEndOfFrame()
}
}
void RewindManager::ProcessFrame(RenderedFrame frame, bool forRewind)
void RewindManager::ProcessFrame(RenderedFrame& frame, bool forRewind)
{
if(_rewindState == RewindState::Starting || _rewindState == RewindState::Started) {
if(!forRewind) {
@ -354,7 +355,7 @@ bool RewindManager::HasHistory()
return _hasHistory;
}
void RewindManager::SendFrame(RenderedFrame frame, bool forRewind)
void RewindManager::SendFrame(RenderedFrame& frame, bool forRewind)
{
ProcessFrame(frame, forRewind);
}

View file

@ -8,6 +8,7 @@
class Emulator;
class EmuSettings;
struct RenderedFrame;
enum class RewindState
{
@ -55,7 +56,7 @@ private:
void Stop();
void ForceStop();
void ProcessFrame(RenderedFrame frame, bool forRewind);
void ProcessFrame(RenderedFrame& frame, bool forRewind);
bool ProcessAudio(int16_t* soundBuffer, uint32_t sampleCount);
void ClearBuffer();
@ -78,6 +79,6 @@ public:
bool HasHistory();
void SendFrame(RenderedFrame frame, bool forRewind);
void SendFrame(RenderedFrame& frame, bool forRewind);
bool SendAudio(int16_t *soundBuffer, uint32_t sampleCount);
};

View file

@ -8,6 +8,7 @@
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Shared/Movies/MovieManager.h"
#include "Shared/RenderedFrame.h"
#include "EventType.h"
#include "Debugger/Debugger.h"
#include "Netplay/GameClient.h"

View file

@ -505,30 +505,6 @@ struct OverscanDimensions
uint32_t Bottom = 0;
};
struct RenderedFrame
{
void* FrameBuffer = nullptr;
void* Data = nullptr; //Used by HD packs
uint32_t Width = 256;
uint32_t Height = 240;
double Scale = 1.0;
uint32_t FrameNumber = 0;
RenderedFrame()
{
}
RenderedFrame(void* buffer, uint32_t width, uint32_t height, double scale = 1.0, uint32_t frameNumber = 0) :
FrameBuffer(buffer),
Width(width),
Height(height),
Scale(scale),
FrameNumber(frameNumber),
Data(nullptr)
{
}
};
struct FrameInfo
{
uint32_t Width;

View file

@ -17,6 +17,22 @@ protected:
int _yScale = 1;
virtual void InternalDraw() = 0;
void InternalDrawPixel(int32_t offset, int color, uint32_t alpha)
{
if(alpha != 0xFF000000) {
if(_argbBuffer[offset] == 0) {
//When drawing on an empty background, premultiply channels & preserve alpha value
//This is needed for hardware blending between the HUD and the game screen
BlendColors((uint8_t*)&_argbBuffer[offset], (uint8_t*)&color, true);
} else {
BlendColors((uint8_t*)&_argbBuffer[offset], (uint8_t*)&color);
}
} else {
_argbBuffer[offset] = color;
}
}
void DrawPixel(uint32_t x, uint32_t y, int color)
{
uint32_t alpha = (color & 0xFF000000);
@ -30,11 +46,7 @@ protected:
return;
}
if(alpha != 0xFF000000) {
BlendColors((uint8_t*)&_argbBuffer[offset], (uint8_t*)&color);
} else {
_argbBuffer[offset] = color;
}
InternalDrawPixel(offset, color, alpha);
} else {
int xPixelCount = _useIntegerScaling ? (int)std::floor(_xScale): (int)((x + 1)*_xScale) - (int)(x*_xScale);
x = (int)(x * (_useIntegerScaling ? (int)std::floor(_xScale) : _xScale));
@ -48,25 +60,25 @@ protected:
continue;
}
if(alpha != 0xFF000000) {
BlendColors((uint8_t*)&_argbBuffer[offset], (uint8_t*)&color);
} else {
_argbBuffer[offset] = color;
}
InternalDrawPixel(offset, color, alpha);
}
}
}
}
}
__forceinline void BlendColors(uint8_t output[4], uint8_t input[4])
__forceinline void BlendColors(uint8_t output[4], uint8_t input[4], bool keepAlpha = false)
{
uint8_t alpha = input[3] + 1;
uint8_t invertedAlpha = 256 - input[3];
output[0] = (uint8_t)((alpha * input[0] + invertedAlpha * output[0]) >> 8);
output[1] = (uint8_t)((alpha * input[1] + invertedAlpha * output[1]) >> 8);
output[2] = (uint8_t)((alpha * input[2] + invertedAlpha * output[2]) >> 8);
output[3] = 0xFF;
if(keepAlpha) {
output[3] = input[3];
} else {
output[3] = 0xFF;
}
}
public:

View file

@ -11,6 +11,7 @@
#include "Shared/Video/ScaleFilter.h"
#include "Shared/Video/DebugHud.h"
#include "Shared/InputHud.h"
#include "Shared/RenderedFrame.h"
#include "Shared/Video/SystemHud.h"
#include "SNES/CartTypes.h"
@ -97,7 +98,7 @@ void VideoDecoder::DecodeFrame(bool forRewind)
overscan.Bottom *= _scaleFilter->GetScale();
}
RenderedFrame convertedFrame((void*)outputBuffer, frameSize.Width, frameSize.Height, _frame.Scale, _frame.FrameNumber);
RenderedFrame convertedFrame((void*)outputBuffer, frameSize.Width, frameSize.Height, _frame.Scale, _frame.FrameNumber, _frame.InputData);
_emu->GetDebugHud()->Draw(outputBuffer, frameSize, overscan, _frame.FrameNumber, true);

View file

@ -3,6 +3,7 @@
#include "Utilities/SimpleLock.h"
#include "Utilities/AutoResetEvent.h"
#include "Shared/SettingTypes.h"
#include "Shared/RenderedFrame.h"
class BaseVideoFilter;
class ScaleFilter;

View file

@ -19,6 +19,7 @@ VideoRenderer::VideoRenderer(Emulator* emu)
_rendererHud.reset(new DebugHud());
_systemHud.reset(new SystemHud(_emu, _rendererHud.get()));
_inputHud.reset(new InputHud(emu, _rendererHud.get()));
_hudSurface = new uint32_t[256*240];
_hudSize.Width = 256;
@ -89,7 +90,14 @@ void VideoRenderer::RenderThread()
_hudSize = size;
}
RenderedFrame frame;
{
_frameLock.AcquireSafe();
frame = _lastFrame;
}
memset(_hudSurface, 0, _hudSize.Width * _hudSize.Height * sizeof(uint32_t));
_inputHud->DrawControllers(_hudSize, frame.InputData);
_systemHud->Draw(_hudSize.Width, _hudSize.Height);
_rendererHud->Draw(_hudSurface, _hudSize, {}, 0, false);
_renderer->Render(_hudSurface, _hudSize.Width, _hudSize.Height);
@ -97,13 +105,18 @@ void VideoRenderer::RenderThread()
}
}
void VideoRenderer::UpdateFrame(RenderedFrame frame)
void VideoRenderer::UpdateFrame(RenderedFrame& frame)
{
shared_ptr<IVideoRecorder> recorder = _recorder;
if(recorder) {
recorder->AddFrame(frame.FrameBuffer, frame.Width, frame.Height, _emu->GetFps());
}
{
_frameLock.AcquireSafe();
_lastFrame = frame;
}
if(_renderer) {
_renderer->UpdateFrame(frame);
_waitForRender.Signal();

View file

@ -2,6 +2,7 @@
#include "stdafx.h"
#include <thread>
#include "Shared/SettingTypes.h"
#include "Shared/RenderedFrame.h"
#include "Utilities/AutoResetEvent.h"
#include "Utilities/SimpleLock.h"
@ -9,6 +10,7 @@ class IRenderingDevice;
class Emulator;
class SystemHud;
class DebugHud;
class InputHud;
class IVideoRecorder;
enum class VideoCodec;
@ -29,9 +31,13 @@ private:
unique_ptr<DebugHud> _rendererHud;
unique_ptr<SystemHud> _systemHud;
unique_ptr<InputHud> _inputHud;
uint32_t* _hudSurface = nullptr;
FrameInfo _hudSize = {};
RenderedFrame _lastFrame;
SimpleLock _frameLock;
shared_ptr<IVideoRecorder> _recorder;
void RenderThread();
@ -46,7 +52,7 @@ public:
void StartThread();
void StopThread();
void UpdateFrame(RenderedFrame frame);
void UpdateFrame(RenderedFrame& frame);
void RegisterRenderingDevice(IRenderingDevice *renderer);
void UnregisterRenderingDevice(IRenderingDevice *renderer);

View file

@ -527,6 +527,8 @@ namespace Mesen.Debugger.Utilities
Audio,
[IconFile("DipSwitches")]
Emulation,
[IconFile("Controller")]
Input,
[IconFile("VideoOptions")]
Video,
[IconFile("NesIcon")]

View file

@ -98,24 +98,29 @@
<Control ID="btnSetupMultitap2">Setup</Control>
<Control ID="btnSetupMultitap3">Setup</Control>
<Control ID="btnSetupMultitap4">Setup</Control>
<Control ID="lblKeyBinding">Warning: Your current configuration contains conflicting key bindings - some physical buttons on your keyboard or gamepad are mapped to multiple buttons on the NES controller. If this is not intentional, please review and correct your key bindings.</Control>
</Form>
<Form ID="InputConfigView">
<Control ID="tpgGeneral">General</Control>
<Control ID="lblSmall">Small</Control>
<Control ID="lblLarge">Large</Control>
<Control ID="lblDeadzone">Controller axis deadzone size:</Control>
<Control ID="tpgAdvanced">Advanced</Control>
<Control ID="grpDisplayInput">Display Controller Input</Control>
<Control ID="lblSensitivity">Mouse speed:</Control>
<Control ID="lblLow">Slow</Control>
<Control ID="lblHigh">Fast</Control>
<Control ID="tpgDisplay">Display</Control>
<Control ID="lblDisplayInputPorts">Display ports: </Control>
<Control ID="chkDisplayPort1">Port 1</Control>
<Control ID="chkDisplayPort2">Port 2</Control>
<Control ID="chkDisplayPort3">Port 3</Control>
<Control ID="chkDisplayPort4">Port 4</Control>
<Control ID="chkDisplayPort5">Port 5</Control>
<Control ID="lblDisplayPosition">Display Position:</Control>
<Control ID="chkDisplayInputHorizontally">Display horizontally</Control>
<Control ID="lblSmall">Small</Control>
<Control ID="lblLarge">Large</Control>
<Control ID="lblDeadzone">Controller axis deadzone size:</Control>
<Control ID="lblDisplayPosition">Display position:</Control>
<Control ID="chkDisplayInputHorizontally">Horizontal display</Control>
<Control ID="chkHideMousePointerForZapper">Hide mouse pointer when using zapper</Control>
<Control ID="lblKeyBinding">Warning: Your current configuration contains conflicting key bindings - some physical buttons on your keyboard or gamepad are mapped to multiple buttons on the NES controller. If this is not intentional, please review and correct your key bindings.</Control>
</Form>
<Form ID="OverscanConfig">
<Control ID="lblLeft">Left</Control>
@ -359,6 +364,7 @@
<Control ID="tabAudio">Audio</Control>
<Control ID="tabEmulation">Emulation</Control>
<Control ID="tabInput">Input</Control>
<Control ID="tabVideo">Video</Control>
<Control ID="tabNes">NES</Control>
<Control ID="tabSnes">SNES</Control>
@ -2160,6 +2166,7 @@ x == [$150] || y == [10]
<Value ID="Audio">Audio</Value>
<Value ID="Emulation">Emulation</Value>
<Value ID="Input">Input</Value>
<Value ID="Video">Video</Value>
<Value ID="Nes">NES</Value>
<Value ID="Snes">SNES</Value>

View file

@ -323,6 +323,9 @@
<Compile Update="Debugger\Windows\MemoryToolsWindow.axaml.cs">
<DependentUpon>MemoryToolsWindow.axaml</DependentUpon>
</Compile>
<Compile Update="Views\InputConfigView.axaml.cs">
<DependentUpon>InputConfigView.axaml</DependentUpon>
</Compile>
<Compile Update="Views\MainMenuView.axaml.cs">
<DependentUpon>MainMenuView.axaml</DependentUpon>
</Compile>

View file

@ -1,6 +1,7 @@
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
@ -12,8 +13,15 @@ namespace Mesen.Utilities
{
foreach(PropertyInfo prop in target.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) {
if(prop.GetCustomAttribute<ReactiveAttribute>() != null) {
if(prop.GetValue(target) is ReactiveObject propValue) {
object? value = prop.GetValue(target);
if(value is ReactiveObject propValue) {
ReactiveHelper.RegisterRecursiveObserver(propValue, handler);
} else if(value is IList list) {
foreach(object listValue in list) {
if(listValue is ReactiveObject) {
ReactiveHelper.RegisterRecursiveObserver((ReactiveObject)listValue, handler);
}
}
}
}
}
@ -27,8 +35,15 @@ namespace Mesen.Utilities
{
foreach(PropertyInfo prop in target.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) {
if(prop.GetCustomAttribute<ReactiveAttribute>() != null) {
if(prop.GetValue(target) is ReactiveObject propValue) {
object? value = prop.GetValue(target);
if(value is ReactiveObject propValue) {
ReactiveHelper.UnregisterRecursiveObserver(propValue, handler);
} else if(value is IList list) {
foreach(object listValue in list) {
if(listValue is ReactiveObject) {
ReactiveHelper.UnregisterRecursiveObserver((ReactiveObject)listValue, handler);
}
}
}
}
}

View file

@ -10,6 +10,7 @@ namespace Mesen.ViewModels
public class ConfigViewModel : DisposableViewModel
{
[Reactive] public AudioConfigViewModel? Audio { get; set; }
[Reactive] public InputConfigViewModel? Input { get; set; }
[Reactive] public VideoConfigViewModel? Video { get; set; }
[Reactive] public PreferencesConfigViewModel? Preferences { get; set; }
[Reactive] public EmulationConfigViewModel? Emulation { get; set; }
@ -38,6 +39,7 @@ namespace Mesen.ViewModels
switch(tab) {
case ConfigWindowTab.Audio: Audio ??= AddDisposable(new AudioConfigViewModel()); break;
case ConfigWindowTab.Emulation: Emulation ??= AddDisposable(new EmulationConfigViewModel()); break;
case ConfigWindowTab.Input: Input ??= AddDisposable(new InputConfigViewModel()); break;
case ConfigWindowTab.Video: Video ??= AddDisposable(new VideoConfigViewModel()); break;
case ConfigWindowTab.Nes:
@ -63,6 +65,7 @@ namespace Mesen.ViewModels
}
ConfigManager.Config.Audio = Audio?.Config.Clone() ?? ConfigManager.Config.Audio;
ConfigManager.Config.Input = Input?.Config.Clone() ?? ConfigManager.Config.Input;
ConfigManager.Config.Video = Video?.Config.Clone() ?? ConfigManager.Config.Video;
ConfigManager.Config.Preferences = Preferences?.Config.Clone() ?? ConfigManager.Config.Preferences;
ConfigManager.Config.Emulation = Emulation?.Config.Clone() ?? ConfigManager.Config.Emulation;
@ -80,12 +83,13 @@ namespace Mesen.ViewModels
{
Audio = 0,
Emulation = 1,
Video = 2,
Nes = 4,
Snes = 5,
Gameboy = 6,
Preferences = 8
Input = 2,
Video = 3,
//separator
Nes = 5,
Snes = 6,
Gameboy = 7,
//separator
Preferences = 9
}
}

View file

@ -8,7 +8,7 @@ namespace Mesen.ViewModels
public class EmulationConfigViewModel : DisposableViewModel
{
[Reactive] public EmulationConfig Config { get; set; }
public EmulationConfigViewModel()
{
Config = ConfigManager.Config.Emulation.Clone();
@ -19,5 +19,5 @@ namespace Mesen.ViewModels
AddDisposable(ReactiveHelper.RegisterRecursiveObserver(Config, (s, e) => { Config.ApplyConfig(); }));
}
}
}
}

View file

@ -0,0 +1,23 @@
using Avalonia.Controls;
using Mesen.Config;
using Mesen.Utilities;
using ReactiveUI.Fody.Helpers;
namespace Mesen.ViewModels
{
public class InputConfigViewModel : DisposableViewModel
{
[Reactive] public InputConfig Config { get; set; }
public InputConfigViewModel()
{
Config = ConfigManager.Config.Input.Clone();
if(Design.IsDesignMode) {
return;
}
AddDisposable(ReactiveHelper.RegisterRecursiveObserver(Config, (s, e) => { Config.ApplyConfig(); }));
}
}
}

View file

@ -374,6 +374,10 @@ namespace Mesen.ViewModels
ActionType = ActionType.Emulation,
OnClick = () => OpenConfig(wnd, ConfigWindowTab.Emulation)
},
new MainMenuAction() {
ActionType = ActionType.Input,
OnClick = () => OpenConfig(wnd, ConfigWindowTab.Input)
},
new MainMenuAction() {
ActionType = ActionType.Video,
OnClick = () => OpenConfig(wnd, ConfigWindowTab.Video)

View file

@ -22,15 +22,15 @@
<StackPanel>
<Grid ColumnDefinitions="Auto,Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<TextBlock Grid.Column="0" Grid.Row="0" Text="{l:Translate lblEmulationSpeed}" />
<NumericUpDown Grid.Column="1" Grid.Row="0" Value="{CompiledBinding Config.EmulationSpeed}" Maximum="5000" Minimum="0" Width="80" />
<NumericUpDown Grid.Column="1" Grid.Row="0" Value="{CompiledBinding Config.EmulationSpeed}" Maximum="5000" Minimum="0" />
<TextBlock Grid.Column="2" Grid.Row="0" Text="{l:Translate lblEmuSpeedHint}" />
<TextBlock Grid.Column="0" Grid.Row="1" Text="{l:Translate lblTurboSpeed}" />
<NumericUpDown Grid.Column="1" Grid.Row="1" Value="{CompiledBinding Config.TurboSpeed}" Maximum="5000" Minimum="0" Width="80" />
<NumericUpDown Grid.Column="1" Grid.Row="1" Value="{CompiledBinding Config.TurboSpeed}" Maximum="5000" Minimum="0" />
<TextBlock Grid.Column="2" Grid.Row="1" Text="{l:Translate lblTurboSpeedHint}" />
<TextBlock Grid.Column="0" Grid.Row="2" Text="{l:Translate lblRewindSpeed}" />
<NumericUpDown Grid.Column="1" Grid.Row="2" Value="{CompiledBinding Config.RewindSpeed}" Maximum="5000" Minimum="0" Width="80" />
<NumericUpDown Grid.Column="1" Grid.Row="2" Value="{CompiledBinding Config.RewindSpeed}" Maximum="5000" Minimum="0" />
<TextBlock Grid.Column="2" Grid.Row="2" Text="{l:Translate lblRewindSpeedHint}" />
<TextBlock Grid.Column="0" Grid.Row="4" Text="{l:Translate lblRunAhead}" />

View file

@ -0,0 +1,89 @@
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:Mesen.ViewModels"
xmlns:c="using:Mesen.Controls"
xmlns:cfg="using:Mesen.Config"
xmlns:l="using:Mesen.Localization"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
x:DataType="vm:InputConfigViewModel"
x:Class="Mesen.Views.InputConfigView"
>
<Design.DataContext>
<vm:InputConfigViewModel />
</Design.DataContext>
<TabControl TabStripPlacement="Top" SelectedIndex="0">
<TabItem Header="{l:Translate tpgGeneral}">
<StackPanel>
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<TextBlock Grid.Row="0" Text="{l:Translate lblDeadzone}" VerticalAlignment="Top" Margin="0 15 5 0" />
<c:MesenSlider
Margin="12 15 10 0"
HorizontalAlignment="Left"
Grid.Row="0"
Grid.Column="1"
Minimum="0"
Maximum="4"
TickFrequency="1"
Width="100"
HideValue="True"
Value="{CompiledBinding Config.ControllerDeadzoneSize}"
/>
<Grid Grid.Row="1" Grid.Column="1" RowDefinitions="Auto" ColumnDefinitions="Auto,*,Auto" Width="115" HorizontalAlignment="Left" Margin="5 -5 5 0">
<TextBlock Text="{l:Translate lblSmall}" />
<TextBlock Text="{l:Translate lblLarge}" Grid.Column="2" />
</Grid>
<TextBlock Grid.Row="2" Text="{l:Translate lblSensitivity}" VerticalAlignment="Top" Margin="0 15 5 0" />
<c:MesenSlider
Margin="12 15 10 0"
HorizontalAlignment="Left"
Grid.Row="2"
Grid.Column="1"
Minimum="0"
Maximum="3"
TickFrequency="1"
Width="100"
HideValue="True"
Value="{CompiledBinding Config.MouseSensitivity}"
/>
<Grid Grid.Row="3" Grid.Column="1" RowDefinitions="Auto" ColumnDefinitions="Auto,*,Auto" Width="107" HorizontalAlignment="Left" Margin="10 -5 5 0">
<TextBlock Text="{l:Translate lblLow}" />
<TextBlock Text="{l:Translate lblHigh}" Grid.Column="2" />
</Grid>
</Grid>
</StackPanel>
</TabItem>
<TabItem Header="{l:Translate tpgDisplay}">
<Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto">
<TextBlock Text="{l:Translate lblDisplayInputPorts}" VerticalAlignment="Top" Margin="0 5 0 0" />
<Grid Grid.Column="1" ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto">
<Grid.Styles>
<Style Selector="CheckBox">
<Setter Property="Margin" Value="0 0 5 0" />
</Style>
</Grid.Styles>
<CheckBox Content="{l:Translate chkDisplayPort1}" IsChecked="{CompiledBinding Config.DisplayInputPort1}" />
<CheckBox Grid.Column="1" Content="{l:Translate chkDisplayPort2}" IsChecked="{CompiledBinding Config.DisplayInputPort2}" />
<CheckBox Grid.Row="1" Content="{l:Translate chkDisplayPort3}" IsChecked="{CompiledBinding Config.DisplayInputPort3}" />
<CheckBox Grid.Row="1" Grid.Column="1" Content="{l:Translate chkDisplayPort4}" IsChecked="{CompiledBinding Config.DisplayInputPort4}" />
<CheckBox Grid.Row="2" Content="{l:Translate chkDisplayPort5}" IsChecked="{CompiledBinding Config.DisplayInputPort5}" />
</Grid>
<TextBlock Grid.Row="1" Text="{l:Translate lblDisplayPosition}" />
<c:EnumComboBox Grid.Row="1" Grid.Column="1" SelectedItem="{CompiledBinding Config.DisplayInputPosition}" />
<CheckBox Grid.Row="2" Grid.ColumnSpan="2" IsChecked="{CompiledBinding Config.DisplayInputHorizontally}" Content="{l:Translate chkDisplayInputHorizontally}" />
</Grid>
</TabItem>
</TabControl>
</UserControl>

View file

@ -0,0 +1,21 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Mesen.Utilities;
using Mesen.Config;
namespace Mesen.Views
{
public class InputConfigView : UserControl
{
public InputConfigView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View file

@ -26,6 +26,9 @@
<DataTemplate DataType="{x:Type vm:EmulationConfigViewModel}">
<v:EmulationConfigView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:InputConfigViewModel}">
<v:InputConfigView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:VideoConfigViewModel}">
<v:VideoConfigView />
</DataTemplate>
@ -71,6 +74,14 @@
</StackPanel>
</TabItem.Header>
</TabItem>
<TabItem Content="{CompiledBinding Input}">
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Assets/Controller.png" Margin="0 0 10 0" />
<TextBlock VerticalAlignment="Center" Text="{l:Translate tabInput}" />
</StackPanel>
</TabItem.Header>
</TabItem>
<TabItem Content="{CompiledBinding Video}">
<TabItem.Header>
<StackPanel Orientation="Horizontal">

View file

@ -120,14 +120,6 @@ void Renderer::CleanupDevice()
{
ResetNesBuffers();
ReleaseRenderTargetView();
if(_pAlphaEnableBlendingState) {
_pAlphaEnableBlendingState->Release();
_pAlphaEnableBlendingState = nullptr;
}
if(_pDepthDisabledStencilState) {
_pDepthDisabledStencilState->Release();
_pDepthDisabledStencilState = nullptr;
}
if(_pSwapChain) {
_pSwapChain->SetFullscreenState(false, nullptr);
_pSwapChain->Release();
@ -318,61 +310,6 @@ HRESULT Renderer::InitDevice()
if(FAILED(hr)) {
return hr;
}
D3D11_DEPTH_STENCIL_DESC depthDisabledStencilDesc;
ZeroMemory(&depthDisabledStencilDesc, sizeof(depthDisabledStencilDesc));
depthDisabledStencilDesc.DepthEnable = false;
depthDisabledStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
depthDisabledStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
depthDisabledStencilDesc.StencilEnable = true;
depthDisabledStencilDesc.StencilReadMask = 0xFF;
depthDisabledStencilDesc.StencilWriteMask = 0xFF;
depthDisabledStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
depthDisabledStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
depthDisabledStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
depthDisabledStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
// Create the state using the device.
hr = _pd3dDevice->CreateDepthStencilState(&depthDisabledStencilDesc, &_pDepthDisabledStencilState);
if(FAILED(hr)) {
MessageManager::Log("D3DDevice::CreateDepthStencilState() failed - Error:" + std::to_string(hr));
return hr;
}
// Clear the blend state description.
D3D11_BLEND_DESC blendStateDescription;
ZeroMemory(&blendStateDescription, sizeof(D3D11_BLEND_DESC));
// Create an alpha enabled blend state description.
blendStateDescription.RenderTarget[0].BlendEnable = TRUE;
blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blendStateDescription.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
blendStateDescription.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blendStateDescription.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blendStateDescription.RenderTarget[0].RenderTargetWriteMask = 0x0f;
// Create the blend state using the description.
hr = _pd3dDevice->CreateBlendState(&blendStateDescription, &_pAlphaEnableBlendingState);
if(FAILED(hr)) {
MessageManager::Log("D3DDevice::CreateBlendState() failed - Error:" + std::to_string(hr));
return hr;
}
float blendFactor[4];
blendFactor[0] = 0.0f;
blendFactor[1] = 0.0f;
blendFactor[2] = 0.0f;
blendFactor[3] = 0.0f;
_pDeviceContext->OMSetBlendState(_pAlphaEnableBlendingState, blendFactor, 0xffffffff);
_pDeviceContext->OMSetDepthStencilState(_pDepthDisabledStencilState, 1);
hr = CreateEmuTextureBuffers();
if(FAILED(hr)) {
return hr;
@ -420,7 +357,7 @@ ID3D11ShaderResourceView* Renderer::GetShaderResourceView(ID3D11Texture2D* textu
return shaderResourceView;
}
void Renderer::UpdateFrame(RenderedFrame frame)
void Renderer::UpdateFrame(RenderedFrame& frame)
{
SetScreenSize(frame.Width, frame.Height);

View file

@ -25,8 +25,6 @@ private:
ID3D11DeviceContext* _pDeviceContext = nullptr;
IDXGISwapChain* _pSwapChain = nullptr;
ID3D11RenderTargetView* _pRenderTargetView = nullptr;
ID3D11DepthStencilState* _pDepthDisabledStencilState = nullptr;
ID3D11BlendState* _pAlphaEnableBlendingState = nullptr;
atomic<bool> _needFlip = false;
uint8_t* _textureBuffer[2] = { nullptr, nullptr };
@ -86,5 +84,5 @@ public:
void Reset();
void Render(uint32_t* hudBuffer, uint32_t hudWidth, uint32_t hudHeight);
void UpdateFrame(RenderedFrame frame);
void UpdateFrame(RenderedFrame& frame);
};