From 3846500db0cc8d1c91175dbf7aee60b5030ee127 Mon Sep 17 00:00:00 2001 From: deanlee Date: Tue, 14 Jan 2025 11:53:53 +0800 Subject: [PATCH] async actions --- system/ui/raylib/util.cc | 5 +- system/ui/raylib/wifi_manager/main.cc | 2 +- .../ui/raylib/wifi_manager/nmcli_backend.cc | 27 ++- system/ui/raylib/wifi_manager/nmcli_backend.h | 7 + system/ui/raylib/wifi_manager/wifi_manager.cc | 169 ++++++++++++------ system/ui/raylib/wifi_manager/wifi_manager.h | 37 ++-- 6 files changed, 172 insertions(+), 75 deletions(-) diff --git a/system/ui/raylib/util.cc b/system/ui/raylib/util.cc index 32904bd724f902..b4407977a16cf5 100644 --- a/system/ui/raylib/util.cc +++ b/system/ui/raylib/util.cc @@ -45,7 +45,10 @@ App::App(const char *title, int fps) { // Load fonts fonts_.reserve(FONT_FILE_PATHS.size()); for (int i = 0; i < FONT_FILE_PATHS.size(); ++i) { - fonts_.push_back(LoadFontEx(FONT_FILE_PATHS[i], 120, nullptr, 250)); + fonts_.push_back(LoadFontEx(FONT_FILE_PATHS[i], 260, nullptr, 0)); + // Improve font texture quality with mipmaps and bilinear filtering + GenTextureMipmaps(&fonts_[i].texture); + SetTextureFilter(fonts_[i].texture, TEXTURE_FILTER_BILINEAR); } pApp = this; diff --git a/system/ui/raylib/wifi_manager/main.cc b/system/ui/raylib/wifi_manager/main.cc index eb022cd5b7a7c9..43bef763548140 100644 --- a/system/ui/raylib/wifi_manager/main.cc +++ b/system/ui/raylib/wifi_manager/main.cc @@ -10,7 +10,7 @@ int main() { BeginDrawing(); ClearBackground(RAYLIB_BLACK); GuiLabel({40, 20, 300, 40}, "Wi-Fi Manager"); - wifi_manager.draw({40, 100, GetScreenWidth() - 80.0f, GetScreenHeight() - 180.0f}); + wifi_manager.render({40, 100, GetScreenWidth() - 80.0f, GetScreenHeight() - 180.0f}); EndDrawing(); } return 0; diff --git a/system/ui/raylib/wifi_manager/nmcli_backend.cc b/system/ui/raylib/wifi_manager/nmcli_backend.cc index c943292712a35e..6864b6c6eeb2d1 100644 --- a/system/ui/raylib/wifi_manager/nmcli_backend.cc +++ b/system/ui/raylib/wifi_manager/nmcli_backend.cc @@ -28,7 +28,7 @@ namespace wifi { std::vector scan_networks() { std::vector networks; - std::string output = util::check_output("nmcli -t -f SSID,IN-USE,SIGNAL,SECURITY --colors no device wifi list"); + std::string output = util::check_output("nmcli -t -c no -f SSID,IN-USE,SIGNAL,SECURITY device wifi list"); for (const auto& line : split(output, '\n')) { auto fields = split(line, ':'); @@ -37,17 +37,28 @@ std::vector scan_networks() { } } - std::sort(networks.begin(), networks.end(), [](const Network& a, const Network& b) { - return std::tie(b.connected, b.strength, b.ssid) < std::tie(a.connected, a.strength, a.ssid); - }); - + std::sort(networks.begin(), networks.end()); return networks; } std::set saved_networks() { - std::string cmd = "nmcli -t -f NAME,TYPE --colors no connection show | grep \":802-11-wireless\" | sed 's/^Auto //g' | cut -d':' -f1"; - auto networks = split(util::check_output(cmd), '\n'); - return std::set(networks.begin(), networks.end()); + std::set result; + std::string cmd = R"(nmcli -t -c no -f NAME,TYPE connection show | grep 802-11-wireless)"; + std::array prefixes{"openpilot connection ", "Auto "}; + auto lines = split(util::check_output(cmd), '\n'); + for (auto& line : lines) { + if (line.empty()) continue; + + auto name = split(line, ':')[0]; + for (const auto& prefix : prefixes) { + if (name.find(prefix) == 0) { + name.erase(0, prefix.length()); + break; + } + } + result.insert(name); + } + return result; } bool connect(const std::string& ssid, const std::string& password) { diff --git a/system/ui/raylib/wifi_manager/nmcli_backend.h b/system/ui/raylib/wifi_manager/nmcli_backend.h index 9f19609591a947..2f72ace494d77c 100644 --- a/system/ui/raylib/wifi_manager/nmcli_backend.h +++ b/system/ui/raylib/wifi_manager/nmcli_backend.h @@ -2,6 +2,7 @@ #include #include +#include #include enum class SecurityType { @@ -17,9 +18,15 @@ struct Network { SecurityType security_type; }; +inline bool operator<(const Network& lhs, const Network& rhs) { + return std::tie(rhs.connected, rhs.strength, rhs.ssid) < std::tie(lhs.connected, lhs.strength, lhs.ssid); +} + namespace wifi { + std::vector scan_networks(); std::set saved_networks(); bool connect(const std::string& ssid, const std::string& password = ""); bool forget(const std::string& ssid); + } // namespace wifi diff --git a/system/ui/raylib/wifi_manager/wifi_manager.cc b/system/ui/raylib/wifi_manager/wifi_manager.cc index a14a56ef6f57e7..837dee6bfbdae1 100644 --- a/system/ui/raylib/wifi_manager/wifi_manager.cc +++ b/system/ui/raylib/wifi_manager/wifi_manager.cc @@ -1,10 +1,18 @@ #include "system/ui/raylib/wifi_manager/wifi_manager.h" +#include #include #include "system/ui/raylib/util.h" #define RAYGUI_IMPLEMENTATION #define BLANK RAYLIB_BLANK +#define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 50 +#define RAYGUI_MESSAGEBOX_BUTTON_HEIGHT 100 +#define RAYGUI_MESSAGEBOX_BUTTON_PADDING 30 +#define RAYGUI_TEXTINPUTBOX_BUTTON_PADDING 50 +#define RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT 100 +#define RAYGUI_TEXTINPUTBOX_HEIGHT 40 + #include "third_party/raylib/include/raygui.h" WifiManager::WifiManager() { @@ -17,45 +25,48 @@ WifiManager::WifiManager() { scanNetworksAsync(); } -void WifiManager::draw(const Rectangle& rect) { +void WifiManager::render(const Rectangle& rect) { rescanIfNeeded(); std::unique_lock lock(mutex_); - if (wifi_networks_.empty()) { - GuiDrawText("Loading Wi-Fi networks...", rect, TEXT_ALIGN_CENTER, RAYLIB_WHITE); - return; - } - if (requires_password_) { - showPasswordDialog(); - } else { - drawNetworkList(rect); + if (current_action_ == ActionState::Forget) { + if (!forgetNetwork()) return; } + if (current_action_ == ActionState::Connect) { + if (!connectToNetwork()) return; + } + + renderNetworkList(rect); } void WifiManager::rescanIfNeeded() { double current_time = GetTime(); - if (current_time - last_scan_time_ > 60.0) { // Rescan after 1 minute + if (current_action_ == ActionState::None && current_time - last_scan_time_ > 60.0) { last_scan_time_ = current_time; scanNetworksAsync(); } } -void WifiManager::drawNetworkList(const Rectangle& rect) { - Rectangle content_rect = {rect.x, rect.y, rect.width - 20, wifi_networks_.size() * item_height_}; +void WifiManager::renderNetworkList(const Rectangle& rect) { + if (available_networks_.empty()) { + GuiDrawText("Loading Wi-Fi networks...", rect, TEXT_ALIGN_CENTER, RAYLIB_WHITE); + return; + } + + Rectangle content_rect = {rect.x, rect.y, rect.width - 20, available_networks_.size() * item_height_}; Rectangle scissor = {0}; GuiScrollPanel(rect, nullptr, content_rect, &scroll_offset_, &scissor); - const int padding = 20; BeginScissorMode(scissor.x, scissor.y, scissor.width, scissor.height); - - for (size_t i = 0; i < wifi_networks_.size(); ++i) { + const int padding = 20; + for (size_t i = 0; i < available_networks_.size(); ++i) { float y = content_rect.y + i * item_height_ + scroll_offset_.y; Rectangle item_rect = {content_rect.x + padding, y, content_rect.width - padding * 2, item_height_}; - drawNetworkItem(item_rect, wifi_networks_[i]); + renderNetworkItem(item_rect, available_networks_[i]); - if (i != wifi_networks_.size() - 1) { + if (i != available_networks_.size() - 1) { float line_y = item_rect.y + item_height_ - 1; DrawLine(item_rect.x, line_y, item_rect.x + item_rect.width, line_y, RAYLIB_LIGHTGRAY); } @@ -64,70 +75,122 @@ void WifiManager::drawNetworkList(const Rectangle& rect) { EndScissorMode(); } -void WifiManager::drawNetworkItem(const Rectangle& rect, const Network& network) { +void WifiManager::renderNetworkItem(const Rectangle& rect, const Network& network) { const int btn_width = 200; Rectangle label_rect{rect.x, rect.y, rect.width - btn_width * 2, item_height_}; GuiLabel(label_rect, network.ssid.c_str()); - if (network.connected) { - GuiLabel({rect.x + rect.width - btn_width * 2 - 20, rect.y, btn_width, item_height_}, "Connected"); - } else if (CheckCollisionPointRec(GetMousePosition(), label_rect) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - initiateConnection(network.ssid); + Rectangle state_rect = {rect.x + rect.width - btn_width * 2 - 30, rect.y, btn_width, item_height_}; + if (network.connected && current_action_ != ActionState::Connecting) { + GuiLabel(state_rect, "Connected"); + } else if (current_action_ == ActionState::Connecting && selected_network_->ssid == network.ssid) { + GuiLabel(state_rect, "CONNECTING..."); + } else if (current_action_ == ActionState::None && + CheckCollisionPointRec(GetMousePosition(), label_rect) && + IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + initiateAction(network, ActionState::Connect); } if (saved_networks_.count(network.ssid)) { - if (GuiButton({rect.x + rect.width - btn_width, rect.y + (item_height_ - 80) / 2, btn_width, 80}, "Forget")) { - forgetNetwork(network.ssid); + if (GuiButton({rect.x + rect.width - btn_width, rect.y + (item_height_ - 80) / 2, btn_width, 80}, "Forget") && + current_action_ == ActionState::None) { + initiateAction(network, ActionState::Forget); } } } -void WifiManager::initiateConnection(const std::string& ssid) { - if (saved_networks_.count(ssid)) { - wifi::connect(ssid); // Directly connect to saved network - return; +void WifiManager::initiateAction(const Network& network, ActionState action) { + current_action_ = action; + selected_network_ = network; +} + +bool WifiManager::forgetNetwork() { + int result = GuiMessageBox( + {GetScreenWidth() / 2.0f - 512, GetScreenHeight() / 2.0f - 384, 1024, 768}, + ("Forget " + selected_network_->ssid + "?").c_str(), "Are you sure you want to forget this network?", "Yes;No"); + + if (result < 0) return false; + + selected_network_.reset(); + if (result != 1) { + current_action_ = ActionState::None; + return true; } - connecting_ssid_ = ssid; - memset(password_input_, 0, sizeof(password_input_)); - requires_password_ = true; -} + current_action_ = ActionState::Forgetting; + saved_networks_.erase(selected_network_->ssid); + for (auto& n : available_networks_) { + if (n.ssid == selected_network_->ssid) { + n.connected = false; + break; + } + } -void WifiManager::forgetNetwork(const std::string& ssid) { - wifi::forget(ssid); - saved_networks_.erase(ssid); - scanNetworksAsync(); + async_forget_task_ = std::async(std::launch::async, [this, ssid = selected_network_->ssid]() { + wifi::forget(ssid); + scanNetworksAsync(); + current_action_ = ActionState::None; + }); + return true; } -void WifiManager::showPasswordDialog() { - // TODO: Implement a keyboard input dialog - int result = GuiTextInputBox( - {GetScreenWidth() / 2.0f - 120, GetScreenHeight() / 2.0f - 60, 240, 140}, - ("Connect to " + connecting_ssid_).c_str(), "Password:", "Ok;Cancel", password_input_, 128, NULL); - if (result < 0) return; - - if (result == 1) { - if (wifi::connect(connecting_ssid_, password_input_)) { - saved_networks_.insert(connecting_ssid_); +bool WifiManager::connectToNetwork() { + std::string password; + if (!saved_networks_.count(selected_network_->ssid) && selected_network_->security_type != SecurityType::OPEN) { + // TODO: Implement a software keyboard input dialog + int result = GuiTextInputBox( + {GetScreenWidth() / 2.0f - 512, GetScreenHeight() / 2.0f - 200, 1024, 400}, + ("Connect to " + selected_network_->ssid).c_str(), "Password:", "Ok;Cancel", password_input_buffer_, 128, NULL); + if (result < 0) return false; + + password = password_input_buffer_; + memset(password_input_buffer_, 0, sizeof(password_input_buffer_)); + if (result != 1) { + selected_network_.reset(); + current_action_ = ActionState::None; + return true; } } - connecting_ssid_.clear(); - requires_password_ = false; + connectToNetworkAsync(selected_network_->ssid, password); + current_action_ = ActionState::Connecting; + return true; } void WifiManager::scanNetworksAsync() { - // Check if the previous scan is still running - if (async_task_.valid() && async_task_.wait_for(std::chrono::seconds(0)) != std::future_status::ready) { + if (async_scan_task_.valid() && async_scan_task_.wait_for(std::chrono::seconds(0)) != std::future_status::ready) { return; } - async_task_ = std::async(std::launch::async, [this]() { - auto networks = wifi::scan_networks(); + async_scan_task_ = std::async(std::launch::async, [this]() { + auto scanned_networks = wifi::scan_networks(); auto known_networks = wifi::saved_networks(); std::unique_lock lock(mutex_); - wifi_networks_ = std::move(networks); + available_networks_ = std::move(scanned_networks); saved_networks_ = std::move(known_networks); }); } + +void WifiManager::connectToNetworkAsync(const std::string& ssid, const std::string& password) { + if (async_connection_task_.valid() && async_connection_task_.wait_for(std::chrono::seconds(0)) != std::future_status::ready) { + return; + } + + async_connection_task_ = std::async(std::launch::async, [this, ssid, password]() { + if (wifi::connect(ssid, password)) { + std::unique_lock lock(mutex_); + saved_networks_.insert(ssid); + selected_network_.reset(); + current_action_ = ActionState::None; + + for (auto& network : available_networks_) { + network.connected = (network.ssid == ssid); + } + std::sort(available_networks_.begin(), available_networks_.end()); + } else { + selected_network_->security_type = SecurityType::WPA; + current_action_ = ActionState::Connect; + } + }); +} diff --git a/system/ui/raylib/wifi_manager/wifi_manager.h b/system/ui/raylib/wifi_manager/wifi_manager.h index b9f60c2a1233e7..10d39536ae5b8d 100644 --- a/system/ui/raylib/wifi_manager/wifi_manager.h +++ b/system/ui/raylib/wifi_manager/wifi_manager.h @@ -1,7 +1,9 @@ #pragma once +#include #include #include +#include #include #include "system/ui/raylib/raylib.h" @@ -10,26 +12,37 @@ class WifiManager { public: WifiManager(); - void draw(const Rectangle &rect); + void render(const Rectangle &rect); protected: - void showPasswordDialog(); + enum class ActionState { + None, + Connect, + Connecting, + Forget, + Forgetting + }; + + bool connectToNetwork(); void scanNetworksAsync(); void rescanIfNeeded(); - void drawNetworkList(const Rectangle &rect); - void drawNetworkItem(const Rectangle& rect, const Network &network); - void initiateConnection(const std::string &ssid); - void forgetNetwork(const std::string& ssid); + bool forgetNetwork(); + void renderNetworkList(const Rectangle &rect); + void renderNetworkItem(const Rectangle& rect, const Network &network); + void connectToNetworkAsync(const std::string &ssid, const std::string &password = ""); + void initiateAction(const Network& network, ActionState action); std::mutex mutex_; - std::future async_task_; - std::vector wifi_networks_; + std::atomic current_action_ = ActionState::None; + std::future async_scan_task_; + std::future async_connection_task_; + std::future async_forget_task_; + std::vector available_networks_; std::set saved_networks_; - double last_scan_time_ = 0; + std::optional selected_network_; Vector2 scroll_offset_ = {0, 0}; - std::string connecting_ssid_; const float item_height_ = 160; - bool requires_password_ = false; - char password_input_[128] = {}; + char password_input_buffer_[128] = {}; + double last_scan_time_ = 0; };