Compare commits

..

3 Commits

Author SHA1 Message Date
ExPikaPaka
19c5eae22f Removed pop-up error dialog. Resolved improper preset location 2026-05-29 08:37:15 +02:00
Mykola Nahirnyi
9417553c32 Resolves UI freeze issue by properly treating 409 issue 2026-05-29 08:18:01 +03:00
d4not
69e16cd7ef fix: prevent CPU-spin in Sidebar leave handler on inactive Wayland workspace (#13897)
When the OrcaSlicer window is on an inactive Hyprland (or any Wayland
compositor that keeps surfaces mapped while hidden) workspace, GTK
keeps delivering synthetic leave-notify events to the printer-preset
row. The wxEVT_LEAVE_WINDOW handler at Plater.cpp:1855 calls
wxFindWindowAtPoint(), which walks the entire wxWidgets window tree
calling IsShown() / gtk_widget_get_child_visible() on each widget,
then Hide()s the edit button and triggers a Layout() of the parent
panel. The Hide()+Layout() re-fires more leave events, creating a
feedback loop that pegs a CPU core at 100% indefinitely.

GDB attached to a frozen process confirmed the main thread stuck in:

  wxFindWindowAtPoint (recursing through widget tree)
    -> wxWindow::IsShown
      -> gtk_widget_get_child_visible
  ...
  Sidebar::Sidebar(Plater*)::$_14   <- the leave handler lambda
  wxEvtHandler::SafelyProcessEvent
  wxGTKImpl::WindowLeaveCallback
  gtk_main_do_event
  ...

IsShownOnScreen() can't be used as a guard here because GTK on Wayland
reports widgets as visible even when the toplevel surface is on an
inactive workspace (see existing comment at Plater.cpp:9304).

Fix: state-based short-circuit. If btn_edit_printer is already hidden,
the handler has no transition to perform - skip the expensive tree walk
and the Hide()+Layout() that would re-trigger the feedback loop. After
the first leave event, every subsequent leave event is O(1).

Refs:
- #12387 (open issue with matching setup: Arch + Hyprland + RTX 3060 + Bambu A1)
- #11196 (introduced the hover-edit-button feature in Nov 2025)
2026-05-28 14:20:41 +08:00
6 changed files with 79 additions and 52 deletions

View File

@@ -27,6 +27,7 @@
#include <iterator>
#include <exception>
#include <cstdlib>
#include <future>
#include <regex>
#include <thread>
#include <string_view>
@@ -3349,8 +3350,6 @@ bool GUI_App::on_init_network(bool try_backup)
std::string country_code = app_config->get_country_code();
m_agent->set_country_code(country_code);
m_agent->start();
// Orca: disable Bambu telemetry up-front (before any login) so it never starts.
check_track_enable();
}
// When using Orca cloud alongside the BBL network plugin, the BBL DLL agent still
@@ -3367,12 +3366,6 @@ bool GUI_App::on_init_network(bool try_backup)
bbl.init_log();
bbl.set_cert_file(resources_dir() + "/cert", "slicer_base64.cer");
bbl.set_country_code(app_config->get_country_code());
// Orca: disable Bambu telemetry before start() so the DLL never spins up tracking
// workers. This covers the case where the BBL plugin is loaded for LAN discovery
// but the user has not registered BBL_CLOUD_PROVIDER (so m_agent->track_enable
// would not reach this DLL instance).
bbl.track_enable(false);
bbl.track_remove_files();
bbl.start();
}
}
@@ -4883,31 +4876,26 @@ void GUI_App::on_http_error(wxCommandEvent &evt)
return;
}
static bool m_is_error_shown = false;
// Show general error notification for Orca Cloud API failures (not Bambu)
if (provider == ORCA_CLOUD_PROVIDER && status >= 400 && code != HttpErrorVersionLimited) {
wxString msg;
if (!error.empty()) {
msg = wxString::Format(_L("Failed to connect to OrcaCloud.\nPlease check your network connectivity\n(HTTP %u): %s"), status, wxString::FromUTF8(error));
} else {
msg = wxString::Format(_L("Failed to connect to OrcaCloud.\nPlease check your network connectivity\n(HTTP %u)"), status);
}
if (app_config->get_bool("developer_mode")) {
// Use notification manager if ImGui is ready; fall back to wxMessageBox on Linux
// where ImGui may not be initialized until the user switches to the Prepare tab.
if (wxGetApp().plater() != nullptr && wxGetApp().imgui()->display_initialized()) {
wxGetApp()
.plater()
->get_notification_manager()
->push_notification(NotificationType::PlaterError, NotificationManager::NotificationLevel::WarningNotificationLevel,
msg.ToUTF8().data());
}
}
// 409 Conflict is expected during normal cloud sync (preset already exists on cloud).
// Treat it as a no-op - the caller handles conflict resolution via sync_push.
if (status == 409) {
BOOST_LOG_TRIVIAL(info) << "Http 409 Conflict — preset already exists on cloud, no dialog needed.";
return;
}
if (!m_is_error_shown) {
m_is_error_shown = true;
wxMessageBox(msg, _L("Cloud Error"), wxOK | wxICON_ERROR, wxGetApp().mainframe);
if (provider == ORCA_CLOUD_PROVIDER && status >= 400 && code != HttpErrorVersionLimited) {
if (wxGetApp().plater() != nullptr && wxGetApp().imgui()->display_initialized()) {
wxString msg;
if (!error.empty()) {
msg = wxString::Format(_L("Failed to connect to OrcaCloud.\nPlease check your network connectivity\n(HTTP %u): %s"), status, wxString::FromUTF8(error));
} else {
msg = wxString::Format(_L("Failed to connect to OrcaCloud.\nPlease check your network connectivity\n(HTTP %u)"), status);
}
wxGetApp()
.plater()
->get_notification_manager()
->push_notification(NotificationType::PlaterError, NotificationManager::NotificationLevel::WarningNotificationLevel,
msg.ToUTF8().data());
}
}
}
@@ -4978,7 +4966,7 @@ void GUI_App::on_user_login_handle(wxCommandEvent &evt)
void GUI_App::check_track_enable()
{
// Orca: telemetry only exists on the BBL cloud agent; always disable it.
// Orca: alaways disable track event
if (m_agent) {
m_agent->track_enable(false);
m_agent->track_remove_files();
@@ -5848,7 +5836,7 @@ void GUI_App::push_notification(const MachineObject* obj, wxString msg, wxStrin
}
}
void GUI_App::reload_settings()
void GUI_App::reload_settings(std::optional<std::weak_ptr<int>> cancel_token)
{
if (preset_bundle && m_agent) {
// Load user's personal presets
@@ -5925,10 +5913,42 @@ void GUI_App::reload_settings()
if (plater_)
plater_->sidebar().update_all_preset_comboboxes();
};
if (is_main_thread_active())
if (is_main_thread_active()) {
refresh_synced_ui();
else
CallAfter(refresh_synced_ui);
} else {
// Block the background sync thread until the main thread has applied the
// cloud-sourced setting_ids back into local presets. Without this wait,
// the periodic push loop starts immediately with stale sync_info="create"
// / empty setting_ids and re-uploads every preset, getting a 409 from the
// cloud for each one (it already has them from the pull that just finished).
auto p = std::make_shared<std::promise<void>>();
std::future<void> fut = p->get_future();
CallAfter([refresh_synced_ui = std::move(refresh_synced_ui), p]() mutable {
try {
refresh_synced_ui();
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "reload_settings: refresh_synced_ui threw: " << e.what();
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "reload_settings: refresh_synced_ui threw unknown exception";
}
p->set_value(); // always unblock the sync thread
});
// Poll every 100 ms so we can bail out on cancellation (app close)
// without blocking stop_sync_user_preset()'s join() for up to 10 s.
auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10);
while (fut.wait_for(std::chrono::milliseconds(100)) == std::future_status::timeout) {
if (cancel_token.has_value() && cancel_token->expired()) {
BOOST_LOG_TRIVIAL(warning) << "reload_settings: cancelled while waiting for UI "
"preset update — sync loop may see stale setting_ids";
break;
}
if (std::chrono::steady_clock::now() >= deadline) {
BOOST_LOG_TRIVIAL(warning) << "reload_settings: timed out waiting for UI "
"preset update — sync loop may see stale setting_ids";
break;
}
}
}
}
}
@@ -6707,7 +6727,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
finishFn(ret == 0);
if (ret == 0 && m_agent && !t.expired())
reload_settings();
reload_settings(t);
// For orca specific syncing
auto orca_agent = std::dynamic_pointer_cast<OrcaCloudServiceAgent>(m_agent->get_cloud_agent());

