Compare commits

..

35 Commits

Author SHA1 Message Date
N00byKing
10955d72ef Update CMakeLists.txt 2018-01-17 18:21:49 +01:00
N00byKing
66388f7576 Update title_metadata.h 2018-01-17 18:20:52 +01:00
N00byKing
8b097aa17e Implement Pull #3333 from citra: citra_qt: Pause emulation on CoreError (#39) 2018-01-16 11:32:27 -05:00
bunnei
d818791866 Merge pull request #24 from nkatz565/nk-inputs
Adding meumart's Citra SDL Joystick support. Citra PR #3116
2018-01-16 10:13:39 -05:00
bunnei
3b28d382d0 Merge pull request #38 from goaaats/citra_merges
Merge citra-emu PR#3001 by Styleoshin(citra-qt : Adding fullscreen mode)
2018-01-16 10:00:24 -05:00
goaaats
8cdc1be0df Merge citra-emu PR#3159 by FearlessTobi(citra-qt : Fix a bug in our fullscreen implementation) 2018-01-16 15:59:30 +01:00
goaaats
f473780c52 Merge citra-emu PR#3001 by Styleoshin(citra-qt : Adding fullscreen mode) 2018-01-16 15:50:33 +01:00
Flame Sage
713c1ed203 Merge pull request #25 from chris062689/master
Updated Discord link to match website.
2018-01-15 20:35:26 -05:00
Flame Sage
21f5161ad8 Updated Discord link to match website. 2018-01-16 01:35:04 +00:00
muemart
eaff98dbb3 Adding meumart's Citra SDL Joystick support. Citra PR #3116 2018-01-15 20:02:30 -05:00
bunnei
20a745aa71 Merge pull request #23 from Simonx22/cmake_rename
CMakeList.txt: rename CITRA to YUZU
2018-01-15 17:48:02 -05:00
bunnei
6f904bb622 nso: Load subsdk4 if available. 2018-01-15 17:18:24 -05:00
Simonx22
e9cf161917 rename CITRA to YUZU 2018-01-15 23:05:20 +01:00
bunnei
1a3b3e9100 pctl: Clang format. 2018-01-15 15:16:06 -05:00
bunnei
1148e2ce7b pctl: GetService should return an IParentalControlService interface. 2018-01-15 15:10:18 -05:00
bunnei
5bc14ab0de applet_oe: Stub SetFocusHandlingMode, GetCurrentFocusState, SetTerminateResult. 2018-01-15 14:41:06 -05:00
bunnei
8e2f62c6a5 settings: Fix button mappings array to have correct entries. 2018-01-15 13:09:14 -05:00
bunnei
67c8b671d2 Merge pull request #17 from spycrab/bindir
CMake: Output binaries to bin/
2018-01-15 12:56:58 -05:00
bunnei
054d3e5fc3 Merge pull request #20 from Andrix44/fixes
Various fixes
2018-01-15 12:56:16 -05:00
bunnei
325f72aceb Merge pull request #16 from shinyquagsire23/hid-sharedmem-impl-start
HID Sharedmem Impl Start
2018-01-15 12:54:54 -05:00
unknown
a4f5ccbf0a Clanggit rebase -i fixes 2018-01-15 18:20:53 +01:00
unknown
8d968780c1 Clang format 2018-01-15 17:55:16 +01:00
unknown
58ee23f16d Change default log level to info 2018-01-15 17:15:10 +01:00
unknown
27960d8c2c Update the internal resolution settings 2018-01-15 16:59:02 +01:00
unknown
af6e0faf0a Fix some warnings 2018-01-15 16:24:29 +01:00
spycrab
8a440faa69 CMake: Output binaries to bin/ 2018-01-15 13:47:38 +01:00
shinyquagsire23
9fba2d68fe yuzu_cmd: Fix default ini, add screenshot button 2018-01-15 02:30:58 -07:00
shinyquagsire23
1ea49442f9 hid: Bare-minimum sharedmem input 2018-01-15 02:30:58 -07:00
shinyquagsire23
bb1fcfac33 hid: Remove redundant HID prefix on structs/enums 2018-01-15 02:30:58 -07:00
shinyquagsire23
aa4fa8bded configure_input: update w/ Switch buttons 2018-01-15 02:30:53 -07:00
shinyquagsire23
801d6c1b6f settings: Screenshot button 2018-01-15 02:30:53 -07:00
shinyquagsire23
cdb43e64c1 yuzu_cmd: fix default ini 2018-01-15 02:30:53 -07:00
shinyquagsire23
74aa14c9b4 settings: adjust button configs for Switch controllers 2018-01-15 02:30:53 -07:00
shinyquagsire23
e08c132175 hid: Add sharedmem structs 2018-01-15 02:30:53 -07:00
Harry Prevor
f2d55bb881 fixed build for gcc c++17 / boost.icl incompatibility 2018-01-15 02:30:53 -07:00
30 changed files with 1287 additions and 308 deletions

View File

@@ -8,7 +8,7 @@ COMPRESSION_FLAGS="-cJvf"
mkdir "$REV_NAME"
cp build/src/yuzu_cmd/yuzu-cmd "$REV_NAME"
cp build/src/yuzu/yuzu "$REV_NAME"
cp build/bin/yuzu-cmd "$REV_NAME"
cp build/bin/yuzu "$REV_NAME"
. .travis/common/post-upload.sh

View File

@@ -8,8 +8,8 @@ COMPRESSION_FLAGS="-czvf"
mkdir "$REV_NAME"
cp build/src/yuzu_cmd/yuzu-cmd "$REV_NAME"
cp -r build/src/yuzu/yuzu.app "$REV_NAME"
cp build/bin/yuzu-cmd "$REV_NAME"
cp -r build/bin/yuzu.app "$REV_NAME"
# move qt libs into app bundle for deployment
$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app"

View File

@@ -166,6 +166,9 @@ if (NOT Boost_FOUND)
find_package(Boost QUIET REQUIRED)
endif()
# Output binaries to bin/
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
@@ -176,7 +179,7 @@ if (ENABLE_SDL2)
if (MSVC14 AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.0.5")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable CITRA_USE_BUNDLED_SDL2 and provide your own.")
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
endif()
if (DEFINED SDL2_VER)
@@ -236,7 +239,7 @@ if (ENABLE_QT)
if (MSVC14 AND ARCHITECTURE_x86_64)
set(QT_VER qt-5.7-msvc2015_64)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.")
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
endif()
if (DEFINED QT_VER)

View File

