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);