mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-22 18:15:14 +00:00
adds microphone selection to the interface
This commit is contained in:
parent
b0a86ce6a4
commit
9d5c0794e5
@ -54,6 +54,7 @@ static bool useSpecialPad = false;
|
|||||||
static int specialPadClass = 1;
|
static int specialPadClass = 1;
|
||||||
static bool isMotionControlsEnabled = true;
|
static bool isMotionControlsEnabled = true;
|
||||||
static bool useUnifiedInputConfig = true;
|
static bool useUnifiedInputConfig = true;
|
||||||
|
static std::string micDevice = "Default Device";
|
||||||
|
|
||||||
// These two entries aren't stored in the config
|
// These two entries aren't stored in the config
|
||||||
static bool overrideControllerColor = false;
|
static bool overrideControllerColor = false;
|
||||||
@ -193,6 +194,10 @@ int getCursorHideTimeout() {
|
|||||||
return cursorHideTimeout;
|
return cursorHideTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string getMicDevice() {
|
||||||
|
return micDevice;
|
||||||
|
}
|
||||||
|
|
||||||
double getTrophyNotificationDuration() {
|
double getTrophyNotificationDuration() {
|
||||||
return trophyNotificationDuration;
|
return trophyNotificationDuration;
|
||||||
}
|
}
|
||||||
@ -445,6 +450,10 @@ void setCursorHideTimeout(int newcursorHideTimeout) {
|
|||||||
cursorHideTimeout = newcursorHideTimeout;
|
cursorHideTimeout = newcursorHideTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setMicDevice(std::string device) {
|
||||||
|
micDevice = device;
|
||||||
|
}
|
||||||
|
|
||||||
void setTrophyNotificationDuration(double newTrophyNotificationDuration) {
|
void setTrophyNotificationDuration(double newTrophyNotificationDuration) {
|
||||||
trophyNotificationDuration = newTrophyNotificationDuration;
|
trophyNotificationDuration = newTrophyNotificationDuration;
|
||||||
}
|
}
|
||||||
@ -643,6 +652,7 @@ void load(const std::filesystem::path& path) {
|
|||||||
toml::find_or<bool>(input, "isMotionControlsEnabled", isMotionControlsEnabled);
|
toml::find_or<bool>(input, "isMotionControlsEnabled", isMotionControlsEnabled);
|
||||||
useUnifiedInputConfig =
|
useUnifiedInputConfig =
|
||||||
toml::find_or<bool>(input, "useUnifiedInputConfig", useUnifiedInputConfig);
|
toml::find_or<bool>(input, "useUnifiedInputConfig", useUnifiedInputConfig);
|
||||||
|
micDevice = toml::find_or<std::string>(input, "micDevice", micDevice);
|
||||||
|
|
||||||
entry_count += input.size();
|
entry_count += input.size();
|
||||||
}
|
}
|
||||||
@ -826,6 +836,7 @@ void save(const std::filesystem::path& path) {
|
|||||||
data["Input"]["specialPadClass"] = specialPadClass;
|
data["Input"]["specialPadClass"] = specialPadClass;
|
||||||
data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled;
|
data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled;
|
||||||
data["Input"]["useUnifiedInputConfig"] = useUnifiedInputConfig;
|
data["Input"]["useUnifiedInputConfig"] = useUnifiedInputConfig;
|
||||||
|
data["Input"]["micDevice"] = micDevice;
|
||||||
data["GPU"]["screenWidth"] = windowWidth;
|
data["GPU"]["screenWidth"] = windowWidth;
|
||||||
data["GPU"]["screenHeight"] = windowHeight;
|
data["GPU"]["screenHeight"] = windowHeight;
|
||||||
data["GPU"]["internalScreenWidth"] = internalScreenWidth;
|
data["GPU"]["internalScreenWidth"] = internalScreenWidth;
|
||||||
@ -927,6 +938,7 @@ void setDefaultValues() {
|
|||||||
controllerCustomColorRGB[0] = 0;
|
controllerCustomColorRGB[0] = 0;
|
||||||
controllerCustomColorRGB[1] = 0;
|
controllerCustomColorRGB[1] = 0;
|
||||||
controllerCustomColorRGB[2] = 255;
|
controllerCustomColorRGB[2] = 255;
|
||||||
|
micDevice = "Default Device";
|
||||||
|
|
||||||
// GPU
|
// GPU
|
||||||
windowWidth = 1280;
|
windowWidth = 1280;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <common/config.h>
|
||||||
#include "sdl_in.h"
|
#include "sdl_in.h"
|
||||||
|
|
||||||
int SDLAudioIn::AudioInit() {
|
int SDLAudioIn::AudioInit() {
|
||||||
@ -43,8 +44,23 @@ int SDLAudioIn::AudioInOpen(int type, uint32_t samples_num, uint32_t freq, uint3
|
|||||||
fmt.channels = port.channels_num;
|
fmt.channels = port.channels_num;
|
||||||
fmt.freq = port.freq;
|
fmt.freq = port.freq;
|
||||||
|
|
||||||
port.stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_RECORDING, &fmt,
|
std::string micDevStr = Config::getMicDevice();
|
||||||
nullptr, nullptr);
|
uint32_t devId;
|
||||||
|
|
||||||
|
if (micDevStr == "None") {
|
||||||
|
return ORBIS_AUDIO_IN_ERROR_INVALID_PORT;
|
||||||
|
} else if (micDevStr == "Default Device") {
|
||||||
|
devId = SDL_AUDIO_DEVICE_DEFAULT_RECORDING;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
devId = static_cast<uint32_t>(std::stoul(micDevStr));
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
return ORBIS_AUDIO_IN_ERROR_INVALID_PORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
port.stream = SDL_OpenAudioDeviceStream(devId, &fmt, nullptr, nullptr);
|
||||||
|
|
||||||
if (!port.stream) {
|
if (!port.stream) {
|
||||||
port.isOpen = false;
|
port.isOpen = false;
|
||||||
return ORBIS_AUDIO_IN_ERROR_INVALID_PORT;
|
return ORBIS_AUDIO_IN_ERROR_INVALID_PORT;
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QHoverEvent>
|
#include <QHoverEvent>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
@ -27,6 +28,7 @@
|
|||||||
#include "settings_dialog.h"
|
#include "settings_dialog.h"
|
||||||
#include "ui_settings_dialog.h"
|
#include "ui_settings_dialog.h"
|
||||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||||
|
|
||||||
QStringList languageNames = {"Arabic",
|
QStringList languageNames = {"Arabic",
|
||||||
"Czech",
|
"Czech",
|
||||||
"Danish",
|
"Danish",
|
||||||
@ -65,6 +67,7 @@ QMap<QString, QString> channelMap;
|
|||||||
QMap<QString, QString> logTypeMap;
|
QMap<QString, QString> logTypeMap;
|
||||||
QMap<QString, QString> screenModeMap;
|
QMap<QString, QString> screenModeMap;
|
||||||
QMap<QString, QString> chooseHomeTabMap;
|
QMap<QString, QString> chooseHomeTabMap;
|
||||||
|
QMap<QString, QString> micMap;
|
||||||
|
|
||||||
int backgroundImageOpacitySlider_backup;
|
int backgroundImageOpacitySlider_backup;
|
||||||
int bgm_volume_backup;
|
int bgm_volume_backup;
|
||||||
@ -92,6 +95,7 @@ SettingsDialog::SettingsDialog(std::shared_ptr<gui_settings> gui_settings,
|
|||||||
{tr("Graphics"), "Graphics"}, {tr("User"), "User"},
|
{tr("Graphics"), "Graphics"}, {tr("User"), "User"},
|
||||||
{tr("Input"), "Input"}, {tr("Paths"), "Paths"},
|
{tr("Input"), "Input"}, {tr("Paths"), "Paths"},
|
||||||
{tr("Debug"), "Debug"}};
|
{tr("Debug"), "Debug"}};
|
||||||
|
micMap = {{tr("None"), "None"}, {tr("Default Device"), "Default Device"}};
|
||||||
|
|
||||||
if (m_physical_devices.empty()) {
|
if (m_physical_devices.empty()) {
|
||||||
// Populate cache of physical devices.
|
// Populate cache of physical devices.
|
||||||
@ -123,6 +127,25 @@ SettingsDialog::SettingsDialog(std::shared_ptr<gui_settings> gui_settings,
|
|||||||
ui->hideCursorComboBox->addItem(tr("Idle"));
|
ui->hideCursorComboBox->addItem(tr("Idle"));
|
||||||
ui->hideCursorComboBox->addItem(tr("Always"));
|
ui->hideCursorComboBox->addItem(tr("Always"));
|
||||||
|
|
||||||
|
ui->micComboBox->addItem(micMap.key("None"), "None");
|
||||||
|
ui->micComboBox->addItem(micMap.key("Default Device"), "Default Device");
|
||||||
|
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||||
|
int count = 0;
|
||||||
|
SDL_AudioDeviceID* devices = SDL_GetAudioRecordingDevices(&count);
|
||||||
|
if (devices) {
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
SDL_AudioDeviceID devId = devices[i];
|
||||||
|
const char* name = SDL_GetAudioDeviceName(devId);
|
||||||
|
if (name) {
|
||||||
|
QString qname = QString::fromUtf8(name);
|
||||||
|
ui->micComboBox->addItem(qname, QString::number(devId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_free(devices);
|
||||||
|
} else {
|
||||||
|
qDebug() << "Erro SDL_GetAudioRecordingDevices:" << SDL_GetError();
|
||||||
|
}
|
||||||
|
|
||||||
InitializeEmulatorLanguages();
|
InitializeEmulatorLanguages();
|
||||||
LoadValuesFromConfig();
|
LoadValuesFromConfig();
|
||||||
|
|
||||||
@ -441,6 +464,14 @@ void SettingsDialog::LoadValuesFromConfig() {
|
|||||||
ui->hideCursorComboBox->setCurrentIndex(toml::find_or<int>(data, "Input", "cursorState", 1));
|
ui->hideCursorComboBox->setCurrentIndex(toml::find_or<int>(data, "Input", "cursorState", 1));
|
||||||
OnCursorStateChanged(toml::find_or<int>(data, "Input", "cursorState", 1));
|
OnCursorStateChanged(toml::find_or<int>(data, "Input", "cursorState", 1));
|
||||||
ui->idleTimeoutSpinBox->setValue(toml::find_or<int>(data, "Input", "cursorHideTimeout", 5));
|
ui->idleTimeoutSpinBox->setValue(toml::find_or<int>(data, "Input", "cursorHideTimeout", 5));
|
||||||
|
|
||||||
|
QString micValue = QString::fromStdString(Config::getMicDevice());
|
||||||
|
int micIndex = ui->micComboBox->findData(micValue);
|
||||||
|
if (micIndex != -1) {
|
||||||
|
ui->micComboBox->setCurrentIndex(micIndex);
|
||||||
|
} else {
|
||||||
|
ui->micComboBox->setCurrentIndex(0);
|
||||||
|
}
|
||||||
// First options is auto selection -1, so gpuId on the GUI will always have to subtract 1
|
// First options is auto selection -1, so gpuId on the GUI will always have to subtract 1
|
||||||
// when setting and add 1 when getting to select the correct gpu in Qt
|
// when setting and add 1 when getting to select the correct gpu in Qt
|
||||||
ui->graphicsAdapterBox->setCurrentIndex(toml::find_or<int>(data, "Vulkan", "gpuId", -1) + 1);
|
ui->graphicsAdapterBox->setCurrentIndex(toml::find_or<int>(data, "Vulkan", "gpuId", -1) + 1);
|
||||||
@ -753,6 +784,7 @@ void SettingsDialog::UpdateSettings() {
|
|||||||
m_gui_settings->SetValue(gui::gl_playBackgroundMusic, ui->playBGMCheckBox->isChecked());
|
m_gui_settings->SetValue(gui::gl_playBackgroundMusic, ui->playBGMCheckBox->isChecked());
|
||||||
Config::setAllowHDR(ui->enableHDRCheckBox->isChecked());
|
Config::setAllowHDR(ui->enableHDRCheckBox->isChecked());
|
||||||
Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString());
|
Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString());
|
||||||
|
Config::setMicDevice(ui->micComboBox->currentData().toString().toStdString());
|
||||||
Config::setLogFilter(ui->logFilterLineEdit->text().toStdString());
|
Config::setLogFilter(ui->logFilterLineEdit->text().toStdString());
|
||||||
Config::setUserName(ui->userNameLineEdit->text().toStdString());
|
Config::setUserName(ui->userNameLineEdit->text().toStdString());
|
||||||
Config::setTrophyKey(ui->trophyKeyLineEdit->text().toStdString());
|
Config::setTrophyKey(ui->trophyKeyLineEdit->text().toStdString());
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>5</number>
|
<number>4</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QScrollArea" name="generalTab">
|
<widget class="QScrollArea" name="generalTab">
|
||||||
<property name="widgetResizable">
|
<property name="widgetResizable">
|
||||||
@ -149,6 +149,19 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_9">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -309,6 +322,32 @@
|
|||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>80</number>
|
<number>80</number>
|
||||||
</property>
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="updateCheckBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>11</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Check for Updates at Startup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="changelogCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Always Show Changelog</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="updaterComboBox">
|
<widget class="QGroupBox" name="updaterComboBox">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -396,32 +435,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="updateCheckBox">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>11</pointsize>
|
|
||||||
<bold>false</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Check for Updates at Startup</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="changelogCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Always Show Changelog</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -1440,7 +1453,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="cursorTabLayoutLeft">
|
<layout class="QVBoxLayout" name="cursorTabLayoutLeft">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>7</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
@ -1603,6 +1616,12 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Controller</string>
|
<string>Controller</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<layout class="QVBoxLayout" name="ControllerLayout">
|
<layout class="QVBoxLayout" name="ControllerLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
@ -1615,27 +1634,14 @@
|
|||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="motionControlsCheckBox">
|
<widget class="QCheckBox" name="motionControlsCheckBox">
|
||||||
<property name="text">
|
|
||||||
<string>Enable Motion Controls</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="controllerWidgetSpacer" native="true">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="text">
|
||||||
<size>
|
<string>Enable Motion Controls</string>
|
||||||
<width>0</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -1652,20 +1658,38 @@
|
|||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="emptyVerticalSpacerBottom">
|
<layout class="QHBoxLayout" name="micTabLayoutLeft">
|
||||||
<property name="orientation">
|
<item>
|
||||||
<enum>Qt::Orientation::Vertical</enum>
|
<widget class="QGroupBox" name="MicGroupBox">
|
||||||
</property>
|
<property name="title">
|
||||||
<property name="sizeType">
|
<string>Microphone</string>
|
||||||
<enum>QSizePolicy::Policy::MinimumExpanding</enum>
|
</property>
|
||||||
</property>
|
<widget class="QComboBox" name="micComboBox">
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="geometry">
|
||||||
<size>
|
<rect>
|
||||||
<width>20</width>
|
<x>14</x>
|
||||||
<height>20</height>
|
<y>33</y>
|
||||||
</size>
|
<width>431</width>
|
||||||
</property>
|
<height>31</height>
|
||||||
</spacer>
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_8">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
@ -1582,6 +1582,18 @@
|
|||||||
<source>Hide Cursor Idle Timeout</source>
|
<source>Hide Cursor Idle Timeout</source>
|
||||||
<translation>Hide Cursor Idle Timeout</translation>
|
<translation>Hide Cursor Idle Timeout</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Microphone</source>
|
||||||
|
<translation>Microphone</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>None</source>
|
||||||
|
<translation>None</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Default Device</source>
|
||||||
|
<translation>Default Device</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>s</source>
|
<source>s</source>
|
||||||
<translation>s</translation>
|
<translation>s</translation>
|
||||||
|
Loading…
Reference in New Issue
Block a user