@@ -12,7 +12,7 @@ yuzu is licensed under the GPLv2 (or any later version). Refer to the license.tx
Check out our [website](https://yuzu-emu.org/)!
For development discussion, please join us on [Discord](https://discord.gg/VXqngT3).
For development discussion, please join us on [Discord](https://discord.gg/XQV6dn9).
### Development

View File

@@ -13,7 +13,7 @@ environment:
# Tell msys2 to inherit the current directory when starting the shell
CHERE_INVOKING: 1
matrix:
#- BUILD_TYPE: mingw
- BUILD_TYPE: mingw
- BUILD_TYPE: msvc
platform:
@@ -36,12 +36,6 @@ install:
C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S $dependencies" 2> $null
}
else
{
$dependecies = "SDL2-2.0.5"
"unicorn-yuzu"
"qt-5.7-msvc2015_64"
}
before_build:
- mkdir %BUILD_TYPE%_build
- cd %BUILD_TYPE%_build

View File

@@ -1991,7 +1991,7 @@ const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected)
else
{
nIndex = nIndex-1;
if(nIndex < UI.GroupMenuCount)
if(static_cast<uint32_t>(nIndex) < UI.GroupMenuCount)
{
MicroProfileGroupMenuItem& Item = UI.GroupMenu[nIndex];
static char buffer[MICROPROFILE_NAME_MAX_LEN+32];
@@ -2134,7 +2134,7 @@ const char* MicroProfileUIMenuCustom(int nIndex, bool* bSelected)
case 1: return "--";
default:
nIndex -= 2;
if(nIndex < UI.nCustomCount)
if(static_cast<uint32_t>(nIndex) < UI.nCustomCount)
{
return UI.Custom[nIndex].pName;
}
@@ -2184,7 +2184,7 @@ void MicroProfileUIClickGroups(int nIndex)
else
{
nIndex -= 1;
if(nIndex < UI.GroupMenuCount)
if(static_cast<uint32_t>(nIndex) < UI.GroupMenuCount)
{
MicroProfileGroupMenuItem& Item = UI.GroupMenu[nIndex];
if(Item.nIsCategory)

View File

@@ -88,6 +88,7 @@ set(HEADERS
file_sys/ivfc_archive.h
file_sys/path_parser.h
file_sys/savedata_archive.h
file_sys/title_metadata.h
frontend/emu_window.h
frontend/framebuffer_layout.h
frontend/input.h

View File

@@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <string>
#include <vector>
#include "common/common_types.h"

View File

@@ -52,7 +52,23 @@ public:
class ISelfController final : public ServiceFramework<ISelfController> {
public:
ISelfController() : ServiceFramework("ISelfController") {}
ISelfController() : ServiceFramework("ISelfController") {
static const FunctionInfo functions[] = {
{13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
};
RegisterHandlers(functions);
}
private:
void SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
// Takes 3 input u8s with each field located immediately after the previous u8, these are
// bool flags. No output.
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
@@ -61,6 +77,7 @@ public:
static const FunctionInfo functions[] = {
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
{1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
{9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"},
};
RegisterHandlers(functions);
@@ -86,12 +103,39 @@ private:
LOG_WARNING(Service, "(STUBBED) called");
}
void GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1); // 1: In focus, 2/3: Out of focus(running in "background")
LOG_WARNING(Service, "(STUBBED) called");
}
Kernel::SharedPtr<Kernel::Event> event;
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
public:
IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {}
IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
static const FunctionInfo functions[] = {
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
};
RegisterHandlers(functions);
}
private:
void SetTerminateResult(Kernel::HLERequestContext& ctx) {
// Takes an input u32 Result, no output.
// For example, in some cases official apps use this with error 0x2A2 then uses svcBreak.
IPC::RequestParser rp{ctx};
u32 result = rp.Pop<u32>();
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called, result=0x%08X", result);
}
};
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
@@ -120,48 +164,56 @@ private:
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IAudioController>();
LOG_DEBUG(Service, "called");
}
void GetDisplayController(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDisplayController>();
LOG_DEBUG(Service, "called");
}
void GetDebugFunctions(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDebugFunctions>();
LOG_DEBUG(Service, "called");
}
void GetWindowController(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IWindowController>();
LOG_DEBUG(Service, "called");
}
void GetSelfController(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISelfController>();
LOG_DEBUG(Service, "called");
}
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ICommonStateGetter>();
LOG_DEBUG(Service, "called");
}
void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILibraryAppletCreator>();
LOG_DEBUG(Service, "called");
}
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationFunctions>();
LOG_DEBUG(Service, "called");
}
};
@@ -169,6 +221,7 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationProxy>();
LOG_DEBUG(Service, "called");
}
AppletOE::AppletOE() : ServiceFramework("appletOE") {

View File

@@ -2,7 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <atomic>
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/frontend/input.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
@@ -13,6 +16,12 @@
namespace Service {
namespace HID {
// Updating period for each HID device.
// TODO(shinyquagsire23): These need better values.
constexpr u64 pad_update_ticks = BASE_CLOCK_RATE / 234;
constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE / 104;
constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE / 101;
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
IAppletResource() : ServiceFramework("IAppletResource") {
@@ -24,6 +33,15 @@ public:
shared_mem = Kernel::SharedMemory::Create(
nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::Read,
0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
// Register update callbacks
pad_update_event = CoreTiming::RegisterEvent(
"HID::UpdatePadCallback",
[this](u64 userdata, int cycles_late) { UpdatePadCallback(userdata, cycles_late); });
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
}
private:
@@ -34,8 +52,76 @@ private:
LOG_DEBUG(Service, "called");
}
void LoadInputDevices() {
std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
// TODO(shinyquagsire23): sticks, gyro, touch, mouse, keyboard
}
void UpdatePadCallback(u64 userdata, int cycles_late) {
SharedMemory* mem = reinterpret_cast<SharedMemory*>(shared_mem->GetPointer());
if (is_device_reload_pending.exchange(false))
LoadInputDevices();
// TODO(shinyquagsire23): This is a hack!
ControllerPadState& state =
mem->controllers[Controller_Handheld].layouts[Layout_Default].entries[0].buttons;
using namespace Settings::NativeButton;
state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
state.lstick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
state.rstick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
state.dleft.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
state.dup.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
state.dright.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
state.ddown.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
state.lstick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
state.lstick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
state.lstick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
state.lstick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
state.rstick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
state.rstick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
state.rstick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
state.rstick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
// TODO(shinyquagsire23): Analog stick vals
// TODO(shinyquagsire23): Update pad info proper, (circular buffers, timestamps, layouts)
// TODO(shinyquagsire23): Update touch info
// TODO(shinyquagsire23): Signal events
// Reschedule recurrent event
CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
}
// Handle to shared memory region designated to HID service
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
// CoreTiming update events
CoreTiming::EventType* pad_update_event;
// Stored input state info
std::atomic<bool> is_device_reload_pending{true};
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
buttons;
};
class Hid final : public ServiceFramework<Hid> {

View File

@@ -5,10 +5,322 @@
#pragma once
#include "core/hle/service/service.h"
#include "core/settings.h"
namespace Service {
namespace HID {
// Begin enums and output structs
enum ControllerType : u32 {
ControllerType_ProController = 1 << 0,
ControllerType_Handheld = 1 << 1,
ControllerType_JoyconPair = 1 << 2,
ControllerType_JoyconLeft = 1 << 3,
ControllerType_JoyconRight = 1 << 4,
};
enum ControllerLayoutType : u32 {
Layout_ProController = 0, // Pro Controller or HID gamepad
Layout_Handheld = 1, // Two Joy-Con docked to rails
Layout_Single = 2, // Horizontal single Joy-Con or pair of Joy-Con, adjusted for orientation
Layout_Left = 3, // Only raw left Joy-Con state, no orientation adjustment
Layout_Right = 4, // Only raw right Joy-Con state, no orientation adjustment
Layout_DefaultDigital = 5, // Same as next, but sticks have 8-direction values only
Layout_Default = 6, // Safe default, single Joy-Con have buttons/sticks rotated for orientation
};
enum ControllerColorDescription {
ColorDesc_ColorsNonexistent = 1 << 1,
};
enum ControllerConnectionState {
ConnectionState_Connected = 1 << 0,
ConnectionState_Wired = 1 << 1,
};
enum ControllerID {
Controller_Player1 = 0,
Controller_Player2 = 1,
Controller_Player3 = 2,
Controller_Player4 = 3,
Controller_Player5 = 4,
Controller_Player6 = 5,
Controller_Player7 = 6,
Controller_Player8 = 7,
Controller_Handheld = 8,
Controller_Unknown = 9,
};
// End enums and output structs
// Begin TouchScreen
struct TouchScreenHeader {
u64 timestampTicks;
u64 numEntries;
u64 latestEntry;
u64 maxEntryIndex;
u64 timestamp;
};
static_assert(sizeof(TouchScreenHeader) == 0x28,
"HID touch screen header structure has incorrect size");
struct TouchScreenEntryHeader {
u64 timestamp;
u64 numTouches;
};
static_assert(sizeof(TouchScreenEntryHeader) == 0x10,
"HID touch screen entry header structure has incorrect size");
struct TouchScreenEntryTouch {
u64 timestamp;
u32 padding;
u32 touchIndex;
u32 x;
u32 y;
u32 diameterX;
u32 diameterY;
u32 angle;
u32 padding_2;
};
static_assert(sizeof(TouchScreenEntryTouch) == 0x28,
"HID touch screen touch structure has incorrect size");
struct TouchScreenEntry {
TouchScreenEntryHeader header;
std::array<TouchScreenEntryTouch, 16> touches;
u64 unk;
};
static_assert(sizeof(TouchScreenEntry) == 0x298,
"HID touch screen entry structure has incorrect size");
struct TouchScreen {
TouchScreenHeader header;
std::array<TouchScreenEntry, 17> entries;
std::array<u8, 0x3c0> padding;
};
static_assert(sizeof(TouchScreen) == 0x3000, "HID touch screen structure has incorrect size");
// End TouchScreen
// Begin Mouse
struct MouseHeader {
u64 timestampTicks;
u64 numEntries;
u64 latestEntry;
u64 maxEntryIndex;
};
static_assert(sizeof(MouseHeader) == 0x20, "HID mouse header structure has incorrect size");
struct MouseButtonState {
union {
u64 hex{};
// Buttons
BitField<0, 1, u64> left;
BitField<1, 1, u64> right;
BitField<2, 1, u64> middle;
BitField<3, 1, u64> forward;
BitField<4, 1, u64> back;
};
};
struct MouseEntry {
u64 timestamp;
u64 timestamp_2;
u32 x;
u32 y;
u32 velocityX;
u32 velocityY;
u32 scrollVelocityX;
u32 scrollVelocityY;
MouseButtonState buttons;
};
static_assert(sizeof(MouseEntry) == 0x30, "HID mouse entry structure has incorrect size");
struct Mouse {
MouseHeader header;
std::array<MouseEntry, 17> entries;
std::array<u8, 0xB0> padding;
};
static_assert(sizeof(Mouse) == 0x400, "HID mouse structure has incorrect size");
// End Mouse
// Begin Keyboard
struct KeyboardHeader {
u64 timestampTicks;
u64 numEntries;
u64 latestEntry;
u64 maxEntryIndex;
};
static_assert(sizeof(KeyboardHeader) == 0x20, "HID keyboard header structure has incorrect size");
struct KeyboardModifierKeyState {
union {
u64 hex{};
// Buttons
BitField<0, 1, u64> lctrl;
BitField<1, 1, u64> lshift;
BitField<2, 1, u64> lalt;
BitField<3, 1, u64> lmeta;
BitField<4, 1, u64> rctrl;
BitField<5, 1, u64> rshift;
BitField<6, 1, u64> ralt;
BitField<7, 1, u64> rmeta;
BitField<8, 1, u64> capslock;
BitField<9, 1, u64> scrolllock;
BitField<10, 1, u64> numlock;
};
};
struct KeyboardEntry {
u64 timestamp;
u64 timestamp_2;
KeyboardModifierKeyState modifier;
u32 keys[8];
};
static_assert(sizeof(KeyboardEntry) == 0x38, "HID keyboard entry structure has incorrect size");
struct Keyboard {
KeyboardHeader header;
std::array<KeyboardEntry, 17> entries;
std::array<u8, 0x28> padding;
};
static_assert(sizeof(Keyboard) == 0x400, "HID keyboard structure has incorrect size");
// End Keyboard
// Begin Controller
struct ControllerMAC {
u64 timestamp;
std::array<u8, 0x8> mac;
u64 unk;
u64 timestamp_2;
};
static_assert(sizeof(ControllerMAC) == 0x20, "HID controller MAC structure has incorrect size");
struct ControllerHeader {
u32 type;
u32 isHalf;
u32 singleColorsDescriptor;
u32 singleColorBody;
u32 singleColorButtons;
u32 splitColorsDescriptor;
u32 leftColorBody;
u32 leftColorButtons;
u32 rightColorBody;
u32 rightColorbuttons;
};
static_assert(sizeof(ControllerHeader) == 0x28,
"HID controller header structure has incorrect size");
struct ControllerLayoutHeader {
u64 timestampTicks;
u64 numEntries;
u64 latestEntry;
u64 maxEntryIndex;
};
static_assert(sizeof(ControllerLayoutHeader) == 0x20,
"HID controller layout header structure has incorrect size");
struct ControllerPadState {
union {
u64 hex{};
// Buttons
BitField<0, 1, u64> a;
BitField<1, 1, u64> b;
BitField<2, 1, u64> x;
BitField<3, 1, u64> y;
BitField<4, 1, u64> lstick;
BitField<5, 1, u64> rstick;
BitField<6, 1, u64> l;
BitField<7, 1, u64> r;
BitField<8, 1, u64> zl;
BitField<9, 1, u64> zr;
BitField<10, 1, u64> plus;
BitField<11, 1, u64> minus;
// D-pad buttons
BitField<12, 1, u64> dleft;
BitField<13, 1, u64> dup;
BitField<14, 1, u64> dright;
BitField<15, 1, u64> ddown;
// Left stick directions
BitField<16, 1, u64> lstick_left;
BitField<17, 1, u64> lstick_up;
BitField<18, 1, u64> lstick_right;
BitField<19, 1, u64> lstick_down;
// Right stick directions
BitField<20, 1, u64> rstick_left;
BitField<21, 1, u64> rstick_up;
BitField<22, 1, u64> rstick_right;
BitField<23, 1, u64> rstick_down;
BitField<24, 1, u64> sl;
BitField<25, 1, u64> sr;
};
};
struct ControllerInputEntry {
u64 timestamp;
u64 timestamp_2;
ControllerPadState buttons;
u32 joystickLeftX;
u32 joystickLeftY;
u32 joystickRightX;
u32 joystickRightY;
u64 connectionState;
};
static_assert(sizeof(ControllerInputEntry) == 0x30,
"HID controller input entry structure has incorrect size");
struct ControllerLayout {
ControllerLayoutHeader header;
std::array<ControllerInputEntry, 17> entries;
};
static_assert(sizeof(ControllerLayout) == 0x350,
"HID controller layout structure has incorrect size");
struct Controller {
ControllerHeader header;
std::array<ControllerLayout, 7> layouts;
std::array<u8, 0x2a70> unk_1;
ControllerMAC macLeft;
ControllerMAC macRight;
std::array<u8, 0xdf8> unk_2;
};
static_assert(sizeof(Controller) == 0x5000, "HID controller structure has incorrect size");
// End Controller
struct SharedMemory {
std::array<u8, 0x400> header;
TouchScreen touchscreen;
Mouse mouse;
Keyboard keyboard;
std::array<u8, 0x400> unkSection1;
std::array<u8, 0x400> unkSection2;
std::array<u8, 0x400> unkSection3;
std::array<u8, 0x400> unkSection4;
std::array<u8, 0x200> unkSection5;
std::array<u8, 0x200> unkSection6;
std::array<u8, 0x200> unkSection7;
std::array<u8, 0x800> unkSection8;
std::array<u8, 0x4000> controllerSerials;
std::array<Controller, 10> controllers;
std::array<u8, 0x4600> unkSection9;
};
static_assert(sizeof(SharedMemory) == 0x40000, "HID Shared Memory structure has incorrect size");
/// Reload input devices. Used when input configuration changed
void ReloadInputDevices();

View File

@@ -9,11 +9,16 @@
namespace Service {
namespace PCTL {
class IParentalControlService final : public ServiceFramework<IParentalControlService> {
public:
IParentalControlService() : ServiceFramework("IParentalControlService") {}
};
void PCTL_A::GetService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2};
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
// TODO(Subv): This should return an IParentalControlService interface.
rb.PushIpcInterface<IParentalControlService>();
LOG_DEBUG(Service, "called");
}
PCTL_A::PCTL_A() : ServiceFramework("pctl:a") {

View File

@@ -157,7 +157,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
// Load NSO modules
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
for (const auto& module : {"rtld", "sdk", "subsdk0", "subsdk1", "subsdk2", "subsdk3"}) {
for (const auto& module : {"rtld", "sdk", "subsdk0", "subsdk1", "subsdk2", "subsdk3", "subsdk4"}) {
const std::string path = filepath.substr(0, filepath.find_last_of("/\\")) + "/" + module;
const VAddr load_addr = next_load_addr;
next_load_addr = LoadNso(path, load_addr);

View File

@@ -16,52 +16,91 @@ enum Values {
B,
X,
Y,
Up,
Down,
Left,
Right,
LStick,
RStick,
L,
R,
Start,
Select,
ZL,
ZR,
Plus,
Minus,
DLeft,
DUp,
DRight,
DDown,
LStick_Left,
LStick_Up,
LStick_Right,
LStick_Down,
RStick_Left,
RStick_Up,
RStick_Right,
RStick_Down,
SL,
SR,
Home,
Screenshot,
NumButtons,
};
constexpr int BUTTON_HID_BEGIN = A;
constexpr int BUTTON_IR_BEGIN = ZL;
constexpr int BUTTON_NS_BEGIN = Home;
constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN;
constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN;
constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
constexpr int BUTTON_NS_END = NumButtons;
constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
constexpr int NUM_BUTTONS_IR = BUTTON_IR_END - BUTTON_IR_BEGIN;
constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
static const std::array<const char*, NumButtons> mapping = {{
"button_a", "button_b", "button_x", "button_y", "button_up", "button_down", "button_left",
"button_right", "button_l", "button_r", "button_start", "button_select", "button_zl",
"button_zr", "button_home",
"button_a",
"button_b",
"button_x",
"button_y",
"button_lstick",
"button_rstick",
"button_l",
"button_r",
"button_zl",
"button_zr",
"button_plus",
"button_minus",
"button_dleft",
"button_dup",
"button_dright",
"button_ddown",
"button_lstick_left",
"button_lstick_up",
"button_lstick_right",
"button_lstick_down",
"button_rstick_left",
"button_rstick_up",
"button_rstick_right",
"button_rstick_down",
"button_sl",
"button_sr",
"button_home",
"button_screenshot",
}};
} // namespace NativeButton
namespace NativeAnalog {
enum Values {
CirclePad,
CStick,
LStick,
RStick,
NumAnalogs,
};
static const std::array<const char*, NumAnalogs> mapping = {{
"circle_pad", "c_stick",
"lstick", "rstick",
}};
} // namespace NativeAnalog

View File

@@ -71,4 +71,15 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
return circle_pad_param.Serialize();
}
namespace Polling {
std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
#ifdef HAVE_SDL2
return SDL::Polling::GetPollers(type);
#else
return {};
#endif
}
} // namespace Polling
} // namespace InputCommon

View File

@@ -4,7 +4,13 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
namespace Common {
class ParamPackage;
}
namespace InputCommon {
@@ -31,4 +37,30 @@ std::string GenerateKeyboardParam(int key_code);
std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
int key_modifier, float modifier_scale);
namespace Polling {
enum class DeviceType { Button, Analog };
/**
* A class that can be used to get inputs from an input device like controllers without having to
* poll the device's status yourself
*/
class DevicePoller {
public:
virtual ~DevicePoller() = default;
/// Setup and start polling for inputs, should be called before GetNextInput
virtual void Start() = 0;
/// Stop polling
virtual void Stop() = 0;
/**
* Every call to this function returns the next input recorded since calling Start
* @return A ParamPackage of the recorded input, which can be used to create an InputDevice.
* If there has been no input, the package is empty
*/
virtual Common::ParamPackage GetNextInput() = 0;
};
// Get all DevicePoller from all backends for a specific device type
std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type);
} // namespace Polling
} // namespace InputCommon

View File

@@ -3,13 +3,15 @@
// Refer to the license.txt file included.
#include <cmath>
#include <memory>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <SDL.h>
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/param_package.h"
#include "input_common/main.h"
#include "input_common/sdl/sdl.h"
namespace InputCommon {
@@ -69,6 +71,10 @@ public:
return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0;
}
SDL_JoystickID GetJoystickID() const {
return SDL_JoystickInstanceID(joystick.get());
}
private:
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick;
};
@@ -247,5 +253,180 @@ void Shutdown() {
}
}
/**
* This function converts a joystick ID used in SDL events to the device index. This is necessary
* because Citra opens joysticks using their indices, not their IDs.
*/
static int JoystickIDToDeviceIndex(SDL_JoystickID id) {
int num_joysticks = SDL_NumJoysticks();
for (int i = 0; i < num_joysticks; i++) {
auto joystick = GetJoystick(i);
if (joystick->GetJoystickID() == id) {
return i;
}
}
return -1;
}
Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
Common::ParamPackage params({{"engine", "sdl"}});
switch (event.type) {
case SDL_JOYAXISMOTION:
params.Set("joystick", JoystickIDToDeviceIndex(event.jaxis.which));
params.Set("axis", event.jaxis.axis);
if (event.jaxis.value > 0) {
params.Set("direction", "+");
params.Set("threshold", "0.5");
} else {
params.Set("direction", "-");
params.Set("threshold", "-0.5");
}
break;
case SDL_JOYBUTTONUP:
params.Set("joystick", JoystickIDToDeviceIndex(event.jbutton.which));
params.Set("button", event.jbutton.button);
break;
case SDL_JOYHATMOTION:
params.Set("joystick", JoystickIDToDeviceIndex(event.jhat.which));
params.Set("hat", event.jhat.hat);
switch (event.jhat.value) {
case SDL_HAT_UP:
params.Set("direction", "up");
break;
case SDL_HAT_DOWN:
params.Set("direction", "down");
break;
case SDL_HAT_LEFT:
params.Set("direction", "left");
break;
case SDL_HAT_RIGHT:
params.Set("direction", "right");
break;
default:
return {};
}
break;
}
return params;
}
namespace Polling {
class SDLPoller : public InputCommon::Polling::DevicePoller {
public:
SDLPoller() = default;
~SDLPoller() = default;
void Start() override {
// SDL joysticks must be opened, otherwise they don't generate events
SDL_JoystickUpdate();
int num_joysticks = SDL_NumJoysticks();
for (int i = 0; i < num_joysticks; i++) {
joysticks_opened.emplace_back(GetJoystick(i));
}
// Empty event queue to get rid of old events. citra-qt doesn't use the queue
SDL_Event dummy;
while (SDL_PollEvent(&dummy)) {
}
}
void Stop() override {
joysticks_opened.clear();
}
private:
std::vector<std::shared_ptr<SDLJoystick>> joysticks_opened;
};
class SDLButtonPoller final : public SDLPoller {
public:
SDLButtonPoller() = default;
~SDLButtonPoller() = default;
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_JOYAXISMOTION:
if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
break;
}
case SDL_JOYBUTTONUP:
case SDL_JOYHATMOTION:
return SDLEventToButtonParamPackage(event);
}
}
return {};
}
};
class SDLAnalogPoller final : public SDLPoller {
public:
SDLAnalogPoller() = default;
~SDLAnalogPoller() = default;
void Start() override {
SDLPoller::Start();
// Reset stored axes
analog_xaxis = -1;
analog_yaxis = -1;
analog_axes_joystick = -1;
}
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
continue;
}
// An analog device needs two axes, so we need to store the axis for later and wait for
// a second SDL event. The axes also must be from the same joystick.
int axis = event.jaxis.axis;
if (analog_xaxis == -1) {
analog_xaxis = axis;
analog_axes_joystick = event.jaxis.which;
} else if (analog_yaxis == -1 && analog_xaxis != axis &&
analog_axes_joystick == event.jaxis.which) {
analog_yaxis = axis;
}
}
Common::ParamPackage params;
if (analog_xaxis != -1 && analog_yaxis != -1) {
params.Set("engine", "sdl");
params.Set("joystick", JoystickIDToDeviceIndex(analog_axes_joystick));
params.Set("axis_x", analog_xaxis);
params.Set("axis_y", analog_yaxis);
analog_xaxis = -1;
analog_yaxis = -1;
analog_axes_joystick = -1;
return params;
}
return params;
}
private:
int analog_xaxis = -1;
int analog_yaxis = -1;
SDL_JoystickID analog_axes_joystick = -1;
};
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
InputCommon::Polling::DeviceType type) {
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers;
switch (type) {
case InputCommon::Polling::DeviceType::Analog:
pollers.push_back(std::make_unique<SDLAnalogPoller>());
break;
case InputCommon::Polling::DeviceType::Button:
pollers.push_back(std::make_unique<SDLButtonPoller>());
break;
}
return std::move(pollers);
}
} // namespace Polling
} // namespace SDL
} // namespace InputCommon

