diff options
Diffstat (limited to '')
24 files changed, 507 insertions, 58 deletions
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index d72d2b5f4..a885f22f8 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -16,7 +16,7 @@ set(HEADERS create_directory_groups(${SRCS} ${HEADERS}) add_executable(citra ${SRCS} ${HEADERS}) -target_link_libraries(citra PRIVATE common core input_common) +target_link_libraries(citra PRIVATE common core input_common network) target_link_libraries(citra PRIVATE inih glad) if (MSVC) target_link_libraries(citra PRIVATE getopt) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 14574e56c..e524c5535 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -165,6 +165,8 @@ int main(int argc, char** argv) { break; // Expected case } + Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); + while (emu_window->IsOpen()) { system.RunLoop(); } diff --git a/src/citra/citra.rc b/src/citra/citra.rc index fea603004..c490ef302 100644 --- a/src/citra/citra.rc +++ b/src/citra/citra.rc @@ -1,3 +1,4 @@ +#include "winresrc.h" ///////////////////////////////////////////////////////////////////////////// // // Icon @@ -7,3 +8,10 @@ // remains consistent on all systems. CITRA_ICON ICON "../../dist/citra.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +1 RT_MANIFEST "../../dist/citra.manifest" diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 957d8dc86..a48ef08c7 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -76,6 +76,11 @@ void Config::ReadValues() { Settings::values.analogs[i] = default_param; } + Settings::values.motion_device = sdl2_config->Get( + "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01"); + Settings::values.touch_device = + sdl2_config->Get("Controls", "touch_device", "engine:emu_window"); + // Core Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); @@ -151,6 +156,14 @@ void Config::ReadValues() { Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); Settings::values.gdbstub_port = static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); + + // Web Service + Settings::values.enable_telemetry = + sdl2_config->GetBoolean("WebService", "enable_telemetry", true); + Settings::values.telemetry_endpoint_url = sdl2_config->Get( + "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry"); + Settings::values.citra_username = sdl2_config->Get("WebService", "citra_username", ""); + Settings::values.citra_token = sdl2_config->Get("WebService", "citra_token", ""); } void Config::Reload() { diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index d8a8fe44f..4b13a2e1b 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -12,7 +12,7 @@ const char* sdl2_config_file = R"( # It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." # Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values -# for button input, the following devices are avaible: +# for button input, the following devices are available: # - "keyboard" (default) for keyboard input. Required parameters: # - "code": the code of the key to bind # - "sdl" for joystick input using SDL. Required parameters: @@ -21,7 +21,7 @@ const char* sdl2_config_file = R"( # - "hat"(optional): the index of the hat to bind as direction buttons # - "axis"(optional): the index of the axis to bind # - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" -# - "threshould"(only used for axis): a float value in (-1.0, 1.0) which the button is +# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is # triggered if the axis value crosses # - "direction"(only used for axis): "+" means the button is triggered when the axis value # is greater than the threshold; "-" means the button is triggered when the axis value @@ -42,8 +42,8 @@ button_zl= button_zr= button_home= -# for analog input, the following devices are avaible: -# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: +# for analog input, the following devices are available: +# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: # - "up", "down", "left", "right": sub-devices for each direction. # Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" # - "modifier": sub-devices as a modifier. @@ -56,6 +56,16 @@ button_home= circle_pad= c_stick= +# for motion input, the following devices are available: +# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: +# - "update_period": update period in milliseconds (default to 100) +# - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01) +motion_device= + +# for touch input, the following devices are available: +# - "emu_window" (default) for emulating touch input from mouse input to the emulation window. No parameters required +touch_device= + [Core] # Whether to use the Just-In-Time (JIT) compiler for CPU emulation # 0: Interpreter (slow), 1 (default): JIT (fast) @@ -168,5 +178,16 @@ log_filter = *:Info # Port for listening to GDB connections. use_gdbstub=false gdbstub_port=24689 + +[WebService] +# Whether or not to enable telemetry +# 0: No, 1 (default): Yes +enable_telemetry = +# Endpoint URL for submitting telemetry data +telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry +# Username and token for Citra Web Service +# See https://services.citra-emu.org/ for more info +citra_username = +citra_token = )"; } diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index 47aadd60c..25643715a 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp @@ -16,10 +16,12 @@ #include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" +#include "input_common/motion_emu.h" +#include "network/network.h" void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); - motion_emu->Tilt(x, y); + InputCommon::GetMotionEmu()->Tilt(x, y); } void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { @@ -31,9 +33,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { } } else if (button == SDL_BUTTON_RIGHT) { if (state == SDL_PRESSED) { - motion_emu->BeginTilt(x, y); + InputCommon::GetMotionEmu()->BeginTilt(x, y); } else { - motion_emu->EndTilt(); + InputCommon::GetMotionEmu()->EndTilt(); } } } @@ -58,8 +60,7 @@ void EmuWindow_SDL2::OnResize() { EmuWindow_SDL2::EmuWindow_SDL2() { InputCommon::Init(); - - motion_emu = std::make_unique<Motion::MotionEmu>(*this); + Network::Init(); SDL_SetMainReady(); @@ -115,7 +116,8 @@ EmuWindow_SDL2::EmuWindow_SDL2() { EmuWindow_SDL2::~EmuWindow_SDL2() { SDL_GL_DeleteContext(gl_context); SDL_Quit(); - motion_emu = nullptr; + + Network::Shutdown(); InputCommon::Shutdown(); } diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h index 1ce2991f7..3664d2fbe 100644 --- a/src/citra/emu_window/emu_window_sdl2.h +++ b/src/citra/emu_window/emu_window_sdl2.h @@ -7,7 +7,6 @@ #include <memory> #include <utility> #include "core/frontend/emu_window.h" -#include "core/frontend/motion_emu.h" struct SDL_Window; @@ -57,7 +56,4 @@ private: using SDL_GLContext = void*; /// The OpenGL context associated with the window SDL_GLContext gl_context; - - /// Motion sensors emulation - std::unique_ptr<Motion::MotionEmu> motion_emu; }; diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 4841cbf05..e0a19fd9e 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -1,4 +1,5 @@ set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) @@ -11,6 +12,7 @@ set(SRCS configuration/configure_graphics.cpp configuration/configure_input.cpp configuration/configure_system.cpp + configuration/configure_web.cpp debugger/graphics/graphics.cpp debugger/graphics/graphics_breakpoint_observer.cpp debugger/graphics/graphics_breakpoints.cpp @@ -41,6 +43,7 @@ set(HEADERS configuration/configure_graphics.h configuration/configure_input.h configuration/configure_system.h + configuration/configure_web.h debugger/graphics/graphics.h debugger/graphics/graphics_breakpoint_observer.h debugger/graphics/graphics_breakpoints.h @@ -70,11 +73,14 @@ set(UIS configuration/configure_graphics.ui configuration/configure_input.ui configuration/configure_system.ui + configuration/configure_web.ui debugger/registers.ui hotkeys.ui main.ui ) +file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) + create_directory_groups(${SRCS} ${HEADERS} ${UIS}) if (Qt5_FOUND) @@ -86,12 +92,12 @@ endif() if (APPLE) set(MACOSX_ICON "../../dist/citra.icns") set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${MACOSX_ICON}) + add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${THEMES} ${MACOSX_ICON}) set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) else() - add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) + add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} ${THEMES}) endif() -target_link_libraries(citra-qt PRIVATE audio_core common core input_common video_core) +target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core) target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets) target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index a8a4aed8b..7107bfc60 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -17,6 +17,8 @@ #include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" +#include "input_common/motion_emu.h" +#include "network/network.h" EmuThread::EmuThread(GRenderWindow* render_window) : exec_step(false), running(false), stop_run(false), render_window(render_window) {} @@ -110,10 +112,12 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) setWindowTitle(QString::fromStdString(window_title)); InputCommon::Init(); + Network::Init(); } GRenderWindow::~GRenderWindow() { InputCommon::Shutdown(); + Network::Shutdown(); } void GRenderWindow::moveContext() { @@ -198,7 +202,6 @@ qreal GRenderWindow::windowPixelRatio() { } void GRenderWindow::closeEvent(QCloseEvent* event) { - motion_emu = nullptr; emit Closed(); QWidget::closeEvent(event); } @@ -218,7 +221,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), static_cast<unsigned>(pos.y() * pixelRatio)); } else if (event->button() == Qt::RightButton) { - motion_emu->BeginTilt(pos.x(), pos.y()); + InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); } } @@ -227,14 +230,14 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { qreal pixelRatio = windowPixelRatio(); this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); - motion_emu->Tilt(pos.x(), pos.y()); + InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); } void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) this->TouchReleased(); else if (event->button() == Qt::RightButton) - motion_emu->EndTilt(); + InputCommon::GetMotionEmu()->EndTilt(); } void GRenderWindow::focusOutEvent(QFocusEvent* event) { @@ -287,13 +290,11 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest( } void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { - motion_emu = std::make_unique<Motion::MotionEmu>(*this); this->emu_thread = emu_thread; child->DisablePainting(); } void GRenderWindow::OnEmulationStopping() { - motion_emu = nullptr; emu_thread = nullptr; child->EnablePainting(); } diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 4b3a3b3cc..6974edcbb 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -12,7 +12,6 @@ #include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" -#include "core/frontend/motion_emu.h" class QKeyEvent; class QScreen; @@ -158,9 +157,6 @@ private: EmuThread* emu_thread; - /// Motion sensors emulation - std::unique_ptr<Motion::MotionEmu> motion_emu; - protected: void showEvent(QShowEvent* event) override; }; diff --git a/src/citra_qt/citra-qt.rc b/src/citra_qt/citra-qt.rc index fea603004..a48a9440d 100644 --- a/src/citra_qt/citra-qt.rc +++ b/src/citra_qt/citra-qt.rc @@ -1,3 +1,4 @@ +#include "winresrc.h" ///////////////////////////////////////////////////////////////////////////// // // Icon @@ -5,5 +6,14 @@ // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -CITRA_ICON ICON "../../dist/citra.ico" +// QT requires that the default application icon is named IDI_ICON1 +IDI_ICON1 ICON "../../dist/citra.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +1 RT_MANIFEST "../../dist/citra.manifest" diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 64ffc9152..ef114aad3 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -57,6 +57,13 @@ void Config::ReadValues() { Settings::values.analogs[i] = default_param; } + Settings::values.motion_device = + qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") + .toString() + .toStdString(); + Settings::values.touch_device = + qt_config->value("touch_device", "engine:emu_window").toString().toStdString(); + qt_config->endGroup(); qt_config->beginGroup("Core"); @@ -133,7 +140,18 @@ void Config::ReadValues() { Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); qt_config->endGroup(); + qt_config->beginGroup("WebService"); + Settings::values.enable_telemetry = qt_config->value("enable_telemetry", true).toBool(); + Settings::values.telemetry_endpoint_url = + qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry") + .toString() + .toStdString(); + Settings::values.citra_username = qt_config->value("citra_username").toString().toStdString(); + Settings::values.citra_token = qt_config->value("citra_token").toString().toStdString(); + qt_config->endGroup(); + qt_config->beginGroup("UI"); + UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); qt_config->beginGroup("UILayout"); UISettings::values.geometry = qt_config->value("geometry").toByteArray(); @@ -181,6 +199,7 @@ void Config::ReadValues() { UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool(); UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool(); UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); + UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt(); qt_config->endGroup(); } @@ -195,6 +214,8 @@ void Config::SaveValues() { qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]), QString::fromStdString(Settings::values.analogs[i])); } + qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); + qt_config->setValue("touch_device", QString::fromStdString(Settings::values.touch_device)); qt_config->endGroup(); qt_config->beginGroup("Core"); @@ -268,7 +289,16 @@ void Config::SaveValues() { qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); qt_config->endGroup(); + qt_config->beginGroup("WebService"); + qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry); + qt_config->setValue("telemetry_endpoint_url", + QString::fromStdString(Settings::values.telemetry_endpoint_url)); + qt_config->setValue("citra_username", QString::fromStdString(Settings::values.citra_username)); + qt_config->setValue("citra_token", QString::fromStdString(Settings::values.citra_token)); + qt_config->endGroup(); + qt_config->beginGroup("UI"); + qt_config->setValue("theme", UISettings::values.theme); qt_config->beginGroup("UILayout"); qt_config->setValue("geometry", UISettings::values.geometry); @@ -300,6 +330,7 @@ void Config::SaveValues() { qt_config->setValue("showStatusBar", UISettings::values.show_status_bar); qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing); qt_config->setValue("firstStart", UISettings::values.first_start); + qt_config->setValue("calloutFlags", UISettings::values.callout_flags); qt_config->endGroup(); } diff --git a/src/citra_qt/configuration/configure.ui b/src/citra_qt/configuration/configure.ui index 85e206e42..6abd1917e 100644 --- a/src/citra_qt/configuration/configure.ui +++ b/src/citra_qt/configuration/configure.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>441</width> - <height>501</height> + <width>740</width> + <height>500</height> </rect> </property> <property name="windowTitle"> @@ -49,6 +49,11 @@ <string>Debug</string> </attribute> </widget> + <widget class="ConfigureWeb" name="webTab"> + <attribute name="title"> + <string>Web</string> + </attribute> + </widget> </widget> </item> <item> @@ -97,6 +102,12 @@ <header>configuration/configure_graphics.h</header> <container>1</container> </customwidget> + <customwidget> + <class>ConfigureWeb</class> + <extends>QWidget</extends> + <header>configuration/configure_web.h</header> + <container>1</container> + </customwidget> </customwidgets> <resources/> <connections> diff --git a/src/citra_qt/configuration/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp index dfc8c03a7..b87dc0e6c 100644 --- a/src/citra_qt/configuration/configure_dialog.cpp +++ b/src/citra_qt/configuration/configure_dialog.cpp @@ -23,5 +23,6 @@ void ConfigureDialog::applyConfiguration() { ui->graphicsTab->applyConfiguration(); ui->audioTab->applyConfiguration(); ui->debugTab->applyConfiguration(); + ui->webTab->applyConfiguration(); Settings::Apply(); } diff --git a/src/citra_qt/configuration/configure_general.cpp b/src/citra_qt/configuration/configure_general.cpp index a21176c34..939379717 100644 --- a/src/citra_qt/configuration/configure_general.cpp +++ b/src/citra_qt/configuration/configure_general.cpp @@ -12,6 +12,11 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureGeneral) { ui->setupUi(this); + + for (auto theme : UISettings::themes) { + ui->theme_combobox->addItem(theme.first, theme.second); + } + this->setConfiguration(); ui->toggle_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); @@ -26,11 +31,15 @@ void ConfigureGeneral::setConfiguration() { // The first item is "auto-select" with actual value -1, so plus one here will do the trick ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1); + + ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); } void ConfigureGeneral::applyConfiguration() { UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); + UISettings::values.theme = + ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); Settings::values.region_value = ui->region_combobox->currentIndex() - 1; Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); Settings::Apply(); diff --git a/src/citra_qt/configuration/configure_general.ui b/src/citra_qt/configuration/configure_general.ui index c739605a4..eedf2cbb0 100644 --- a/src/citra_qt/configuration/configure_general.ui +++ b/src/citra_qt/configuration/configure_general.ui @@ -132,6 +132,34 @@ </widget> </item> <item> + <widget class="QGroupBox" name="theme_group_box"> + <property name="title"> + <string>Theme</string> + </property> + <layout class="QHBoxLayout" name="theme_qhbox_layout"> + <item> + <layout class="QVBoxLayout" name="theme_qvbox_layout"> + <item> + <layout class="QHBoxLayout" name="theme_qhbox_layout_2"> + <item> + <widget class="QLabel" name="theme_label"> + <property name="text"> + <string>Theme:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="theme_combobox"> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> <widget class="QGroupBox" name="groupBox_3"> <property name="title"> <string>Hotkeys</string> diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index 228f2a869..b340149d5 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -146,17 +146,22 @@ <widget class="QComboBox" name="layout_combobox"> <item> <property name="text"> - <string notr="true">Default</string> + <string>Default</string> </property> </item> <item> <property name="text"> - <string notr="true">Single Screen</string> + <string>Single Screen</string> </property> </item> <item> <property name="text"> - <string notr="true">Large Screen</string> + <string>Large Screen</string> + </property> + </item> + <item> + <property name="text"> + <string>Side by Side</string> </property> </item> </widget> diff --git a/src/citra_qt/configuration/configure_web.cpp b/src/citra_qt/configuration/configure_web.cpp new file mode 100644 index 000000000..8715fb018 --- /dev/null +++ b/src/citra_qt/configuration/configure_web.cpp @@ -0,0 +1,52 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "citra_qt/configuration/configure_web.h" +#include "core/settings.h" +#include "core/telemetry_session.h" +#include "ui_configure_web.h" + +ConfigureWeb::ConfigureWeb(QWidget* parent) + : QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) { + ui->setupUi(this); + connect(ui->button_regenerate_telemetry_id, &QPushButton::clicked, this, + &ConfigureWeb::refreshTelemetryID); + + this->setConfiguration(); +} + +ConfigureWeb::~ConfigureWeb() {} + +void ConfigureWeb::setConfiguration() { + ui->web_credentials_disclaimer->setWordWrap(true); + ui->telemetry_learn_more->setOpenExternalLinks(true); + ui->telemetry_learn_more->setText("<a " + "href='https://citra-emu.org/entry/" + "telemetry-and-why-thats-a-good-thing/'>Learn more</a>"); + + ui->web_signup_link->setOpenExternalLinks(true); + ui->web_signup_link->setText("<a href='https://services.citra-emu.org/'>Sign up</a>"); + ui->web_token_info_link->setOpenExternalLinks(true); + ui->web_token_info_link->setText( + "<a href='https://citra-emu.org/wiki/citra-web-service/'>What is my token?</a>"); + + ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry); + ui->edit_username->setText(QString::fromStdString(Settings::values.citra_username)); + ui->edit_token->setText(QString::fromStdString(Settings::values.citra_token)); + ui->label_telemetry_id->setText("Telemetry ID: 0x" + + QString::number(Core::GetTelemetryId(), 16).toUpper()); +} + +void ConfigureWeb::applyConfiguration() { + Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked(); + Settings::values.citra_username = ui->edit_username->text().toStdString(); + Settings::values.citra_token = ui->edit_token->text().toStdString(); + Settings::Apply(); +} + +void ConfigureWeb::refreshTelemetryID() { + const u64 new_telemetry_id{Core::RegenerateTelemetryId()}; + ui->label_telemetry_id->setText("Telemetry ID: 0x" + + QString::number(new_telemetry_id, 16).toUpper()); +} diff --git a/src/citra_qt/configuration/configure_web.h b/src/citra_qt/configuration/configure_web.h new file mode 100644 index 000000000..20bc254b9 --- /dev/null +++ b/src/citra_qt/configuration/configure_web.h @@ -0,0 +1,30 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QWidget> + +namespace Ui { +class ConfigureWeb; +} + +class ConfigureWeb : public QWidget { + Q_OBJECT + +public: + explicit ConfigureWeb(QWidget* parent = nullptr); + ~ConfigureWeb(); + + void applyConfiguration(); + +public slots: + void refreshTelemetryID(); + +private: + void setConfiguration(); + + std::unique_ptr<Ui::ConfigureWeb> ui; +}; diff --git a/src/citra_qt/configuration/configure_web.ui b/src/citra_qt/configuration/configure_web.ui new file mode 100644 index 000000000..d8d283fad --- /dev/null +++ b/src/citra_qt/configuration/configure_web.ui @@ -0,0 +1,153 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureWeb</class> + <widget class="QWidget" name="ConfigureWeb"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QGroupBox" name="groupBoxWebConfig"> + <property name="title"> + <string>Citra Web Service</string> + </property> + <layout class="QVBoxLayout" name="verticalLayoutCitraWebService"> + <item> + <widget class="QLabel" name="web_credentials_disclaimer"> + <property name="text"> + <string>By providing your username and token, you agree to allow Citra to collect additional usage data, which may include user identifying information.</string> + </property> + </widget> + </item> + <item> + <layout class="QGridLayout" name="gridLayoutCitraUsername"> + <item row="0" column="0"> + <widget class="QLabel" name="label_username"> + <property name="text"> + <string>Username: </string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="edit_username"> + <property name="maxLength"> + <number>36</number> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_token"> + <property name="text"> + <string>Token: </string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="edit_token"> + <property name="maxLength"> + <number>36</number> + </property> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="web_signup_link"> + <property name="text"> + <string>Sign up</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="web_token_info_link"> + <property name="text"> + <string>What is my token?</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Telemetry</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="toggle_telemetry"> + <property name="text"> + <string>Share anonymous usage data with the Citra team</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="telemetry_learn_more"> + <property name="text"> + <string>Learn more</string> + </property> + </widget> + </item> + <item> + <layout class="QGridLayout" name="gridLayoutTelemetryId"> + <item row="0" column="0"> + <widget class="QLabel" name="label_telemetry_id"> + <property name="text"> + <string>Telemetry ID:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QPushButton" name="button_regenerate_telemetry_id"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="layoutDirection"> + <enum>Qt::RightToLeft</enum> + </property> + <property name="text"> + <string>Regenerate</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp index e3f3194db..7f4ec0c52 100644 --- a/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp +++ b/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp @@ -183,23 +183,13 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con print_input(output, src1, swizzle.negate_src1, SelectorToString(swizzle.src1_selector)); AlignToColumn(kInputOperandColumnWidth); - if (src_is_inverted) { - print_input(output, src2, swizzle.negate_src2, - SelectorToString(swizzle.src2_selector)); - } else { - print_input(output, src2, swizzle.negate_src2, - SelectorToString(swizzle.src2_selector), true, - instr.mad.AddressRegisterName()); - } + print_input(output, src2, swizzle.negate_src2, + SelectorToString(swizzle.src2_selector), true, + src_is_inverted ? "" : instr.mad.AddressRegisterName()); AlignToColumn(kInputOperandColumnWidth); - if (src_is_inverted) { - print_input(output, src3, swizzle.negate_src3, - SelectorToString(swizzle.src3_selector), true, - instr.mad.AddressRegisterName()); - } else { - print_input(output, src3, swizzle.negate_src3, - SelectorToString(swizzle.src3_selector)); - } + print_input(output, src3, swizzle.negate_src3, + SelectorToString(swizzle.src3_selector), true, + src_is_inverted ? instr.mad.AddressRegisterName() : ""); AlignToColumn(kInputOperandColumnWidth); break; } @@ -222,16 +212,15 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), true, - instr.common.AddressRegisterName()); + src_is_inverted ? "" : instr.common.AddressRegisterName()); AlignToColumn(kInputOperandColumnWidth); } - // TODO: In some cases, the Address Register is used as an index for SRC2 - // instead of SRC1 if (opcode_info.subtype & OpCode::Info::Src2) { SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); print_input(output, src2, swizzle.negate_src2, - swizzle.SelectorToString(true)); + swizzle.SelectorToString(true), true, + src_is_inverted ? instr.common.AddressRegisterName() : ""); AlignToColumn(kInputOperandColumnWidth); } break; @@ -247,7 +236,9 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con switch (opcode.EffectiveOpCode()) { case OpCode::Id::LOOP: - output << "(unknown instruction format)"; + output << 'i' << instr.flow_control.int_uniform_id << " (end on 0x" + << std::setw(4) << std::right << std::setfill('0') << std::hex + << (4 * instr.flow_control.dest_offset) << ")"; break; default: @@ -255,7 +246,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con output << '('; if (instr.flow_control.op != instr.flow_control.JustY) { - if (instr.flow_control.refx) + if (!instr.flow_control.refx) output << '!'; output << "cc.x"; } @@ -267,13 +258,17 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con } if (instr.flow_control.op != instr.flow_control.JustX) { - if (instr.flow_control.refy) + if (!instr.flow_control.refy) output << '!'; output << "cc.y"; } output << ") "; } else if (opcode_info.subtype & OpCode::Info::HasUniformIndex) { + if (opcode.EffectiveOpCode() == OpCode::Id::JMPU && + (instr.flow_control.num_instructions & 1) == 1) { + output << '!'; + } output << 'b' << instr.flow_control.bool_uniform_id << ' '; } diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 4f5b2ddab..8adbcfe86 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -48,6 +48,47 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); #endif +/** + * "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 + * user. This is 32-bits - if we have more than 32 callouts, we should retire and recyle old ones. + */ +enum class CalloutFlag : uint32_t { + Telemetry = 0x1, +}; + +static void ShowCalloutMessage(const QString& message, CalloutFlag flag) { + if (UISettings::values.callout_flags & static_cast<uint32_t>(flag)) { + return; + } + + UISettings::values.callout_flags |= static_cast<uint32_t>(flag); + + QMessageBox msg; + msg.setText(message); + msg.setStandardButtons(QMessageBox::Ok); + msg.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + msg.setStyleSheet("QLabel{min-width: 900px;}"); + msg.exec(); +} + +void GMainWindow::ShowCallouts() { + static const QString telemetry_message = + tr("To help improve Citra, the Citra Team collects anonymous usage data. No private or " + "personally identifying information is collected. This data helps us to understand how " + "people use Citra and prioritize our efforts. Furthermore, it helps us to more easily " + "identify emulation bugs and performance issues. This data includes:<ul><li>Information" + " about the version of Citra you are using</li><li>Performance data about the games you " + "play</li><li>Your configuration settings</li><li>Information about your computer " + "hardware</li><li>Emulation errors and crash information</li></ul>By default, this " + "feature is enabled. To disable this feature, click 'Emulation' from the menu and then " + "select 'Configure...'. Then, on the 'Web' tab, uncheck 'Share anonymous usage data with" + " the Citra team'. <br/><br/>By using this software, you agree to the above terms.<br/>" + "<br/><a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Learn " + "more</a>"); + ShowCalloutMessage(telemetry_message, CalloutFlag::Telemetry); +} + GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { Pica::g_debug_context = Pica::DebugContext::Construct(); setAcceptDrops(true); @@ -71,6 +112,11 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + UpdateUITheme(); + + // Show one-time "callout" messages to the user + ShowCallouts(); + QStringList args = QApplication::arguments(); if (args.length() >= 2) { BootGame(args[1]); @@ -309,7 +355,7 @@ bool GMainWindow::LoadROM(const QString& filename) { if (!gladLoadGL()) { QMessageBox::critical(this, tr("Error while initializing OpenGL 3.3 Core!"), - tr("Your GPU may not support OpenGL 3.3, or you do not" + tr("Your GPU may not support OpenGL 3.3, or you do not " "have the latest graphics driver.")); return false; } @@ -318,6 +364,8 @@ bool GMainWindow::LoadROM(const QString& filename) { const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())}; + Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "Qt"); + if (result != Core::System::ResultStatus::Success) { switch (result) { case Core::System::ResultStatus::ErrorGetLoader: @@ -606,6 +654,7 @@ void GMainWindow::OnConfigure() { auto result = configureDialog.exec(); if (result == QDialog::Accepted) { configureDialog.applyConfiguration(); + UpdateUITheme(); config->Save(); } } @@ -791,6 +840,24 @@ void GMainWindow::filterBarSetChecked(bool state) { emit(OnToggleFilterBar()); } +void GMainWindow::UpdateUITheme() { + if (UISettings::values.theme != UISettings::themes[0].second) { + QString theme_uri(":" + UISettings::values.theme + "/style.qss"); + QFile f(theme_uri); + if (!f.exists()) { + LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found"); + } else { + f.open(QFile::ReadOnly | QFile::Text); + QTextStream ts(&f); + qApp->setStyleSheet(ts.readAll()); + GMainWindow::setStyleSheet(ts.readAll()); + } + } else { + qApp->setStyleSheet(""); + GMainWindow::setStyleSheet(""); + } +} + #ifdef main #undef main #endif diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 952a50974..d59a6d67d 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -42,6 +42,7 @@ class GMainWindow : public QMainWindow { public: void filterBarSetChecked(bool state); + void UpdateUITheme(); GMainWindow(); ~GMainWindow(); @@ -79,6 +80,8 @@ private: void BootGame(const QString& filename); void ShutdownGame(); + void ShowCallouts(); + /** * Stores the filename in the recently loaded files list. * The new filename is stored at the beginning of the recently loaded files list. diff --git a/src/citra_qt/ui_settings.h b/src/citra_qt/ui_settings.h index bc37f81c5..d85c92765 100644 --- a/src/citra_qt/ui_settings.h +++ b/src/citra_qt/ui_settings.h @@ -4,6 +4,7 @@ #pragma once +#include <array> #include <vector> #include <QByteArray> #include <QString> @@ -14,6 +15,10 @@ namespace UISettings { using ContextualShortcut = std::pair<QString, int>; using Shortcut = std::pair<QString, ContextualShortcut>; +static const std::array<std::pair<QString, QString>, 2> themes = { + {std::make_pair(QString("Default"), QString("default")), + std::make_pair(QString("Dark"), QString("qdarkstyle"))}}; + struct Values { QByteArray geometry; QByteArray state; @@ -39,8 +44,12 @@ struct Values { bool gamedir_deepscan; QStringList recent_files; + QString theme; + // Shortcut name <Shortcut, context> std::vector<Shortcut> shortcuts; + + uint32_t callout_flags; }; extern Values values; |