CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/GameSettingsScreen.cpp
Views: 1401
1
// Copyright (c) 2013- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "ppsspp_config.h"
19
20
#include <algorithm>
21
#include <set>
22
23
#include "Common/Net/Resolve.h"
24
#include "Common/GPU/OpenGL/GLFeatures.h"
25
#include "Common/Render/DrawBuffer.h"
26
#include "Common/UI/Root.h"
27
#include "Common/UI/View.h"
28
#include "Common/UI/ViewGroup.h"
29
#include "Common/UI/Context.h"
30
#include "Common/Render/ManagedTexture.h"
31
#include "Common/VR/PPSSPPVR.h"
32
33
#include "Common/System/Display.h" // Only to check screen aspect ratio with pixel_yres/pixel_xres
34
#include "Common/System/Request.h"
35
#include "Common/System/OSD.h"
36
#include "Common/Battery/Battery.h"
37
#include "Common/System/NativeApp.h"
38
#include "Common/Data/Color/RGBAUtil.h"
39
#include "Common/Math/curves.h"
40
#include "Common/Data/Text/I18n.h"
41
#include "Common/Data/Encoding/Utf8.h"
42
#include "UI/EmuScreen.h"
43
#include "UI/DriverManagerScreen.h"
44
#include "UI/GameSettingsScreen.h"
45
#include "UI/GameInfoCache.h"
46
#include "UI/GamepadEmu.h"
47
#include "UI/MiscScreens.h"
48
#include "UI/ControlMappingScreen.h"
49
#include "UI/DevScreens.h"
50
#include "UI/DisplayLayoutScreen.h"
51
#include "UI/RemoteISOScreen.h"
52
#include "UI/SavedataScreen.h"
53
#include "UI/TouchControlLayoutScreen.h"
54
#include "UI/TouchControlVisibilityScreen.h"
55
#include "UI/TiltAnalogSettingsScreen.h"
56
#include "UI/GPUDriverTestScreen.h"
57
#include "UI/MemStickScreen.h"
58
#include "UI/Theme.h"
59
#include "UI/RetroAchievementScreens.h"
60
#include "UI/OnScreenDisplay.h"
61
62
#include "Common/File/FileUtil.h"
63
#include "Common/File/AndroidContentURI.h"
64
#include "Common/OSVersion.h"
65
#include "Common/TimeUtil.h"
66
#include "Common/StringUtils.h"
67
#include "Core/Config.h"
68
#include "Core/ConfigValues.h"
69
#include "Core/KeyMap.h"
70
#include "Core/TiltEventProcessor.h"
71
#include "Core/Instance.h"
72
#include "Core/System.h"
73
#include "Core/Reporting.h"
74
#include "Core/WebServer.h"
75
#include "Core/HLE/sceUsbCam.h"
76
#include "Core/HLE/sceUsbMic.h"
77
#include "GPU/Common/TextureReplacer.h"
78
#include "GPU/Common/PostShader.h"
79
#include "android/jni/TestRunner.h"
80
#include "GPU/GPUInterface.h"
81
#include "GPU/Common/FramebufferManagerCommon.h"
82
83
#include "Core/Core.h" // for Core_IsStepping
84
#include "Core/MIPS/MIPSTracer.h"
85
86
#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
87
#include "UI/DarwinFileSystemServices.h"
88
#endif
89
90
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
91
#pragma warning(disable:4091) // workaround bug in VS2015 headers
92
#include "Windows/MainWindow.h"
93
#include <shlobj.h>
94
#include "Windows/W32Util/ShellUtil.h"
95
#endif
96
97
#if PPSSPP_PLATFORM(ANDROID)
98
99
#include "android/jni/AndroidAudio.h"
100
#include "Common/File/AndroidStorage.h"
101
102
extern AndroidAudioState *g_audioState;
103
104
static bool CheckKgslPresent() {
105
constexpr auto KgslPath{"/dev/kgsl-3d0"};
106
107
return access(KgslPath, F_OK) == 0;
108
}
109
110
static bool SupportsCustomDriver() {
111
return android_get_device_api_level() >= 28 && CheckKgslPresent();
112
}
113
114
#else
115
116
static bool SupportsCustomDriver() {
117
#ifdef _DEBUG
118
return false; // change to true to debug driver installation on other platforms
119
#else
120
return false;
121
#endif
122
}
123
124
#endif
125
126
#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
127
static void SetMemStickDirDarwin(int requesterToken) {
128
auto initialPath = g_Config.memStickDirectory;
129
INFO_LOG(Log::System, "Current path: %s", initialPath.c_str());
130
System_BrowseForFolder(requesterToken, "", initialPath, [](const std::string &value, int) {
131
INFO_LOG(Log::System, "Selected path: %s", value.c_str());
132
DarwinFileSystemServices::setUserPreferredMemoryStickDirectory(Path(value));
133
});
134
}
135
#endif
136
137
GameSettingsScreen::GameSettingsScreen(const Path &gamePath, std::string gameID, bool editThenRestore)
138
: TabbedUIDialogScreenWithGameBackground(gamePath), gameID_(gameID), editThenRestore_(editThenRestore) {
139
prevInflightFrames_ = g_Config.iInflightFrames;
140
analogSpeedMapped_ = KeyMap::InputMappingsFromPspButton(VIRTKEY_SPEED_ANALOG, nullptr, true);
141
}
142
143
// This needs before run CheckGPUFeatures()
144
// TODO: Remove this if fix the issue
145
bool CheckSupportShaderTessellationGLES() {
146
#if PPSSPP_PLATFORM(UWP)
147
return true;
148
#else
149
// TODO: Make work with non-GL backends
150
int maxVertexTextureImageUnits = gl_extensions.maxVertexTextureUnits;
151
bool vertexTexture = maxVertexTextureImageUnits >= 3; // At least 3 for hardware tessellation
152
153
bool textureFloat = gl_extensions.ARB_texture_float || gl_extensions.OES_texture_float;
154
bool hasTexelFetch = gl_extensions.GLES3 || (!gl_extensions.IsGLES && gl_extensions.VersionGEThan(3, 3, 0)) || gl_extensions.EXT_gpu_shader4;
155
156
return vertexTexture && textureFloat && hasTexelFetch;
157
#endif
158
}
159
160
bool DoesBackendSupportHWTess() {
161
switch (GetGPUBackend()) {
162
case GPUBackend::OPENGL:
163
return CheckSupportShaderTessellationGLES();
164
case GPUBackend::VULKAN:
165
case GPUBackend::DIRECT3D11:
166
return true;
167
default:
168
return false;
169
}
170
}
171
172
static bool UsingHardwareTextureScaling() {
173
// For now, Vulkan only.
174
return g_Config.bTexHardwareScaling && GetGPUBackend() == GPUBackend::VULKAN && !g_Config.bSoftwareRendering;
175
}
176
177
static std::string TextureTranslateName(std::string_view value) {
178
const TextureShaderInfo *info = GetTextureShaderInfo(value);
179
if (info) {
180
auto ts = GetI18NCategory(I18NCat::TEXTURESHADERS);
181
return std::string(ts->T(value, info->name.c_str()));
182
} else {
183
return std::string(value);
184
}
185
}
186
187
static std::string *GPUDeviceNameSetting() {
188
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {
189
return &g_Config.sVulkanDevice;
190
}
191
#ifdef _WIN32
192
if (g_Config.iGPUBackend == (int)GPUBackend::DIRECT3D11) {
193
return &g_Config.sD3D11Device;
194
}
195
#endif
196
return nullptr;
197
}
198
199
bool PathToVisualUsbPath(Path path, std::string &outPath) {
200
switch (path.Type()) {
201
case PathType::NATIVE:
202
if (path.StartsWith(g_Config.memStickDirectory)) {
203
return g_Config.memStickDirectory.ComputePathTo(path, outPath);
204
}
205
break;
206
case PathType::CONTENT_URI:
207
#if PPSSPP_PLATFORM(ANDROID)
208
{
209
// Try to parse something sensible out of the content URI.
210
AndroidContentURI uri(path.ToString());
211
outPath = uri.RootPath();
212
if (startsWith(outPath, "primary:")) {
213
outPath = "/" + outPath.substr(8);
214
}
215
return true;
216
}
217
#endif
218
default:
219
break;
220
}
221
return false;
222
}
223
224
static std::string PostShaderTranslateName(std::string_view value) {
225
const ShaderInfo *info = GetPostShaderInfo(value);
226
if (info) {
227
auto ps = GetI18NCategory(I18NCat::POSTSHADERS);
228
return std::string(ps->T(value, info->name));
229
} else {
230
return std::string(value);
231
}
232
}
233
234
void GameSettingsScreen::PreCreateViews() {
235
ReloadAllPostShaderInfo(screenManager()->getDrawContext());
236
ReloadAllThemeInfo();
237
238
if (editThenRestore_) {
239
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO);
240
g_Config.loadGameConfig(gameID_, info->GetTitle());
241
}
242
243
iAlternateSpeedPercent1_ = g_Config.iFpsLimit1 < 0 ? -1 : (g_Config.iFpsLimit1 * 100) / 60;
244
iAlternateSpeedPercent2_ = g_Config.iFpsLimit2 < 0 ? -1 : (g_Config.iFpsLimit2 * 100) / 60;
245
iAlternateSpeedPercentAnalog_ = (g_Config.iAnalogFpsLimit * 100) / 60;
246
}
247
248
void GameSettingsScreen::CreateTabs() {
249
using namespace UI;
250
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
251
252
LinearLayout *graphicsSettings = AddTab("GameSettingsGraphics", ms->T("Graphics"));
253
CreateGraphicsSettings(graphicsSettings);
254
255
LinearLayout *controlsSettings = AddTab("GameSettingsControls", ms->T("Controls"));
256
CreateControlsSettings(controlsSettings);
257
258
LinearLayout *audioSettings = AddTab("GameSettingsAudio", ms->T("Audio"));
259
CreateAudioSettings(audioSettings);
260
261
LinearLayout *networkingSettings = AddTab("GameSettingsNetworking", ms->T("Networking"));
262
CreateNetworkingSettings(networkingSettings);
263
264
LinearLayout *tools = AddTab("GameSettingsTools", ms->T("Tools"));
265
CreateToolsSettings(tools);
266
267
LinearLayout *systemSettings = AddTab("GameSettingsSystem", ms->T("System"));
268
systemSettings->SetSpacing(0);
269
CreateSystemSettings(systemSettings);
270
271
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
272
if ((deviceType == DEVICE_TYPE_VR) || g_Config.bForceVR) {
273
LinearLayout *vrSettings = AddTab("GameSettingsVR", ms->T("VR"));
274
CreateVRSettings(vrSettings);
275
}
276
}
277
278
// Graphics
279
void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings) {
280
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
281
auto vr = GetI18NCategory(I18NCat::VR);
282
283
using namespace UI;
284
285
graphicsSettings->Add(new ItemHeader(gr->T("Rendering Mode")));
286
287
Draw::DrawContext *draw = screenManager()->getDrawContext();
288
289
#if !PPSSPP_PLATFORM(UWP)
290
static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan" };
291
PopupMultiChoice *renderingBackendChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, (int)GPUBackend::OPENGL, ARRAY_SIZE(renderingBackend), I18NCat::GRAPHICS, screenManager()));
292
if (g_Config.iGPUBackend != (int)GPUBackend::DIRECT3D9 && !draw->GetDeviceCaps().supportsD3D9) {
293
renderingBackendChoice->HideChoice(1);
294
}
295
renderingBackendChoice->OnChoice.Handle(this, &GameSettingsScreen::OnRenderingBackend);
296
297
if (!g_Config.IsBackendEnabled(GPUBackend::OPENGL))
298
renderingBackendChoice->HideChoice((int)GPUBackend::OPENGL);
299
if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D9))
300
renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D9);
301
if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11))
302
renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D11);
303
if (!g_Config.IsBackendEnabled(GPUBackend::VULKAN))
304
renderingBackendChoice->HideChoice((int)GPUBackend::VULKAN);
305
306
if (!IsFirstInstance()) {
307
// If we're not the first instance, can't save the setting, and it requires a restart, so...
308
renderingBackendChoice->SetEnabled(false);
309
}
310
#endif
311
312
// Backends that don't allow a device choice will only expose one device.
313
if (draw->GetDeviceList().size() > 1) {
314
std::string *deviceNameSetting = GPUDeviceNameSetting();
315
if (deviceNameSetting) {
316
PopupMultiChoiceDynamic *deviceChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(deviceNameSetting, gr->T("Device"), draw->GetDeviceList(), I18NCat::NONE, screenManager()));
317
deviceChoice->OnChoice.Handle(this, &GameSettingsScreen::OnRenderingDevice);
318
}
319
}
320
321
static const char *internalResolutions[] = { "Auto (1:1)", "1x PSP", "2x PSP", "3x PSP", "4x PSP", "5x PSP", "6x PSP", "7x PSP", "8x PSP", "9x PSP", "10x PSP" };
322
resolutionChoice_ = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iInternalResolution, gr->T("Rendering Resolution"), internalResolutions, 0, ARRAY_SIZE(internalResolutions), I18NCat::GRAPHICS, screenManager()));
323
resolutionChoice_->OnChoice.Handle(this, &GameSettingsScreen::OnResolutionChange);
324
resolutionChoice_->SetEnabledFunc([] {
325
return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects;
326
});
327
328
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
329
330
if (deviceType != DEVICE_TYPE_VR) {
331
CheckBox *softwareGPU = graphicsSettings->Add(new CheckBox(&g_Config.bSoftwareRendering, gr->T("Software Rendering", "Software Rendering (slow)")));
332
softwareGPU->SetEnabled(!PSP_IsInited());
333
}
334
335
if (draw->GetDeviceCaps().multiSampleLevelsMask != 1) {
336
static const char *msaaModes[] = { "Off", "2x", "4x", "8x", "16x" };
337
auto msaaChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iMultiSampleLevel, gr->T("Antialiasing (MSAA)"), msaaModes, 0, ARRAY_SIZE(msaaModes), I18NCat::GRAPHICS, screenManager()));
338
msaaChoice->OnChoice.Add([&](UI::EventParams &) -> UI::EventReturn {
339
System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);
340
return UI::EVENT_DONE;
341
});
342
msaaChoice->SetEnabledFunc([] {
343
return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects;
344
});
345
if (g_Config.iMultiSampleLevel > 1 && draw->GetDeviceCaps().isTilingGPU) {
346
msaaChoice->SetIcon(ImageID("I_WARNING"), 0.7f);
347
}
348
msaaChoice->SetEnabledFunc([] {
349
return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects;
350
});
351
352
// Hide unsupported levels.
353
for (int i = 1; i < 5; i++) {
354
if ((draw->GetDeviceCaps().multiSampleLevelsMask & (1 << i)) == 0) {
355
msaaChoice->HideChoice(i);
356
} else if (i > 0 && draw->GetDeviceCaps().isTilingGPU) {
357
msaaChoice->SetChoiceIcon(i, ImageID("I_WARNING"));
358
}
359
}
360
} else {
361
g_Config.iMultiSampleLevel = 0;
362
}
363
364
#if PPSSPP_PLATFORM(ANDROID)
365
if ((deviceType != DEVICE_TYPE_TV) && (deviceType != DEVICE_TYPE_VR)) {
366
static const char *deviceResolutions[] = { "Native device resolution", "Same as Rendering resolution", "1x PSP", "2x PSP", "3x PSP", "4x PSP", "5x PSP" };
367
int max_res_temp = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)) / 480 + 2;
368
if (max_res_temp == 3)
369
max_res_temp = 4; // At least allow 2x
370
int max_res = std::min(max_res_temp, (int)ARRAY_SIZE(deviceResolutions));
371
UI::PopupMultiChoice *hwscale = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iAndroidHwScale, gr->T("Display Resolution (HW scaler)"), deviceResolutions, 0, max_res, I18NCat::GRAPHICS, screenManager()));
372
hwscale->OnChoice.Add([](UI::EventParams &) {
373
System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);
374
System_PostUIMessage(UIMessage::GPU_DISPLAY_RESIZED);
375
System_RecreateActivity();
376
return UI::EVENT_DONE;
377
});
378
}
379
#endif
380
381
if (deviceType != DEVICE_TYPE_VR) {
382
#if !defined(MOBILE_DEVICE)
383
graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange);
384
if (System_GetPropertyInt(SYSPROP_DISPLAY_COUNT) > 1) {
385
CheckBox *fullscreenMulti = new CheckBox(&g_Config.bFullScreenMulti, gr->T("Use all displays"));
386
fullscreenMulti->SetEnabledFunc([] {
387
return g_Config.UseFullScreen();
388
});
389
graphicsSettings->Add(fullscreenMulti)->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenMultiChange);
390
}
391
#endif
392
393
// All backends support FIFO. Check if any immediate modes are supported, if so we can allow the user to choose.
394
if (draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::IMMEDIATE | Draw::PresentMode::MAILBOX)) {
395
CheckBox *vSync = graphicsSettings->Add(new CheckBox(&g_Config.bVSync, gr->T("VSync")));
396
vSync->OnClick.Add([=](EventParams &e) {
397
NativeResized();
398
return UI::EVENT_CONTINUE;
399
});
400
}
401
402
#if PPSSPP_PLATFORM(ANDROID)
403
// Hide Immersive Mode on pre-kitkat Android
404
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19) {
405
// Let's reuse the Fullscreen translation string from desktop.
406
graphicsSettings->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnImmersiveModeChange);
407
}
408
#endif
409
// Display Layout Editor: To avoid overlapping touch controls on large tablets, meet geeky demands for integer zoom/unstretched image etc.
410
displayEditor_ = graphicsSettings->Add(new Choice(gr->T("Display layout & effects")));
411
displayEditor_->OnClick.Add([&](UI::EventParams &) -> UI::EventReturn {
412
screenManager()->push(new DisplayLayoutScreen(gamePath_));
413
return UI::EVENT_DONE;
414
});
415
}
416
417
graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control")));
418
static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"};
419
graphicsSettings->Add(new PopupMultiChoice(&g_Config.iFrameSkip, gr->T("Frame Skipping"), frameSkip, 0, ARRAY_SIZE(frameSkip), I18NCat::GRAPHICS, screenManager()));
420
static const char *frameSkipType[] = {"Number of Frames", "Percent of FPS"};
421
graphicsSettings->Add(new PopupMultiChoice(&g_Config.iFrameSkipType, gr->T("Frame Skipping Type"), frameSkipType, 0, ARRAY_SIZE(frameSkipType), I18NCat::GRAPHICS, screenManager()));
422
frameSkipAuto_ = graphicsSettings->Add(new CheckBox(&g_Config.bAutoFrameSkip, gr->T("Auto FrameSkip")));
423
frameSkipAuto_->OnClick.Handle(this, &GameSettingsScreen::OnAutoFrameskip);
424
425
PopupSliderChoice *altSpeed1 = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent1_, 0, 1000, NO_DEFAULT_INT, gr->T("Alternative Speed", "Alternative speed"), 5, screenManager(), gr->T("%, 0:unlimited")));
426
altSpeed1->SetFormat("%i%%");
427
altSpeed1->SetZeroLabel(gr->T("Unlimited"));
428
altSpeed1->SetNegativeDisable(gr->T("Disabled"));
429
430
PopupSliderChoice *altSpeed2 = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent2_, 0, 1000, NO_DEFAULT_INT, gr->T("Alternative Speed 2", "Alternative speed 2 (in %, 0 = unlimited)"), 5, screenManager(), gr->T("%, 0:unlimited")));
431
altSpeed2->SetFormat("%i%%");
432
altSpeed2->SetZeroLabel(gr->T("Unlimited"));
433
altSpeed2->SetNegativeDisable(gr->T("Disabled"));
434
435
if (analogSpeedMapped_) {
436
PopupSliderChoice *analogSpeed = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercentAnalog_, 1, 1000, NO_DEFAULT_INT, gr->T("Analog Alternative Speed", "Analog alternative speed (in %)"), 5, screenManager(), gr->T("%")));
437
altSpeed2->SetFormat("%i%%");
438
}
439
440
graphicsSettings->Add(new ItemHeader(gr->T("Speed Hacks", "Speed Hacks (can cause rendering errors!)")));
441
442
CheckBox *skipBufferEffects = graphicsSettings->Add(new CheckBox(&g_Config.bSkipBufferEffects, gr->T("Skip Buffer Effects")));
443
skipBufferEffects->OnClick.Add([=](EventParams &e) {
444
if (g_Config.bSkipBufferEffects) {
445
settingInfo_->Show(gr->T("RenderingMode NonBuffered Tip", "Faster, but graphics may be missing in some games"), e.v);
446
g_Config.bAutoFrameSkip = false;
447
}
448
System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);
449
return UI::EVENT_DONE;
450
});
451
skipBufferEffects->SetDisabledPtr(&g_Config.bSoftwareRendering);
452
453
CheckBox *disableCulling = graphicsSettings->Add(new CheckBox(&g_Config.bDisableRangeCulling, gr->T("Disable culling")));
454
disableCulling->SetDisabledPtr(&g_Config.bSoftwareRendering);
455
456
static const char *skipGpuReadbackModes[] = { "No (default)", "Skip", "Copy to texture" };
457
458
PopupMultiChoice *skipGPUReadbacks = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iSkipGPUReadbackMode, gr->T("Skip GPU Readbacks"), skipGpuReadbackModes, 0, ARRAY_SIZE(skipGpuReadbackModes), I18NCat::GRAPHICS, screenManager()));
459
skipGPUReadbacks->SetDisabledPtr(&g_Config.bSoftwareRendering);
460
461
CheckBox *texBackoff = graphicsSettings->Add(new CheckBox(&g_Config.bTextureBackoffCache, gr->T("Lazy texture caching", "Lazy texture caching (speedup)")));
462
texBackoff->SetDisabledPtr(&g_Config.bSoftwareRendering);
463
texBackoff->OnClick.Add([=](EventParams& e) {
464
settingInfo_->Show(gr->T("Lazy texture caching Tip", "Faster, but can cause text problems in a few games"), e.v);
465
return UI::EVENT_CONTINUE;
466
});
467
468
static const char *quality[] = { "Low", "Medium", "High" };
469
PopupMultiChoice *beziersChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iSplineBezierQuality, gr->T("LowCurves", "Spline/Bezier curves quality"), quality, 0, ARRAY_SIZE(quality), I18NCat::GRAPHICS, screenManager()));
470
beziersChoice->OnChoice.Add([=](EventParams &e) {
471
if (g_Config.iSplineBezierQuality != 0) {
472
settingInfo_->Show(gr->T("LowCurves Tip", "Only used by some games, controls smoothness of curves"), e.v);
473
}
474
return UI::EVENT_CONTINUE;
475
});
476
477
graphicsSettings->Add(new ItemHeader(gr->T("Performance")));
478
CheckBox *frameDuplication = graphicsSettings->Add(new CheckBox(&g_Config.bRenderDuplicateFrames, gr->T("Render duplicate frames to 60hz")));
479
frameDuplication->OnClick.Add([=](EventParams &e) {
480
settingInfo_->Show(gr->T("RenderDuplicateFrames Tip", "Can make framerate smoother in games that run at lower framerates"), e.v);
481
return UI::EVENT_CONTINUE;
482
});
483
frameDuplication->SetEnabledFunc([] {
484
return !g_Config.bSkipBufferEffects && g_Config.iFrameSkip == 0;
485
});
486
487
if (draw->GetDeviceCaps().setMaxFrameLatencySupported) {
488
static const char *bufferOptions[] = { "No buffer", "Up to 1", "Up to 2" };
489
PopupMultiChoice *inflightChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iInflightFrames, gr->T("Buffer graphics commands (faster, input lag)"), bufferOptions, 1, ARRAY_SIZE(bufferOptions), I18NCat::GRAPHICS, screenManager()));
490
inflightChoice->OnChoice.Handle(this, &GameSettingsScreen::OnInflightFramesChoice);
491
}
492
493
if (GetGPUBackend() == GPUBackend::VULKAN) {
494
const bool usable = draw->GetDeviceCaps().geometryShaderSupported && !draw->GetBugs().Has(Draw::Bugs::GEOMETRY_SHADERS_SLOW_OR_BROKEN);
495
const bool vertexSupported = draw->GetDeviceCaps().clipDistanceSupported && draw->GetDeviceCaps().cullDistanceSupported;
496
if (usable && !vertexSupported) {
497
CheckBox *geometryCulling = graphicsSettings->Add(new CheckBox(&g_Config.bUseGeometryShader, gr->T("Geometry shader culling")));
498
geometryCulling->SetDisabledPtr(&g_Config.bSoftwareRendering);
499
}
500
}
501
502
if (deviceType != DEVICE_TYPE_VR) {
503
CheckBox *hwTransform = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTransform, gr->T("Hardware Transform")));
504
hwTransform->SetDisabledPtr(&g_Config.bSoftwareRendering);
505
}
506
507
CheckBox *swSkin = graphicsSettings->Add(new CheckBox(&g_Config.bSoftwareSkinning, gr->T("Software Skinning")));
508
swSkin->OnClick.Add([=](EventParams &e) {
509
settingInfo_->Show(gr->T("SoftwareSkinning Tip", "Combine skinned model draws on the CPU, faster in most games"), e.v);
510
return UI::EVENT_CONTINUE;
511
});
512
swSkin->SetDisabledPtr(&g_Config.bSoftwareRendering);
513
514
CheckBox *tessellationHW = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTessellation, gr->T("Hardware Tessellation")));
515
tessellationHW->OnClick.Add([=](EventParams &e) {
516
settingInfo_->Show(gr->T("HardwareTessellation Tip", "Uses hardware to make curves"), e.v);
517
return UI::EVENT_CONTINUE;
518
});
519
520
tessellationHW->SetEnabledFunc([]() {
521
return DoesBackendSupportHWTess() && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform;
522
});
523
524
// In case we're going to add few other antialiasing option like MSAA in the future.
525
// graphicsSettings->Add(new CheckBox(&g_Config.bFXAA, gr->T("FXAA")));
526
graphicsSettings->Add(new ItemHeader(gr->T("Texture Scaling")));
527
#ifndef MOBILE_DEVICE
528
static const char *texScaleLevels[] = {"Off", "2x", "3x", "4x", "5x"};
529
#else
530
static const char *texScaleLevels[] = {"Off", "2x", "3x"};
531
#endif
532
533
static const char *texScaleAlgos[] = { "xBRZ", "Hybrid", "Bicubic", "Hybrid + Bicubic", };
534
PopupMultiChoice *texScalingType = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iTexScalingType, gr->T("Upscale Type"), texScaleAlgos, 0, ARRAY_SIZE(texScaleAlgos), I18NCat::GRAPHICS, screenManager()));
535
texScalingType->SetEnabledFunc([]() {
536
return !g_Config.bSoftwareRendering && !UsingHardwareTextureScaling();
537
});
538
PopupMultiChoice *texScalingChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iTexScalingLevel, gr->T("Upscale Level"), texScaleLevels, 1, ARRAY_SIZE(texScaleLevels), I18NCat::GRAPHICS, screenManager()));
539
// TODO: Better check? When it won't work, it scales down anyway.
540
if (!gl_extensions.OES_texture_npot && GetGPUBackend() == GPUBackend::OPENGL) {
541
texScalingChoice->HideChoice(3); // 3x
542
texScalingChoice->HideChoice(5); // 5x
543
}
544
texScalingChoice->OnChoice.Add([=](EventParams &e) {
545
if (g_Config.iTexScalingLevel != 1 && !UsingHardwareTextureScaling()) {
546
settingInfo_->Show(gr->T("UpscaleLevel Tip", "CPU heavy - some scaling may be delayed to avoid stutter"), e.v);
547
}
548
return UI::EVENT_CONTINUE;
549
});
550
texScalingChoice->SetEnabledFunc([]() {
551
return !g_Config.bSoftwareRendering && !UsingHardwareTextureScaling();
552
});
553
554
CheckBox *deposterize = graphicsSettings->Add(new CheckBox(&g_Config.bTexDeposterize, gr->T("Deposterize")));
555
deposterize->OnClick.Add([=](EventParams &e) {
556
if (g_Config.bTexDeposterize == true) {
557
settingInfo_->Show(gr->T("Deposterize Tip", "Fixes visual banding glitches in upscaled textures"), e.v);
558
}
559
return UI::EVENT_CONTINUE;
560
});
561
deposterize->SetEnabledFunc([]() {
562
return !g_Config.bSoftwareRendering && !UsingHardwareTextureScaling();
563
});
564
565
ChoiceWithValueDisplay *textureShaderChoice = graphicsSettings->Add(new ChoiceWithValueDisplay(&g_Config.sTextureShaderName, gr->T("Texture Shader"), &TextureTranslateName));
566
textureShaderChoice->OnClick.Handle(this, &GameSettingsScreen::OnTextureShader);
567
textureShaderChoice->SetEnabledFunc([]() {
568
return GetGPUBackend() == GPUBackend::VULKAN && !g_Config.bSoftwareRendering;
569
});
570
571
graphicsSettings->Add(new ItemHeader(gr->T("Texture Filtering")));
572
static const char *anisoLevels[] = { "Off", "2x", "4x", "8x", "16x" };
573
PopupMultiChoice *anisoFiltering = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iAnisotropyLevel, gr->T("Anisotropic Filtering"), anisoLevels, 0, ARRAY_SIZE(anisoLevels), I18NCat::GRAPHICS, screenManager()));
574
anisoFiltering->SetDisabledPtr(&g_Config.bSoftwareRendering);
575
576
static const char *texFilters[] = { "Auto", "Nearest", "Linear", "Auto Max Quality"};
577
PopupMultiChoice *filters = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iTexFiltering, gr->T("Texture Filter"), texFilters, 1, ARRAY_SIZE(texFilters), I18NCat::GRAPHICS, screenManager()));
578
filters->SetDisabledPtr(&g_Config.bSoftwareRendering);
579
580
CheckBox *smartFiltering = graphicsSettings->Add(new CheckBox(&g_Config.bSmart2DTexFiltering, gr->T("Smart 2D texture filtering")));
581
smartFiltering->SetDisabledPtr(&g_Config.bSoftwareRendering);
582
583
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
584
bool showCardboardSettings = deviceType != DEVICE_TYPE_VR;
585
#else
586
// If you enabled it through the ini, you can see this. Useful for testing.
587
bool showCardboardSettings = g_Config.bEnableCardboardVR;
588
#endif
589
if (showCardboardSettings) {
590
graphicsSettings->Add(new ItemHeader(gr->T("Cardboard VR Settings", "Cardboard VR Settings")));
591
graphicsSettings->Add(new CheckBox(&g_Config.bEnableCardboardVR, gr->T("Enable Cardboard VR", "Enable Cardboard VR")));
592
PopupSliderChoice *cardboardScreenSize = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iCardboardScreenSize, 30, 150, 50, gr->T("Cardboard Screen Size", "Screen Size (in % of the viewport)"), 1, screenManager(), gr->T("% of viewport")));
593
cardboardScreenSize->SetEnabledPtr(&g_Config.bEnableCardboardVR);
594
PopupSliderChoice *cardboardXShift = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iCardboardXShift, -150, 150, 0, gr->T("Cardboard Screen X Shift", "X Shift (in % of the void)"), 1, screenManager(), gr->T("% of the void")));
595
cardboardXShift->SetEnabledPtr(&g_Config.bEnableCardboardVR);
596
PopupSliderChoice *cardboardYShift = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iCardboardYShift, -100, 100, 0, gr->T("Cardboard Screen Y Shift", "Y Shift (in % of the void)"), 1, screenManager(), gr->T("% of the void")));
597
cardboardYShift->SetEnabledPtr(&g_Config.bEnableCardboardVR);
598
}
599
600
std::vector<std::string> cameraList = Camera::getDeviceList();
601
if (cameraList.size() >= 1) {
602
graphicsSettings->Add(new ItemHeader(gr->T("Camera")));
603
PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sCameraDevice, gr->T("Camera Device"), cameraList, I18NCat::NONE, screenManager()));
604
cameraChoice->OnChoice.Handle(this, &GameSettingsScreen::OnCameraDeviceChange);
605
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
606
graphicsSettings->Add(new CheckBox(&g_Config.bCameraMirrorHorizontal, gr->T("Mirror camera image")));
607
#endif
608
}
609
610
graphicsSettings->Add(new ItemHeader(gr->T("Hack Settings", "Hack Settings (these WILL cause glitches)")));
611
612
static const char *bloomHackOptions[] = { "Off", "Safe", "Balanced", "Aggressive" };
613
PopupMultiChoice *bloomHack = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iBloomHack, gr->T("Lower resolution for effects (reduces artifacts)"), bloomHackOptions, 0, ARRAY_SIZE(bloomHackOptions), I18NCat::GRAPHICS, screenManager()));
614
bloomHack->SetEnabledFunc([] {
615
return !g_Config.bSoftwareRendering && g_Config.iInternalResolution != 1;
616
});
617
618
graphicsSettings->Add(new ItemHeader(gr->T("Overlay Information")));
619
BitCheckBox *showFPSCtr = graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::FPS_COUNTER, gr->T("Show FPS Counter")));
620
BitCheckBox *showSpeed = graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::SPEED_COUNTER, gr->T("Show Speed")));
621
#ifdef CAN_DISPLAY_CURRENT_BATTERY_CAPACITY
622
BitCheckBox *showBattery = graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::BATTERY_PERCENT, gr->T("Show Battery %")));
623
#endif
624
AddOverlayList(graphicsSettings, screenManager());
625
}
626
627
void GameSettingsScreen::CreateAudioSettings(UI::ViewGroup *audioSettings) {
628
using namespace UI;
629
630
auto a = GetI18NCategory(I18NCat::AUDIO);
631
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
632
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
633
634
audioSettings->Add(new ItemHeader(ms->T("Audio")));
635
CheckBox *enableSound = audioSettings->Add(new CheckBox(&g_Config.bEnableSound,a->T("Enable Sound")));
636
637
#if PPSSPP_PLATFORM(IOS)
638
CheckBox *respectSilentMode = audioSettings->Add(new CheckBox(&g_Config.bAudioRespectSilentMode, a->T("Respect silent mode")));
639
respectSilentMode->OnClick.Add([=](EventParams &e) {
640
System_Notify(SystemNotification::AUDIO_MODE_CHANGED);
641
return UI::EVENT_DONE;
642
});
643
respectSilentMode->SetEnabledPtr(&g_Config.bEnableSound);
644
CheckBox *mixWithOthers = audioSettings->Add(new CheckBox(&g_Config.bAudioMixWithOthers, a->T("Mix audio with other apps")));
645
mixWithOthers->OnClick.Add([=](EventParams &e) {
646
System_Notify(SystemNotification::AUDIO_MODE_CHANGED);
647
return UI::EVENT_DONE;
648
});
649
mixWithOthers->SetEnabledPtr(&g_Config.bEnableSound);
650
#endif
651
652
PopupSliderChoice *volume = audioSettings->Add(new PopupSliderChoice(&g_Config.iGlobalVolume, VOLUME_OFF, VOLUME_FULL, VOLUME_FULL, a->T("Global volume"), screenManager()));
653
volume->SetEnabledPtr(&g_Config.bEnableSound);
654
volume->SetZeroLabel(a->T("Mute"));
655
656
PopupSliderChoice *altVolume = audioSettings->Add(new PopupSliderChoice(&g_Config.iAltSpeedVolume, VOLUME_OFF, VOLUME_FULL, NO_DEFAULT_INT, a->T("Alternate speed volume"), screenManager()));
657
altVolume->SetEnabledPtr(&g_Config.bEnableSound);
658
altVolume->SetZeroLabel(a->T("Mute"));
659
altVolume->SetNegativeDisable(a->T("Use global volume"));
660
661
PopupSliderChoice *reverbVolume = audioSettings->Add(new PopupSliderChoice(&g_Config.iReverbVolume, VOLUME_OFF, 2 * VOLUME_FULL, VOLUME_FULL, a->T("Reverb volume"), screenManager()));
662
reverbVolume->SetEnabledPtr(&g_Config.bEnableSound);
663
reverbVolume->SetZeroLabel(a->T("Disabled"));
664
665
PopupSliderChoice *achievementVolume = audioSettings->Add(new PopupSliderChoice(&g_Config.iAchievementSoundVolume, VOLUME_OFF, VOLUME_FULL, VOLUME_FULL, ac->T("Achievement sound volume"), screenManager()));
666
achievementVolume->SetEnabledPtr(&g_Config.bEnableSound);
667
achievementVolume->SetZeroLabel(a->T("Mute"));
668
669
// Hide the backend selector in UWP builds (we only support XAudio2 there).
670
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
671
if (IsVistaOrHigher()) {
672
static const char *backend[] = { "Auto", "DSound (compatible)", "WASAPI (fast)" };
673
PopupMultiChoice *audioBackend = audioSettings->Add(new PopupMultiChoice(&g_Config.iAudioBackend, a->T("Audio backend", "Audio backend (restart req.)"), backend, 0, ARRAY_SIZE(backend), I18NCat::AUDIO, screenManager()));
674
audioBackend->SetEnabledPtr(&g_Config.bEnableSound);
675
}
676
#endif
677
678
bool sdlAudio = false;
679
#if defined(SDL)
680
std::vector<std::string> audioDeviceList;
681
SplitString(System_GetProperty(SYSPROP_AUDIO_DEVICE_LIST), '\0', audioDeviceList);
682
audioDeviceList.insert(audioDeviceList.begin(), a->T_cstr("Auto"));
683
PopupMultiChoiceDynamic *audioDevice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sAudioDevice, a->T("Device"), audioDeviceList, I18NCat::NONE, screenManager()));
684
audioDevice->OnChoice.Handle(this, &GameSettingsScreen::OnAudioDevice);
685
sdlAudio = true;
686
#endif
687
688
if (sdlAudio || g_Config.iAudioBackend == AUDIO_BACKEND_WASAPI) {
689
audioSettings->Add(new CheckBox(&g_Config.bAutoAudioDevice, a->T("Use new audio devices automatically")));
690
}
691
692
#if PPSSPP_PLATFORM(ANDROID)
693
CheckBox *extraAudio = audioSettings->Add(new CheckBox(&g_Config.bExtraAudioBuffering, a->T("AudioBufferingForBluetooth", "Bluetooth-friendly buffer (slower)")));
694
extraAudio->SetEnabledPtr(&g_Config.bEnableSound);
695
696
// Show OpenSL debug info
697
const std::string audioErrorStr = AndroidAudio_GetErrorString(g_audioState);
698
if (!audioErrorStr.empty()) {
699
audioSettings->Add(new InfoItem(a->T("Audio Error"), audioErrorStr));
700
}
701
#endif
702
703
std::vector<std::string> micList = Microphone::getDeviceList();
704
if (!micList.empty()) {
705
audioSettings->Add(new ItemHeader(a->T("Microphone")));
706
PopupMultiChoiceDynamic *MicChoice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sMicDevice, a->T("Microphone Device"), micList, I18NCat::NONE, screenManager()));
707
MicChoice->OnChoice.Handle(this, &GameSettingsScreen::OnMicDeviceChange);
708
}
709
}
710
711
void GameSettingsScreen::CreateControlsSettings(UI::ViewGroup *controlsSettings) {
712
using namespace UI;
713
714
auto co = GetI18NCategory(I18NCat::CONTROLS);
715
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
716
auto di = GetI18NCategory(I18NCat::DIALOG);
717
718
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
719
720
controlsSettings->Add(new ItemHeader(ms->T("Controls")));
721
controlsSettings->Add(new Choice(co->T("Control Mapping")))->OnClick.Handle(this, &GameSettingsScreen::OnControlMapping);
722
controlsSettings->Add(new Choice(co->T("Calibrate Analog Stick")))->OnClick.Handle(this, &GameSettingsScreen::OnCalibrateAnalogs);
723
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fAnalogTriggerThreshold, 0.02f, 0.98f, 0.75f, co->T("Analog trigger threshold"), screenManager()));
724
725
#if defined(USING_WIN_UI) || (PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID))
726
controlsSettings->Add(new CheckBox(&g_Config.bSystemControls, co->T("Enable standard shortcut keys")));
727
#endif
728
#if defined(USING_WIN_UI)
729
controlsSettings->Add(new CheckBox(&g_Config.bGamepadOnlyFocused, co->T("Ignore gamepads when not focused")));
730
#endif
731
732
if (System_GetPropertyBool(SYSPROP_HAS_ACCELEROMETER)) {
733
// Show the tilt type on the item.
734
Choice *customizeTilt = controlsSettings->Add(new ChoiceWithCallbackValueDisplay(co->T("Tilt control setup"), []() -> std::string {
735
auto co = GetI18NCategory(I18NCat::CONTROLS);
736
if ((u32)g_Config.iTiltInputType < (u32)g_numTiltTypes) {
737
return std::string(co->T(g_tiltTypes[g_Config.iTiltInputType]));
738
}
739
return "";
740
}));
741
customizeTilt->OnClick.Handle(this, &GameSettingsScreen::OnTiltCustomize);
742
} else if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_VR) { // TODO: This seems like a regression
743
controlsSettings->Add(new CheckBox(&g_Config.bHapticFeedback, co->T("HapticFeedback", "Haptic Feedback (vibration)")));
744
}
745
746
// TVs don't have touch control, at least not yet.
747
if ((deviceType != DEVICE_TYPE_TV) && (deviceType != DEVICE_TYPE_VR)) {
748
controlsSettings->Add(new ItemHeader(co->T("OnScreen", "On-Screen Touch Controls")));
749
controlsSettings->Add(new CheckBox(&g_Config.bShowTouchControls, co->T("OnScreen", "On-Screen Touch Controls")));
750
layoutEditorChoice_ = controlsSettings->Add(new Choice(co->T("Customize Touch Controls")));
751
layoutEditorChoice_->OnClick.Handle(this, &GameSettingsScreen::OnTouchControlLayout);
752
layoutEditorChoice_->SetEnabledPtr(&g_Config.bShowTouchControls);
753
754
Choice *gesture = controlsSettings->Add(new Choice(co->T("Gesture mapping")));
755
gesture->OnClick.Add([=](EventParams &e) {
756
screenManager()->push(new GestureMappingScreen(gamePath_));
757
return UI::EVENT_DONE;
758
});
759
gesture->SetEnabledPtr(&g_Config.bShowTouchControls);
760
761
static const char *touchControlStyles[] = { "Classic", "Thin borders", "Glowing borders" };
762
View *style = controlsSettings->Add(new PopupMultiChoice(&g_Config.iTouchButtonStyle, co->T("Button style"), touchControlStyles, 0, ARRAY_SIZE(touchControlStyles), I18NCat::CONTROLS, screenManager()));
763
style->SetEnabledPtr(&g_Config.bShowTouchControls);
764
765
PopupSliderChoice *opacity = controlsSettings->Add(new PopupSliderChoice(&g_Config.iTouchButtonOpacity, 0, 100, 65, co->T("Button Opacity"), screenManager(), "%"));
766
opacity->SetEnabledPtr(&g_Config.bShowTouchControls);
767
opacity->SetFormat("%i%%");
768
PopupSliderChoice *autoHide = controlsSettings->Add(new PopupSliderChoice(&g_Config.iTouchButtonHideSeconds, 0, 300, 20, co->T("Auto-hide buttons after delay"), screenManager(), di->T("seconds, 0:off")));
769
autoHide->SetEnabledPtr(&g_Config.bShowTouchControls);
770
autoHide->SetFormat(di->T("%d seconds"));
771
autoHide->SetZeroLabel(co->T("Off"));
772
773
// Hide stick background, useful when increasing the size
774
CheckBox *hideStickBackground = controlsSettings->Add(new CheckBox(&g_Config.bHideStickBackground, co->T("Hide touch analog stick background circle")));
775
hideStickBackground->SetEnabledPtr(&g_Config.bShowTouchControls);
776
777
// Sticky D-pad.
778
CheckBox *stickyDpad = controlsSettings->Add(new CheckBox(&g_Config.bStickyTouchDPad, co->T("Sticky D-Pad (easier sweeping movements)")));
779
stickyDpad->SetEnabledPtr(&g_Config.bShowTouchControls);
780
781
// Re-centers itself to the touch location on touch-down.
782
CheckBox *floatingAnalog = controlsSettings->Add(new CheckBox(&g_Config.bAutoCenterTouchAnalog, co->T("Auto-centering analog stick")));
783
floatingAnalog->SetEnabledPtr(&g_Config.bShowTouchControls);
784
785
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {
786
controlsSettings->Add(new CheckBox(&g_Config.bHapticFeedback, co->T("HapticFeedback", "Haptic Feedback (vibration)")));
787
}
788
789
// On non iOS systems, offer to let the user see this button.
790
// Some Windows touch devices don't have a back button or other button to call up the menu.
791
if (System_GetPropertyBool(SYSPROP_HAS_BACK_BUTTON)) {
792
CheckBox *enablePauseBtn = controlsSettings->Add(new CheckBox(&g_Config.bShowTouchPause, co->T("Show Touch Pause Menu Button")));
793
794
// Don't allow the user to disable it once in-game, so they can't lock themselves out of the menu.
795
if (!PSP_IsInited()) {
796
enablePauseBtn->SetEnabledPtr(&g_Config.bShowTouchControls);
797
} else {
798
enablePauseBtn->SetEnabled(false);
799
}
800
}
801
802
CheckBox *disableDiags = controlsSettings->Add(new CheckBox(&g_Config.bDisableDpadDiagonals, co->T("Disable D-Pad diagonals (4-way touch)")));
803
disableDiags->SetEnabledPtr(&g_Config.bShowTouchControls);
804
}
805
806
if (deviceType != DEVICE_TYPE_VR) {
807
controlsSettings->Add(new ItemHeader(co->T("Keyboard", "Keyboard Control Settings")));
808
#if defined(USING_WIN_UI)
809
controlsSettings->Add(new CheckBox(&g_Config.bIgnoreWindowsKey, co->T("Ignore Windows Key")));
810
#endif // #if defined(USING_WIN_UI)
811
auto analogLimiter = new PopupSliderChoiceFloat(&g_Config.fAnalogLimiterDeadzone, 0.0f, 1.0f, 0.6f, co->T("Analog Limiter"), 0.10f, screenManager(), "/ 1.0");
812
controlsSettings->Add(analogLimiter);
813
analogLimiter->OnChange.Add([=](EventParams &e) {
814
settingInfo_->Show(co->T("AnalogLimiter Tip", "When the analog limiter button is pressed"), e.v);
815
return UI::EVENT_CONTINUE;
816
});
817
controlsSettings->Add(new PopupSliderChoice(&g_Config.iRapidFireInterval, 1, 10, 5, co->T("Rapid fire interval"), screenManager(), "frames"));
818
#if defined(USING_WIN_UI) || defined(SDL) || PPSSPP_PLATFORM(ANDROID)
819
bool enableMouseSettings = true;
820
#if PPSSPP_PLATFORM(ANDROID)
821
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) < 12) {
822
enableMouseSettings = false;
823
}
824
#endif
825
#else
826
bool enableMouseSettings = false;
827
#endif
828
if (enableMouseSettings) {
829
// The mousewheel button-release setting is independent of actual mouse delta control.
830
controlsSettings->Add(new ItemHeader(co->T("Mouse", "Mouse settings")));
831
auto wheelUpDelaySlider = controlsSettings->Add(new PopupSliderChoice(&g_Config.iMouseWheelUpDelayMs, 10, 300, 1, co->T("Mouse wheel button-release delay"), screenManager()));
832
wheelUpDelaySlider->SetFormat(di->T("%d ms"));
833
834
CheckBox *mouseControl = controlsSettings->Add(new CheckBox(&g_Config.bMouseControl, co->T("Use Mouse Control")));
835
mouseControl->OnClick.Add([=](EventParams &e) {
836
if (g_Config.bMouseControl)
837
settingInfo_->Show(co->T("MouseControl Tip", "You can now map mouse in control mapping screen by pressing the 'M' icon."), e.v);
838
return UI::EVENT_CONTINUE;
839
});
840
#if !PPSSPP_PLATFORM(ANDROID)
841
controlsSettings->Add(new CheckBox(&g_Config.bMouseConfine, co->T("Confine Mouse", "Trap mouse within window/display area")))->SetEnabledPtr(&g_Config.bMouseControl);
842
#endif
843
auto sensitivitySlider = controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMouseSensitivity, 0.01f, 1.0f, 0.1f, co->T("Mouse sensitivity"), 0.01f, screenManager(), "x"));
844
sensitivitySlider->SetEnabledPtr(&g_Config.bMouseControl);
845
sensitivitySlider->SetLiveUpdate(true);
846
auto smoothingSlider = controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMouseSmoothing, 0.0f, 0.95f, 0.9f, co->T("Mouse smoothing"), 0.05f, screenManager(), "x"));
847
smoothingSlider->SetEnabledPtr(&g_Config.bMouseControl);
848
smoothingSlider->SetLiveUpdate(true);
849
}
850
}
851
}
852
853
// Compound view just like the audio file choosers
854
class MacAddressChooser : public UI::LinearLayout {
855
public:
856
MacAddressChooser(RequesterToken token, Path gamePath, std::string *value, std::string_view title, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr);
857
};
858
859
static constexpr UI::Size ITEM_HEIGHT = 64.f;
860
861
MacAddressChooser::MacAddressChooser(RequesterToken token, Path gamePath_, std::string *value, std::string_view title, ScreenManager *screenManager, UI::LayoutParams *layoutParams) : UI::LinearLayout(UI::ORIENT_HORIZONTAL, layoutParams) {
862
using namespace UI;
863
SetSpacing(5.0f);
864
if (!layoutParams) {
865
layoutParams_->width = FILL_PARENT;
866
layoutParams_->height = ITEM_HEIGHT;
867
}
868
auto n = GetI18NCategory(I18NCat::NETWORKING);
869
870
std::string initialValue = *value;
871
Add(new PopupTextInputChoice(token, value, title, g_Config.sMACAddress, 17, screenManager, new LinearLayoutParams(1.0f)))->OnChange.Add([=](UI::EventParams &e) {
872
// Validate the chosen address, and restore to initialValue if bad.
873
if (g_Config.sMACAddress.size() != 17) {
874
// TODO: Alert the user
875
*value = initialValue;
876
}
877
return UI::EVENT_DONE;
878
});
879
Add(new Choice(n->T("Randomize"), new LinearLayoutParams(WRAP_CONTENT, ITEM_HEIGHT)))->OnClick.Add([=](UI::EventParams &) {
880
auto n = GetI18NCategory(I18NCat::NETWORKING);
881
auto di = GetI18NCategory(I18NCat::DIALOG);
882
883
std::string_view confirmMessage = n->T("ChangeMacSaveConfirm", "Generate a new MAC address?");
884
std::string_view warningMessage = n->T("ChangeMacSaveWarning", "Some games verify the MAC address when loading savedata, so this may break old saves.");
885
std::string combined = g_Config.sMACAddress + "\n\n" + std::string(confirmMessage) + "\n\n" + std::string(warningMessage);
886
887
auto confirmScreen = new PromptScreen(
888
gamePath_,
889
combined, di->T("Yes"), di->T("No"),
890
[&](bool success) {
891
if (success) {
892
g_Config.sMACAddress = CreateRandMAC();
893
}}
894
);
895
screenManager->push(confirmScreen);
896
return UI::EVENT_DONE;
897
});
898
}
899
900
void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSettings) {
901
using namespace UI;
902
903
auto n = GetI18NCategory(I18NCat::NETWORKING);
904
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
905
906
networkingSettings->Add(new ItemHeader(ms->T("Networking")));
907
908
networkingSettings->Add(new Choice(n->T("Open PPSSPP Multiplayer Wiki Page")))->OnClick.Handle(this, &GameSettingsScreen::OnAdhocGuides);
909
910
networkingSettings->Add(new CheckBox(&g_Config.bEnableWlan, n->T("Enable networking", "Enable networking/wlan (beta)")));
911
networkingSettings->Add(new MacAddressChooser(GetRequesterToken(), gamePath_, &g_Config.sMACAddress, n->T("Change Mac Address"), screenManager()));
912
static const char* wlanChannels[] = { "Auto", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11" };
913
auto wlanChannelChoice = networkingSettings->Add(new PopupMultiChoice(&g_Config.iWlanAdhocChannel, n->T("WLAN Channel"), wlanChannels, 0, ARRAY_SIZE(wlanChannels), I18NCat::NETWORKING, screenManager()));
914
for (int i = 0; i < 4; i++) {
915
wlanChannelChoice->HideChoice(i + 2);
916
wlanChannelChoice->HideChoice(i + 7);
917
}
918
networkingSettings->Add(new CheckBox(&g_Config.bDiscordPresence, n->T("Send Discord Presence information")));
919
920
networkingSettings->Add(new ItemHeader(n->T("AdHoc Server")));
921
networkingSettings->Add(new CheckBox(&g_Config.bEnableAdhocServer, n->T("Enable built-in PRO Adhoc Server", "Enable built-in PRO Adhoc Server")));
922
networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.proAdhocServer, n->T("Change proAdhocServer Address", "Change proAdhocServer Address (localhost = multiple instance)"), I18NCat::NONE))->OnClick.Handle(this, &GameSettingsScreen::OnChangeproAdhocServerAddress);
923
924
networkingSettings->Add(new ItemHeader(n->T("UPnP (port-forwarding)")));
925
networkingSettings->Add(new CheckBox(&g_Config.bEnableUPnP, n->T("Enable UPnP", "Enable UPnP (need a few seconds to detect)")));
926
auto useOriPort = networkingSettings->Add(new CheckBox(&g_Config.bUPnPUseOriginalPort, n->T("UPnP use original port", "UPnP use original port (Enabled = PSP compatibility)")));
927
useOriPort->OnClick.Add([=](EventParams& e) {
928
if (g_Config.bUPnPUseOriginalPort)
929
settingInfo_->Show(n->T("UseOriginalPort Tip", "May not work for all devices or games, see wiki."), e.v);
930
return UI::EVENT_CONTINUE;
931
});
932
useOriPort->SetEnabledPtr(&g_Config.bEnableUPnP);
933
934
networkingSettings->Add(new ItemHeader(n->T("Chat")));
935
networkingSettings->Add(new CheckBox(&g_Config.bEnableNetworkChat, n->T("Enable network chat", "Enable network chat")));
936
static const char *chatButtonPositions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right", "None" };
937
networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatButtonPosition, n->T("Chat Button Position"), chatButtonPositions, 0, ARRAY_SIZE(chatButtonPositions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat);
938
static const char *chatScreenPositions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right" };
939
networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatScreenPosition, n->T("Chat Screen Position"), chatScreenPositions, 0, ARRAY_SIZE(chatScreenPositions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat);
940
941
#if (!defined(MOBILE_DEVICE) && !defined(USING_QT_UI)) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) // Missing only iOS?
942
networkingSettings->Add(new ItemHeader(n->T("QuickChat", "Quick Chat")));
943
CheckBox *qc = networkingSettings->Add(new CheckBox(&g_Config.bEnableQuickChat, n->T("EnableQuickChat", "Enable Quick Chat")));
944
qc->SetEnabledPtr(&g_Config.bEnableNetworkChat);
945
#endif
946
947
#if !defined(MOBILE_DEVICE) && !defined(USING_QT_UI) // TODO: Add all platforms where KEY_CHAR support is added
948
PopupTextInputChoice *qc1 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat0, n->T("Quick Chat 1"), "", 32, screenManager()));
949
PopupTextInputChoice *qc2 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat1, n->T("Quick Chat 2"), "", 32, screenManager()));
950
PopupTextInputChoice *qc3 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat2, n->T("Quick Chat 3"), "", 32, screenManager()));
951
PopupTextInputChoice *qc4 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat3, n->T("Quick Chat 4"), "", 32, screenManager()));
952
PopupTextInputChoice *qc5 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat4, n->T("Quick Chat 5"), "", 32, screenManager()));
953
#elif defined(USING_QT_UI)
954
Choice *qc1 = networkingSettings->Add(new Choice(n->T("Quick Chat 1")));
955
Choice *qc2 = networkingSettings->Add(new Choice(n->T("Quick Chat 2")));
956
Choice *qc3 = networkingSettings->Add(new Choice(n->T("Quick Chat 3")));
957
Choice *qc4 = networkingSettings->Add(new Choice(n->T("Quick Chat 4")));
958
Choice *qc5 = networkingSettings->Add(new Choice(n->T("Quick Chat 5")));
959
#elif PPSSPP_PLATFORM(ANDROID)
960
ChoiceWithValueDisplay *qc1 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat0, n->T("Quick Chat 1"), I18NCat::NONE));
961
ChoiceWithValueDisplay *qc2 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat1, n->T("Quick Chat 2"), I18NCat::NONE));
962
ChoiceWithValueDisplay *qc3 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat2, n->T("Quick Chat 3"), I18NCat::NONE));
963
ChoiceWithValueDisplay *qc4 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat3, n->T("Quick Chat 4"), I18NCat::NONE));
964
ChoiceWithValueDisplay *qc5 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat4, n->T("Quick Chat 5"), I18NCat::NONE));
965
#endif
966
967
#if (!defined(MOBILE_DEVICE) && !defined(USING_QT_UI)) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)
968
qc1->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });
969
qc2->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });
970
qc3->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });
971
qc4->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });
972
qc5->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });
973
#endif
974
975
#if defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)
976
if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) {
977
qc1->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat0);
978
qc2->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat1);
979
qc3->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat2);
980
qc4->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat3);
981
qc5->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat4);
982
}
983
#endif
984
985
auto di = GetI18NCategory(I18NCat::DIALOG);
986
987
networkingSettings->Add(new ItemHeader(n->T("Misc", "Misc (default = compatibility)")));
988
networkingSettings->Add(new PopupSliderChoice(&g_Config.iPortOffset, 0, 60000, 10000, n->T("Port offset", "Port offset (0 = PSP compatibility)"), 100, screenManager()));
989
networkingSettings->Add(new PopupSliderChoice(&g_Config.iMinTimeout, 0, 15000, 0, n->T("Minimum Timeout", "Minimum Timeout (override in ms, 0 = default)"), 50, screenManager()))->SetFormat(di->T("%d ms"));
990
networkingSettings->Add(new CheckBox(&g_Config.bForcedFirstConnect, n->T("Forced First Connect", "Forced First Connect (faster Connect)")));
991
}
992
993
void GameSettingsScreen::CreateToolsSettings(UI::ViewGroup *tools) {
994
using namespace UI;
995
996
auto sa = GetI18NCategory(I18NCat::SAVEDATA);
997
auto sy = GetI18NCategory(I18NCat::SYSTEM);
998
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
999
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
1000
auto ri = GetI18NCategory(I18NCat::REMOTEISO);
1001
1002
tools->Add(new ItemHeader(ms->T("Tools")));
1003
1004
const bool showRetroAchievements = true;
1005
if (showRetroAchievements) {
1006
auto retro = tools->Add(new Choice(sy->T("RetroAchievements")));
1007
retro->OnClick.Add([=](UI::EventParams &) -> UI::EventReturn {
1008
screenManager()->push(new RetroAchievementsSettingsScreen(gamePath_));
1009
return UI::EVENT_DONE;
1010
});
1011
retro->SetIcon(ImageID("I_RETROACHIEVEMENTS_LOGO"));
1012
}
1013
1014
// These were moved here so use the wrong translation objects, to avoid having to change all inis... This isn't a sustainable situation :P
1015
tools->Add(new Choice(sa->T("Savedata Manager")))->OnClick.Add([=](UI::EventParams &) {
1016
screenManager()->push(new SavedataScreen(gamePath_));
1017
return UI::EVENT_DONE;
1018
});
1019
tools->Add(new Choice(dev->T("System Information")))->OnClick.Add([=](UI::EventParams &) {
1020
screenManager()->push(new SystemInfoScreen(gamePath_));
1021
return UI::EVENT_DONE;
1022
});
1023
tools->Add(new Choice(sy->T("Developer Tools")))->OnClick.Add([=](UI::EventParams &) {
1024
screenManager()->push(new DeveloperToolsScreen(gamePath_));
1025
return UI::EVENT_DONE;
1026
});
1027
tools->Add(new Choice(ri->T("Remote disc streaming")))->OnClick.Add([=](UI::EventParams &) {
1028
screenManager()->push(new RemoteISOScreen(gamePath_));
1029
return UI::EVENT_DONE;
1030
});
1031
}
1032
1033
void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) {
1034
using namespace UI;
1035
1036
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1037
auto di = GetI18NCategory(I18NCat::DIALOG);
1038
auto vr = GetI18NCategory(I18NCat::VR);
1039
auto th = GetI18NCategory(I18NCat::THEMES);
1040
auto psps = GetI18NCategory(I18NCat::PSPSETTINGS); // TODO: Should move more into this section.
1041
1042
systemSettings->Add(new ItemHeader(sy->T("UI")));
1043
1044
auto langCodeToName = [](std::string_view value) -> std::string {
1045
auto &mapping = g_Config.GetLangValuesMapping();
1046
auto iter = mapping.find(value);
1047
if (iter != mapping.end()) {
1048
return iter->second.first;
1049
}
1050
return std::string(value);
1051
};
1052
1053
systemSettings->Add(new ChoiceWithValueDisplay(&g_Config.sLanguageIni, sy->T("Language"), langCodeToName))->OnClick.Add([&](UI::EventParams &e) {
1054
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1055
auto langScreen = new NewLanguageScreen(sy->T("Language"));
1056
langScreen->OnChoice.Add([&](UI::EventParams &e) {
1057
screenManager()->RecreateAllViews();
1058
System_Notify(SystemNotification::UI);
1059
return UI::EVENT_DONE;
1060
});
1061
if (e.v)
1062
langScreen->SetPopupOrigin(e.v);
1063
screenManager()->push(langScreen);
1064
return UI::EVENT_DONE;
1065
});
1066
1067
#if PPSSPP_PLATFORM(IOS)
1068
static const char *indicator[] = {
1069
"Swipe once to switch app (indicator auto-hides)",
1070
"Swipe twice to switch app (indicator stays visible)"
1071
};
1072
PopupMultiChoice *switchMode = systemSettings->Add(new PopupMultiChoice(&g_Config.iAppSwitchMode, sy->T("App switching mode"), indicator, 0, ARRAY_SIZE(indicator), I18NCat::SYSTEM, screenManager()));
1073
switchMode->OnChoice.Add([](EventParams &e) {
1074
System_Notify(SystemNotification::APP_SWITCH_MODE_CHANGED);
1075
return UI::EVENT_DONE;
1076
});
1077
#endif
1078
1079
systemSettings->Add(new CheckBox(&g_Config.bUISound, sy->T("UI Sound")));
1080
const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png";
1081
const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg";
1082
if (File::Exists(bgPng) || File::Exists(bgJpg)) {
1083
backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Clear UI background")));
1084
} else if (System_GetPropertyBool(SYSPROP_HAS_IMAGE_BROWSER)) {
1085
backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Set UI background...")));
1086
} else {
1087
backgroundChoice_ = nullptr;
1088
}
1089
if (backgroundChoice_ != nullptr) {
1090
backgroundChoice_->OnClick.Handle(this, &GameSettingsScreen::OnChangeBackground);
1091
}
1092
1093
systemSettings->Add(new CheckBox(&g_Config.bTransparentBackground, sy->T("Transparent UI background")));
1094
1095
static const char *backgroundAnimations[] = { "No animation", "Floating symbols", "Recent games", "Waves", "Moving background" };
1096
systemSettings->Add(new PopupMultiChoice(&g_Config.iBackgroundAnimation, sy->T("UI background animation"), backgroundAnimations, 0, ARRAY_SIZE(backgroundAnimations), I18NCat::SYSTEM, screenManager()));
1097
1098
PopupMultiChoiceDynamic *theme = systemSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sThemeName, sy->T("Theme"), GetThemeInfoNames(), I18NCat::THEMES, screenManager()));
1099
theme->OnChoice.Add([=](EventParams &e) {
1100
UpdateTheme(screenManager()->getUIContext());
1101
return UI::EVENT_CONTINUE;
1102
});
1103
1104
Draw::DrawContext *draw = screenManager()->getDrawContext();
1105
1106
if (!draw->GetBugs().Has(Draw::Bugs::RASPBERRY_SHADER_COMP_HANG)) {
1107
// We use shaders without tint capability on hardware with this driver bug.
1108
PopupSliderChoiceFloat *tint = new PopupSliderChoiceFloat(&g_Config.fUITint, 0.0f, 1.0f, 0.0f, sy->T("Color Tint"), 0.01f, screenManager());
1109
tint->SetHasDropShadow(false);
1110
tint->SetLiveUpdate(true);
1111
systemSettings->Add(tint);
1112
PopupSliderChoiceFloat *saturation = new PopupSliderChoiceFloat(&g_Config.fUISaturation, 0.0f, 2.0f, 1.0f, sy->T("Color Saturation"), 0.01f, screenManager());
1113
saturation->SetHasDropShadow(false);
1114
saturation->SetLiveUpdate(true);
1115
systemSettings->Add(saturation);
1116
}
1117
1118
systemSettings->Add(new ItemHeader(sy->T("PSP Memory Stick")));
1119
1120
if (System_GetPropertyBool(SYSPROP_HAS_OPEN_DIRECTORY)) {
1121
systemSettings->Add(new Choice(sy->T("Show Memory Stick folder")))->OnClick.Add([](UI::EventParams &p) {
1122
System_ShowFileInFolder(g_Config.memStickDirectory);
1123
return UI::EVENT_DONE;
1124
});
1125
}
1126
1127
#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
1128
bool showItHere = true;
1129
#if PPSSPP_PLATFORM(IOS_APP_STORE)
1130
if (g_Config.memStickDirectory == DarwinFileSystemServices::defaultMemoryStickPath()) {
1131
// We still keep a way to access it on the developer tools screen.
1132
showItHere = false;
1133
}
1134
#endif
1135
if (showItHere) {
1136
systemSettings->Add(new Choice(sy->T("Set Memory Stick folder")))->OnClick.Add(
1137
[=](UI::EventParams &) {
1138
SetMemStickDirDarwin(GetRequesterToken());
1139
return UI::EVENT_DONE;
1140
});
1141
}
1142
#endif
1143
1144
#if PPSSPP_PLATFORM(ANDROID)
1145
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_VR) {
1146
memstickDisplay_ = g_Config.memStickDirectory.ToVisualString();
1147
auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&memstickDisplay_, sy->T("Memory Stick folder", "Memory Stick folder"), I18NCat::NONE));
1148
memstickPath->SetEnabled(!PSP_IsInited());
1149
memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnShowMemstickScreen);
1150
1151
// Display USB path for convenience.
1152
std::string usbPath;
1153
if (PathToVisualUsbPath(g_Config.memStickDirectory, usbPath)) {
1154
if (usbPath.empty()) {
1155
// Probably it's just the root. So let's add PSP to make it clear.
1156
usbPath = "/PSP";
1157
}
1158
systemSettings->Add(new InfoItem(sy->T("USB"), usbPath))->SetChoiceStyle(true);
1159
}
1160
}
1161
#elif defined(_WIN32)
1162
#if PPSSPP_PLATFORM(UWP)
1163
memstickDisplay_ = g_Config.memStickDirectory.ToVisualString();
1164
auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&memstickDisplay_, sy->T("Memory Stick folder", "Memory Stick folder"), I18NCat::NONE));
1165
memstickPath->SetEnabled(!PSP_IsInited());
1166
memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnShowMemstickScreen);
1167
#else
1168
SavePathInMyDocumentChoice = systemSettings->Add(new CheckBox(&installed_, sy->T("Save path in My Documents", "Save path in My Documents")));
1169
SavePathInMyDocumentChoice->SetEnabled(!PSP_IsInited());
1170
SavePathInMyDocumentChoice->OnClick.Handle(this, &GameSettingsScreen::OnSavePathMydoc);
1171
SavePathInOtherChoice = systemSettings->Add(new CheckBox(&otherinstalled_, sy->T("Save path in installed.txt", "Save path in installed.txt")));
1172
SavePathInOtherChoice->SetEnabled(false);
1173
SavePathInOtherChoice->OnClick.Handle(this, &GameSettingsScreen::OnSavePathOther);
1174
const bool myDocsExists = W32Util::UserDocumentsPath().size() != 0;
1175
1176
const Path &PPSSPPpath = File::GetExeDirectory();
1177
const Path installedFile = PPSSPPpath / "installed.txt";
1178
installed_ = File::Exists(installedFile);
1179
otherinstalled_ = false;
1180
if (!installed_ && myDocsExists) {
1181
if (File::CreateEmptyFile(PPSSPPpath / "installedTEMP.txt")) {
1182
// Disable the setting whether cannot create & delete file
1183
if (!(File::Delete(PPSSPPpath / "installedTEMP.txt")))
1184
SavePathInMyDocumentChoice->SetEnabled(false);
1185
else
1186
SavePathInOtherChoice->SetEnabled(!PSP_IsInited());
1187
} else
1188
SavePathInMyDocumentChoice->SetEnabled(false);
1189
} else {
1190
if (installed_ && myDocsExists) {
1191
FILE *testInstalled = File::OpenCFile(installedFile, "rt");
1192
if (testInstalled) {
1193
char temp[2048];
1194
char *tempStr = fgets(temp, sizeof(temp), testInstalled);
1195
// Skip UTF-8 encoding bytes if there are any. There are 3 of them.
1196
if (tempStr && strncmp(tempStr, "\xEF\xBB\xBF", 3) == 0) {
1197
tempStr += 3;
1198
}
1199
SavePathInOtherChoice->SetEnabled(!PSP_IsInited());
1200
if (tempStr && strlen(tempStr) != 0 && strcmp(tempStr, "\n") != 0) {
1201
installed_ = false;
1202
otherinstalled_ = true;
1203
}
1204
fclose(testInstalled);
1205
}
1206
} else if (!myDocsExists) {
1207
SavePathInMyDocumentChoice->SetEnabled(false);
1208
}
1209
}
1210
#endif
1211
#endif
1212
systemSettings->Add(new CheckBox(&g_Config.bMemStickInserted, sy->T("Memory Stick inserted")));
1213
UI::PopupSliderChoice *sizeChoice = systemSettings->Add(new PopupSliderChoice(&g_Config.iMemStickSizeGB, 1, 32, 16, sy->T("Memory Stick size", "Memory Stick size"), screenManager(), "GB"));
1214
sizeChoice->SetFormat("%d GB");
1215
1216
systemSettings->Add(new ItemHeader(sy->T("Help the PPSSPP team")));
1217
if (!enableReportsSet_)
1218
enableReports_ = Reporting::IsEnabled();
1219
enableReportsSet_ = true;
1220
enableReportsCheckbox_ = new CheckBox(&enableReports_, sy->T("Enable Compatibility Server Reports"));
1221
enableReportsCheckbox_->SetEnabled(Reporting::IsSupported());
1222
systemSettings->Add(enableReportsCheckbox_);
1223
1224
systemSettings->Add(new ItemHeader(sy->T("Emulation")));
1225
1226
systemSettings->Add(new CheckBox(&g_Config.bFastMemory, sy->T("Fast Memory", "Fast Memory")))->OnClick.Handle(this, &GameSettingsScreen::OnJitAffectingSetting);
1227
systemSettings->Add(new CheckBox(&g_Config.bIgnoreBadMemAccess, sy->T("Ignore bad memory accesses")));
1228
1229
static const char *ioTimingMethods[] = { "Fast (lag on slow storage)", "Host (bugs, less lag)", "Simulate UMD delays", "Simulate UMD slow reading speed"};
1230
View *ioTimingMethod = systemSettings->Add(new PopupMultiChoice(&g_Config.iIOTimingMethod, sy->T("IO timing method"), ioTimingMethods, 0, ARRAY_SIZE(ioTimingMethods), I18NCat::SYSTEM, screenManager()));
1231
systemSettings->Add(new CheckBox(&g_Config.bForceLagSync, sy->T("Force real clock sync (slower, less lag)")))->SetDisabledPtr(&g_Config.bAutoFrameSkip);
1232
PopupSliderChoice *lockedMhz = systemSettings->Add(new PopupSliderChoice(&g_Config.iLockedCPUSpeed, 0, 1000, 0, sy->T("Change CPU Clock", "Change CPU Clock (unstable)"), screenManager(), sy->T("MHz, 0:default")));
1233
lockedMhz->OnChange.Add([&](UI::EventParams &) {
1234
enableReportsCheckbox_->SetEnabled(Reporting::IsSupported());
1235
return UI::EVENT_CONTINUE;
1236
});
1237
lockedMhz->SetZeroLabel(sy->T("Auto"));
1238
PopupSliderChoice *rewindInterval = systemSettings->Add(new PopupSliderChoice(&g_Config.iRewindSnapshotInterval, 0, 60, 0, sy->T("Rewind Snapshot Interval"), screenManager(), di->T("seconds, 0:off")));
1239
rewindInterval->SetFormat(di->T("%d seconds"));
1240
rewindInterval->SetZeroLabel(sy->T("Off"));
1241
1242
systemSettings->Add(new ItemHeader(sy->T("General")));
1243
1244
#if PPSSPP_PLATFORM(ANDROID)
1245
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {
1246
auto co = GetI18NCategory(I18NCat::CONTROLS);
1247
1248
static const char *screenRotation[] = { "Auto", "Landscape", "Portrait", "Landscape Reversed", "Portrait Reversed", "Landscape Auto" };
1249
PopupMultiChoice *rot = systemSettings->Add(new PopupMultiChoice(&g_Config.iScreenRotation, co->T("Screen Rotation"), screenRotation, 0, ARRAY_SIZE(screenRotation), I18NCat::CONTROLS, screenManager()));
1250
rot->OnChoice.Handle(this, &GameSettingsScreen::OnScreenRotation);
1251
1252
if (System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE)) {
1253
systemSettings->Add(new CheckBox(&g_Config.bSustainedPerformanceMode, sy->T("Sustained performance mode")))->OnClick.Handle(this, &GameSettingsScreen::OnSustainedPerformanceModeChange);
1254
}
1255
}
1256
#endif
1257
1258
systemSettings->Add(new Choice(sy->T("Restore Default Settings")))->OnClick.Handle(this, &GameSettingsScreen::OnRestoreDefaultSettings);
1259
systemSettings->Add(new CheckBox(&g_Config.bEnableStateUndo, sy->T("Savestate slot backups")));
1260
static const char *autoLoadSaveStateChoices[] = { "Off", "Oldest Save", "Newest Save", "Slot 1", "Slot 2", "Slot 3", "Slot 4", "Slot 5" };
1261
systemSettings->Add(new PopupMultiChoice(&g_Config.iAutoLoadSaveState, sy->T("Auto Load Savestate"), autoLoadSaveStateChoices, 0, ARRAY_SIZE(autoLoadSaveStateChoices), I18NCat::SYSTEM, screenManager()));
1262
if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD))
1263
systemSettings->Add(new CheckBox(&g_Config.bBypassOSKWithKeyboard, sy->T("Use system native keyboard")));
1264
1265
systemSettings->Add(new CheckBox(&g_Config.bCacheFullIsoInRam, sy->T("Cache ISO in RAM", "Cache full ISO in RAM")))->SetEnabled(!PSP_IsInited());
1266
systemSettings->Add(new CheckBox(&g_Config.bCheckForNewVersion, sy->T("VersionCheck", "Check for new versions of PPSSPP")));
1267
systemSettings->Add(new CheckBox(&g_Config.bScreenshotsAsPNG, sy->T("Screenshots as PNG")));
1268
// TODO: Make this setting available on Mac too.
1269
#if PPSSPP_PLATFORM(WINDOWS)
1270
systemSettings->Add(new CheckBox(&g_Config.bPauseOnLostFocus, sy->T("Pause when not focused")));
1271
#endif
1272
1273
systemSettings->Add(new ItemHeader(sy->T("Cheats", "Cheats")));
1274
CheckBox *enableCheats = systemSettings->Add(new CheckBox(&g_Config.bEnableCheats, sy->T("Enable Cheats")));
1275
enableCheats->OnClick.Add([&](UI::EventParams &) {
1276
enableReportsCheckbox_->SetEnabled(Reporting::IsSupported());
1277
return UI::EVENT_CONTINUE;
1278
});
1279
systemSettings->Add(new CheckBox(&g_Config.bEnablePlugins, sy->T("Enable plugins")));
1280
1281
systemSettings->Add(new ItemHeader(sy->T("PSP Settings")));
1282
1283
// The ordering here is simply mapping directly to PSP_SYSTEMPARAM_LANGUAGE_*.
1284
static const char *defaultLanguages[] = { "Auto", "Japanese", "English", "French", "Spanish", "German", "Italian", "Dutch", "Portuguese", "Russian", "Korean", "Chinese (traditional)", "Chinese (simplified)" };
1285
systemSettings->Add(new PopupMultiChoice(&g_Config.iLanguage, psps->T("Game language"), defaultLanguages, -1, ARRAY_SIZE(defaultLanguages), I18NCat::PSPSETTINGS, screenManager()));
1286
static const char *models[] = { "PSP-1000", "PSP-2000/3000" };
1287
systemSettings->Add(new PopupMultiChoice(&g_Config.iPSPModel, sy->T("PSP Model"), models, 0, ARRAY_SIZE(models), I18NCat::SYSTEM, screenManager()))->SetEnabled(!PSP_IsInited());
1288
systemSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sNickName, sy->T("Change Nickname"), "", 32, screenManager()));
1289
systemSettings->Add(new CheckBox(&g_Config.bDayLightSavings, sy->T("Day Light Saving")));
1290
static const char *dateFormat[] = { "YYYYMMDD", "MMDDYYYY", "DDMMYYYY" };
1291
systemSettings->Add(new PopupMultiChoice(&g_Config.iDateFormat, sy->T("Date Format"), dateFormat, 0, ARRAY_SIZE(dateFormat), I18NCat::SYSTEM, screenManager()));
1292
static const char *timeFormat[] = { "24HR", "12HR" };
1293
systemSettings->Add(new PopupMultiChoice(&g_Config.iTimeFormat, sy->T("Time Format"), timeFormat, 0, ARRAY_SIZE(timeFormat), I18NCat::SYSTEM, screenManager()));
1294
static const char *buttonPref[] = { "Use O to confirm", "Use X to confirm" };
1295
systemSettings->Add(new PopupMultiChoice(&g_Config.iButtonPreference, sy->T("Confirmation Button"), buttonPref, 0, ARRAY_SIZE(buttonPref), I18NCat::SYSTEM, screenManager()));
1296
1297
#if defined(_WIN32) || (defined(USING_QT_UI) && !defined(MOBILE_DEVICE))
1298
systemSettings->Add(new ItemHeader(sy->T("Recording")));
1299
systemSettings->Add(new CheckBox(&g_Config.bDumpFrames, sy->T("Record Display")));
1300
systemSettings->Add(new CheckBox(&g_Config.bUseFFV1, sy->T("Use Lossless Video Codec (FFV1)")));
1301
systemSettings->Add(new CheckBox(&g_Config.bDumpVideoOutput, sy->T("Use output buffer (with overlay) for recording")));
1302
systemSettings->Add(new CheckBox(&g_Config.bDumpAudio, sy->T("Record Audio")));
1303
systemSettings->Add(new CheckBox(&g_Config.bSaveLoadResetsAVdumping, sy->T("Reset Recording on Save/Load State")));
1304
#endif
1305
}
1306
1307
void GameSettingsScreen::CreateVRSettings(UI::ViewGroup *vrSettings) {
1308
using namespace UI;
1309
1310
auto vr = GetI18NCategory(I18NCat::VR);
1311
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
1312
1313
if (deviceType == DEVICE_TYPE_VR) {
1314
vrSettings->Add(new ItemHeader(vr->T("Virtual reality")));
1315
vrSettings->Add(new CheckBox(&g_Config.bEnableVR, vr->T("Virtual reality")));
1316
vrSettings->Add(new CheckBox(&g_Config.bEnable6DoF, vr->T("6DoF movement")));
1317
vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Stereoscopic vision (Experimental)")));
1318
vrSettings->Add(new CheckBox(&g_Config.bEnableImmersiveVR, vr->T("Enable immersive mode")));
1319
if (IsPassthroughSupported()) {
1320
vrSettings->Add(new CheckBox(&g_Config.bPassthrough, vr->T("Enable passthrough")));
1321
}
1322
vrSettings->Add(new CheckBox(&g_Config.bForce72Hz, vr->T("Force 72Hz update")));
1323
}
1324
1325
vrSettings->Add(new ItemHeader(vr->T("VR camera")));
1326
if (deviceType == DEVICE_TYPE_VR) {
1327
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fCanvasDistance, 1.0f, 15.0f, 12.0f, vr->T("Distance to 2D menus and scenes"), 1.0f, screenManager(), ""));
1328
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fCanvas3DDistance, 1.0f, 15.0f, 3.0f, vr->T("Distance to 3D scenes when VR disabled"), 1.0f, screenManager(), ""));
1329
}
1330
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fFieldOfViewPercentage, 100.0f, 200.0f, 100.0f, vr->T("Field of view scale"), 10.0f, screenManager(), vr->T("% of native FoV")));
1331
vrSettings->Add(new CheckBox(&g_Config.bRescaleHUD, vr->T("Heads-up display detection")));
1332
PopupSliderChoiceFloat* vrHudScale = vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fHeadUpDisplayScale, 0.0f, 1.5f, 0.3f, vr->T("Heads-up display scale"), 0.1f, screenManager(), ""));
1333
vrHudScale->SetEnabledPtr(&g_Config.bRescaleHUD);
1334
vrSettings->Add(new CheckBox(&g_Config.bManualForceVR, vr->T("Manual switching between flat screen and VR using SCREEN key")));
1335
}
1336
1337
UI::EventReturn GameSettingsScreen::OnAutoFrameskip(UI::EventParams &e) {
1338
g_Config.UpdateAfterSettingAutoFrameSkip();
1339
return UI::EVENT_DONE;
1340
}
1341
1342
UI::EventReturn GameSettingsScreen::OnScreenRotation(UI::EventParams &e) {
1343
INFO_LOG(Log::System, "New display rotation: %d", g_Config.iScreenRotation);
1344
INFO_LOG(Log::System, "Sending rotate");
1345
System_Notify(SystemNotification::ROTATE_UPDATED);
1346
INFO_LOG(Log::System, "Got back from rotate");
1347
return UI::EVENT_DONE;
1348
}
1349
1350
UI::EventReturn GameSettingsScreen::OnAdhocGuides(UI::EventParams &e) {
1351
auto n = GetI18NCategory(I18NCat::NETWORKING);
1352
std::string url(n->T("MultiplayerHowToURL", "https://github.com/hrydgard/ppsspp/wiki/How-to-play-multiplayer-games-with-PPSSPP"));
1353
System_LaunchUrl(LaunchUrlType::BROWSER_URL, url.c_str());
1354
return UI::EVENT_DONE;
1355
}
1356
1357
UI::EventReturn GameSettingsScreen::OnImmersiveModeChange(UI::EventParams &e) {
1358
System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE);
1359
if (g_Config.iAndroidHwScale != 0) {
1360
System_RecreateActivity();
1361
}
1362
return UI::EVENT_DONE;
1363
}
1364
1365
UI::EventReturn GameSettingsScreen::OnSustainedPerformanceModeChange(UI::EventParams &e) {
1366
System_Notify(SystemNotification::SUSTAINED_PERF_CHANGE);
1367
return UI::EVENT_DONE;
1368
}
1369
1370
UI::EventReturn GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) {
1371
System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);
1372
return UI::EVENT_DONE;
1373
}
1374
1375
UI::EventReturn GameSettingsScreen::OnShowMemstickScreen(UI::EventParams &e) {
1376
screenManager()->push(new MemStickScreen(false));
1377
return UI::EVENT_DONE;
1378
}
1379
1380
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
1381
1382
UI::EventReturn GameSettingsScreen::OnSavePathMydoc(UI::EventParams &e) {
1383
const Path &PPSSPPpath = File::GetExeDirectory();
1384
const Path installedFile = PPSSPPpath / "installed.txt";
1385
installed_ = File::Exists(installedFile);
1386
if (otherinstalled_) {
1387
File::Delete(PPSSPPpath / "installed.txt");
1388
File::CreateEmptyFile(PPSSPPpath / "installed.txt");
1389
otherinstalled_ = false;
1390
const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/";
1391
g_Config.memStickDirectory = Path(myDocsPath);
1392
} else if (installed_) {
1393
File::Delete(PPSSPPpath / "installed.txt");
1394
installed_ = false;
1395
g_Config.memStickDirectory = PPSSPPpath / "memstick";
1396
} else {
1397
FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb");
1398
if (f) {
1399
fclose(f);
1400
}
1401
1402
const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/";
1403
g_Config.memStickDirectory = Path(myDocsPath);
1404
installed_ = true;
1405
}
1406
return UI::EVENT_DONE;
1407
}
1408
1409
UI::EventReturn GameSettingsScreen::OnSavePathOther(UI::EventParams &e) {
1410
const Path &PPSSPPpath = File::GetExeDirectory();
1411
if (otherinstalled_) {
1412
auto di = GetI18NCategory(I18NCat::DIALOG);
1413
std::string initialPath = g_Config.memStickDirectory.ToCString();
1414
std::string folder = W32Util::BrowseForFolder(MainWindow::GetHWND(), di->T("Choose PPSSPP save folder"), initialPath);
1415
if (folder.size()) {
1416
g_Config.memStickDirectory = Path(folder);
1417
FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb");
1418
if (f) {
1419
std::string utfstring("\xEF\xBB\xBF");
1420
utfstring.append(folder);
1421
fwrite(utfstring.c_str(), 1, utfstring.length(), f);
1422
fclose(f);
1423
}
1424
installed_ = false;
1425
}
1426
else
1427
otherinstalled_ = false;
1428
}
1429
else {
1430
File::Delete(PPSSPPpath / "installed.txt");
1431
SavePathInMyDocumentChoice->SetEnabled(true);
1432
otherinstalled_ = false;
1433
installed_ = false;
1434
g_Config.memStickDirectory = PPSSPPpath / "memstick";
1435
}
1436
return UI::EVENT_DONE;
1437
}
1438
1439
#endif
1440
1441
UI::EventReturn GameSettingsScreen::OnChangeBackground(UI::EventParams &e) {
1442
const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png";
1443
const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg";
1444
1445
if (File::Exists(bgPng) || File::Exists(bgJpg)) {
1446
File::Delete(bgPng);
1447
File::Delete(bgJpg);
1448
UIBackgroundShutdown();
1449
RecreateViews();
1450
} else {
1451
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1452
System_BrowseForImage(GetRequesterToken(), sy->T("Set UI background..."), [=](const std::string &value, int) {
1453
if (!value.empty()) {
1454
Path path(value);
1455
1456
// Check the file format. Don't rely on the file extension here due to scoped storage URLs.
1457
FILE *f = File::OpenCFile(path, "rb");
1458
uint8_t buffer[8];
1459
ImageFileType type = ImageFileType::UNKNOWN;
1460
if (f != nullptr && 8 == fread(buffer, 1, ARRAY_SIZE(buffer), f)) {
1461
type = DetectImageFileType(buffer, ARRAY_SIZE(buffer));
1462
}
1463
1464
std::string filename;
1465
switch (type) {
1466
case ImageFileType::JPEG:
1467
filename = "background.jpg";
1468
break;
1469
case ImageFileType::PNG:
1470
filename = "background.png";
1471
break;
1472
default:
1473
break;
1474
}
1475
1476
if (!filename.empty()) {
1477
Path dest = GetSysDirectory(DIRECTORY_SYSTEM) / filename;
1478
File::Copy(Path(value), dest);
1479
} else {
1480
g_OSD.Show(OSDType::MESSAGE_ERROR, sy->T("Only JPG and PNG images are supported"), path.GetFilename(), 5.0);
1481
}
1482
}
1483
// It will init again automatically. We can't init outside a frame on Vulkan.
1484
UIBackgroundShutdown();
1485
RecreateViews();
1486
});
1487
}
1488
1489
// Change to a browse or clear button.
1490
return UI::EVENT_DONE;
1491
}
1492
1493
UI::EventReturn GameSettingsScreen::OnFullscreenChange(UI::EventParams &e) {
1494
g_Config.iForceFullScreen = -1;
1495
System_ToggleFullscreenState(g_Config.UseFullScreen() ? "1" : "0");
1496
return UI::EVENT_DONE;
1497
}
1498
1499
UI::EventReturn GameSettingsScreen::OnFullscreenMultiChange(UI::EventParams &e) {
1500
System_ToggleFullscreenState(g_Config.UseFullScreen() ? "1" : "0");
1501
return UI::EVENT_DONE;
1502
}
1503
1504
UI::EventReturn GameSettingsScreen::OnResolutionChange(UI::EventParams &e) {
1505
if (g_Config.iAndroidHwScale == 1) {
1506
System_RecreateActivity();
1507
}
1508
Reporting::UpdateConfig();
1509
System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);
1510
return UI::EVENT_DONE;
1511
}
1512
1513
void GameSettingsScreen::onFinish(DialogResult result) {
1514
Reporting::Enable(enableReports_, "report.ppsspp.org");
1515
Reporting::UpdateConfig();
1516
if (!g_Config.Save("GameSettingsScreen::onFinish")) {
1517
System_Toast("Failed to save settings!\nCheck permissions, or try to restart the device.");
1518
}
1519
1520
if (editThenRestore_) {
1521
// In case we didn't have the title yet before, try again.
1522
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO);
1523
g_Config.changeGameSpecific(gameID_, info->GetTitle());
1524
g_Config.unloadGameConfig();
1525
}
1526
1527
System_Notify(SystemNotification::UI);
1528
1529
KeyMap::UpdateNativeMenuKeys();
1530
1531
// Wipe some caches after potentially changing settings.
1532
// Let's not send resize messages here, handled elsewhere.
1533
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
1534
}
1535
1536
void GameSettingsScreen::dialogFinished(const Screen *dialog, DialogResult result) {
1537
if (result == DialogResult::DR_OK) {
1538
g_Config.iFpsLimit1 = iAlternateSpeedPercent1_ < 0 ? -1 : (iAlternateSpeedPercent1_ * 60) / 100;
1539
g_Config.iFpsLimit2 = iAlternateSpeedPercent2_ < 0 ? -1 : (iAlternateSpeedPercent2_ * 60) / 100;
1540
g_Config.iAnalogFpsLimit = (iAlternateSpeedPercentAnalog_ * 60) / 100;
1541
1542
RecreateViews();
1543
}
1544
1545
// Show/hide the Analog Alternative Speed as appropriate - need to recreate views if this changed.
1546
bool mapped = KeyMap::InputMappingsFromPspButton(VIRTKEY_SPEED_ANALOG, nullptr, true);
1547
if (mapped != analogSpeedMapped_) {
1548
analogSpeedMapped_ = mapped;
1549
RecreateViews();
1550
}
1551
}
1552
1553
void GameSettingsScreen::CallbackMemstickFolder(bool yes) {
1554
if (yes) {
1555
Path memstickDirFile = g_Config.internalDataDirectory / "memstick_dir.txt";
1556
std::string testWriteFile = pendingMemstickFolder_ + "/.write_verify_file";
1557
1558
// Already, create away.
1559
if (!File::Exists(Path(pendingMemstickFolder_))) {
1560
File::CreateFullPath(Path(pendingMemstickFolder_));
1561
}
1562
if (!File::WriteDataToFile(true, "1", 1, Path(testWriteFile))) {
1563
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1564
settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr);
1565
return;
1566
}
1567
File::Delete(Path(testWriteFile));
1568
1569
if (!File::WriteDataToFile(true, pendingMemstickFolder_.c_str(), pendingMemstickFolder_.size(), memstickDirFile)) {
1570
WARN_LOG(Log::System, "Failed to write memstick folder to '%s'", memstickDirFile.c_str());
1571
} else {
1572
// Save so the settings, at least, are transferred.
1573
g_Config.memStickDirectory = Path(pendingMemstickFolder_);
1574
g_Config.Save("MemstickPathChanged");
1575
}
1576
screenManager()->RecreateAllViews();
1577
}
1578
}
1579
1580
void TriggerRestart(const char *why, bool editThenRestore, const Path &gamePath) {
1581
// Extra save here to make sure the choice really gets saved even if there are shutdown bugs in
1582
// the GPU backend code.
1583
g_Config.Save(why);
1584
std::string param = "--gamesettings";
1585
if (editThenRestore) {
1586
// We won't pass the gameID, so don't resume back into settings.
1587
param.clear();
1588
} else if (!gamePath.empty()) {
1589
param += " \"" + ReplaceAll(ReplaceAll(gamePath.ToString(), "\\", "\\\\"), "\"", "\\\"") + "\"";
1590
}
1591
// Make sure the new instance is considered the first.
1592
ShutdownInstanceCounter();
1593
System_RestartApp(param);
1594
}
1595
1596
UI::EventReturn GameSettingsScreen::OnRenderingBackend(UI::EventParams &e) {
1597
// It only makes sense to show the restart prompt if the backend was actually changed.
1598
if (g_Config.iGPUBackend != (int)GetGPUBackend()) {
1599
auto di = GetI18NCategory(I18NCat::DIALOG);
1600
screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) {
1601
if (yes) {
1602
TriggerRestart("GameSettingsScreen::RenderingBackendYes", editThenRestore_, gamePath_);
1603
} else {
1604
g_Config.iGPUBackend = (int)GetGPUBackend();
1605
}
1606
}));
1607
}
1608
return UI::EVENT_DONE;
1609
}
1610
1611
UI::EventReturn GameSettingsScreen::OnRenderingDevice(UI::EventParams &e) {
1612
// It only makes sense to show the restart prompt if the device was actually changed.
1613
std::string *deviceNameSetting = GPUDeviceNameSetting();
1614
if (deviceNameSetting && *deviceNameSetting != GetGPUBackendDevice()) {
1615
auto di = GetI18NCategory(I18NCat::DIALOG);
1616
screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) {
1617
// If the user ends up deciding not to restart, set the config back to the current backend
1618
// so it doesn't get switched by accident.
1619
if (yes) {
1620
TriggerRestart("GameSettingsScreen::RenderingDeviceYes", editThenRestore_, gamePath_);
1621
} else {
1622
std::string *deviceNameSetting = GPUDeviceNameSetting();
1623
if (deviceNameSetting)
1624
*deviceNameSetting = GetGPUBackendDevice();
1625
// Needed to redraw the setting.
1626
RecreateViews();
1627
}
1628
}));
1629
}
1630
return UI::EVENT_DONE;
1631
}
1632
1633
UI::EventReturn GameSettingsScreen::OnInflightFramesChoice(UI::EventParams &e) {
1634
if (g_Config.iInflightFrames != prevInflightFrames_) {
1635
auto di = GetI18NCategory(I18NCat::DIALOG);
1636
screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) {
1637
if (yes) {
1638
TriggerRestart("GameSettingsScreen::InflightFramesYes", editThenRestore_, gamePath_);
1639
} else {
1640
g_Config.iInflightFrames = prevInflightFrames_;
1641
}
1642
}));
1643
}
1644
return UI::EVENT_DONE;
1645
}
1646
1647
UI::EventReturn GameSettingsScreen::OnCameraDeviceChange(UI::EventParams& e) {
1648
Camera::onCameraDeviceChange();
1649
return UI::EVENT_DONE;
1650
}
1651
1652
UI::EventReturn GameSettingsScreen::OnMicDeviceChange(UI::EventParams& e) {
1653
Microphone::onMicDeviceChange();
1654
return UI::EVENT_DONE;
1655
}
1656
1657
UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) {
1658
auto a = GetI18NCategory(I18NCat::AUDIO);
1659
if (g_Config.sAudioDevice == a->T("Auto")) {
1660
g_Config.sAudioDevice.clear();
1661
}
1662
System_Notify(SystemNotification::AUDIO_RESET_DEVICE);
1663
return UI::EVENT_DONE;
1664
}
1665
1666
UI::EventReturn GameSettingsScreen::OnChangeQuickChat0(UI::EventParams &e) {
1667
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
1668
auto n = GetI18NCategory(I18NCat::NETWORKING);
1669
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 1"), g_Config.sQuickChat0, false, [](const std::string &value, int) {
1670
g_Config.sQuickChat0 = value;
1671
});
1672
#endif
1673
return UI::EVENT_DONE;
1674
}
1675
1676
UI::EventReturn GameSettingsScreen::OnChangeQuickChat1(UI::EventParams &e) {
1677
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
1678
auto n = GetI18NCategory(I18NCat::NETWORKING);
1679
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 2"), g_Config.sQuickChat1, false, [](const std::string &value, int) {
1680
g_Config.sQuickChat1 = value;
1681
});
1682
#endif
1683
return UI::EVENT_DONE;
1684
}
1685
1686
UI::EventReturn GameSettingsScreen::OnChangeQuickChat2(UI::EventParams &e) {
1687
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
1688
auto n = GetI18NCategory(I18NCat::NETWORKING);
1689
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 3"), g_Config.sQuickChat2, false, [](const std::string &value, int) {
1690
g_Config.sQuickChat2 = value;
1691
});
1692
#endif
1693
return UI::EVENT_DONE;
1694
}
1695
1696
UI::EventReturn GameSettingsScreen::OnChangeQuickChat3(UI::EventParams &e) {
1697
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
1698
auto n = GetI18NCategory(I18NCat::NETWORKING);
1699
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 4"), g_Config.sQuickChat3, false, [](const std::string &value, int) {
1700
g_Config.sQuickChat3 = value;
1701
});
1702
#endif
1703
return UI::EVENT_DONE;
1704
}
1705
1706
UI::EventReturn GameSettingsScreen::OnChangeQuickChat4(UI::EventParams &e) {
1707
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
1708
auto n = GetI18NCategory(I18NCat::NETWORKING);
1709
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 5"), g_Config.sQuickChat4, false, [](const std::string &value, int) {
1710
g_Config.sQuickChat4 = value;
1711
});
1712
#endif
1713
return UI::EVENT_DONE;
1714
}
1715
1716
UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) {
1717
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
1718
auto n = GetI18NCategory(I18NCat::NETWORKING);
1719
System_InputBoxGetString(GetRequesterToken(), n->T("Enter a new PSP nickname"), g_Config.sNickName, false, [](const std::string &value, int) {
1720
g_Config.sNickName = StripSpaces(value);
1721
});
1722
#endif
1723
return UI::EVENT_DONE;
1724
}
1725
1726
UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParams &e) {
1727
auto n = GetI18NCategory(I18NCat::NETWORKING);
1728
1729
screenManager()->push(new HostnameSelectScreen(&g_Config.proAdhocServer, n->T("proAdhocServer Address:")));
1730
1731
return UI::EVENT_DONE;
1732
}
1733
1734
UI::EventReturn GameSettingsScreen::OnTextureShader(UI::EventParams &e) {
1735
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
1736
auto shaderScreen = new TextureShaderScreen(gr->T("Texture Shader"));
1737
shaderScreen->OnChoice.Handle(this, &GameSettingsScreen::OnTextureShaderChange);
1738
if (e.v)
1739
shaderScreen->SetPopupOrigin(e.v);
1740
screenManager()->push(shaderScreen);
1741
return UI::EVENT_DONE;
1742
}
1743
1744
UI::EventReturn GameSettingsScreen::OnTextureShaderChange(UI::EventParams &e) {
1745
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
1746
RecreateViews(); // Update setting name
1747
g_Config.bTexHardwareScaling = g_Config.sTextureShaderName != "Off";
1748
return UI::EVENT_DONE;
1749
}
1750
1751
UI::EventReturn GameSettingsScreen::OnControlMapping(UI::EventParams &e) {
1752
screenManager()->push(new ControlMappingScreen(gamePath_));
1753
return UI::EVENT_DONE;
1754
}
1755
1756
UI::EventReturn GameSettingsScreen::OnCalibrateAnalogs(UI::EventParams &e) {
1757
screenManager()->push(new AnalogSetupScreen(gamePath_));
1758
return UI::EVENT_DONE;
1759
}
1760
1761
UI::EventReturn GameSettingsScreen::OnTouchControlLayout(UI::EventParams &e) {
1762
screenManager()->push(new TouchControlLayoutScreen(gamePath_));
1763
return UI::EVENT_DONE;
1764
}
1765
1766
UI::EventReturn GameSettingsScreen::OnTiltCustomize(UI::EventParams &e) {
1767
screenManager()->push(new TiltAnalogSettingsScreen(gamePath_));
1768
return UI::EVENT_DONE;
1769
};
1770
1771
void DeveloperToolsScreen::CreateViews() {
1772
using namespace UI;
1773
root_ = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT));
1774
ScrollView *settingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));
1775
settingsScroll->SetTag("DevToolsSettings");
1776
root_->Add(settingsScroll);
1777
1778
auto di = GetI18NCategory(I18NCat::DIALOG);
1779
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
1780
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
1781
auto a = GetI18NCategory(I18NCat::AUDIO);
1782
auto sy = GetI18NCategory(I18NCat::SYSTEM);
1783
auto ps = GetI18NCategory(I18NCat::POSTSHADERS);
1784
auto ms = GetI18NCategory(I18NCat::MEMSTICK);
1785
auto si = GetI18NCategory(I18NCat::SYSINFO);
1786
1787
AddStandardBack(root_);
1788
1789
LinearLayout *list = settingsScroll->Add(new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
1790
list->SetSpacing(0);
1791
1792
list->Add(new ItemHeader(dev->T("Texture Replacement")));
1793
list->Add(new CheckBox(&g_Config.bSaveNewTextures, dev->T("Save new textures")));
1794
list->Add(new CheckBox(&g_Config.bReplaceTextures, dev->T("Replace textures")));
1795
1796
Choice *createTextureIni = list->Add(new Choice(dev->T("Create/Open textures.ini file for current game")));
1797
createTextureIni->OnClick.Handle(this, &DeveloperToolsScreen::OnOpenTexturesIniFile);
1798
createTextureIni->SetEnabledFunc([&] {
1799
if (!PSP_IsInited())
1800
return false;
1801
1802
// Disable the choice to Open/Create if the textures.ini file already exists, and we can't open it due to platform support limitations.
1803
if (!System_GetPropertyBool(SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR)) {
1804
if (hasTexturesIni_ == HasIni::MAYBE)
1805
hasTexturesIni_ = TextureReplacer::IniExists(g_paramSFO.GetDiscID()) ? HasIni::YES : HasIni::NO;
1806
return hasTexturesIni_ != HasIni::YES;
1807
}
1808
return true;
1809
});
1810
1811
list->Add(new ItemHeader(sy->T("General")));
1812
1813
bool canUseJit = System_GetPropertyBool(SYSPROP_CAN_JIT);
1814
// iOS can now use JIT on all modes, apparently.
1815
// The bool may come in handy for future non-jit platforms though (UWP XB1?)
1816
// In iOS App Store builds, we disable the JIT.
1817
1818
static const char *cpuCores[] = {"Interpreter", "Dynarec/JIT (recommended)", "IR Interpreter", "JIT using IR"};
1819
PopupMultiChoice *core = list->Add(new PopupMultiChoice(&g_Config.iCpuCore, sy->T("CPU Core"), cpuCores, 0, ARRAY_SIZE(cpuCores), I18NCat::SYSTEM, screenManager()));
1820
core->OnChoice.Handle(this, &DeveloperToolsScreen::OnJitAffectingSetting);
1821
core->OnChoice.Add([](UI::EventParams &) {
1822
g_Config.NotifyUpdatedCpuCore();
1823
return UI::EVENT_DONE;
1824
});
1825
if (!canUseJit) {
1826
core->HideChoice(1);
1827
core->HideChoice(3);
1828
}
1829
// TODO: Enable "JIT using IR" on more architectures.
1830
#if !PPSSPP_ARCH(X86) && !PPSSPP_ARCH(AMD64) && !PPSSPP_ARCH(ARM64)
1831
core->HideChoice(3);
1832
#endif
1833
1834
list->Add(new Choice(dev->T("JIT debug tools")))->OnClick.Handle(this, &DeveloperToolsScreen::OnJitDebugTools);
1835
list->Add(new CheckBox(&g_Config.bShowDeveloperMenu, dev->T("Show Developer Menu")));
1836
list->Add(new CheckBox(&g_Config.bDumpDecryptedEboot, dev->T("Dump Decrypted Eboot", "Dump Decrypted EBOOT.BIN (If Encrypted) When Booting Game")));
1837
1838
#if !PPSSPP_PLATFORM(UWP)
1839
Choice *cpuTests = new Choice(dev->T("Run CPU Tests"));
1840
list->Add(cpuTests)->OnClick.Handle(this, &DeveloperToolsScreen::OnRunCPUTests);
1841
1842
cpuTests->SetEnabled(TestsAvailable());
1843
#endif
1844
1845
list->Add(new CheckBox(&g_Config.bUseNewAtrac, dev->T("Use experimental sceAtrac")));
1846
1847
AddOverlayList(list, screenManager());
1848
1849
list->Add(new CheckBox(&g_Config.bEnableLogging, dev->T("Enable Logging")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLoggingChanged);
1850
list->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLogConfig);
1851
list->Add(new CheckBox(&g_Config.bLogFrameDrops, dev->T("Log Dropped Frame Statistics")));
1852
if (GetGPUBackend() == GPUBackend::VULKAN) {
1853
list->Add(new CheckBox(&g_Config.bGpuLogProfiler, dev->T("GPU log profiler")));
1854
}
1855
1856
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {
1857
list->Add(new CheckBox(&g_Config.bRenderMultiThreading, dev->T("Multi-threaded rendering"), ""))->OnClick.Add([](UI::EventParams &e) {
1858
// TODO: Not translating yet. Will combine with other translations of settings that need restart.
1859
g_OSD.Show(OSDType::MESSAGE_WARNING, "Restart required");
1860
return UI::EVENT_DONE;
1861
});
1862
}
1863
1864
if (GetGPUBackend() == GPUBackend::VULKAN && SupportsCustomDriver()) {
1865
auto driverChoice = list->Add(new Choice(gr->T("Adreno Driver Manager")));
1866
driverChoice->OnClick.Add([=](UI::EventParams &e) {
1867
screenManager()->push(new DriverManagerScreen(gamePath_));
1868
return UI::EVENT_DONE;
1869
});
1870
}
1871
1872
// For now, we only implement GPU driver tests for Vulkan and OpenGL. This is simply
1873
// because the D3D drivers are generally solid enough to not need this type of investigation.
1874
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL) {
1875
list->Add(new Choice(dev->T("GPU Driver Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnGPUDriverTest);
1876
}
1877
list->Add(new CheckBox(&g_Config.bVendorBugChecksEnabled, dev->T("Enable driver bug workarounds")));
1878
list->Add(new Choice(dev->T("Framedump tests")))->OnClick.Handle(this, &DeveloperToolsScreen::OnFramedumpTest);
1879
list->Add(new Choice(dev->T("Touchscreen Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnTouchscreenTest);
1880
// list->Add(new Choice(dev->T("Memstick Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnMemstickTest);
1881
1882
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
1883
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
1884
CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger"));
1885
list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger);
1886
allowDebugger->SetEnabledPtr(&canAllowDebugger_);
1887
1888
list->Add(new CheckBox(&g_Config.bShowOnScreenMessages, dev->T("Show on-screen messages")));
1889
1890
list->Add(new Choice(dev->T("GPI/GPO switches/LEDs")))->OnClick.Add([=](UI::EventParams &e) {
1891
screenManager()->push(new GPIGPOScreen(dev->T("GPI/GPO switches/LEDs")));
1892
return UI::EVENT_DONE;
1893
});
1894
1895
#if PPSSPP_PLATFORM(ANDROID)
1896
static const char *framerateModes[] = { "Default", "Request 60Hz", "Force 60Hz" };
1897
PopupMultiChoice *framerateMode = list->Add(new PopupMultiChoice(&g_Config.iDisplayFramerateMode, gr->T("Framerate mode"), framerateModes, 0, ARRAY_SIZE(framerateModes), I18NCat::GRAPHICS, screenManager()));
1898
framerateMode->SetEnabledFunc([]() { return System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30; });
1899
framerateMode->OnChoice.Add([this](UI::EventParams &e) {
1900
System_Notify(SystemNotification::FORCE_RECREATE_ACTIVITY);
1901
return UI::EVENT_DONE;
1902
});
1903
#endif
1904
1905
#if PPSSPP_PLATFORM(IOS_APP_STORE)
1906
list->Add(new NoticeView(NoticeLevel::WARN, ms->T("Moving the memstick directory is NOT recommended on iOS"), ""));
1907
list->Add(new Choice(sy->T("Set Memory Stick folder")))->OnClick.Add(
1908
[=](UI::EventParams &) {
1909
SetMemStickDirDarwin(GetRequesterToken());
1910
return UI::EVENT_DONE;
1911
});
1912
#endif
1913
1914
static const char *ffModes[] = { "Render all frames", "", "Frame Skipping" };
1915
PopupMultiChoice *ffMode = list->Add(new PopupMultiChoice(&g_Config.iFastForwardMode, dev->T("Fast-forward mode"), ffModes, 0, ARRAY_SIZE(ffModes), I18NCat::GRAPHICS, screenManager()));
1916
ffMode->SetEnabledFunc([]() { return !g_Config.bVSync; });
1917
ffMode->HideChoice(1); // not used
1918
1919
auto displayRefreshRate = list->Add(new PopupSliderChoice(&g_Config.iDisplayRefreshRate, 60, 1000, 60, dev->T("Display refresh rate"), 1, screenManager()));
1920
displayRefreshRate->SetFormat(si->T("%d Hz"));
1921
1922
#if !PPSSPP_PLATFORM(ANDROID) && !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(SWITCH)
1923
list->Add(new ItemHeader(dev->T("MIPSTracer")));
1924
1925
MIPSTracerEnabled_ = mipsTracer.tracing_enabled;
1926
CheckBox *MIPSTracerEnabled = new CheckBox(&MIPSTracerEnabled_, dev->T("MIPSTracer enabled"));
1927
list->Add(MIPSTracerEnabled)->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerEnabled);
1928
MIPSTracerEnabled->SetEnabledFunc([]() {
1929
bool temp = g_Config.iCpuCore == static_cast<int>(CPUCore::IR_INTERPRETER) && PSP_IsInited();
1930
return temp && Core_IsStepping() && coreState != CORE_POWERDOWN;
1931
});
1932
1933
Choice *TraceDumpPath = list->Add(new Choice(dev->T("Select the file path for the trace")));
1934
TraceDumpPath->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerPathChanged);
1935
TraceDumpPath->SetEnabledFunc([]() {
1936
if (!PSP_IsInited())
1937
return false;
1938
return true;
1939
});
1940
1941
MIPSTracerPath_ = mipsTracer.get_logging_path();
1942
MIPSTracerPath = list->Add(new InfoItem(dev->T("Current log file"), MIPSTracerPath_));
1943
1944
PopupSliderChoice* storage_capacity = list->Add(
1945
new PopupSliderChoice(
1946
&mipsTracer.in_storage_capacity, 0x4'0000, 0x40'0000, 0x10'0000, dev->T("Storage capacity"), 0x10000, screenManager()
1947
)
1948
);
1949
storage_capacity->SetFormat("0x%x asm opcodes");
1950
storage_capacity->OnChange.Add([&](UI::EventParams &) {
1951
INFO_LOG(Log::JIT, "User changed the tracer's storage capacity to 0x%x", mipsTracer.in_storage_capacity);
1952
return UI::EVENT_CONTINUE;
1953
});
1954
1955
PopupSliderChoice* trace_max_size = list->Add(
1956
new PopupSliderChoice(
1957
&mipsTracer.in_max_trace_size, 0x1'0000, 0x40'0000, 0x10'0000, dev->T("Max allowed trace size"), 0x10000, screenManager()
1958
)
1959
);
1960
trace_max_size->SetFormat("%d basic blocks");
1961
trace_max_size->OnChange.Add([&](UI::EventParams &) {
1962
INFO_LOG(Log::JIT, "User changed the tracer's max trace size to %d", mipsTracer.in_max_trace_size);
1963
return UI::EVENT_CONTINUE;
1964
});
1965
1966
Button *FlushTrace = list->Add(new Button(dev->T("Flush the trace")));
1967
FlushTrace->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerFlushTrace);
1968
1969
Button *InvalidateJitCache = list->Add(new Button(dev->T("Clear the JIT cache")));
1970
InvalidateJitCache->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearJitCache);
1971
1972
Button *ClearMIPSTracer = list->Add(new Button(dev->T("Clear the MIPSTracer")));
1973
ClearMIPSTracer->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearTracer);
1974
#endif
1975
1976
Draw::DrawContext *draw = screenManager()->getDrawContext();
1977
1978
list->Add(new ItemHeader(dev->T("Ubershaders")));
1979
if (draw->GetShaderLanguageDesc().bitwiseOps && !draw->GetBugs().Has(Draw::Bugs::UNIFORM_INDEXING_BROKEN)) {
1980
// If the above if fails, the checkbox is redundant since it'll be force disabled anyway.
1981
list->Add(new CheckBox(&g_Config.bUberShaderVertex, dev->T("Vertex")));
1982
}
1983
#if !PPSSPP_PLATFORM(UWP)
1984
if (g_Config.iGPUBackend != (int)GPUBackend::OPENGL || gl_extensions.GLES3) {
1985
#else
1986
{
1987
#endif
1988
list->Add(new CheckBox(&g_Config.bUberShaderFragment, dev->T("Fragment")));
1989
}
1990
1991
// Experimental, allow some VR features without OpenXR
1992
if (GetGPUBackend() == GPUBackend::OPENGL) {
1993
auto vr = GetI18NCategory(I18NCat::VR);
1994
list->Add(new ItemHeader(vr->T("Virtual reality")));
1995
list->Add(new CheckBox(&g_Config.bForceVR, vr->T("VR camera")));
1996
}
1997
1998
// Experimental, will move to main graphics settings later.
1999
bool multiViewSupported = draw->GetDeviceCaps().multiViewSupported;
2000
2001
auto enableStereo = [=]() -> bool {
2002
return g_Config.bStereoRendering && multiViewSupported;
2003
};
2004
2005
if (multiViewSupported) {
2006
list->Add(new ItemHeader(gr->T("Stereo rendering")));
2007
list->Add(new CheckBox(&g_Config.bStereoRendering, gr->T("Stereo rendering")));
2008
std::vector<std::string> stereoShaderNames;
2009
2010
ChoiceWithValueDisplay *stereoShaderChoice = list->Add(new ChoiceWithValueDisplay(&g_Config.sStereoToMonoShader, gr->T("Stereo display shader"), &PostShaderTranslateName));
2011
stereoShaderChoice->SetEnabledFunc(enableStereo);
2012
stereoShaderChoice->OnClick.Add([=](EventParams &e) {
2013
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
2014
auto procScreen = new PostProcScreen(gr->T("Stereo display shader"), 0, true);
2015
if (e.v)
2016
procScreen->SetPopupOrigin(e.v);
2017
screenManager()->push(procScreen);
2018
return UI::EVENT_DONE;
2019
});
2020
const ShaderInfo *shaderInfo = GetPostShaderInfo(g_Config.sStereoToMonoShader);
2021
if (shaderInfo) {
2022
for (size_t i = 0; i < ARRAY_SIZE(shaderInfo->settings); ++i) {
2023
auto &setting = shaderInfo->settings[i];
2024
if (!setting.name.empty()) {
2025
std::string key = StringFromFormat("%sSettingCurrentValue%d", shaderInfo->section.c_str(), i + 1);
2026
bool keyExisted = g_Config.mPostShaderSetting.find(key) != g_Config.mPostShaderSetting.end();
2027
auto &value = g_Config.mPostShaderSetting[key];
2028
if (!keyExisted)
2029
value = setting.value;
2030
2031
PopupSliderChoiceFloat *settingValue = list->Add(new PopupSliderChoiceFloat(&value, setting.minValue, setting.maxValue, setting.value, ps->T(setting.name), setting.step, screenManager()));
2032
settingValue->SetEnabledFunc([=] {
2033
return !g_Config.bSkipBufferEffects && enableStereo();
2034
});
2035
}
2036
}
2037
}
2038
}
2039
2040
// Makes it easy to get savestates out of an iOS device. The file listing shown in MacOS doesn't allow
2041
// you to descend into directories.
2042
#if PPSSPP_PLATFORM(IOS)
2043
list->Add(new Choice(dev->T("Copy savestates to memstick root")))->OnClick.Handle(this, &DeveloperToolsScreen::OnCopyStatesToRoot);
2044
#endif
2045
2046
// Reconsider whenever recreating views.
2047
hasTexturesIni_ = HasIni::MAYBE;
2048
}
2049
2050
void DeveloperToolsScreen::onFinish(DialogResult result) {
2051
g_Config.Save("DeveloperToolsScreen::onFinish");
2052
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
2053
}
2054
2055
void GameSettingsScreen::CallbackRestoreDefaults(bool yes) {
2056
if (yes) {
2057
g_Config.RestoreDefaults(RestoreSettingsBits::SETTINGS);
2058
}
2059
System_Notify(SystemNotification::UI);
2060
}
2061
2062
UI::EventReturn GameSettingsScreen::OnRestoreDefaultSettings(UI::EventParams &e) {
2063
auto sy = GetI18NCategory(I18NCat::SYSTEM);
2064
if (g_Config.bGameSpecific) {
2065
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
2066
auto di = GetI18NCategory(I18NCat::DIALOG);
2067
screenManager()->push(
2068
new PromptScreen(gamePath_, dev->T("RestoreGameDefaultSettings", "Are you sure you want to restore the game-specific settings back to the ppsspp defaults?\n"), di->T("OK"), di->T("Cancel"),
2069
std::bind(&GameSettingsScreen::CallbackRestoreDefaults, this, std::placeholders::_1)));
2070
} else {
2071
std::string_view title = sy->T("Restore Default Settings");
2072
screenManager()->push(new RestoreSettingsScreen(title));
2073
}
2074
return UI::EVENT_DONE;
2075
}
2076
2077
UI::EventReturn DeveloperToolsScreen::OnLoggingChanged(UI::EventParams &e) {
2078
System_Notify(SystemNotification::TOGGLE_DEBUG_CONSOLE);
2079
return UI::EVENT_DONE;
2080
}
2081
2082
UI::EventReturn DeveloperToolsScreen::OnRunCPUTests(UI::EventParams &e) {
2083
#if !PPSSPP_PLATFORM(UWP)
2084
RunTests();
2085
#endif
2086
return UI::EVENT_DONE;
2087
}
2088
2089
UI::EventReturn DeveloperToolsScreen::OnOpenTexturesIniFile(UI::EventParams &e) {
2090
std::string gameID = g_paramSFO.GetDiscID();
2091
Path generatedFilename;
2092
2093
if (TextureReplacer::GenerateIni(gameID, generatedFilename)) {
2094
if (System_GetPropertyBool(SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR)) {
2095
File::OpenFileInEditor(generatedFilename);
2096
} else {
2097
// Can't do much here, let's send a "toast" so the user sees that something happened.
2098
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
2099
System_Toast((generatedFilename.ToVisualString() + ": " + dev->T_cstr("Texture ini file created")).c_str());
2100
}
2101
2102
hasTexturesIni_ = HasIni::YES;
2103
}
2104
return UI::EVENT_DONE;
2105
}
2106
2107
UI::EventReturn DeveloperToolsScreen::OnLogConfig(UI::EventParams &e) {
2108
screenManager()->push(new LogConfigScreen());
2109
return UI::EVENT_DONE;
2110
}
2111
2112
UI::EventReturn DeveloperToolsScreen::OnJitDebugTools(UI::EventParams &e) {
2113
screenManager()->push(new JitDebugScreen());
2114
return UI::EVENT_DONE;
2115
}
2116
2117
UI::EventReturn DeveloperToolsScreen::OnGPUDriverTest(UI::EventParams &e) {
2118
screenManager()->push(new GPUDriverTestScreen());
2119
return UI::EVENT_DONE;
2120
}
2121
2122
UI::EventReturn DeveloperToolsScreen::OnFramedumpTest(UI::EventParams &e) {
2123
screenManager()->push(new FrameDumpTestScreen());
2124
return UI::EVENT_DONE;
2125
}
2126
2127
UI::EventReturn DeveloperToolsScreen::OnTouchscreenTest(UI::EventParams &e) {
2128
screenManager()->push(new TouchTestScreen(gamePath_));
2129
return UI::EVENT_DONE;
2130
}
2131
2132
UI::EventReturn DeveloperToolsScreen::OnJitAffectingSetting(UI::EventParams &e) {
2133
System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);
2134
return UI::EVENT_DONE;
2135
}
2136
2137
UI::EventReturn DeveloperToolsScreen::OnCopyStatesToRoot(UI::EventParams &e) {
2138
Path savestate_dir = GetSysDirectory(DIRECTORY_SAVESTATE);
2139
Path root_dir = GetSysDirectory(DIRECTORY_MEMSTICK_ROOT);
2140
2141
std::vector<File::FileInfo> files;
2142
GetFilesInDir(savestate_dir, &files, nullptr, 0);
2143
2144
for (const File::FileInfo &file : files) {
2145
Path src = file.fullName;
2146
Path dst = root_dir / file.name;
2147
INFO_LOG(Log::System, "Copying file '%s' to '%s'", src.c_str(), dst.c_str());
2148
File::Copy(src, dst);
2149
}
2150
2151
return UI::EVENT_DONE;
2152
}
2153
2154
UI::EventReturn DeveloperToolsScreen::OnRemoteDebugger(UI::EventParams &e) {
2155
if (allowDebugger_) {
2156
StartWebServer(WebServerFlags::DEBUGGER);
2157
} else {
2158
StopWebServer(WebServerFlags::DEBUGGER);
2159
}
2160
// Persist the setting. Maybe should separate?
2161
g_Config.bRemoteDebuggerOnStartup = allowDebugger_;
2162
return UI::EVENT_DONE;
2163
}
2164
2165
UI::EventReturn DeveloperToolsScreen::OnMIPSTracerEnabled(UI::EventParams &e) {
2166
if (MIPSTracerEnabled_) {
2167
u32 capacity = mipsTracer.in_storage_capacity;
2168
u32 trace_size = mipsTracer.in_max_trace_size;
2169
2170
mipsTracer.initialize(capacity, trace_size);
2171
mipsTracer.start_tracing();
2172
}
2173
else {
2174
mipsTracer.stop_tracing();
2175
}
2176
return UI::EVENT_DONE;
2177
}
2178
2179
UI::EventReturn DeveloperToolsScreen::OnMIPSTracerPathChanged(UI::EventParams &e) {
2180
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
2181
System_BrowseForFile(GetRequesterToken(), dev->T("Select the log file"), BrowseFileType::ANY,
2182
[this](const std::string &value, int) {
2183
mipsTracer.set_logging_path(value);
2184
MIPSTracerPath_ = value;
2185
MIPSTracerPath->SetRightText(MIPSTracerPath_);
2186
});
2187
return UI::EVENT_DONE;
2188
}
2189
2190
UI::EventReturn DeveloperToolsScreen::OnMIPSTracerFlushTrace(UI::EventParams &e) {
2191
mipsTracer.flush_to_file();
2192
// The error logs are emitted inside the tracer
2193
2194
return UI::EVENT_DONE;
2195
}
2196
2197
UI::EventReturn DeveloperToolsScreen::OnMIPSTracerClearJitCache(UI::EventParams &e) {
2198
INFO_LOG(Log::JIT, "Clearing the jit cache...");
2199
System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);
2200
return UI::EVENT_DONE;
2201
}
2202
2203
UI::EventReturn DeveloperToolsScreen::OnMIPSTracerClearTracer(UI::EventParams &e) {
2204
INFO_LOG(Log::JIT, "Clearing the MIPSTracer...");
2205
mipsTracer.clear();
2206
return UI::EVENT_DONE;
2207
}
2208
2209
void DeveloperToolsScreen::update() {
2210
UIDialogScreenWithBackground::update();
2211
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
2212
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
2213
}
2214
2215
static bool RunMemstickTest(std::string *error) {
2216
Path testRoot = GetSysDirectory(PSPDirectories::DIRECTORY_CACHE) / "test";
2217
2218
*error = "N/A";
2219
2220
File::CreateDir(testRoot);
2221
if (!File::Exists(testRoot)) {
2222
return false;
2223
}
2224
2225
Path testFilePath = testRoot / "temp.txt";
2226
File::CreateEmptyFile(testFilePath);
2227
2228
// Attempt to delete the test root. This should fail since it still contains files.
2229
File::DeleteDir(testRoot);
2230
if (!File::Exists(testRoot)) {
2231
*error = "testroot was deleted with a file in it!";
2232
return false;
2233
}
2234
2235
File::Delete(testFilePath);
2236
if (File::Exists(testFilePath)) {
2237
*error = "testfile wasn't deleted";
2238
return false;
2239
}
2240
2241
File::DeleteDir(testRoot);
2242
if (File::Exists(testRoot)) {
2243
*error = "testroot wasn't deleted, even when empty";
2244
return false;
2245
}
2246
2247
*error = "passed";
2248
return true;
2249
}
2250
2251
UI::EventReturn DeveloperToolsScreen::OnMemstickTest(UI::EventParams &e) {
2252
std::string error;
2253
if (RunMemstickTest(&error)) {
2254
g_OSD.Show(OSDType::MESSAGE_SUCCESS, "Memstick test passed", error, 6.0f);
2255
} else {
2256
g_OSD.Show(OSDType::MESSAGE_ERROR, "Memstick test failed", error, 6.0f);
2257
}
2258
2259
return UI::EVENT_DONE;
2260
}
2261
2262
void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) {
2263
using namespace UI;
2264
auto sy = GetI18NCategory(I18NCat::SYSTEM);
2265
auto di = GetI18NCategory(I18NCat::DIALOG);
2266
auto n = GetI18NCategory(I18NCat::NETWORKING);
2267
2268
LinearLayout *valueRow = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, Margins(0, 0, 0, 10)));
2269
2270
addrView_ = new TextEdit(*value_, n->T("Hostname"), "");
2271
addrView_->SetTextAlign(FLAG_DYNAMIC_ASCII);
2272
valueRow->Add(addrView_);
2273
parent->Add(valueRow);
2274
2275
LinearLayout *buttonsRow1 = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
2276
LinearLayout *buttonsRow2 = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
2277
parent->Add(buttonsRow1);
2278
parent->Add(buttonsRow2);
2279
2280
buttonsRow1->Add(new Spacer(new LinearLayoutParams(1.0, G_LEFT)));
2281
for (char c = '0'; c <= '9'; ++c) {
2282
char label[] = { c, '\0' };
2283
auto button = buttonsRow1->Add(new Button(label));
2284
button->OnClick.Handle(this, &HostnameSelectScreen::OnNumberClick);
2285
button->SetTag(label);
2286
}
2287
buttonsRow1->Add(new Button("."))->OnClick.Handle(this, &HostnameSelectScreen::OnPointClick);
2288
buttonsRow1->Add(new Spacer(new LinearLayoutParams(1.0, G_RIGHT)));
2289
2290
buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, G_LEFT)));
2291
if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) {
2292
buttonsRow2->Add(new Button(di->T("Edit")))->OnClick.Handle(this, &HostnameSelectScreen::OnEditClick);
2293
}
2294
buttonsRow2->Add(new Button(di->T("Delete")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteClick);
2295
buttonsRow2->Add(new Button(di->T("Delete all")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteAllClick);
2296
buttonsRow2->Add(new Button(di->T("Toggle List")))->OnClick.Handle(this, &HostnameSelectScreen::OnShowIPListClick);
2297
buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, G_RIGHT)));
2298
2299
std::vector<std::string> listIP = {"socom.cc", "psp.gameplayer.club", "myneighborsushicat.com", "localhost"}; // TODO: Add some saved recent history too?
2300
net::GetIPList(listIP);
2301
ipRows_ = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
2302
ScrollView* scrollView = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
2303
LinearLayout* innerView = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
2304
if (listIP.size() > 0) {
2305
for (const auto& label : listIP) {
2306
// Filter out IP prefixed with "127." and "169.254." also "0." since they can be rendundant or unusable
2307
if (label.find("127.") != 0 && label.find("169.254.") != 0 && label.find("0.") != 0) {
2308
auto button = innerView->Add(new Button(label, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
2309
button->OnClick.Handle(this, &HostnameSelectScreen::OnIPClick);
2310
button->SetTag(label);
2311
}
2312
}
2313
}
2314
scrollView->Add(innerView);
2315
ipRows_->Add(scrollView);
2316
ipRows_->SetVisibility(V_GONE);
2317
parent->Add(ipRows_);
2318
listIP.clear(); listIP.shrink_to_fit();
2319
2320
progressView_ = parent->Add(new TextView(n->T("Validating address..."), ALIGN_HCENTER, false, new LinearLayoutParams(Margins(0, 5, 0, 0))));
2321
progressView_->SetVisibility(UI::V_GONE);
2322
}
2323
2324
void HostnameSelectScreen::SendEditKey(InputKeyCode keyCode, int flags) {
2325
auto oldView = UI::GetFocusedView();
2326
UI::SetFocusedView(addrView_);
2327
KeyInput fakeKey{ DEVICE_ID_KEYBOARD, keyCode, KEY_DOWN | flags };
2328
addrView_->Key(fakeKey);
2329
UI::SetFocusedView(oldView);
2330
}
2331
2332
UI::EventReturn HostnameSelectScreen::OnNumberClick(UI::EventParams &e) {
2333
std::string text = e.v ? e.v->Tag() : "";
2334
if (text.length() == 1 && text[0] >= '0' && text[0] <= '9') {
2335
SendEditKey((InputKeyCode)text[0], KEY_CHAR); // ASCII for digits match keycodes.
2336
}
2337
return UI::EVENT_DONE;
2338
}
2339
2340
UI::EventReturn HostnameSelectScreen::OnPointClick(UI::EventParams &e) {
2341
SendEditKey((InputKeyCode)'.', KEY_CHAR);
2342
return UI::EVENT_DONE;
2343
}
2344
2345
UI::EventReturn HostnameSelectScreen::OnDeleteClick(UI::EventParams &e) {
2346
SendEditKey(NKCODE_DEL);
2347
return UI::EVENT_DONE;
2348
}
2349
2350
UI::EventReturn HostnameSelectScreen::OnDeleteAllClick(UI::EventParams &e) {
2351
addrView_->SetText("");
2352
return UI::EVENT_DONE;
2353
}
2354
2355
UI::EventReturn HostnameSelectScreen::OnEditClick(UI::EventParams& e) {
2356
auto n = GetI18NCategory(I18NCat::NETWORKING);
2357
System_InputBoxGetString(GetRequesterToken(), n->T("proAdhocServer Address:"), addrView_->GetText(), false, [this](const std::string& value, int) {
2358
addrView_->SetText(value);
2359
});
2360
return UI::EVENT_DONE;
2361
}
2362
2363
UI::EventReturn HostnameSelectScreen::OnShowIPListClick(UI::EventParams& e) {
2364
if (ipRows_->GetVisibility() == UI::V_GONE) {
2365
ipRows_->SetVisibility(UI::V_VISIBLE);
2366
}
2367
else {
2368
ipRows_->SetVisibility(UI::V_GONE);
2369
}
2370
return UI::EVENT_DONE;
2371
}
2372
2373
UI::EventReturn HostnameSelectScreen::OnIPClick(UI::EventParams& e) {
2374
std::string text = e.v ? e.v->Tag() : "";
2375
if (text.length() > 0) {
2376
addrView_->SetText(text);
2377
// Copy the IP to clipboard for the host to easily share their IP through chatting apps.
2378
System_CopyStringToClipboard(text);
2379
}
2380
return UI::EVENT_DONE;
2381
}
2382
2383
void HostnameSelectScreen::ResolverThread() {
2384
std::unique_lock<std::mutex> guard(resolverLock_);
2385
2386
while (resolverState_ != ResolverState::QUIT) {
2387
resolverCond_.wait(guard);
2388
2389
if (resolverState_ == ResolverState::QUEUED) {
2390
resolverState_ = ResolverState::PROGRESS;
2391
2392
addrinfo *resolved = nullptr;
2393
std::string err;
2394
toResolveResult_ = net::DNSResolve(toResolve_, "80", &resolved, err);
2395
if (resolved)
2396
net::DNSResolveFree(resolved);
2397
2398
resolverState_ = ResolverState::READY;
2399
}
2400
}
2401
}
2402
2403
bool HostnameSelectScreen::CanComplete(DialogResult result) {
2404
auto n = GetI18NCategory(I18NCat::NETWORKING);
2405
2406
if (result != DR_OK)
2407
return true;
2408
2409
std::string value = addrView_->GetText();
2410
if (lastResolved_ == value) {
2411
return true;
2412
}
2413
2414
// Currently running.
2415
if (resolverState_ == ResolverState::PROGRESS)
2416
return false;
2417
2418
std::lock_guard<std::mutex> guard(resolverLock_);
2419
switch (resolverState_) {
2420
case ResolverState::PROGRESS:
2421
case ResolverState::QUIT:
2422
return false;
2423
2424
case ResolverState::QUEUED:
2425
case ResolverState::WAITING:
2426
break;
2427
2428
case ResolverState::READY:
2429
if (toResolve_ == value) {
2430
// Reset the state, nothing there now.
2431
resolverState_ = ResolverState::WAITING;
2432
toResolve_.clear();
2433
lastResolved_ = value;
2434
lastResolvedResult_ = toResolveResult_;
2435
2436
if (lastResolvedResult_) {
2437
progressView_->SetVisibility(UI::V_GONE);
2438
} else {
2439
progressView_->SetText(n->T("Invalid IP or hostname"));
2440
progressView_->SetTextColor(0xFF3030FF);
2441
progressView_->SetVisibility(UI::V_VISIBLE);
2442
}
2443
return true;
2444
}
2445
2446
// Throw away that last result, it was for a different value.
2447
break;
2448
}
2449
2450
resolverState_ = ResolverState::QUEUED;
2451
toResolve_ = value;
2452
resolverCond_.notify_one();
2453
2454
progressView_->SetText(n->T("Validating address..."));
2455
progressView_->SetTextColor(0xFFFFFFFF);
2456
progressView_->SetVisibility(UI::V_VISIBLE);
2457
2458
return false;
2459
}
2460
2461
void HostnameSelectScreen::OnCompleted(DialogResult result) {
2462
if (result == DR_OK)
2463
*value_ = StripSpaces(addrView_->GetText());
2464
}
2465
2466
void GestureMappingScreen::CreateViews() {
2467
using namespace UI;
2468
2469
auto di = GetI18NCategory(I18NCat::DIALOG);
2470
auto co = GetI18NCategory(I18NCat::CONTROLS);
2471
auto mc = GetI18NCategory(I18NCat::MAPPABLECONTROLS);
2472
2473
root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
2474
AddStandardBack(root_);
2475
TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, 200, new AnchorLayoutParams(10, 0, 10, 0, false));
2476
root_->Add(tabHolder);
2477
ScrollView *rightPanel = new ScrollView(ORIENT_VERTICAL);
2478
tabHolder->AddTab(co->T("Gesture"), rightPanel);
2479
LinearLayout *vert = rightPanel->Add(new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT)));
2480
vert->SetSpacing(0);
2481
2482
static const char *gestureButton[ARRAY_SIZE(GestureKey::keyList)+1];
2483
gestureButton[0] = "None";
2484
for (int i = 1; i < ARRAY_SIZE(gestureButton); ++i) {
2485
gestureButton[i] = KeyMap::GetPspButtonNameCharPointer(GestureKey::keyList[i-1]);
2486
}
2487
2488
vert->Add(new CheckBox(&g_Config.bGestureControlEnabled, co->T("Enable gesture control")));
2489
2490
vert->Add(new ItemHeader(co->T("Swipe")));
2491
vert->Add(new PopupMultiChoice(&g_Config.iSwipeUp, mc->T("Swipe Up"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
2492
vert->Add(new PopupMultiChoice(&g_Config.iSwipeDown, mc->T("Swipe Down"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
2493
vert->Add(new PopupMultiChoice(&g_Config.iSwipeLeft, mc->T("Swipe Left"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
2494
vert->Add(new PopupMultiChoice(&g_Config.iSwipeRight, mc->T("Swipe Right"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
2495
vert->Add(new PopupSliderChoiceFloat(&g_Config.fSwipeSensitivity, 0.01f, 1.0f, 1.0f, co->T("Swipe sensitivity"), 0.01f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
2496
vert->Add(new PopupSliderChoiceFloat(&g_Config.fSwipeSmoothing, 0.0f, 0.95f, 0.3f, co->T("Swipe smoothing"), 0.05f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
2497
2498
vert->Add(new ItemHeader(co->T("Double tap")));
2499
vert->Add(new PopupMultiChoice(&g_Config.iDoubleTapGesture, mc->T("Double tap button"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
2500
2501
vert->Add(new ItemHeader(co->T("Analog Stick")));
2502
vert->Add(new CheckBox(&g_Config.bAnalogGesture, co->T("Enable analog stick gesture")));
2503
vert->Add(new PopupSliderChoiceFloat(&g_Config.fAnalogGestureSensibility, 0.01f, 5.0f, 1.0f, co->T("Sensitivity"), 0.01f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bAnalogGesture);
2504
}
2505
2506
RestoreSettingsScreen::RestoreSettingsScreen(std::string_view title)
2507
: PopupScreen(title, "OK", "Cancel") {}
2508
2509
void RestoreSettingsScreen::CreatePopupContents(UI::ViewGroup *parent) {
2510
using namespace UI;
2511
// Carefully re-use various translations.
2512
auto ga = GetI18NCategory(I18NCat::GAME);
2513
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
2514
auto mm = GetI18NCategory(I18NCat::MAINMENU);
2515
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
2516
2517
std::string_view text = dev->T(
2518
"RestoreDefaultSettings",
2519
"Restore these settings back to their defaults?\nYou can't undo this.\nPlease restart PPSSPP after restoring settings.");
2520
2521
TextView *textView = parent->Add(new TextView(text, FLAG_WRAP_TEXT, false));
2522
textView->SetPadding(10.0f);
2523
2524
parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::SETTINGS, ga->T("Game Settings")));
2525
parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::CONTROLS, ms->T("Controls")));
2526
parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::RECENT, mm->T("Recent")));
2527
}
2528
2529
void RestoreSettingsScreen::OnCompleted(DialogResult result) {
2530
if (result == DialogResult::DR_OK) {
2531
g_Config.RestoreDefaults((RestoreSettingsBits)restoreFlags_);
2532
}
2533
}
2534
2535