From faa808b3851dcac7ae0ebddea4045b63832f174a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 11 Aug 2021 12:03:44 +0200 Subject: [PATCH 01/17] First draft of SendSystemInfoDialog --- src/libslic3r/Platform.cpp | 38 +++ src/libslic3r/Platform.hpp | 33 ++- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GUI_App.cpp | 4 + src/slic3r/GUI/OpenGLManager.cpp | 2 +- src/slic3r/GUI/OpenGLManager.hpp | 1 + src/slic3r/GUI/SendSystemInfoDialog.cpp | 301 ++++++++++++++++++++++++ src/slic3r/GUI/SendSystemInfoDialog.hpp | 39 +++ 8 files changed, 401 insertions(+), 19 deletions(-) create mode 100644 src/slic3r/GUI/SendSystemInfoDialog.cpp create mode 100644 src/slic3r/GUI/SendSystemInfoDialog.hpp diff --git a/src/libslic3r/Platform.cpp b/src/libslic3r/Platform.cpp index 4aa72c95f..338752112 100644 --- a/src/libslic3r/Platform.cpp +++ b/src/libslic3r/Platform.cpp @@ -105,4 +105,42 @@ PlatformFlavor platform_flavor() return s_platform_flavor; } + + +std::string platform_to_string(Platform platform) +{ + switch (platform) { + case Platform::Uninitialized: return "Unitialized"; + case Platform::Unknown : return "Unknown"; + case Platform::Windows : return "Windows"; + case Platform::OSX : return "OSX"; + case Platform::Linux : return "Linux"; + case Platform::BSDUnix : return "BSDUnix"; + } + assert(false); + return ""; +} + + + +std::string platform_flavor_to_string(PlatformFlavor pf) +{ + switch (pf) { + case PlatformFlavor::Uninitialized : return "Unitialized"; + case PlatformFlavor::Unknown : return "Unknown"; + case PlatformFlavor::Generic : return "Generic"; + case PlatformFlavor::GenericLinux : return "GenericLinux"; + case PlatformFlavor::LinuxOnChromium : return "LinuxOnChromium"; + case PlatformFlavor::WSL : return "WSL"; + case PlatformFlavor::WSL2 : return "WSL2"; + case PlatformFlavor::OpenBSD : return "OpenBSD"; + case PlatformFlavor::GenericOSX : return "GenericOSX"; + case PlatformFlavor::OSXOnX86 : return "OSXOnX86"; + case PlatformFlavor::OSXOnArm : return "OSXOnArm"; + } + assert(false); + return ""; +} + + } // namespace Slic3r diff --git a/src/libslic3r/Platform.hpp b/src/libslic3r/Platform.hpp index 1b4d74c02..6db8ba880 100644 --- a/src/libslic3r/Platform.hpp +++ b/src/libslic3r/Platform.hpp @@ -1,6 +1,8 @@ #ifndef SLIC3R_Platform_HPP #define SLIC3R_Platform_HPP +#include + namespace Slic3r { enum class Platform @@ -16,24 +18,16 @@ enum class Platform enum class PlatformFlavor { Uninitialized, - Unknown, - // For Windows and OSX, until we need to be more specific. - Generic, - // For Platform::Linux - GenericLinux, - LinuxOnChromium, - // Microsoft's Windows on Linux (Linux kernel simulated on NTFS kernel) - WSL, - // Microsoft's Windows on Linux, version 2 (virtual machine) - WSL2, - // For Platform::BSDUnix - OpenBSD, - // For Platform::OSX - GenericOSX, - // For Apple's on Intel X86 CPU - OSXOnX86, - // For Apple's on Arm CPU - OSXOnArm, + Unknown, + Generic, // For Windows and OSX, until we need to be more specific. + GenericLinux, // For Platform::Linux + LinuxOnChromium, // For Platform::Linux + WSL, // Microsoft's Windows on Linux (Linux kernel simulated on NTFS kernel) + WSL2, // Microsoft's Windows on Linux, version 2 (virtual machine) + OpenBSD, // For Platform::BSDUnix + GenericOSX, // For Platform::OSX + OSXOnX86, // For Apple's on Intel X86 CPU + OSXOnArm, // For Apple's on Arm CPU }; // To be called on program start-up. @@ -42,6 +36,9 @@ void detect_platform(); Platform platform(); PlatformFlavor platform_flavor(); +std::string platform_to_string(Platform platform); +std::string platform_flavor_to_string(PlatformFlavor pf); + } // namespace Slic3r #endif // SLIC3R_Platform_HPP diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 29d1fae29..9a5252949 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -141,6 +141,8 @@ set(SLIC3R_GUI_SOURCES GUI/RammingChart.hpp GUI/RemovableDriveManager.cpp GUI/RemovableDriveManager.hpp + GUI/SendSystemInfoDialog.cpp + GUI/SendSystemInfoDialog.hpp GUI/BonjourDialog.cpp GUI/BonjourDialog.hpp GUI/ButtonsDescription.cpp diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 41f4c4b89..56f58a515 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -70,6 +70,7 @@ #include "SavePresetDialog.hpp" #include "PrintHostDialogs.hpp" #include "DesktopIntegrationDialog.hpp" +#include "SendSystemInfoDialog.hpp" #include "BitmapCache.hpp" #include "Notebook.hpp" @@ -668,6 +669,9 @@ void GUI_App::post_init() if (app_config->get("show_hints") == "1" && ! is_gcode_viewer()) plater_->get_notification_manager()->push_hint_notification(true); + // 'Send system info' dialog + show_send_system_info_dialog_if_needed(); + // The extra CallAfter() is needed because of Mac, where this is the only way // to popup a modal dialog on start without screwing combo boxes. // This is ugly but I honestly found no better way to do it. diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 91f1f1f0b..3baa8a4da 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -27,7 +27,7 @@ namespace Slic3r { namespace GUI { // A safe wrapper around glGetString to report a "N/A" string in case glGetString returns nullptr. -inline std::string gl_get_string_safe(GLenum param, const std::string& default_value) +std::string gl_get_string_safe(GLenum param, const std::string& default_value) { const char* value = (const char*)::glGetString(param); return std::string((value != nullptr) ? value : default_value); diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp index ca9452db6..716256e40 100644 --- a/src/slic3r/GUI/OpenGLManager.hpp +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -10,6 +10,7 @@ class wxGLContext; namespace Slic3r { namespace GUI { + class OpenGLManager { public: diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp new file mode 100644 index 000000000..435c82e73 --- /dev/null +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -0,0 +1,301 @@ +#include "SendSystemInfoDialog.hpp" + +#include "libslic3r/AppConfig.hpp" +#include "libslic3r/Platform.hpp" +#include "libslic3r/Utils.hpp" + +#include "slic3r/GUI/format.hpp" +#include "slic3r/Utils/Http.hpp" + +#include "GUI_App.hpp" +#include "I18N.hpp" +#include "MainFrame.hpp" +#include "OpenGLManager.hpp" + +#include +#include + +#include "GL/glew.h" + +#include +#include + +namespace Slic3r { +namespace GUI { + +// Declaration of a free function defined in OpenGLManager.cpp: +std::string gl_get_string_safe(GLenum param, const std::string& default_value); + + +// Read a string formatted as SLIC3R_VERSION (e.g. "2.4.0-alpha1") and get major and +// minor versions and alpha/beta flags. +static void extract_major_minor(std::string version, + int& major, int& minor, + bool* is_alpha = nullptr, bool* is_beta = nullptr) +{ + if (is_alpha) + *is_alpha = version.find("alpha") != std::string::npos; + if (is_beta) + *is_beta = version.find("beta") != std::string::npos; + if (std::count(version.begin(), version.end(), '.') != 2) + return; + std::replace(version.begin(), version.end(), '.', ' '); + std::istringstream ss{version}; + ss >> major >> minor; +} + + + +// Last version where the info was sent / dialog dismissed is saved in appconfig. +// Only show the dialog when this info is not found (e.g. fresh install) or when +// current version is newer. Only major and minor versions are compared. +static bool should_dialog_be_shown() +{ + std::string last_sent_version = wxGetApp().app_config->get("system_info_sent_version"); + + int last_sent_major = 0; + int last_sent_minor = 0; + int current_major = 0; + int current_minor = 0; + bool alpha = false; + bool beta = false; + extract_major_minor(SLIC3R_VERSION, current_major, current_minor, &alpha, &beta); + extract_major_minor(last_sent_version, last_sent_major, last_sent_minor); + + + if (current_major == 0 // This should never happen. + /*|| alpha + || beta*/) + return false; + + return ((current_major > last_sent_major) + || (current_major == last_sent_major && current_minor > last_sent_minor )); +} + + + +static void send_info(const std::string& data) +{ + std::cout << data << std::endl; +} + + +// Following function saves current PrusaSlicer version into app config. +// It will be later used to decide whether to open the dialog or not. +static void save_version() +{ + wxGetApp().app_config->set("system_info_sent_version", SLIC3R_VERSION); +} + + + +// Following function generates one string that will be shown in the preview +// and later sent if confirmed by the user. +static std::string generate_system_info_json() +{ + // Calculate hash of datadir path so it is possible to identify duplicates. + // The result is mod 10000 so most of the information is lost and it is + // not possible to unhash the datadir (which usually contains username). + // It is more than enough to help identify duplicate entries. + size_t datadir_hash = std::hash{}(std::string(wxGetUserId().ToUTF8().data())) % 10000; + + // Get system language. + std::string sys_language = "Unknown"; + const wxLanguage lang_system = wxLanguage(wxLocale::GetSystemLanguage()); + if (lang_system != wxLANGUAGE_UNKNOWN) + sys_language = wxLocale::GetLanguageInfo(lang_system)->CanonicalName.ToUTF8().data(); + + // Build a property tree with all the information. + namespace pt = boost::property_tree; + + pt::ptree root; + root.put("PrusaSlicerVersion", SLIC3R_VERSION); + root.put("BuildID", SLIC3R_BUILD_ID); + root.put("UsernameHash", datadir_hash); + root.put("Platform", platform_to_string(platform())); + root.put("PlatformFlavor", platform_flavor_to_string(platform_flavor())); + root.put("SystemLanguage", sys_language); + root.put("TranslationLanguage: ", wxGetApp().app_config->get("translation_language")); + + pt::ptree hw_node; + hw_node.put("ArchName", wxPlatformInfo::Get().GetArchName()); + hw_node.put("RAM_MB", size_t(Slic3r::total_physical_memory()/1000000)); + root.add_child("Hardware", hw_node); + + pt::ptree opengl_node; + opengl_node.put("Version", OpenGLManager::get_gl_info().get_version()); + opengl_node.put("GLSLVersion", OpenGLManager::get_gl_info().get_glsl_version()); + opengl_node.put("Vendor", OpenGLManager::get_gl_info().get_vendor()); + opengl_node.put("Renderer", OpenGLManager::get_gl_info().get_renderer()); + // Generate list of OpenGL extensions: + std::string extensions_str = gl_get_string_safe(GL_EXTENSIONS, ""); + std::vector extensions_list; + boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off); + pt::ptree extensions_node; + for (const std::string& s : extensions_list) { + if (s.empty()) + continue; + pt::ptree ext_node; // Create an unnamed node containing the value + ext_node.put("", s); + extensions_node.push_back(std::make_pair("", ext_node)); // Add this node to the list. + } + opengl_node.add_child("Extensions", extensions_node); + root.add_child("OpenGL", opengl_node); + + // Serialize the tree into JSON and return it. + std::stringstream ss; + pt::write_json(ss, root); + return ss.str(); + + //std::cout << wxPlatformInfo::Get().GetOperatingSystemFamilyName() << std::endl; // Unix + //std::cout << wxPlatformInfo::Get().GetOperatingSystemDescription() << std::endl; // Linux 4.15.0-142-generic x86_64 + // ? CPU, GPU, UNKNOWN, wxWidgets ??? + // tiskárny? budou už nainstalované? +} + + + +SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) + : m_system_info_json{generate_system_info_json()}, + GUI::DPIDialog(parent, wxID_ANY, _L("Send system info"), wxDefaultPosition, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) +{ + int version_major = 0; + int version_minor = 0; + bool is_alpha = false; + bool is_beta = false; + extract_major_minor(SLIC3R_VERSION, version_major, version_minor, &is_alpha, &is_beta); + std::string app_name = std::string(SLIC3R_APP_NAME) + " " + std::to_string(version_major) + + "." + std::to_string(version_minor) + " " + + (is_alpha ? "Alpha" : is_beta ? "Beta" : ""); + + const int em = GUI::wxGetApp().em_unit(); + m_min_width = MIN_WIDTH * em; + m_min_height = MIN_HEIGHT * em; + + + const wxFont& mono_font = GUI::wxGetApp().code_font(); + + auto *panel = new wxPanel(this); + auto *vsizer = new wxBoxSizer(wxVERTICAL); + panel->SetSizer(vsizer); + + /*auto *topsizer = new wxBoxSizer(wxVERTICAL); + topsizer->Add(panel, 1, wxEXPAND | wxALL, DIALOG_MARGIN); + SetMinSize(wxSize(m_min_width, m_min_height)); + SetSizerAndFit(topsizer); +*/ + std::string filename(__FILE__); + size_t last_slash_idx = filename.find_last_of("/"); + if (last_slash_idx != std::string::npos) + filename = filename.substr(last_slash_idx+1); + auto* text = new wxStaticText(panel, wxID_ANY, wxEmptyString); + text->SetLabelMarkup( + GUI::format_wxstr(_L("This is the first time you are running %1%. We would like to " + "ask you to send some of your system information to us. This will only " + "happen once and we will not ask you to do this again (only after you " + "upgrade to the next version)."), app_name ) + + "\n\n" + + "" + _L("Why is it needed") + "" + + "\n" + + _L("If we know your hardware, operating system, etc., it will greatly help us " + "in development, prioritization and possible deprecation of features that " + "are no more needed (for example legacy OpenGL support). This will help " + "us to focus our effort more efficiently and spend time on features that " + "are needed the most.") + + "\n\n" + + "" + _L("Is it safe?") + "\n" + + GUI::format_wxstr( + _L("We do not send any personal information nor anything that would allow us " + "to identify you later. To detect duplicate entries, a number derived " + "from your username is sent, but it cannot be used to recover the username. " + "Apart from that, only general data about your OS, hardware and OpenGL " + "installation are sent. PrusaSlicer is open source, if you want to " + "inspect the code actually performing the communication, see %1%."), + std::string("") + filename + "") + + "\n\n" + + "" + _L("Verbatim data that will be sent:") + ""); + vsizer->Add(text, 1, wxEXPAND); + + + auto* txt_json = new wxTextCtrl(panel, wxID_ANY, m_system_info_json, + wxDefaultPosition, wxDefaultSize, + wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); + txt_json->SetFont(mono_font); + txt_json->ShowPosition(0); + vsizer->Add(txt_json, 0, wxEXPAND, SPACING); + + auto* ask_later_button = new wxButton(panel, wxID_ANY, _L("Ask me next time")); + auto* dont_send_button = new wxButton(panel, wxID_ANY, _L("Do not send anything")); + auto* send_button = new wxButton(panel, wxID_ANY, _L("Send system info")); + + auto* hsizer = new wxBoxSizer(wxHORIZONTAL); + hsizer->Add(ask_later_button); + hsizer->AddSpacer(em); + hsizer->Add(dont_send_button); + hsizer->AddSpacer(em); + hsizer->Add(send_button); + vsizer->Add(hsizer, 0, wxALIGN_CENTER_HORIZONTAL, 10); + + const auto size = GetSize(); + SetSize(std::max(size.GetWidth(), m_min_width), + std::max(size.GetHeight(), m_min_height)); + Layout(); + + send_button->Bind(wxEVT_BUTTON, [this](const wxEvent&) + { + send_info(m_system_info_json); + save_version(); + EndModal(0); + }); + dont_send_button->Bind(wxEVT_BUTTON, [this](const wxEvent&) + { + save_version(); + EndModal(0); + }); + ask_later_button->Bind(wxEVT_BUTTON, [this](const wxEvent&) { EndModal(0); }); +} + + + +void SendSystemInfoDialog::on_dpi_changed(const wxRect&) +{ + /*const int& em = em_unit(); + + msw_buttons_rescale(this, em, { p->btn_close->GetId(), + p->btn_rescan->GetId(), + p->btn_flash->GetId(), + p->hex_picker->GetPickerCtrl()->GetId() + }); + + p->min_width = MIN_WIDTH * em; + p->min_height = MIN_HEIGHT * em; + p->min_height_expanded = MIN_HEIGHT_EXPANDED * em; + + const int min_height = p->spoiler->IsExpanded() ? p->min_height_expanded : p->min_height; + SetMinSize(wxSize(p->min_width, min_height)); + Fit(); + + Refresh();*/ +} + + + +void show_send_system_info_dialog_if_needed() +{ + if (wxGetApp().is_gcode_viewer() || ! should_dialog_be_shown()) + return; + + SendSystemInfoDialog dlg(wxGetApp().mainframe); + dlg.ShowModal(); +} + + +} // namespace GUI +} // namespace Slic3r + + + + + + diff --git a/src/slic3r/GUI/SendSystemInfoDialog.hpp b/src/slic3r/GUI/SendSystemInfoDialog.hpp new file mode 100644 index 000000000..54fdbf986 --- /dev/null +++ b/src/slic3r/GUI/SendSystemInfoDialog.hpp @@ -0,0 +1,39 @@ +#ifndef slic3r_SendSystemInfoDialog_hpp_ +#define slic3r_SendSystemInfoDialog_hpp_ + +#include "GUI_Utils.hpp" + +#include + +namespace Slic3r { + +namespace GUI { + +class SendSystemInfoDialog : public DPIDialog +{ + enum { + DIALOG_MARGIN = 15, + SPACING = 10, + MIN_WIDTH = 60, + MIN_HEIGHT = 40, + MIN_HEIGHT_EXPANDED = 40, + }; + +public: + SendSystemInfoDialog(wxWindow* parent); + +private: + const std::string m_system_info_json; + int m_min_width; + int m_min_height; + + void on_dpi_changed(const wxRect&) override; +}; + +void show_send_system_info_dialog_if_needed(); + + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_SendSystemInfoDialog_hpp_ From d6222cf8a8b1c788394659c63f7ff0fdcf0f2c2f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 10 Sep 2021 10:15:20 +0200 Subject: [PATCH 02/17] SendSystemInfoDialog: Layout adjustments --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 58 +++++++++++++++++-------- src/slic3r/GUI/SendSystemInfoDialog.hpp | 2 +- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index 435c82e73..4c2c9f44e 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -157,8 +157,8 @@ static std::string generate_system_info_json() SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) : m_system_info_json{generate_system_info_json()}, - GUI::DPIDialog(parent, wxID_ANY, _L("Send system info"), wxDefaultPosition, wxDefaultSize, - wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) + GUI::DPIDialog(parent, wxID_ANY, _L("Send system info"), wxDefaultPosition, /*wxDefaultSize*/wxSize(MIN_WIDTH * GUI::wxGetApp().em_unit(), MIN_HEIGHT * GUI::wxGetApp().em_unit()), + wxDEFAULT_DIALOG_STYLE/*|wxRESIZE_BORDER*/) { int version_major = 0; int version_minor = 0; @@ -173,14 +173,22 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) m_min_width = MIN_WIDTH * em; m_min_height = MIN_HEIGHT * em; - const wxFont& mono_font = GUI::wxGetApp().code_font(); - auto *panel = new wxPanel(this); - auto *vsizer = new wxBoxSizer(wxVERTICAL); - panel->SetSizer(vsizer); + auto *topSizer = new wxBoxSizer(wxVERTICAL); - /*auto *topsizer = new wxBoxSizer(wxVERTICAL); +// topSizer->SetMinSize(wxSize(-1, m_min_height)); + //auto *panel = new wxPanel(this); + auto *vsizer = new wxBoxSizer(wxVERTICAL); +// panel->SetSizer(vsizer); + + wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(2, 1, 1, 0); + grid_sizer->SetFlexibleDirection(wxBOTH); + grid_sizer->AddGrowableCol(0, 1); + grid_sizer->AddGrowableRow(0, 1); + grid_sizer->AddGrowableRow(1, 1); + +/* auto *topsizer = new wxBoxSizer(wxVERTICAL); topsizer->Add(panel, 1, wxEXPAND | wxALL, DIALOG_MARGIN); SetMinSize(wxSize(m_min_width, m_min_height)); SetSizerAndFit(topsizer); @@ -189,7 +197,7 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) size_t last_slash_idx = filename.find_last_of("/"); if (last_slash_idx != std::string::npos) filename = filename.substr(last_slash_idx+1); - auto* text = new wxStaticText(panel, wxID_ANY, wxEmptyString); + auto* text = new wxStaticText(/*panel*/this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(m_min_width, -1)/**/); text->SetLabelMarkup( GUI::format_wxstr(_L("This is the first time you are running %1%. We would like to " "ask you to send some of your system information to us. This will only " @@ -214,20 +222,21 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) "inspect the code actually performing the communication, see %1%."), std::string("") + filename + "") + "\n\n" - + "" + _L("Verbatim data that will be sent:") + ""); - vsizer->Add(text, 1, wxEXPAND); + + "" + _L("Verbatim data that will be sent:") + "" + + "\n\n"); + vsizer/*grid_sizer*/->Add(text/*, 0, wxEXPAND*/); - auto* txt_json = new wxTextCtrl(panel, wxID_ANY, m_system_info_json, + auto* txt_json = new wxTextCtrl(/*panel*/this, wxID_ANY, m_system_info_json, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); txt_json->SetFont(mono_font); txt_json->ShowPosition(0); - vsizer->Add(txt_json, 0, wxEXPAND, SPACING); + vsizer/*grid_sizer*/->Add(txt_json, 1, wxEXPAND, SPACING); - auto* ask_later_button = new wxButton(panel, wxID_ANY, _L("Ask me next time")); - auto* dont_send_button = new wxButton(panel, wxID_ANY, _L("Do not send anything")); - auto* send_button = new wxButton(panel, wxID_ANY, _L("Send system info")); + auto* ask_later_button = new wxButton(/*panel*/this, wxID_ANY, _L("Ask me next time")); + auto* dont_send_button = new wxButton(/*panel*/this, wxID_ANY, _L("Do not send anything")); + auto* send_button = new wxButton(/*panel*/this, wxID_ANY, _L("Send system info")); auto* hsizer = new wxBoxSizer(wxHORIZONTAL); hsizer->Add(ask_later_button); @@ -235,12 +244,27 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) hsizer->Add(dont_send_button); hsizer->AddSpacer(em); hsizer->Add(send_button); - vsizer->Add(hsizer, 0, wxALIGN_CENTER_HORIZONTAL, 10); + +// vsizer->Add(grid_sizer, 1, wxEXPAND); + vsizer->Add(hsizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 10); + + //panel->SetSizer(vsizer); + //vsizer->SetSizeHints(panel); + + topSizer->Add(vsizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 10); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); + + +#ifdef _WIN32 + wxGetApp().UpdateDlgDarkUI(this); +#endif const auto size = GetSize(); SetSize(std::max(size.GetWidth(), m_min_width), std::max(size.GetHeight(), m_min_height)); - Layout(); +// Layout(); send_button->Bind(wxEVT_BUTTON, [this](const wxEvent&) { diff --git a/src/slic3r/GUI/SendSystemInfoDialog.hpp b/src/slic3r/GUI/SendSystemInfoDialog.hpp index 54fdbf986..3bf7ce36c 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.hpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.hpp @@ -15,7 +15,7 @@ class SendSystemInfoDialog : public DPIDialog DIALOG_MARGIN = 15, SPACING = 10, MIN_WIDTH = 60, - MIN_HEIGHT = 40, + MIN_HEIGHT = 60,//40, MIN_HEIGHT_EXPANDED = 40, }; From e8af0d6d0f4b62b63c2c86d4d790f3f5a90eb6ad Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 10 Sep 2021 13:09:55 +0200 Subject: [PATCH 03/17] SendSystemInfoDialog: Use HTML for formatting --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 198 +++++++++++++----------- src/slic3r/GUI/SendSystemInfoDialog.hpp | 11 +- 2 files changed, 110 insertions(+), 99 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index 4c2c9f44e..2aeb765e1 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -10,6 +10,7 @@ #include "GUI_App.hpp" #include "I18N.hpp" #include "MainFrame.hpp" +#include "MsgDialog.hpp" #include "OpenGLManager.hpp" #include @@ -17,7 +18,9 @@ #include "GL/glew.h" -#include +#include +#include +#include #include namespace Slic3r { @@ -27,6 +30,37 @@ namespace GUI { std::string gl_get_string_safe(GLenum param, const std::string& default_value); + +class ShowJsonDialog : public wxDialog +{ +public: + ShowJsonDialog(wxWindow* parent, const wxString& json, const wxSize& size) + : wxDialog(parent, wxID_ANY, _L("Data to send"), wxDefaultPosition, size, wxCAPTION) + { + auto* text = new wxTextCtrl(this, wxID_ANY, json, + wxDefaultPosition, wxDefaultSize, + wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); + text->SetFont(wxGetApp().code_font()); + text->ShowPosition(0); + + auto* btn = new wxButton(this, wxID_CANCEL, _L("Close")); + auto* vsizer = new wxBoxSizer(wxVERTICAL); + auto *top_sizer = new wxBoxSizer(wxVERTICAL); + vsizer->Add(text, 1, wxEXPAND); + vsizer->AddSpacer(5); + vsizer->Add(btn, 0, wxALIGN_CENTER_HORIZONTAL); + top_sizer->Add(vsizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT | wxBOTTOM, 10); + SetSizer(top_sizer); + #ifdef _WIN32 + wxGetApp().UpdateDlgDarkUI(this); + #endif + + } +}; + + + + // Read a string formatted as SLIC3R_VERSION (e.g. "2.4.0-alpha1") and get major and // minor versions and alpha/beta flags. static void extract_major_minor(std::string version, @@ -157,9 +191,10 @@ static std::string generate_system_info_json() SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) : m_system_info_json{generate_system_info_json()}, - GUI::DPIDialog(parent, wxID_ANY, _L("Send system info"), wxDefaultPosition, /*wxDefaultSize*/wxSize(MIN_WIDTH * GUI::wxGetApp().em_unit(), MIN_HEIGHT * GUI::wxGetApp().em_unit()), - wxDEFAULT_DIALOG_STYLE/*|wxRESIZE_BORDER*/) + GUI::DPIDialog(parent, wxID_ANY, _L("Send system info"), wxDefaultPosition, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE) { + // Get current PrusaSliver version info. int version_major = 0; int version_minor = 0; bool is_alpha = false; @@ -169,88 +204,76 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) + "." + std::to_string(version_minor) + " " + (is_alpha ? "Alpha" : is_beta ? "Beta" : ""); - const int em = GUI::wxGetApp().em_unit(); - m_min_width = MIN_WIDTH * em; - m_min_height = MIN_HEIGHT * em; - - const wxFont& mono_font = GUI::wxGetApp().code_font(); - - auto *topSizer = new wxBoxSizer(wxVERTICAL); - -// topSizer->SetMinSize(wxSize(-1, m_min_height)); - //auto *panel = new wxPanel(this); - auto *vsizer = new wxBoxSizer(wxVERTICAL); -// panel->SetSizer(vsizer); - - wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(2, 1, 1, 0); - grid_sizer->SetFlexibleDirection(wxBOTH); - grid_sizer->AddGrowableCol(0, 1); - grid_sizer->AddGrowableRow(0, 1); - grid_sizer->AddGrowableRow(1, 1); - -/* auto *topsizer = new wxBoxSizer(wxVERTICAL); - topsizer->Add(panel, 1, wxEXPAND | wxALL, DIALOG_MARGIN); - SetMinSize(wxSize(m_min_width, m_min_height)); - SetSizerAndFit(topsizer); -*/ + // Get current source file name. std::string filename(__FILE__); - size_t last_slash_idx = filename.find_last_of("/"); + size_t last_slash_idx = filename.find_last_of("/\\"); if (last_slash_idx != std::string::npos) filename = filename.substr(last_slash_idx+1); - auto* text = new wxStaticText(/*panel*/this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(m_min_width, -1)/**/); - text->SetLabelMarkup( - GUI::format_wxstr(_L("This is the first time you are running %1%. We would like to " + + // Set dialog background color, fonts, etc. + SetFont(wxGetApp().normal_font()); + wxColour bgr_clr = wxGetApp().get_window_default_clr(); + SetBackgroundColour(bgr_clr); + const auto text_clr = wxGetApp().get_label_clr_default(); + auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); + auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); + + + auto *topSizer = new wxBoxSizer(wxVERTICAL); + auto *vsizer = new wxBoxSizer(wxVERTICAL); + + wxString text0 = GUI::format_wxstr(_L("This is the first time you are running %1%. We would like to " "ask you to send some of your system information to us. This will only " "happen once and we will not ask you to do this again (only after you " - "upgrade to the next version)."), app_name ) - + "\n\n" - + "" + _L("Why is it needed") + "" - + "\n" - + _L("If we know your hardware, operating system, etc., it will greatly help us " - "in development, prioritization and possible deprecation of features that " - "are no more needed (for example legacy OpenGL support). This will help " - "us to focus our effort more efficiently and spend time on features that " - "are needed the most.") - + "\n\n" - + "" + _L("Is it safe?") + "\n" - + GUI::format_wxstr( - _L("We do not send any personal information nor anything that would allow us " - "to identify you later. To detect duplicate entries, a number derived " - "from your username is sent, but it cannot be used to recover the username. " - "Apart from that, only general data about your OS, hardware and OpenGL " - "installation are sent. PrusaSlicer is open source, if you want to " - "inspect the code actually performing the communication, see %1%."), - std::string("") + filename + "") - + "\n\n" - + "" + _L("Verbatim data that will be sent:") + "" - + "\n\n"); - vsizer/*grid_sizer*/->Add(text/*, 0, wxEXPAND*/); + "upgrade to the next version)."), app_name ); + wxString label1 = _L("Why is it needed"); + wxString text1 = _L("If we know your hardware, operating system, etc., it will greatly help us " + "in development, prioritization and possible deprecation of features that " + "are no more needed (for example legacy OpenGL support). This will help " + "us to focus our effort more efficiently and spend time on features that " + "are needed the most."); + wxString label2 = _L("Is it safe?"); + wxString text2 = GUI::format_wxstr( + _L("We do not send any personal information nor anything that would allow us " + "to identify you later. To detect duplicate entries, a number derived " + "from your username is sent, but it cannot be used to recover the username. " + "Apart from that, only general data about your OS, hardware and OpenGL " + "installation are sent. PrusaSlicer is open source, if you want to " + "inspect the code actually performing the communication, see %1%."), + std::string("") + filename + ""); + wxString label3 = _L("Show verbatim data that will be sent"); + auto* html_window = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER); + wxString html = GUI::format_wxstr( + "" + + text0 + "

