CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/UI/DisplayLayoutScreen.cpp
Views: 1401
// Copyright (c) 2013- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <algorithm>1819#include "Common/System/Display.h"20#include "Common/System/System.h"21#include "Common/Render/TextureAtlas.h"22#include "Common/Render/DrawBuffer.h"23#include "Common/UI/Context.h"24#include "Common/UI/View.h"25#include "Common/UI/UIScreen.h"26#include "Common/Math/math_util.h"27#include "Common/System/Display.h"28#include "Common/System/NativeApp.h"29#include "Common/VR/PPSSPPVR.h"30#include "Common/StringUtils.h"3132#include "Common/Data/Color/RGBAUtil.h"33#include "Common/Data/Text/I18n.h"34#include "UI/DisplayLayoutScreen.h"35#include "Core/Config.h"36#include "Core/ConfigValues.h"37#include "Core/System.h"38#include "GPU/Common/FramebufferManagerCommon.h"39#include "GPU/Common/PresentationCommon.h"4041static const int leftColumnWidth = 200;42static const float orgRatio = 1.764706f; // 480.0 / 272.04344enum Mode {45MODE_INACTIVE = 0,46MODE_MOVE = 1,47MODE_RESIZE = 2,48};4950static Bounds FRectToBounds(FRect rc) {51Bounds b;52b.x = rc.x * g_display.dpi_scale_x;53b.y = rc.y * g_display.dpi_scale_y;54b.w = rc.w * g_display.dpi_scale_x;55b.h = rc.h * g_display.dpi_scale_y;56return b;57}5859class DisplayLayoutBackground : public UI::View {60public:61DisplayLayoutBackground(UI::ChoiceStrip *mode, UI::LayoutParams *layoutParams) : UI::View(layoutParams), mode_(mode) {}6263bool Touch(const TouchInput &touch) override {64int mode = mode_ ? mode_->GetSelection() : 0;6566if ((touch.flags & TOUCH_MOVE) != 0 && dragging_) {67float relativeTouchX = touch.x - startX_;68float relativeTouchY = touch.y - startY_;6970switch (mode) {71case MODE_MOVE:72g_Config.fDisplayOffsetX = clamp_value(startDisplayOffsetX_ + relativeTouchX / bounds_.w, 0.0f, 1.0f);73g_Config.fDisplayOffsetY = clamp_value(startDisplayOffsetY_ + relativeTouchY / bounds_.h, 0.0f, 1.0f);74break;75case MODE_RESIZE:76{77// Resize. Vertical = scaling; Up should be bigger so let's negate in that direction78float diffYProp = -relativeTouchY * 0.007f;79g_Config.fDisplayScale = clamp_value(startScale_ * powf(2.0f, diffYProp), 0.2f, 2.0f);80break;81}82}83}8485if ((touch.flags & TOUCH_DOWN) != 0 && !dragging_) {86// Check that we're in the central 80% of the screen.87// If outside, it may be a drag from displaying the back button on phones88// where you have to drag from the side, etc.89if (touch.x >= bounds_.w * 0.1f && touch.x <= bounds_.w * 0.9f &&90touch.y >= bounds_.h * 0.1f && touch.y <= bounds_.h * 0.9f) {91dragging_ = true;92startX_ = touch.x;93startY_ = touch.y;94startDisplayOffsetX_ = g_Config.fDisplayOffsetX;95startDisplayOffsetY_ = g_Config.fDisplayOffsetY;96startScale_ = g_Config.fDisplayScale;97}98}99100if ((touch.flags & TOUCH_UP) != 0 && dragging_) {101dragging_ = false;102}103104return true;105}106107private:108UI::ChoiceStrip *mode_;109bool dragging_ = false;110111// Touch down state for drag to resize etc112float startX_ = 0.0f;113float startY_ = 0.0f;114float startScale_ = -1.0f;115float startDisplayOffsetX_ = -1.0f;116float startDisplayOffsetY_ = -1.0f;117};118119DisplayLayoutScreen::DisplayLayoutScreen(const Path &filename) : UIDialogScreenWithGameBackground(filename) {}120121void DisplayLayoutScreen::DrawBackground(UIContext &dc) {122if (PSP_IsInited() && !g_Config.bSkipBufferEffects) {123// We normally rely on the PSP screen showing through.124} else {125// But if it's not present (we're not in game, or skip buffer effects is used),126// we have to draw a substitute ourselves.127UIContext &dc = *screenManager()->getUIContext();128129// TODO: Clean this up a bit, this GetScreenFrame/CenterDisplay combo is too common.130FRect screenFrame = GetScreenFrame(g_display.pixel_xres, g_display.pixel_yres);131FRect rc;132CalculateDisplayOutputRect(&rc, 480.0f, 272.0f, screenFrame, g_Config.iInternalScreenRotation);133134dc.Flush();135ImageID bg = ImageID("I_PSP_DISPLAY");136dc.Draw()->DrawImageStretch(bg, dc.GetBounds(), 0x7F000000);137dc.Draw()->DrawImageStretch(bg, FRectToBounds(rc), 0x7FFFFFFF);138}139}140141void DisplayLayoutScreen::onFinish(DialogResult reason) {142g_Config.Save("DisplayLayoutScreen::onFinish");143}144145void DisplayLayoutScreen::dialogFinished(const Screen *dialog, DialogResult result) {146RecreateViews();147}148149UI::EventReturn DisplayLayoutScreen::OnPostProcShaderChange(UI::EventParams &e) {150// Remove the virtual "Off" entry. TODO: Get rid of it generally.151g_Config.vPostShaderNames.erase(std::remove(g_Config.vPostShaderNames.begin(), g_Config.vPostShaderNames.end(), "Off"), g_Config.vPostShaderNames.end());152FixPostShaderOrder(&g_Config.vPostShaderNames);153154System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);155System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED); // To deal with shaders that can change render resolution like upscaling.156System_PostUIMessage(UIMessage::POSTSHADER_UPDATED);157158if (gpu) {159gpu->NotifyConfigChanged();160}161return UI::EVENT_DONE;162}163164static std::string PostShaderTranslateName(std::string_view value) {165if (value == "Off") {166auto gr = GetI18NCategory(I18NCat::GRAPHICS);167// Off is a legacy fake item (gonna migrate off it later).168return std::string(gr->T("Add postprocessing shader"));169}170171const ShaderInfo *info = GetPostShaderInfo(value);172if (info) {173auto ps = GetI18NCategory(I18NCat::POSTSHADERS);174return std::string(ps->T(value, info->name));175} else {176return std::string(value);177}178}179180void DisplayLayoutScreen::sendMessage(UIMessage message, const char *value) {181UIDialogScreenWithGameBackground::sendMessage(message, value);182if (message == UIMessage::POSTSHADER_UPDATED) {183g_Config.bShaderChainRequires60FPS = PostShaderChainRequires60FPS(GetFullPostShadersChain(g_Config.vPostShaderNames));184RecreateViews();185}186}187188void DisplayLayoutScreen::CreateViews() {189const Bounds &bounds = screenManager()->getUIContext()->GetBounds();190191using namespace UI;192193auto di = GetI18NCategory(I18NCat::DIALOG);194auto gr = GetI18NCategory(I18NCat::GRAPHICS);195auto co = GetI18NCategory(I18NCat::CONTROLS);196auto ps = GetI18NCategory(I18NCat::POSTSHADERS);197198root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));199200bool vertical = bounds.h > bounds.w;201202// Make it so that a touch can only affect one view. Makes manipulating the background through the buttons203// impossible.204root_->SetExclusiveTouch(true);205206LinearLayout *leftColumn;207if (!vertical) {208ScrollView *leftScrollView = new ScrollView(ORIENT_VERTICAL, new AnchorLayoutParams(420.0f, FILL_PARENT, 0.f, 0.f, NONE, 0.f, false));209leftColumn = new LinearLayout(ORIENT_VERTICAL);210leftColumn->padding.SetAll(8.0f);211leftScrollView->Add(leftColumn);212leftScrollView->SetClickableBackground(true);213root_->Add(leftScrollView);214}215216ScrollView *rightScrollView = new ScrollView(ORIENT_VERTICAL, new AnchorLayoutParams(300.0f, FILL_PARENT, NONE, 0.f, 0.f, 0.f, false));217LinearLayout *rightColumn = new LinearLayout(ORIENT_VERTICAL);218rightColumn->padding.SetAll(8.0f);219rightScrollView->Add(rightColumn);220rightScrollView->SetClickableBackground(true);221root_->Add(rightScrollView);222223LinearLayout *bottomControls;224if (vertical) {225bottomControls = new LinearLayout(ORIENT_HORIZONTAL);226rightColumn->Add(bottomControls);227leftColumn = rightColumn;228} else {229bottomControls = new LinearLayout(ORIENT_HORIZONTAL, new AnchorLayoutParams(NONE, NONE, NONE, 10.0f, false));230root_->Add(bottomControls);231}232233// Set backgrounds for readability234Drawable backgroundWithAlpha(GetBackgroundColorWithAlpha(*screenManager()->getUIContext()));235leftColumn->SetBG(backgroundWithAlpha);236rightColumn->SetBG(backgroundWithAlpha);237238if (!IsVREnabled()) {239auto stretch = new CheckBox(&g_Config.bDisplayStretch, gr->T("Stretch"));240stretch->SetDisabledPtr(&g_Config.bDisplayIntegerScale);241rightColumn->Add(stretch);242243PopupSliderChoiceFloat *aspectRatio = new PopupSliderChoiceFloat(&g_Config.fDisplayAspectRatio, 0.1f, 2.0f, 1.0f, gr->T("Aspect Ratio"), screenManager());244rightColumn->Add(aspectRatio);245aspectRatio->SetEnabledFunc([]() {246return !g_Config.bDisplayStretch && !g_Config.bDisplayIntegerScale;247});248aspectRatio->SetHasDropShadow(false);249aspectRatio->SetLiveUpdate(true);250251rightColumn->Add(new CheckBox(&g_Config.bDisplayIntegerScale, gr->T("Integer scale factor")));252253#if PPSSPP_PLATFORM(ANDROID)254// Hide insets option if no insets, or OS too old.255if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 28 &&256(System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT) != 0.0f ||257System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP) != 0.0f ||258System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT) != 0.0f ||259System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM) != 0.0f)) {260rightColumn->Add(new CheckBox(&g_Config.bIgnoreScreenInsets, gr->T("Ignore camera notch when centering")));261}262#endif263264mode_ = new ChoiceStrip(ORIENT_HORIZONTAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT));265mode_->AddChoice(di->T("Inactive"));266mode_->AddChoice(di->T("Move"));267mode_->AddChoice(di->T("Resize"));268mode_->SetSelection(0, false);269bottomControls->Add(mode_);270271static const char *displayRotation[] = { "Landscape", "Portrait", "Landscape Reversed", "Portrait Reversed" };272auto rotation = new PopupMultiChoice(&g_Config.iInternalScreenRotation, gr->T("Rotation"), displayRotation, 1, ARRAY_SIZE(displayRotation), I18NCat::CONTROLS, screenManager());273rotation->SetEnabledFunc([] {274return !g_Config.bSkipBufferEffects || g_Config.bSoftwareRendering;275});276rotation->SetHideTitle(true);277rightColumn->Add(rotation);278279Choice *center = new Choice(di->T("Reset"));280center->OnClick.Add([&](UI::EventParams &) {281g_Config.fDisplayAspectRatio = 1.0f;282g_Config.fDisplayScale = 1.0f;283g_Config.fDisplayOffsetX = 0.5f;284g_Config.fDisplayOffsetY = 0.5f;285return UI::EVENT_DONE;286});287rightColumn->Add(center);288289rightColumn->Add(new Spacer(12.0f));290}291292Choice *back = new Choice(di->T("Back"), "", false);293back->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);294rightColumn->Add(back);295296if (vertical) {297leftColumn->Add(new Spacer(24.0f));298}299300if (!IsVREnabled()) {301static const char *bufFilters[] = { "Linear", "Nearest", };302leftColumn->Add(new PopupMultiChoice(&g_Config.iDisplayFilter, gr->T("Screen Scaling Filter"), bufFilters, 1, ARRAY_SIZE(bufFilters), I18NCat::GRAPHICS, screenManager()));303}304305Draw::DrawContext *draw = screenManager()->getDrawContext();306307bool multiViewSupported = draw->GetDeviceCaps().multiViewSupported;308309auto enableStereo = [=]() -> bool {310return g_Config.bStereoRendering && multiViewSupported;311};312313leftColumn->Add(new ItemHeader(gr->T("Postprocessing shaders")));314315std::set<std::string> alreadyAddedShader;316// If there's a single post shader and we're just entering the dialog,317// auto-open the settings.318if (settingsVisible_.empty() && g_Config.vPostShaderNames.size() == 1) {319settingsVisible_.push_back(true);320} else if (settingsVisible_.size() < g_Config.vPostShaderNames.size()) {321settingsVisible_.resize(g_Config.vPostShaderNames.size());322}323324static ContextMenuItem postShaderContextMenu[] = {325{ "Move Up", "I_ARROW_UP" },326{ "Move Down", "I_ARROW_DOWN" },327{ "Remove", "I_TRASHCAN" },328};329330for (int i = 0; i < (int)g_Config.vPostShaderNames.size() + 1 && i < ARRAY_SIZE(shaderNames_); ++i) {331// Vector element pointer get invalidated on resize, cache name to have always a valid reference in the rendering thread332shaderNames_[i] = i == g_Config.vPostShaderNames.size() ? "Off" : g_Config.vPostShaderNames[i];333334LinearLayout *shaderRow = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT));335shaderRow->SetSpacing(4.0f);336leftColumn->Add(shaderRow);337338if (shaderNames_[i] != "Off") {339postProcChoice_ = shaderRow->Add(new ChoiceWithValueDisplay(&shaderNames_[i], "", &PostShaderTranslateName, new LinearLayoutParams(1.0f)));340} else {341postProcChoice_ = shaderRow->Add(new Choice(ImageID("I_PLUS")));342}343postProcChoice_->OnClick.Add([=](EventParams &e) {344auto gr = GetI18NCategory(I18NCat::GRAPHICS);345auto procScreen = new PostProcScreen(gr->T("Postprocessing shaders"), i, false);346procScreen->SetHasDropShadow(false);347procScreen->OnChoice.Handle(this, &DisplayLayoutScreen::OnPostProcShaderChange);348if (e.v)349procScreen->SetPopupOrigin(e.v);350screenManager()->push(procScreen);351return UI::EVENT_DONE;352});353postProcChoice_->SetEnabledFunc([=] {354return !g_Config.bSkipBufferEffects && !enableStereo();355});356357if (i < g_Config.vPostShaderNames.size()) {358bool hasSettings = false;359std::vector<const ShaderInfo *> shaderChain = GetPostShaderChain(g_Config.vPostShaderNames[i]);360for (auto shaderInfo : shaderChain) {361for (size_t i = 0; i < ARRAY_SIZE(shaderInfo->settings); ++i) {362auto &setting = shaderInfo->settings[i];363if (!setting.name.empty()) {364hasSettings = true;365break;366}367}368}369if (hasSettings) {370CheckBox *checkBox = new CheckBox(&settingsVisible_[i], ImageID("I_SLIDERS"), new LinearLayoutParams(0.0f));371auto settingsButton = shaderRow->Add(checkBox);372settingsButton->OnClick.Add([=](EventParams &e) {373RecreateViews();374return UI::EVENT_DONE;375});376}377378auto removeButton = shaderRow->Add(new Choice(ImageID("I_TRASHCAN"), new LinearLayoutParams(0.0f)));379removeButton->OnClick.Add([=](EventParams &e) -> UI::EventReturn {380g_Config.vPostShaderNames.erase(g_Config.vPostShaderNames.begin() + i);381System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);382RecreateViews();383return UI::EVENT_DONE;384});385386auto moreButton = shaderRow->Add(new Choice(ImageID("I_THREE_DOTS"), new LinearLayoutParams(0.0f)));387moreButton->OnClick.Add([=](EventParams &e) -> UI::EventReturn {388PopupContextMenuScreen *contextMenu = new UI::PopupContextMenuScreen(postShaderContextMenu, ARRAY_SIZE(postShaderContextMenu), I18NCat::DIALOG, moreButton);389screenManager()->push(contextMenu);390const ShaderInfo *info = GetPostShaderInfo(g_Config.vPostShaderNames[i]);391bool usesLastFrame = info ? info->usePreviousFrame : false;392contextMenu->SetEnabled(0, i > 0 && !usesLastFrame);393contextMenu->SetEnabled(1, i < g_Config.vPostShaderNames.size() - 1);394contextMenu->OnChoice.Add([=](EventParams &e) -> UI::EventReturn {395switch (e.a) {396case 0: // Move up397std::swap(g_Config.vPostShaderNames[i - 1], g_Config.vPostShaderNames[i]);398break;399case 1: // Move down400std::swap(g_Config.vPostShaderNames[i], g_Config.vPostShaderNames[i + 1]);401break;402case 2: // Remove403g_Config.vPostShaderNames.erase(g_Config.vPostShaderNames.begin() + i);404break;405default:406return UI::EVENT_DONE;407}408FixPostShaderOrder(&g_Config.vPostShaderNames);409System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);410RecreateViews();411return UI::EVENT_DONE;412});413return UI::EVENT_DONE;414});415}416417418// No need for settings on the last one.419if (i == g_Config.vPostShaderNames.size())420continue;421422if (!settingsVisible_[i])423continue;424425std::vector<const ShaderInfo *> shaderChain = GetPostShaderChain(g_Config.vPostShaderNames[i]);426for (auto shaderInfo : shaderChain) {427// Disable duplicated shader slider428bool duplicated = alreadyAddedShader.find(shaderInfo->section) != alreadyAddedShader.end();429alreadyAddedShader.insert(shaderInfo->section);430431LinearLayout *settingContainer = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT, UI::Margins(24.0f, 0.0f, 0.0f, 0.0f)));432leftColumn->Add(settingContainer);433for (size_t i = 0; i < ARRAY_SIZE(shaderInfo->settings); ++i) {434auto &setting = shaderInfo->settings[i];435if (!setting.name.empty()) {436// This map lookup will create the setting in the mPostShaderSetting map if it doesn't exist, with a default value of 0.0.437std::string key = StringFromFormat("%sSettingCurrentValue%d", shaderInfo->section.c_str(), i + 1);438bool keyExisted = g_Config.mPostShaderSetting.find(key) != g_Config.mPostShaderSetting.end();439auto &value = g_Config.mPostShaderSetting[key];440if (!keyExisted)441value = setting.value;442443if (duplicated) {444auto sliderName = StringFromFormat("%s %s", ps->T(setting.name), ps->T("(duplicated setting, previous slider will be used)"));445PopupSliderChoiceFloat *settingValue = settingContainer->Add(new PopupSliderChoiceFloat(&value, setting.minValue, setting.maxValue, setting.value, sliderName, setting.step, screenManager()));446settingValue->SetEnabled(false);447} else {448PopupSliderChoiceFloat *settingValue = settingContainer->Add(new PopupSliderChoiceFloat(&value, setting.minValue, setting.maxValue, setting.value, ps->T(setting.name), setting.step, screenManager()));449settingValue->SetLiveUpdate(true);450settingValue->SetHasDropShadow(false);451settingValue->SetEnabledFunc([=] {452return !g_Config.bSkipBufferEffects && !enableStereo();453});454}455}456}457}458}459460root_->Add(new DisplayLayoutBackground(mode_, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT, 0.0f, 0.0f, 0.0f, 0.0f)));461}462463void PostProcScreen::CreateViews() {464auto ps = GetI18NCategory(I18NCat::POSTSHADERS);465ReloadAllPostShaderInfo(screenManager()->getDrawContext());466shaders_ = GetAllPostShaderInfo();467std::vector<std::string> items;468int selected = -1;469const std::string selectedName = id_ >= (int)g_Config.vPostShaderNames.size() ? "Off" : g_Config.vPostShaderNames[id_];470471for (int i = 0; i < (int)shaders_.size(); i++) {472if (!shaders_[i].visible)473continue;474if (shaders_[i].isStereo != showStereoShaders_)475continue;476if (shaders_[i].section == selectedName)477selected = (int)indexTranslation_.size();478items.push_back(std::string(ps->T(shaders_[i].section.c_str(), shaders_[i].name.c_str())));479indexTranslation_.push_back(i);480}481adaptor_ = UI::StringVectorListAdaptor(items, selected);482ListPopupScreen::CreateViews();483}484485void PostProcScreen::OnCompleted(DialogResult result) {486if (result != DR_OK)487return;488const std::string &value = shaders_[indexTranslation_[listView_->GetSelected()]].section;489// I feel this logic belongs more in the caller, but eh...490if (showStereoShaders_) {491if (g_Config.sStereoToMonoShader != value) {492g_Config.sStereoToMonoShader = value;493System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);494}495} else {496if (id_ < (int)g_Config.vPostShaderNames.size()) {497if (g_Config.vPostShaderNames[id_] != value) {498g_Config.vPostShaderNames[id_] = value;499System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);500}501} else {502g_Config.vPostShaderNames.push_back(value);503System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);504}505}506}507508509