ppsspp/UI/ControlMappingScreen.cpp
Unknown W. Brackets 540a5e3a3b UI: Log analog movement at a lower treshold.
This should be high enough still to be outside the deadzone, and clearer.
See #12062.
2021-04-04 07:29:12 -07:00

734 lines
23 KiB
C++

// Copyright (c) 2013- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "ppsspp_config.h"
#include <algorithm>
#include <deque>
#include <mutex>
#include "Common/Render/TextureAtlas.h"
#include "Common/UI/Root.h"
#include "Common/UI/UI.h"
#include "Common/UI/Context.h"
#include "Common/UI/View.h"
#include "Common/UI/ViewGroup.h"
#include "Common/Log.h"
#include "Common/Data/Color/RGBAUtil.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Input/KeyCodes.h"
#include "Common/Input/InputState.h"
#include "Common/System/Display.h"
#include "Common/System/System.h"
#include "Core/KeyMap.h"
#include "Core/Host.h"
#include "Core/HLE/sceCtrl.h"
#include "Core/System.h"
#include "Core/Config.h"
#include "UI/ControlMappingScreen.h"
#include "UI/GameSettingsScreen.h"
class ControlMapper : public UI::LinearLayout {
public:
ControlMapper(ControlMappingScreen *ctrlScreen, int pspKey, std::string keyName, ScreenManager *scrm, UI::LinearLayoutParams *layoutParams = 0);
void Update() override;
int GetPspKey() const { return pspKey_; }
private:
void Refresh();
UI::EventReturn OnAdd(UI::EventParams &params);
UI::EventReturn OnAddMouse(UI::EventParams &params);
UI::EventReturn OnDelete(UI::EventParams &params);
UI::EventReturn OnReplace(UI::EventParams &params);
UI::EventReturn OnReplaceAll(UI::EventParams &params);
void MappedCallback(KeyDef key);
enum Action {
NONE,
REPLACEONE,
REPLACEALL,
ADD,
};
ControlMappingScreen *ctrlScreen_;
Action action_;
int actionIndex_;
int pspKey_;
std::string keyName_;
ScreenManager *scrm_;
bool refresh_;
};
ControlMapper::ControlMapper(ControlMappingScreen *ctrlScreen, int pspKey, std::string keyName, ScreenManager *scrm, UI::LinearLayoutParams *layoutParams)
: UI::LinearLayout(UI::ORIENT_VERTICAL, layoutParams), ctrlScreen_(ctrlScreen), action_(NONE), pspKey_(pspKey), keyName_(keyName), scrm_(scrm), refresh_(false) {
Refresh();
}
void ControlMapper::Update() {
if (refresh_) {
refresh_ = false;
Refresh();
host->UpdateUI();
}
}
void ControlMapper::Refresh() {
bool hasFocus = UI::GetFocusedView() == this;
Clear();
auto mc = GetI18NCategory("MappableControls");
std::map<std::string, ImageID> keyImages;
keyImages["Circle"] = ImageID("I_CIRCLE");
keyImages["Cross"] = ImageID("I_CROSS");
keyImages["Square"] = ImageID("I_SQUARE");
keyImages["Triangle"] = ImageID("I_TRIANGLE");
keyImages["Start"] = ImageID("I_START");
keyImages["Select"] = ImageID("I_SELECT");
keyImages["L"] = ImageID("I_L");
keyImages["R"] = ImageID("I_R");
using namespace UI;
float itemH = 45;
LinearLayout *root = Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(550, WRAP_CONTENT)));
root->SetSpacing(3.0f);
auto iter = keyImages.find(keyName_);
// First, look among images.
if (iter != keyImages.end()) {
Choice *c = root->Add(new Choice(iter->second, new LinearLayoutParams(200, itemH)));
c->OnClick.Handle(this, &ControlMapper::OnReplaceAll);
} else {
// No image? Let's translate.
Choice *c = new Choice(mc->T(keyName_.c_str()), new LinearLayoutParams(200, itemH));
c->SetCentered(true);
root->Add(c)->OnClick.Handle(this, &ControlMapper::OnReplaceAll);
}
Choice *p = root->Add(new Choice(" + ", new LayoutParams(WRAP_CONTENT, itemH)));
p->OnClick.Handle(this, &ControlMapper::OnAdd);
if (g_Config.bMouseControl) {
Choice *p = root->Add(new Choice("M", new LayoutParams(WRAP_CONTENT, itemH)));
p->OnClick.Handle(this, &ControlMapper::OnAddMouse);
}
LinearLayout *rightColumn = root->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f)));
rightColumn->SetSpacing(2.0f);
std::vector<KeyDef> mappings;
KeyMap::KeyFromPspButton(pspKey_, &mappings, false);
for (size_t i = 0; i < mappings.size(); i++) {
std::string deviceName = GetDeviceName(mappings[i].deviceId);
std::string keyName = KeyMap::GetKeyOrAxisName(mappings[i].keyCode);
LinearLayout *row = rightColumn->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
row->SetSpacing(1.0f);
Choice *c = row->Add(new Choice(deviceName + "." + keyName, new LinearLayoutParams(FILL_PARENT, itemH, 1.0f)));
char tagbuf[16];
sprintf(tagbuf, "%i", (int)i);
c->SetTag(tagbuf);
c->OnClick.Handle(this, &ControlMapper::OnReplace);
Choice *d = row->Add(new Choice(" X ", new LayoutParams(WRAP_CONTENT, itemH)));
d->SetTag(tagbuf);
d->OnClick.Handle(this, &ControlMapper::OnDelete);
}
if (mappings.size() == 0) {
// look like an empty line
Choice *c = rightColumn->Add(new Choice("", new LinearLayoutParams(FILL_PARENT, itemH)));
c->OnClick.Handle(this, &ControlMapper::OnAdd);
}
if (hasFocus)
this->SetFocus();
}
void ControlMapper::MappedCallback(KeyDef kdf) {
switch (action_) {
case ADD:
KeyMap::SetKeyMapping(pspKey_, kdf, false);
break;
case REPLACEALL:
KeyMap::SetKeyMapping(pspKey_, kdf, true);
break;
case REPLACEONE:
KeyMap::g_controllerMap[pspKey_][actionIndex_] = kdf;
KeyMap::g_controllerMapGeneration++;
break;
default:
;
}
g_Config.bMapMouse = false;
refresh_ = true;
ctrlScreen_->KeyMapped(pspKey_);
// After this, we do not exist any more. So the refresh_ = true is probably irrelevant.
}
UI::EventReturn ControlMapper::OnReplace(UI::EventParams &params) {
actionIndex_ = atoi(params.v->Tag().c_str());
action_ = REPLACEONE;
auto km = GetI18NCategory("KeyMapping");
scrm_->push(new KeyMappingNewKeyDialog(pspKey_, true, std::bind(&ControlMapper::MappedCallback, this, std::placeholders::_1), km));
return UI::EVENT_DONE;
}
UI::EventReturn ControlMapper::OnReplaceAll(UI::EventParams &params) {
action_ = REPLACEALL;
auto km = GetI18NCategory("KeyMapping");
scrm_->push(new KeyMappingNewKeyDialog(pspKey_, true, std::bind(&ControlMapper::MappedCallback, this, std::placeholders::_1), km));
return UI::EVENT_DONE;
}
UI::EventReturn ControlMapper::OnAdd(UI::EventParams &params) {
action_ = ADD;
auto km = GetI18NCategory("KeyMapping");
scrm_->push(new KeyMappingNewKeyDialog(pspKey_, true, std::bind(&ControlMapper::MappedCallback, this, std::placeholders::_1), km));
return UI::EVENT_DONE;
}
UI::EventReturn ControlMapper::OnAddMouse(UI::EventParams &params) {
action_ = ADD;
g_Config.bMapMouse = true;
auto km = GetI18NCategory("KeyMapping");
scrm_->push(new KeyMappingNewMouseKeyDialog(pspKey_, true, std::bind(&ControlMapper::MappedCallback, this, std::placeholders::_1), km));
return UI::EVENT_DONE;
}
UI::EventReturn ControlMapper::OnDelete(UI::EventParams &params) {
int index = atoi(params.v->Tag().c_str());
KeyMap::g_controllerMap[pspKey_].erase(KeyMap::g_controllerMap[pspKey_].begin() + index);
KeyMap::g_controllerMapGeneration++;
refresh_ = true;
return UI::EVENT_DONE;
}
void ControlMappingScreen::CreateViews() {
using namespace UI;
mappers_.clear();
auto km = GetI18NCategory("KeyMapping");
root_ = new LinearLayout(ORIENT_HORIZONTAL);
LinearLayout *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(200, FILL_PARENT, Margins(10, 0, 0, 10)));
leftColumn->Add(new Choice(km->T("Clear All")))->OnClick.Handle(this, &ControlMappingScreen::OnClearMapping);
leftColumn->Add(new Choice(km->T("Default All")))->OnClick.Handle(this, &ControlMappingScreen::OnDefaultMapping);
std::string sysName = System_GetProperty(SYSPROP_NAME);
// If there's a builtin controller, restore to default should suffice. No need to conf the controller on top.
if (!KeyMap::HasBuiltinController(sysName) && KeyMap::GetSeenPads().size()) {
leftColumn->Add(new Choice(km->T("Autoconfigure")))->OnClick.Handle(this, &ControlMappingScreen::OnAutoConfigure);
}
leftColumn->Add(new Choice(km->T("Test Analogs")))->OnClick.Handle(this, &ControlMappingScreen::OnTestAnalogs);
leftColumn->Add(new Spacer(new LinearLayoutParams(1.0f)));
AddStandardBack(leftColumn);
rightScroll_ = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));
rightScroll_->SetTag("ControlMapping");
rightScroll_->SetScrollToTop(false);
LinearLayout *rightColumn = new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));
rightScroll_->Add(rightColumn);
root_->Add(leftColumn);
root_->Add(rightScroll_);
std::vector<KeyMap::KeyMap_IntStrPair> mappableKeys = KeyMap::GetMappableKeys();
for (size_t i = 0; i < mappableKeys.size(); i++) {
ControlMapper *mapper = rightColumn->Add(new ControlMapper(this, mappableKeys[i].key, mappableKeys[i].name, screenManager(), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
mappers_.push_back(mapper);
}
}
UI::EventReturn ControlMappingScreen::OnClearMapping(UI::EventParams &params) {
KeyMap::g_controllerMap.clear();
RecreateViews();
return UI::EVENT_DONE;
}
UI::EventReturn ControlMappingScreen::OnDefaultMapping(UI::EventParams &params) {
KeyMap::RestoreDefault();
RecreateViews();
return UI::EVENT_DONE;
}
UI::EventReturn ControlMappingScreen::OnAutoConfigure(UI::EventParams &params) {
std::vector<std::string> items;
const auto seenPads = KeyMap::GetSeenPads();
for (auto s = seenPads.begin(), end = seenPads.end(); s != end; ++s) {
items.push_back(*s);
}
auto km = GetI18NCategory("KeyMapping");
ListPopupScreen *autoConfList = new ListPopupScreen(km->T("Autoconfigure for device"), items, -1);
if (params.v)
autoConfList->SetPopupOrigin(params.v);
screenManager()->push(autoConfList);
return UI::EVENT_DONE;
}
UI::EventReturn ControlMappingScreen::OnTestAnalogs(UI::EventParams &params) {
screenManager()->push(new AnalogTestScreen());
return UI::EVENT_DONE;
}
void ControlMappingScreen::dialogFinished(const Screen *dialog, DialogResult result) {
if (result == DR_OK && dialog->tag() == "listpopup") {
ListPopupScreen *popup = (ListPopupScreen *)dialog;
KeyMap::AutoConfForPad(popup->GetChoiceString());
RecreateViews();
}
}
void ControlMappingScreen::KeyMapped(int pspkey) { // Notification to let us refocus the same one after recreating views.
for (size_t i = 0; i < mappers_.size(); i++) {
if (mappers_[i]->GetPspKey() == pspkey)
SetFocusedView(mappers_[i]);
}
}
void KeyMappingNewKeyDialog::CreatePopupContents(UI::ViewGroup *parent) {
using namespace UI;
auto km = GetI18NCategory("KeyMapping");
auto mc = GetI18NCategory("MappableControls");
std::string pspButtonName = KeyMap::GetPspButtonName(this->pspBtn_);
parent->Add(new TextView(std::string(km->T("Map a new key for")) + " " + mc->T(pspButtonName), new LinearLayoutParams(Margins(10,0))));
}
bool KeyMappingNewKeyDialog::key(const KeyInput &key) {
if (mapped_)
return false;
if (key.flags & KEY_DOWN) {
if (key.keyCode == NKCODE_EXT_MOUSEBUTTON_1) {
return true;
}
mapped_ = true;
KeyDef kdf(key.deviceId, key.keyCode);
TriggerFinish(DR_OK);
if (callback_)
callback_(kdf);
}
return true;
}
void KeyMappingNewMouseKeyDialog::CreatePopupContents(UI::ViewGroup *parent) {
using namespace UI;
auto km = GetI18NCategory("KeyMapping");
parent->Add(new TextView(std::string(km->T("You can press ESC to cancel.")), new LinearLayoutParams(Margins(10, 0))));
}
bool KeyMappingNewMouseKeyDialog::key(const KeyInput &key) {
if (mapped_)
return false;
if (key.flags & KEY_DOWN) {
if (key.keyCode == NKCODE_ESCAPE) {
TriggerFinish(DR_OK);
g_Config.bMapMouse = false;
return false;
}
mapped_ = true;
KeyDef kdf(key.deviceId, key.keyCode);
TriggerFinish(DR_OK);
g_Config.bMapMouse = false;
if (callback_)
callback_(kdf);
}
return true;
}
static bool IgnoreAxisForMapping(int axis) {
switch (axis) {
// Ignore the accelerometer for mapping for now.
case JOYSTICK_AXIS_ACCELEROMETER_X:
case JOYSTICK_AXIS_ACCELEROMETER_Y:
case JOYSTICK_AXIS_ACCELEROMETER_Z:
return true;
// Also ignore some weird axis events we get on Ouya.
case JOYSTICK_AXIS_OUYA_UNKNOWN1:
case JOYSTICK_AXIS_OUYA_UNKNOWN2:
case JOYSTICK_AXIS_OUYA_UNKNOWN3:
case JOYSTICK_AXIS_OUYA_UNKNOWN4:
return true;
default:
return false;
}
}
bool KeyMappingNewKeyDialog::axis(const AxisInput &axis) {
if (mapped_)
return false;
if (IgnoreAxisForMapping(axis.axisId))
return false;
if (axis.value > AXIS_BIND_THRESHOLD) {
mapped_ = true;
KeyDef kdf(axis.deviceId, KeyMap::TranslateKeyCodeFromAxis(axis.axisId, 1));
TriggerFinish(DR_OK);
if (callback_)
callback_(kdf);
}
if (axis.value < -AXIS_BIND_THRESHOLD) {
mapped_ = true;
KeyDef kdf(axis.deviceId, KeyMap::TranslateKeyCodeFromAxis(axis.axisId, -1));
TriggerFinish(DR_OK);
if (callback_)
callback_(kdf);
}
return true;
}
bool KeyMappingNewMouseKeyDialog::axis(const AxisInput &axis) {
if (mapped_)
return false;
if (IgnoreAxisForMapping(axis.axisId))
return false;
if (axis.value > AXIS_BIND_THRESHOLD) {
mapped_ = true;
KeyDef kdf(axis.deviceId, KeyMap::TranslateKeyCodeFromAxis(axis.axisId, 1));
TriggerFinish(DR_OK);
if (callback_)
callback_(kdf);
}
if (axis.value < -AXIS_BIND_THRESHOLD) {
mapped_ = true;
KeyDef kdf(axis.deviceId, KeyMap::TranslateKeyCodeFromAxis(axis.axisId, -1));
TriggerFinish(DR_OK);
if (callback_)
callback_(kdf);
}
return true;
}
class JoystickHistoryView : public UI::InertView {
public:
JoystickHistoryView(int xAxis, int xDevice, int xDir, int yAxis, int yDevice, int yDir, UI::LayoutParams *layoutParams = nullptr)
: UI::InertView(layoutParams),
xAxis_(xAxis), xDir_(xDir),
yAxis_(yAxis), yDir_(yDir) {}
void Draw(UIContext &dc) override;
std::string DescribeText() const override { return ""; }
void Update() override;
void Axis(const AxisInput &input) override {
// TODO: Check input.deviceId?
if (input.axisId == xAxis_) {
curX_ = input.value * xDir_;
} else if (input.axisId == yAxis_) {
curY_ = input.value * yDir_;
}
}
private:
struct Location {
float x;
float y;
};
int xAxis_;
int xDir_;
int yAxis_;
int yDir_;
float curX_ = 0.0f;
float curY_ = 0.0f;
std::deque<Location> locations_;
int maxCount_ = 500;
};
void JoystickHistoryView::Draw(UIContext &dc) {
if (xAxis_ > -1 && yAxis_ > -1) {
const AtlasImage *image = dc.Draw()->GetAtlas()->getImage(ImageID("I_CROSS"));
if (!image) {
return;
}
float minRadius = std::min(bounds_.w, bounds_.h) * 0.5f - image->w;
dc.BeginNoTex();
dc.Draw()->RectOutline(bounds_.centerX() - minRadius, bounds_.centerY() - minRadius, minRadius * 2.0f, minRadius * 2.0f, 0x80FFFFFF);
dc.Flush();
dc.Begin();
int a = maxCount_ - (int)locations_.size();
for (auto iter = locations_.begin(); iter != locations_.end(); ++iter) {
float x = bounds_.centerX() + minRadius * iter->x;
float y = bounds_.centerY() - minRadius * iter->y;
float alpha = (float)a / maxCount_;
if (alpha < 0.0f) {
alpha = 0.0f;
}
dc.Draw()->DrawImage(ImageID("I_CROSS"), x, y, 0.8f, colorAlpha(0xFFFFFF, alpha), ALIGN_CENTER);
a++;
}
dc.Flush();
} else {
dc.Begin();
dc.DrawText("N/A", bounds_.centerX(), bounds_.centerY(), 0xFFFFFFFF, ALIGN_CENTER);
dc.Flush();
}
}
void JoystickHistoryView::Update() {
if (xAxis_ > -1 && yAxis_ > -1) {
locations_.push_back(Location{ curX_, curY_ });
if ((int)locations_.size() > maxCount_) {
locations_.pop_front();
}
}
}
bool AnalogTestScreen::key(const KeyInput &key) {
bool retval = true;
if (UI::IsEscapeKey(key)) {
TriggerFinish(DR_BACK);
return true;
}
char buf[512];
snprintf(buf, sizeof(buf), "Keycode: %d Device ID: %d [%s%s%s%s]", key.keyCode, key.deviceId,
(key.flags & KEY_IS_REPEAT) ? "REP" : "",
(key.flags & KEY_UP) ? "UP" : "",
(key.flags & KEY_DOWN) ? "DOWN" : "",
(key.flags & KEY_CHAR) ? "CHAR" : "");
if (lastLastKeyEvent_ && lastKeyEvent_) {
lastLastKeyEvent_->SetText(lastKeyEvent_->GetText());
lastKeyEvent_->SetText(buf);
}
return retval;
}
bool AnalogTestScreen::axis(const AxisInput &axis) {
UIScreen::axis(axis);
// This is mainly to catch axis events that would otherwise get translated
// into arrow keys, since seeing keyboard arrow key events appear when using
// a controller would be confusing for the user.
if (IgnoreAxisForMapping(axis.axisId))
return false;
const float AXIS_LOG_THRESHOLD = AXIS_BIND_THRESHOLD * 0.5f;
if (axis.value > AXIS_LOG_THRESHOLD || axis.value < -AXIS_LOG_THRESHOLD) {
char buf[512];
snprintf(buf, sizeof(buf), "Axis: %d (value %1.3f) Device ID: %d",
axis.axisId, axis.value, axis.deviceId);
// Null-check just in case they weren't created yet.
if (lastLastKeyEvent_ && lastKeyEvent_) {
lastLastKeyEvent_->SetText(lastKeyEvent_->GetText());
lastKeyEvent_->SetText(buf);
}
return true;
}
return false;
}
void AnalogTestScreen::CreateViews() {
using namespace UI;
auto di = GetI18NCategory("Dialog");
root_ = new LinearLayout(ORIENT_VERTICAL);
LinearLayout *theTwo = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0f));
int axis1, device1, dir1;
int axis2, device2, dir2;
if (!KeyMap::AxisFromPspButton(VIRTKEY_AXIS_X_MAX, &device1, &axis1, &dir1)) axis1 = -1;
if (!KeyMap::AxisFromPspButton(VIRTKEY_AXIS_Y_MAX, &device2, &axis2, &dir2)) axis2 = -1;
theTwo->Add(new JoystickHistoryView(axis1, device1, dir1, axis2, device2, dir2, new LinearLayoutParams(1.0f)));
if (!KeyMap::AxisFromPspButton(VIRTKEY_AXIS_RIGHT_X_MAX, &device1, &axis1, &dir1)) axis1 = -1;
if (!KeyMap::AxisFromPspButton(VIRTKEY_AXIS_RIGHT_Y_MAX, &device2, &axis2, &dir2)) axis2 = -1;
theTwo->Add(new JoystickHistoryView(axis1, device1, dir1, axis2, device2, dir2, new LinearLayoutParams(1.0f)));
root_->Add(theTwo);
lastLastKeyEvent_ = root_->Add(new TextView("-", new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
lastLastKeyEvent_->SetTextColor(0x80FFFFFF); // semi-transparent
lastKeyEvent_ = root_->Add(new TextView("-", new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
root_->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
}
bool TouchTestScreen::touch(const TouchInput &touch) {
UIDialogScreenWithBackground::touch(touch);
if (touch.flags & TOUCH_DOWN) {
bool found = false;
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == touch.id) {
WARN_LOG(SYSTEM, "Double touch");
touches_[i].x = touch.x;
touches_[i].y = touch.y;
found = true;
}
}
if (!found) {
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == -1) {
touches_[i].id = touch.id;
touches_[i].x = touch.x;
touches_[i].y = touch.y;
break;
}
}
}
}
if (touch.flags & TOUCH_MOVE) {
bool found = false;
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == touch.id) {
touches_[i].x = touch.x;
touches_[i].y = touch.y;
found = true;
}
}
if (!found) {
WARN_LOG(SYSTEM, "Move without touch down: %d", touch.id);
}
}
if (touch.flags & TOUCH_UP) {
bool found = false;
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == touch.id) {
found = true;
touches_[i].id = -1;
break;
}
}
if (!found) {
WARN_LOG(SYSTEM, "Touch release without touch down");
}
}
return true;
}
void TouchTestScreen::CreateViews() {
using namespace UI;
auto di = GetI18NCategory("Dialog");
auto gr = GetI18NCategory("Graphics");
root_ = new LinearLayout(ORIENT_VERTICAL);
LinearLayout *theTwo = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0f));
root_->Add(theTwo);
#if !PPSSPP_PLATFORM(UWP)
static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan" };
PopupMultiChoice *renderingBackendChoice = root_->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, (int)GPUBackend::OPENGL, ARRAY_SIZE(renderingBackend), gr->GetName(), screenManager()));
renderingBackendChoice->OnChoice.Handle(this, &TouchTestScreen::OnRenderingBackend);
if (!g_Config.IsBackendEnabled(GPUBackend::OPENGL))
renderingBackendChoice->HideChoice((int)GPUBackend::OPENGL);
if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D9))
renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D9);
if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11))
renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D11);
if (!g_Config.IsBackendEnabled(GPUBackend::VULKAN))
renderingBackendChoice->HideChoice((int)GPUBackend::VULKAN);
#endif
#if PPSSPP_PLATFORM(ANDROID)
root_->Add(new Choice(gr->T("Recreate Activity")))->OnClick.Handle(this, &TouchTestScreen::OnRecreateActivity);
#endif
root_->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &TouchTestScreen::OnImmersiveModeChange);
root_->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
}
#if PPSSPP_PLATFORM(ANDROID)
extern int display_xres;
extern int display_yres;
#endif
void TouchTestScreen::render() {
UIDialogScreenWithBackground::render();
UIContext *ui_context = screenManager()->getUIContext();
Bounds bounds = ui_context->GetLayoutBounds();
ui_context->BeginNoTex();
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id != -1) {
ui_context->Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
}
}
ui_context->Flush();
ui_context->Begin();
char buffer[1024];
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id != -1) {
ui_context->Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
snprintf(buffer, sizeof(buffer), "%0.1fx%0.1f", touches_[i].x, touches_[i].y);
ui_context->DrawText(buffer, touches_[i].x, touches_[i].y + (touches_[i].y > dp_yres - 100.0f ? -135.0f : 95.0f), 0xFFFFFFFF, ALIGN_HCENTER | FLAG_DYNAMIC_ASCII);
}
}
snprintf(buffer, sizeof(buffer),
#if PPSSPP_PLATFORM(ANDROID)
"display_res: %dx%d\n"
#endif
"dp_res: %dx%d\n"
"pixel_res: %dx%d\n"
"g_dpi: %f\n"
"g_dpi_scale: %0.3fx%0.3f\n"
"g_dpi_scale_real: %0.3fx%0.3f\n",
#if PPSSPP_PLATFORM(ANDROID)
display_xres, display_yres,
#endif
dp_xres, dp_yres,
pixel_xres, pixel_yres,
g_dpi,
g_dpi_scale_x, g_dpi_scale_y,
g_dpi_scale_real_x, g_dpi_scale_real_y);
ui_context->DrawTextShadow(buffer, bounds.x + 20.0f, bounds.y + 20.0f, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
ui_context->Flush();
}
void RecreateActivity();
UI::EventReturn TouchTestScreen::OnImmersiveModeChange(UI::EventParams &e) {
System_SendMessage("immersive", "");
if (g_Config.iAndroidHwScale != 0) {
RecreateActivity();
}
return UI::EVENT_DONE;
}
UI::EventReturn TouchTestScreen::OnRenderingBackend(UI::EventParams &e) {
g_Config.Save("GameSettingsScreen::RenderingBackend");
System_SendMessage("graphics_restart", "--touchscreentest");
return UI::EVENT_DONE;
}
UI::EventReturn TouchTestScreen::OnRecreateActivity(UI::EventParams &e) {
RecreateActivity();
return UI::EVENT_DONE;
}