Download with basic UI

This commit is contained in:
David Kocik 2022-05-27 14:53:38 +02:00
parent ad5b0d0bed
commit 816dd9490d
11 changed files with 710 additions and 194 deletions

View File

@ -2,10 +2,14 @@
#project(PrusaSlicer_Downloader)
add_executable(PrusaSlicer_Downloader WIN32
Downloader.cpp
Downloader.hpp
DownloaderApp.cpp
DownloaderApp.hpp
Download.cpp
Download.hpp
FileGet.cpp
FileGet.hpp
InstanceSend.cpp
InstanceSend.hpp
FromSlicer/Http.cpp
FromSlicer/Http.hpp
)

View File

@ -0,0 +1,32 @@
#include "Download.hpp"
namespace Downloader{
namespace {
std::string filename_from_url(const std::string& url)
{
// TODO: can it be done with curl?
size_t slash = url.find_last_of("/");
if (slash == std::string::npos && slash != url.size() - 1)
return std::string();
return url.substr(slash + 1, url.size() - slash + 1);
}
}
Download::Download(int ID, std::string url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder)
: m_id(ID)
, m_filename(filename_from_url(url))
{
assert(boost::filesystem::is_directory(dest_folder));
m_final_path = dest_folder / m_filename;
m_file_get = std::make_shared<FileGet>(ID, std::move(url), m_filename, evt_handler, dest_folder);
}
void Download::start()
{
m_file_get->get();
}
void Download::stop()
{
}
}

View File

@ -0,0 +1,28 @@
#ifndef slic3r_Download_hpp_
#define slic3r_Download_hpp_
#include "FileGet.hpp"
#include <wx/wx.h>
namespace Downloader {
class Download {
public:
Download(int ID, std::string url, wxEvtHandler* evt_handler,const boost::filesystem::path& dest_folder);
void start();
void stop();
// void pause();
int get_id() const { return m_id; }
boost::filesystem::path get_final_path() const { return m_final_path; }
std::string get_filename() const { return m_filename; }
private:
const int m_id;
std::string m_filename;
boost::filesystem::path m_final_path;
std::shared_ptr<FileGet> m_file_get;
};
}
#endif

View File

