diff --git a/resources/images/vcamera_off_hover.svg b/resources/images/vcamera_off_hover.svg new file mode 100644 index 000000000..1693162c9 --- /dev/null +++ b/resources/images/vcamera_off_hover.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/vcamera_off_normal.svg b/resources/images/vcamera_off_normal.svg new file mode 100644 index 000000000..f90e972b1 --- /dev/null +++ b/resources/images/vcamera_off_normal.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/vcamera_on_hover.svg b/resources/images/vcamera_on_hover.svg new file mode 100644 index 000000000..ea7708347 --- /dev/null +++ b/resources/images/vcamera_on_hover.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/vcamera_on_normal.svg b/resources/images/vcamera_on_normal.svg new file mode 100644 index 000000000..1c12fe644 --- /dev/null +++ b/resources/images/vcamera_on_normal.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 6ce1ad644..48b39ee9c 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -70,6 +70,7 @@ enum PrinterFunction { FUNC_REMOTE_TUNNEL, FUNC_LOCAL_TUNNEL, FUNC_PRINT_WITHOUT_SD, + FUNC_VIRTUAL_CAMERA, FUNC_MAX }; diff --git a/src/slic3r/GUI/DownloadProgressDialog.cpp b/src/slic3r/GUI/DownloadProgressDialog.cpp index 9c943d1c3..cf6a3892e 100644 --- a/src/slic3r/GUI/DownloadProgressDialog.cpp +++ b/src/slic3r/GUI/DownloadProgressDialog.cpp @@ -141,12 +141,12 @@ bool DownloadProgressDialog::Show(bool show) { if (show) { m_simplebook_status->SetSelection(0); - m_upgrade_job = std::make_shared(m_status_bar); + m_upgrade_job = make_job(m_status_bar); m_upgrade_job->set_event_handle(this); m_status_bar->set_progress(0); Bind(EVT_UPGRADE_NETWORK_SUCCESS, [this](wxCommandEvent& evt) { m_status_bar->change_button_label(_L("Finish")); - wxGetApp().restart_networking(); + on_finish(); m_status_bar->set_cancel_callback_fina( [this]() { this->Close(); @@ -205,4 +205,8 @@ void DownloadProgressDialog::on_dpi_changed(const wxRect &suggested_rect) {} void DownloadProgressDialog::update_release_note(std::string release_note, std::string version) {} +std::shared_ptr DownloadProgressDialog::make_job(std::shared_ptr pri) { return std::make_shared(pri); } + +void DownloadProgressDialog::on_finish() { wxGetApp().restart_networking(); } + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/DownloadProgressDialog.hpp b/src/slic3r/GUI/DownloadProgressDialog.hpp index 0f5504cc1..938d2706a 100644 --- a/src/slic3r/GUI/DownloadProgressDialog.hpp +++ b/src/slic3r/GUI/DownloadProgressDialog.hpp @@ -50,7 +50,9 @@ public: std::shared_ptr m_upgrade_job { nullptr }; wxPanel * m_panel_download; - +protected: + virtual std::shared_ptr make_job(std::shared_ptr pri); + virtual void on_finish(); }; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 8906cd9e4..568c799e7 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1257,13 +1257,13 @@ std::string GUI_App::get_http_url(std::string country_code) return url; } -std::string GUI_App::get_plugin_url(std::string country_code) +std::string GUI_App::get_plugin_url(std::string name, std::string country_code) { std::string url = get_http_url(country_code); std::string curr_version = SLIC3R_VERSION; std::string using_version = curr_version.substr(0, 9) + "00"; - url += (boost::format("?slicer/plugins/cloud=%1%") % using_version).str(); + url += (boost::format("?slicer/%1%/cloud=%2%") % name % using_version).str(); //url += (boost::format("?slicer/plugins/cloud=%1%") % "01.01.00.00").str(); return url; } @@ -1283,7 +1283,7 @@ static std::string decode(std::string const& extra, std::string const& path = {} return Slic3r::decode_path(path.c_str()); } -int GUI_App::download_plugin(InstallProgressFn pro_fn, WasCancelledFn cancel_fn) +int GUI_App::download_plugin(std::string name, std::string package_name, InstallProgressFn pro_fn, WasCancelledFn cancel_fn) { int result = 0; // get country_code @@ -1294,12 +1294,12 @@ int GUI_App::download_plugin(InstallProgressFn pro_fn, WasCancelledFn cancel_fn) BOOST_LOG_TRIVIAL(info) << "[download_plugin]: enter"; m_networking_cancel_update = false; // get temp path - fs::path target_file_path = (fs::temp_directory_path() / "network_plugin.zip"); + fs::path target_file_path = (fs::temp_directory_path() / package_name); fs::path tmp_path = target_file_path; tmp_path += format(".%1%%2%", get_current_pid(), ".tmp"); // get_url - std::string url = get_plugin_url(app_config->get_country_code()); + std::string url = get_plugin_url(name, app_config->get_country_code()); std::string download_url; Slic3r::Http http_url = Slic3r::Http::get(url); BOOST_LOG_TRIVIAL(info) << "[download_plugin]: check the plugin from " << url; @@ -1418,16 +1418,16 @@ int GUI_App::download_plugin(InstallProgressFn pro_fn, WasCancelledFn cancel_fn) return result; } -int GUI_App::install_plugin(InstallProgressFn pro_fn, WasCancelledFn cancel_fn) +int GUI_App::install_plugin(std::string name, std::string package_name, InstallProgressFn pro_fn, WasCancelledFn cancel_fn) { bool cancel = false; - std::string target_file_path = (fs::temp_directory_path() / "network_plugin.zip").string(); + std::string target_file_path = (fs::temp_directory_path() / package_name).string(); BOOST_LOG_TRIVIAL(info) << "[install_plugin] enter"; // get plugin folder std::string data_dir_str = data_dir(); boost::filesystem::path data_dir_path(data_dir_str); - auto plugin_folder = data_dir_path / "plugins"; + auto plugin_folder = data_dir_path / name; //auto plugin_folder = boost::filesystem::path(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data()) / "plugins"; auto backup_folder = plugin_folder/"backup"; if (!boost::filesystem::exists(plugin_folder)) { @@ -1534,7 +1534,8 @@ int GUI_App::install_plugin(InstallProgressFn pro_fn, WasCancelledFn cancel_fn) if (pro_fn) pro_fn(InstallStatusInstallCompleted, 100, cancel); - app_config->set_str("app", "installed_networking", "1"); + if (name == "plugins") + app_config->set_str("app", "installed_networking", "1"); BOOST_LOG_TRIVIAL(info) << "[install_plugin] success"; return 0; } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index b7f97873e..d1d544a01 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -534,9 +534,9 @@ public: void associate_files(std::wstring extend); void disassociate_files(std::wstring extend); #endif // __WXMSW__ - std::string get_plugin_url(std::string country_code); - int download_plugin(InstallProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr); - int install_plugin(InstallProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr); + std::string get_plugin_url(std::string name, std::string country_code); + int download_plugin(std::string name, std::string package_name, InstallProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr); + int install_plugin(std::string name, std::string package_name, InstallProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr); std::string get_http_url(std::string country_code); bool is_compatibility_version(); bool check_networking_version(); diff --git a/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp b/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp index 827cd8843..2a346adfa 100644 --- a/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp +++ b/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp @@ -16,7 +16,8 @@ wxDEFINE_EVENT(EVT_INSTALL_NETWORK_FAILED, wxCommandEvent); UpgradeNetworkJob::UpgradeNetworkJob(std::shared_ptr pri) : Job{std::move(pri)} { - ; + name = "plugins"; + package_name = "networking_plugins.zip"; } void UpgradeNetworkJob::on_exception(const std::exception_ptr &eptr) @@ -56,7 +57,7 @@ void UpgradeNetworkJob::process() BOOST_LOG_TRIVIAL(info) << "[UpgradeNetworkJob process]: enter"; // get temp path - fs::path target_file_path = (fs::temp_directory_path() / "network_plugin.zip"); + fs::path target_file_path = (fs::temp_directory_path() / package_name); fs::path tmp_path = target_file_path; auto path_str = tmp_path.string() + wxString::Format(".%d%s", get_current_pid(), ".tmp").ToStdString(); tmp_path = fs::path(path_str); @@ -67,7 +68,7 @@ void UpgradeNetworkJob::process() return was_canceled(); }; int curr_percent = 0; - result = wxGetApp().download_plugin( + result = wxGetApp().download_plugin(name, package_name, [this, &curr_percent](int state, int percent, bool &cancel) { if (state == InstallStatusNormal) { update_status(percent, _L("Downloading")); @@ -95,7 +96,9 @@ void UpgradeNetworkJob::process() return; } - result = wxGetApp().install_plugin([this](int state, int percent, bool&cancel) { + result = wxGetApp().install_plugin( + name, package_name, + [this](int state, int percent, bool &cancel) { if (state == InstallStatusInstallCompleted) { update_status(percent, _L("Finish")); } else { diff --git a/src/slic3r/GUI/Jobs/UpgradeNetworkJob.hpp b/src/slic3r/GUI/Jobs/UpgradeNetworkJob.hpp index f26dcba37..20bbf2b4f 100644 --- a/src/slic3r/GUI/Jobs/UpgradeNetworkJob.hpp +++ b/src/slic3r/GUI/Jobs/UpgradeNetworkJob.hpp @@ -29,6 +29,9 @@ class UpgradeNetworkJob : public Job InstallProgressFn pro_fn { nullptr }; protected: + std::string name; + std::string package_name; + void on_exception(const std::exception_ptr &) override; public: UpgradeNetworkJob(std::shared_ptr pri); diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index 0f3ad7f34..c2e081909 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -125,13 +125,7 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) m_button_year->Bind(wxEVT_COMMAND_BUTTON_CLICKED, time_button_clicked); m_button_month->Bind(wxEVT_COMMAND_BUTTON_CLICKED, time_button_clicked); m_button_all->Bind(wxEVT_COMMAND_BUTTON_CLICKED, time_button_clicked); - - { - wxCommandEvent e(wxEVT_CHECKBOX); - auto b = m_button_all; - e.SetEventObject(b); - b->GetEventHandler()->ProcessEvent(e); - } + m_button_all->SetValue(true); // File type auto type_button_clicked = [this](wxEvent &e) { @@ -143,26 +137,12 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) return; m_image_grid->SetFileType(type); m_last_type = type; - { - wxCommandEvent e(wxEVT_CHECKBOX); - e.SetEventObject(m_button_timelapse); - m_button_timelapse->GetEventHandler()->ProcessEvent(e); - } - { - wxCommandEvent e(wxEVT_CHECKBOX); - e.SetEventObject(m_button_video); - m_button_video->GetEventHandler()->ProcessEvent(e); - } + m_button_timelapse->SetValue(!m_button_timelapse->GetValue()); + m_button_video->SetValue(!m_button_video->GetValue()); }; m_button_video->Bind(wxEVT_COMMAND_BUTTON_CLICKED, type_button_clicked); m_button_timelapse->Bind(wxEVT_COMMAND_BUTTON_CLICKED, type_button_clicked); - - { - wxCommandEvent e(wxEVT_CHECKBOX); - auto b = m_button_timelapse; - e.SetEventObject(b); - b->GetEventHandler()->ProcessEvent(e); - } + m_button_timelapse->SetValue(true); // File management m_button_management->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto &e) { @@ -310,15 +290,12 @@ void MediaFilePanel::modeChanged(wxCommandEvent& e1) if (m_last_mode == mode) return; ::Button* buttons[] = {m_button_all, m_button_month, m_button_year}; - wxCommandEvent e(wxEVT_CHECKBOX); auto b = buttons[m_last_mode]; b->SetFont(Label::Body_14); - e.SetEventObject(b); - b->GetEventHandler()->ProcessEvent(e); + b->SetValue(false); b = buttons[mode]; b->SetFont(Label::Head_14); - e.SetEventObject(b); - b->GetEventHandler()->ProcessEvent(e); + b->SetValue(true); m_last_mode = mode; } diff --git a/src/slic3r/GUI/MediaPlayCtrl.cpp b/src/slic3r/GUI/MediaPlayCtrl.cpp index 0df1b9fca..90935bf48 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.cpp +++ b/src/slic3r/GUI/MediaPlayCtrl.cpp @@ -5,6 +5,21 @@ #include "GUI_App.hpp" #include "libslic3r/AppConfig.hpp" #include "I18N.hpp" +#include "MsgDialog.hpp" +#include "DownloadProgressDialog.hpp" + +#undef pid_t +#include +#ifdef __WIN32__ +#include +#elif __APPLE__ +#include +#include +#else +#include +#include /* For mode constants */ +#include /* For O_* constants */ +#endif namespace Slic3r { namespace GUI { @@ -21,8 +36,7 @@ MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl2 *media_ctrl, const w m_label_status = new Label(this, "", LB_HYPERLINK); - m_button_play->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto & e) { TogglePlay(); }); - + m_button_play->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto &e) { TogglePlay(); }); m_button_play->Bind(wxEVT_RIGHT_UP, [this](auto & e) { m_media_ctrl->Play(); }); m_label_status->Bind(wxEVT_LEFT_UP, [this](auto &e) { auto url = wxString::Format(L"https://wiki.bambulab.com/%s/software/bambu-studio/faq/live-view", L"en"); @@ -90,6 +104,12 @@ void MediaPlayCtrl::SetMachineObject(MachineObject* obj) } m_machine = machine; m_failed_retry = 0; + std::string stream_url; + if (get_stream_url(&stream_url)) { + m_streaming = boost::algorithm::contains(stream_url, "device=" + m_machine); + } else { + m_streaming = false; + } if (m_last_state != MEDIASTATE_IDLE) Stop(); if (m_next_retry.IsValid()) @@ -217,6 +237,127 @@ void MediaPlayCtrl::TogglePlay() } } +struct detach_process +#ifdef __WIN32__ + : public ::boost::process::detail::windows::handler_base_ext + #else + : public ::boost::process::detail::posix::handler_base_ext + #endif + { +#ifdef __WIN32__ + template void on_setup(Executor &exec) const { + exec.creation_flags |= ::boost::winapi::CREATE_NO_WINDOW_; + } +#endif +}; + +void MediaPlayCtrl::ToggleStream() +{ + std::string file_url = data_dir() + "/cameratools/url.txt"; + if (m_streaming) { + boost::nowide::ofstream file(file_url); + file.close(); + m_streaming = false; + return; + } else if (!boost::filesystem::exists(file_url)) { + boost::nowide::ofstream file(file_url); + file.close(); + } + std::string url; + if (!get_stream_url(&url)) { + // create stream pipeline +#ifdef __WIN32__ + std::string file_source = data_dir() + "\\cameratools\\bambu_source.exe"; + std::string file_ffmpeg = data_dir() + "\\cameratools\\ffmpeg.exe"; + std::string file_ff_cfg = data_dir() + "\\cameratools\\ffmpeg.cfg"; +#else + std::string file_source = data_dir() + "/cameratools/bambu_source"; + std::string file_ffmpeg = data_dir() + "/cameratools/ffmpeg"; + std::string file_ff_cfg = data_dir() + "/cameratools/ffmpeg.cfg"; +#endif + if (!boost::filesystem::exists(file_source) || !boost::filesystem::exists(file_ffmpeg) || !boost::filesystem::exists(file_ff_cfg)) { + auto res = MessageDialog(this, _L("Virtual Camera Tools is required for this task!\nDo you want to install them?"), _L("Error"), + wxOK | wxCANCEL).ShowModal(); + if (res == wxID_OK) { + // download tools + struct DownloadProgressDialog2 : DownloadProgressDialog + { + MediaPlayCtrl *ctrl; + DownloadProgressDialog2(MediaPlayCtrl *ctrl) : DownloadProgressDialog(_L("Downloading Virtual Camera Tools")), ctrl(ctrl) {} + struct UpgradeNetworkJob2 : UpgradeNetworkJob + { + UpgradeNetworkJob2(std::shared_ptr pri) : UpgradeNetworkJob(pri) { + name = "cameratools"; + package_name = "camera_tools.zip"; + } + }; + std::shared_ptr make_job(std::shared_ptr pri) override + { return std::make_shared(pri); } + void on_finish() override + { + wxGetApp().CallAfter([ctrl = this->ctrl] { ctrl->ToggleStream(); }); + EndModal(wxID_CLOSE); + } + }; + DownloadProgressDialog2 dlg(this); + dlg.ShowModal(); + } + return; + } + wxString url = L"bambu:///camera/" + from_u8(file_url); + url.Replace("\\", "/"); + url = wxURI(url).BuildURI(); + std::string configs; + try { + boost::filesystem::load_string_file(file_ff_cfg, configs); + } catch (...) {} + std::vector configss; + boost::algorithm::split(configss, configs, boost::algorithm::is_any_of("\r\n")); + configss.erase(std::remove(configss.begin(), configss.end(), std::string()), configss.end()); + boost::process::pipe intermediate; + boost::process::child process_source(file_source, url.data().AsInternal(), boost::process::std_out > intermediate, detach_process(), + boost::process::start_dir(boost::filesystem::path(data_dir()) / "plugins")); + boost::process::child process_ffmpeg(file_ffmpeg, configss, boost::process::std_in < intermediate, detach_process()); + process_source.detach(); + process_ffmpeg.detach(); + } + if (!url.empty() && wxGetApp().app_config->get("not_show_vcamera_stop_prev") != "1") { + MessageDialog dlg(this, _L("Another virtual camera is running.\nBambu Studio supports only a single virtual camera.\nDo you want to stop this virtual camera?"), _L("Warning"), + wxYES | wxCANCEL | wxICON_INFORMATION); + dlg.show_dsa_button(); + auto res = dlg.ShowModal(); + if (dlg.get_checkbox_state()) + wxGetApp().app_config->set("not_show_vcamera_stop_prev", "1"); + if (res == wxID_CANCEL) return; + } + NetworkAgent *agent = wxGetApp().getAgent(); + if (!agent) return; + agent->get_camera_url(m_machine, [this, m = m_machine](std::string url) { + BOOST_LOG_TRIVIAL(info) << "camera_url: " << url; + CallAfter([this, m, url] { + if (m != m_machine || url.empty()) return; + std::string file_url = data_dir() + "/cameratools/url.txt"; + boost::nowide::ofstream file(file_url); + auto url2 = encode_path((url + "&device=" + m).c_str()); + file.write(url2.c_str(), url2.size()); + file.close(); + m_streaming = true; + if (wxGetApp().app_config->get("not_show_vcamera_wiki") != "1") { + MessageDialog dlg(this, _L("Virtual camera is started.\nPress 'OK' to navigate the guide page of 'Streaming video of Bambu Printer'."), _L("Information"), + wxOK | wxCANCEL | wxICON_INFORMATION); + dlg.show_dsa_button(); + auto res = dlg.ShowModal(); + if (dlg.get_checkbox_state()) + wxGetApp().app_config->set("not_show_vcamera_wiki", "1"); + if (res == wxID_OK) { + auto url = wxString::Format(L"https://wiki.bambulab.com/%s/software/bambu-studio/virtual-camera", L"en"); + wxLaunchDefaultBrowser(url); + } + } + }); + }); +} + void MediaPlayCtrl::SetStatus(wxString const &msg2, bool hyperlink) { auto msg = wxString::Format(msg2, m_failed_code); @@ -236,6 +377,8 @@ void MediaPlayCtrl::SetStatus(wxString const &msg2, bool hyperlink) Layout(); } +bool MediaPlayCtrl::IsStreaming() const { return m_streaming; } + void MediaPlayCtrl::media_proc() { boost::unique_lock lock(m_mutex); @@ -270,6 +413,54 @@ void MediaPlayCtrl::media_proc() } } +bool MediaPlayCtrl::get_stream_url(std::string *url) +{ +#ifdef __WIN32__ + HANDLE shm = ::OpenFileMapping(FILE_MAP_READ, FALSE, L"bambu_stream_url"); + if (shm == NULL) return false; + if (url) { + char *addr = (char *) MapViewOfFile(shm, FILE_MAP_READ, 0, 0, 0); + if (addr) { + *url = addr; + UnmapViewOfFile(addr); + url = nullptr; + } + } + CloseHandle(shm); +#elif __APPLE__ + std::string file_url = data_dir() + "/url.txt"; + key_t key = ::ftok(file_url.c_str(), 1000); + int shm = ::shmget(key, 1024, 0); + if (shm == -1) return false; + struct shmid_ds ds; + ::shmctl(shm, IPC_STAT, &ds); + if (ds.shm_nattch == 0) { + return false; + } + if (url) { + char *addr = (char *) ::shmat(shm, nullptr, 0); + if (addr != (void*) -1) { + *url = addr; + ::shmdt(addr); + url = nullptr; + } + } +#else + int shm = ::shm_open("bambu_stream_url", O_RDONLY, 0); + if (shm == -1) return false; + if (url) { + char *addr = (char *) ::mmap(nullptr, 1024, PROT_READ, MAP_SHARED, shm, 0); + if (addr != MAP_FAILED) { + *url = addr; + ::munmap(addr, 1024); + url = nullptr; + } + } + ::close(shm); +#endif + return url == nullptr; +} + void MediaPlayCtrl::onStateChanged(wxMediaEvent& event) { auto last_state = m_last_state; diff --git a/src/slic3r/GUI/MediaPlayCtrl.h b/src/slic3r/GUI/MediaPlayCtrl.h index 6cf059eb2..4c8c1d010 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.h +++ b/src/slic3r/GUI/MediaPlayCtrl.h @@ -35,6 +35,10 @@ public: void SetMachineObject(MachineObject * obj); + bool IsStreaming() const; + + void ToggleStream(); + protected: void onStateChanged(wxMediaEvent & event); @@ -49,6 +53,8 @@ protected: private: void media_proc(); + bool get_stream_url(std::string * url = nullptr); + private: static constexpr wxMediaState MEDIASTATE_IDLE = (wxMediaState) 3; static constexpr wxMediaState MEDIASTATE_INITIALIZING = (wxMediaState) 4; @@ -71,11 +77,12 @@ private: boost::condition_variable m_cond; boost::thread m_thread; + bool m_streaming = false; int m_failed_retry = 0; int m_failed_code = 0; wxDateTime m_next_retry; - ::Button * m_button_play; + ::Button *m_button_play; ::Label * m_label_status; }; diff --git a/src/slic3r/GUI/NetworkTestDialog.cpp b/src/slic3r/GUI/NetworkTestDialog.cpp index ccf3c9a37..bc6123088 100644 --- a/src/slic3r/GUI/NetworkTestDialog.cpp +++ b/src/slic3r/GUI/NetworkTestDialog.cpp @@ -566,7 +566,7 @@ void NetworkTestDialog::start_test_oss_download() tmp_path += (boost::format(".%1%%2%") % get_current_pid() % ".tmp").str(); // get_url - std::string url = wxGetApp().get_plugin_url(app_config->get_country_code()); + std::string url = wxGetApp().get_plugin_url("plugins", app_config->get_country_code()); std::string download_url; Slic3r::Http http_url = Slic3r::Http::get(url); update_status(-1, "[test_oss_download]: url=" + url); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7cb0f6e2e..48d2c165d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -8318,7 +8318,7 @@ int GUI::Plater::close_with_confirm(std::function second_check) auto result = MessageDialog(static_cast(this), _L("The current project has unsaved changes, save it before continue?"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Save"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal(); if (result == wxID_CANCEL) - return result; + return result; else if (result == wxID_YES) { result = save_project(); if (result == wxID_CANCEL) diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index 7f40da205..f0a6e006c 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -235,13 +235,19 @@ wxBoxSizer *StatusBasePanel::create_monitoring_page() m_recording_button->SetMinSize(wxSize(FromDIP(38), FromDIP(24))); m_recording_button->SetBackgroundColour(STATUS_TITLE_BG); + m_vcamera_button = new CameraItem(m_panel_monitoring_title, "vcamera_off_normal", "vcamera_on_normal", "vcamera_off_hover", "vcamera_on_hover"); + m_vcamera_button->SetMinSize(wxSize(FromDIP(38), FromDIP(24))); + m_vcamera_button->SetBackgroundColour(STATUS_TITLE_BG); + m_timelapse_button->SetToolTip(_L("Timelapse")); m_recording_button->SetToolTip(_L("Video")); + m_vcamera_button->SetToolTip(_L("Virtual Camera")); bSizer_monitoring_title->Add(m_bitmap_sdcard_off_img, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); bSizer_monitoring_title->Add(m_bitmap_sdcard_on_img, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); bSizer_monitoring_title->Add(m_timelapse_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); bSizer_monitoring_title->Add(m_recording_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); + bSizer_monitoring_title->Add(m_vcamera_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); bSizer_monitoring_title->Add(FromDIP(13), 0, 0); @@ -1029,6 +1035,7 @@ void StatusBasePanel::upodate_camera_state(bool recording, bool timelapse, bool //recording m_recording_button->set_switch(recording); + m_vcamera_button->set_switch(m_media_play_ctrl->IsStreaming()); //timelapse m_timelapse_button->set_switch(timelapse); @@ -1083,6 +1090,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co //m_bitmap_thumbnail->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(StatusPanel::on_thumbnail_enter), NULL, this); //m_bitmap_thumbnail->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(StatusPanel::on_thumbnail_leave), NULL, this); m_recording_button->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_switch_recording), NULL, this); + m_vcamera_button->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_switch_vcamera), NULL, this); m_project_task_panel->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(StatusPanel::on_thumbnail_leave), NULL, this); m_button_pause_resume->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this); @@ -1123,6 +1131,7 @@ StatusPanel::~StatusPanel() //m_bitmap_thumbnail->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(StatusPanel::on_thumbnail_enter), NULL, this); //m_bitmap_thumbnail->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(StatusPanel::on_thumbnail_leave), NULL, this); m_recording_button->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_switch_recording), NULL, this); + m_vcamera_button->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_switch_vcamera), NULL, this); m_button_pause_resume->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this); m_button_abort->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this); m_button_clean->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_clean), NULL, this); @@ -1355,6 +1364,12 @@ void StatusPanel::update(MachineObject *obj) m_recording_button->Hide(); } + if (obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_CAMERA) && obj->has_ipcam) { + m_vcamera_button->Show(); + } else { + m_vcamera_button->Hide(); + } + if (obj->is_function_supported(PrinterFunction::FUNC_CHAMBER_TEMP)) { m_tempCtrl_frame->Enable(); } else { @@ -2489,6 +2504,14 @@ void StatusPanel::on_switch_recording(wxMouseEvent &event) obj->command_ipcam_record(!value); } +void StatusPanel::on_switch_vcamera(wxMouseEvent &event) +{ + //if (!obj) return; + //bool value = m_recording_button->get_switch_status(); + //obj->command_ipcam_record(!value); + m_media_play_ctrl->ToggleStream(); +} + void StatusPanel::on_camera_enter(wxMouseEvent& event) { if (obj) { @@ -2578,6 +2601,7 @@ void StatusPanel::set_default() m_timelapse_button->Show(); m_recording_button->Show(); + m_vcamera_button->Show(); m_tempCtrl_frame->Show(); m_options_btn->Show(); @@ -2652,6 +2676,7 @@ void StatusPanel::msw_rescale() m_timelapse_button->SetMinSize(wxSize(38, 24)); m_recording_button->SetMinSize(wxSize(38, 24)); + m_vcamera_button->SetMinSize(wxSize(38, 24)); m_bitmap_sdcard_off_img->SetMinSize(wxSize(FromDIP(38), FromDIP(24))); m_bitmap_sdcard_on_img->SetMinSize(wxSize(FromDIP(38), FromDIP(24))); diff --git a/src/slic3r/GUI/StatusPanel.hpp b/src/slic3r/GUI/StatusPanel.hpp index 6f47d4070..7163cb8c4 100644 --- a/src/slic3r/GUI/StatusPanel.hpp +++ b/src/slic3r/GUI/StatusPanel.hpp @@ -91,6 +91,7 @@ protected: CameraItem *m_timelapse_button; CameraItem *m_recording_button; + CameraItem *m_vcamera_button; wxBitmap m_bitmap_camera; wxBitmap m_bitmap_sdcard_state_on; @@ -319,6 +320,7 @@ protected: void on_thumbnail_enter(wxMouseEvent &event); void on_thumbnail_leave(wxMouseEvent &event); void on_switch_recording(wxMouseEvent &event); + void on_switch_vcamera(wxMouseEvent &event); void on_camera_enter(wxMouseEvent &event); void on_camera_leave(wxMouseEvent& event); void on_auto_leveling(wxCommandEvent &event); diff --git a/src/slic3r/GUI/WebDownPluginDlg.cpp b/src/slic3r/GUI/WebDownPluginDlg.cpp index b651323df..1e3827d06 100644 --- a/src/slic3r/GUI/WebDownPluginDlg.cpp +++ b/src/slic3r/GUI/WebDownPluginDlg.cpp @@ -309,12 +309,14 @@ void DownPluginFrame::OnScriptResponseMessage(wxCommandEvent &WXUNUSED(evt)) int DownPluginFrame::DownloadPlugin() { - return wxGetApp().download_plugin([this](int status, int percent, bool &cancel) { return ShowPluginStatus(status, percent, cancel); }, nullptr); + return wxGetApp().download_plugin( + "plugins", "network_plugin.zip", [this](int status, int percent, bool &cancel) { return ShowPluginStatus(status, percent, cancel); }, nullptr); } int DownPluginFrame::InstallPlugin() { - return wxGetApp().install_plugin([this](int status, int percent, bool &cancel) { return ShowPluginStatus(status, percent, cancel); }); + return wxGetApp().install_plugin( + "plugins", "network_plugin.zip", [this](int status, int percent, bool &cancel) { return ShowPluginStatus(status, percent, cancel); }); } int DownPluginFrame::ShowPluginStatus(int status, int percent, bool &cancel) diff --git a/src/slic3r/GUI/WebGuideDialog.cpp b/src/slic3r/GUI/WebGuideDialog.cpp index b50a9d7e7..8c3cdaf20 100644 --- a/src/slic3r/GUI/WebGuideDialog.cpp +++ b/src/slic3r/GUI/WebGuideDialog.cpp @@ -1509,6 +1509,7 @@ bool GuideFrame::LoadFile(std::string jPath, std::string &sContent) int GuideFrame::DownloadPlugin() { return wxGetApp().download_plugin( + "plugins", "network_plugin.zip", [this](int status, int percent, bool& cancel) { return ShowPluginStatus(status, percent, cancel); } @@ -1517,7 +1518,7 @@ int GuideFrame::DownloadPlugin() int GuideFrame::InstallPlugin() { - return wxGetApp().install_plugin( + return wxGetApp().install_plugin("plugins", "network_plugin.zip", [this](int status, int percent, bool &cancel) { return ShowPluginStatus(status, percent, cancel); } diff --git a/src/slic3r/GUI/Widgets/Button.cpp b/src/slic3r/GUI/Widgets/Button.cpp index 273d1d841..82d8e0d79 100644 --- a/src/slic3r/GUI/Widgets/Button.cpp +++ b/src/slic3r/GUI/Widgets/Button.cpp @@ -127,6 +127,14 @@ bool Button::Enable(bool enable) void Button::SetCanFocus(bool canFocus) { this->canFocus = canFocus; } +void Button::SetValue(bool state) +{ + if (GetValue() == state) return; + state_handler.set_state(state ? StateHandler::Checked : 0, StateHandler::Checked); +} + +bool Button::GetValue() const { return state_handler.states() & StateHandler::Checked; } + void Button::Rescale() { if (this->active_icon.bmp().IsOk()) diff --git a/src/slic3r/GUI/Widgets/Button.hpp b/src/slic3r/GUI/Widgets/Button.hpp index 43aac6b9f..56e46ca8f 100644 --- a/src/slic3r/GUI/Widgets/Button.hpp +++ b/src/slic3r/GUI/Widgets/Button.hpp @@ -48,6 +48,10 @@ public: void SetCanFocus(bool canFocus) override; + void SetValue(bool state); + + bool GetValue() const; + void Rescale(); protected: diff --git a/src/slic3r/GUI/Widgets/StateHandler.cpp b/src/slic3r/GUI/Widgets/StateHandler.cpp index 72616ed8f..f66585fa3 100644 --- a/src/slic3r/GUI/Widgets/StateHandler.cpp +++ b/src/slic3r/GUI/Widgets/StateHandler.cpp @@ -69,6 +69,19 @@ void StateHandler::update_binds() for (auto &c : children_) c->update_binds(); } +void StateHandler::set_state(int state, int mask) +{ + if (states_ & mask == state & mask) return; + int old = states_; + states_ = states_ & ~mask | state & mask; + if (old != states_ && (old | states2_) != (states_ | states2_)) { + if (parent_) + parent_->changed(states_ | states2_); + else + owner_->Refresh(); + } +} + StateHandler::StateHandler(StateHandler *parent, wxWindow *owner) : StateHandler(owner) { diff --git a/src/slic3r/GUI/Widgets/StateHandler.hpp b/src/slic3r/GUI/Widgets/StateHandler.hpp index 9ef155c7d..c231b8f63 100644 --- a/src/slic3r/GUI/Widgets/StateHandler.hpp +++ b/src/slic3r/GUI/Widgets/StateHandler.hpp @@ -41,6 +41,8 @@ public: int states() const { return states_ | states2_; } + void set_state(int state, int mask); + private: StateHandler(StateHandler * parent, wxWindow *owner);