diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a69d3bbf..a7d3dbf92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ project(PrusaSlicer) include("version.inc") include(GNUInstallDirs) +include(CMakeDependentOption) set(SLIC3R_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/resources") file(TO_NATIVE_PATH "${SLIC3R_RESOURCES_DIR}" SLIC3R_RESOURCES_DIR_WIN) @@ -32,6 +33,8 @@ option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1) option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1) option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0) option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0) +# If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable. +CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 0 "NOT SLIC3R_FHS" 0) set(OPENVDB_FIND_MODULE_PATH "" CACHE PATH "Path to OpenVDB installation's find modules.") @@ -71,6 +74,10 @@ if (SLIC3R_GUI) add_definitions(-DSLIC3R_GUI) endif () +if(SLIC3R_DESKTOP_INTEGRATION) + add_definitions(-DSLIC3R_DESKTOP_INTEGRATION) +endif () + if (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) set(IS_CLANG_CL TRUE) diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp index a2f7c8933..1ec008bd5 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.cpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -1,15 +1,18 @@ #ifdef __linux__ #include "DesktopIntegrationDialog.hpp" #include "GUI_App.hpp" +#include "GUI.hpp" #include "format.hpp" #include "I18N.hpp" #include "NotificationManager.hpp" #include "libslic3r/AppConfig.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Platform.hpp" +#include "libslic3r/Config.hpp" #include #include +#include #include #include @@ -17,9 +20,9 @@ namespace Slic3r { namespace GUI { -namespace integrate_desktop_internal{ +namespace { // Disects path strings stored in system variable divided by ':' and adds into vector -static void resolve_path_from_var(const std::string& var, std::vector& paths) +void resolve_path_from_var(const std::string& var, std::vector& paths) { wxString wxdirs; if (! wxGetEnv(boost::nowide::widen(var), &wxdirs) || wxdirs.empty() ) @@ -34,7 +37,7 @@ static void resolve_path_from_var(const std::string& var, std::vectorget("desktop_integration_app_path")); BOOST_LOG_TRIVIAL(debug) << "Desktop integration desktop file path: " << path; @@ -126,10 +125,6 @@ bool DesktopIntegrationDialog::is_integrated() } bool DesktopIntegrationDialog::integration_possible() { - - const char *appimage_env = std::getenv("APPIMAGE"); - if (!appimage_env) - return false; return true; } void DesktopIntegrationDialog::perform_desktop_integration() @@ -143,14 +138,26 @@ void DesktopIntegrationDialog::perform_desktop_integration() try { appimage_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string(); } catch (std::exception &) { + BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - boost::filesystem::canonical did not return appimage path."; + show_error(nullptr, _L("Performing desktop integration failed - boost::filesystem::canonical did not return appimage path.")); + return; } } else { - // not appimage - not performing - BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - not Appimage executable."; - wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); - return; + // not appimage - find executable + appimage_path = boost::dll::program_location().string(); + //appimage_path = wxStandardPaths::Get().GetExecutablePath().string(); + BOOST_LOG_TRIVIAL(debug) << "non-appimage path to executable: " << appimage_path; + if (appimage_path.empty()) + { + BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - no executable found."; + show_error(nullptr, _L("Performing desktop integration failed - Could not find executable.")); + return; + } } + // Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'appimage_path' + //appimage_path = std::regex_replace(appimage_path, std::regex("\'"), "\\\'"); + // Find directories icons and applications // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. // If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used. @@ -158,8 +165,8 @@ void DesktopIntegrationDialog::perform_desktop_integration() // The directories in $XDG_DATA_DIRS should be seperated with a colon ':'. // If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used. std::vectortarget_candidates; - integrate_desktop_internal::resolve_path_from_var("XDG_DATA_HOME", target_candidates); - integrate_desktop_internal::resolve_path_from_var("XDG_DATA_DIRS", target_candidates); + resolve_path_from_var("XDG_DATA_HOME", target_candidates); + resolve_path_from_var("XDG_DATA_DIRS", target_candidates); AppConfig *app_config = wxGetApp().app_config; // suffix string to create different desktop file for alpha, beta. @@ -186,7 +193,6 @@ void DesktopIntegrationDialog::perform_desktop_integration() icon_theme_dirs = "/hicolor/96x96/apps"; } - std::string target_dir_icons; std::string target_dir_desktop; @@ -194,24 +200,24 @@ void DesktopIntegrationDialog::perform_desktop_integration() // iterate thru target_candidates to find icons folder for (size_t i = 0; i < target_candidates.size(); ++i) { // Copy icon PrusaSlicer.png from resources_dir()/icons to target_dir_icons/icons/ - if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "icons")) { + if (contains_path_dir(target_candidates[i], "icons")) { target_dir_icons = target_candidates[i]; std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir()); std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix); - if (integrate_desktop_internal::copy_icon(icon_path, dest_path)) + if (copy_icon(icon_path, dest_path)) break; // success else target_dir_icons.clear(); // copying failed // if all failed - try creating default home folder if (i == target_candidates.size() - 1) { // create $HOME/.local/share - integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs); + create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs); // copy icon target_dir_icons = GUI::format("%1%/.local/share",wxFileName::GetHomeDir()); std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir()); std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix); - if (!integrate_desktop_internal::contains_path_dir(target_dir_icons, "icons") - || !integrate_desktop_internal::copy_icon(icon_path, dest_path)) { + if (!contains_path_dir(target_dir_icons, "icons") + || !copy_icon(icon_path, dest_path)) { // every attempt failed - icon wont be present target_dir_icons.clear(); } @@ -228,7 +234,7 @@ void DesktopIntegrationDialog::perform_desktop_integration() // iterate thru target_candidates to find applications folder for (size_t i = 0; i < target_candidates.size(); ++i) { - if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "applications")) { + if (contains_path_dir(target_candidates[i], "applications")) { target_dir_desktop = target_candidates[i]; // Write slicer desktop file std::string desktop_file = GUI::format( @@ -236,7 +242,7 @@ void DesktopIntegrationDialog::perform_desktop_integration() "Name=PrusaSlicer%1%\n" "GenericName=3D Printing Software\n" "Icon=PrusaSlicer%2%\n" - "Exec=%3% %%F\n" + "Exec=\'%3%\' %%F\n" "Terminal=false\n" "Type=Application\n" "MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n" @@ -246,23 +252,23 @@ void DesktopIntegrationDialog::perform_desktop_integration() "StartupWMClass=prusa-slicer", name_suffix, version_suffix, appimage_path); std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix); - if (integrate_desktop_internal::create_desktop_file(path, desktop_file)){ + if (create_desktop_file(path, desktop_file)){ BOOST_LOG_TRIVIAL(debug) << "PrusaSlicer.desktop file installation success."; break; } else { // write failed - try another path - BOOST_LOG_TRIVIAL(error) << "PrusaSlicer.desktop file installation failed."; + BOOST_LOG_TRIVIAL(debug) << "Attempt to PrusaSlicer.desktop file installation failed. failed path: " << target_candidates[i]; target_dir_desktop.clear(); } // if all failed - try creating default home folder if (i == target_candidates.size() - 1) { // create $HOME/.local/share - integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications"); + create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications"); // create desktop file target_dir_desktop = GUI::format("%1%/.local/share",wxFileName::GetHomeDir()); std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix); - if (integrate_desktop_internal::contains_path_dir(target_dir_desktop, "applications")) { - if (!integrate_desktop_internal::create_desktop_file(path, desktop_file)) { + if (contains_path_dir(target_dir_desktop, "applications")) { + if (!create_desktop_file(path, desktop_file)) { // Desktop file not written - end desktop integration BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create desktop file"; return; @@ -278,7 +284,7 @@ void DesktopIntegrationDialog::perform_desktop_integration() if(target_dir_desktop.empty()) { // Desktop file not written - end desktop integration BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not find applications directory"; - wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); + show_error(nullptr, _L("Performing desktop integration failed - could not find applications directory.")); return; } // save path to desktop file @@ -290,7 +296,7 @@ void DesktopIntegrationDialog::perform_desktop_integration() { std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir()); std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix); - if (integrate_desktop_internal::copy_icon(icon_path, dest_path)) + if (copy_icon(icon_path, dest_path)) // save path to icon app_config->set("desktop_integration_icon_viewer_path", dest_path); else @@ -303,7 +309,7 @@ void DesktopIntegrationDialog::perform_desktop_integration() "Name=Prusa Gcode Viewer%1%\n" "GenericName=3D Printing Software\n" "Icon=PrusaSlicer-gcodeviewer%2%\n" - "Exec=%3% --gcodeviwer %%F\n" + "Exec=\'%3%\' --gcodeviwer %%F\n" "Terminal=false\n" "Type=Application\n" "MimeType=text/x.gcode;\n" @@ -312,23 +318,17 @@ void DesktopIntegrationDialog::perform_desktop_integration() "StartupNotify=false", name_suffix, version_suffix, appimage_path); std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix); - if (integrate_desktop_internal::create_desktop_file(desktop_path, desktop_file)) + if (create_desktop_file(desktop_path, desktop_file)) // save path to desktop file app_config->set("desktop_integration_app_viewer_path", desktop_path); else { - BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could create gcode viewer desktop file"; - wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); + BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create Gcodeviewer desktop file"; + show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully.")); } wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess); } void DesktopIntegrationDialog::undo_desktop_intgration() { - const char *appimage_env = std::getenv("APPIMAGE"); - if (!appimage_env) { - BOOST_LOG_TRIVIAL(error) << "Undo desktop integration failed - not Appimage executable."; - wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationFail); - return; - } const AppConfig *app_config = wxGetApp().app_config; // slicer .desktop std::string path = std::string(app_config->get("desktop_integration_app_path")); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 91d16c602..06d805eeb 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1806,10 +1806,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu) local_menu->Append(config_id_base + ConfigMenuSnapshots, _L("&Configuration Snapshots") + dots, _L("Inspect / activate configuration snapshots")); local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _L("Take Configuration &Snapshot"), _L("Capture a configuration snapshot")); local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for updates"), _L("Check for configuration updates")); -#ifdef __linux__ - if (DesktopIntegrationDialog::integration_possible()) - local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration")); -#endif +#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) + //if (DesktopIntegrationDialog::integration_possible()) + local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration")); +#endif //(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) local_menu->AppendSeparator(); } local_menu->Append(config_id_base + ConfigMenuPreferences, _L("&Preferences") + dots +