View File

@@ -4,8 +4,21 @@
#pragma once
#include <memory>
#include <vector>
#include "core/frontend/input.h"
union SDL_Event;
namespace Common {
class ParamPackage;
}
namespace InputCommon {
namespace Polling {
class DevicePoller;
enum class DeviceType;
} // namespace Polling
} // namespace InputCommon
namespace InputCommon {
namespace SDL {
@@ -15,5 +28,15 @@ void Init();
/// Unresisters SDL device factories and shut them down.
void Shutdown();
/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event);
namespace Polling {
/// Get all DevicePoller that use the SDL backend for a specific device type
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
InputCommon::Polling::DeviceType type);
} // namespace Polling
} // namespace SDL
} // namespace InputCommon

View File

@@ -40,6 +40,7 @@ void EmuThread::run() {
Core::System::ResultStatus result = Core::System::GetInstance().RunLoop();
if (result != Core::System::ResultStatus::Success) {
this->SetRunning(false);
emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails());
}

View File

@@ -8,7 +8,6 @@
#include "yuzu/configuration/config.h"
#include "yuzu/ui_settings.h"
Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
qt_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "qt-config.ini";
@@ -19,16 +18,18 @@ Config::Config() {
}
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_1, Qt::Key_2, Qt::Key_B,
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, Qt::Key_W,
Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, Qt::Key_H, Qt::Key_G,
Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, Qt::Key_Down, Qt::Key_J, Qt::Key_I, Qt::Key_L,
Qt::Key_K, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
};
const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
{
Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_E,
},
{
Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L, Qt::Key_D,
Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L, Qt::Key_R,
},
}};
@@ -86,7 +87,7 @@ void Config::ReadValues() {
qt_config->endGroup();
qt_config->beginGroup("Miscellaneous");
Settings::values.log_filter = qt_config->value("log_filter", "*:Trace").toString().toStdString();
Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString();
qt_config->endGroup();
qt_config->beginGroup("Debugging");
@@ -136,6 +137,7 @@ void Config::ReadValues() {
qt_config->endGroup();
UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool();
UISettings::values.fullscreen = qt_config->value("fullscreen", false).toBool();
UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool();
UISettings::values.show_filter_bar = qt_config->value("showFilterBar", true).toBool();
UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool();
@@ -215,6 +217,7 @@ void Config::SaveValues() {
qt_config->endGroup();
qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode);
qt_config->setValue("fullscreen", UISettings::values.fullscreen);
qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar);
qt_config->setValue("showFilterBar", UISettings::values.show_filter_bar);
qt_config->setValue("showStatusBar", UISettings::values.show_status_bar);

View File

@@ -7,7 +7,6 @@
#include "ui_configure_graphics.h"
#include "yuzu/configuration/configure_graphics.h"
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGraphics) {
@@ -23,12 +22,6 @@ enum class Resolution : int {
Scale2x,
Scale3x,
Scale4x,
Scale5x,
Scale6x,
Scale7x,
Scale8x,
Scale9x,
Scale10x,
};
float ToResolutionFactor(Resolution option) {
@@ -43,18 +36,6 @@ float ToResolutionFactor(Resolution option) {
return 3.f;
case Resolution::Scale4x:
return 4.f;
case Resolution::Scale5x:
return 5.f;
case Resolution::Scale6x:
return 6.f;
case Resolution::Scale7x:
return 7.f;
case Resolution::Scale8x:
return 8.f;
case Resolution::Scale9x:
return 9.f;
case Resolution::Scale10x:
return 10.f;
}
return 0.f;
}
@@ -70,18 +51,6 @@ Resolution FromResolutionFactor(float factor) {
return Resolution::Scale3x;
} else if (factor == 4.f) {
return Resolution::Scale4x;
} else if (factor == 5.f) {
return Resolution::Scale5x;
} else if (factor == 6.f) {
return Resolution::Scale6x;
} else if (factor == 7.f) {
return Resolution::Scale7x;
} else if (factor == 8.f) {
return Resolution::Scale8x;
} else if (factor == 9.f) {
return Resolution::Scale9x;
} else if (factor == 10.f) {
return Resolution::Scale10x;
}
return Resolution::Auto;
}

