From 30dcc2a13a94eaf287abeb0dcd0f606e074e0370 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Aug 2019 18:24:57 -0700 Subject: [PATCH] UI: Validate address entered in settings. --- UI/GameSettingsScreen.cpp | 84 +++++++++++++++++++++++++++++++++++++ UI/GameSettingsScreen.h | 34 ++++++++++++++- ext/native/ui/ui_screen.cpp | 10 +++-- ext/native/ui/ui_screen.h | 1 + 4 files changed, 125 insertions(+), 4 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 399b666a8e..b1bf46f218 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -22,6 +22,7 @@ #include "base/colorutil.h" #include "base/timeutil.h" #include "math/curves.h" +#include "net/resolve.h" #include "gfx_es2/gpu_features.h" #include "gfx_es2/draw_buffer.h" #include "i18n/i18n.h" @@ -1468,6 +1469,7 @@ void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; I18NCategory *sy = GetI18NCategory("System"); I18NCategory *di = GetI18NCategory("Dialog"); + I18NCategory *n = GetI18NCategory("Networking"); LinearLayout *valueRow = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, Margins(0, 0, 0, 10))); @@ -1495,6 +1497,13 @@ void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) { buttonsRow2->Add(new Button(di->T("Delete")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteClick); buttonsRow2->Add(new Button(di->T("Delete all")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteAllClick); buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, G_RIGHT))); + + errorView_ = parent->Add(new TextView(n->T("Invalid IP or hostname"), ALIGN_HCENTER, false, new LinearLayoutParams(Margins(0, 10, 0, 0)))); + errorView_->SetTextColor(0xFF3030FF); + errorView_->SetVisibility(V_GONE); + + progressView_ = parent->Add(new TextView(n->T("Validating address..."), ALIGN_HCENTER, false, new LinearLayoutParams(Margins(0, 10, 0, 0)))); + progressView_->SetVisibility(V_GONE); } void HostnameSelectScreen::SendEditKey(int keyCode, int flags) { @@ -1528,6 +1537,81 @@ UI::EventReturn HostnameSelectScreen::OnDeleteAllClick(UI::EventParams &e) { return UI::EVENT_DONE; } +void HostnameSelectScreen::ResolverThread() { + std::unique_lock guard(resolverLock_); + + while (resolverState_ != ResolverState::QUIT) { + resolverCond_.wait(guard); + + if (resolverState_ == ResolverState::QUEUED) { + resolverState_ = ResolverState::PROGRESS; + + addrinfo *resolved = nullptr; + std::string err; + toResolveResult_ = net::DNSResolve(toResolve_, "80", &resolved, err); + if (resolved) + net::DNSResolveFree(resolved); + + resolverState_ = ResolverState::READY; + } + } +} + +bool HostnameSelectScreen::CanComplete(DialogResult result) { + if (result != DR_OK) + return true; + + std::string value = addrView_->GetText(); + if (lastResolved_ == value) { + return true; + } + + // Currently running. + if (resolverState_ == ResolverState::PROGRESS) + return false; + + std::lock_guard guard(resolverLock_); + switch (resolverState_) { + case ResolverState::PROGRESS: + case ResolverState::QUIT: + return false; + + case ResolverState::QUEUED: + case ResolverState::WAITING: + break; + + case ResolverState::READY: + if (toResolve_ == value) { + // Reset the state, nothing there now. + resolverState_ = ResolverState::WAITING; + toResolve_.clear(); + lastResolved_ = value; + lastResolvedResult_ = toResolveResult_; + + if (lastResolvedResult_) { + errorView_->SetVisibility(UI::V_GONE); + } else { + errorView_->SetVisibility(UI::V_VISIBLE); + } + progressView_->SetVisibility(UI::V_GONE); + + return true; + } + + // Throw away that last result, it was for a different value. + break; + } + + resolverState_ = ResolverState::QUEUED; + toResolve_ = value; + resolverCond_.notify_one(); + + progressView_->SetVisibility(UI::V_VISIBLE); + errorView_->SetVisibility(UI::V_GONE); + + return false; +} + void HostnameSelectScreen::OnCompleted(DialogResult result) { if (result == DR_OK) *value_ = addrView_->GetText(); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index a228cda4dc..8620336907 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -18,6 +18,9 @@ #pragma once #include "ppsspp_config.h" +#include +#include +#include #include "ui/ui_screen.h" #include "UI/MiscScreens.h" @@ -179,20 +182,49 @@ class HostnameSelectScreen : public PopupScreen { public: HostnameSelectScreen(std::string *value, const std::string &title) : PopupScreen(title, "OK", "Cancel"), value_(value) { + resolver_ = std::thread([](HostnameSelectScreen *thiz) { + thiz->ResolverThread(); + }, this); + } + ~HostnameSelectScreen() { + resolverState_ = ResolverState::QUIT; + resolverCond_.notify_one(); + resolver_.join(); } void CreatePopupContents(UI::ViewGroup *parent) override; protected: void OnCompleted(DialogResult result) override; + bool CanComplete(DialogResult result) override; private: + void ResolverThread(); void SendEditKey(int keyCode, int flags = 0); UI::EventReturn OnNumberClick(UI::EventParams &e); UI::EventReturn OnPointClick(UI::EventParams &e); UI::EventReturn OnDeleteClick(UI::EventParams &e); UI::EventReturn OnDeleteAllClick(UI::EventParams &e); + enum class ResolverState { + WAITING, + QUEUED, + PROGRESS, + READY, + QUIT, + }; + std::string *value_; - UI::TextEdit *addrView_; + UI::TextEdit *addrView_ = nullptr; + UI::TextView *errorView_ = nullptr; + UI::TextView *progressView_ = nullptr; + + std::thread resolver_; + ResolverState resolverState_ = ResolverState::WAITING; + std::mutex resolverLock_; + std::condition_variable resolverCond_; + std::string toResolve_ = ""; + bool toResolveResult_ = false; + std::string lastResolved_ = ""; + bool lastResolvedResult_ = false; }; diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index a49877327d..2e90d14c8c 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -268,6 +268,8 @@ bool PopupScreen::key(const KeyInput &key) { void PopupScreen::update() { UIDialogScreen::update(); + defaultButton_->SetEnabled(CanComplete(DR_OK)); + float animatePos = 1.0f; ++frames_; @@ -314,10 +316,12 @@ void PopupScreen::SetPopupOrigin(const UI::View *view) { } void PopupScreen::TriggerFinish(DialogResult result) { - finishFrame_ = frames_; - finishResult_ = result; + if (CanComplete(result)) { + finishFrame_ = frames_; + finishResult_ = result; - OnCompleted(result); + OnCompleted(result); + } } void PopupScreen::resized() { diff --git a/ext/native/ui/ui_screen.h b/ext/native/ui/ui_screen.h index 1d2dfa4399..c45c8b1329 100644 --- a/ext/native/ui/ui_screen.h +++ b/ext/native/ui/ui_screen.h @@ -84,6 +84,7 @@ protected: virtual bool FillVertical() const { return false; } virtual UI::Size PopupWidth() const { return 550; } virtual bool ShowButtons() const { return true; } + virtual bool CanComplete(DialogResult result) { return true; } virtual void OnCompleted(DialogResult result) {} virtual void update() override;