summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/citra/config.cpp7
-rw-r--r--src/citra/default_ini.h19
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp7
-rw-r--r--src/citra_qt/CMakeLists.txt9
-rw-r--r--src/citra_qt/bootmanager.cpp7
-rw-r--r--src/citra_qt/config.cpp8
-rw-r--r--src/citra_qt/config/controller_config.cpp95
-rw-r--r--src/citra_qt/config/controller_config.h56
-rw-r--r--src/citra_qt/config/controller_config.ui308
-rw-r--r--src/citra_qt/config/controller_config_util.cpp125
-rw-r--r--src/citra_qt/config/controller_config_util.h82
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp125
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.h22
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.cpp356
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.h76
-rw-r--r--src/citra_qt/debugger/graphics_surface.cpp736
-rw-r--r--src/citra_qt/debugger/graphics_surface.h120
-rw-r--r--src/citra_qt/main.cpp19
-rw-r--r--src/citra_qt/main.h1
-rw-r--r--src/common/emu_window.cpp24
-rw-r--r--src/common/emu_window.h46
-rw-r--r--src/common/key_map.cpp126
-rw-r--r--src/common/key_map.h59
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/gdbstub/gdbstub.cpp2
-rw-r--r--src/core/hle/service/dlp/dlp.cpp24
-rw-r--r--src/core/hle/service/dlp/dlp.h15
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.cpp20
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.h22
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.cpp20
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.h22
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.cpp (renamed from src/core/hle/service/dlp_srvr.cpp)22
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.h (renamed from src/core/hle/service/dlp_srvr.h)13
-rw-r--r--src/core/hle/service/hid/hid.cpp70
-rw-r--r--src/core/hle/service/hid/hid.h3
-rw-r--r--src/core/hle/service/service.cpp5
-rw-r--r--src/core/settings.h22
37 files changed, 1349 insertions, 1354 deletions
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index c64de8e22..22cb51ea8 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -44,12 +44,16 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
}
static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
+ // directly mapped keys
SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X,
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2,
SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B,
SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H,
+ SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L,
+
+ // indirectly mapped keys
SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,
- SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L
+ SDL_SCANCODE_D,
};
void Config::ReadValues() {
@@ -58,6 +62,7 @@ void Config::ReadValues() {
Settings::values.input_mappings[Settings::NativeInput::All[i]] =
sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
}
+ Settings::values.pad_circle_modifier_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
// Core
Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0);
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 49126356f..4e63f3206 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -23,14 +23,19 @@ pad_l =
pad_r =
pad_zl =
pad_zr =
-pad_sup =
-pad_sdown =
-pad_sleft =
-pad_sright =
pad_cup =
pad_cdown =
pad_cleft =
pad_cright =
+pad_circle_up =
+pad_circle_down =
+pad_circle_left =
+pad_circle_right =
+pad_circle_modifier =
+
+# The applied modifier scale to circle pad.
+# Must be in range of 0.0-1.0. Defaults to 0.5
+pad_circle_modifier_scale =
[Core]
# The applied frameskip amount. Must be a power of two.
@@ -66,7 +71,11 @@ output_engine =
# 1 (default): Yes, 0: No
use_virtual_sd =
-[System Region]
+[System]
+# The system model that Citra will try to emulate
+# 0: Old 3DS (default), 1: New 3DS
+is_new_3ds =
+
# The system region that Citra will use during emulation
# 0: Japan, 1: USA (default), 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
region_value =
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
index 12cdd9d95..591f68aa4 100644
--- a/src/citra/emu_window/emu_window_sdl2.cpp
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -40,9 +40,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
if (state == SDL_PRESSED) {
- KeyPressed({ key, keyboard_id });
+ KeyMap::PressKey(*this, { key, keyboard_id });
} else if (state == SDL_RELEASED) {
- KeyReleased({ key, keyboard_id });
+ KeyMap::ReleaseKey(*this, { key, keyboard_id });
}
}
@@ -168,8 +168,9 @@ void EmuWindow_SDL2::DoneCurrent() {
}
void EmuWindow_SDL2::ReloadSetKeymaps() {
+ KeyMap::ClearKeyMapping(keyboard_id);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, Service::HID::pad_mapping[i]);
+ KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]);
}
}
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 0a5d4624b..43a766053 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -2,8 +2,6 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(SRCS
- config/controller_config.cpp
- config/controller_config_util.cpp
config.cpp
debugger/callstack.cpp
debugger/disassembler.cpp
@@ -11,7 +9,7 @@ set(SRCS
debugger/graphics_breakpoint_observer.cpp
debugger/graphics_breakpoints.cpp
debugger/graphics_cmdlists.cpp
- debugger/graphics_framebuffer.cpp
+ debugger/graphics_surface.cpp
debugger/graphics_tracing.cpp
debugger/graphics_vertex_shader.cpp
debugger/profiler.cpp
@@ -33,8 +31,6 @@ set(SRCS
)
set(HEADERS
- config/controller_config.h
- config/controller_config_util.h
config.h
debugger/callstack.h
debugger/disassembler.h
@@ -43,7 +39,7 @@ set(HEADERS
debugger/graphics_breakpoints.h
debugger/graphics_breakpoints_p.h
debugger/graphics_cmdlists.h
- debugger/graphics_framebuffer.h
+ debugger/graphics_surface.h
debugger/graphics_tracing.h
debugger/graphics_vertex_shader.h
debugger/profiler.h
@@ -65,7 +61,6 @@ set(HEADERS
)
set(UIS
- config/controller_config.ui
debugger/callstack.ui
debugger/disassembler.ui
debugger/profiler.ui
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 01b81c11c..414b2f8af 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -235,12 +235,12 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
void GRenderWindow::keyPressEvent(QKeyEvent* event)
{
- this->KeyPressed({event->key(), keyboard_id});
+ KeyMap::PressKey(*this, { event->key(), keyboard_id });
}
void GRenderWindow::keyReleaseEvent(QKeyEvent* event)
{
- this->KeyReleased({event->key(), keyboard_id});
+ KeyMap::ReleaseKey(*this, { event->key(), keyboard_id });
}
void GRenderWindow::mousePressEvent(QMouseEvent *event)
@@ -270,8 +270,9 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent *event)
void GRenderWindow::ReloadSetKeymaps()
{
+ KeyMap::ClearKeyMapping(keyboard_id);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- KeyMap::SetKeyMapping({Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, Service::HID::pad_mapping[i]);
+ KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]);
}
}
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 6e4ba3907..ba7edaff9 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -22,12 +22,16 @@ Config::Config() {
}
static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults = {
+ // directly mapped keys
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X,
Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2,
Qt::Key_M, Qt::Key_N, Qt::Key_B,
Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
+ Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L,
+
+ // indirectly mapped keys
Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right,
- Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L
+ Qt::Key_D,
};
void Config::ReadValues() {
@@ -36,6 +40,7 @@ void Config::ReadValues() {
Settings::values.input_mappings[Settings::NativeInput::All[i]] =
qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]).toInt();
}
+ Settings::values.pad_circle_modifier_scale = qt_config->value("pad_circle_modifier_scale", 0.5).toFloat();
qt_config->endGroup();
qt_config->beginGroup("Core");
@@ -126,6 +131,7 @@ void Config::SaveValues() {
qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]),
Settings::values.input_mappings[Settings::NativeInput::All[i]]);
}
+ qt_config->setValue("pad_circle_modifier_scale", (double)Settings::values.pad_circle_modifier_scale);
qt_config->endGroup();
qt_config->beginGroup("Core");
diff --git a/src/citra_qt/config/controller_config.cpp b/src/citra_qt/config/controller_config.cpp
deleted file mode 100644
index 512879f1b..000000000
--- a/src/citra_qt/config/controller_config.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <QDialogButtonBox>
-
-#include "controller_config.h"
-#include "controller_config_util.h"
-
-/* TODO(bunnei): ImplementMe
-
-using common::Config;
-
-GControllerConfig::GControllerConfig(common::Config::ControllerPort* initial_config, QWidget* parent) : QWidget(parent)
-{
- ui.setupUi(this);
- ((QGridLayout*)ui.mainStickTab->layout())->addWidget(new GStickConfig(Config::ANALOG_LEFT, Config::ANALOG_RIGHT, Config::ANALOG_UP, Config::ANALOG_DOWN, this, this), 1, 1);
- ((QGridLayout*)ui.cStickTab->layout())->addWidget(new GStickConfig(Config::C_LEFT, Config::C_RIGHT, Config::C_UP, Config::C_DOWN, this, this), 1, 1);
- ((QGridLayout*)ui.dPadTab->layout())->addWidget(new GStickConfig(Config::DPAD_LEFT, Config::DPAD_RIGHT, Config::DPAD_UP, Config::DPAD_DOWN, this, this), 1, 1);
-
- // TODO: Arrange these more compactly?
- QVBoxLayout* layout = (QVBoxLayout*)ui.buttonsTab->layout();
- layout->addWidget(new GButtonConfigGroup("A Button", Config::BUTTON_A, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("B Button", Config::BUTTON_B, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("X Button", Config::BUTTON_X, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("Y Button", Config::BUTTON_Y, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("Z Button", Config::BUTTON_Z, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("L Trigger", Config::TRIGGER_L, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("R Trigger", Config::TRIGGER_R, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("Start Button", Config::BUTTON_START, this, ui.buttonsTab));
-
- memcpy(config, initial_config, sizeof(config));
-
- emit ActivePortChanged(config[0]);
-}
-
-void GControllerConfig::OnKeyConfigChanged(common::Config::Control id, int key, const QString& name)
-{
- if (InputSourceJoypad())
- {
- config[GetActiveController()].pads.key_code[id] = key;
- }
- else
- {
- config[GetActiveController()].keys.key_code[id] = key;
- }
- emit ActivePortChanged(config[GetActiveController()]);
-}
-
-int GControllerConfig::GetActiveController()
-{
- return ui.activeControllerCB->currentIndex();
-}
-
-bool GControllerConfig::InputSourceJoypad()
-{
- return ui.inputSourceCB->currentIndex() == 1;
-}
-
-GControllerConfigDialog::GControllerConfigDialog(common::Config::ControllerPort* controller_ports, QWidget* parent) : QDialog(parent), config_ptr(controller_ports)
-{
- setWindowTitle(tr("Input configuration"));
-
- QVBoxLayout* layout = new QVBoxLayout(this);
- config_widget = new GControllerConfig(controller_ports, this);
- layout->addWidget(config_widget);
-
- QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- layout->addWidget(buttons);
-
- connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
- connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
-
- connect(this, SIGNAL(accepted()), this, SLOT(EnableChanges()));
-
- layout->setSizeConstraint(QLayout::SetFixedSize);
- setLayout(layout);
- setModal(true);
- show();
-}
-
-void GControllerConfigDialog::EnableChanges()
-{
- for (unsigned int i = 0; i < 4; ++i)
- {
- memcpy(&config_ptr[i], &config_widget->GetControllerConfig(i), sizeof(common::Config::ControllerPort));
-
- if (common::g_config) {
- // Apply changes if running a game
- memcpy(&common::g_config->controller_ports(i), &config_widget->GetControllerConfig(i), sizeof(common::Config::ControllerPort));
- }
- }
-}
-
-*/
diff --git a/src/citra_qt/config/controller_config.h b/src/citra_qt/config/controller_config.h
deleted file mode 100644
index 451593de1..000000000
--- a/src/citra_qt/config/controller_config.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#ifndef _CONTROLLER_CONFIG_HXX_
-#define _CONTROLLER_CONFIG_HXX_
-
-#include <QDialog>
-
-//#include "ui_controller_config.h"
-
-/* TODO(bunnei): ImplementMe
-
-#include "config.h"
-
-class GControllerConfig : public QWidget
-{
- Q_OBJECT
-
-public:
- GControllerConfig(common::Config::ControllerPort* initial_config, QWidget* parent = NULL);
-
- const common::Config::ControllerPort& GetControllerConfig(int index) const { return config[index]; }
-
-signals:
- void ActivePortChanged(const common::Config::ControllerPort&);
-
-public slots:
- void OnKeyConfigChanged(common::Config::Control id, int key, const QString& name);
-
-private:
- int GetActiveController();
- bool InputSourceJoypad();
-
- Ui::ControllerConfig ui;
- common::Config::ControllerPort config[4];
-};
-
-class GControllerConfigDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- GControllerConfigDialog(common::Config::ControllerPort* controller_ports, QWidget* parent = NULL);
-
-public slots:
- void EnableChanges();
-
-private:
- GControllerConfig* config_widget;
- common::Config::ControllerPort* config_ptr;
-};
-
-*/
-
-#endif // _CONTROLLER_CONFIG_HXX_
diff --git a/src/citra_qt/config/controller_config.ui b/src/citra_qt/config/controller_config.ui
deleted file mode 100644
index 9f650047b..000000000
--- a/src/citra_qt/config/controller_config.ui
+++ /dev/null
@@ -1,308 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>ControllerConfig</class>
- <widget class="QWidget" name="ControllerConfig">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>503</width>
- <height>293</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="windowTitle">
- <string>Controller Configuration</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="sizeConstraint">
- <enum>QLayout::SetFixedSize</enum>
- </property>
- <item>
- <layout class="QGridLayout" name="gridLayout">
- <item row="1" column="1">
- <widget class="QComboBox" name="activeControllerCB">
- <item>
- <property name="text">
- <string>Controller 1</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Controller 2</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Controller 3</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Controller 4</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="0" column="2">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="2">
- <widget class="QCheckBox" name="checkBox">
- <property name="text">
- <string>Enabled</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="inputSourceCB">
- <item>
- <property name="text">
- <string>Keyboard</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Joypad</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Active Controller:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Input Source:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QTabWidget" name="tabWidget">
- <property name="currentIndex">
- <number>0</number>
- </property>
- <widget class="QWidget" name="mainStickTab">
- <attribute name="title">
- <string>Main Stick</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="2" column="2">
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="2">
- <spacer name="verticalSpacer_3">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="0">
- <spacer name="horizontalSpacer_4">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="4">
- <spacer name="horizontalSpacer_3">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="cStickTab">
- <attribute name="title">
- <string>C-Stick</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_4">
- <item row="1" column="0">
- <spacer name="horizontalSpacer_6">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="1">
- <spacer name="verticalSpacer_5">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="2" column="1">
- <spacer name="verticalSpacer_4">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="2">
- <spacer name="horizontalSpacer_5">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="dPadTab">
- <attribute name="title">
- <string>D-Pad</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_5">
- <item row="1" column="2">
- <spacer name="horizontalSpacer_7">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="1">
- <spacer name="verticalSpacer_7">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="2" column="1">
- <spacer name="verticalSpacer_6">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="0">
- <spacer name="horizontalSpacer_8">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="buttonsTab">
- <attribute name="title">
- <string>Buttons</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_2"/>
- </widget>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
- <slots>
- <signal>ControlsChanged()</signal>
- <signal>MainStickCleared()</signal>
- <signal>CStickCleared()</signal>
- <signal>DPadCleared()</signal>
- <signal>ButtonsCleared()</signal>
- <slot>OnControlsChanged()</slot>
- <slot>OnMainStickCleared()</slot>
- <slot>OnCStickCleared()</slot>
- <slot>OnDPadCleared()</slot>
- <slot>OnButtonsCleared()</slot>
- </slots>
-</ui>
diff --git a/src/citra_qt/config/controller_config_util.cpp b/src/citra_qt/config/controller_config_util.cpp
deleted file mode 100644
index d68b119df..000000000
--- a/src/citra_qt/config/controller_config_util.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <QPushButton>
-#include <QStyle>
-#include <QGridLayout>
-#include <QKeyEvent>
-#include <QHBoxLayout>
-#include <QLabel>
-
-#include "controller_config_util.h"
-
-/* TODO(bunnei): ImplementMe
-GStickConfig::GStickConfig(common::Config::Control leftid, common::Config::Control rightid, common::Config::Control upid, common::Config::Control downid, QObject* change_receiver, QWidget* parent) : QWidget(parent)
-{
- left = new GKeyConfigButton(leftid, style()->standardIcon(QStyle::SP_ArrowLeft), QString(), change_receiver, this);
- right = new GKeyConfigButton(rightid, style()->standardIcon(QStyle::SP_ArrowRight), QString(), change_receiver, this);
- up = new GKeyConfigButton(upid, style()->standardIcon(QStyle::SP_ArrowUp), QString(), change_receiver, this);
- down = new GKeyConfigButton(downid, style()->standardIcon(QStyle::SP_ArrowDown), QString(), change_receiver, this);
- clear = new QPushButton(tr("Clear"), this);
-
- QGridLayout* layout = new QGridLayout(this);
- layout->addWidget(left, 1, 0);
- layout->addWidget(right, 1, 2);
- layout->addWidget(up, 0, 1);
- layout->addWidget(down, 2, 1);
- layout->addWidget(clear, 1, 1);
-
- setLayout(layout);
-}
-
-GKeyConfigButton::GKeyConfigButton(common::Config::Control id, const QIcon& icon, const QString& text, QObject* change_receiver, QWidget* parent) : QPushButton(icon, text, parent), id(id), inputGrabbed(false)
-{
- connect(this, SIGNAL(clicked()), this, SLOT(OnClicked()));
- connect(this, SIGNAL(KeyAssigned(common::Config::Control, int, const QString&)), change_receiver, SLOT(OnKeyConfigChanged(common::Config::Control, int, const QString&)));
- connect(change_receiver, SIGNAL(ActivePortChanged(const common::Config::ControllerPort&)), this, SLOT(OnActivePortChanged(const common::Config::ControllerPort&)));
-}
-
-GKeyConfigButton::GKeyConfigButton(common::Config::Control id, const QString& text, QObject* change_receiver, QWidget* parent) : QPushButton(text, parent), id(id), inputGrabbed(false)
-{
- connect(this, SIGNAL(clicked()), this, SLOT(OnClicked()));
- connect(this, SIGNAL(KeyAssigned(common::Config::Control, int, const QString&)), change_receiver, SLOT(OnKeyConfigChanged(common::Config::Control, int, const QString&)));
- connect(change_receiver, SIGNAL(ActivePortChanged(const common::Config::ControllerPort&)), this, SLOT(OnActivePortChanged(const common::Config::ControllerPort&)));
-}
-
-void GKeyConfigButton::OnActivePortChanged(const common::Config::ControllerPort& config)
-{
- // TODO: Doesn't use joypad struct if that's the input source...
- QString text = QKeySequence(config.keys.key_code[id]).toString(); // has a nicer format
- if (config.keys.key_code[id] == Qt::Key_Shift) text = tr("Shift");
- else if (config.keys.key_code[id] == Qt::Key_Control) text = tr("Control");
- else if (config.keys.key_code[id] == Qt::Key_Alt) text = tr("Alt");
- else if (config.keys.key_code[id] == Qt::Key_Meta) text = tr("Meta");
- setText(text);
-}
-
-void GKeyConfigButton::OnClicked()
-{
- grabKeyboard();
- grabMouse();
- inputGrabbed = true;
-
- old_text = text();
- setText(tr("Input..."));
-}
-
-void GKeyConfigButton::keyPressEvent(QKeyEvent* event)
-{
- if (inputGrabbed)
- {
- releaseKeyboard();
- releaseMouse();
- setText(QString());
-
- // TODO: Doesn't capture "return" key
- // TODO: This doesn't quite work well, yet... find a better way
- QString text = QKeySequence(event->key()).toString(); // has a nicer format than event->text()
- int key = event->key();
- if (event->modifiers() == Qt::ShiftModifier) { text = tr("Shift"); key = Qt::Key_Shift; }
- else if (event->modifiers() == Qt::ControlModifier) { text = tr("Ctrl"); key = Qt::Key_Control; }
- else if (event->modifiers() == Qt::AltModifier) { text = tr("Alt"); key = Qt::Key_Alt; }
- else if (event->modifiers() == Qt::MetaModifier) { text = tr("Meta"); key = Qt::Key_Meta; }
-
- setText(old_text);
- emit KeyAssigned(id, key, text);
-
- inputGrabbed = false;
-
- // TODO: Keys like "return" cause another keyPressEvent to be generated after this one...
- }
-
- QPushButton::keyPressEvent(event); // TODO: Necessary?
-}
-
-void GKeyConfigButton::mousePressEvent(QMouseEvent* event)
-{
- // Abort key assignment
- if (inputGrabbed)
- {
- releaseKeyboard();
- releaseMouse();
- setText(old_text);
- inputGrabbed = false;
- }
-
- QAbstractButton::mousePressEvent(event);
-}
-
-GButtonConfigGroup::GButtonConfigGroup(const QString& name, common::Config::Control id, QObject* change_receiver, QWidget* parent) : QWidget(parent), id(id)
-{
- QHBoxLayout* layout = new QHBoxLayout(this);
-
- QPushButton* clear_button = new QPushButton(tr("Clear"));
-
- layout->addWidget(new QLabel(name, this));
- layout->addWidget(config_button = new GKeyConfigButton(id, QString(), change_receiver, this));
- layout->addWidget(clear_button);
-
- // TODO: connect config_button, clear_button
-
- setLayout(layout);
-}
-
-*/
diff --git a/src/citra_qt/config/controller_config_util.h b/src/citra_qt/config/controller_config_util.h
deleted file mode 100644
index 15e025b57..000000000
--- a/src/citra_qt/config/controller_config_util.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#ifndef _CONTROLLER_CONFIG_UTIL_HXX_
-#define _CONTROLLER_CONFIG_UTIL_HXX_
-
-#include <QWidget>
-#include <QPushButton>
-
-/* TODO(bunnei): ImplementMe
-
-#include "config.h"
-
-class GStickConfig : public QWidget
-{
- Q_OBJECT
-
-public:
- // change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
- GStickConfig(common::Config::Control leftid, common::Config::Control rightid, common::Config::Control upid, common::Config::Control downid, QObject* change_receiver, QWidget* parent = NULL);
-
-signals:
- void LeftChanged();
- void RightChanged();
- void UpChanged();
- void DownChanged();
-
-private:
- QPushButton* left;
- QPushButton* right;
- QPushButton* up;
- QPushButton* down;
-
- QPushButton* clear;
-};
-
-class GKeyConfigButton : public QPushButton
-{
- Q_OBJECT
-
-public:
- // TODO: change_receiver also needs to have an ActivePortChanged(const common::Config::ControllerPort&) signal
- // change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
- GKeyConfigButton(common::Config::Control id, const QIcon& icon, const QString& text, QObject* change_receiver, QWidget* parent);
- GKeyConfigButton(common::Config::Control id, const QString& text, QObject* change_receiver, QWidget* parent);
-
-signals:
- void KeyAssigned(common::Config::Control id, int key, const QString& text);
-
-private slots:
- void OnActivePortChanged(const common::Config::ControllerPort& config);
-
- void OnClicked();
-
- void keyPressEvent(QKeyEvent* event); // TODO: bGrabbed?
- void mousePressEvent(QMouseEvent* event);
-
-private:
- common::Config::Control id;
- bool inputGrabbed;
-
- QString old_text;
-};
-
-class GButtonConfigGroup : public QWidget
-{
- Q_OBJECT
-
-public:
- // change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
- GButtonConfigGroup(const QString& name, common::Config::Control id, QObject* change_receiver, QWidget* parent = NULL);
-
-private:
- GKeyConfigButton* config_button;
-
- common::Config::Control id;
-};
-
-*/
-
-#endif // _CONTROLLER_CONFIG_HXX_
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
index 5186d2b44..3e0a0a145 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -50,123 +50,6 @@ public:
}
};
-TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent)
- : QDockWidget(tr("Texture 0x%1").arg(info.physical_address, 8, 16, QLatin1Char('0'))),
- info(info) {
-
- QWidget* main_widget = new QWidget;
-
- QLabel* image_widget = new QLabel;
-
- connect(this, SIGNAL(UpdatePixmap(const QPixmap&)), image_widget, SLOT(setPixmap(const QPixmap&)));
-
- CSpinBox* phys_address_spinbox = new CSpinBox;
- phys_address_spinbox->SetBase(16);
- phys_address_spinbox->SetRange(0, 0xFFFFFFFF);
- phys_address_spinbox->SetPrefix("0x");
- phys_address_spinbox->SetValue(info.physical_address);
- connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64)));
-
- QComboBox* format_choice = new QComboBox;
- format_choice->addItem(tr("RGBA8"));
- format_choice->addItem(tr("RGB8"));
- format_choice->addItem(tr("RGB5A1"));
- format_choice->addItem(tr("RGB565"));
- format_choice->addItem(tr("RGBA4"));
- format_choice->addItem(tr("IA8"));
- format_choice->addItem(tr("RG8"));
- format_choice->addItem(tr("I8"));
- format_choice->addItem(tr("A8"));
- format_choice->addItem(tr("IA4"));
- format_choice->addItem(tr("I4"));
- format_choice->addItem(tr("A4"));
- format_choice->addItem(tr("ETC1"));
- format_choice->addItem(tr("ETC1A4"));
- format_choice->setCurrentIndex(static_cast<int>(info.format));
- connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int)));
-
- QSpinBox* width_spinbox = new QSpinBox;
- width_spinbox->setMaximum(65535);
- width_spinbox->setValue(info.width);
- connect(width_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnWidthChanged(int)));
-
- QSpinBox* height_spinbox = new QSpinBox;
- height_spinbox->setMaximum(65535);
- height_spinbox->setValue(info.height);
- connect(height_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnHeightChanged(int)));
-
- QSpinBox* stride_spinbox = new QSpinBox;
- stride_spinbox->setMaximum(65535 * 4);
- stride_spinbox->setValue(info.stride);
- connect(stride_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnStrideChanged(int)));
-
- QVBoxLayout* main_layout = new QVBoxLayout;
- main_layout->addWidget(image_widget);
-
- {
- QHBoxLayout* sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Source Address:")));
- sub_layout->addWidget(phys_address_spinbox);
- main_layout->addLayout(sub_layout);
- }
-
- {
- QHBoxLayout* sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Format")));
- sub_layout->addWidget(format_choice);
- main_layout->addLayout(sub_layout);
- }
-
- {
- QHBoxLayout* sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Width:")));
- sub_layout->addWidget(width_spinbox);
- sub_layout->addStretch();
- sub_layout->addWidget(new QLabel(tr("Height:")));
- sub_layout->addWidget(height_spinbox);
- sub_layout->addStretch();
- sub_layout->addWidget(new QLabel(tr("Stride:")));
- sub_layout->addWidget(stride_spinbox);
- main_layout->addLayout(sub_layout);
- }
-
- main_widget->setLayout(main_layout);
-
- emit UpdatePixmap(ReloadPixmap());
-
- setWidget(main_widget);
-}
-
-void TextureInfoDockWidget::OnAddressChanged(qint64 value) {
- info.physical_address = value;
- emit UpdatePixmap(ReloadPixmap());
-}
-
-void TextureInfoDockWidget::OnFormatChanged(int value) {
- info.format = static_cast<Pica::Regs::TextureFormat>(value);
- emit UpdatePixmap(ReloadPixmap());
-}
-
-void TextureInfoDockWidget::OnWidthChanged(int value) {
- info.width = value;
- emit UpdatePixmap(ReloadPixmap());
-}
-
-void TextureInfoDockWidget::OnHeightChanged(int value) {
- info.height = value;
- emit UpdatePixmap(ReloadPixmap());
-}
-
-void TextureInfoDockWidget::OnStrideChanged(int value) {
- info.stride = value;
- emit UpdatePixmap(ReloadPixmap());
-}
-
-QPixmap TextureInfoDockWidget::ReloadPixmap() const {
- u8* src = Memory::GetPhysicalPointer(info.physical_address);
- return QPixmap::fromImage(LoadTexture(src, info));
-}
-
GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {
}
@@ -249,16 +132,16 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
index = 0;
} else if (COMMAND_IN_RANGE(command_id, texture1)) {
index = 1;
- } else {
+ } else if (COMMAND_IN_RANGE(command_id, texture2)) {
index = 2;
+ } else {
+ UNREACHABLE_MSG("Unknown texture command");
}
auto config = Pica::g_state.regs.GetTextures()[index].config;
auto format = Pica::g_state.regs.GetTextures()[index].format;
auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format);
- // TODO: Instead, emit a signal here to be caught by the main window widget.
- auto main_window = static_cast<QMainWindow*>(parent());
- main_window->tabifyDockWidget(this, new TextureInfoDockWidget(info, main_window));
+ // TODO: Open a surface debugger
}
}
diff --git a/src/citra_qt/debugger/graphics_cmdlists.h b/src/citra_qt/debugger/graphics_cmdlists.h
index 586cc7239..8a2a294b9 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.h
+++ b/src/citra_qt/debugger/graphics_cmdlists.h
@@ -61,25 +61,3 @@ private:
QWidget* command_info_widget;
QPushButton* toggle_tracing;
};
-
-class TextureInfoDockWidget : public QDockWidget {
- Q_OBJECT
-
-public:
- TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr);
-
-signals:
- void UpdatePixmap(const QPixmap& pixmap);
-
-private slots:
- void OnAddressChanged(qint64 value);
- void OnFormatChanged(int value);
- void OnWidthChanged(int value);
- void OnHeightChanged(int value);
- void OnStrideChanged(int value);
-
-private:
- QPixmap ReloadPixmap() const;
-
- Pica::DebugUtils::TextureInfo info;
-};
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp
deleted file mode 100644
index 68cff78b2..000000000
--- a/src/citra_qt/debugger/graphics_framebuffer.cpp
+++ /dev/null
@@ -1,356 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <QBoxLayout>
-#include <QComboBox>
-#include <QDebug>
-#include <QLabel>
-#include <QPushButton>
-#include <QSpinBox>
-
-#include "citra_qt/debugger/graphics_framebuffer.h"
-#include "citra_qt/util/spinbox.h"
-
-#include "common/color.h"
-
-#include "core/memory.h"
-#include "core/hw/gpu.h"
-
-#include "video_core/pica.h"
-#include "video_core/pica_state.h"
-#include "video_core/utils.h"
-
-GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context,
- QWidget* parent)
- : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent),
- framebuffer_source(Source::PicaTarget)
-{
- setObjectName("PicaFramebuffer");
-
- framebuffer_source_list = new QComboBox;
- framebuffer_source_list->addItem(tr("Active Render Target"));
- framebuffer_source_list->addItem(tr("Active Depth Buffer"));
- framebuffer_source_list->addItem(tr("Custom"));
- framebuffer_source_list->setCurrentIndex(static_cast<int>(framebuffer_source));
-
- framebuffer_address_control = new CSpinBox;
- framebuffer_address_control->SetBase(16);
- framebuffer_address_control->SetRange(0, 0xFFFFFFFF);
- framebuffer_address_control->SetPrefix("0x");
-
- framebuffer_width_control = new QSpinBox;
- framebuffer_width_control->setMinimum(1);
- framebuffer_width_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
-
- framebuffer_height_control = new QSpinBox;
- framebuffer_height_control->setMinimum(1);
- framebuffer_height_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
-
- framebuffer_format_control = new QComboBox;
- framebuffer_format_control->addItem(tr("RGBA8"));
- framebuffer_format_control->addItem(tr("RGB8"));
- framebuffer_format_control->addItem(tr("RGB5A1"));
- framebuffer_format_control->addItem(tr("RGB565"));
- framebuffer_format_control->addItem(tr("RGBA4"));
- framebuffer_format_control->addItem(tr("D16"));
- framebuffer_format_control->addItem(tr("D24"));
- framebuffer_format_control->addItem(tr("D24X8"));
- framebuffer_format_control->addItem(tr("X24S8"));
- framebuffer_format_control->addItem(tr("(unknown)"));
-
- // TODO: This QLabel should shrink the image to the available space rather than just expanding...
- framebuffer_picture_label = new QLabel;
-
- auto enlarge_button = new QPushButton(tr("Enlarge"));
-
- // Connections
- connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
- connect(framebuffer_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferSourceChanged(int)));
- connect(framebuffer_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnFramebufferAddressChanged(qint64)));
- connect(framebuffer_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferWidthChanged(int)));
- connect(framebuffer_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferHeightChanged(int)));
- connect(framebuffer_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferFormatChanged(int)));
-
- auto main_widget = new QWidget;
- auto main_layout = new QVBoxLayout;
- {
- auto sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Source:")));
- sub_layout->addWidget(framebuffer_source_list);
- main_layout->addLayout(sub_layout);
- }
- {
- auto sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Virtual Address:")));
- sub_layout->addWidget(framebuffer_address_control);
- main_layout->addLayout(sub_layout);
- }
- {
- auto sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Width:")));
- sub_layout->addWidget(framebuffer_width_control);
- main_layout->addLayout(sub_layout);
- }
- {
- auto sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Height:")));
- sub_layout->addWidget(framebuffer_height_control);
- main_layout->addLayout(sub_layout);
- }
- {
- auto sub_layout = new QHBoxLayout;
- sub_layout->addWidget(new QLabel(tr("Format:")));
- sub_layout->addWidget(framebuffer_format_control);
- main_layout->addLayout(sub_layout);
- }
- main_layout->addWidget(framebuffer_picture_label);
- main_layout->addWidget(enlarge_button);
- main_widget->setLayout(main_layout);
- setWidget(main_widget);
-
- // Load current data - TODO: Make sure this works when emulation is not running
- if (debug_context && debug_context->at_breakpoint)
- emit Update();
- widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint
-}
-
-void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
-{
- emit Update();
- widget()->setEnabled(true);
-}
-
-void GraphicsFramebufferWidget::OnResumed()
-{
- widget()->setEnabled(false);
-}
-
-void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value)
-{
- framebuffer_source = static_cast<Source>(new_value);
- emit Update();
-}
-
-void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value)
-{
- if (framebuffer_address != new_value) {
- framebuffer_address = static_cast<unsigned>(new_value);
-
- framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
- emit Update();
- }
-}
-
-void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value)
-{
- if (framebuffer_width != static_cast<unsigned>(new_value)) {
- framebuffer_width = static_cast<unsigned>(new_value);
-
- framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
- emit Update();
- }
-}
-
-void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value)
-{
- if (framebuffer_height != static_cast<unsigned>(new_value)) {
- framebuffer_height = static_cast<unsigned>(new_value);
-
- framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
- emit Update();
- }
-}
-
-void GraphicsFramebufferWidget::OnFramebufferFormatChanged(int new_value)
-{
- if (framebuffer_format != static_cast<Format>(new_value)) {
- framebuffer_format = static_cast<Format>(new_value);
-
- framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
- emit Update();
- }
-}
-
-void GraphicsFramebufferWidget::OnUpdate()
-{
- QPixmap pixmap;
-
- switch (framebuffer_source) {
- case Source::PicaTarget:
- {
- // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
-
- const auto& framebuffer = Pica::g_state.regs.framebuffer;
-
- framebuffer_address = framebuffer.GetColorBufferPhysicalAddress();
- framebuffer_width = framebuffer.GetWidth();
- framebuffer_height = framebuffer.GetHeight();
-
- switch (framebuffer.color_format) {
- case Pica::Regs::ColorFormat::RGBA8:
- framebuffer_format = Format::RGBA8;
- break;
-
- case Pica::Regs::ColorFormat::RGB8:
- framebuffer_format = Format::RGB8;
- break;
-
- case Pica::Regs::ColorFormat::RGB5A1:
- framebuffer_format = Format::RGB5A1;
- break;
-
- case Pica::Regs::ColorFormat::RGB565:
- framebuffer_format = Format::RGB565;
- break;
-
- case Pica::Regs::ColorFormat::RGBA4:
- framebuffer_format = Format::RGBA4;
- break;
-
- default:
- framebuffer_format = Format::Unknown;
- break;
- }
-
- break;
- }
-
- case Source::DepthBuffer:
- {
- const auto& framebuffer = Pica::g_state.regs.framebuffer;
-
- framebuffer_address = framebuffer.GetDepthBufferPhysicalAddress();
- framebuffer_width = framebuffer.GetWidth();
- framebuffer_height = framebuffer.GetHeight();
-
- switch (framebuffer.depth_format) {
- case Pica::Regs::DepthFormat::D16:
- framebuffer_format = Format::D16;
- break;
-
- case Pica::Regs::DepthFormat::D24:
- framebuffer_format = Format::D24;
- break;
-
- case Pica::Regs::DepthFormat::D24S8:
- framebuffer_format = Format::D24X8;
- break;
-
- default:
- framebuffer_format = Format::Unknown;
- break;
- }
-
- break;
- }
-
- case Source::Custom:
- {
- // Keep user-specified values
- break;
- }
-
- default:
- qDebug() << "Unknown framebuffer source " << static_cast<int>(framebuffer_source);
- break;
- }
-
- // TODO: Implement a good way to visualize alpha components!
- // TODO: Unify this decoding code with the texture decoder
- u32 bytes_per_pixel = GraphicsFramebufferWidget::BytesPerPixel(framebuffer_format);
-
- QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
- u8* buffer = Memory::GetPhysicalPointer(framebuffer_address);
-
- for (unsigned int y = 0; y < framebuffer_height; ++y) {
- for (unsigned int x = 0; x < framebuffer_width; ++x) {
- const u32 coarse_y = y & ~7;
- u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * framebuffer_width * bytes_per_pixel;
- const u8* pixel = buffer + offset;
- Math::Vec4<u8> color = { 0, 0, 0, 0 };
-
- switch (framebuffer_format) {
- case Format::RGBA8:
- color = Color::DecodeRGBA8(pixel);
- break;
- case Format::RGB8:
- color = Color::DecodeRGB8(pixel);
- break;
- case Format::RGB5A1:
- color = Color::DecodeRGB5A1(pixel);
- break;
- case Format::RGB565:
- color = Color::DecodeRGB565(pixel);
- break;
- case Format::RGBA4:
- color = Color::DecodeRGBA4(pixel);
- break;
- case Format::D16:
- {
- u32 data = Color::DecodeD16(pixel);
- color.r() = data & 0xFF;
- color.g() = (data >> 8) & 0xFF;
- break;
- }
- case Format::D24:
- {
- u32 data = Color::DecodeD24(pixel);
- color.r() = data & 0xFF;
- color.g() = (data >> 8) & 0xFF;
- color.b() = (data >> 16) & 0xFF;
- break;
- }
- case Format::D24X8:
- {
- Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
- color.r() = data.x & 0xFF;
- color.g() = (data.x >> 8) & 0xFF;
- color.b() = (data.x >> 16) & 0xFF;
- break;
- }
- case Format::X24S8:
- {
- Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
- color.r() = color.g() = color.b() = data.y;
- break;
- }
- default:
- qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format);
- break;
- }
-
- decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
- }
- }
- pixmap = QPixmap::fromImage(decoded_image);
-
- framebuffer_address_control->SetValue(framebuffer_address);
- framebuffer_width_control->setValue(framebuffer_width);
- framebuffer_height_control->setValue(framebuffer_height);
- framebuffer_format_control->setCurrentIndex(static_cast<int>(framebuffer_format));
- framebuffer_picture_label->setPixmap(pixmap);
-}
-
-u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format format) {
- switch (format) {
- case Format::RGBA8:
- case Format::D24X8:
- case Format::X24S8:
- return 4;
- case Format::RGB8:
- case Format::D24:
- return 3;
- case Format::RGB5A1:
- case Format::RGB565:
- case Format::RGBA4:
- case Format::D16:
- return 2;
- default:
- UNREACHABLE_MSG("GraphicsFramebufferWidget::BytesPerPixel: this "
- "should not be reached as this function should "
- "be given a format which is in "
- "GraphicsFramebufferWidget::Format. Instead got %i",
- static_cast<int>(format));
- }
-}
diff --git a/src/citra_qt/debugger/graphics_framebuffer.h b/src/citra_qt/debugger/graphics_framebuffer.h
deleted file mode 100644
index 5cd96f2e9..000000000
--- a/src/citra_qt/debugger/graphics_framebuffer.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "citra_qt/debugger/graphics_breakpoint_observer.h"
-
-class QComboBox;
-class QLabel;
-class QSpinBox;
-
-class CSpinBox;
-
-class GraphicsFramebufferWidget : public BreakPointObserverDock {
- Q_OBJECT
-
- using Event = Pica::DebugContext::Event;
-
- enum class Source {
- PicaTarget = 0,
- DepthBuffer = 1,
- Custom = 2,
-
- // TODO: Add GPU framebuffer sources!
- };
-
- enum class Format {
- RGBA8 = 0,
- RGB8 = 1,
- RGB5A1 = 2,
- RGB565 = 3,
- RGBA4 = 4,
- D16 = 5,
- D24 = 6,
- D24X8 = 7,
- X24S8 = 8,
- Unknown = 9
- };
-
- static u32 BytesPerPixel(Format format);
-
-public:
- GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr);
-
-public slots:
- void OnFramebufferSourceChanged(int new_value);
- void OnFramebufferAddressChanged(qint64 new_value);
- void OnFramebufferWidthChanged(int new_value);
- void OnFramebufferHeightChanged(int new_value);
- void OnFramebufferFormatChanged(int new_value);
- void OnUpdate();
-
-private slots:
- void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
- void OnResumed() override;
-
-signals:
- void Update();
-
-private:
-
- QComboBox* framebuffer_source_list;
- CSpinBox* framebuffer_address_control;
- QSpinBox* framebuffer_width_control;
- QSpinBox* framebuffer_height_control;
- QComboBox* framebuffer_format_control;
-
- QLabel* framebuffer_picture_label;
-
- Source framebuffer_source;
- unsigned framebuffer_address;
- unsigned framebuffer_width;
- unsigned framebuffer_height;
- Format framebuffer_format;
-};
diff --git a/src/citra_qt/debugger/graphics_surface.cpp b/src/citra_qt/debugger/graphics_surface.cpp
new file mode 100644
index 000000000..ac2d6f89b
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_surface.cpp
@@ -0,0 +1,736 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QBoxLayout>
+#include <QComboBox>
+#include <QDebug>
+#include <QFileDialog>
+#include <QLabel>
+#include <QMouseEvent>
+#include <QPushButton>
+#include <QScrollArea>
+#include <QSpinBox>
+
+#include "citra_qt/debugger/graphics_surface.h"
+#include "citra_qt/util/spinbox.h"
+
+#include "common/color.h"
+
+#include "core/memory.h"
+#include "core/hw/gpu.h"
+
+#include "video_core/pica.h"
+#include "video_core/pica_state.h"
+#include "video_core/utils.h"
+
+SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) : QLabel(parent), surface_widget(surface_widget_) {}
+SurfacePicture::~SurfacePicture() {}
+
+void SurfacePicture::mousePressEvent(QMouseEvent* event)
+{
+ // Only do something while the left mouse button is held down
+ if (!(event->buttons() & Qt::LeftButton))
+ return;
+
+ if (pixmap() == nullptr)
+ return;
+
+ if (surface_widget)
+ surface_widget->Pick(event->x() * pixmap()->width() / width(),
+ event->y() * pixmap()->height() / height());
+}
+
+void SurfacePicture::mouseMoveEvent(QMouseEvent* event)
+{
+ // We also want to handle the event if the user moves the mouse while holding down the LMB
+ mousePressEvent(event);
+}
+
+
+GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
+ QWidget* parent)
+ : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent),
+ surface_source(Source::ColorBuffer)
+{
+ setObjectName("PicaSurface");
+
+ surface_source_list = new QComboBox;
+ surface_source_list->addItem(tr("Color Buffer"));
+ surface_source_list->addItem(tr("Depth Buffer"));
+ surface_source_list->addItem(tr("Stencil Buffer"));
+ surface_source_list->addItem(tr("Texture 0"));
+ surface_source_list->addItem(tr("Texture 1"));
+ surface_source_list->addItem(tr("Texture 2"));
+ surface_source_list->addItem(tr("Custom"));
+ surface_source_list->setCurrentIndex(static_cast<int>(surface_source));
+
+ surface_address_control = new CSpinBox;
+ surface_address_control->SetBase(16);
+ surface_address_control->SetRange(0, 0xFFFFFFFF);
+ surface_address_control->SetPrefix("0x");
+
+ unsigned max_dimension = 16384; // TODO: Find actual maximum
+
+ surface_width_control = new QSpinBox;
+ surface_width_control->setRange(0, max_dimension);
+
+ surface_height_control = new QSpinBox;
+ surface_height_control->setRange(0, max_dimension);
+
+ surface_picker_x_control = new QSpinBox;
+ surface_picker_x_control->setRange(0, max_dimension - 1);
+
+ surface_picker_y_control = new QSpinBox;
+ surface_picker_y_control->setRange(0, max_dimension - 1);
+
+ surface_format_control = new QComboBox;
+
+ // Color formats sorted by Pica texture format index
+ surface_format_control->addItem(tr("RGBA8"));
+ surface_format_control->addItem(tr("RGB8"));
+ surface_format_control->addItem(tr("RGB5A1"));
+ surface_format_control->addItem(tr("RGB565"));
+ surface_format_control->addItem(tr("RGBA4"));
+ surface_format_control->addItem(tr("IA8"));
+ surface_format_control->addItem(tr("RG8"));
+ surface_format_control->addItem(tr("I8"));
+ surface_format_control->addItem(tr("A8"));
+ surface_format_control->addItem(tr("IA4"));
+ surface_format_control->addItem(tr("I4"));
+ surface_format_control->addItem(tr("A4"));
+ surface_format_control->addItem(tr("ETC1"));
+ surface_format_control->addItem(tr("ETC1A4"));
+ surface_format_control->addItem(tr("D16"));
+ surface_format_control->addItem(tr("D24"));
+ surface_format_control->addItem(tr("D24X8"));
+ surface_format_control->addItem(tr("X24S8"));
+ surface_format_control->addItem(tr("Unknown"));
+
+ surface_info_label = new QLabel();
+ surface_info_label->setWordWrap(true);
+
+ surface_picture_label = new SurfacePicture(0, this);
+ surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ surface_picture_label->setScaledContents(false);
+
+ auto scroll_area = new QScrollArea();
+ scroll_area->setBackgroundRole(QPalette::Dark);
+ scroll_area->setWidgetResizable(false);
+ scroll_area->setWidget(surface_picture_label);
+
+ save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save"));
+
+ // Connections
+ connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
+ connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceSourceChanged(int)));
+ connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnSurfaceAddressChanged(qint64)));
+ connect(surface_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceWidthChanged(int)));
+ connect(surface_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfaceHeightChanged(int)));
+ connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSurfaceFormatChanged(int)));
+ connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerXChanged(int)));
+ connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this, SLOT(OnSurfacePickerYChanged(int)));
+ connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface()));
+
+ auto main_widget = new QWidget;
+ auto main_layout = new QVBoxLayout;
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Source:")));
+ sub_layout->addWidget(surface_source_list);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Physical Address:")));
+ sub_layout->addWidget(surface_address_control);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Width:")));
+ sub_layout->addWidget(surface_width_control);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Height:")));
+ sub_layout->addWidget(surface_height_control);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Format:")));
+ sub_layout->addWidget(surface_format_control);
+ main_layout->addLayout(sub_layout);
+ }
+ main_layout->addWidget(scroll_area);
+
+ auto info_layout = new QHBoxLayout;
+ {
+ auto xy_layout = new QVBoxLayout;
+ {
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("X:")));
+ sub_layout->addWidget(surface_picker_x_control);
+ xy_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Y:")));
+ sub_layout->addWidget(surface_picker_y_control);
+ xy_layout->addLayout(sub_layout);
+ }
+ }
+ info_layout->addLayout(xy_layout);
+ surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ info_layout->addWidget(surface_info_label);
+ }
+ main_layout->addLayout(info_layout);
+
+ main_layout->addWidget(save_surface);
+ main_widget->setLayout(main_layout);
+ setWidget(main_widget);
+
+ // Load current data - TODO: Make sure this works when emulation is not running
+ if (debug_context && debug_context->at_breakpoint) {
+ emit Update();
+ widget()->setEnabled(debug_context->at_breakpoint);
+ } else {
+ widget()->setEnabled(false);
+ }
+}
+
+void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
+{
+ emit Update();
+ widget()->setEnabled(true);
+}
+
+void GraphicsSurfaceWidget::OnResumed()
+{
+ widget()->setEnabled(false);
+}
+
+void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value)
+{
+ surface_source = static_cast<Source>(new_value);
+ emit Update();
+}
+
+void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value)
+{
+ if (surface_address != new_value) {
+ surface_address = static_cast<unsigned>(new_value);
+
+ surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value)
+{
+ if (surface_width != static_cast<unsigned>(new_value)) {
+ surface_width = static_cast<unsigned>(new_value);
+
+ surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value)
+{
+ if (surface_height != static_cast<unsigned>(new_value)) {
+ surface_height = static_cast<unsigned>(new_value);
+
+ surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value)
+{
+ if (surface_format != static_cast<Format>(new_value)) {
+ surface_format = static_cast<Format>(new_value);
+
+ surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value)
+{
+ if (surface_picker_x != new_value) {
+ surface_picker_x = new_value;
+ Pick(surface_picker_x, surface_picker_y);
+ }
+}
+
+void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value)
+{
+ if (surface_picker_y != new_value) {
+ surface_picker_y = new_value;
+ Pick(surface_picker_x, surface_picker_y);
+ }
+}
+
+void GraphicsSurfaceWidget::Pick(int x, int y)
+{
+ surface_picker_x_control->setValue(x);
+ surface_picker_y_control->setValue(y);
+
+ if (x < 0 || x >= surface_width || y < 0 || y >= surface_height) {
+ surface_info_label->setText(tr("Pixel out of bounds"));
+ surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+ return;
+ }
+
+ u8* buffer = Memory::GetPhysicalPointer(surface_address);
+ if (buffer == nullptr) {
+ surface_info_label->setText(tr("(unable to access pixel data)"));
+ surface_info_label->setAlignment(Qt::AlignCenter);
+ return;
+ }
+
+ unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
+ unsigned stride = nibbles_per_pixel * surface_width / 2;
+
+ unsigned bytes_per_pixel;
+ bool nibble_mode = (nibbles_per_pixel == 1);
+ if (nibble_mode) {
+ // As nibbles are contained in a byte we still need to access one byte per nibble
+ bytes_per_pixel = 1;
+ } else {
+ bytes_per_pixel = nibbles_per_pixel / 2;
+ }
+
+ const u32 coarse_y = y & ~7;
+ u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ const u8* pixel = buffer + (nibble_mode ? (offset / 2) : offset);
+
+ auto GetText = [offset](Format format, const u8* pixel) {
+ switch (format) {
+ case Format::RGBA8:
+ {
+ auto value = Color::DecodeRGBA8(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2))
+ .arg(QString::number(value.b(), 'f', 2))
+ .arg(QString::number(value.a(), 'f', 2));
+ }
+ case Format::RGB8:
+ {
+ auto value = Color::DecodeRGB8(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2, Blue: %3")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2))
+ .arg(QString::number(value.b(), 'f', 2));
+ }
+ case Format::RGB5A1:
+ {
+ auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2))
+ .arg(QString::number(value.b(), 'f', 2))
+ .arg(QString::number(value.a(), 'f', 2));
+ }
+ case Format::RGB565:
+ {
+ auto value = Color::DecodeRGB565(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2, Blue: %3")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2))
+ .arg(QString::number(value.b(), 'f', 2));
+ }
+ case Format::RGBA4:
+ {
+ auto value = Color::DecodeRGBA4(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2))
+ .arg(QString::number(value.b(), 'f', 2))
+ .arg(QString::number(value.a(), 'f', 2));
+ }
+ case Format::IA8:
+ return QString("Index: %1, Alpha: %2")
+ .arg(pixel[0])
+ .arg(pixel[1]);
+ case Format::RG8: {
+ auto value = Color::DecodeRG8(pixel) / 255.0f;
+ return QString("Red: %1, Green: %2")
+ .arg(QString::number(value.r(), 'f', 2))
+ .arg(QString::number(value.g(), 'f', 2));
+ }
+ case Format::I8:
+ return QString("Index: %1").arg(*pixel);
+ case Format::A8:
+ return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
+ case Format::IA4:
+ return QString("Index: %1, Alpha: %2")
+ .arg(*pixel & 0xF)
+ .arg((*pixel & 0xF0) >> 4);
+ case Format::I4:
+ {
+ u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
+ return QString("Index: %1").arg(i);
+ }
+ case Format::A4:
+ {
+ u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
+ return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2));
+ }
+ case Format::ETC1:
+ case Format::ETC1A4:
+ // TODO: Display block information or channel values?
+ return QString("Compressed data");
+ case Format::D16:
+ {
+ auto value = Color::DecodeD16(pixel);
+ return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4));
+ }
+ case Format::D24:
+ {
+ auto value = Color::DecodeD24(pixel);
+ return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
+ }
+ case Format::D24X8:
+ case Format::X24S8:
+ {
+ auto values = Color::DecodeD24S8(pixel);
+ return QString("Depth: %1, Stencil: %2").arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4)).arg(values[1]);
+ }
+ case Format::Unknown:
+ return QString("Unknown format");
+ default:
+ return QString("Unhandled format");
+ }
+ return QString("");
+ };
+
+ QString nibbles = "";
+ for (unsigned i = 0; i < nibbles_per_pixel; i++) {
+ unsigned nibble_index = i;
+ if (nibble_mode) {
+ nibble_index += (offset % 2) ? 0 : 1;
+ }
+ u8 byte = pixel[nibble_index / 2];
+ u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF;
+ nibbles.append(QString::number(nibble, 16).toUpper());
+ }
+
+ surface_info_label->setText(QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel)));
+ surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+}
+
+void GraphicsSurfaceWidget::OnUpdate()
+{
+ QPixmap pixmap;
+
+ switch (surface_source) {
+ case Source::ColorBuffer:
+ {
+ // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
+
+ const auto& framebuffer = Pica::g_state.regs.framebuffer;
+
+ surface_address = framebuffer.GetColorBufferPhysicalAddress();
+ surface_width = framebuffer.GetWidth();
+ surface_height = framebuffer.GetHeight();
+
+ switch (framebuffer.color_format) {
+ case Pica::Regs::ColorFormat::RGBA8:
+ surface_format = Format::RGBA8;
+ break;
+
+ case Pica::Regs::ColorFormat::RGB8:
+ surface_format = Format::RGB8;
+ break;
+
+ case Pica::Regs::ColorFormat::RGB5A1:
+ surface_format = Format::RGB5A1;
+ break;
+
+ case Pica::Regs::ColorFormat::RGB565:
+ surface_format = Format::RGB565;
+ break;
+
+ case Pica::Regs::ColorFormat::RGBA4:
+ surface_format = Format::RGBA4;
+ break;
+
+ default:
+ surface_format = Format::Unknown;
+ break;
+ }
+
+ break;
+ }
+
+ case Source::DepthBuffer:
+ {
+ const auto& framebuffer = Pica::g_state.regs.framebuffer;
+
+ surface_address = framebuffer.GetDepthBufferPhysicalAddress();
+ surface_width = framebuffer.GetWidth();
+ surface_height = framebuffer.GetHeight();
+
+ switch (framebuffer.depth_format) {
+ case Pica::Regs::DepthFormat::D16:
+ surface_format = Format::D16;
+ break;
+
+ case Pica::Regs::DepthFormat::D24:
+ surface_format = Format::D24;
+ break;
+
+ case Pica::Regs::DepthFormat::D24S8:
+ surface_format = Format::D24X8;
+ break;
+
+ default:
+ surface_format = Format::Unknown;
+ break;
+ }
+
+ break;
+ }
+
+ case Source::StencilBuffer:
+ {
+ const auto& framebuffer = Pica::g_state.regs.framebuffer;
+
+ surface_address = framebuffer.GetDepthBufferPhysicalAddress();
+ surface_width = framebuffer.GetWidth();
+ surface_height = framebuffer.GetHeight();
+
+ switch (framebuffer.depth_format) {
+ case Pica::Regs::DepthFormat::D24S8:
+ surface_format = Format::X24S8;
+ break;
+
+ default:
+ surface_format = Format::Unknown;
+ break;
+ }
+
+ break;
+ }
+
+ case Source::Texture0:
+ case Source::Texture1:
+ case Source::Texture2:
+ {
+ unsigned texture_index;
+ if (surface_source == Source::Texture0) texture_index = 0;
+ else if (surface_source == Source::Texture1) texture_index = 1;
+ else if (surface_source == Source::Texture2) texture_index = 2;
+ else {
+ qDebug() << "Unknown texture source " << static_cast<int>(surface_source);
+ break;
+ }
+
+ const auto texture = Pica::g_state.regs.GetTextures()[texture_index];
+ auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format);
+
+ surface_address = info.physical_address;
+ surface_width = info.width;
+ surface_height = info.height;
+ surface_format = static_cast<Format>(info.format);
+
+ if (surface_format > Format::MaxTextureFormat) {
+ qDebug() << "Unknown texture format " << static_cast<int>(info.format);
+ }
+ break;
+ }
+
+ case Source::Custom:
+ {
+ // Keep user-specified values
+ break;
+ }
+
+ default:
+ qDebug() << "Unknown surface source " << static_cast<int>(surface_source);
+ break;
+ }
+
+ surface_address_control->SetValue(surface_address);
+ surface_width_control->setValue(surface_width);
+ surface_height_control->setValue(surface_height);
+ surface_format_control->setCurrentIndex(static_cast<int>(surface_format));
+
+ // TODO: Implement a good way to visualize alpha components!
+
+ QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
+ u8* buffer = Memory::GetPhysicalPointer(surface_address);
+
+ if (buffer == nullptr) {
+ surface_picture_label->hide();
+ surface_info_label->setText(tr("(invalid surface address)"));
+ surface_info_label->setAlignment(Qt::AlignCenter);
+ surface_picker_x_control->setEnabled(false);
+ surface_picker_y_control->setEnabled(false);
+ save_surface->setEnabled(false);
+ return;
+ }
+
+ if (surface_format == Format::Unknown) {
+ surface_picture_label->hide();
+ surface_info_label->setText(tr("(unknown surface format)"));
+ surface_info_label->setAlignment(Qt::AlignCenter);
+ surface_picker_x_control->setEnabled(false);
+ surface_picker_y_control->setEnabled(false);
+ save_surface->setEnabled(false);
+ return;
+ }
+
+ surface_picture_label->show();
+
+ unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
+ unsigned stride = nibbles_per_pixel * surface_width / 2;
+
+ // We handle depth formats here because DebugUtils only supports TextureFormats
+ if (surface_format <= Format::MaxTextureFormat) {
+
+ // Generate a virtual texture
+ Pica::DebugUtils::TextureInfo info;
+ info.physical_address = surface_address;
+ info.width = surface_width;
+ info.height = surface_height;
+ info.format = static_cast<Pica::Regs::TextureFormat>(surface_format);
+ info.stride = stride;
+
+ for (unsigned int y = 0; y < surface_height; ++y) {
+ for (unsigned int x = 0; x < surface_width; ++x) {
+ Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(buffer, x, y, info, true);
+ decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
+ }
+ }
+
+ } else {
+
+ ASSERT_MSG(nibbles_per_pixel >= 2, "Depth decoder only supports formats with at least one byte per pixel");
+ unsigned bytes_per_pixel = nibbles_per_pixel / 2;
+
+ for (unsigned int y = 0; y < surface_height; ++y) {
+ for (unsigned int x = 0; x < surface_width; ++x) {
+ const u32 coarse_y = y & ~7;
+ u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ const u8* pixel = buffer + offset;
+ Math::Vec4<u8> color = { 0, 0, 0, 0 };
+
+ switch(surface_format) {
+ case Format::D16:
+ {
+ u32 data = Color::DecodeD16(pixel);
+ color.r() = data & 0xFF;
+ color.g() = (data >> 8) & 0xFF;
+ break;
+ }
+ case Format::D24:
+ {
+ u32 data = Color::DecodeD24(pixel);
+ color.r() = data & 0xFF;
+ color.g() = (data >> 8) & 0xFF;
+ color.b() = (data >> 16) & 0xFF;
+ break;
+ }
+ case Format::D24X8:
+ {
+ Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
+ color.r() = data.x & 0xFF;
+ color.g() = (data.x >> 8) & 0xFF;
+ color.b() = (data.x >> 16) & 0xFF;
+ break;
+ }
+ case Format::X24S8:
+ {
+ Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
+ color.r() = color.g() = color.b() = data.y;
+ break;
+ }
+ default:
+ qDebug() << "Unknown surface format " << static_cast<int>(surface_format);
+ break;
+ }
+
+ decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
+ }
+ }
+
+ }
+
+ pixmap = QPixmap::fromImage(decoded_image);
+ surface_picture_label->setPixmap(pixmap);
+ surface_picture_label->resize(pixmap.size());
+
+ // Update the info with pixel data
+ surface_picker_x_control->setEnabled(true);
+ surface_picker_y_control->setEnabled(true);
+ Pick(surface_picker_x, surface_picker_y);
+
+ // Enable saving the converted pixmap to file
+ save_surface->setEnabled(true);
+}
+
+void GraphicsSurfaceWidget::SaveSurface() {
+ QString png_filter = tr("Portable Network Graphic (*.png)");
+ QString bin_filter = tr("Binary data (*.bin)");
+
+ QString selectedFilter;
+ QString filename = QFileDialog::getSaveFileName(this, tr("Save Surface"), QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
+ QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
+
+ if (filename.isEmpty()) {
+ // If the user canceled the dialog, don't save anything.
+ return;
+ }
+
+ if (selectedFilter == png_filter) {
+ const QPixmap* pixmap = surface_picture_label->pixmap();
+ ASSERT_MSG(pixmap != nullptr, "No pixmap set");
+
+ QFile file(filename);
+ file.open(QIODevice::WriteOnly);
+ if (pixmap)
+ pixmap->save(&file, "PNG");
+ } else if (selectedFilter == bin_filter) {
+ const u8* buffer = Memory::GetPhysicalPointer(surface_address);
+ ASSERT_MSG(buffer != nullptr, "Memory not accessible");
+
+ QFile file(filename);
+ file.open(QIODevice::WriteOnly);
+ int size = surface_width * surface_height * NibblesPerPixel(surface_format) / 2;
+ QByteArray data(reinterpret_cast<const char*>(buffer), size);
+ file.write(data);
+ } else {
+ UNREACHABLE_MSG("Unhandled filter selected");
+ }
+}
+
+unsigned int GraphicsSurfaceWidget::NibblesPerPixel(GraphicsSurfaceWidget::Format format) {
+ if (format <= Format::MaxTextureFormat) {
+ return Pica::Regs::NibblesPerPixel(static_cast<Pica::Regs::TextureFormat>(format));
+ }
+
+ switch (format) {
+ case Format::D24X8:
+ case Format::X24S8:
+ return 4 * 2;
+ case Format::D24:
+ return 3 * 2;
+ case Format::D16:
+ return 2 * 2;
+ default:
+ UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this "
+ "should not be reached as this function should "
+ "be given a format which is in "
+ "GraphicsSurfaceWidget::Format. Instead got %i",
+ static_cast<int>(format));
+ return 0;
+ }
+}
diff --git a/src/citra_qt/debugger/graphics_surface.h b/src/citra_qt/debugger/graphics_surface.h
new file mode 100644
index 000000000..7c7f50e38
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_surface.h
@@ -0,0 +1,120 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "citra_qt/debugger/graphics_breakpoint_observer.h"
+
+#include <QLabel>
+#include <QPushButton>
+
+class QComboBox;
+class QSpinBox;
+class CSpinBox;
+
+class GraphicsSurfaceWidget;
+
+class SurfacePicture : public QLabel
+{
+ Q_OBJECT
+
+public:
+ SurfacePicture(QWidget* parent = 0, GraphicsSurfaceWidget* surface_widget = nullptr);
+ ~SurfacePicture();
+
+protected slots:
+ virtual void mouseMoveEvent(QMouseEvent* event);
+ virtual void mousePressEvent(QMouseEvent* event);
+
+private:
+ GraphicsSurfaceWidget* surface_widget;
+
+};
+
+class GraphicsSurfaceWidget : public BreakPointObserverDock {
+ Q_OBJECT
+
+ using Event = Pica::DebugContext::Event;
+
+ enum class Source {
+ ColorBuffer = 0,
+ DepthBuffer = 1,
+ StencilBuffer = 2,
+ Texture0 = 3,
+ Texture1 = 4,
+ Texture2 = 5,
+ Custom = 6,
+ };
+
+ enum class Format {
+ // These must match the TextureFormat type!
+ RGBA8 = 0,
+ RGB8 = 1,
+ RGB5A1 = 2,
+ RGB565 = 3,
+ RGBA4 = 4,
+ IA8 = 5,
+ RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
+ I8 = 7,
+ A8 = 8,
+ IA4 = 9,
+ I4 = 10,
+ A4 = 11,
+ ETC1 = 12, // compressed
+ ETC1A4 = 13,
+ MaxTextureFormat = 13,
+ D16 = 14,
+ D24 = 15,
+ D24X8 = 16,
+ X24S8 = 17,
+ Unknown = 18,
+ };
+
+ static unsigned int NibblesPerPixel(Format format);
+
+public:
+ GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr);
+ void Pick(int x, int y);
+
+public slots:
+ void OnSurfaceSourceChanged(int new_value);
+ void OnSurfaceAddressChanged(qint64 new_value);
+ void OnSurfaceWidthChanged(int new_value);
+ void OnSurfaceHeightChanged(int new_value);
+ void OnSurfaceFormatChanged(int new_value);
+ void OnSurfacePickerXChanged(int new_value);
+ void OnSurfacePickerYChanged(int new_value);
+ void OnUpdate();
+
+private slots:
+ void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
+ void OnResumed() override;
+
+ void SaveSurface();
+
+signals:
+ void Update();
+
+private:
+
+ QComboBox* surface_source_list;
+ CSpinBox* surface_address_control;
+ QSpinBox* surface_width_control;
+ QSpinBox* surface_height_control;
+ QComboBox* surface_format_control;
+
+ SurfacePicture* surface_picture_label;
+ QSpinBox* surface_picker_x_control;
+ QSpinBox* surface_picker_y_control;
+ QLabel* surface_info_label;
+ QPushButton* save_surface;
+
+ Source surface_source;
+ unsigned surface_address;
+ unsigned surface_width;
+ unsigned surface_height;
+ Format surface_format;
+ int surface_picker_x = 0;
+ int surface_picker_y = 0;
+};
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 6239160bc..0ed1ffa5a 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -29,7 +29,7 @@
#include "citra_qt/debugger/graphics.h"
#include "citra_qt/debugger/graphics_breakpoints.h"
#include "citra_qt/debugger/graphics_cmdlists.h"
-#include "citra_qt/debugger/graphics_framebuffer.h"
+#include "citra_qt/debugger/graphics_surface.h"
#include "citra_qt/debugger/graphics_tracing.h"
#include "citra_qt/debugger/graphics_vertex_shader.h"
#include "citra_qt/debugger/profiler.h"
@@ -101,10 +101,6 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
graphicsBreakpointsWidget->hide();
- auto graphicsFramebufferWidget = new GraphicsFramebufferWidget(Pica::g_debug_context, this);
- addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget);
- graphicsFramebufferWidget->hide();
-
auto graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget);
graphicsVertexShaderWidget->hide();
@@ -113,7 +109,12 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget);
graphicsTracingWidget->hide();
+ auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this);
+ connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, SLOT(OnCreateGraphicsSurfaceViewer()));
+
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
+ debug_menu->addAction(graphicsSurfaceViewerAction);
+ debug_menu->addSeparator();
debug_menu->addAction(profilerWidget->toggleViewAction());
#if MICROPROFILE_ENABLED
debug_menu->addAction(microProfileDialog->toggleViewAction());
@@ -124,7 +125,6 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
debug_menu->addAction(graphicsWidget->toggleViewAction());
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
- debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction());
debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
@@ -517,6 +517,13 @@ void GMainWindow::OnConfigure() {
}
}
+void GMainWindow::OnCreateGraphicsSurfaceViewer() {
+ auto graphicsSurfaceViewerWidget = new GraphicsSurfaceWidget(Pica::g_debug_context, this);
+ addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceViewerWidget);
+ // TODO: Maybe graphicsSurfaceViewerWidget->setFloating(true);
+ graphicsSurfaceViewerWidget->show();
+}
+
bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true;
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 477db5c5c..b836b13fb 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -108,6 +108,7 @@ private slots:
void OnConfigure();
void OnDisplayTitleBars(bool);
void ToggleWindowMode();
+ void OnCreateGraphicsSurfaceViewer();
private:
Ui::MainWindow ui;
diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp
index b2807354a..08270dd88 100644
--- a/src/common/emu_window.cpp
+++ b/src/common/emu_window.cpp
@@ -11,12 +11,28 @@
#include "emu_window.h"
#include "video_core/video_core.h"
-void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) {
- pad_state.hex |= KeyMap::GetPadKey(key).hex;
+void EmuWindow::ButtonPressed(Service::HID::PadState pad) {
+ pad_state.hex |= pad.hex;
}
-void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) {
- pad_state.hex &= ~KeyMap::GetPadKey(key).hex;
+void EmuWindow::ButtonReleased(Service::HID::PadState pad) {
+ pad_state.hex &= ~pad.hex;
+}
+
+void EmuWindow::CirclePadUpdated(float x, float y) {
+ constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position
+
+ // Make sure the coordinates are in the unit circle,
+ // otherwise normalize it.
+ float r = x * x + y * y;
+ if (r > 1) {
+ r = std::sqrt(r);
+ x /= r;
+ y /= r;
+ }
+
+ circle_pad_x = static_cast<s16>(x * MAX_CIRCLEPAD_POS);
+ circle_pad_y = static_cast<s16>(y * MAX_CIRCLEPAD_POS);
}
/**
diff --git a/src/common/emu_window.h b/src/common/emu_window.h
index 7c3486dea..57e303b6d 100644
--- a/src/common/emu_window.h
+++ b/src/common/emu_window.h
@@ -12,10 +12,6 @@
#include "core/hle/service/hid/hid.h"
-namespace KeyMap {
-struct HostDeviceKey;
-}
-
/**
* Abstraction class used to provide an interface between emulation code and the frontend
* (e.g. SDL, QGLWidget, GLFW, etc...).
@@ -76,11 +72,27 @@ public:
virtual void ReloadSetKeymaps() = 0;
- /// Signals a key press action to the HID module
- void KeyPressed(KeyMap::HostDeviceKey key);
+ /**
+ * Signals a button press action to the HID module.
+ * @param pad_state indicates which button to press
+ * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
+ */
+ void ButtonPressed(Service::HID::PadState pad_state);
+
+ /**
+ * Signals a button release action to the HID module.
+ * @param pad_state indicates which button to press
+ * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
+ */
+ void ButtonReleased(Service::HID::PadState pad_state);
- /// Signals a key release action to the HID module
- void KeyReleased(KeyMap::HostDeviceKey key);
+ /**
+ * Signals a circle pad change action to the HID module.
+ * @param x new x-coordinate of the circle pad, in the range [-1.0, 1.0]
+ * @param y new y-coordinate of the circle pad, in the range [-1.0, 1.0]
+ * @note the coordinates will be normalized if the radius is larger than 1
+ */
+ void CirclePadUpdated(float x, float y);
/**
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
@@ -100,8 +112,9 @@ public:
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
/**
- * Gets the current pad state (which buttons are pressed and the circle pad direction).
+ * Gets the current pad state (which buttons are pressed).
* @note This should be called by the core emu thread to get a state set by the window thread.
+ * @note This doesn't include analog input like circle pad direction
* @todo Fix this function to be thread-safe.
* @return PadState object indicating the current pad state
*/
@@ -110,6 +123,16 @@ public:
}
/**
+ * Gets the current circle pad state.
+ * @note This should be called by the core emu thread to get a state set by the window thread.
+ * @todo Fix this function to be thread-safe.
+ * @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates
+ */
+ std::tuple<s16, s16> GetCirclePadState() const {
+ return std::make_tuple(circle_pad_x, circle_pad_y);
+ }
+
+ /**
* Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
* @note This should be called by the core emu thread to get a state set by the window thread.
* @todo Fix this function to be thread-safe.
@@ -200,6 +223,8 @@ protected:
pad_state.hex = 0;
touch_x = 0;
touch_y = 0;
+ circle_pad_x = 0;
+ circle_pad_y = 0;
touch_pressed = false;
}
virtual ~EmuWindow() {}
@@ -260,6 +285,9 @@ private:
u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
+ s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156)
+ s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156)
+
/**
* Clip the provided coordinates to be inside the touchscreen area.
*/
diff --git a/src/common/key_map.cpp b/src/common/key_map.cpp
index 844d5df68..ad311d66b 100644
--- a/src/common/key_map.cpp
+++ b/src/common/key_map.cpp
@@ -2,24 +2,138 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "key_map.h"
#include <map>
+#include "common/emu_window.h"
+#include "common/key_map.h"
+
namespace KeyMap {
-static std::map<HostDeviceKey, Service::HID::PadState> key_map;
+// TODO (wwylele): currently we treat c-stick as four direction buttons
+// and map it directly to EmuWindow::ButtonPressed.
+// It should go the analog input way like circle pad does.
+const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets = {{
+ Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y,
+ Service::HID::PAD_L, Service::HID::PAD_R, Service::HID::PAD_ZL, Service::HID::PAD_ZR,
+ Service::HID::PAD_START, Service::HID::PAD_SELECT, Service::HID::PAD_NONE,
+ Service::HID::PAD_UP, Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, Service::HID::PAD_RIGHT,
+ Service::HID::PAD_C_UP, Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, Service::HID::PAD_C_RIGHT,
+
+ IndirectTarget::CirclePadUp,
+ IndirectTarget::CirclePadDown,
+ IndirectTarget::CirclePadLeft,
+ IndirectTarget::CirclePadRight,
+ IndirectTarget::CirclePadModifier,
+}};
+
+static std::map<HostDeviceKey, KeyTarget> key_map;
static int next_device_id = 0;
+static bool circle_pad_up = false;
+static bool circle_pad_down = false;
+static bool circle_pad_left = false;
+static bool circle_pad_right = false;
+static bool circle_pad_modifier = false;
+
+static void UpdateCirclePad(EmuWindow& emu_window) {
+ constexpr float SQRT_HALF = 0.707106781;
+ int x = 0, y = 0;
+
+ if (circle_pad_right)
+ ++x;
+ if (circle_pad_left)
+ --x;
+ if (circle_pad_up)
+ ++y;
+ if (circle_pad_down)
+ --y;
+
+ float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0;
+ emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0 : SQRT_HALF), y * modifier * (x == 0 ? 1.0 : SQRT_HALF));
+}
+
int NewDeviceId() {
return next_device_id++;
}
-void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState) {
- key_map[key].hex = padState.hex;
+void SetKeyMapping(HostDeviceKey key, KeyTarget target) {
+ key_map[key] = target;
+}
+
+void ClearKeyMapping(int device_id) {
+ auto iter = key_map.begin();
+ while (iter != key_map.end()) {
+ if (iter->first.device_id == device_id)
+ key_map.erase(iter++);
+ else
+ ++iter;
+ }
}
-Service::HID::PadState GetPadKey(HostDeviceKey key) {
- return key_map[key];
+void PressKey(EmuWindow& emu_window, HostDeviceKey key) {
+ auto target = key_map.find(key);
+ if (target == key_map.end())
+ return;
+
+ if (target->second.direct) {
+ emu_window.ButtonPressed({{target->second.target.direct_target_hex}});
+ } else {
+ switch (target->second.target.indirect_target) {
+ case IndirectTarget::CirclePadUp:
+ circle_pad_up = true;
+ UpdateCirclePad(emu_window);
+ break;
+ case IndirectTarget::CirclePadDown:
+ circle_pad_down = true;
+ UpdateCirclePad(emu_window);
+ break;
+ case IndirectTarget::CirclePadLeft:
+ circle_pad_left = true;
+ UpdateCirclePad(emu_window);
+ break;
+ case IndirectTarget::CirclePadRight:
+ circle_pad_right = true;
+ UpdateCirclePad(emu_window);
+ break;
+ case IndirectTarget::CirclePadModifier:
+ circle_pad_modifier = true;
+ UpdateCirclePad(emu_window);
+ break;
+ }
+ }
+}
+
+void ReleaseKey(EmuWindow& emu_window,HostDeviceKey key) {
+ auto target = key_map.find(key);
+ if (target == key_map.end())
+ return;
+
+ if (target->second.direct) {
+ emu_window.ButtonReleased({{target->second.target.direct_target_hex}});
+ } else {
+ switch (target->second.target.indirect_target) {
+ case IndirectTarget::CirclePadUp:
+ circle_pad_up = false;
+ UpdateCirclePad(emu_window);
+ break;
+ case IndirectTarget::CirclePadDown:
+ circle_pad_down = false;
+ UpdateCirclePad(emu_window);
+ break;
+ case IndirectTarget::CirclePadLeft:
+ circle_pad_left = false;
+ UpdateCirclePad(emu_window);
+ break;
+ case IndirectTarget::CirclePadRight:
+ circle_pad_right = false;
+ UpdateCirclePad(emu_window);
+ break;
+ case IndirectTarget::CirclePadModifier:
+ circle_pad_modifier = false;
+ UpdateCirclePad(emu_window);
+ break;
+ }
+ }
}
}
diff --git a/src/common/key_map.h b/src/common/key_map.h
index 68f7e2f99..b62f017c6 100644
--- a/src/common/key_map.h
+++ b/src/common/key_map.h
@@ -4,12 +4,51 @@
#pragma once
+#include <array>
#include <tuple>
#include "core/hle/service/hid/hid.h"
+class EmuWindow;
+
namespace KeyMap {
/**
+ * Represents key mapping targets that are not real 3DS buttons.
+ * They will be handled by KeyMap and translated to 3DS input.
+ */
+enum class IndirectTarget {
+ CirclePadUp,
+ CirclePadDown,
+ CirclePadLeft,
+ CirclePadRight,
+ CirclePadModifier,
+};
+
+/**
+ * Represents a key mapping target. It can be a PadState that represents real 3DS buttons,
+ * or an IndirectTarget.
+ */
+struct KeyTarget {
+ bool direct;
+ union {
+ u32 direct_target_hex;
+ IndirectTarget indirect_target;
+ } target;
+
+ KeyTarget() : direct(true) {
+ target.direct_target_hex = 0;
+ }
+
+ KeyTarget(Service::HID::PadState pad) : direct(true) {
+ target.direct_target_hex = pad.hex;
+ }
+
+ KeyTarget(IndirectTarget i) : direct(false) {
+ target.indirect_target = i;
+ }
+};
+
+/**
* Represents a key for a specific host device.
*/
struct HostDeviceKey {
@@ -27,19 +66,31 @@ struct HostDeviceKey {
}
};
+extern const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets;
+
/**
* Generates a new device id, which uniquely identifies a host device within KeyMap.
*/
int NewDeviceId();
/**
- * Maps a device-specific key to a PadState.
+ * Maps a device-specific key to a target (a PadState or an IndirectTarget).
+ */
+void SetKeyMapping(HostDeviceKey key, KeyTarget target);
+
+/**
+ * Clears all key mappings belonging to one device.
+ */
+void ClearKeyMapping(int device_id);
+
+/**
+ * Maps a key press action and call the corresponding function in EmuWindow
*/
-void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState);
+void PressKey(EmuWindow& emu_window, HostDeviceKey key);
/**
- * Gets the PadState that's mapped to the provided device-specific key.
+ * Maps a key release action and call the corresponding function in EmuWindow
*/
-Service::HID::PadState GetPadKey(HostDeviceKey key);
+void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key);
}
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 2fde9747c..7d267fdcf 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -72,7 +72,10 @@ set(SRCS
hle/service/cfg/cfg_s.cpp
hle/service/cfg/cfg_u.cpp
hle/service/csnd_snd.cpp
- hle/service/dlp_srvr.cpp
+ hle/service/dlp/dlp.cpp
+ hle/service/dlp/dlp_clnt.cpp
+ hle/service/dlp/dlp_fkcl.cpp
+ hle/service/dlp/dlp_srvr.cpp
hle/service/dsp_dsp.cpp
hle/service/err_f.cpp
hle/service/frd/frd.cpp
@@ -210,7 +213,10 @@ set(HEADERS
hle/service/cfg/cfg_s.h
hle/service/cfg/cfg_u.h
hle/service/csnd_snd.h
- hle/service/dlp_srvr.h
+ hle/service/dlp/dlp.h
+ hle/service/dlp/dlp_clnt.h
+ hle/service/dlp/dlp_fkcl.h
+ hle/service/dlp/dlp_srvr.h
hle/service/dsp_dsp.h
hle/service/err_f.h
hle/service/frd/frd.h
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 820b19e1a..28d403158 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -646,7 +646,7 @@ static void ReadMemory() {
u8* data = Memory::GetPointer(addr);
if (!data) {
- return SendReply("E0");
+ return SendReply("E00");
}
MemToGdbHex(reply, data, len);
diff --git a/src/core/hle/service/dlp/dlp.cpp b/src/core/hle/service/dlp/dlp.cpp
new file mode 100644
index 000000000..7c8db794b
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp.cpp
@@ -0,0 +1,24 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/service.h"
+#include "core/hle/service/dlp/dlp.h"
+#include "core/hle/service/dlp/dlp_clnt.h"
+#include "core/hle/service/dlp/dlp_fkcl.h"
+#include "core/hle/service/dlp/dlp_srvr.h"
+
+namespace Service {
+namespace DLP {
+
+void Init() {
+ AddService(new DLP_CLNT_Interface);
+ AddService(new DLP_FKCL_Interface);
+ AddService(new DLP_SRVR_Interface);
+}
+
+void Shutdown() {
+}
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp.h b/src/core/hle/service/dlp/dlp.h
new file mode 100644
index 000000000..ec2fe46e8
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp.h
@@ -0,0 +1,15 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+namespace Service {
+namespace DLP {
+
+/// Initializes the DLP services.
+void Init();
+
+/// Shuts down the DLP services.
+void Shutdown();
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp
new file mode 100644
index 000000000..0b31d47df
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_clnt.cpp
@@ -0,0 +1,20 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/dlp/dlp_clnt.h"
+
+namespace Service {
+namespace DLP {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000100C3, nullptr, "Initialize"},
+ {0x00110000, nullptr, "GetWirelessRebootPassphrase"},
+};
+
+DLP_CLNT_Interface::DLP_CLNT_Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_clnt.h b/src/core/hle/service/dlp/dlp_clnt.h
new file mode 100644
index 000000000..067f11e37
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_clnt.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace DLP {
+
+class DLP_CLNT_Interface final : public Interface {
+public:
+ DLP_CLNT_Interface();
+
+ std::string GetPortName() const override {
+ return "dlp:CLNT";
+ }
+};
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp
new file mode 100644
index 000000000..a845260e5
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_fkcl.cpp
@@ -0,0 +1,20 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/dlp/dlp_fkcl.h"
+
+namespace Service {
+namespace DLP {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010083, nullptr, "Initialize"},
+ {0x000F0000, nullptr, "GetWirelessRebootPassphrase"},
+};
+
+DLP_FKCL_Interface::DLP_FKCL_Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_fkcl.h b/src/core/hle/service/dlp/dlp_fkcl.h
new file mode 100644
index 000000000..e4837a167
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_fkcl.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace DLP {
+
+class DLP_FKCL_Interface final : public Interface {
+public:
+ DLP_FKCL_Interface();
+
+ std::string GetPortName() const override {
+ return "dlp:FKCL";
+ }
+};
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp
index 1f30188da..da9b30f56 100644
--- a/src/core/hle/service/dlp_srvr.cpp
+++ b/src/core/hle/service/dlp/dlp_srvr.cpp
@@ -2,16 +2,15 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/hle/hle.h"
-#include "core/hle/service/dlp_srvr.h"
+#include "core/hle/result.h"
+#include "core/hle/service/dlp/dlp_srvr.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace DLP_SRVR
+namespace Service {
+namespace DLP {
-namespace DLP_SRVR {
-
-static void unk_0x000E0040(Service::Interface* self) {
+static void unk_0x000E0040(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw;
@@ -23,14 +22,13 @@ static void unk_0x000E0040(Service::Interface* self) {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010183, nullptr, "Initialize"},
{0x00020000, nullptr, "Finalize"},
+ {0x000800C0, nullptr, "SendWirelessRebootPassphrase"},
{0x000E0040, unk_0x000E0040, "unk_0x000E0040"},
};
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
+DLP_SRVR_Interface::DLP_SRVR_Interface() {
Register(FunctionTable);
}
-} // namespace
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp_srvr.h b/src/core/hle/service/dlp/dlp_srvr.h
index d65d00814..19fe17840 100644
--- a/src/core/hle/service/dlp_srvr.h
+++ b/src/core/hle/service/dlp/dlp_srvr.h
@@ -6,18 +6,17 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace DLP_SRVR
+namespace Service {
+namespace DLP {
-namespace DLP_SRVR {
-
-class Interface : public Service::Interface {
+class DLP_SRVR_Interface final : public Interface {
public:
- Interface();
+ DLP_SRVR_Interface();
std::string GetPortName() const override {
return "dlp:SRVR";
}
};
-} // namespace
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d216cecb4..cdec11388 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cmath>
+
#include "common/logging/log.h"
#include "common/emu_window.h"
@@ -19,8 +21,6 @@
namespace Service {
namespace HID {
-static const int MAX_CIRCLEPAD_POS = 0x9C; ///< Max value for a circle pad position
-
// Handle to shared memory region designated to HID_User service
static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
@@ -39,38 +39,48 @@ static u32 next_gyroscope_index;
static int enable_accelerometer_count = 0; // positive means enabled
static int enable_gyroscope_count = 0; // positive means enabled
-const std::array<Service::HID::PadState, Settings::NativeInput::NUM_INPUTS> pad_mapping = {{
- Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y,
- Service::HID::PAD_L, Service::HID::PAD_R, Service::HID::PAD_ZL, Service::HID::PAD_ZR,
- Service::HID::PAD_START, Service::HID::PAD_SELECT, Service::HID::PAD_NONE,
- Service::HID::PAD_UP, Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, Service::HID::PAD_RIGHT,
- Service::HID::PAD_CIRCLE_UP, Service::HID::PAD_CIRCLE_DOWN, Service::HID::PAD_CIRCLE_LEFT, Service::HID::PAD_CIRCLE_RIGHT,
- Service::HID::PAD_C_UP, Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, Service::HID::PAD_C_RIGHT
-}};
-
-
-// TODO(peachum):
-// Add a method for setting analog input from joystick device for the circle Pad.
-//
-// This method should:
-// * Be called after both PadButton<Press, Release>().
-// * Be called before PadUpdateComplete()
-// * Set current PadEntry.circle_pad_<axis> using analog data
-// * Set PadData.raw_circle_pad_data
-// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_x >= 41
-// * Set PadData.current_state.circle_up = 1 if current PadEntry.circle_pad_y >= 41
-// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41
-// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41
+static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
+ constexpr float TAN30 = 0.577350269, TAN60 = 1 / TAN30; // 30 degree and 60 degree are angular thresholds for directions
+ constexpr int CIRCLE_PAD_THRESHOLD_SQUARE = 40 * 40; // a circle pad radius greater than 40 will trigger circle pad direction
+ PadState state;
+ state.hex = 0;
+
+ if (circle_pad_x * circle_pad_x + circle_pad_y * circle_pad_y > CIRCLE_PAD_THRESHOLD_SQUARE) {
+ float t = std::abs(static_cast<float>(circle_pad_y) / circle_pad_x);
+
+ if (circle_pad_x != 0 && t < TAN60) {
+ if (circle_pad_x > 0)
+ state.circle_right.Assign(1);
+ else
+ state.circle_left.Assign(1);
+ }
+
+ if (circle_pad_x == 0 || t > TAN30) {
+ if (circle_pad_y > 0)
+ state.circle_up.Assign(1);
+ else
+ state.circle_down.Assign(1);
+ }
+ }
+
+ return state;
+}
void Update() {
SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());
- const PadState state = VideoCore::g_emu_window->GetPadState();
if (mem == nullptr) {
LOG_DEBUG(Service_HID, "Cannot update HID prior to mapping shared memory!");
return;
}
+ PadState state = VideoCore::g_emu_window->GetPadState();
+
+ // Get current circle pad position and update circle pad direction
+ s16 circle_pad_x, circle_pad_y;
+ std::tie(circle_pad_x, circle_pad_y) = VideoCore::g_emu_window->GetCirclePadState();
+ state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex;
+
mem->pad.current_state.hex = state.hex;
mem->pad.index = next_pad_index;
next_pad_index = (next_pad_index + 1) % mem->pad.entries.size();
@@ -88,13 +98,9 @@ void Update() {
// Update entry properties
pad_entry.current_state.hex = state.hex;
pad_entry.delta_additions.hex = changed.hex & state.hex;
- pad_entry.delta_removals.hex = changed.hex & old_state.hex;;
-
- // Set circle Pad
- pad_entry.circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS :
- state.circle_right ? MAX_CIRCLEPAD_POS : 0x0;
- pad_entry.circle_pad_y = state.circle_down ? -MAX_CIRCLEPAD_POS :
- state.circle_up ? MAX_CIRCLEPAD_POS : 0x0;
+ pad_entry.delta_removals.hex = changed.hex & old_state.hex;
+ pad_entry.circle_pad_x = circle_pad_x;
+ pad_entry.circle_pad_y = circle_pad_y;
// If we just updated index 0, provide a new timestamp
if (mem->pad.index == 0) {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 170d19ea8..669b1f723 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -215,9 +215,6 @@ const PadState PAD_CIRCLE_LEFT = {{1u << 29}};
const PadState PAD_CIRCLE_UP = {{1u << 30}};
const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
-
-extern const std::array<Service::HID::PadState, Settings::NativeInput::NUM_INPUTS> pad_mapping;
-
/**
* HID::GetIPCHandles service function
* Inputs:
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index d7e7d4fe3..395880843 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -10,7 +10,6 @@
#include "core/hle/service/act_a.h"
#include "core/hle/service/act_u.h"
#include "core/hle/service/csnd_snd.h"
-#include "core/hle/service/dlp_srvr.h"
#include "core/hle/service/dsp_dsp.h"
#include "core/hle/service/err_f.h"
#include "core/hle/service/gsp_gpu.h"
@@ -31,6 +30,7 @@
#include "core/hle/service/boss/boss.h"
#include "core/hle/service/cam/cam.h"
#include "core/hle/service/cecd/cecd.h"
+#include "core/hle/service/dlp/dlp.h"
#include "core/hle/service/frd/frd.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/cfg/cfg.h"
@@ -111,6 +111,7 @@ void Init() {
Service::CAM::Init();
Service::CECD::Init();
Service::CFG::Init();
+ Service::DLP::Init();
Service::FRD::Init();
Service::HID::Init();
Service::IR::Init();
@@ -123,7 +124,6 @@ void Init() {
AddService(new ACT_A::Interface);
AddService(new ACT_U::Interface);
AddService(new CSND_SND::Interface);
- AddService(new DLP_SRVR::Interface);
AddService(new DSP_DSP::Interface);
AddService(new GSP_GPU::Interface);
AddService(new GSP_LCD::Interface);
@@ -150,6 +150,7 @@ void Shutdown() {
Service::IR::Shutdown();
Service::HID::Shutdown();
Service::FRD::Shutdown();
+ Service::DLP::Shutdown();
Service::CFG::Shutdown();
Service::CECD::Shutdown();
Service::CAM::Shutdown();
diff --git a/src/core/settings.h b/src/core/settings.h
index ea72f4d9c..f95e62390 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -13,29 +13,40 @@ namespace Settings {
namespace NativeInput {
enum Values {
+ // directly mapped keys
A, B, X, Y,
L, R, ZL, ZR,
START, SELECT, HOME,
DUP, DDOWN, DLEFT, DRIGHT,
- SUP, SDOWN, SLEFT, SRIGHT,
CUP, CDOWN, CLEFT, CRIGHT,
+
+ // indirectly mapped keys
+ CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT,
+ CIRCLE_MODIFIER,
+
NUM_INPUTS
};
+
static const std::array<const char*, NUM_INPUTS> Mapping = {{
+ // directly mapped keys
"pad_a", "pad_b", "pad_x", "pad_y",
"pad_l", "pad_r", "pad_zl", "pad_zr",
"pad_start", "pad_select", "pad_home",
"pad_dup", "pad_ddown", "pad_dleft", "pad_dright",
- "pad_sup", "pad_sdown", "pad_sleft", "pad_sright",
- "pad_cup", "pad_cdown", "pad_cleft", "pad_cright"
+ "pad_cup", "pad_cdown", "pad_cleft", "pad_cright",
+
+ // indirectly mapped keys
+ "pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right",
+ "pad_circle_modifier",
}};
static const std::array<Values, NUM_INPUTS> All = {{
A, B, X, Y,
L, R, ZL, ZR,
START, SELECT, HOME,
DUP, DDOWN, DLEFT, DRIGHT,
- SUP, SDOWN, SLEFT, SRIGHT,
- CUP, CDOWN, CLEFT, CRIGHT
+ CUP, CDOWN, CLEFT, CRIGHT,
+ CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT,
+ CIRCLE_MODIFIER,
}};
}
@@ -46,6 +57,7 @@ struct Values {
// Controls
std::array<int, NativeInput::NUM_INPUTS> input_mappings;
+ float pad_circle_modifier_scale;
// Core
int frame_skip;