Merge branch 'master' into feature/savestates-2
This commit is contained in:
@@ -162,6 +162,20 @@ add_executable(citra-qt
|
||||
util/util.h
|
||||
)
|
||||
|
||||
if (ENABLE_FFMPEG_VIDEO_DUMPER)
|
||||
target_sources(citra-qt PRIVATE
|
||||
dumping/dumping_dialog.cpp
|
||||
dumping/dumping_dialog.h
|
||||
dumping/dumping_dialog.ui
|
||||
dumping/option_set_dialog.cpp
|
||||
dumping/option_set_dialog.h
|
||||
dumping/option_set_dialog.ui
|
||||
dumping/options_dialog.cpp
|
||||
dumping/options_dialog.h
|
||||
dumping/options_dialog.ui
|
||||
)
|
||||
endif()
|
||||
|
||||
file(GLOB COMPAT_LIST
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
|
@@ -104,8 +104,8 @@ void EmuThread::run() {
|
||||
}
|
||||
|
||||
OpenGLWindow::OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
|
||||
: QWindow(parent), event_handler(event_handler),
|
||||
context(new QOpenGLContext(shared_context->parent())) {
|
||||
: QWindow(parent), context(new QOpenGLContext(shared_context->parent())),
|
||||
event_handler(event_handler) {
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = shared_context->format();
|
||||
@@ -201,6 +201,8 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
|
||||
setLayout(layout);
|
||||
InputCommon::Init();
|
||||
|
||||
this->setMouseTracking(true);
|
||||
|
||||
GMainWindow* parent = GetMainWindow();
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
||||
}
|
||||
@@ -297,6 +299,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
|
||||
}
|
||||
QWidget::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||
@@ -307,6 +310,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||
const auto [x, y] = ScaleTouch(pos);
|
||||
this->TouchMoved(x, y);
|
||||
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
|
||||
QWidget::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
||||
|
@@ -95,6 +95,7 @@ void Config::ReadValues() {
|
||||
ReadMiscellaneousValues();
|
||||
ReadDebuggingValues();
|
||||
ReadWebServiceValues();
|
||||
ReadVideoDumpingValues();
|
||||
ReadUIValues();
|
||||
ReadUtilityValues();
|
||||
}
|
||||
@@ -456,8 +457,6 @@ void Config::ReadRendererValues() {
|
||||
ReadSetting(QStringLiteral("texture_filter_name"), QStringLiteral("none"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.texture_filter_factor =
|
||||
ReadSetting(QStringLiteral("texture_filter_factor"), 1).toInt();
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -484,7 +483,7 @@ void Config::ReadShortcutValues() {
|
||||
void Config::ReadSystemValues() {
|
||||
qt_config->beginGroup(QStringLiteral("System"));
|
||||
|
||||
Settings::values.is_new_3ds = ReadSetting(QStringLiteral("is_new_3ds"), false).toBool();
|
||||
Settings::values.is_new_3ds = ReadSetting(QStringLiteral("is_new_3ds"), true).toBool();
|
||||
Settings::values.region_value =
|
||||
ReadSetting(QStringLiteral("region_value"), Settings::REGION_VALUE_AUTO_SELECT).toInt();
|
||||
Settings::values.init_clock = static_cast<Settings::InitClock>(
|
||||
@@ -496,6 +495,49 @@ void Config::ReadSystemValues() {
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
// Options for variable bit rate live streaming taken from here:
|
||||
// https://developers.google.com/media/vp9/live-encoding
|
||||
const QString DEFAULT_VIDEO_ENCODER_OPTIONS =
|
||||
QStringLiteral("quality:realtime,speed:6,tile-columns:4,frame-parallel:1,threads:8,row-mt:1");
|
||||
const QString DEFAULT_AUDIO_ENCODER_OPTIONS = QString{};
|
||||
|
||||
void Config::ReadVideoDumpingValues() {
|
||||
qt_config->beginGroup(QStringLiteral("VideoDumping"));
|
||||
|
||||
Settings::values.output_format =
|
||||
ReadSetting(QStringLiteral("output_format"), QStringLiteral("webm"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.format_options =
|
||||
ReadSetting(QStringLiteral("format_options")).toString().toStdString();
|
||||
|
||||
Settings::values.video_encoder =
|
||||
ReadSetting(QStringLiteral("video_encoder"), QStringLiteral("libvpx-vp9"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
|
||||
Settings::values.video_encoder_options =
|
||||
ReadSetting(QStringLiteral("video_encoder_options"), DEFAULT_VIDEO_ENCODER_OPTIONS)
|
||||
.toString()
|
||||
.toStdString();
|
||||
|
||||
Settings::values.video_bitrate =
|
||||
ReadSetting(QStringLiteral("video_bitrate"), 2500000).toULongLong();
|
||||
|
||||
Settings::values.audio_encoder =
|
||||
ReadSetting(QStringLiteral("audio_encoder"), QStringLiteral("libvorbis"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.audio_encoder_options =
|
||||
ReadSetting(QStringLiteral("audio_encoder_options"), DEFAULT_AUDIO_ENCODER_OPTIONS)
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.audio_bitrate =
|
||||
ReadSetting(QStringLiteral("audio_bitrate"), 64000).toULongLong();
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::ReadUIValues() {
|
||||
qt_config->beginGroup(QStringLiteral("UI"));
|
||||
|
||||
@@ -530,6 +572,8 @@ void Config::ReadUIValues() {
|
||||
UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool();
|
||||
UISettings::values.pause_when_in_background =
|
||||
ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool();
|
||||
UISettings::values.hide_mouse =
|
||||
ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool();
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -628,6 +672,7 @@ void Config::SaveValues() {
|
||||
SaveMiscellaneousValues();
|
||||
SaveDebuggingValues();
|
||||
SaveWebServiceValues();
|
||||
SaveVideoDumpingValues();
|
||||
SaveUIValues();
|
||||
SaveUtilityValues();
|
||||
}
|
||||
@@ -895,8 +940,6 @@ void Config::SaveRendererValues() {
|
||||
WriteSetting(QStringLiteral("texture_filter_name"),
|
||||
QString::fromStdString(Settings::values.texture_filter_name),
|
||||
QStringLiteral("none"));
|
||||
WriteSetting(QStringLiteral("texture_filter_factor"), Settings::values.texture_filter_factor,
|
||||
1);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -923,7 +966,7 @@ void Config::SaveShortcutValues() {
|
||||
void Config::SaveSystemValues() {
|
||||
qt_config->beginGroup(QStringLiteral("System"));
|
||||
|
||||
WriteSetting(QStringLiteral("is_new_3ds"), Settings::values.is_new_3ds, false);
|
||||
WriteSetting(QStringLiteral("is_new_3ds"), Settings::values.is_new_3ds, true);
|
||||
WriteSetting(QStringLiteral("region_value"), Settings::values.region_value,
|
||||
Settings::REGION_VALUE_AUTO_SELECT);
|
||||
WriteSetting(QStringLiteral("init_clock"), static_cast<u32>(Settings::values.init_clock),
|
||||
@@ -934,6 +977,33 @@ void Config::SaveSystemValues() {
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::SaveVideoDumpingValues() {
|
||||
qt_config->beginGroup(QStringLiteral("VideoDumping"));
|
||||
|
||||
WriteSetting(QStringLiteral("output_format"),
|
||||
QString::fromStdString(Settings::values.output_format), QStringLiteral("webm"));
|
||||
WriteSetting(QStringLiteral("format_options"),
|
||||
QString::fromStdString(Settings::values.format_options));
|
||||
WriteSetting(QStringLiteral("video_encoder"),
|
||||
QString::fromStdString(Settings::values.video_encoder),
|
||||
QStringLiteral("libvpx-vp9"));
|
||||
WriteSetting(QStringLiteral("video_encoder_options"),
|
||||
QString::fromStdString(Settings::values.video_encoder_options),
|
||||
DEFAULT_VIDEO_ENCODER_OPTIONS);
|
||||
WriteSetting(QStringLiteral("video_bitrate"),
|
||||
static_cast<unsigned long long>(Settings::values.video_bitrate), 2500000);
|
||||
WriteSetting(QStringLiteral("audio_encoder"),
|
||||
QString::fromStdString(Settings::values.audio_encoder),
|
||||
QStringLiteral("libvorbis"));
|
||||
WriteSetting(QStringLiteral("audio_encoder_options"),
|
||||
QString::fromStdString(Settings::values.audio_encoder_options),
|
||||
DEFAULT_AUDIO_ENCODER_OPTIONS);
|
||||
WriteSetting(QStringLiteral("audio_bitrate"),
|
||||
static_cast<unsigned long long>(Settings::values.audio_bitrate), 64000);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::SaveUIValues() {
|
||||
qt_config->beginGroup(QStringLiteral("UI"));
|
||||
|
||||
@@ -962,6 +1032,7 @@ void Config::SaveUIValues() {
|
||||
WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false);
|
||||
WriteSetting(QStringLiteral("pauseWhenInBackground"),
|
||||
UISettings::values.pause_when_in_background, false);
|
||||
WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
@@ -44,6 +44,7 @@ private:
|
||||
void ReadUpdaterValues();
|
||||
void ReadUtilityValues();
|
||||
void ReadWebServiceValues();
|
||||
void ReadVideoDumpingValues();
|
||||
|
||||
void SaveValues();
|
||||
void SaveAudioValues();
|
||||
@@ -65,6 +66,7 @@ private:
|
||||
void SaveUpdaterValues();
|
||||
void SaveUtilityValues();
|
||||
void SaveWebServiceValues();
|
||||
void SaveVideoDumpingValues();
|
||||
|
||||
QVariant ReadSetting(const QString& name) const;
|
||||
QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
|
||||
|
@@ -8,17 +8,14 @@
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_enhancements.h"
|
||||
#include "video_core/renderer_opengl/post_processing_opengl.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filter_manager.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||
|
||||
ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
|
||||
: QWidget(parent), ui(new Ui::ConfigureEnhancements) {
|
||||
ui->setupUi(this);
|
||||
|
||||
for (const auto& filter : OpenGL::TextureFilterManager::TextureFilterMap())
|
||||
ui->texture_filter_combobox->addItem(QString::fromStdString(filter.first.data()));
|
||||
|
||||
connect(ui->texture_filter_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureEnhancements::updateTextureFilter);
|
||||
for (const auto& filter : OpenGL::TextureFilterer::GetFilterNames())
|
||||
ui->texture_filter_combobox->addItem(QString::fromStdString(filter.data()));
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
@@ -60,7 +57,6 @@ void ConfigureEnhancements::SetConfiguration() {
|
||||
ui->factor_3d->setValue(Settings::values.factor_3d);
|
||||
updateShaders(Settings::values.render_3d);
|
||||
ui->toggle_linear_filter->setChecked(Settings::values.filter_mode);
|
||||
ui->texture_scale_spinbox->setValue(Settings::values.texture_filter_factor);
|
||||
int tex_filter_idx = ui->texture_filter_combobox->findText(
|
||||
QString::fromStdString(Settings::values.texture_filter_name));
|
||||
if (tex_filter_idx == -1) {
|
||||
@@ -68,7 +64,6 @@ void ConfigureEnhancements::SetConfiguration() {
|
||||
} else {
|
||||
ui->texture_filter_combobox->setCurrentIndex(tex_filter_idx);
|
||||
}
|
||||
updateTextureFilter(tex_filter_idx);
|
||||
ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option));
|
||||
ui->swap_screen->setChecked(Settings::values.swap_screen);
|
||||
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_hw_shader &&
|
||||
@@ -105,17 +100,6 @@ void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_op
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureEnhancements::updateTextureFilter(int index) {
|
||||
if (index == -1)
|
||||
return;
|
||||
ui->texture_filter_group->setEnabled(index != 0);
|
||||
const auto& clamp = OpenGL::TextureFilterManager::TextureFilterMap()
|
||||
.at(ui->texture_filter_combobox->currentText().toStdString())
|
||||
.clamp_scale;
|
||||
ui->texture_scale_spinbox->setMinimum(clamp.min);
|
||||
ui->texture_scale_spinbox->setMaximum(clamp.max);
|
||||
}
|
||||
|
||||
void ConfigureEnhancements::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
@@ -130,7 +114,6 @@ void ConfigureEnhancements::ApplyConfiguration() {
|
||||
ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString();
|
||||
Settings::values.filter_mode = ui->toggle_linear_filter->isChecked();
|
||||
Settings::values.texture_filter_name = ui->texture_filter_combobox->currentText().toStdString();
|
||||
Settings::values.texture_filter_factor = ui->texture_scale_spinbox->value();
|
||||
Settings::values.layout_option =
|
||||
static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex());
|
||||
Settings::values.swap_screen = ui->swap_screen->isChecked();
|
||||
|
@@ -131,42 +131,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="texture_filter_group" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<property name="leftMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Texture Scale Factor</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="texture_scale_spinbox">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@@ -27,6 +27,7 @@ ConfigureGeneral::~ConfigureGeneral() = default;
|
||||
void ConfigureGeneral::SetConfiguration() {
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
|
||||
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse);
|
||||
|
||||
ui->toggle_update_check->setChecked(UISettings::values.check_for_update_on_start);
|
||||
ui->toggle_auto_update->setChecked(UISettings::values.update_on_close);
|
||||
@@ -55,6 +56,7 @@ void ConfigureGeneral::ResetDefaults() {
|
||||
void ConfigureGeneral::ApplyConfiguration() {
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
|
||||
UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
|
||||
|
||||
UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked();
|
||||
UISettings::values.update_on_close = ui->toggle_auto_update->isChecked();
|
||||
|
@@ -36,6 +36,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_hide_mouse">
|
||||
<property name="text">
|
||||
<string>Hide mouse on inactivity</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@@ -30,7 +30,7 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
|
||||
setLayout(layout);
|
||||
|
||||
using namespace InputCommon::CemuhookUDP;
|
||||
job = std::move(std::make_unique<CalibrationConfigurationJob>(
|
||||
job = std::make_unique<CalibrationConfigurationJob>(
|
||||
host, port, pad_index, client_id,
|
||||
[this](CalibrationConfigurationJob::Status status) {
|
||||
QString text;
|
||||
@@ -56,7 +56,7 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
|
||||
min_y = min_y_;
|
||||
max_x = max_x_;
|
||||
max_y = max_y_;
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
CalibrationConfigurationDialog::~CalibrationConfigurationDialog() = default;
|
||||
|
@@ -277,6 +277,8 @@ void ConfigureSystem::SetConfiguration() {
|
||||
ui->slider_clock_speed->setValue(SettingsToSlider(Settings::values.cpu_clock_percentage));
|
||||
ui->clock_display_label->setText(
|
||||
QStringLiteral("%1%").arg(Settings::values.cpu_clock_percentage));
|
||||
|
||||
ui->toggle_new_3ds->setChecked(Settings::values.is_new_3ds);
|
||||
}
|
||||
|
||||
void ConfigureSystem::ReadSystemSettings() {
|
||||
@@ -374,6 +376,8 @@ void ConfigureSystem::ApplyConfiguration() {
|
||||
Settings::values.init_clock =
|
||||
static_cast<Settings::InitClock>(ui->combo_init_clock->currentIndex());
|
||||
Settings::values.init_time = ui->edit_init_time->dateTime().toTime_t();
|
||||
|
||||
Settings::values.is_new_3ds = ui->toggle_new_3ds->isChecked();
|
||||
}
|
||||
|
||||
Settings::values.cpu_clock_percentage = SliderToSettings(ui->slider_clock_speed->value());
|
||||
|
@@ -22,14 +22,74 @@
|
||||
<string>System Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_username">
|
||||
<property name="text">
|
||||
<string>Username</string>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="combo_language">
|
||||
<property name="toolTip">
|
||||
<string>Note: this can be overridden when region setting is auto-select</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Japanese (日本語)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>English</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>French (français)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>German (Deutsch)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Italian (italiano)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Spanish (español)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Simplified Chinese (简体中文)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Korean (한국어)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Dutch (Nederlands)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Portuguese (português)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Russian (Русский)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Traditional Chinese (正體中文)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="edit_username">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
@@ -42,14 +102,33 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_birthday">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_username">
|
||||
<property name="text">
|
||||
<string>Birthday</string>
|
||||
<string>Username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="combo_sound">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Mono</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Stereo</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Surround</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_birthday2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_birthmonth">
|
||||
@@ -120,124 +199,38 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_language">
|
||||
<property name="text">
|
||||
<string>Language</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="combo_language">
|
||||
<property name="toolTip">
|
||||
<string>Note: this can be overridden when region setting is auto-select</string>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_birthday">
|
||||
<property name="text">
|
||||
<string>Birthday</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Japanese (日本語)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>English</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>French (français)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>German (Deutsch)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Italian (italiano)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Spanish (español)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Simplified Chinese (简体中文)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Korean (한국어)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Dutch (Nederlands)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Portuguese (português)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Russian (Русский)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Traditional Chinese (正體中文)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_sound">
|
||||
<property name="text">
|
||||
<string>Sound output mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="combo_sound">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Mono</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Stereo</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Surround</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<item row="6" column="1">
|
||||
<widget class="QComboBox" name="combo_country"/>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_country">
|
||||
<property name="text">
|
||||
<string>Country</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="combo_country"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_init_clock">
|
||||
<property name="text">
|
||||
<string>Clock</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="7" column="1">
|
||||
<widget class="QComboBox" name="combo_init_clock">
|
||||
<item>
|
||||
<property name="text">
|
||||
@@ -251,42 +244,35 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_init_clock">
|
||||
<property name="text">
|
||||
<string>Clock</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_init_time">
|
||||
<property name="text">
|
||||
<string>Startup time</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QDateTimeEdit" name="edit_init_time">
|
||||
<property name="displayFormat">
|
||||
<string>yyyy-MM-ddTHH:mm:ss</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_play_coins">
|
||||
<property name="text">
|
||||
<string>Play Coins:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<item row="9" column="1">
|
||||
<widget class="QSpinBox" name="spinBox_play_coins">
|
||||
<property name="maximum">
|
||||
<number>300</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_console_id">
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="label_play_coins">
|
||||
<property name="text">
|
||||
<string>Console ID:</string>
|
||||
<string>Play Coins:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<item row="12" column="1">
|
||||
<widget class="QPushButton" name="button_regenerate_console_id">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
@@ -302,6 +288,27 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label_console_id">
|
||||
<property name="text">
|
||||
<string>Console ID:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QDateTimeEdit" name="edit_init_time">
|
||||
<property name="displayFormat">
|
||||
<string>yyyy-MM-ddTHH:mm:ss</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="toggle_new_3ds">
|
||||
<property name="text">
|
||||
<string>Enable New 3DS mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
220
src/citra_qt/dumping/dumping_dialog.cpp
Normal file
220
src/citra_qt/dumping/dumping_dialog.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include "citra_qt/dumping/dumping_dialog.h"
|
||||
#include "citra_qt/dumping/options_dialog.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_dumping_dialog.h"
|
||||
|
||||
DumpingDialog::DumpingDialog(QWidget* parent)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::DumpingDialog>()) {
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
format_generic_options = VideoDumper::GetFormatGenericOptions();
|
||||
encoder_generic_options = VideoDumper::GetEncoderGenericOptions();
|
||||
|
||||
connect(ui->pathExplore, &QToolButton::clicked, this, &DumpingDialog::OnToolButtonClicked);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, [this] {
|
||||
if (ui->pathLineEdit->text().isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Citra"), tr("Please specify the output path."));
|
||||
return;
|
||||
}
|
||||
ApplyConfiguration();
|
||||
accept();
|
||||
});
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &DumpingDialog::reject);
|
||||
connect(ui->formatOptionsButton, &QToolButton::clicked, [this] {
|
||||
OpenOptionsDialog(formats.at(ui->formatComboBox->currentData().toUInt()).options,
|
||||
format_generic_options, ui->formatOptionsLineEdit);
|
||||
});
|
||||
connect(ui->videoEncoderOptionsButton, &QToolButton::clicked, [this] {
|
||||
OpenOptionsDialog(
|
||||
video_encoders.at(ui->videoEncoderComboBox->currentData().toUInt()).options,
|
||||
encoder_generic_options, ui->videoEncoderOptionsLineEdit);
|
||||
});
|
||||
connect(ui->audioEncoderOptionsButton, &QToolButton::clicked, [this] {
|
||||
OpenOptionsDialog(
|
||||
audio_encoders.at(ui->audioEncoderComboBox->currentData().toUInt()).options,
|
||||
encoder_generic_options, ui->audioEncoderOptionsLineEdit);
|
||||
});
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
connect(ui->formatComboBox, qOverload<int>(&QComboBox::currentIndexChanged), [this] {
|
||||
ui->pathLineEdit->setText(QString{});
|
||||
ui->formatOptionsLineEdit->clear();
|
||||
PopulateEncoders();
|
||||
});
|
||||
|
||||
connect(ui->videoEncoderComboBox, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this] { ui->videoEncoderOptionsLineEdit->clear(); });
|
||||
connect(ui->audioEncoderComboBox, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this] { ui->audioEncoderOptionsLineEdit->clear(); });
|
||||
}
|
||||
|
||||
DumpingDialog::~DumpingDialog() = default;
|
||||
|
||||
QString DumpingDialog::GetFilePath() const {
|
||||
return ui->pathLineEdit->text();
|
||||
}
|
||||
|
||||
void DumpingDialog::Populate() {
|
||||
formats = VideoDumper::ListFormats();
|
||||
video_encoders = VideoDumper::ListEncoders(AVMEDIA_TYPE_VIDEO);
|
||||
audio_encoders = VideoDumper::ListEncoders(AVMEDIA_TYPE_AUDIO);
|
||||
|
||||
// Check that these are not empty
|
||||
QString missing;
|
||||
if (formats.empty()) {
|
||||
missing = tr("output formats");
|
||||
}
|
||||
if (video_encoders.empty()) {
|
||||
missing = tr("video encoders");
|
||||
}
|
||||
if (audio_encoders.empty()) {
|
||||
missing = tr("audio encoders");
|
||||
}
|
||||
|
||||
if (!missing.isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Citra"),
|
||||
tr("Could not find any available %1.\nPlease check your FFmpeg "
|
||||
"installation used for compilation.")
|
||||
.arg(missing));
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate formats
|
||||
for (std::size_t i = 0; i < formats.size(); ++i) {
|
||||
const auto& format = formats[i];
|
||||
|
||||
// Check format: only formats that have video encoders and audio encoders are displayed
|
||||
bool has_video = false;
|
||||
for (const auto& video_encoder : video_encoders) {
|
||||
if (format.supported_video_codecs.count(video_encoder.codec)) {
|
||||
has_video = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_video)
|
||||
continue;
|
||||
|
||||
bool has_audio = false;
|
||||
for (const auto& audio_encoder : audio_encoders) {
|
||||
if (format.supported_audio_codecs.count(audio_encoder.codec)) {
|
||||
has_audio = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_audio)
|
||||
continue;
|
||||
|
||||
ui->formatComboBox->addItem(tr("%1 (%2)").arg(QString::fromStdString(format.long_name),
|
||||
QString::fromStdString(format.name)),
|
||||
static_cast<unsigned long long>(i));
|
||||
if (format.name == Settings::values.output_format) {
|
||||
ui->formatComboBox->setCurrentIndex(ui->formatComboBox->count() - 1);
|
||||
}
|
||||
}
|
||||
PopulateEncoders();
|
||||
}
|
||||
|
||||
void DumpingDialog::PopulateEncoders() {
|
||||
const auto& format = formats.at(ui->formatComboBox->currentData().toUInt());
|
||||
|
||||
ui->videoEncoderComboBox->clear();
|
||||
for (std::size_t i = 0; i < video_encoders.size(); ++i) {
|
||||
const auto& video_encoder = video_encoders[i];
|
||||
if (!format.supported_video_codecs.count(video_encoder.codec)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ui->videoEncoderComboBox->addItem(
|
||||
tr("%1 (%2)").arg(QString::fromStdString(video_encoder.long_name),
|
||||
QString::fromStdString(video_encoder.name)),
|
||||
static_cast<unsigned long long>(i));
|
||||
if (video_encoder.name == Settings::values.video_encoder) {
|
||||
ui->videoEncoderComboBox->setCurrentIndex(ui->videoEncoderComboBox->count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
ui->audioEncoderComboBox->clear();
|
||||
for (std::size_t i = 0; i < audio_encoders.size(); ++i) {
|
||||
const auto& audio_encoder = audio_encoders[i];
|
||||
if (!format.supported_audio_codecs.count(audio_encoder.codec)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ui->audioEncoderComboBox->addItem(
|
||||
tr("%1 (%2)").arg(QString::fromStdString(audio_encoder.long_name),
|
||||
QString::fromStdString(audio_encoder.name)),
|
||||
static_cast<unsigned long long>(i));
|
||||
if (audio_encoder.name == Settings::values.audio_encoder) {
|
||||
ui->audioEncoderComboBox->setCurrentIndex(ui->audioEncoderComboBox->count() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DumpingDialog::OnToolButtonClicked() {
|
||||
const auto& format = formats.at(ui->formatComboBox->currentData().toUInt());
|
||||
|
||||
QString extensions;
|
||||
for (const auto& ext : format.extensions) {
|
||||
if (!extensions.isEmpty()) {
|
||||
extensions.append(QLatin1Char{' '});
|
||||
}
|
||||
extensions.append(QStringLiteral("*.%1").arg(QString::fromStdString(ext)));
|
||||
}
|
||||
|
||||
const auto path = QFileDialog::getSaveFileName(
|
||||
this, tr("Select Video Output Path"), last_path,
|
||||
tr("%1 (%2)").arg(QString::fromStdString(format.long_name), extensions));
|
||||
if (!path.isEmpty()) {
|
||||
last_path = QFileInfo(ui->pathLineEdit->text()).path();
|
||||
ui->pathLineEdit->setText(path);
|
||||
}
|
||||
}
|
||||
|
||||
void DumpingDialog::OpenOptionsDialog(const std::vector<VideoDumper::OptionInfo>& specific_options,
|
||||
const std::vector<VideoDumper::OptionInfo>& generic_options,
|
||||
QLineEdit* line_edit) {
|
||||
OptionsDialog dialog(this, specific_options, generic_options, line_edit->text().toStdString());
|
||||
if (dialog.exec() != QDialog::DialogCode::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
line_edit->setText(QString::fromStdString(dialog.GetCurrentValue()));
|
||||
}
|
||||
|
||||
void DumpingDialog::SetConfiguration() {
|
||||
Populate();
|
||||
|
||||
ui->formatOptionsLineEdit->setText(QString::fromStdString(Settings::values.format_options));
|
||||
ui->videoEncoderOptionsLineEdit->setText(
|
||||
QString::fromStdString(Settings::values.video_encoder_options));
|
||||
ui->audioEncoderOptionsLineEdit->setText(
|
||||
QString::fromStdString(Settings::values.audio_encoder_options));
|
||||
last_path = UISettings::values.video_dumping_path;
|
||||
ui->videoBitrateSpinBox->setValue(static_cast<int>(Settings::values.video_bitrate));
|
||||
ui->audioBitrateSpinBox->setValue(static_cast<int>(Settings::values.audio_bitrate));
|
||||
}
|
||||
|
||||
void DumpingDialog::ApplyConfiguration() {
|
||||
Settings::values.output_format = formats.at(ui->formatComboBox->currentData().toUInt()).name;
|
||||
Settings::values.format_options = ui->formatOptionsLineEdit->text().toStdString();
|
||||
Settings::values.video_encoder =
|
||||
video_encoders.at(ui->videoEncoderComboBox->currentData().toUInt()).name;
|
||||
Settings::values.video_encoder_options = ui->videoEncoderOptionsLineEdit->text().toStdString();
|
||||
Settings::values.video_bitrate = ui->videoBitrateSpinBox->value();
|
||||
Settings::values.audio_encoder =
|
||||
audio_encoders.at(ui->audioEncoderComboBox->currentData().toUInt()).name;
|
||||
Settings::values.audio_encoder_options = ui->audioEncoderOptionsLineEdit->text().toStdString();
|
||||
Settings::values.audio_bitrate = ui->audioBitrateSpinBox->value();
|
||||
UISettings::values.video_dumping_path = last_path;
|
||||
Settings::Apply();
|
||||
}
|
43
src/citra_qt/dumping/dumping_dialog.h
Normal file
43
src/citra_qt/dumping/dumping_dialog.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
#include "core/dumping/ffmpeg_backend.h"
|
||||
|
||||
namespace Ui {
|
||||
class DumpingDialog;
|
||||
}
|
||||
|
||||
class QLineEdit;
|
||||
|
||||
class DumpingDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DumpingDialog(QWidget* parent);
|
||||
~DumpingDialog() override;
|
||||
|
||||
QString GetFilePath() const;
|
||||
void ApplyConfiguration();
|
||||
|
||||
private:
|
||||
void Populate();
|
||||
void PopulateEncoders();
|
||||
void SetConfiguration();
|
||||
void OnToolButtonClicked();
|
||||
void OpenOptionsDialog(const std::vector<VideoDumper::OptionInfo>& specific_options,
|
||||
const std::vector<VideoDumper::OptionInfo>& generic_options,
|
||||
QLineEdit* line_edit);
|
||||
|
||||
std::unique_ptr<Ui::DumpingDialog> ui;
|
||||
|
||||
QString last_path;
|
||||
|
||||
std::vector<VideoDumper::FormatInfo> formats;
|
||||
std::vector<VideoDumper::OptionInfo> format_generic_options;
|
||||
std::vector<VideoDumper::EncoderInfo> video_encoders;
|
||||
std::vector<VideoDumper::EncoderInfo> audio_encoders;
|
||||
std::vector<VideoDumper::OptionInfo> encoder_generic_options;
|
||||
};
|
213
src/citra_qt/dumping/dumping_dialog.ui
Normal file
213
src/citra_qt/dumping/dumping_dialog.ui
Normal file
@@ -0,0 +1,213 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DumpingDialog</class>
|
||||
<widget class="QDialog" name="DumpingDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>420</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dump Video</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox">
|
||||
<property name="title">
|
||||
<string>Output</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Format:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="formatComboBox"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Options:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="formatOptionsLineEdit"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QToolButton" name="formatOptionsButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="pathLineEdit"/>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QToolButton" name="pathExplore">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox">
|
||||
<property name="title">
|
||||
<string>Video</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Encoder:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="videoEncoderComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Options:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="videoEncoderOptionsLineEdit"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QToolButton" name="videoEncoderOptionsButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Bitrate:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="videoBitrateSpinBox">
|
||||
<property name="maximum">
|
||||
<number>10000000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>bps</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox">
|
||||
<property name="title">
|
||||
<string>Audio</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Encoder:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="audioEncoderComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Options:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="audioEncoderOptionsLineEdit"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QToolButton" name="audioEncoderOptionsButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Bitrate:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="audioBitrateSpinBox">
|
||||
<property name="maximum">
|
||||
<number>1000000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>bps</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</ui>
|
299
src/citra_qt/dumping/option_set_dialog.cpp
Normal file
299
src/citra_qt/dumping/option_set_dialog.cpp
Normal file
@@ -0,0 +1,299 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <unordered_map>
|
||||
#include <QCheckBox>
|
||||
#include <QStringList>
|
||||
#include "citra_qt/dumping/option_set_dialog.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "ui_option_set_dialog.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/pixdesc.h>
|
||||
}
|
||||
|
||||
static const std::unordered_map<AVOptionType, const char*> TypeNameMap{{
|
||||
{AV_OPT_TYPE_BOOL, QT_TR_NOOP("boolean")},
|
||||
{AV_OPT_TYPE_FLAGS, QT_TR_NOOP("flags")},
|
||||
{AV_OPT_TYPE_DURATION, QT_TR_NOOP("duration")},
|
||||
{AV_OPT_TYPE_INT, QT_TR_NOOP("int")},
|
||||
{AV_OPT_TYPE_UINT64, QT_TR_NOOP("uint64")},
|
||||
{AV_OPT_TYPE_INT64, QT_TR_NOOP("int64")},
|
||||
{AV_OPT_TYPE_DOUBLE, QT_TR_NOOP("double")},
|
||||
{AV_OPT_TYPE_FLOAT, QT_TR_NOOP("float")},
|
||||
{AV_OPT_TYPE_RATIONAL, QT_TR_NOOP("rational")},
|
||||
{AV_OPT_TYPE_PIXEL_FMT, QT_TR_NOOP("pixel format")},
|
||||
{AV_OPT_TYPE_SAMPLE_FMT, QT_TR_NOOP("sample format")},
|
||||
{AV_OPT_TYPE_COLOR, QT_TR_NOOP("color")},
|
||||
{AV_OPT_TYPE_IMAGE_SIZE, QT_TR_NOOP("image size")},
|
||||
{AV_OPT_TYPE_STRING, QT_TR_NOOP("string")},
|
||||
{AV_OPT_TYPE_DICT, QT_TR_NOOP("dictionary")},
|
||||
{AV_OPT_TYPE_VIDEO_RATE, QT_TR_NOOP("video rate")},
|
||||
{AV_OPT_TYPE_CHANNEL_LAYOUT, QT_TR_NOOP("channel layout")},
|
||||
}};
|
||||
|
||||
static const std::unordered_map<AVOptionType, const char*> TypeDescriptionMap{{
|
||||
{AV_OPT_TYPE_DURATION, QT_TR_NOOP("[<hours (integer)>:][<minutes (integer):]<seconds "
|
||||
"(decimal)> e.g. 03:00.5 (3min 500ms)")},
|
||||
{AV_OPT_TYPE_RATIONAL, QT_TR_NOOP("<num>/<den>")},
|
||||
{AV_OPT_TYPE_COLOR, QT_TR_NOOP("0xRRGGBBAA")},
|
||||
{AV_OPT_TYPE_IMAGE_SIZE, QT_TR_NOOP("<width>x<height>, or preset values like 'vga'.")},
|
||||
{AV_OPT_TYPE_DICT,
|
||||
QT_TR_NOOP("Comma-splitted list of <key>=<value>. Do not put spaces.")},
|
||||
{AV_OPT_TYPE_VIDEO_RATE, QT_TR_NOOP("<num>/<den>, or preset values like 'pal'.")},
|
||||
{AV_OPT_TYPE_CHANNEL_LAYOUT, QT_TR_NOOP("Hexadecimal channel layout mask starting with '0x'.")},
|
||||
}};
|
||||
|
||||
/// Get the preset values of an option. returns {display value, real value}
|
||||
std::vector<std::pair<QString, QString>> GetPresetValues(const VideoDumper::OptionInfo& option) {
|
||||
switch (option.type) {
|
||||
case AV_OPT_TYPE_BOOL: {
|
||||
return {{QObject::tr("auto"), QStringLiteral("auto")},
|
||||
{QObject::tr("true"), QStringLiteral("true")},
|
||||
{QObject::tr("false"), QStringLiteral("false")}};
|
||||
}
|
||||
case AV_OPT_TYPE_PIXEL_FMT: {
|
||||
std::vector<std::pair<QString, QString>> out{{QObject::tr("none"), QStringLiteral("none")}};
|
||||
// List all pixel formats
|
||||
const AVPixFmtDescriptor* current = nullptr;
|
||||
while ((current = av_pix_fmt_desc_next(current))) {
|
||||
out.emplace_back(QString::fromUtf8(current->name), QString::fromUtf8(current->name));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
case AV_OPT_TYPE_SAMPLE_FMT: {
|
||||
std::vector<std::pair<QString, QString>> out{{QObject::tr("none"), QStringLiteral("none")}};
|
||||
// List all sample formats
|
||||
int current = 0;
|
||||
while (true) {
|
||||
const char* name = av_get_sample_fmt_name(static_cast<AVSampleFormat>(current));
|
||||
if (name == nullptr)
|
||||
break;
|
||||
out.emplace_back(QString::fromUtf8(name), QString::fromUtf8(name));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
case AV_OPT_TYPE_INT:
|
||||
case AV_OPT_TYPE_INT64:
|
||||
case AV_OPT_TYPE_UINT64: {
|
||||
std::vector<std::pair<QString, QString>> out;
|
||||
// Add in all named constants
|
||||
for (const auto& constant : option.named_constants) {
|
||||
out.emplace_back(QObject::tr("%1 (0x%2)")
|
||||
.arg(QString::fromStdString(constant.name))
|
||||
.arg(constant.value, 0, 16),
|
||||
QString::fromStdString(constant.name));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void OptionSetDialog::InitializeUI(const std::string& initial_value) {
|
||||
const QString type_name =
|
||||
TypeNameMap.count(option.type) ? tr(TypeNameMap.at(option.type)) : tr("unknown");
|
||||
ui->nameLabel->setText(tr("%1 <%2> %3")
|
||||
.arg(QString::fromStdString(option.name), type_name,
|
||||
QString::fromStdString(option.description)));
|
||||
if (TypeDescriptionMap.count(option.type)) {
|
||||
ui->formatLabel->setVisible(true);
|
||||
ui->formatLabel->setText(tr(TypeDescriptionMap.at(option.type)));
|
||||
}
|
||||
|
||||
if (option.type == AV_OPT_TYPE_INT || option.type == AV_OPT_TYPE_INT64 ||
|
||||
option.type == AV_OPT_TYPE_UINT64 || option.type == AV_OPT_TYPE_FLOAT ||
|
||||
option.type == AV_OPT_TYPE_DOUBLE || option.type == AV_OPT_TYPE_DURATION ||
|
||||
option.type == AV_OPT_TYPE_RATIONAL) { // scalar types
|
||||
|
||||
ui->formatLabel->setVisible(true);
|
||||
if (!ui->formatLabel->text().isEmpty()) {
|
||||
ui->formatLabel->text().append(QStringLiteral("\n"));
|
||||
}
|
||||
ui->formatLabel->setText(
|
||||
ui->formatLabel->text().append(tr("Range: %1 - %2").arg(option.min).arg(option.max)));
|
||||
}
|
||||
|
||||
// Decide and initialize layout
|
||||
if (option.type == AV_OPT_TYPE_BOOL || option.type == AV_OPT_TYPE_PIXEL_FMT ||
|
||||
option.type == AV_OPT_TYPE_SAMPLE_FMT ||
|
||||
((option.type == AV_OPT_TYPE_INT || option.type == AV_OPT_TYPE_INT64 ||
|
||||
option.type == AV_OPT_TYPE_UINT64) &&
|
||||
!option.named_constants.empty())) { // Use the combobox layout
|
||||
|
||||
layout_type = 1;
|
||||
ui->comboBox->setVisible(true);
|
||||
ui->comboBoxHelpLabel->setVisible(true);
|
||||
|
||||
QString real_initial_value = QString::fromStdString(initial_value);
|
||||
if (option.type == AV_OPT_TYPE_INT || option.type == AV_OPT_TYPE_INT64 ||
|
||||
option.type == AV_OPT_TYPE_UINT64) {
|
||||
|
||||
// Get the name of the initial value
|
||||
try {
|
||||
s64 initial_value_integer = std::stoll(initial_value, nullptr, 0);
|
||||
for (const auto& constant : option.named_constants) {
|
||||
if (constant.value == initial_value_integer) {
|
||||
real_initial_value = QString::fromStdString(constant.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// Not convertible to integer, ignore
|
||||
}
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (const auto& [display, value] : GetPresetValues(option)) {
|
||||
ui->comboBox->addItem(display, value);
|
||||
if (value == real_initial_value) {
|
||||
found = true;
|
||||
ui->comboBox->setCurrentIndex(ui->comboBox->count() - 1);
|
||||
}
|
||||
}
|
||||
ui->comboBox->addItem(tr("custom"));
|
||||
|
||||
if (!found) {
|
||||
ui->comboBox->setCurrentIndex(ui->comboBox->count() - 1);
|
||||
ui->lineEdit->setText(QString::fromStdString(initial_value));
|
||||
}
|
||||
|
||||
UpdateUIDisplay();
|
||||
|
||||
connect(ui->comboBox, &QComboBox::currentTextChanged, this,
|
||||
&OptionSetDialog::UpdateUIDisplay);
|
||||
} else if (option.type == AV_OPT_TYPE_FLAGS &&
|
||||
!option.named_constants.empty()) { // Use the check boxes layout
|
||||
|
||||
layout_type = 2;
|
||||
|
||||
for (const auto& constant : option.named_constants) {
|
||||
auto* checkBox = new QCheckBox(tr("%1 (0x%2) %3")
|
||||
.arg(QString::fromStdString(constant.name))
|
||||
.arg(constant.value, 0, 16)
|
||||
.arg(QString::fromStdString(constant.description)));
|
||||
checkBox->setProperty("value", static_cast<unsigned long long>(constant.value));
|
||||
checkBox->setProperty("name", QString::fromStdString(constant.name));
|
||||
ui->checkBoxLayout->addWidget(checkBox);
|
||||
}
|
||||
SetCheckBoxDefaults(initial_value);
|
||||
} else { // Use the line edit layout
|
||||
layout_type = 0;
|
||||
ui->lineEdit->setVisible(true);
|
||||
ui->lineEdit->setText(QString::fromStdString(initial_value));
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void OptionSetDialog::SetCheckBoxDefaults(const std::string& initial_value) {
|
||||
if (initial_value.size() >= 2 &&
|
||||
(initial_value.substr(0, 2) == "0x" || initial_value.substr(0, 2) == "0X")) {
|
||||
// This is a hex mask
|
||||
try {
|
||||
u64 value = std::stoull(initial_value, nullptr, 16);
|
||||
for (int i = 0; i < ui->checkBoxLayout->count(); ++i) {
|
||||
auto* checkBox = qobject_cast<QCheckBox*>(ui->checkBoxLayout->itemAt(i)->widget());
|
||||
if (checkBox) {
|
||||
checkBox->setChecked(value & checkBox->property("value").toULongLong());
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
LOG_ERROR(Frontend, "Could not convert {} to number", initial_value);
|
||||
}
|
||||
} else {
|
||||
// This is a combination of constants, splitted with + or |
|
||||
std::vector<std::string> tmp;
|
||||
Common::SplitString(initial_value, '+', tmp);
|
||||
|
||||
std::vector<std::string> out;
|
||||
std::vector<std::string> tmp2;
|
||||
for (const auto& str : tmp) {
|
||||
Common::SplitString(str, '|', tmp2);
|
||||
out.insert(out.end(), tmp2.begin(), tmp2.end());
|
||||
}
|
||||
for (int i = 0; i < ui->checkBoxLayout->count(); ++i) {
|
||||
auto* checkBox = qobject_cast<QCheckBox*>(ui->checkBoxLayout->itemAt(i)->widget());
|
||||
if (checkBox) {
|
||||
checkBox->setChecked(
|
||||
std::find(out.begin(), out.end(),
|
||||
checkBox->property("name").toString().toStdString()) != out.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OptionSetDialog::UpdateUIDisplay() {
|
||||
if (layout_type != 1)
|
||||
return;
|
||||
|
||||
if (ui->comboBox->currentIndex() == ui->comboBox->count() - 1) { // custom
|
||||
ui->comboBoxHelpLabel->setVisible(false);
|
||||
ui->lineEdit->setVisible(true);
|
||||
adjustSize();
|
||||
return;
|
||||
}
|
||||
|
||||
ui->lineEdit->setVisible(false);
|
||||
for (const auto& constant : option.named_constants) {
|
||||
if (constant.name == ui->comboBox->currentData().toString().toStdString()) {
|
||||
ui->comboBoxHelpLabel->setVisible(true);
|
||||
ui->comboBoxHelpLabel->setText(QString::fromStdString(constant.description));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<bool, std::string> OptionSetDialog::GetCurrentValue() {
|
||||
if (!is_set) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (layout_type) {
|
||||
case 0: // line edit layout
|
||||
return {true, ui->lineEdit->text().toStdString()};
|
||||
case 1: // combo box layout
|
||||
if (ui->comboBox->currentIndex() == ui->comboBox->count() - 1) {
|
||||
return {true, ui->lineEdit->text().toStdString()}; // custom
|
||||
}
|
||||
return {true, ui->comboBox->currentData().toString().toStdString()};
|
||||
case 2: { // check boxes layout
|
||||
std::string out;
|
||||
for (int i = 0; i < ui->checkBoxLayout->count(); ++i) {
|
||||
auto* checkBox = qobject_cast<QCheckBox*>(ui->checkBoxLayout->itemAt(i)->widget());
|
||||
if (checkBox && checkBox->isChecked()) {
|
||||
if (!out.empty()) {
|
||||
out.append("+");
|
||||
}
|
||||
out.append(checkBox->property("name").toString().toStdString());
|
||||
}
|
||||
}
|
||||
if (out.empty()) {
|
||||
out = "0x0";
|
||||
}
|
||||
return {true, out};
|
||||
}
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
OptionSetDialog::OptionSetDialog(QWidget* parent, VideoDumper::OptionInfo option_,
|
||||
const std::string& initial_value)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::OptionSetDialog>()), option(std::move(option_)) {
|
||||
|
||||
ui->setupUi(this);
|
||||
InitializeUI(initial_value);
|
||||
|
||||
connect(ui->unsetButton, &QPushButton::clicked, [this] {
|
||||
is_set = false;
|
||||
accept();
|
||||
});
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &OptionSetDialog::accept);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &OptionSetDialog::reject);
|
||||
}
|
||||
|
||||
OptionSetDialog::~OptionSetDialog() = default;
|
33
src/citra_qt/dumping/option_set_dialog.h
Normal file
33
src/citra_qt/dumping/option_set_dialog.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
#include "core/dumping/ffmpeg_backend.h"
|
||||
|
||||
namespace Ui {
|
||||
class OptionSetDialog;
|
||||
}
|
||||
|
||||
class OptionSetDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OptionSetDialog(QWidget* parent, VideoDumper::OptionInfo option,
|
||||
const std::string& initial_value);
|
||||
~OptionSetDialog() override;
|
||||
|
||||
// {is_set, value}
|
||||
std::pair<bool, std::string> GetCurrentValue();
|
||||
|
||||
private:
|
||||
void InitializeUI(const std::string& initial_value);
|
||||
void SetCheckBoxDefaults(const std::string& initial_value);
|
||||
void UpdateUIDisplay();
|
||||
|
||||
std::unique_ptr<Ui::OptionSetDialog> ui;
|
||||
VideoDumper::OptionInfo option;
|
||||
bool is_set = true;
|
||||
int layout_type = -1; // 0 - line edit, 1 - combo box, 2 - flags (check boxes)
|
||||
};
|
89
src/citra_qt/dumping/option_set_dialog.ui
Normal file
89
src/citra_qt/dumping/option_set_dialog.ui
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OptionSetDialog</class>
|
||||
<widget class="QDialog" name="OptionSetDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>150</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="nameLabel"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="formatLabel">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="comboBoxLayout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="comboBoxHelpLabel">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="checkBoxLayout"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="unsetButton">
|
||||
<property name="text">
|
||||
<string>Unset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</ui>
|
68
src/citra_qt/dumping/options_dialog.cpp
Normal file
68
src/citra_qt/dumping/options_dialog.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QTreeWidgetItem>
|
||||
#include "citra_qt/dumping/option_set_dialog.h"
|
||||
#include "citra_qt/dumping/options_dialog.h"
|
||||
#include "ui_options_dialog.h"
|
||||
|
||||
constexpr char UNSET_TEXT[] = QT_TR_NOOP("[not set]");
|
||||
|
||||
void OptionsDialog::PopulateOptions() {
|
||||
const auto& options = ui->specificRadioButton->isChecked() ? specific_options : generic_options;
|
||||
ui->main->clear();
|
||||
ui->main->setSortingEnabled(false);
|
||||
for (std::size_t i = 0; i < options.size(); ++i) {
|
||||
const auto& option = options.at(i);
|
||||
auto* item = new QTreeWidgetItem(
|
||||
{QString::fromStdString(option.name), QString::fromStdString(current_values.Get(
|
||||
option.name, tr(UNSET_TEXT).toStdString()))});
|
||||
item->setData(1, Qt::UserRole, static_cast<unsigned long long>(i)); // ID
|
||||
ui->main->addTopLevelItem(item);
|
||||
}
|
||||
ui->main->setSortingEnabled(true);
|
||||
ui->main->sortItems(0, Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
void OptionsDialog::OnSetOptionValue(QTreeWidgetItem* item) {
|
||||
const auto& options = ui->specificRadioButton->isChecked() ? specific_options : generic_options;
|
||||
const int id = item->data(1, Qt::UserRole).toInt();
|
||||
OptionSetDialog dialog(this, options[id],
|
||||
current_values.Get(options[id].name, options[id].default_value));
|
||||
if (dialog.exec() != QDialog::DialogCode::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& [is_set, value] = dialog.GetCurrentValue();
|
||||
if (is_set) {
|
||||
current_values.Set(options[id].name, value);
|
||||
} else {
|
||||
current_values.Erase(options[id].name);
|
||||
}
|
||||
item->setText(1, is_set ? QString::fromStdString(value) : tr(UNSET_TEXT));
|
||||
}
|
||||
|
||||
std::string OptionsDialog::GetCurrentValue() const {
|
||||
return current_values.Serialize();
|
||||
}
|
||||
|
||||
OptionsDialog::OptionsDialog(QWidget* parent,
|
||||
std::vector<VideoDumper::OptionInfo> specific_options_,
|
||||
std::vector<VideoDumper::OptionInfo> generic_options_,
|
||||
const std::string& current_value)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::OptionsDialog>()),
|
||||
specific_options(std::move(specific_options_)), generic_options(std::move(generic_options_)),
|
||||
current_values(current_value) {
|
||||
|
||||
ui->setupUi(this);
|
||||
PopulateOptions();
|
||||
|
||||
connect(ui->main, &QTreeWidget::itemDoubleClicked,
|
||||
[this](QTreeWidgetItem* item, int column) { OnSetOptionValue(item); });
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &OptionsDialog::accept);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &OptionsDialog::reject);
|
||||
connect(ui->specificRadioButton, &QRadioButton::toggled, this, &OptionsDialog::PopulateOptions);
|
||||
}
|
||||
|
||||
OptionsDialog::~OptionsDialog() = default;
|
36
src/citra_qt/dumping/options_dialog.h
Normal file
36
src/citra_qt/dumping/options_dialog.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <QDialog>
|
||||
#include "common/param_package.h"
|
||||
#include "core/dumping/ffmpeg_backend.h"
|
||||
|
||||
class QTreeWidgetItem;
|
||||
|
||||
namespace Ui {
|
||||
class OptionsDialog;
|
||||
}
|
||||
|
||||
class OptionsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OptionsDialog(QWidget* parent, std::vector<VideoDumper::OptionInfo> specific_options,
|
||||
std::vector<VideoDumper::OptionInfo> generic_options,
|
||||
const std::string& current_value);
|
||||
~OptionsDialog() override;
|
||||
|
||||
std::string GetCurrentValue() const;
|
||||
|
||||
private:
|
||||
void PopulateOptions();
|
||||
void OnSetOptionValue(QTreeWidgetItem* item);
|
||||
|
||||
std::unique_ptr<Ui::OptionsDialog> ui;
|
||||
std::vector<VideoDumper::OptionInfo> specific_options;
|
||||
std::vector<VideoDumper::OptionInfo> generic_options;
|
||||
Common::ParamPackage current_values;
|
||||
};
|
71
src/citra_qt/dumping/options_dialog.ui
Normal file
71
src/citra_qt/dumping/options_dialog.ui
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OptionsDialog</class>
|
||||
<widget class="QDialog" name="OptionsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>650</width>
|
||||
<height>350</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel">
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Double click to see the description and change the values of the options.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="specificRadioButton">
|
||||
<property name="text">
|
||||
<string>Specific</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="genericRadioButton">
|
||||
<property name="text">
|
||||
<string>Generic</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="main">
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</ui>
|
@@ -89,6 +89,10 @@
|
||||
#include "citra_qt/discord_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FFMPEG_VIDEO_DUMPER
|
||||
#include "citra_qt/dumping/dumping_dialog.h"
|
||||
#endif
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
|
||||
#endif
|
||||
@@ -100,6 +104,8 @@ __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
}
|
||||
#endif
|
||||
|
||||
constexpr int default_mouse_timeout = 2500;
|
||||
|
||||
/**
|
||||
* "Callouts" are one-time instructional messages shown to the user. In the config settings, there
|
||||
* is a bitfield "callout_flags" options, used to track if a message has already been shown to the
|
||||
@@ -193,6 +199,14 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
|
||||
// Show one-time "callout" messages to the user
|
||||
ShowTelemetryCallout();
|
||||
|
||||
// make sure menubar has the arrow cursor instead of inheriting from this
|
||||
ui.menubar->setCursor(QCursor());
|
||||
statusBar()->setCursor(QCursor());
|
||||
|
||||
mouse_hide_timer.setInterval(default_mouse_timeout);
|
||||
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
|
||||
connect(ui.menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
|
||||
|
||||
if (UISettings::values.check_for_update_on_start) {
|
||||
CheckForUpdates();
|
||||
}
|
||||
@@ -713,9 +727,7 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
connect(ui.action_Capture_Screenshot, &QAction::triggered, this,
|
||||
&GMainWindow::OnCaptureScreenshot);
|
||||
|
||||
#ifndef ENABLE_FFMPEG_VIDEO_DUMPER
|
||||
ui.action_Dump_Video->setEnabled(false);
|
||||
#endif
|
||||
#ifdef ENABLE_FFMPEG_VIDEO_DUMPER
|
||||
connect(ui.action_Dump_Video, &QAction::triggered, [this] {
|
||||
if (ui.action_Dump_Video->isChecked()) {
|
||||
OnStartVideoDumping();
|
||||
@@ -723,6 +735,9 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
OnStopVideoDumping();
|
||||
}
|
||||
});
|
||||
#else
|
||||
ui.action_Dump_Video->setEnabled(false);
|
||||
#endif
|
||||
|
||||
// Help
|
||||
connect(ui.action_Open_Citra_Folder, &QAction::triggered, this,
|
||||
@@ -994,6 +1009,13 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||
}
|
||||
status_bar_update_timer.start(2000);
|
||||
|
||||
if (UISettings::values.hide_mouse) {
|
||||
mouse_hide_timer.start();
|
||||
setMouseTracking(true);
|
||||
ui.centralwidget->setMouseTracking(true);
|
||||
ui.menubar->setMouseTracking(true);
|
||||
}
|
||||
|
||||
// show and hide the render_window to create the context
|
||||
render_window->show();
|
||||
render_window->hide();
|
||||
@@ -1009,8 +1031,14 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||
if (video_dumping_on_start) {
|
||||
Layout::FramebufferLayout layout{
|
||||
Layout::FrameLayoutFromResolutionScale(VideoCore::GetResolutionScaleFactor())};
|
||||
Core::System::GetInstance().VideoDumper().StartDumping(video_dumping_path.toStdString(),
|
||||
"webm", layout);
|
||||
if (!Core::System::GetInstance().VideoDumper().StartDumping(
|
||||
video_dumping_path.toStdString(), layout)) {
|
||||
|
||||
QMessageBox::critical(
|
||||
this, tr("Citra"),
|
||||
tr("Could not start video dumping.<br>Refer to the log for details."));
|
||||
ui.action_Dump_Video->setChecked(false);
|
||||
}
|
||||
video_dumping_on_start = false;
|
||||
video_dumping_path.clear();
|
||||
}
|
||||
@@ -1026,11 +1054,13 @@ void GMainWindow::ShutdownGame() {
|
||||
HideFullscreen();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FFMPEG_VIDEO_DUMPER
|
||||
if (Core::System::GetInstance().VideoDumper().IsDumping()) {
|
||||
game_shutdown_delayed = true;
|
||||
OnStopVideoDumping();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
AllowOSSleep();
|
||||
|
||||
@@ -1084,6 +1114,10 @@ void GMainWindow::ShutdownGame() {
|
||||
game_list->show();
|
||||
game_list->setFilterFocus();
|
||||
|
||||
setMouseTracking(false);
|
||||
ui.centralwidget->setMouseTracking(false);
|
||||
ui.menubar->setMouseTracking(false);
|
||||
|
||||
// Disable status bar updates
|
||||
status_bar_update_timer.stop();
|
||||
message_label->setVisible(false);
|
||||
@@ -1290,7 +1324,7 @@ void GMainWindow::OnGameListDumpRomFS(QString game_path, u64 program_id) {
|
||||
using FutureWatcher = QFutureWatcher<std::pair<Loader::ResultStatus, Loader::ResultStatus>>;
|
||||
auto* future_watcher = new FutureWatcher(this);
|
||||
connect(future_watcher, &FutureWatcher::finished,
|
||||
[this, program_id, dialog, base_path, update_path, future_watcher] {
|
||||
[this, dialog, base_path, update_path, future_watcher] {
|
||||
dialog->hide();
|
||||
const auto& [base, update] = future_watcher->result();
|
||||
if (base != Loader::ResultStatus::Success) {
|
||||
@@ -1676,6 +1710,16 @@ void GMainWindow::OnConfigure() {
|
||||
SyncMenuUISettings();
|
||||
game_list->RefreshGameDirectory();
|
||||
config->Save();
|
||||
if (UISettings::values.hide_mouse && emulation_running) {
|
||||
setMouseTracking(true);
|
||||
ui.centralwidget->setMouseTracking(true);
|
||||
ui.menubar->setMouseTracking(true);
|
||||
mouse_hide_timer.start();
|
||||
} else {
|
||||
setMouseTracking(false);
|
||||
ui.centralwidget->setMouseTracking(false);
|
||||
ui.menubar->setMouseTracking(false);
|
||||
}
|
||||
} else {
|
||||
Settings::values.input_profiles = old_input_profiles;
|
||||
Settings::LoadProfile(old_input_profile_index);
|
||||
@@ -1915,18 +1959,23 @@ void GMainWindow::OnCaptureScreenshot() {
|
||||
OnStartGame();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FFMPEG_VIDEO_DUMPER
|
||||
void GMainWindow::OnStartVideoDumping() {
|
||||
const QString path = QFileDialog::getSaveFileName(
|
||||
this, tr("Save Video"), UISettings::values.video_dumping_path, tr("WebM Videos (*.webm)"));
|
||||
if (path.isEmpty()) {
|
||||
DumpingDialog dialog(this);
|
||||
if (dialog.exec() != QDialog::DialogCode::Accepted) {
|
||||
ui.action_Dump_Video->setChecked(false);
|
||||
return;
|
||||
}
|
||||
UISettings::values.video_dumping_path = QFileInfo(path).path();
|
||||
const auto path = dialog.GetFilePath();
|
||||
if (emulation_running) {
|
||||
Layout::FramebufferLayout layout{
|
||||
Layout::FrameLayoutFromResolutionScale(VideoCore::GetResolutionScaleFactor())};
|
||||
Core::System::GetInstance().VideoDumper().StartDumping(path.toStdString(), "webm", layout);
|
||||
if (!Core::System::GetInstance().VideoDumper().StartDumping(path.toStdString(), layout)) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Citra"),
|
||||
tr("Could not start video dumping.<br>Refer to the log for details."));
|
||||
ui.action_Dump_Video->setChecked(false);
|
||||
}
|
||||
} else {
|
||||
video_dumping_on_start = true;
|
||||
video_dumping_path = path;
|
||||
@@ -1943,6 +1992,8 @@ void GMainWindow::OnStopVideoDumping() {
|
||||
const bool was_dumping = Core::System::GetInstance().VideoDumper().IsDumping();
|
||||
if (!was_dumping)
|
||||
return;
|
||||
|
||||
game_paused_for_dumping = emu_thread->IsRunning();
|
||||
OnPauseGame();
|
||||
|
||||
auto future =
|
||||
@@ -1952,13 +2003,15 @@ void GMainWindow::OnStopVideoDumping() {
|
||||
if (game_shutdown_delayed) {
|
||||
game_shutdown_delayed = false;
|
||||
ShutdownGame();
|
||||
} else {
|
||||
} else if (game_paused_for_dumping) {
|
||||
game_paused_for_dumping = false;
|
||||
OnStartGame();
|
||||
}
|
||||
});
|
||||
future_watcher->setFuture(future);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void GMainWindow::UpdateStatusBar() {
|
||||
if (emu_thread == nullptr) {
|
||||
@@ -1983,6 +2036,30 @@ void GMainWindow::UpdateStatusBar() {
|
||||
emu_frametime_label->setVisible(true);
|
||||
}
|
||||
|
||||
void GMainWindow::HideMouseCursor() {
|
||||
if (emu_thread == nullptr || UISettings::values.hide_mouse == false) {
|
||||
mouse_hide_timer.stop();
|
||||
ShowMouseCursor();
|
||||
return;
|
||||
}
|
||||
setCursor(QCursor(Qt::BlankCursor));
|
||||
}
|
||||
|
||||
void GMainWindow::ShowMouseCursor() {
|
||||
unsetCursor();
|
||||
if (emu_thread != nullptr && UISettings::values.hide_mouse) {
|
||||
mouse_hide_timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||
ShowMouseCursor();
|
||||
}
|
||||
|
||||
void GMainWindow::mousePressEvent(QMouseEvent* event) {
|
||||
ShowMouseCursor();
|
||||
}
|
||||
|
||||
void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
|
||||
QString status_message;
|
||||
|
||||
|
@@ -207,8 +207,10 @@ private slots:
|
||||
void OnPlayMovie();
|
||||
void OnStopRecordingPlayback();
|
||||
void OnCaptureScreenshot();
|
||||
#ifdef ENABLE_FFMPEG_VIDEO_DUMPER
|
||||
void OnStartVideoDumping();
|
||||
void OnStopVideoDumping();
|
||||
#endif
|
||||
void OnCoreError(Core::System::ResultStatus, std::string);
|
||||
/// Called whenever a user selects Help->About Citra
|
||||
void OnMenuAboutCitra();
|
||||
@@ -225,6 +227,8 @@ private:
|
||||
void UpdateWindowTitle();
|
||||
void RetranslateStatusBar();
|
||||
void InstallCIA(QStringList filepaths);
|
||||
void HideMouseCursor();
|
||||
void ShowMouseCursor();
|
||||
|
||||
Ui::MainWindow ui;
|
||||
|
||||
@@ -253,6 +257,7 @@ private:
|
||||
QString game_path;
|
||||
|
||||
bool auto_paused = false;
|
||||
QTimer mouse_hide_timer;
|
||||
|
||||
// Movie
|
||||
bool movie_record_on_start = false;
|
||||
@@ -263,6 +268,8 @@ private:
|
||||
QString video_dumping_path;
|
||||
// Whether game shutdown is delayed due to video dumping
|
||||
bool game_shutdown_delayed = false;
|
||||
// Whether game was paused due to stopping video dumping
|
||||
bool game_paused_for_dumping = false;
|
||||
|
||||
// Debugger panes
|
||||
ProfilerWidget* profilerWidget;
|
||||
@@ -301,6 +308,8 @@ protected:
|
||||
void dropEvent(QDropEvent* event) override;
|
||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||
void dragMoveEvent(QDragMoveEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(std::size_t);
|
||||
|
@@ -76,6 +76,7 @@ struct Values {
|
||||
bool confirm_before_closing;
|
||||
bool first_start;
|
||||
bool pause_when_in_background;
|
||||
bool hide_mouse;
|
||||
|
||||
bool updater_found;
|
||||
bool update_on_close;
|
||||
|
Reference in New Issue
Block a user