View File

@@ -2,6 +2,7 @@
#define slic3r_GUI_App_hpp_
#include <memory>
#include <optional>
#include <string>
#include "ImGuiWrapper.hpp"
#include "ConfigWizard.hpp"
@@ -519,7 +520,7 @@ public:
std::string format_IP(const std::string& ip);
void show_dialog(wxString msg);
void push_notification(const MachineObject* obj, wxString msg, wxString title = wxEmptyString, UserNotificationStyle style = UserNotificationStyle::UNS_NORMAL);
void reload_settings();
void reload_settings(std::optional<std::weak_ptr<int>> cancel_token = std::nullopt);
void remove_user_presets();
bool maybe_migrate_user_presets_on_login();

View File

@@ -1853,6 +1853,14 @@ Sidebar::Sidebar(Plater *parent)
e.Skip();
});
w->Bind(wxEVT_LEAVE_WINDOW, [this, panel_color](wxMouseEvent &e) {
// Orca: if the edit button is already hidden the handler has no
// state to change, so skip the expensive wxFindWindowAtPoint tree
// walk. Without this guard, when the parent window is on an
// inactive Hyprland/Wayland workspace, GTK keeps delivering
// synthetic leave events and the Hide()+Layout() below re-enters
// the same handler in a feedback loop that pegs a CPU core.
// (IsShownOnScreen() can't be used here — see Plater.cpp:9304.)
if (!p->btn_edit_printer->IsShown()) { e.Skip(); return; }
// Use event-relative coords instead of wxGetMousePosition() which
// returns (0,0) on Wayland for global screen coordinates.
wxWindow* evtObj = dynamic_cast<wxWindow*>(e.GetEventObject());

View File

@@ -582,22 +582,21 @@ int NetworkAgent::get_my_token(std::string ticket, unsigned int* http_code, std:
return -1;
}
int NetworkAgent::track_enable(bool enable)
int NetworkAgent::track_enable(bool enable, const std::string& provider)
{
// Orca cloud has no telemetry; the only cloud agent that tracks events is BBL.
this->enable_track = enable;
const auto cloud_agent = get_cloud_agent(BBL_CLOUD_PROVIDER);
this->enable_track = enable;
const auto cloud_agent = get_cloud_agent(provider);
if (cloud_agent)
return cloud_agent->track_enable(enable);
return 0;
return -1;
}
int NetworkAgent::track_remove_files()
int NetworkAgent::track_remove_files(const std::string& provider)
{
const auto cloud_agent = get_cloud_agent(BBL_CLOUD_PROVIDER);
const auto cloud_agent = get_cloud_agent(provider);
if (cloud_agent)
return cloud_agent->track_remove_files();
return 0;
return -1;
}
int NetworkAgent::track_event(std::string evt_key, std::string content, const std::string& provider)

View File

@@ -118,9 +118,8 @@ public:
int get_model_mall_detail_url(std::string* url, std::string id, const std::string& provider = ORCA_CLOUD_PROVIDER);
int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body, const std::string& provider = ORCA_CLOUD_PROVIDER);
int get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body, const std::string& provider = ORCA_CLOUD_PROVIDER);
// Orca: telemetry only exists on the BBL cloud agent (Orca cloud has no track events).
int track_enable(bool enable);
int track_remove_files();
int track_enable(bool enable, const std::string& provider = ORCA_CLOUD_PROVIDER);
int track_remove_files(const std::string& provider = ORCA_CLOUD_PROVIDER);
int track_event(std::string evt_key, std::string content, const std::string& provider = ORCA_CLOUD_PROVIDER);
int track_header(std::string header, const std::string& provider = ORCA_CLOUD_PROVIDER);
int track_update_property(std::string name, std::string value, std::string type = "string", const std::string& provider = ORCA_CLOUD_PROVIDER);

View File

@@ -1888,7 +1888,7 @@ int OrcaCloudServiceAgent::http_post(const std::string& path, const std::string&
.on_error([&](std::string resp_body, std::string error, unsigned resp_status) {
result.success = false;
result.status = resp_status == 0 ? 404 : resp_status;
result.body = body;
result.body = resp_body;
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: HTTP error - " << error;
})
.timeout_max(30)
@@ -1958,7 +1958,7 @@ int OrcaCloudServiceAgent::http_put(const std::string& path, const std::string&
.on_error([&](std::string resp_body, std::string error, unsigned resp_status) {
result.success = false;
result.status = resp_status == 0 ? 404 : resp_status;
result.body = body;
result.body = resp_body;
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: HTTP error - " << error;
})
.timeout_max(30)