" + + "" + label1 + "
" + + text1 + "

" + + "" + label2 + "
" + + text2 + "

" + + "" + label3 + "
" + + "
", bgr_clr_str, text_clr_str); + html_window->SetPage(html); + html_window->Bind(wxEVT_HTML_LINK_CLICKED, [this](wxHtmlLinkEvent &evt) { + ShowJsonDialog dlg(this, m_system_info_json, GetSize().Scale(0.9, 0.7)); + dlg.ShowModal(); + }); - auto* txt_json = new wxTextCtrl(/*panel*/this, wxID_ANY, m_system_info_json, - wxDefaultPosition, wxDefaultSize, - wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); - txt_json->SetFont(mono_font); - txt_json->ShowPosition(0); - vsizer/*grid_sizer*/->Add(txt_json, 1, wxEXPAND, SPACING); + vsizer->Add(html_window, 1, wxEXPAND); - auto* ask_later_button = new wxButton(/*panel*/this, wxID_ANY, _L("Ask me next time")); - auto* dont_send_button = new wxButton(/*panel*/this, wxID_ANY, _L("Do not send anything")); - auto* send_button = new wxButton(/*panel*/this, wxID_ANY, _L("Send system info")); + m_btn_ask_later = new wxButton(this, wxID_ANY, _L("Ask me next time")); + m_btn_dont_send = new wxButton(this, wxID_ANY, _L("Do not send anything")); + m_btn_send = new wxButton(this, wxID_ANY, _L("Send system info")); auto* hsizer = new wxBoxSizer(wxHORIZONTAL); - hsizer->Add(ask_later_button); + const int em = GUI::wxGetApp().em_unit(); + hsizer->Add(m_btn_ask_later); hsizer->AddSpacer(em); - hsizer->Add(dont_send_button); + hsizer->Add(m_btn_dont_send); hsizer->AddSpacer(em); - hsizer->Add(send_button); + hsizer->Add(m_btn_send); -// vsizer->Add(grid_sizer, 1, wxEXPAND); vsizer->Add(hsizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 10); - - //panel->SetSizer(vsizer); - //vsizer->SetSizeHints(panel); - topSizer->Add(vsizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 10); SetSizer(topSizer); @@ -262,45 +285,34 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) #endif const auto size = GetSize(); - SetSize(std::max(size.GetWidth(), m_min_width), - std::max(size.GetHeight(), m_min_height)); -// Layout(); + SetSize(std::max(size.GetWidth(), MIN_WIDTH * em), + std::max(size.GetHeight(), MIN_HEIGHT * em)); - send_button->Bind(wxEVT_BUTTON, [this](const wxEvent&) + m_btn_send->Bind(wxEVT_BUTTON, [this](const wxEvent&) { send_info(m_system_info_json); save_version(); EndModal(0); }); - dont_send_button->Bind(wxEVT_BUTTON, [this](const wxEvent&) + m_btn_dont_send->Bind(wxEVT_BUTTON, [this](const wxEvent&) { save_version(); EndModal(0); }); - ask_later_button->Bind(wxEVT_BUTTON, [this](const wxEvent&) { EndModal(0); }); + m_btn_ask_later->Bind(wxEVT_BUTTON, [this](const wxEvent&) { EndModal(0); }); } void SendSystemInfoDialog::on_dpi_changed(const wxRect&) { - /*const int& em = em_unit(); - - msw_buttons_rescale(this, em, { p->btn_close->GetId(), - p->btn_rescan->GetId(), - p->btn_flash->GetId(), - p->hex_picker->GetPickerCtrl()->GetId() - }); - - p->min_width = MIN_WIDTH * em; - p->min_height = MIN_HEIGHT * em; - p->min_height_expanded = MIN_HEIGHT_EXPANDED * em; - - const int min_height = p->spoiler->IsExpanded() ? p->min_height_expanded : p->min_height; - SetMinSize(wxSize(p->min_width, min_height)); + const int& em = em_unit(); + msw_buttons_rescale(this, em, { m_btn_send->GetId(), + m_btn_dont_send->GetId(), + m_btn_ask_later->GetId() }); + SetMinSize(wxSize(MIN_WIDTH * em, MIN_HEIGHT * em)); Fit(); - - Refresh();*/ + Refresh(); } diff --git a/src/slic3r/GUI/SendSystemInfoDialog.hpp b/src/slic3r/GUI/SendSystemInfoDialog.hpp index 3bf7ce36c..b0877c868 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.hpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.hpp @@ -12,11 +12,8 @@ namespace GUI { class SendSystemInfoDialog : public DPIDialog { enum { - DIALOG_MARGIN = 15, - SPACING = 10, MIN_WIDTH = 60, - MIN_HEIGHT = 60,//40, - MIN_HEIGHT_EXPANDED = 40, + MIN_HEIGHT = 40 }; public: @@ -24,12 +21,14 @@ public: private: const std::string m_system_info_json; - int m_min_width; - int m_min_height; + wxButton* m_btn_send; + wxButton* m_btn_dont_send; + wxButton* m_btn_ask_later; void on_dpi_changed(const wxRect&) override; }; + void show_send_system_info_dialog_if_needed(); From 6a819dad89903861e8b77fdce70aeeb1f9f001f2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 13 Sep 2021 09:00:20 +0200 Subject: [PATCH 04/17] SendSystemInfoDialog: Actual sending of the info --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 220 ++++++++++++++++-------- src/slic3r/GUI/SendSystemInfoDialog.hpp | 20 --- 2 files changed, 144 insertions(+), 96 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index 2aeb765e1..1e0ed69d2 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -13,24 +13,76 @@ #include "MsgDialog.hpp" #include "OpenGLManager.hpp" -#include #include +#include #include "GL/glew.h" #include -#include -#include +#include +#include #include +#include +#include + namespace Slic3r { namespace GUI { + + // Declaration of a free function defined in OpenGLManager.cpp: std::string gl_get_string_safe(GLenum param, const std::string& default_value); +// A dialog with the information text and buttons send/dont send/ask later. +class SendSystemInfoDialog : public DPIDialog +{ + enum { + MIN_WIDTH = 60, + MIN_HEIGHT = 40 + }; +public: + SendSystemInfoDialog(wxWindow* parent); + +private: + bool send_info(); + const std::string m_system_info_json; + wxButton* m_btn_send; + wxButton* m_btn_dont_send; + wxButton* m_btn_ask_later; + + void on_dpi_changed(const wxRect&) override; +}; + + + +// A dialog to show when the upload is in progress (with a Cancel button). +class SendSystemInfoProgressDialog : public wxDialog +{ +public: + SendSystemInfoProgressDialog(wxWindow* parent, const wxString& message) + : wxDialog(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxCAPTION) + { + auto* text = new wxStaticText(this, wxID_ANY, message, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); + auto* btn = new wxButton(this, wxID_CANCEL, _L("Cancel")); + auto* vsizer = new wxBoxSizer(wxVERTICAL); + auto *top_sizer = new wxBoxSizer(wxVERTICAL); + vsizer->Add(text, 1, wxEXPAND|wxALIGN_CENTER_HORIZONTAL); + vsizer->AddSpacer(5); + vsizer->Add(btn, 0, wxALIGN_CENTER_HORIZONTAL); + top_sizer->Add(vsizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT | wxBOTTOM, 10); + SetSizer(top_sizer); + #ifdef _WIN32 + wxGetApp().UpdateDlgDarkUI(this); + #endif + } +}; + + + +// A dialog with multiline read-only text control to show the JSON. class ShowJsonDialog : public wxDialog { public: @@ -54,71 +106,37 @@ public: #ifdef _WIN32 wxGetApp().UpdateDlgDarkUI(this); #endif - } }; - -// Read a string formatted as SLIC3R_VERSION (e.g. "2.4.0-alpha1") and get major and -// minor versions and alpha/beta flags. -static void extract_major_minor(std::string version, - int& major, int& minor, - bool* is_alpha = nullptr, bool* is_beta = nullptr) -{ - if (is_alpha) - *is_alpha = version.find("alpha") != std::string::npos; - if (is_beta) - *is_beta = version.find("beta") != std::string::npos; - if (std::count(version.begin(), version.end(), '.') != 2) - return; - std::replace(version.begin(), version.end(), '.', ' '); - std::istringstream ss{version}; - ss >> major >> minor; -} - - - // Last version where the info was sent / dialog dismissed is saved in appconfig. // Only show the dialog when this info is not found (e.g. fresh install) or when // current version is newer. Only major and minor versions are compared. static bool should_dialog_be_shown() { - std::string last_sent_version = wxGetApp().app_config->get("system_info_sent_version"); + std::string last_sent_version = wxGetApp().app_config->get("version_system_info_sent"); + Semver semver_current(SLIC3R_VERSION); + Semver semver_last_sent; + if (! last_sent_version.empty()) + semver_last_sent = Semver(last_sent_version); - int last_sent_major = 0; - int last_sent_minor = 0; - int current_major = 0; - int current_minor = 0; - bool alpha = false; - bool beta = false; - extract_major_minor(SLIC3R_VERSION, current_major, current_minor, &alpha, &beta); - extract_major_minor(last_sent_version, last_sent_major, last_sent_minor); + if (semver_current.prerelease() && std::string(semver_current.prerelease()) != "rc") + return false; // Only show in rcs / finals. - - if (current_major == 0 // This should never happen. - /*|| alpha - || beta*/) - return false; - - return ((current_major > last_sent_major) - || (current_major == last_sent_major && current_minor > last_sent_minor )); + // Show the dialog if current > last, but they differ in more than just patch. + return ((semver_current.maj() > semver_last_sent.maj()) + || (semver_current.maj() == semver_last_sent.maj() && semver_current.min() > semver_last_sent.min() )); } -static void send_info(const std::string& data) -{ - std::cout << data << std::endl; -} - - // Following function saves current PrusaSlicer version into app config. // It will be later used to decide whether to open the dialog or not. static void save_version() { - wxGetApp().app_config->set("system_info_sent_version", SLIC3R_VERSION); + wxGetApp().app_config->set("version_system_info_sent", std::string(SLIC3R_VERSION)); } @@ -142,19 +160,19 @@ static std::string generate_system_info_json() // Build a property tree with all the information. namespace pt = boost::property_tree; - pt::ptree root; - root.put("PrusaSlicerVersion", SLIC3R_VERSION); - root.put("BuildID", SLIC3R_BUILD_ID); - root.put("UsernameHash", datadir_hash); - root.put("Platform", platform_to_string(platform())); - root.put("PlatformFlavor", platform_flavor_to_string(platform_flavor())); - root.put("SystemLanguage", sys_language); - root.put("TranslationLanguage: ", wxGetApp().app_config->get("translation_language")); + pt::ptree data_node; + data_node.put("PrusaSlicerVersion", SLIC3R_VERSION); + data_node.put("BuildID", SLIC3R_BUILD_ID); + data_node.put("UsernameHash", datadir_hash); + data_node.put("Platform", platform_to_string(platform())); + data_node.put("PlatformFlavor", platform_flavor_to_string(platform_flavor())); + data_node.put("SystemLanguage", sys_language); + data_node.put("TranslationLanguage: ", wxGetApp().app_config->get("translation_language")); pt::ptree hw_node; hw_node.put("ArchName", wxPlatformInfo::Get().GetArchName()); hw_node.put("RAM_MB", size_t(Slic3r::total_physical_memory()/1000000)); - root.add_child("Hardware", hw_node); + data_node.add_child("Hardware", hw_node); pt::ptree opengl_node; opengl_node.put("Version", OpenGLManager::get_gl_info().get_version()); @@ -174,17 +192,21 @@ static std::string generate_system_info_json() extensions_node.push_back(std::make_pair("", ext_node)); // Add this node to the list. } opengl_node.add_child("Extensions", extensions_node); - root.add_child("OpenGL", opengl_node); + data_node.add_child("OpenGL", opengl_node); + + pt::ptree root; + root.add_child("data", data_node); // Serialize the tree into JSON and return it. std::stringstream ss; pt::write_json(ss, root); return ss.str(); + // FURTHER THINGS TO CONSIDER: //std::cout << wxPlatformInfo::Get().GetOperatingSystemFamilyName() << std::endl; // Unix //std::cout << wxPlatformInfo::Get().GetOperatingSystemDescription() << std::endl; // Linux 4.15.0-142-generic x86_64 // ? CPU, GPU, UNKNOWN, wxWidgets ??? - // tiskárny? budou už nainstalované? + // printers? will they be installed already? } @@ -195,14 +217,15 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) wxDEFAULT_DIALOG_STYLE) { // Get current PrusaSliver version info. - int version_major = 0; - int version_minor = 0; - bool is_alpha = false; - bool is_beta = false; - extract_major_minor(SLIC3R_VERSION, version_major, version_minor, &is_alpha, &is_beta); - std::string app_name = std::string(SLIC3R_APP_NAME) + " " + std::to_string(version_major) - + "." + std::to_string(version_minor) + " " - + (is_alpha ? "Alpha" : is_beta ? "Beta" : ""); + std::string app_name; + { + Semver semver(SLIC3R_VERSION); + bool is_alpha = std::string{semver.prerelease()}.find("alpha") != std::string::npos; + bool is_beta = std::string{semver.prerelease()}.find("beta") != std::string::npos; + app_name = std::string(SLIC3R_APP_NAME) + " " + std::to_string(semver.maj()) + + "." + std::to_string(semver.min()) + " " + + (is_alpha ? "Alpha" : is_beta ? "Beta" : ""); + } // Get current source file name. std::string filename(__FILE__); @@ -220,7 +243,7 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) auto *topSizer = new wxBoxSizer(wxVERTICAL); - auto *vsizer = new wxBoxSizer(wxVERTICAL); + auto *vsizer = new wxBoxSizer(wxVERTICAL); wxString text0 = GUI::format_wxstr(_L("This is the first time you are running %1%. We would like to " "ask you to send some of your system information to us. This will only " @@ -290,8 +313,8 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) m_btn_send->Bind(wxEVT_BUTTON, [this](const wxEvent&) { - send_info(m_system_info_json); - save_version(); + if (send_info()) + save_version(); EndModal(0); }); m_btn_dont_send->Bind(wxEVT_BUTTON, [this](const wxEvent&) @@ -317,6 +340,57 @@ void SendSystemInfoDialog::on_dpi_changed(const wxRect&) +// This actually sends the info. +bool SendSystemInfoDialog::send_info() +{ + std::atomic job_done = false; // Flag to communicate between threads. + struct Result { + enum { + Success, + Cancelled, + Error + } value; + wxString str; + } result; // No synchronization needed, UI thread reads only after worker is joined. + + auto send = [&job_done, &result](const std::string& data) { + const std::string url = "https://files.prusa3d.com/wp-json/v1/ps"; + Http http = Http::post(url); + http.set_post_body(data) + .on_complete([&result](std::string body, unsigned status) { + result = { Result::Success, _L("System info sent successfully. Thank you.") }; + }) + .on_error([&result](std::string body, std::string error, unsigned status) { + result = { Result::Error, GUI::format_wxstr(_L("Sending system info failed! Status: %1%"), status) }; + }) + .on_progress([&job_done, &result](Http::Progress, bool &cancel) { + if (job_done) // UI thread wants us to cancel. + cancel = true; + if (cancel) + result = { Result::Cancelled, _L("Sending system info was cancelled.") }; + }) + .perform_sync(); + job_done = true; // So that the dialog knows we are done. + }; + + std::thread sending_thread(send, m_system_info_json); + SendSystemInfoProgressDialog dlg(this, _L("Sending system info...")); + wxTimer timer(&dlg); // Periodically check the status of the other thread, close dialog when done. + dlg.Bind(wxEVT_TIMER, [&dlg, &job_done](wxTimerEvent&){ if (job_done) dlg.EndModal(0); }); + timer.Start(50); + dlg.ShowModal(); + // The dialog is closed, either by user, or by the now terminated worker thread. + job_done = true; // In case the user closed the dialog, let the other thread know + sending_thread.join(); // and wait until it terminates. + + InfoDialog info_dlg(wxGetApp().mainframe, wxEmptyString, result.str); + info_dlg.ShowModal(); + return result.value == Result::Success; +} + + + +// The only function callable from outside this unit. void show_send_system_info_dialog_if_needed() { if (wxGetApp().is_gcode_viewer() || ! should_dialog_be_shown()) @@ -329,9 +403,3 @@ void show_send_system_info_dialog_if_needed() } // namespace GUI } // namespace Slic3r - - - - - - diff --git a/src/slic3r/GUI/SendSystemInfoDialog.hpp b/src/slic3r/GUI/SendSystemInfoDialog.hpp index b0877c868..e7556b5ab 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.hpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.hpp @@ -9,26 +9,6 @@ namespace Slic3r { namespace GUI { -class SendSystemInfoDialog : public DPIDialog -{ - enum { - MIN_WIDTH = 60, - MIN_HEIGHT = 40 - }; - -public: - SendSystemInfoDialog(wxWindow* parent); - -private: - const std::string m_system_info_json; - wxButton* m_btn_send; - wxButton* m_btn_dont_send; - wxButton* m_btn_ask_later; - - void on_dpi_changed(const wxRect&) override; -}; - - void show_send_system_info_dialog_if_needed(); From b87f512b9e241aded71d88953222c21f73dc7b62 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Sep 2021 16:17:17 +0200 Subject: [PATCH 05/17] SendSystemInfo: do not close the dialog when failed/cancelled, set content-type to json --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index 1e0ed69d2..f4fb50fd5 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -313,9 +313,10 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) m_btn_send->Bind(wxEVT_BUTTON, [this](const wxEvent&) { - if (send_info()) + if (send_info()) { save_version(); - EndModal(0); + EndModal(0); + } }); m_btn_dont_send->Bind(wxEVT_BUTTON, [this](const wxEvent&) { @@ -356,7 +357,8 @@ bool SendSystemInfoDialog::send_info() auto send = [&job_done, &result](const std::string& data) { const std::string url = "https://files.prusa3d.com/wp-json/v1/ps"; Http http = Http::post(url); - http.set_post_body(data) + http.header("Content-Type", "application/json") + .set_post_body(data) .on_complete([&result](std::string body, unsigned status) { result = { Result::Success, _L("System info sent successfully. Thank you.") }; }) From af5abc8079bbb9b83de4179e7818d3e3ee12f6dc Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Sep 2021 16:18:04 +0200 Subject: [PATCH 06/17] REVERT LATER: Something for debugging --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index f4fb50fd5..139779eb8 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -48,7 +48,7 @@ public: private: bool send_info(); - const std::string m_system_info_json; + /*const */std::string m_system_info_json; // will be const, this is FOR DEBUGGING wxButton* m_btn_send; wxButton* m_btn_dont_send; wxButton* m_btn_ask_later; @@ -91,7 +91,9 @@ public: { auto* text = new wxTextCtrl(this, wxID_ANY, json, wxDefaultPosition, wxDefaultSize, - wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); + wxTE_MULTILINE + // | wxTE_READONLY // commented out for DEBUGGING ONLY + | wxTE_DONTWRAP); text->SetFont(wxGetApp().code_font()); text->ShowPosition(0); @@ -106,6 +108,15 @@ public: #ifdef _WIN32 wxGetApp().UpdateDlgDarkUI(this); #endif + + m_text = text; // DEBUGGING ONLY + } + + // DEBUGGING ONLY: + wxTextCtrl* m_text; + std::string get_data() const // debugging only + { + return m_text->GetValue().ToUTF8().data(); } }; @@ -116,6 +127,8 @@ public: // current version is newer. Only major and minor versions are compared. static bool should_dialog_be_shown() { + return true; // DEBUGGING ONLY + std::string last_sent_version = wxGetApp().app_config->get("version_system_info_sent"); Semver semver_current(SLIC3R_VERSION); Semver semver_last_sent; @@ -280,6 +293,7 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) html_window->Bind(wxEVT_HTML_LINK_CLICKED, [this](wxHtmlLinkEvent &evt) { ShowJsonDialog dlg(this, m_system_info_json, GetSize().Scale(0.9, 0.7)); dlg.ShowModal(); + m_system_info_json = dlg.get_data(); // DEBUGGING ONLY }); vsizer->Add(html_window, 1, wxEXPAND); @@ -361,6 +375,9 @@ bool SendSystemInfoDialog::send_info() .set_post_body(data) .on_complete([&result](std::string body, unsigned status) { result = { Result::Success, _L("System info sent successfully. Thank you.") }; + + // DEBUGGING ONLY: + std::cout << "Response: " << std::endl << body << std::endl << "-----------------" << std::endl; }) .on_error([&result](std::string body, std::string error, unsigned status) { result = { Result::Error, GUI::format_wxstr(_L("Sending system info failed! Status: %1%"), status) }; From bdc4c4cf3003251db26e6cc73888c7fcd8512e73 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 15 Sep 2021 16:09:05 +0200 Subject: [PATCH 07/17] Added some more info (wxWidgets, gtk, Linux distro, CPU info, monitors) --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 112 ++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index 139779eb8..0d5e0c369 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -14,10 +14,12 @@ #include "OpenGLManager.hpp" #include +#include #include #include "GL/glew.h" +#include #include #include #include @@ -87,7 +89,7 @@ class ShowJsonDialog : public wxDialog { public: ShowJsonDialog(wxWindow* parent, const wxString& json, const wxSize& size) - : wxDialog(parent, wxID_ANY, _L("Data to send"), wxDefaultPosition, size, wxCAPTION) + : wxDialog(parent, wxID_ANY, _L("Data to send"), wxDefaultPosition, size, wxCAPTION|wxRESIZE_BORDER) { auto* text = new wxTextCtrl(this, wxID_ANY, json, wxDefaultPosition, wxDefaultSize, @@ -154,14 +156,42 @@ static void save_version() +static std::map parse_lscpu_etc(const std::string& name, char delimiter) +{ + std::map out; + constexpr size_t max_len = 100; + char cline[max_len] = ""; + FILE* fp = popen(name.data(), "r"); + if (fp != NULL) { + while (fgets(cline, max_len, fp) != NULL) { + std::string line(cline); + line.erase(std::remove_if(line.begin(), line.end(), + [](char c) { return c=='\"' || c=='\r' || c=='\n'; }), + line.end()); + size_t pos = line.find(delimiter); + if (pos < line.size() - 1) { + std::string key = line.substr(0, pos); + std::string value = line.substr(pos+1); + boost::trim_all(key); // remove leading and trailing spaces + boost::trim_all(value); + out[key] = value; + } + } + pclose(fp); + } + return out; +} + + + // Following function generates one string that will be shown in the preview // and later sent if confirmed by the user. static std::string generate_system_info_json() { - // Calculate hash of datadir path so it is possible to identify duplicates. + // Calculate hash of username so it is possible to identify duplicates. // The result is mod 10000 so most of the information is lost and it is - // not possible to unhash the datadir (which usually contains username). - // It is more than enough to help identify duplicate entries. + // not possible to unhash the username. It is more than enough to help + // identify duplicate entries. size_t datadir_hash = std::hash{}(std::string(wxGetUserId().ToUTF8().data())) % 10000; // Get system language. @@ -179,12 +209,80 @@ static std::string generate_system_info_json() data_node.put("UsernameHash", datadir_hash); data_node.put("Platform", platform_to_string(platform())); data_node.put("PlatformFlavor", platform_flavor_to_string(platform_flavor())); + data_node.put("OSDescription", wxPlatformInfo::Get().GetOperatingSystemDescription().ToUTF8().data()); +#ifdef __linux__ + std::string distro_id = wxGetLinuxDistributionInfo().Id.ToUTF8().data(); // uses lsb-release + std::string distro_ver = wxGetLinuxDistributionInfo().Release.ToUTF8().data(); + if (distro_id.empty()) { // lsb-release probably not available + std::map dist_info = parse_lscpu_etc("cat /etc/*release", '='); + distro_id = dist_info["ID"]; + distro_ver = dist_info["VERSION_ID"]; + } + data_node.put("Linux_DistroID", distro_id); + data_node.put("Linux_DistroVer", distro_ver); + data_node.put("Linux_Wayland", wxGetEnv("WAYLAND_DISPLAY", nullptr)); +#endif + data_node.put("wxWidgets", wxVERSION_NUM_DOT_STRING); +#ifdef __WXGTK__ + data_node.put("GTK", + #if defined(__WXGTK2__) + 2 + #elif defined(__WXGTK3__) + 3 + #elif defined(__WXGTK4__) + 4 + #elif defined(__WXGTK5__) + 5 + #else + "Unknown" + #endif + ); +#endif // __WXGTK__ data_node.put("SystemLanguage", sys_language); data_node.put("TranslationLanguage: ", wxGetApp().app_config->get("translation_language")); pt::ptree hw_node; hw_node.put("ArchName", wxPlatformInfo::Get().GetArchName()); hw_node.put("RAM_MB", size_t(Slic3r::total_physical_memory()/1000000)); + + // Now get some CPU info: + pt::ptree cpu_node; +#ifdef _WIN32 + +#elif __APPLE__ + std::map sysctl = parse_lscpu_etc("sysctl -a", ':'); + cpu_node.put("CPU(s)", sysctl["machdep.cpu.core_count"]); + cpu_node.put("CPU_Model", sysctl["machdep.cpu.brand_string"]); + cpu_node.put("CPU_Vendor", sysctl["machdep.cpu.vendor"]); +#else + std::map lscpu = parse_lscpu_etc("lscpu", ':'); + cpu_node.put("Arch", lscpu["Architecture"]); + cpu_node.put("Cores", lscpu["CPU(s)"]); + cpu_node.put("Model", lscpu["Model name"]); + cpu_node.put("Vendor", lscpu["Vendor ID"]); +#endif + hw_node.add_child("CPU", cpu_node); + + pt::ptree monitors_node; + for (int i=0; i Date: Thu, 16 Sep 2021 14:48:24 +0200 Subject: [PATCH 08/17] SendSystemInfoDialog now opens through CallAfter, GL extensions are sorted --- src/slic3r/GUI/GUI_App.cpp | 7 ++++--- src/slic3r/GUI/SendSystemInfoDialog.cpp | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 56f58a515..e1fcc029a 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -669,9 +669,6 @@ void GUI_App::post_init() if (app_config->get("show_hints") == "1" && ! is_gcode_viewer()) plater_->get_notification_manager()->push_hint_notification(true); - // 'Send system info' dialog - show_send_system_info_dialog_if_needed(); - // The extra CallAfter() is needed because of Mac, where this is the only way // to popup a modal dialog on start without screwing combo boxes. // This is ugly but I honestly found no better way to do it. @@ -685,6 +682,10 @@ void GUI_App::post_init() }); } + // 'Send system info' dialog. Again, a CallAfter is needed on mac. + // Without it, GL extensions did not show. + CallAfter([] { show_send_system_info_dialog_if_needed(); }); + #ifdef _WIN32 // Sets window property to mainframe so other instances can indentify it. OtherInstanceMessageHandler::init_windows_properties(mainframe, m_instance_hash_int); diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index 0d5e0c369..eead1f781 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -294,6 +294,7 @@ static std::string generate_system_info_json() std::string extensions_str = gl_get_string_safe(GL_EXTENSIONS, ""); std::vector extensions_list; boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off); + std::sort(extensions_list.begin(), extensions_list.end()); pt::ptree extensions_node; for (const std::string& s : extensions_list) { if (s.empty()) From 4733e1726648b18093b47e8ef9f985df8d28a84b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 17 Sep 2021 14:01:16 +0200 Subject: [PATCH 09/17] SendSystemInfoDialog: Collect CPU info on Windows --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 60 +++++++++++++++++++++---- src/slic3r/GUI/SendSystemInfoDialog.hpp | 4 -- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index eead1f781..29459dd8d 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -8,6 +8,7 @@ #include "slic3r/Utils/Http.hpp" #include "GUI_App.hpp" +#include "GUI_Utils.hpp" #include "I18N.hpp" #include "MainFrame.hpp" #include "MsgDialog.hpp" @@ -28,6 +29,10 @@ #include #include +#ifdef _WIN32 + #include +#endif + namespace Slic3r { namespace GUI { @@ -156,6 +161,41 @@ static void save_version() +#ifdef _WIN32 +static std::map get_cpu_info_from_registry() +{ + std::map out; + + int idx = -1; + constexpr DWORD bufsize_ = 200; + DWORD bufsize = bufsize_; + char buf[bufsize_] = ""; + const std::string reg_dir = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\"; + std::string reg_path = reg_dir; + + // Look into that reg dir and possibly into subdirs called 0, 1, 2, etc. + // If the latter, count them. + + while (true) { + if (RegGetValueA(HKEY_LOCAL_MACHINE, reg_path.c_str(), "ProcessorNameString", + RRF_RT_REG_SZ, NULL, &buf, &bufsize) == ERROR_SUCCESS) { + out["Model"] = buf; + out["Cores"] = std::to_string(std::max(1, idx + 1)); + if (RegGetValueA(HKEY_LOCAL_MACHINE, reg_path.c_str(), + "VendorIdentifier", RRF_RT_REG_SZ, NULL, &buf, &bufsize) == ERROR_SUCCESS) + out["Vendor"] = buf; + } + else { + if (idx >= 0) + break; + } + ++idx; + reg_path = reg_dir + std::to_string(idx) + "\\"; + bufsize = bufsize_; + } + return out; +} +#else // Apple, Linux, BSD static std::map parse_lscpu_etc(const std::string& name, char delimiter) { std::map out; @@ -166,12 +206,12 @@ static std::map parse_lscpu_etc(const std::string& nam while (fgets(cline, max_len, fp) != NULL) { std::string line(cline); line.erase(std::remove_if(line.begin(), line.end(), - [](char c) { return c=='\"' || c=='\r' || c=='\n'; }), - line.end()); + [](char c) { return c == '\"' || c == '\r' || c == '\n'; }), + line.end()); size_t pos = line.find(delimiter); if (pos < line.size() - 1) { std::string key = line.substr(0, pos); - std::string value = line.substr(pos+1); + std::string value = line.substr(pos + 1); boost::trim_all(key); // remove leading and trailing spaces boost::trim_all(value); out[key] = value; @@ -181,6 +221,7 @@ static std::map parse_lscpu_etc(const std::string& nam } return out; } +#endif @@ -248,13 +289,16 @@ static std::string generate_system_info_json() // Now get some CPU info: pt::ptree cpu_node; #ifdef _WIN32 - + std::map cpu_info = get_cpu_info_from_registry(); + cpu_node.put("Cores", cpu_info["Cores"]); + cpu_node.put("Model", cpu_info["Model"]); + cpu_node.put("Vendor", cpu_info["Vendor"]); #elif __APPLE__ std::map sysctl = parse_lscpu_etc("sysctl -a", ':'); - cpu_node.put("CPU(s)", sysctl["machdep.cpu.core_count"]); - cpu_node.put("CPU_Model", sysctl["machdep.cpu.brand_string"]); - cpu_node.put("CPU_Vendor", sysctl["machdep.cpu.vendor"]); -#else + cpu_node.put("Cores", sysctl["machdep.cpu.core_count"]); + cpu_node.put("Model", sysctl["machdep.cpu.brand_string"]); + cpu_node.put("Vendor", sysctl["machdep.cpu.vendor"]); +#else // linux/BSD std::map lscpu = parse_lscpu_etc("lscpu", ':'); cpu_node.put("Arch", lscpu["Architecture"]); cpu_node.put("Cores", lscpu["CPU(s)"]); diff --git a/src/slic3r/GUI/SendSystemInfoDialog.hpp b/src/slic3r/GUI/SendSystemInfoDialog.hpp index e7556b5ab..dbc3ca978 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.hpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.hpp @@ -1,10 +1,6 @@ #ifndef slic3r_SendSystemInfoDialog_hpp_ #define slic3r_SendSystemInfoDialog_hpp_ -#include "GUI_Utils.hpp" - -#include - namespace Slic3r { namespace GUI { From c0eb4fbbd96f0864057fe351ccdf26af5c926333 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 1 Oct 2021 12:27:04 +0200 Subject: [PATCH 10/17] Use MAC address as a base of unique system ID on Windows --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 72 +++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index 29459dd8d..bdf6743fc 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -14,9 +14,11 @@ #include "MsgDialog.hpp" #include "OpenGLManager.hpp" +#include #include #include #include +#include #include "GL/glew.h" @@ -31,6 +33,8 @@ #ifdef _WIN32 #include + #include + #pragma comment(lib, "iphlpapi.lib") #endif namespace Slic3r { @@ -225,15 +229,75 @@ static std::map parse_lscpu_etc(const std::string& nam +static std::string get_unique_id() +{ + std::vector unique; + +#ifdef _WIN32 + // On Windows, get the MAC address of a network adaptor (preferably Ethernet + // or IEEE 802.11 wireless + + DWORD dwBufLen = sizeof(IP_ADAPTER_INFO); + PIP_ADAPTER_INFO AdapterInfo = (PIP_ADAPTER_INFO)malloc(dwBufLen); + + if (GetAdaptersInfo(AdapterInfo, &dwBufLen) == ERROR_BUFFER_OVERFLOW) { + free(AdapterInfo); + AdapterInfo = (IP_ADAPTER_INFO*)malloc(dwBufLen); + } + if (GetAdaptersInfo(AdapterInfo, &dwBufLen) == NO_ERROR) { + const IP_ADAPTER_INFO* pAdapterInfo = AdapterInfo; + std::vector> macs; + bool ethernet_seen = false; + while (pAdapterInfo) { + macs.emplace_back(); + for (unsigned char i = 0; i < pAdapterInfo->AddressLength; ++i) + macs.back().emplace_back(pAdapterInfo->Address[i]); + // Prefer Ethernet and IEEE 802.11 wireless + if (! ethernet_seen) { + if ((pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET && (ethernet_seen = true)) + || pAdapterInfo->Type == IF_TYPE_IEEE80211) + std::swap(macs.front(), macs.back()); + } + pAdapterInfo = pAdapterInfo->Next; + } + if (! macs.empty()) + unique = macs.front(); + } + free(AdapterInfo); +#elif __APPLE__ +#else // Linux/BSD +#endif + + // We should have a unique vector. Append a long prime to be + // absolutely safe against unhashing. + uint64_t prime = 1171432692373; + size_t beg = unique.size(); + unique.resize(beg + 8); + memcpy(&unique[beg], &prime, 8); + + // Compute an MD5 hash and convert to std::string. + using boost::uuids::detail::md5; + md5 hash; + md5::digest_type digest; + hash.process_bytes(unique.data(), unique.size()); + hash.get_digest(digest); + const unsigned char* charDigest = reinterpret_cast(&digest); + std::string result; + boost::algorithm::hex(charDigest, charDigest + sizeof(md5::digest_type), std::back_inserter(result)); + return result; +} + + // Following function generates one string that will be shown in the preview // and later sent if confirmed by the user. static std::string generate_system_info_json() { + get_unique_id(); // Calculate hash of username so it is possible to identify duplicates. // The result is mod 10000 so most of the information is lost and it is // not possible to unhash the username. It is more than enough to help // identify duplicate entries. - size_t datadir_hash = std::hash{}(std::string(wxGetUserId().ToUTF8().data())) % 10000; + std::string unique_id = get_unique_id(); // Get system language. std::string sys_language = "Unknown"; @@ -247,7 +311,7 @@ static std::string generate_system_info_json() pt::ptree data_node; data_node.put("PrusaSlicerVersion", SLIC3R_VERSION); data_node.put("BuildID", SLIC3R_BUILD_ID); - data_node.put("UsernameHash", datadir_hash); + data_node.put("UniqueID", unique_id); data_node.put("Platform", platform_to_string(platform())); data_node.put("PlatformFlavor", platform_flavor_to_string(platform_flavor())); data_node.put("OSDescription", wxPlatformInfo::Get().GetOperatingSystemDescription().ToUTF8().data()); @@ -413,8 +477,8 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) wxString label2 = _L("Is it safe?"); wxString text2 = GUI::format_wxstr( _L("We do not send any personal information nor anything that would allow us " - "to identify you later. To detect duplicate entries, a number derived " - "from your username is sent, but it cannot be used to recover the username. " + "to identify you later. To detect duplicate entries, a unique number derived " + "from your system is sent, but the source information cannot be reconstructed. " "Apart from that, only general data about your OS, hardware and OpenGL " "installation are sent. PrusaSlicer is open source, if you want to " "inspect the code actually performing the communication, see %1%."), From 1bef2eacc5f2c766c58d521663926fcec7d63cda Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 1 Oct 2021 12:32:44 +0200 Subject: [PATCH 11/17] Use hardware UUID as unique id on Apple --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index bdf6743fc..34178133f 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -35,6 +35,8 @@ #include #include #pragma comment(lib, "iphlpapi.lib") +#elif __APPLE__ +#import #endif namespace Slic3r { @@ -265,7 +267,19 @@ static std::string get_unique_id() } free(AdapterInfo); #elif __APPLE__ + constexpr int buf_size = 100; + char buf[buf_size] = ""; + memset(&buf, 0, sizeof(buf)); + io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/"); + CFStringRef uuidCf = (CFStringRef)IORegistryEntryCreateCFProperty(ioRegistryRoot, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0); + IOObjectRelease(ioRegistryRoot); + CFStringGetCString(uuidCf, buf, buf_size, kCFStringEncodingMacRoman); + CFRelease(uuidCf); + // Now convert the string to std::vector. + for (char* c = buf; *c != 0; ++c) + unique.emplace_back((unsigned char)(*c)); #else // Linux/BSD + #endif // We should have a unique vector. Append a long prime to be From e8412d654ca0b6ebf0f24939517f501e08ac6b74 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 1 Oct 2021 13:32:04 +0200 Subject: [PATCH 12/17] Use /etc/machine-id as unique id on Linux --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 19 +++++++++++++++++-- src/slic3r/GUI/format.hpp | 2 ++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index 34178133f..ee1e4c5c3 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -279,9 +279,25 @@ static std::string get_unique_id() for (char* c = buf; *c != 0; ++c) unique.emplace_back((unsigned char)(*c)); #else // Linux/BSD - + constexpr size_t max_len = 100; + char cline[max_len] = ""; + FILE* fp = popen("cat /etc/machine-id", "r"); + if (fp != NULL) { + // Maybe the only way to silence -Wunused-result on gcc... + // cline is simply not modified on failure, who cares. + [[maybe_unused]]auto dummy = fgets(cline, max_len, fp); + pclose(fp); + } + // Now convert the string to std::vector. + for (char* c = cline; *c != 0; ++c) + unique.emplace_back((unsigned char)(*c)); #endif + // In case that we did not manage to get the unique info, just return an empty + // string, so it is easily detectable and not masked by the hashing. + if (unique.empty()) + return ""; + // We should have a unique vector. Append a long prime to be // absolutely safe against unhashing. uint64_t prime = 1171432692373; @@ -306,7 +322,6 @@ static std::string get_unique_id() // and later sent if confirmed by the user. static std::string generate_system_info_json() { - get_unique_id(); // Calculate hash of username so it is possible to identify duplicates. // The result is mod 10000 so most of the information is lost and it is // not possible to unhash the username. It is more than enough to help diff --git a/src/slic3r/GUI/format.hpp b/src/slic3r/GUI/format.hpp index 894803917..928171cd5 100644 --- a/src/slic3r/GUI/format.hpp +++ b/src/slic3r/GUI/format.hpp @@ -9,6 +9,8 @@ #include +#include + namespace Slic3r { namespace GUI { From 5b5c72e73e217d6b20cfcbe98cd98253704729b2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 1 Oct 2021 13:40:04 +0200 Subject: [PATCH 13/17] REVERT LATER: More debugging logging --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index ee1e4c5c3..e27e981ea 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -266,6 +266,13 @@ static std::string get_unique_id() unique = macs.front(); } free(AdapterInfo); + + // DEBUGGING ONLY: + printf("SENDSYSTEMINFODIALOG DEBUGGING: Windows MAC: "); + for (unsigned char c : unique) + printf("%02X ", c); + printf("\n"); + #elif __APPLE__ constexpr int buf_size = 100; char buf[buf_size] = ""; @@ -278,6 +285,9 @@ static std::string get_unique_id() // Now convert the string to std::vector. for (char* c = buf; *c != 0; ++c) unique.emplace_back((unsigned char)(*c)); + + // DEBUGGING ONLY: + std::cout << "SENDSYSTEMINFODIALOG DEBUGGING: Apple hardware UUID: " << buf << std::endl; #else // Linux/BSD constexpr size_t max_len = 100; char cline[max_len] = ""; @@ -291,6 +301,9 @@ static std::string get_unique_id() // Now convert the string to std::vector. for (char* c = cline; *c != 0; ++c) unique.emplace_back((unsigned char)(*c)); + + // DEBUGGING ONLY: + std::cout << "SENDSYSTEMINFODIALOG DEBUGGING: Linux machine-id: " << cline << std::endl; #endif // In case that we did not manage to get the unique info, just return an empty From 5dd9e98f27bfd7a206d4b4b1a97d20d487e0ff90 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 5 Oct 2021 15:03:48 +0200 Subject: [PATCH 14/17] SendSystemInfo: Return number of logical cores on Apple --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index e27e981ea..a9583216f 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -401,7 +401,7 @@ static std::string generate_system_info_json() cpu_node.put("Vendor", cpu_info["Vendor"]); #elif __APPLE__ std::map sysctl = parse_lscpu_etc("sysctl -a", ':'); - cpu_node.put("Cores", sysctl["machdep.cpu.core_count"]); + cpu_node.put("Cores", sysctl["hw.ncpu"]); cpu_node.put("Model", sysctl["machdep.cpu.brand_string"]); cpu_node.put("Vendor", sysctl["machdep.cpu.vendor"]); #else // linux/BSD From d7d056c8ba6cfa432876e03af99ae53111078069 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 5 Oct 2021 15:04:24 +0200 Subject: [PATCH 15/17] This reverts commits a186017 and 2423e7f --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 34 ++----------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index a9583216f..a9cbdea19 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -61,7 +61,7 @@ public: private: bool send_info(); - /*const */std::string m_system_info_json; // will be const, this is FOR DEBUGGING + const std::string m_system_info_json; wxButton* m_btn_send; wxButton* m_btn_dont_send; wxButton* m_btn_ask_later; @@ -104,9 +104,7 @@ public: { auto* text = new wxTextCtrl(this, wxID_ANY, json, wxDefaultPosition, wxDefaultSize, - wxTE_MULTILINE - // | wxTE_READONLY // commented out for DEBUGGING ONLY - | wxTE_DONTWRAP); + wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); text->SetFont(wxGetApp().code_font()); text->ShowPosition(0); @@ -121,15 +119,6 @@ public: #ifdef _WIN32 wxGetApp().UpdateDlgDarkUI(this); #endif - - m_text = text; // DEBUGGING ONLY - } - - // DEBUGGING ONLY: - wxTextCtrl* m_text; - std::string get_data() const // debugging only - { - return m_text->GetValue().ToUTF8().data(); } }; @@ -140,8 +129,6 @@ public: // current version is newer. Only major and minor versions are compared. static bool should_dialog_be_shown() { - return true; // DEBUGGING ONLY - std::string last_sent_version = wxGetApp().app_config->get("version_system_info_sent"); Semver semver_current(SLIC3R_VERSION); Semver semver_last_sent; @@ -266,13 +253,6 @@ static std::string get_unique_id() unique = macs.front(); } free(AdapterInfo); - - // DEBUGGING ONLY: - printf("SENDSYSTEMINFODIALOG DEBUGGING: Windows MAC: "); - for (unsigned char c : unique) - printf("%02X ", c); - printf("\n"); - #elif __APPLE__ constexpr int buf_size = 100; char buf[buf_size] = ""; @@ -285,9 +265,6 @@ static std::string get_unique_id() // Now convert the string to std::vector. for (char* c = buf; *c != 0; ++c) unique.emplace_back((unsigned char)(*c)); - - // DEBUGGING ONLY: - std::cout << "SENDSYSTEMINFODIALOG DEBUGGING: Apple hardware UUID: " << buf << std::endl; #else // Linux/BSD constexpr size_t max_len = 100; char cline[max_len] = ""; @@ -301,9 +278,6 @@ static std::string get_unique_id() // Now convert the string to std::vector. for (char* c = cline; *c != 0; ++c) unique.emplace_back((unsigned char)(*c)); - - // DEBUGGING ONLY: - std::cout << "SENDSYSTEMINFODIALOG DEBUGGING: Linux machine-id: " << cline << std::endl; #endif // In case that we did not manage to get the unique info, just return an empty @@ -541,7 +515,6 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) html_window->Bind(wxEVT_HTML_LINK_CLICKED, [this](wxHtmlLinkEvent &evt) { ShowJsonDialog dlg(this, m_system_info_json, GetSize().Scale(0.9, 0.7)); dlg.ShowModal(); - m_system_info_json = dlg.get_data(); // DEBUGGING ONLY }); vsizer->Add(html_window, 1, wxEXPAND); @@ -623,9 +596,6 @@ bool SendSystemInfoDialog::send_info() .set_post_body(data) .on_complete([&result](std::string body, unsigned status) { result = { Result::Success, _L("System info sent successfully. Thank you.") }; - - // DEBUGGING ONLY: - std::cout << "Response: " << std::endl << body << std::endl << "-----------------" << std::endl; }) .on_error([&result](std::string body, std::string error, unsigned status) { result = { Result::Error, GUI::format_wxstr(_L("Sending system info failed! Status: %1%"), status) }; From deec0330bb06a4bb4ed2dabbc633fadef45c6847 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 5 Oct 2021 15:08:04 +0200 Subject: [PATCH 16/17] Disable the dialog before merge with master --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index a9cbdea19..a8f776354 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -129,14 +129,16 @@ public: // current version is newer. Only major and minor versions are compared. static bool should_dialog_be_shown() { + return false; + std::string last_sent_version = wxGetApp().app_config->get("version_system_info_sent"); Semver semver_current(SLIC3R_VERSION); Semver semver_last_sent; if (! last_sent_version.empty()) semver_last_sent = Semver(last_sent_version); - if (semver_current.prerelease() && std::string(semver_current.prerelease()) != "rc") - return false; // Only show in rcs / finals. + if (semver_current.prerelease() && std::string(semver_current.prerelease()) == "alpha") + return false; // Don't show in alphas. // Show the dialog if current > last, but they differ in more than just patch. return ((semver_current.maj() > semver_last_sent.maj()) From 8401ba192b2dac73f65c7b6e622bc67149335746 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 5 Oct 2021 15:36:00 +0200 Subject: [PATCH 17/17] Show PrusaSlicer logo in the SendSystemInfoDialog --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index a8f776354..db6ebad57 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -52,8 +52,8 @@ std::string gl_get_string_safe(GLenum param, const std::string& default_value); class SendSystemInfoDialog : public DPIDialog { enum { - MIN_WIDTH = 60, - MIN_HEIGHT = 40 + MIN_WIDTH = 80, + MIN_HEIGHT = 50 }; public: @@ -486,12 +486,9 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) "ask you to send some of your system information to us. This will only " "happen once and we will not ask you to do this again (only after you " "upgrade to the next version)."), app_name ); - wxString label1 = _L("Why is it needed"); wxString text1 = _L("If we know your hardware, operating system, etc., it will greatly help us " - "in development, prioritization and possible deprecation of features that " - "are no more needed (for example legacy OpenGL support). This will help " - "us to focus our effort more efficiently and spend time on features that " - "are needed the most."); + "in development and prioritization, because we will be able to focus our effort more efficiently " + "and spend time on features that are needed the most."); wxString label2 = _L("Is it safe?"); wxString text2 = GUI::format_wxstr( _L("We do not send any personal information nor anything that would allow us " @@ -506,9 +503,12 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) auto* html_window = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER); wxString html = GUI::format_wxstr( "" - + text0 + "

" - + "" + label1 + "
" + "
" + "" + "" + + text0 + "

" + text1 + "

" + "
" + "" + label2 + "
" + text2 + "

" + "" + label3 + "
"