View File

@@ -34,7 +34,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Internal Resolution:</string>
<string>Internal Resolution:(Currently does nothing.)</string>
</property>
</widget>
</item>
@@ -47,52 +47,22 @@
</item>
<item>
<property name="text">
<string>Native (400x240)</string>
<string>Native (1280x720)</string>
</property>
</item>
<item>
<property name="text">
<string>2x Native (800x480)</string>
<string>2x Native (2560x1440)</string>
</property>
</item>
<item>
<property name="text">
<string>3x Native (1200x720)</string>
<string>3x Native (3840x2160)</string>
</property>
</item>
<item>
<property name="text">
<string>4x Native (1600x960)</string>
</property>
</item>
<item>
<property name="text">
<string>5x Native (2000x1200)</string>
</property>
</item>
<item>
<property name="text">
<string>6x Native (2400x1440)</string>
</property>
</item>
<item>
<property name="text">
<string>7x Native (2800x1680)</string>
</property>
</item>
<item>
<property name="text">
<string>8x Native (3200x1920)</string>
</property>
</item>
<item>
<property name="text">
<string>9x Native (3600x2160)</string>
</property>
</item>
<item>
<property name="text">
<string>10x Native (4000x2400)</string>
<string>4x Native (5120x2880)</string>
</property>
</item>
</widget>

