mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu.git
				synced 2025-05-12 00:45:25 +00:00 
			
		
		
		
	Merge pull request #2897 from bunnei/telemetry-ui
Telemetry UI and final touches
This commit is contained in:
		
						commit
						22fc378fe9
					
				@ -165,6 +165,8 @@ int main(int argc, char** argv) {
 | 
				
			|||||||
        break; // Expected case
 | 
					        break; // Expected case
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while (emu_window->IsOpen()) {
 | 
					    while (emu_window->IsOpen()) {
 | 
				
			||||||
        system.RunLoop();
 | 
					        system.RunLoop();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -156,8 +156,12 @@ void Config::ReadValues() {
 | 
				
			|||||||
        static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
 | 
					        static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Web Service
 | 
					    // Web Service
 | 
				
			||||||
 | 
					    Settings::values.enable_telemetry =
 | 
				
			||||||
 | 
					        sdl2_config->GetBoolean("WebService", "enable_telemetry", true);
 | 
				
			||||||
    Settings::values.telemetry_endpoint_url = sdl2_config->Get(
 | 
					    Settings::values.telemetry_endpoint_url = sdl2_config->Get(
 | 
				
			||||||
        "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry");
 | 
					        "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() {
 | 
					void Config::Reload() {
 | 
				
			||||||
 | 
				
			|||||||
@ -176,7 +176,14 @@ use_gdbstub=false
 | 
				
			|||||||
gdbstub_port=24689
 | 
					gdbstub_port=24689
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[WebService]
 | 
					[WebService]
 | 
				
			||||||
 | 
					# Whether or not to enable telemetry
 | 
				
			||||||
 | 
					# 0: No, 1 (default): Yes
 | 
				
			||||||
 | 
					enable_telemetry =
 | 
				
			||||||
# Endpoint URL for submitting telemetry data
 | 
					# Endpoint URL for submitting telemetry data
 | 
				
			||||||
telemetry_endpoint_url =
 | 
					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 =
 | 
				
			||||||
)";
 | 
					)";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@ set(SRCS
 | 
				
			|||||||
            configuration/configure_graphics.cpp
 | 
					            configuration/configure_graphics.cpp
 | 
				
			||||||
            configuration/configure_input.cpp
 | 
					            configuration/configure_input.cpp
 | 
				
			||||||
            configuration/configure_system.cpp
 | 
					            configuration/configure_system.cpp
 | 
				
			||||||
 | 
					            configuration/configure_web.cpp
 | 
				
			||||||
            debugger/graphics/graphics.cpp
 | 
					            debugger/graphics/graphics.cpp
 | 
				
			||||||
            debugger/graphics/graphics_breakpoint_observer.cpp
 | 
					            debugger/graphics/graphics_breakpoint_observer.cpp
 | 
				
			||||||
            debugger/graphics/graphics_breakpoints.cpp
 | 
					            debugger/graphics/graphics_breakpoints.cpp
 | 
				
			||||||
@ -42,6 +43,7 @@ set(HEADERS
 | 
				
			|||||||
            configuration/configure_graphics.h
 | 
					            configuration/configure_graphics.h
 | 
				
			||||||
            configuration/configure_input.h
 | 
					            configuration/configure_input.h
 | 
				
			||||||
            configuration/configure_system.h
 | 
					            configuration/configure_system.h
 | 
				
			||||||
 | 
					            configuration/configure_web.h
 | 
				
			||||||
            debugger/graphics/graphics.h
 | 
					            debugger/graphics/graphics.h
 | 
				
			||||||
            debugger/graphics/graphics_breakpoint_observer.h
 | 
					            debugger/graphics/graphics_breakpoint_observer.h
 | 
				
			||||||
            debugger/graphics/graphics_breakpoints.h
 | 
					            debugger/graphics/graphics_breakpoints.h
 | 
				
			||||||
@ -71,6 +73,7 @@ set(UIS
 | 
				
			|||||||
            configuration/configure_graphics.ui
 | 
					            configuration/configure_graphics.ui
 | 
				
			||||||
            configuration/configure_input.ui
 | 
					            configuration/configure_input.ui
 | 
				
			||||||
            configuration/configure_system.ui
 | 
					            configuration/configure_system.ui
 | 
				
			||||||
 | 
					            configuration/configure_web.ui
 | 
				
			||||||
            debugger/registers.ui
 | 
					            debugger/registers.ui
 | 
				
			||||||
            hotkeys.ui
 | 
					            hotkeys.ui
 | 
				
			||||||
            main.ui
 | 
					            main.ui
 | 
				
			||||||
 | 
				
			|||||||
@ -139,10 +139,13 @@ void Config::ReadValues() {
 | 
				
			|||||||
    qt_config->endGroup();
 | 
					    qt_config->endGroup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qt_config->beginGroup("WebService");
 | 
					    qt_config->beginGroup("WebService");
 | 
				
			||||||
 | 
					    Settings::values.enable_telemetry = qt_config->value("enable_telemetry", true).toBool();
 | 
				
			||||||
    Settings::values.telemetry_endpoint_url =
 | 
					    Settings::values.telemetry_endpoint_url =
 | 
				
			||||||
        qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry")
 | 
					        qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry")
 | 
				
			||||||
            .toString()
 | 
					            .toString()
 | 
				
			||||||
            .toStdString();
 | 
					            .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->endGroup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qt_config->beginGroup("UI");
 | 
					    qt_config->beginGroup("UI");
 | 
				
			||||||
@ -194,6 +197,7 @@ void Config::ReadValues() {
 | 
				
			|||||||
    UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool();
 | 
					    UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool();
 | 
				
			||||||
    UISettings::values.confirm_before_closing = qt_config->value("confirmClose", 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.first_start = qt_config->value("firstStart", true).toBool();
 | 
				
			||||||
 | 
					    UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qt_config->endGroup();
 | 
					    qt_config->endGroup();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -283,8 +287,11 @@ void Config::SaveValues() {
 | 
				
			|||||||
    qt_config->endGroup();
 | 
					    qt_config->endGroup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qt_config->beginGroup("WebService");
 | 
					    qt_config->beginGroup("WebService");
 | 
				
			||||||
 | 
					    qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry);
 | 
				
			||||||
    qt_config->setValue("telemetry_endpoint_url",
 | 
					    qt_config->setValue("telemetry_endpoint_url",
 | 
				
			||||||
                        QString::fromStdString(Settings::values.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->endGroup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qt_config->beginGroup("UI");
 | 
					    qt_config->beginGroup("UI");
 | 
				
			||||||
@ -320,6 +327,7 @@ void Config::SaveValues() {
 | 
				
			|||||||
    qt_config->setValue("showStatusBar", UISettings::values.show_status_bar);
 | 
					    qt_config->setValue("showStatusBar", UISettings::values.show_status_bar);
 | 
				
			||||||
    qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
 | 
					    qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
 | 
				
			||||||
    qt_config->setValue("firstStart", UISettings::values.first_start);
 | 
					    qt_config->setValue("firstStart", UISettings::values.first_start);
 | 
				
			||||||
 | 
					    qt_config->setValue("calloutFlags", UISettings::values.callout_flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qt_config->endGroup();
 | 
					    qt_config->endGroup();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,8 +6,8 @@
 | 
				
			|||||||
   <rect>
 | 
					   <rect>
 | 
				
			||||||
    <x>0</x>
 | 
					    <x>0</x>
 | 
				
			||||||
    <y>0</y>
 | 
					    <y>0</y>
 | 
				
			||||||
    <width>441</width>
 | 
					    <width>740</width>
 | 
				
			||||||
    <height>501</height>
 | 
					    <height>500</height>
 | 
				
			||||||
   </rect>
 | 
					   </rect>
 | 
				
			||||||
  </property>
 | 
					  </property>
 | 
				
			||||||
  <property name="windowTitle">
 | 
					  <property name="windowTitle">
 | 
				
			||||||
@ -49,6 +49,11 @@
 | 
				
			|||||||
       <string>Debug</string>
 | 
					       <string>Debug</string>
 | 
				
			||||||
      </attribute>
 | 
					      </attribute>
 | 
				
			||||||
     </widget>
 | 
					     </widget>
 | 
				
			||||||
 | 
					     <widget class="ConfigureWeb" name="webTab">
 | 
				
			||||||
 | 
					      <attribute name="title">
 | 
				
			||||||
 | 
					       <string>Web</string>
 | 
				
			||||||
 | 
					      </attribute>
 | 
				
			||||||
 | 
					     </widget>
 | 
				
			||||||
    </widget>
 | 
					    </widget>
 | 
				
			||||||
   </item>
 | 
					   </item>
 | 
				
			||||||
   <item>
 | 
					   <item>
 | 
				
			||||||
@ -97,6 +102,12 @@
 | 
				
			|||||||
   <header>configuration/configure_graphics.h</header>
 | 
					   <header>configuration/configure_graphics.h</header>
 | 
				
			||||||
   <container>1</container>
 | 
					   <container>1</container>
 | 
				
			||||||
  </customwidget>
 | 
					  </customwidget>
 | 
				
			||||||
 | 
					  <customwidget>
 | 
				
			||||||
 | 
					   <class>ConfigureWeb</class>
 | 
				
			||||||
 | 
					   <extends>QWidget</extends>
 | 
				
			||||||
 | 
					   <header>configuration/configure_web.h</header>
 | 
				
			||||||
 | 
					   <container>1</container>
 | 
				
			||||||
 | 
					  </customwidget>
 | 
				
			||||||
 </customwidgets>
 | 
					 </customwidgets>
 | 
				
			||||||
 <resources/>
 | 
					 <resources/>
 | 
				
			||||||
 <connections>
 | 
					 <connections>
 | 
				
			||||||
 | 
				
			|||||||
@ -23,5 +23,6 @@ void ConfigureDialog::applyConfiguration() {
 | 
				
			|||||||
    ui->graphicsTab->applyConfiguration();
 | 
					    ui->graphicsTab->applyConfiguration();
 | 
				
			||||||
    ui->audioTab->applyConfiguration();
 | 
					    ui->audioTab->applyConfiguration();
 | 
				
			||||||
    ui->debugTab->applyConfiguration();
 | 
					    ui->debugTab->applyConfiguration();
 | 
				
			||||||
 | 
					    ui->webTab->applyConfiguration();
 | 
				
			||||||
    Settings::Apply();
 | 
					    Settings::Apply();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										52
									
								
								src/citra_qt/configuration/configure_web.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/citra_qt/configuration/configure_web.cpp
									
									
									
									
									
										Normal file
									
								
							@ -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());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/citra_qt/configuration/configure_web.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/citra_qt/configuration/configure_web.h
									
									
									
									
									
										Normal file
									
								
							@ -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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										153
									
								
								src/citra_qt/configuration/configure_web.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/citra_qt/configuration/configure_web.ui
									
									
									
									
									
										Normal file
									
								
							@ -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>
 | 
				
			||||||
@ -48,6 +48,47 @@
 | 
				
			|||||||
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
 | 
					Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
 | 
				
			||||||
#endif
 | 
					#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) {
 | 
					GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
 | 
				
			||||||
    Pica::g_debug_context = Pica::DebugContext::Construct();
 | 
					    Pica::g_debug_context = Pica::DebugContext::Construct();
 | 
				
			||||||
    setAcceptDrops(true);
 | 
					    setAcceptDrops(true);
 | 
				
			||||||
@ -73,6 +114,9 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    UpdateUITheme();
 | 
					    UpdateUITheme();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Show one-time "callout" messages to the user
 | 
				
			||||||
 | 
					    ShowCallouts();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    QStringList args = QApplication::arguments();
 | 
					    QStringList args = QApplication::arguments();
 | 
				
			||||||
    if (args.length() >= 2) {
 | 
					    if (args.length() >= 2) {
 | 
				
			||||||
        BootGame(args[1]);
 | 
					        BootGame(args[1]);
 | 
				
			||||||
@ -320,6 +364,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())};
 | 
					    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) {
 | 
					    if (result != Core::System::ResultStatus::Success) {
 | 
				
			||||||
        switch (result) {
 | 
					        switch (result) {
 | 
				
			||||||
        case Core::System::ResultStatus::ErrorGetLoader:
 | 
					        case Core::System::ResultStatus::ErrorGetLoader:
 | 
				
			||||||
 | 
				
			|||||||
@ -80,6 +80,8 @@ private:
 | 
				
			|||||||
    void BootGame(const QString& filename);
 | 
					    void BootGame(const QString& filename);
 | 
				
			||||||
    void ShutdownGame();
 | 
					    void ShutdownGame();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void ShowCallouts();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Stores the filename in the recently loaded files list.
 | 
					     * Stores the filename in the recently loaded files list.
 | 
				
			||||||
     * The new filename is stored at the beginning of the recently loaded files list.
 | 
					     * The new filename is stored at the beginning of the recently loaded files list.
 | 
				
			||||||
 | 
				
			|||||||
@ -48,6 +48,8 @@ struct Values {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Shortcut name <Shortcut, context>
 | 
					    // Shortcut name <Shortcut, context>
 | 
				
			||||||
    std::vector<Shortcut> shortcuts;
 | 
					    std::vector<Shortcut> shortcuts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    uint32_t callout_flags;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern Values values;
 | 
					extern Values values;
 | 
				
			||||||
 | 
				
			|||||||
@ -130,7 +130,10 @@ struct Values {
 | 
				
			|||||||
    u16 gdbstub_port;
 | 
					    u16 gdbstub_port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // WebService
 | 
					    // WebService
 | 
				
			||||||
 | 
					    bool enable_telemetry;
 | 
				
			||||||
    std::string telemetry_endpoint_url;
 | 
					    std::string telemetry_endpoint_url;
 | 
				
			||||||
 | 
					    std::string citra_username;
 | 
				
			||||||
 | 
					    std::string citra_token;
 | 
				
			||||||
} extern values;
 | 
					} extern values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// a special value for Values::region_value indicating that citra will automatically select a region
 | 
					// a special value for Values::region_value indicating that citra will automatically select a region
 | 
				
			||||||
 | 
				
			|||||||
@ -3,8 +3,10 @@
 | 
				
			|||||||
// Refer to the license.txt file included.
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <cstring>
 | 
					#include <cstring>
 | 
				
			||||||
 | 
					#include <cryptopp/osrng.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "common/assert.h"
 | 
					#include "common/assert.h"
 | 
				
			||||||
 | 
					#include "common/file_util.h"
 | 
				
			||||||
#include "common/scm_rev.h"
 | 
					#include "common/scm_rev.h"
 | 
				
			||||||
#include "common/x64/cpu_detect.h"
 | 
					#include "common/x64/cpu_detect.h"
 | 
				
			||||||
#include "core/core.h"
 | 
					#include "core/core.h"
 | 
				
			||||||
@ -29,12 +31,65 @@ static const char* CpuVendorToStr(Common::CPUVendor vendor) {
 | 
				
			|||||||
    UNREACHABLE();
 | 
					    UNREACHABLE();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static u64 GenerateTelemetryId() {
 | 
				
			||||||
 | 
					    u64 telemetry_id{};
 | 
				
			||||||
 | 
					    CryptoPP::AutoSeededRandomPool rng;
 | 
				
			||||||
 | 
					    rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&telemetry_id), sizeof(u64));
 | 
				
			||||||
 | 
					    return telemetry_id;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u64 GetTelemetryId() {
 | 
				
			||||||
 | 
					    u64 telemetry_id{};
 | 
				
			||||||
 | 
					    static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (FileUtil::Exists(filename)) {
 | 
				
			||||||
 | 
					        FileUtil::IOFile file(filename, "rb");
 | 
				
			||||||
 | 
					        if (!file.IsOpen()) {
 | 
				
			||||||
 | 
					            LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
 | 
				
			||||||
 | 
					            return {};
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        file.ReadBytes(&telemetry_id, sizeof(u64));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        FileUtil::IOFile file(filename, "wb");
 | 
				
			||||||
 | 
					        if (!file.IsOpen()) {
 | 
				
			||||||
 | 
					            LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
 | 
				
			||||||
 | 
					            return {};
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        telemetry_id = GenerateTelemetryId();
 | 
				
			||||||
 | 
					        file.WriteBytes(&telemetry_id, sizeof(u64));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return telemetry_id;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u64 RegenerateTelemetryId() {
 | 
				
			||||||
 | 
					    const u64 new_telemetry_id{GenerateTelemetryId()};
 | 
				
			||||||
 | 
					    static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FileUtil::IOFile file(filename, "wb");
 | 
				
			||||||
 | 
					    if (!file.IsOpen()) {
 | 
				
			||||||
 | 
					        LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
 | 
				
			||||||
 | 
					        return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    file.WriteBytes(&new_telemetry_id, sizeof(u64));
 | 
				
			||||||
 | 
					    return new_telemetry_id;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TelemetrySession::TelemetrySession() {
 | 
					TelemetrySession::TelemetrySession() {
 | 
				
			||||||
#ifdef ENABLE_WEB_SERVICE
 | 
					#ifdef ENABLE_WEB_SERVICE
 | 
				
			||||||
    backend = std::make_unique<WebService::TelemetryJson>();
 | 
					    if (Settings::values.enable_telemetry) {
 | 
				
			||||||
 | 
					        backend = std::make_unique<WebService::TelemetryJson>(
 | 
				
			||||||
 | 
					            Settings::values.telemetry_endpoint_url, Settings::values.citra_username,
 | 
				
			||||||
 | 
					            Settings::values.citra_token);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        backend = std::make_unique<Telemetry::NullVisitor>();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
    backend = std::make_unique<Telemetry::NullVisitor>();
 | 
					    backend = std::make_unique<Telemetry::NullVisitor>();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					    // Log one-time top-level information
 | 
				
			||||||
 | 
					    AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Log one-time session start information
 | 
					    // Log one-time session start information
 | 
				
			||||||
    const s64 init_time{std::chrono::duration_cast<std::chrono::milliseconds>(
 | 
					    const s64 init_time{std::chrono::duration_cast<std::chrono::milliseconds>(
 | 
				
			||||||
                            std::chrono::system_clock::now().time_since_epoch())
 | 
					                            std::chrono::system_clock::now().time_since_epoch())
 | 
				
			||||||
 | 
				
			|||||||
@ -35,4 +35,16 @@ private:
 | 
				
			|||||||
    std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
 | 
					    std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Gets TelemetryId, a unique identifier used for the user's telemetry sessions.
 | 
				
			||||||
 | 
					 * @returns The current TelemetryId for the session.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					u64 GetTelemetryId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Regenerates TelemetryId, a unique identifier used for the user's telemetry sessions.
 | 
				
			||||||
 | 
					 * @returns The new TelemetryId that was generated.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					u64 RegenerateTelemetryId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace Core
 | 
					} // namespace Core
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@
 | 
				
			|||||||
// Refer to the license.txt file included.
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "common/assert.h"
 | 
					#include "common/assert.h"
 | 
				
			||||||
#include "core/settings.h"
 | 
					 | 
				
			||||||
#include "web_service/telemetry_json.h"
 | 
					#include "web_service/telemetry_json.h"
 | 
				
			||||||
#include "web_service/web_backend.h"
 | 
					#include "web_service/web_backend.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -81,7 +80,7 @@ void TelemetryJson::Complete() {
 | 
				
			|||||||
    SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
 | 
					    SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
 | 
				
			||||||
    SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
 | 
					    SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
 | 
				
			||||||
    SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
 | 
					    SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
 | 
				
			||||||
    PostJson(Settings::values.telemetry_endpoint_url, TopSection().dump());
 | 
					    PostJson(endpoint_url, TopSection().dump(), true, username, token);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace WebService
 | 
					} // namespace WebService
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,9 @@ namespace WebService {
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
class TelemetryJson : public Telemetry::VisitorInterface {
 | 
					class TelemetryJson : public Telemetry::VisitorInterface {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    TelemetryJson() = default;
 | 
					    TelemetryJson(const std::string& endpoint_url, const std::string& username,
 | 
				
			||||||
 | 
					                  const std::string& token)
 | 
				
			||||||
 | 
					        : endpoint_url(endpoint_url), username(username), token(token) {}
 | 
				
			||||||
    ~TelemetryJson() = default;
 | 
					    ~TelemetryJson() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void Visit(const Telemetry::Field<bool>& field) override;
 | 
					    void Visit(const Telemetry::Field<bool>& field) override;
 | 
				
			||||||
@ -49,6 +51,9 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    nlohmann::json output;
 | 
					    nlohmann::json output;
 | 
				
			||||||
    std::array<nlohmann::json, 7> sections;
 | 
					    std::array<nlohmann::json, 7> sections;
 | 
				
			||||||
 | 
					    std::string endpoint_url;
 | 
				
			||||||
 | 
					    std::string username;
 | 
				
			||||||
 | 
					    std::string token;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace WebService
 | 
					} // namespace WebService
 | 
				
			||||||
 | 
				
			|||||||
@ -2,51 +2,62 @@
 | 
				
			|||||||
// Licensed under GPLv2 or any later version
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
// Refer to the license.txt file included.
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					#include <winsock.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
 | 
					#include <thread>
 | 
				
			||||||
#include <cpr/cpr.h>
 | 
					#include <cpr/cpr.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include "common/logging/log.h"
 | 
					#include "common/logging/log.h"
 | 
				
			||||||
#include "web_service/web_backend.h"
 | 
					#include "web_service/web_backend.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace WebService {
 | 
					namespace WebService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static constexpr char API_VERSION[]{"1"};
 | 
					static constexpr char API_VERSION[]{"1"};
 | 
				
			||||||
static constexpr char ENV_VAR_USERNAME[]{"CITRA_WEB_SERVICES_USERNAME"};
 | 
					 | 
				
			||||||
static constexpr char ENV_VAR_TOKEN[]{"CITRA_WEB_SERVICES_TOKEN"};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static std::string GetEnvironmentVariable(const char* name) {
 | 
					static std::unique_ptr<cpr::Session> g_session;
 | 
				
			||||||
    const char* value{getenv(name)};
 | 
					 | 
				
			||||||
    if (value) {
 | 
					 | 
				
			||||||
        return value;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return {};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const std::string& GetUsername() {
 | 
					void PostJson(const std::string& url, const std::string& data, bool allow_anonymous,
 | 
				
			||||||
    static const std::string username{GetEnvironmentVariable(ENV_VAR_USERNAME)};
 | 
					              const std::string& username, const std::string& token) {
 | 
				
			||||||
    return username;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const std::string& GetToken() {
 | 
					 | 
				
			||||||
    static const std::string token{GetEnvironmentVariable(ENV_VAR_TOKEN)};
 | 
					 | 
				
			||||||
    return token;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void PostJson(const std::string& url, const std::string& data) {
 | 
					 | 
				
			||||||
    if (url.empty()) {
 | 
					    if (url.empty()) {
 | 
				
			||||||
        LOG_ERROR(WebService, "URL is invalid");
 | 
					        LOG_ERROR(WebService, "URL is invalid");
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (GetUsername().empty() || GetToken().empty()) {
 | 
					    const bool are_credentials_provided{!token.empty() && !username.empty()};
 | 
				
			||||||
        LOG_ERROR(WebService, "Environment variables %s and %s must be set to POST JSON",
 | 
					    if (!allow_anonymous && !are_credentials_provided) {
 | 
				
			||||||
                  ENV_VAR_USERNAME, ENV_VAR_TOKEN);
 | 
					        LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cpr::PostAsync(cpr::Url{url}, cpr::Body{data}, cpr::Header{{"Content-Type", "application/json"},
 | 
					#ifdef _WIN32
 | 
				
			||||||
                                                               {"x-username", GetUsername()},
 | 
					    // On Windows, CPR/libcurl does not properly initialize Winsock. The below code is used to
 | 
				
			||||||
                                                               {"x-token", GetToken()},
 | 
					    // initialize Winsock globally, which fixes this problem. Without this, only the first CPR
 | 
				
			||||||
                                                               {"api-version", API_VERSION}});
 | 
					    // session will properly be created, and subsequent ones will fail.
 | 
				
			||||||
 | 
					    WSADATA wsa_data;
 | 
				
			||||||
 | 
					    const int wsa_result{WSAStartup(MAKEWORD(2, 2), &wsa_data)};
 | 
				
			||||||
 | 
					    if (wsa_result) {
 | 
				
			||||||
 | 
					        LOG_CRITICAL(WebService, "WSAStartup failed: %d", wsa_result);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Built request header
 | 
				
			||||||
 | 
					    cpr::Header header;
 | 
				
			||||||
 | 
					    if (are_credentials_provided) {
 | 
				
			||||||
 | 
					        // Authenticated request if credentials are provided
 | 
				
			||||||
 | 
					        header = {{"Content-Type", "application/json"},
 | 
				
			||||||
 | 
					                  {"x-username", username.c_str()},
 | 
				
			||||||
 | 
					                  {"x-token", token.c_str()},
 | 
				
			||||||
 | 
					                  {"api-version", API_VERSION}};
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // Otherwise, anonymous request
 | 
				
			||||||
 | 
					        header = cpr::Header{{"Content-Type", "application/json"}, {"api-version", API_VERSION}};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Post JSON asynchronously
 | 
				
			||||||
 | 
					    static cpr::AsyncResponse future;
 | 
				
			||||||
 | 
					    future = cpr::PostAsync(cpr::Url{url.c_str()}, cpr::Body{data.c_str()}, header);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace WebService
 | 
					} // namespace WebService
 | 
				
			||||||
 | 
				
			|||||||
@ -9,23 +9,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace WebService {
 | 
					namespace WebService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Gets the current username for accessing services.citra-emu.org.
 | 
					 | 
				
			||||||
 * @returns Username as a string, empty if not set.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
const std::string& GetUsername();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Gets the current token for accessing services.citra-emu.org.
 | 
					 | 
				
			||||||
 * @returns Token as a string, empty if not set.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
const std::string& GetToken();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Posts JSON to services.citra-emu.org.
 | 
					 * Posts JSON to services.citra-emu.org.
 | 
				
			||||||
 * @param url URL of the services.citra-emu.org endpoint to post data to.
 | 
					 * @param url URL of the services.citra-emu.org endpoint to post data to.
 | 
				
			||||||
 * @param data String of JSON data to use for the body of the POST request.
 | 
					 * @param data String of JSON data to use for the body of the POST request.
 | 
				
			||||||
 | 
					 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
 | 
				
			||||||
 | 
					 * @param username Citra username to use for authentication.
 | 
				
			||||||
 | 
					 * @param token Citra token to use for authentication.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void PostJson(const std::string& url, const std::string& data);
 | 
					void PostJson(const std::string& url, const std::string& data, bool allow_anonymous,
 | 
				
			||||||
 | 
					              const std::string& username = {}, const std::string& token = {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace WebService
 | 
					} // namespace WebService
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user