summaryrefslogtreecommitdiffstats
path: root/src/citra
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/citra/CMakeLists.txt2
-rw-r--r--src/citra/citra.cpp2
-rw-r--r--src/citra/citra.rc8
-rw-r--r--src/citra/config.cpp13
-rw-r--r--src/citra/default_ini.h29
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp14
-rw-r--r--src/citra/emu_window/emu_window_sdl2.h4
-rw-r--r--src/citra_qt/CMakeLists.txt12
-rw-r--r--src/citra_qt/bootmanager.cpp13
-rw-r--r--src/citra_qt/bootmanager.h4
-rw-r--r--src/citra_qt/citra-qt.rc12
-rw-r--r--src/citra_qt/configuration/config.cpp31
-rw-r--r--src/citra_qt/configuration/configure.ui15
-rw-r--r--src/citra_qt/configuration/configure_dialog.cpp1
-rw-r--r--src/citra_qt/configuration/configure_general.cpp9
-rw-r--r--src/citra_qt/configuration/configure_general.ui28
-rw-r--r--src/citra_qt/configuration/configure_graphics.ui11
-rw-r--r--src/citra_qt/configuration/configure_web.cpp52
-rw-r--r--src/citra_qt/configuration/configure_web.h30
-rw-r--r--src/citra_qt/configuration/configure_web.ui153
-rw-r--r--src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp41
-rw-r--r--src/citra_qt/main.cpp69
-rw-r--r--src/citra_qt/main.h3
-rw-r--r--src/citra_qt/ui_settings.h9
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;