View File

@@ -5,13 +5,13 @@
#include <algorithm>
#include <memory>
#include <utility>
#include <QMessageBox>
#include <QTimer>
#include "common/param_package.h"
#include "input_common/main.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input.h"
const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>
ConfigureInput::analog_sub_buttons{{
"up", "down", "left", "right", "modifier",
@@ -32,73 +32,95 @@ static QString getKeyName(int key_code) {
}
}
static void SetButtonKey(int key, Common::ParamPackage& button_param) {
button_param = Common::ParamPackage{InputCommon::GenerateKeyboardParam(key)};
}
static void SetAnalogKey(int key, Common::ParamPackage& analog_param,
const std::string& button_name) {
static void SetAnalogButton(const Common::ParamPackage& input_param,
Common::ParamPackage& analog_param, const std::string& button_name) {
if (analog_param.Get("engine", "") != "analog_from_button") {
analog_param = {
{"engine", "analog_from_button"}, {"modifier_scale", "0.5"},
};
}
analog_param.Set(button_name, InputCommon::GenerateKeyboardParam(key));
analog_param.Set(button_name, input_param.Serialize());
}
ConfigureInput::ConfigureInput(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
timer(std::make_unique<QTimer>()) {
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
button_map = {
ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, ui->buttonDpadUp,
ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonL, ui->buttonR,
ui->buttonStart, ui->buttonSelect, ui->buttonZL, ui->buttonZR, ui->buttonHome,
ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
};
analog_map = {{
analog_map_buttons = {{
{
ui->buttonCircleUp, ui->buttonCircleDown, ui->buttonCircleLeft, ui->buttonCircleRight,
ui->buttonCircleMod,
ui->buttonLStickUp, ui->buttonLStickDown, ui->buttonLStickLeft, ui->buttonLStickRight,
ui->buttonLStickMod,
},
{
ui->buttonCStickUp, ui->buttonCStickDown, ui->buttonCStickLeft, ui->buttonCStickRight,
nullptr,
ui->buttonRStickUp, ui->buttonRStickDown, ui->buttonRStickLeft, ui->buttonRStickRight,
ui->buttonRStickMod,
},
}};
analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
if (button_map[button_id])
connect(button_map[button_id], &QPushButton::released, [=]() {
handleClick(button_map[button_id],
[=](int key) { SetButtonKey(key, buttons_param[button_id]); });
handleClick(
button_map[button_id],
[=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
InputCommon::Polling::DeviceType::Button);
});
}
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
if (analog_map[analog_id][sub_button_id] != nullptr) {
connect(analog_map[analog_id][sub_button_id], &QPushButton::released, [=]() {
handleClick(analog_map[analog_id][sub_button_id], [=](int key) {
SetAnalogKey(key, analogs_param[analog_id],
analog_sub_buttons[sub_button_id]);
});
});
if (analog_map_buttons[analog_id][sub_button_id] != nullptr) {
connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released,
[=]() {
handleClick(analog_map_buttons[analog_id][sub_button_id],
[=](const Common::ParamPackage& params) {
SetAnalogButton(params, analogs_param[analog_id],
analog_sub_buttons[sub_button_id]);
},
InputCommon::Polling::DeviceType::Button);
});
}
}
connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
QMessageBox::information(
this, "Information",
"After pressing OK, first move your joystick horizontally, and then vertically.");
handleClick(
analog_map_stick[analog_id],
[=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
InputCommon::Polling::DeviceType::Analog);
});
}
connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
timer->setSingleShot(true);
connect(timer.get(), &QTimer::timeout, [this]() {
releaseKeyboard();
releaseMouse();
key_setter = boost::none;
updateButtonLabels();
timeout_timer->setSingleShot(true);
connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
connect(poll_timer.get(), &QTimer::timeout, [this]() {
Common::ParamPackage params;
for (auto& poller : device_pollers) {
params = poller->GetNextInput();
if (params.Has("engine")) {
setPollingResult(params, false);
return;
}
}
});
this->loadConfiguration();
@@ -128,13 +150,15 @@ void ConfigureInput::loadConfiguration() {
void ConfigureInput::restoreDefaults() {
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
SetButtonKey(Config::default_buttons[button_id], buttons_param[button_id]);
buttons_param[button_id] = Common::ParamPackage{
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
}
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
SetAnalogKey(Config::default_analogs[analog_id][sub_button_id],
analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
Config::default_analogs[analog_id][sub_button_id])};
SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
}
}
updateButtonLabels();
@@ -145,7 +169,9 @@ void ConfigureInput::updateButtonLabels() {
QString non_keyboard(tr("[non-keyboard]"));
auto KeyToText = [&non_keyboard](const Common::ParamPackage& param) {
if (param.Get("engine", "") != "keyboard") {
if (!param.Has("engine")) {
return QString("[not set]");
} else if (param.Get("engine", "") != "keyboard") {
return non_keyboard;
} else {
return getKeyName(param.Get("code", 0));
@@ -158,7 +184,7 @@ void ConfigureInput::updateButtonLabels() {
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
if (analogs_param[analog_id].Get("engine", "") != "analog_from_button") {
for (QPushButton* button : analog_map[analog_id]) {
for (QPushButton* button : analog_map_buttons[analog_id]) {
if (button)
button->setText(non_keyboard);
}
@@ -166,35 +192,66 @@ void ConfigureInput::updateButtonLabels() {
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
Common::ParamPackage param(
analogs_param[analog_id].Get(analog_sub_buttons[sub_button_id], ""));
if (analog_map[analog_id][sub_button_id])
analog_map[analog_id][sub_button_id]->setText(KeyToText(param));
if (analog_map_buttons[analog_id][sub_button_id])
analog_map_buttons[analog_id][sub_button_id]->setText(KeyToText(param));
}
}
analog_map_stick[analog_id]->setText("Set Analog Stick");
}
}
void ConfigureInput::handleClick(QPushButton* button, std::function<void(int)> new_key_setter) {
void ConfigureInput::handleClick(QPushButton* button,
std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type) {
button->setText(tr("[press key]"));
button->setFocus();
key_setter = new_key_setter;
input_setter = new_input_setter;
device_pollers = InputCommon::Polling::GetPollers(type);
// Keyboard keys can only be used as button devices
want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
for (auto& poller : device_pollers) {
poller->Start();
}
grabKeyboard();
grabMouse();
timer->start(5000); // Cancel after 5 seconds
timeout_timer->start(5000); // Cancel after 5 seconds
poll_timer->start(200); // Check for new inputs every 200ms
}
void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool abort) {
releaseKeyboard();
releaseMouse();
timeout_timer->stop();
poll_timer->stop();
for (auto& poller : device_pollers) {
poller->Stop();
}
if (!abort) {
(*input_setter)(params);
}
updateButtonLabels();
input_setter = boost::none;
}
void ConfigureInput::keyPressEvent(QKeyEvent* event) {
releaseKeyboard();
releaseMouse();
if (!key_setter || !event)
if (!input_setter || !event)
return;
if (event->key() != Qt::Key_Escape)
(*key_setter)(event->key());
updateButtonLabels();
key_setter = boost::none;
timer->stop();
if (event->key() != Qt::Key_Escape) {
if (want_keyboard_keys) {
setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
false);
} else {
// Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
return;
}
}
setPollingResult({}, true);
}

View File

@@ -8,11 +8,13 @@
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <QKeyEvent>
#include <QWidget>
#include <boost/optional.hpp>
#include "common/param_package.h"
#include "core/settings.h"
#include "input_common/main.h"
#include "ui_configure_input.h"
class QPushButton;
@@ -35,10 +37,11 @@ public:
private:
std::unique_ptr<Ui::ConfigureInput> ui;
std::unique_ptr<QTimer> timer;
std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer;
/// This will be the the setting function when an input is awaiting configuration.
boost::optional<std::function<void(int)>> key_setter;
boost::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
@@ -48,13 +51,23 @@ private:
/// Each button input is represented by a QPushButton.
std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
/// Each analog input is represented by five QPushButtons which represents up, down, left, right
/// and modifier
/// A group of five QPushButtons represent one analog input. The buttons each represent up,
/// down, left, right, and modifier, respectively.
std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
analog_map;
analog_map_buttons;
/// Analog inputs are also represented each with a single button, used to configure with an
/// actual analog stick
std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
/// keyboard events are ignored.
bool want_keyboard_keys = false;
/// Load configuration settings.
void loadConfiguration();
/// Restore all buttons to their default values.
@@ -63,7 +76,13 @@ private:
void updateButtonLabels();
/// Called when the button was pressed.
void handleClick(QPushButton* button, std::function<void(int)> new_key_setter);
void handleClick(QPushButton* button,
std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type);
/// Finish polling and configure input using the input_setter
void setPollingResult(const Common::ParamPackage& params, bool abort);
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
};

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>370</width>
<height>534</height>
<width>343</width>
<height>677</height>
</rect>
</property>
<property name="windowTitle">
@@ -16,6 +16,107 @@
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QGridLayout" name="gridLayout_7">
<item row="3" column="1">
<widget class="QGroupBox" name="faceButtons_6">
<property name="title">
<string>Misc.</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_25">
<item>
<widget class="QLabel" name="label_29">
<property name="text">
<string>Plus:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonPlus">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_26">
<item>
<widget class="QLabel" name="label_30">
<property name="text">
<string>Minus:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonMinus">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_27">
<item>
<widget class="QLabel" name="label_31">
<property name="text">
<string>Home:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonHome">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_28">
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Screen
Capture:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonScreenshot">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="faceButtons">
<property name="title">
@@ -190,7 +291,7 @@
</layout>
</widget>
</item>
<item row="1" column="0">
<item row="3" column="0">
<widget class="QGroupBox" name="faceButtons_3">
<property name="title">
<string>Shoulder Buttons</string>
@@ -274,100 +375,52 @@
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>SL:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonSL">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<layout class="QVBoxLayout" name="verticalLayout_29">
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>SR:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonSR">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QGroupBox" name="faceButtons_4">
<property name="title">
<string>Circle Pad</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_17">
<item>
<widget class="QLabel" name="label_21">
<property name="text">
<string>Left:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCircleLeft">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_18">
<item>
<widget class="QLabel" name="label_23">
<property name="text">
<string>Right:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCircleRight">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_19">
<item>
<widget class="QLabel" name="label_24">
<property name="text">
<string>Up:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCircleUp">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_20">
<item>
<widget class="QLabel" name="label_22">
<property name="text">
<string>Down:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCircleDown">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="faceButtons_5">
<property name="title">
<string>C-Stick</string>
<string>Right Stick</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="flat">
<bool>false</bool>
@@ -376,17 +429,17 @@
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_21">
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_24">
<item>
<widget class="QLabel" name="label_25">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Left:</string>
<string>Down:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCStickLeft">
<widget class="QPushButton" name="buttonRStickDown">
<property name="text">
<string/>
</property>
@@ -404,7 +457,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCStickRight">
<widget class="QPushButton" name="buttonRStickRight">
<property name="text">
<string/>
</property>
@@ -412,8 +465,33 @@
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<widget class="QPushButton" name="buttonRStickAnalog">
<property name="text">
<string>Set Analog Stick</string>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_23">
<layout class="QVBoxLayout" name="verticalLayout_21">
<item>
<widget class="QLabel" name="label_25">
<property name="text">
<string>Left:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonRStickLeft">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_25">
<item>
<widget class="QLabel" name="label_28">
<property name="text">
@@ -422,7 +500,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCStickUp">
<widget class="QPushButton" name="buttonRStickUp">
<property name="text">
<string/>
</property>
@@ -430,17 +508,35 @@
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_24">
<item row="2" column="0">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QLabel" name="label_26">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Down:</string>
<string>Pressed:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCStickDown">
<widget class="QPushButton" name="buttonRStick">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<layout class="QVBoxLayout" name="verticalLayout_32">
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>Modifier:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonRStickMod">
<property name="text">
<string/>
</property>
@@ -451,10 +547,10 @@
</layout>
</widget>
</item>
<item row="2" column="1">
<widget class="QGroupBox" name="faceButtons_6">
<item row="1" column="0">
<widget class="QGroupBox" name="faceButtons_4">
<property name="title">
<string>Misc.</string>
<string>Left Stick</string>
</property>
<property name="flat">
<bool>false</bool>
@@ -462,18 +558,18 @@
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_25">
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_20">
<item>
<widget class="QLabel" name="label_29">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Start:</string>
<string>Down:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonStart">
<widget class="QPushButton" name="buttonLStickDown">
<property name="text">
<string/>
</property>
@@ -481,17 +577,42 @@
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<widget class="QPushButton" name="buttonLStickAnalog">
<property name="text">
<string>Set Analog Stick</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_26">
<layout class="QVBoxLayout" name="verticalLayout_18">
<item>
<widget class="QLabel" name="label_30">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Select:</string>
<string>Right:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonSelect">
<widget class="QPushButton" name="buttonLStickRight">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_17">
<item>
<widget class="QLabel" name="label_21">
<property name="text">
<string>Left:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonLStickLeft">
<property name="text">
<string/>
</property>
@@ -500,16 +621,16 @@
</layout>
</item>
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_27">
<layout class="QVBoxLayout" name="verticalLayout_19">
<item>
<widget class="QLabel" name="label_31">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Home:</string>
<string>Up:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonHome">
<widget class="QPushButton" name="buttonLStickUp">
<property name="text">
<string/>
</property>
@@ -517,17 +638,35 @@
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_28">
<item row="3" column="0">
<layout class="QVBoxLayout" name="verticalLayout_31">
<item>
<widget class="QLabel" name="label_36">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Circle Mod:</string>
<string>Modifier:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCircleMod">
<widget class="QPushButton" name="buttonLStickMod">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="1">
<layout class="QVBoxLayout" name="verticalLayout_7" stretch="0,0">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Pressed:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonLStick">
<property name="text">
<string/>
</property>

View File

@@ -185,12 +185,24 @@ void GMainWindow::InitializeRecentFileMenuActions() {
void GMainWindow::InitializeHotkeys() {
RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
RegisterHotkey("Main Window", "Start Emulation");
RegisterHotkey( "Main Window", "Fullscreen", QKeySequence::FullScreen );
RegisterHotkey( "Main Window", "Exit Fullscreen", QKeySequence::Cancel, Qt::ApplicationShortcut );
LoadHotkeys();
connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this,
SLOT(OnMenuLoadFile()));
connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this,
SLOT(OnStartGame()));
connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated,
ui.action_Fullscreen, &QAction::trigger);
connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously,
ui.action_Fullscreen, &QAction::trigger);
connect(GetHotkey("Main Window", "Exit Fullscreen", this), &QShortcut::activated, this, [&] {
if (emulation_running) {
ui.action_Fullscreen->setChecked(false);
ToggleFullscreen();
}
});
}
void GMainWindow::SetDefaultUIGeometry() {
@@ -219,6 +231,8 @@ void GMainWindow::RestoreUIState() {
ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode);
ToggleWindowMode();
ui.action_Fullscreen->setChecked(UISettings::values.fullscreen);
ui.action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar);
OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked());
@@ -263,6 +277,10 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar);
connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
// Fullscreen
ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key());
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
// Help
connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
}
@@ -402,6 +420,9 @@ void GMainWindow::BootGame(const QString& filename) {
render_window->setFocus();
emulation_running = true;
if (ui.action_Fullscreen->isChecked()) {
ShowFullscreen();
}
OnStartGame();
}
@@ -548,6 +569,41 @@ void GMainWindow::OnStopGame() {
ShutdownGame();
}
void GMainWindow::ToggleFullscreen() {
if (!emulation_running) {
return;
}
if (ui.action_Fullscreen->isChecked()) {
ShowFullscreen();
} else {
HideFullscreen();
}
}
void GMainWindow::ShowFullscreen() {
if (ui.action_Single_Window_Mode->isChecked()) {
UISettings::values.geometry = saveGeometry();
ui.menubar->hide();
statusBar()->hide();
showFullScreen();
} else {
UISettings::values.renderwindow_geometry = render_window->saveGeometry();
render_window->showFullScreen();
}
}
void GMainWindow::HideFullscreen() {
if (ui.action_Single_Window_Mode->isChecked()) {
statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
ui.menubar->show();
showNormal();
restoreGeometry(UISettings::values.geometry);
} else {
render_window->showNormal();
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
}
}
void GMainWindow::ToggleWindowMode() {
if (ui.action_Single_Window_Mode->isChecked()) {
// Render in the main window...
@@ -670,6 +726,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
} else {
// Only show the message if the game is still running.
if (emu_thread) {
emu_thread->SetRunning(true);
message_label->setText(status_message);
message_label->setVisible(true);
}
@@ -700,6 +757,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
UISettings::values.microprofile_visible = microProfileDialog->isVisible();
#endif
UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked();
UISettings::values.fullscreen = ui.action_Fullscreen->isChecked();
UISettings::values.display_titlebar = ui.action_Display_Dock_Widget_Headers->isChecked();
UISettings::values.show_filter_bar = ui.action_Show_Filter_Bar->isChecked();
UISettings::values.show_status_bar = ui.action_Show_Status_Bar->isChecked();

View File

@@ -127,6 +127,9 @@ private slots:
void OnAbout();
void OnToggleFilterBar();
void OnDisplayTitleBars(bool);
void ToggleFullscreen();
void ShowFullscreen();
void HideFullscreen();
void ToggleWindowMode();
void OnCoreError(Core::System::ResultStatus, std::string);

View File

@@ -83,6 +83,7 @@
<string>Debugging</string>
</property>
</widget>
<addaction name="action_Fullscreen"/>
<addaction name="action_Single_Window_Mode"/>
<addaction name="action_Display_Dock_Widget_Headers"/>
<addaction name="action_Show_Filter_Bar"/>
@@ -189,6 +190,14 @@
<string>Selects a folder to display in the game list</string>
</property>
</action>
</widget>
<action name="action_Fullscreen">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Fullscreen</string>
</property>
</action>
</widget>
<resources/>
</ui>

View File

@@ -27,6 +27,7 @@ struct Values {
bool microprofile_visible;
bool single_window_mode;
bool fullscreen;
bool display_titlebar;
bool show_filter_bar;
bool show_status_bar;

View File

@@ -30,17 +30,26 @@ button_a=
button_b=
button_x=
button_y=
button_up=
button_down=
button_left=
button_right=
button_lstick=
button_rstick=
button_l=
button_r=
button_start=
button_select=
button_zl=
button_zr=
button_plus=
button_minus=
button_dleft=
button_dup=
button_dright=
button_ddown=
button_lstick_left=
button_lstick_up=
button_lstick_right=
button_lstick_down=
button_sl=
button_sr=
button_home=
button_screenshot=
# for analog input, the following devices are available:
# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
@@ -53,8 +62,8 @@ button_home=
# - "joystick": the index of the joystick to bind
# - "axis_x": the index of the axis to bind as x-axis (default to 0)
# - "axis_y": the index of the axis to bind as y-axis (default to 1)
circle_pad=
c_stick=
lstick=
rstick=
# for motion input, the following devices are available:
# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters:
@@ -81,8 +90,8 @@ use_hw_renderer =
use_shader_jit =
# Resolution scale factor
# 0: Auto (scales resolution to window size), 1: Native 3DS screen resolution, Otherwise a scale
# factor for the 3DS resolution
# 0: Auto (scales resolution to window size), 1: Native Switch screen resolution, Otherwise a scale
# factor for the Switch resolution
resolution_factor =
# Whether to enable V-Sync (caps the framerate at 60FPS) or not.