@ -1,122 +0,0 @@
#include "Downloader.hpp"
#include "FileGet.hpp"
#include <iostream>
#include <vector>
#include <string>
#include <boost/nowide/convert.hpp>
#include <wx/event.h>
namespace Downloader {
enum
{
ID_Hello = 1,
};
wxBEGIN_EVENT_TABLE(DownloadFrame, wxFrame)
EVT_MENU(ID_Hello, DownloadFrame::OnHello)
EVT_MENU(wxID_EXIT, DownloadFrame::OnExit)
EVT_MENU(wxID_ABOUT, DownloadFrame::OnAbout)
wxEND_EVENT_TABLE()
bool DownloadApp::OnInit()
{
DownloadFrame* frame = new DownloadFrame("Hello World", wxPoint(50, 50), wxSize(450, 340));
frame->Show(true);
return true;
}
DownloadFrame::DownloadFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame(NULL, wxID_ANY, title, pos, size)
{
wxMenu* menuFile = new wxMenu;
menuFile->Append(ID_Hello, "&Hello...\tCtrl-H",
"Help string shown in status bar for this menu item");
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT);
wxMenu* menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT);
wxMenuBar* menuBar = new wxMenuBar;
menuBar->Append(menuFile, "&File");
menuBar->Append(menuHelp, "&Help");
SetMenuBar(menuBar);
CreateStatusBar();
SetStatusText("Welcome to wxWidgets!");
m_log_label = new wxStaticText(this, wxID_ANY, "Log:");
auto* sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_log_label, wxEXPAND);
SetSizer(sizer);
// Bind(EVT_FILE_COMPLETE, &on_complete, this);
Bind(EVT_FILE_COMPLETE, &DownloadFrame::on_complete, this);
Bind(EVT_FILE_PROGRESS, &DownloadFrame::on_progress, this);
Bind(EVT_FILE_ERROR, &DownloadFrame::on_error, this);
}
void DownloadFrame::OnExit(wxCommandEvent& event)
{
Close(true);
}
void DownloadFrame::OnAbout(wxCommandEvent& event)
{
wxMessageBox("This is a wxWidgets' Hello world sample",
"About Hello World", wxOK | wxICON_INFORMATION);
}
void DownloadFrame::OnHello(wxCommandEvent& event)
{
std::string test_url = "https%3A%2F%2Fmedia.printables.com%2Fmedia%2Fprints%2F152208%2Fstls%2F1431590_8b8287b3-03b1-4cbe-82d0-268a0affa171%2Ff1_logo.stl";
std::shared_ptr<FileGet> file_get = FileGet(get_next_id(), test_url, this, boost::filesystem::path("C:\\Users\\User\\Downloads")) .get();
}
void DownloadFrame::log(const wxString& msg)
{
m_log_lines++;
wxString old_log = m_log_label->GetLabel();
if (m_log_lines > 10) {
size_t endline = old_log.Find('\n');
endline = old_log.find('\n', endline + 1);
if (endline != wxString::npos) {
old_log = "Log:\n" + old_log.substr(endline + 1);
}
}
m_log_label->SetLabel(old_log +"\n"+ msg);
m_full_log += +"\n" + msg;
}
void DownloadFrame::on_progress(wxCommandEvent& event)
{
log(std::to_string(event.GetInt()) + ": " + event.GetString());
//SetStatusText("Progress: " + std::to_string(event.GetInt()));
}
void DownloadFrame::on_error(wxCommandEvent& event)
{
log(std::to_string(event.GetInt()) + ": " + event.GetString());
//SetStatusText(event.GetString().c_str());
}
void DownloadFrame::on_complete(wxCommandEvent& event)
{
log(std::to_string(event.GetInt()) + ": Download complete " + event.GetString());
}
//wxIMPLEMENT_APP_NO_MAIN(MyApp);
//int main()
//{
// wxEntry();
// return 0;
//}
//int APIENTRY WinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, PWSTR /* lpCmdLine */, int /* nCmdShow */)
//{
// wxEntry();
// return 0;
//}
}
wxIMPLEMENT_APP(Downloader::DownloadApp);

View File

@ -1,37 +0,0 @@
#ifndef slic3r_Downloader_hpp_
#define slic3r_Downloader_hpp_
#include <wx/wx.h>
namespace Downloader {
class DownloadApp : public wxApp
{
public:
virtual bool OnInit();
};
class DownloadFrame : public wxFrame
{
public:
DownloadFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
private:
void OnHello(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void on_progress(wxCommandEvent& event);
void on_error(wxCommandEvent& event);
void on_complete(wxCommandEvent& event);
wxDECLARE_EVENT_TABLE();
void log(const wxString& msg);
int m_next_id { 0 };
int get_next_id() {return ++m_next_id; }
wxStaticText* m_log_label;
size_t m_log_lines { 0 };
wxString m_full_log;
};
}
#endif

View File

@ -0,0 +1,251 @@
#include "DownloaderApp.hpp"
#include <iostream>
#include <vector>
#include <string>
#include <boost/nowide/convert.hpp>
#include <wx/event.h>
#include <wx/cmdline.h>
namespace Downloader {
bool DownloadApp::OnInit()
{
m_dwnldr_send = std::make_unique<DownloaderSend>();
if (m_dwnldr_send->get_instance_exists()) {
m_other_exists = true;
m_frame = new DownloadFrame("PrusaSlicer-Downloader", wxPoint(50, 50), wxSize(0,0));
return wxApp::OnInit();
}
m_frame = new DownloadFrame("PrusaSlicer-Downloader", wxPoint(50, 50), wxSize(450, 340));
m_frame->Show(true);
wxWindow::MSWRegisterMessageHandler(WM_COPYDATA, [](wxWindow* win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
auto frame = dynamic_cast<DownloadFrame*>(win);
COPYDATASTRUCT* copy_data_structure = { 0 };
copy_data_structure = (COPYDATASTRUCT*)lParam;
if (copy_data_structure->dwData == 1) {
LPCWSTR arguments = (LPCWSTR)copy_data_structure->lpData;
frame->handle_message(arguments);
}
return true;
});
return wxApp::OnInit();
}
void DownloadApp::OnInitCmdLine(wxCmdLineParser& parser)
{
static const wxCmdLineEntryDesc cmdLineDesc[] =
{
{ wxCMD_LINE_SWITCH, "v", "verbose", "be verbose" },
{ wxCMD_LINE_SWITCH, "q", "quiet", "be quiet" },
// { wxCMD_LINE_OPTION, "s", "slicer", "path to prusaslicer", wxCMD_LINE_VAL_STRING },
{ wxCMD_LINE_OPTION, "u", "url", "url to download", wxCMD_LINE_VAL_STRING },
{ wxCMD_LINE_NONE }
};
parser.SetDesc(cmdLineDesc);
}
bool DownloadApp::OnCmdLineParsed(wxCmdLineParser& parser)
{
wxString option;
wxString url;
if (parser.Found("u", &option)) {
url = option;
}
if (m_other_exists) {
m_dwnldr_send->send_url(url);
m_frame->log("sent " + url);
m_frame->Close(true);
return false;
} else {
if (!url.empty() && m_frame != nullptr)
m_frame->start_download(std::move(url));
return wxApp::OnCmdLineParsed(parser);
}
}
DownloadFrame::DownloadFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame(NULL, wxID_ANY, title, pos, size)
, m_instance_send(std::make_unique<SlicerSend>())
{
dataview = new wxDataViewListCtrl(this, wxID_ANY);
/* dataview->AppendColumn(new wxDataViewColumn("ID", new TextRenderer(), 0, 100, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE));
dataview->AppendColumn(new wxDataViewColumn("Filename", new TextRenderer(), 1, 100, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE));
dataview->AppendProgressColumn("Progress", wxDATAVIEW_CELL_INERT, 100, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
dataview->AppendColumn(new wxDataViewColumn("status", new TextRenderer(), 2, 100, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE));*/
dataview->AppendTextColumn("ID", wxDATAVIEW_CELL_INERT, 30, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
dataview->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT, 100, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
dataview->AppendProgressColumn("Progress", wxDATAVIEW_CELL_INERT, 100, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
dataview->AppendTextColumn("status", wxDATAVIEW_CELL_INERT, 100, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
m_dest_folder = boost::filesystem::path("C:\\Users\\User\\Downloads");
m_log_label = new wxStaticText(this, wxID_ANY, "Log:");
auto* sizer = new wxBoxSizer(wxVERTICAL);
//sizer->Add(m_log_label, wxEXPAND);
sizer->Add(dataview, 1, wxEXPAND | wxBOTTOM);
SetSizer(sizer);
Bind(EVT_FILE_COMPLETE, &DownloadFrame::on_complete, this);
Bind(EVT_FILE_PROGRESS, &DownloadFrame::on_progress, this);
Bind(EVT_FILE_ERROR, &DownloadFrame::on_error, this);
}
void DownloadFrame::start_download(wxString url)
{
// prusaslicer://open?file=https%3A%2F%2Fmedia.printables.com%2Fmedia%2Fprints%2F152208%2Fstls%2F1431590_8b8287b3-03b1-4cbe-82d0-268a0affa171%2Ff1_logo.stl
if (url.starts_with("open?file=")) {
int id = get_next_id();
std::string escaped_url = FileGet::escape_url(boost::nowide::narrow(url.substr(10)));
log(std::to_string(id) + ": start " + escaped_url);
m_downloads.emplace_back(std::make_unique<Download>(id, std::move(escaped_url), this, m_dest_folder));
m_downloads.back()->start();
wxVector<wxVariant> fields;
fields.push_back(wxVariant(std::to_wstring(id)));
fields.push_back(wxVariant(m_downloads.back()->get_filename()));
fields.push_back(wxVariant(0));
fields.push_back(wxVariant("Pending"));
dataview->AppendItem(fields);
} else {
log("wrong url: " + url);
}
}
void DownloadFrame::log(const wxString& msg)
{
m_log_lines++;
wxString old_log = m_log_label->GetLabel();
if (m_log_lines > 10) {
size_t endline = old_log.Find('\n');
endline = old_log.find('\n', endline + 1);
if (endline != wxString::npos) {
old_log = "Log:\n" + old_log.substr(endline + 1);
}
}
//m_log_label->SetLabel(old_log +"\n"+ msg);
//printf("%s\n",old_log + "\n" + msg);
m_full_log += +"\n" + msg;
}
void DownloadFrame::on_progress(wxCommandEvent& event)
{
//log(std::to_string(event.GetInt()) + ": " + event.GetString());
dataview->SetValue(std::stoi(boost::nowide::narrow(event.GetString())), event.GetInt() - 1, 2);
dataview->SetValue("Downloading", event.GetInt() -1, 3);
}
void DownloadFrame::on_error(wxCommandEvent& event)
{
//log(std::to_string(event.GetInt()) + ": " + event.GetString());
//SetStatusText(event.GetString().c_str());
dataview->SetValue("Error", event.GetInt() - 1, 3);
}
void DownloadFrame::on_complete(wxCommandEvent& event)
{
dataview->SetValue("Done", event.GetInt() - 1, 3);
m_instance_send->start_with_path(event.GetString());
}
void DownloadFrame::handle_message(const wxString& msg)
{
log("recieved: " + msg);
start_download(msg);
}
//wxIMPLEMENT_APP_NO_MAIN(MyApp);
//int main()
//{
// wxEntry();
// return 0;
//}
//int APIENTRY WinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, PWSTR /* lpCmdLine */, int /* nCmdShow */)
//{
// wxEntry();
// return 0;
//}
//
//// ----------------------------------------------------------------------------
//// TextRenderer
//// ----------------------------------------------------------------------------
//
//bool TextRenderer::SetValue(const wxVariant& value)
//{
// m_value = value.GetString();
// return true;
//}
//
//bool TextRenderer::GetValue(wxVariant& value) const
//{
// value = m_value;
// return false;
//}
//
//bool TextRenderer::Render(wxRect rect, wxDC* dc, int state)
//{
//
// RenderText(m_value, 0, rect, dc, state);
//
////
////#ifdef _WIN32
//// // workaround for Windows DarkMode : Don't respect to the state & wxDATAVIEW_CELL_SELECTED to avoid update of the text color
//// RenderText(m_value, 0, rect, dc, state & wxDATAVIEW_CELL_SELECTED ? 0 : state);
////#else
//// RenderText(m_value, 0, rect, dc, state);
////#endif
//
// return true;
//}
//
//wxSize TextRenderer::GetSize() const
//{
// return GetTextExtent(m_value);
//}
//
}
wxIMPLEMENT_APP(Downloader::DownloadApp);

View File

@ -0,0 +1,96 @@
#ifndef slic3r_DownloaderApp_hpp_
#define slic3r_DownloadeAppr_hpp_
#include "InstanceSend.hpp"
#include "Download.hpp"
#include <vector>
#include <wx/wx.h>
#include <wx/dataview.h>
namespace Downloader {
class DownloadFrame : public wxFrame
{
public:
DownloadFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
void set_path_to_slicer(wxString path) { m_path_to_slicer = path; }
void start_download(wxString url);
void log(const wxString& msg);
void handle_message(const wxString& msg);
private:
void on_progress(wxCommandEvent& event);
void on_error(wxCommandEvent& event);
void on_complete(wxCommandEvent& event);
int m_next_id { 0 };
int get_next_id() {return ++m_next_id; }
wxStaticText* m_log_label;
size_t m_log_lines { 0 };
wxString m_full_log;
wxDataViewListCtrl* dataview;
wxString m_url;
wxString m_path_to_slicer;
std::unique_ptr<SlicerSend> m_instance_send;
boost::filesystem::path m_dest_folder;
std::vector<std::unique_ptr<Download>> m_downloads;
};
class DownloadApp : public wxApp
{
protected:
DownloadFrame* m_frame{ nullptr };
std::unique_ptr<DownloaderSend> m_dwnldr_send;
bool m_other_exists { false };
public:
bool OnInit() override;
void OnInitCmdLine(wxCmdLineParser& parser) override;
bool OnCmdLineParsed(wxCmdLineParser& parser) override;
};
wxDECLARE_APP(DownloadApp);
//
//
//
//
//
//class TextRenderer : public wxDataViewCustomRenderer
//{
//public:
// TextRenderer(wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT
// , int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL
// ) : wxDataViewCustomRenderer(wxT("string"), mode, align) {}
//
// bool SetValue(const wxVariant& value) override;
// bool GetValue(wxVariant& value) const override;
//
// virtual bool Render(wxRect cell, wxDC* dc, int state) override;
// virtual wxSize GetSize() const override;
//
// bool HasEditorCtrl() const override { return false; }
//
//private:
// wxString m_value;
//};
//
//
}
#endif

View File

@ -8,8 +8,8 @@
namespace Downloader {
namespace {
std::string escape_url(const std::string& unescaped)
std::string FileGet::escape_url(const std::string& unescaped)
{
std::string ret_val;
CURL* curl = curl_easy_init();
@ -24,14 +24,7 @@ std::string escape_url(const std::string& unescaped)
}
return ret_val;
}
std::string filename_from_url(const std::string& url)
{
// TODO: can it be done with curl?
size_t slash = url.find_last_of("/");
if (slash == std::string::npos && slash != url.size()-1)
return std::string();
return url.substr(slash + 1, url.size() - slash + 1);
}
namespace {
unsigned get_current_pid()
{
#ifdef WIN32
@ -57,15 +50,16 @@ struct FileGet::priv
std::thread m_io_thread;
wxEvtHandler* m_evt_handler;
boost::filesystem::path m_dest_folder;
priv(int ID, std::string&& url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder);
std::atomic_bool m_cancel = false;
priv(int ID, std::string&& url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder);
void get_perform();
};
FileGet::priv::priv(int ID, std::string&& url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder)
FileGet::priv::priv(int ID, std::string&& url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder)
: m_id(ID)
, m_url(std::move(url))
, m_filename(filename)
, m_evt_handler(evt_handler)
, m_dest_folder(dest_folder)
{
@ -75,20 +69,23 @@ void FileGet::priv::get_perform()
{
assert(m_evt_handler);
assert(!m_url.empty());
m_url = escape_url(m_url);
assert(!m_url.empty());
m_filename = filename_from_url(m_url);
assert(!m_filename.empty());
assert(boost::filesystem::is_directory(m_dest_folder));
Downloader::Http::get(m_url)
//.size_limit(size_limit)
.on_progress([&](Downloader::Http::Progress progress, bool& cancel) {
if (m_cancel) {
cancel = true;
return;
// TODO: send canceled event?
}
wxCommandEvent* evt = new wxCommandEvent(EVT_FILE_PROGRESS);
if (progress.dlnow == 0)
evt->SetString("0");
else
evt->SetString(std::to_string((float)progress.dltotal / (float)progress.dlnow));
evt->SetString(std::to_string(progress.dlnow * 100 / progress.dltotal));
evt->SetInt(m_id);
m_evt_handler->QueueEvent(evt);
})
@ -99,7 +96,7 @@ void FileGet::priv::get_perform()
m_evt_handler->QueueEvent(evt);
})
.on_complete([&](std::string body, unsigned /* http_status */) {
size_t body_size = body.size();
// TODO:
//if (body_size != expected_size) {
@ -148,12 +145,11 @@ void FileGet::priv::get_perform()
}
FileGet::FileGet(int ID, std::string url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder)
: p(new priv(ID, std::move(url), evt_handler, dest_folder))
, m_ID(ID)
FileGet::FileGet(int ID, std::string url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder)
: p(new priv(ID, std::move(url), filename, evt_handler, dest_folder))
{}
FileGet::FileGet(FileGet&& other) : p(std::move(other.p)), m_ID(other.get_ID()) {}
FileGet::FileGet(FileGet&& other) : p(std::move(other.p)) {}
FileGet::~FileGet()
{
@ -162,18 +158,21 @@ FileGet::~FileGet()
}
}
std::shared_ptr<FileGet> FileGet::get()
void FileGet::get()
{
auto self = std::make_shared<FileGet>(std::move(*this));
if (self->p) {
auto io_thread = std::thread([self]() {
self->p->get_perform();
if (p) {
auto io_thread = std::thread([&priv = p]() {
priv->get_perform();
});
self->p->m_io_thread = std::move(io_thread);
p->m_io_thread = std::move(io_thread);
}
}
return self;
void FileGet::cancel()
{
if(p){
p->m_cancel = true;
}
}
}

View File

@ -14,18 +14,21 @@ class FileGet : public std::enable_shared_from_this<FileGet> {
private:
struct priv;
public:
FileGet(int ID, std::string url, wxEvtHandler* evt_handler,const boost::filesystem::path& dest_folder);
FileGet(int ID, std::string url, const std::string& filename, wxEvtHandler* evt_handler,const boost::filesystem::path& dest_folder);
FileGet(FileGet&& other);
~FileGet();
std::shared_ptr<FileGet> get();
const int get_ID() const { return m_ID; }
void get();
void cancel();
static std::string escape_url(const std::string& url);
private:
std::unique_ptr<priv> p;
const int m_ID;
};
// int = DOWNLOAD ID; string = file path
wxDECLARE_EVENT(EVT_FILE_COMPLETE, wxCommandEvent);
// int = DOWNLOAD ID; string = error msg
wxDECLARE_EVENT(EVT_FILE_PROGRESS, wxCommandEvent);
// int = DOWNLOAD ID; string = progress percent
wxDECLARE_EVENT(EVT_FILE_ERROR, wxCommandEvent);
}

View File

@ -0,0 +1,239 @@
#include "InstanceSend.hpp"
#ifdef _WIN32
#include <windows.h>
#include <strsafe.h>
#endif //WIN32
#include <boost/nowide/convert.hpp>
#include <wx/utils.h>
namespace Downloader {
namespace {
// TODO: Taken from from Config.cpp
std::string escape_strings_cstyle(const std::vector<std::string>& strs)
{
// 1) Estimate the output buffer size to avoid buffer reallocation.
size_t outbuflen = 0;
for (size_t i = 0; i < strs.size(); ++i)
// Reserve space for every character escaped + quotes + semicolon.
outbuflen += strs[i].size() * 2 + 3;
// 2) Fill in the buffer.
std::vector<char> out(outbuflen, 0);
char* outptr = out.data();
for (size_t j = 0; j < strs.size(); ++j) {
if (j > 0)
// Separate the strings.
(*outptr++) = ';';
const std::string& str = strs[j];
// Is the string simple or complex? Complex string contains spaces, tabs, new lines and other
// escapable characters. Empty string shall be quoted as well, if it is the only string in strs.
bool should_quote = strs.size() == 1 && str.empty();
for (size_t i = 0; i < str.size(); ++i) {
char c = str[i];
if (c == ' ' || c == ';' || c == '\t' || c == '\\' || c == '"' || c == '\r' || c == '\n') {
should_quote = true;
break;
}
}
if (should_quote) {
(*outptr++) = '"';
for (size_t i = 0; i < str.size(); ++i) {
char c = str[i];
if (c == '\\' || c == '"') {
(*outptr++) = '\\';
(*outptr++) = c;
}
else if (c == '\r') {
(*outptr++) = '\\';
(*outptr++) = 'r';
}
else if (c == '\n') {
(*outptr++) = '\\';
(*outptr++) = 'n';
}
else
(*outptr++) = c;
}
(*outptr++) = '"';
}
else {
memcpy(outptr, str.data(), str.size());
outptr += str.size();
}
}
return std::string(out.data(), outptr - out.data());
}
#ifdef _WIN32
static HWND l_prusa_slicer_hwnd;
static HWND l_downloader_hwnd;
BOOL CALLBACK EnumWindowsProcSlicer(_In_ HWND hwnd, _In_ LPARAM lParam)
{
//checks for other instances of prusaslicer, if found brings it to front and return false to stop enumeration and quit this instance
//search is done by classname(wxWindowNR is wxwidgets thing, so probably not unique) and name in window upper panel
//other option would be do a mutex and check for its existence
//BOOST_LOG_TRIVIAL(error) << "ewp: version: " << l_version_wstring;
TCHAR wndText[1000];
TCHAR className[1000];
int err;
err = GetClassName(hwnd, className, 1000);
if (err == 0)
return true;
err = GetWindowText(hwnd, wndText, 1000);
if (err == 0)
return true;
std::wstring classNameString(className);
std::wstring wndTextString(wndText);
if (wndTextString.find(L"PrusaSlicer") != std::wstring::npos && classNameString == L"wxWindowNR") {
//check if other instances has same instance hash
//if not it is not same version(binary) as this version
HANDLE handle = GetProp(hwnd, L"Instance_Hash_Minor");
uint64_t other_instance_hash = PtrToUint(handle);
uint64_t other_instance_hash_major;
//TODO
//uint64_t my_instance_hash = GUI::wxGetApp().get_instance_hash_int();
handle = GetProp(hwnd, L"Instance_Hash_Major");
other_instance_hash_major = PtrToUint(handle);
other_instance_hash_major = other_instance_hash_major << 32;
other_instance_hash += other_instance_hash_major;
//if (my_instance_hash == other_instance_hash)
{
//BOOST_LOG_TRIVIAL(debug) << "win enum - found correct instance";
l_prusa_slicer_hwnd = hwnd;
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
SetForegroundWindow(hwnd);
return false;
}
//BOOST_LOG_TRIVIAL(debug) << "win enum - found wrong instance";
}
return true;
}
bool send_message_slicer(const wxString& message)
{
if (!EnumWindows(EnumWindowsProcSlicer, 0)) {
std::wstring wstr(message.c_str());//boost::nowide::widen(message);
std::unique_ptr<LPWSTR> command_line_args = std::make_unique<LPWSTR>(const_cast<LPWSTR>(wstr.c_str()));
/*LPWSTR command_line_args = new wchar_t[wstr.size() + 1];
copy(wstr.begin(), wstr.end(), command_line_args);
command_line_args[wstr.size()] = 0;*/
//Create a COPYDATASTRUCT to send the information
//cbData represents the size of the information we want to send.
//lpData represents the information we want to send.
//dwData is an ID defined by us(this is a type of ID different than WM_COPYDATA).
COPYDATASTRUCT data_to_send = { 0 };
data_to_send.dwData = 1;
data_to_send.cbData = sizeof(TCHAR) * (wcslen(*command_line_args.get()) + 1);
data_to_send.lpData = *command_line_args.get();
SendMessage(l_prusa_slicer_hwnd, WM_COPYDATA, 0, (LPARAM)&data_to_send);
return true;
}
return false;
}
BOOL CALLBACK EnumWindowsProcDownloader(_In_ HWND hwnd, _In_ LPARAM lParam)
{
//checks for other instances of prusaslicer, if found brings it to front and return false to stop enumeration and quit this instance
//search is done by classname(wxWindowNR is wxwidgets thing, so probably not unique) and name in window upper panel
//other option would be do a mutex and check for its existence
//BOOST_LOG_TRIVIAL(error) << "ewp: version: " << l_version_wstring;
TCHAR wndText[1000];
TCHAR className[1000];
int err;
err = GetClassName(hwnd, className, 1000);
if (err == 0)
return true;
err = GetWindowText(hwnd, wndText, 1000);
if (err == 0)
return true;
std::wstring classNameString(className);
std::wstring wndTextString(wndText);
if (wndTextString.find(L"PrusaSlicer-Downloader") != std::wstring::npos && classNameString == L"wxWindowNR") {
//check if other instances has same instance hash
//if not it is not same version(binary) as this version
HANDLE handle = GetProp(hwnd, L"Instance_Hash_Minor");
uint64_t other_instance_hash = PtrToUint(handle);
uint64_t other_instance_hash_major;
//TODO
//uint64_t my_instance_hash = GUI::wxGetApp().get_instance_hash_int();
handle = GetProp(hwnd, L"Instance_Hash_Major");
other_instance_hash_major = PtrToUint(handle);
other_instance_hash_major = other_instance_hash_major << 32;
other_instance_hash += other_instance_hash_major;
//if (my_instance_hash == other_instance_hash)
{
//BOOST_LOG_TRIVIAL(debug) << "win enum - found correct instance";
l_downloader_hwnd = hwnd;
ShowWindow(hwnd, SW_NORMAL);
SetForegroundWindow(hwnd);
return false;
}
//BOOST_LOG_TRIVIAL(debug) << "win enum - found wrong instance";
}
return true;
}
bool send_message_downloader(const wxString& message)
{
if (!EnumWindows(EnumWindowsProcDownloader, 0)) {
std::wstring wstr(message.c_str());//boost::nowide::widen(message);
std::unique_ptr<LPWSTR> command_line_args = std::make_unique<LPWSTR>(const_cast<LPWSTR>(wstr.c_str()));
/*LPWSTR command_line_args = new wchar_t[wstr.size() + 1];
copy(wstr.begin(), wstr.end(), command_line_args);
command_line_args[wstr.size()] = 0;*/
//Create a COPYDATASTRUCT to send the information
//cbData represents the size of the information we want to send.
//lpData represents the information we want to send.
//dwData is an ID defined by us(this is a type of ID different than WM_COPYDATA).
COPYDATASTRUCT data_to_send = { 0 };
data_to_send.dwData = 1;
data_to_send.cbData = sizeof(TCHAR) * (wcslen(*command_line_args.get()) + 1);
data_to_send.lpData = *command_line_args.get();
SendMessage(l_downloader_hwnd, WM_COPYDATA, 0, (LPARAM)&data_to_send);
return true;
}
return false;
}
bool execute_command(const wxString& command)
{
return wxExecute(command);
}
#endif
}
bool SlicerSend::get_instance_exists() const
{
return !EnumWindows(EnumWindowsProcSlicer, 0);
}
bool SlicerSend::send_path(const wxString& path) const
{
std::string escaped = escape_strings_cstyle({ "prusa-downloader", boost::nowide::narrow(path) });
return send_message_slicer(boost::nowide::widen(escaped));
}
bool SlicerSend::start_with_path(const wxString& path) const
{
// "C:\\Users\\User\\Downloads\\PrusaSlicer-2.4.2+win64-202204251110\\prusa-slicer.exe "
std::string escaped = escape_strings_cstyle({ boost::nowide::narrow(path) });
//return execute_command(boost::nowide::widen(escaped));
return execute_command("C:\\Users\\User\\Downloads\\PrusaSlicer-2.4.2+win64-202204251110\\prusa-slicer.exe " + boost::nowide::widen(escaped));
}
bool DownloaderSend::get_instance_exists() const
{
return !EnumWindows(EnumWindowsProcDownloader, 0);
}
bool DownloaderSend::send_url(const wxString& url) const
{
//std::string escaped = escape_strings_cstyle({ boost::nowide::narrow(url) });
return send_message_downloader(url);
}
}

View File

@ -0,0 +1,23 @@
#ifndef slic3r_InstanceSend_hpp_
#define slic3r_InstanceSend_hpp_
#include <boost/filesystem/path.hpp>
#include <wx/string.h>
namespace Downloader {
class SlicerSend
{
public:
bool get_instance_exists() const;
bool send_path(const wxString& path) const;
bool start_with_path(const wxString& path) const;
};
class DownloaderSend
{
public:
bool get_instance_exists() const;
bool send_url(const wxString& url) const;
};
}
#endif