From 18cf1fdb43cd6940733fdda8a4c1b07adff23b0e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 7 Jan 2021 12:02:44 +0100 Subject: [PATCH 01/13] Fixing the SD card eject issue on OSX by pushing the call to "diskutil eject" to a worker thread. Hopefully fixes Ejecting an SD card is slow and locks UI #4844 --- src/slic3r/GUI/RemovableDriveManager.cpp | 89 +++++++++++++++++------- src/slic3r/GUI/RemovableDriveManager.hpp | 2 + 2 files changed, 64 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index ec69bb198..9ee1f9ec4 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -261,19 +261,34 @@ void RemovableDriveManager::eject_drive() #ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS this->update(); #endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS +#if __APPLE__ + // If eject is still pending on the eject thread, wait until it finishes. + //FIXME while waiting for the eject thread to finish, the main thread is not pumping Cocoa messages, which may lead + // to blocking by the diskutil tool for a couple (up to 10) seconds. This is likely not critical, as the eject normally + // finishes quickly. + this->eject_thread_finish(); +#endif + BOOST_LOG_TRIVIAL(info) << "Ejecting started"; - tbb::mutex::scoped_lock lock(m_drives_mutex); - auto it_drive_data = this->find_last_save_path_drive_data(); - if (it_drive_data != m_current_drives.end()) { - std::string correct_path(m_last_save_path); -#ifndef __APPLE__ - for (size_t i = 0; i < correct_path.size(); ++i) - if (correct_path[i]==' ') { - correct_path = correct_path.insert(i,1,'\\'); - ++ i; - } + DriveData drive_data; + { + tbb::mutex::scoped_lock lock(m_drives_mutex); + auto it_drive_data = this->find_last_save_path_drive_data(); + if (it_drive_data == m_current_drives.end()) + return; + drive_data = *it_drive_data; + } + + std::string correct_path(m_last_save_path); +#if __APPLE__ + // On Apple, run the eject asynchronously on a worker thread, see the discussion at GH issue #4844. + m_eject_thread = std::thread([this, correct_path, drive_data]() +#else + // Escape spaces on Unix systems. Why not on Apple? + boost::replace_all(correct_path, " ", "\\ "); #endif + { //std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n"; // there is no usable command in c++ so terminal command is used instead // but neither triggers "succesful safe removal messege" @@ -296,31 +311,36 @@ void RemovableDriveManager::eject_drive() // wait for command to finnish (blocks ui thread) std::error_code ec; child.wait(ec); + bool success = false; if (ec) { // The wait call can fail, as it did in https://github.com/prusa3d/PrusaSlicer/issues/5507 // It can happen even in cases where the eject is sucessful, but better report it as failed. // We did not find a way to reliably retrieve the exit code of the process. BOOST_LOG_TRIVIAL(error) << "boost::process::child::wait() failed during Ejection. State of Ejection is unknown. Error code: " << ec.value(); - assert(m_callback_evt_handler); - if (m_callback_evt_handler) - wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair(*it_drive_data, false))); - return; + } else { + int err = child.exit_code(); + if (err) { + BOOST_LOG_TRIVIAL(error) << "Ejecting failed. Exit code: " << err; + } else { + BOOST_LOG_TRIVIAL(info) << "Ejecting finished"; + success = true; + } } - int err = child.exit_code(); - if (err) { - BOOST_LOG_TRIVIAL(error) << "Ejecting failed. Exit code: " << err; - assert(m_callback_evt_handler); - if (m_callback_evt_handler) - wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair(*it_drive_data, false))); - return; - } - BOOST_LOG_TRIVIAL(info) << "Ejecting finished"; - assert(m_callback_evt_handler); if (m_callback_evt_handler) - wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair(std::move(*it_drive_data), true))); - m_current_drives.erase(it_drive_data); + wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair(drive_data, success))); + if (success) { + // Remove the drive_data from m_current drives, searching by value, not by pointer, as m_current_drives may get modified during + // asynchronous execution on m_eject_thread. + tbb::mutex::scoped_lock lock(m_drives_mutex); + auto it = m_current_drives.find(drive_data); + if (it != m_current_drives.end()) + m_current_drives.erase(it); + } } +#if __APPLE__ + ); +#endif // __APPLE__ } std::string RemovableDriveManager::get_removable_drive_path(const std::string &path) @@ -382,7 +402,11 @@ void RemovableDriveManager::init(wxEvtHandler *callback_evt_handler) void RemovableDriveManager::shutdown() { #if __APPLE__ - this->unregister_window_osx(); + // If eject is still pending on the eject thread, wait until it finishes. + //FIXME while waiting for the eject thread to finish, the main thread is not pumping Cocoa messages, which may lead + // to blocking by the diskutil tool for a couple (up to 10) seconds. This is likely not critical, as the eject normally + // finishes quickly. + this->eject_thread_finish(); #endif #ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS @@ -493,4 +517,15 @@ std::vector::const_iterator RemovableDriveManager::find_last_save_pat [this](const DriveData &data){ return data.path == m_last_save_path; }); } +#if __APPLE__ +void RemovableDriveManager::eject_thread_finish() +{ + if (m_eject_thread) { + m_eject_thread->join(); + delete m_eject_thread; + m_eject_thread = nullptr; + } +} +#endif // __APPLE__ + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index 26ee12e40..d250f3710 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -132,6 +132,8 @@ private: void eject_device(const std::string &path); // Opaque pointer to RemovableDriveManagerMM void *m_impl_osx; + std::thread *m_eject_thread { nullptr }; + void eject_thread_finish(); #endif }; From 961abd75e84ffaa419c3f595aff249d0550e622b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 7 Jan 2021 12:09:47 +0100 Subject: [PATCH 02/13] Fix of previopus commut --- src/slic3r/GUI/RemovableDriveManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 9ee1f9ec4..8cd39a689 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -333,7 +333,7 @@ void RemovableDriveManager::eject_drive() // Remove the drive_data from m_current drives, searching by value, not by pointer, as m_current_drives may get modified during // asynchronous execution on m_eject_thread. tbb::mutex::scoped_lock lock(m_drives_mutex); - auto it = m_current_drives.find(drive_data); + auto it = std::find(m_current_drives.begin(), m_current_drives.end(), drive_data); if (it != m_current_drives.end()) m_current_drives.erase(it); } From 546fbe1e8e59f5038dc730def1f853166a3a4391 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 7 Jan 2021 12:19:41 +0100 Subject: [PATCH 03/13] Fix of previous commit for OSX --- src/slic3r/GUI/RemovableDriveManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 8cd39a689..cc6c78d8d 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -283,7 +283,7 @@ void RemovableDriveManager::eject_drive() std::string correct_path(m_last_save_path); #if __APPLE__ // On Apple, run the eject asynchronously on a worker thread, see the discussion at GH issue #4844. - m_eject_thread = std::thread([this, correct_path, drive_data]() + m_eject_thread = new std::thread([this, correct_path, drive_data]() #else // Escape spaces on Unix systems. Why not on Apple? boost::replace_all(correct_path, " ", "\\ "); From 8e7958da124d128e8d4b8ebdec3ec6bf2c00af47 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 7 Jan 2021 12:26:16 +0100 Subject: [PATCH 04/13] Fix of #5698 - When editing presets, list items are always green even if values have changed compared to preset or defaults --- src/slic3r/GUI/Tab.cpp | 8 ++++---- src/slic3r/GUI/Tab.hpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 0656c7e0d..a0d7d17f2 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -501,7 +501,7 @@ void Tab::update_labels_colour() if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" || opt.first == "compatible_prints" || opt.first == "compatible_printers" ) { if (m_colored_Label_colors.find(opt.first) != m_colored_Label_colors.end()) - *m_colored_Label_colors.at(opt.first) = *color; + m_colored_Label_colors.at(opt.first) = *color; continue; } @@ -540,7 +540,7 @@ void Tab::decorate() if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" || opt.first == "compatible_prints" || opt.first == "compatible_printers") - colored_label_clr = (m_colored_Label_colors.find(opt.first) == m_colored_Label_colors.end()) ? nullptr : m_colored_Label_colors.at(opt.first); + colored_label_clr = (m_colored_Label_colors.find(opt.first) == m_colored_Label_colors.end()) ? nullptr : &m_colored_Label_colors.at(opt.first); if (!colored_label_clr) { field = get_field(opt.first); @@ -3553,8 +3553,8 @@ void Tab::create_line_with_widget(ConfigOptionsGroup* optgroup, const std::strin line.widget = widget; line.label_path = path; - m_colored_Label_colors[opt_key] = &m_default_text_clr; - line.full_Label_color = m_colored_Label_colors[opt_key]; + m_colored_Label_colors[opt_key] = m_default_text_clr; + line.full_Label_color = &m_colored_Label_colors[opt_key]; optgroup->append_line(line); } diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 04aa9a0ef..927787933 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -246,7 +246,7 @@ public: // map of option name -> wxColour (color of the colored label, associated with option) // Used for options which don't have corresponded field - std::map m_colored_Label_colors; + std::map m_colored_Label_colors; // Counter for the updating (because of an update() function can have a recursive behavior): // 1. increase value from the very beginning of an update() function From f43b34fb578cb91c72f96c6a72910f8255ddf87c Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 7 Jan 2021 12:54:53 +0100 Subject: [PATCH 05/13] wxWakeUpIdle when render timer ends to ensure idle function spawns and triggers rendering --- src/slic3r/GUI/GLCanvas3D.cpp | 1 + src/slic3r/GUI/NotificationManager.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 71517880a..03d6b94ce 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2996,6 +2996,7 @@ void GLCanvas3D::on_render_timer(wxTimerEvent& evt) } //render(); m_dirty = true; + wxWakeUpIdle(); } void GLCanvas3D::request_extra_frame_delayed(int miliseconds) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 410990ef7..5695eb62f 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1444,7 +1444,7 @@ void NotificationManager::update_notifications() } if (next_render == 0) - wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_framee_delayed(33); //few milliseconds to get from GLCanvas::render else if (next_render < max) wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(int(next_render)); From c227e1579443d28510665c3927848298ae117374 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 7 Jan 2021 13:13:35 +0100 Subject: [PATCH 06/13] Fixed compilation on OSX --- src/slic3r/GUI/RemovableDriveManager.cpp | 2 +- src/slic3r/GUI/RemovableDriveManager.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index cc6c78d8d..3421654e0 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -283,7 +283,7 @@ void RemovableDriveManager::eject_drive() std::string correct_path(m_last_save_path); #if __APPLE__ // On Apple, run the eject asynchronously on a worker thread, see the discussion at GH issue #4844. - m_eject_thread = new std::thread([this, correct_path, drive_data]() + m_eject_thread = new boost::thread([this, correct_path, drive_data]() #else // Escape spaces on Unix systems. Why not on Apple? boost::replace_all(correct_path, " ", "\\ "); diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index d250f3710..f707b40c3 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -132,7 +132,7 @@ private: void eject_device(const std::string &path); // Opaque pointer to RemovableDriveManagerMM void *m_impl_osx; - std::thread *m_eject_thread { nullptr }; + boost::thread *m_eject_thread { nullptr }; void eject_thread_finish(); #endif }; From c61a6289c595027b40399064c998cdfc3ee5b5a8 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 7 Jan 2021 13:16:23 +0100 Subject: [PATCH 07/13] update notification before erasing finished ones --- src/slic3r/GUI/NotificationManager.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 5695eb62f..64217ecf0 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1387,15 +1387,19 @@ void NotificationManager::update_notifications() if (!top_level_wnd->IsActive()) return; - static size_t last_size = m_pop_notifications.size(); + //static size_t last_size = m_pop_notifications.size(); + //request frames + int64_t next_render = std::numeric_limits::max(); for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { std::unique_ptr& notification = *it; + notification->set_paused(m_hovered); + notification->update_state(); + next_render = std::min(next_render, notification->next_render()); if (notification->get_state() == PopNotification::EState::Finished) it = m_pop_notifications.erase(it); else { - notification->set_paused(m_hovered); - notification->update_state(); + ++it; } } @@ -1436,16 +1440,11 @@ void NotificationManager::update_notifications() if (m_requires_render) m_requires_update = true; */ - //request frames - int64_t next_render = std::numeric_limits::max(); - const int64_t max = std::numeric_limits::max(); - for (const std::unique_ptr& notification : m_pop_notifications) { - next_render = std::min(next_render, notification->next_render()); - } + if (next_render == 0) - wxGetApp().plater()->get_current_canvas3D()->request_extra_framee_delayed(33); //few milliseconds to get from GLCanvas::render - else if (next_render < max) + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(33); //few milliseconds to get from GLCanvas::render + else if (next_render < std::numeric_limits::max()) wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(int(next_render)); /* From a5882a1a1c3f561233a22cc7663514b97e5fb3ba Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 6 Jan 2021 16:01:39 +0100 Subject: [PATCH 08/13] success with text ellipsize for combobox --- src/slic3r/CMakeLists.txt | 9 +++++++++ src/slic3r/GUI/PresetComboBoxes.cpp | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 699135d27..2101832f5 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -255,3 +255,12 @@ endif () if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () + +target_include_directories(libslic3r_gui PUBLIC /usr/include/glib-2.0) +target_include_directories(libslic3r_gui PUBLIC /usr/lib/glib-2.0/include) +target_include_directories(libslic3r_gui PUBLIC /usr/include/pango-1.0) +target_include_directories(libslic3r_gui PUBLIC /usr/include/harfbuzz) +target_include_directories(libslic3r_gui PUBLIC /usr/include/gtk-3.0/) +target_include_directories(libslic3r_gui PUBLIC /usr/include/cairo/) +target_include_directories(libslic3r_gui PUBLIC /usr/include/gdk-pixbuf-2.0/) +target_include_directories(libslic3r_gui PUBLIC /usr/include/atk-1.0/) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 6031edf78..c7598f9db 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -32,6 +32,10 @@ #include "PhysicalPrinterDialog.hpp" #include "SavePresetDialog.hpp" +#include +#include +#include + using Slic3r::GUI::format_wxstr; namespace Slic3r { @@ -130,6 +134,8 @@ PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const } evt.Skip(); }); + +// g_object_set( G_OBJECT( this ), "ellipsize", PANGO_ELLIPSIZE_END, nullptr); } PresetComboBox::~PresetComboBox() @@ -179,6 +185,20 @@ void PresetComboBox::update_selection() SetSelection(m_last_selected); SetToolTip(GetString(m_last_selected)); + + GList* cells = gtk_cell_layout_get_cells( GTK_CELL_LAYOUT( m_widget ) ); + if( !cells ) + return; + + GtkCellRendererText* cell = (GtkCellRendererText *) cells->next->data; + + if( !cell ) + return; + + g_object_set( G_OBJECT( cell ), "ellipsize", PANGO_ELLIPSIZE_END, NULL ); + + // Only the list of cells must be freed, the renderer isn't ours to free + g_list_free( cells ); } void PresetComboBox::update(std::string select_preset_name) From df59f9497a63abcdb5cf9633b41035e48dc025ec Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 7 Jan 2021 13:33:05 +0100 Subject: [PATCH 09/13] request frame with delay when new notification pushed --- src/slic3r/GUI/NotificationManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 64217ecf0..98cbf1e55 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1233,11 +1233,11 @@ bool NotificationManager::push_notification_data(std::unique_ptractivate_existing(notification.get())) { m_pop_notifications.back()->update(notification->get_data()); - canvas.request_extra_frame(); + canvas.request_extra_frame_delayed(33); return false; } else { m_pop_notifications.emplace_back(std::move(notification)); - canvas.request_extra_frame(); + canvas.request_extra_frame_delayed(33); return true; } } From 2505c0a82dcbb1ec8f607218a336c4a16b7c03b2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 7 Jan 2021 13:38:16 +0100 Subject: [PATCH 10/13] Fix for incorrect text fitting on gtk comboboxes. fixes #4584 --- cmake/modules/FindGTK3.cmake | 46 +++++++++++++++++++++++++++++ src/slic3r/CMakeLists.txt | 16 +++++----- src/slic3r/GUI/PresetComboBoxes.cpp | 33 +++++++++++++-------- 3 files changed, 74 insertions(+), 21 deletions(-) create mode 100644 cmake/modules/FindGTK3.cmake diff --git a/cmake/modules/FindGTK3.cmake b/cmake/modules/FindGTK3.cmake new file mode 100644 index 000000000..9f62658d0 --- /dev/null +++ b/cmake/modules/FindGTK3.cmake @@ -0,0 +1,46 @@ +# - Try to find GTK+ 3 +# Once done, this will define +# +# GTK3_FOUND - system has GTK+ 3. +# GTK3_INCLUDE_DIRS - the GTK+ 3. include directories +# GTK3_LIBRARIES - link these to use GTK+ 3. +# +# Copyright (C) 2012 Raphael Kubo da Costa +# Copyright (C) 2013 Igalia S.L. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +find_package(PkgConfig) +pkg_check_modules(GTK3 QUIET gtk+-3.0) +set(VERSION_OK TRUE) +if (GTK3_VERSION) +if (GTK3_FIND_VERSION_EXACT) +if (NOT("${GTK3_FIND_VERSION}" VERSION_EQUAL "${GTK3_VERSION}")) +set(VERSION_OK FALSE) +endif () +else () +if ("${GTK3_VERSION}" VERSION_LESS "${GTK3_FIND_VERSION}") +set(VERSION_OK FALSE) +endif () +endif () +endif () +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTK3 DEFAULT_MSG GTK3_INCLUDE_DIRS GTK3_LIBRARIES VERSION_OK) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 2101832f5..019850a98 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -256,11 +256,11 @@ if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () -target_include_directories(libslic3r_gui PUBLIC /usr/include/glib-2.0) -target_include_directories(libslic3r_gui PUBLIC /usr/lib/glib-2.0/include) -target_include_directories(libslic3r_gui PUBLIC /usr/include/pango-1.0) -target_include_directories(libslic3r_gui PUBLIC /usr/include/harfbuzz) -target_include_directories(libslic3r_gui PUBLIC /usr/include/gtk-3.0/) -target_include_directories(libslic3r_gui PUBLIC /usr/include/cairo/) -target_include_directories(libslic3r_gui PUBLIC /usr/include/gdk-pixbuf-2.0/) -target_include_directories(libslic3r_gui PUBLIC /usr/include/atk-1.0/) +# We need to implement some hacks for wxWidgets and touch the underlying GTK +# layer and sub-libraries. This forces us to use the include locations of these +# libraries. No need to link to them, wxWidgets does that already. +# See PresetComboBox.cpp for the includes and subsequent workarounds. +if (UNIX AND NOT APPLE) + find_package(GTK${SLIC3R_GTK} REQUIRED) + target_include_directories(libslic3r_gui PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS}) +endif () diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index c7598f9db..8dd35a591 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -32,9 +32,13 @@ #include "PhysicalPrinterDialog.hpp" #include "SavePresetDialog.hpp" -#include -#include -#include +// A workaround for a set of issues related to text fitting into gtk widgets: +// See e.g.: https://github.com/prusa3d/PrusaSlicer/issues/4584 +#if defined(__WXGTK20__) || defined(__WXGTK3__) + #include + #include + #include +#endif using Slic3r::GUI::format_wxstr; @@ -134,8 +138,6 @@ PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const } evt.Skip(); }); - -// g_object_set( G_OBJECT( this ), "ellipsize", PANGO_ELLIPSIZE_END, nullptr); } PresetComboBox::~PresetComboBox() @@ -186,19 +188,24 @@ void PresetComboBox::update_selection() SetSelection(m_last_selected); SetToolTip(GetString(m_last_selected)); - GList* cells = gtk_cell_layout_get_cells( GTK_CELL_LAYOUT( m_widget ) ); - if( !cells ) - return; +// A workaround for a set of issues related to text fitting into gtk widgets: +// See e.g.: https://github.com/prusa3d/PrusaSlicer/issues/4584 +#if defined(__WXGTK20__) || defined(__WXGTK3__) + GList* cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(m_widget)); - GtkCellRendererText* cell = (GtkCellRendererText *) cells->next->data; + // 'cells' contains the GtkCellRendererPixBuf for the icon, + // 'cells->next' contains GtkCellRendererText for the text we need to ellipsize + if (!cells || !cells->next) return; - if( !cell ) - return; + auto cell = static_cast(cells->next->data); - g_object_set( G_OBJECT( cell ), "ellipsize", PANGO_ELLIPSIZE_END, NULL ); + if (!cell) return; + + g_object_set(G_OBJECT(cell), "ellipsize", PANGO_ELLIPSIZE_END, NULL); // Only the list of cells must be freed, the renderer isn't ours to free - g_list_free( cells ); + g_list_free(cells); +#endif } void PresetComboBox::update(std::string select_preset_name) From 2ad4447035548378860ad45d4c649ce5ad06ad50 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 7 Jan 2021 14:17:09 +0100 Subject: [PATCH 11/13] Fix of Ejecting of device has failed on Ubuntu Linux #3975 Eject failed due to superfluous escaping of spaces in path to the SD card mount point. --- src/slic3r/GUI/RemovableDriveManager.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 3421654e0..b11cc8dd5 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -284,9 +284,6 @@ void RemovableDriveManager::eject_drive() #if __APPLE__ // On Apple, run the eject asynchronously on a worker thread, see the discussion at GH issue #4844. m_eject_thread = new boost::thread([this, correct_path, drive_data]() -#else - // Escape spaces on Unix systems. Why not on Apple? - boost::replace_all(correct_path, " ", "\\ "); #endif { //std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n"; From 71808dc006e7e664f79af5ff26e29d21fcdff013 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 7 Jan 2021 17:38:56 +0100 Subject: [PATCH 12/13] boost::process::spawn() sets SIGINT to SIGIGN, which collides with boost::process waiting for a child to finish! https://jmmv.dev/2008/10/boostprocess-and-sigchld.html Thus reset the SIGINT to its default, so that posix waitpid() and similar continue to work. Fixes Crash on Eject in Second Instance on macOS #5507 --- src/slic3r/Utils/Process.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index bc6961f9a..1199e5e76 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -17,6 +17,7 @@ // For starting another PrusaSlicer instance on OSX. // Fails to compile on Windows on the build server. #ifdef __APPLE__ + #include #include #include #endif @@ -78,6 +79,11 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance if (instance_type == NewSlicerInstanceType::Slicer && single_instance) args.emplace_back("--single-instance"); boost::process::spawn(bin_path, args); + // boost::process::spawn() sets SIGINT to SIGIGN, which collides with boost::process waiting for a child to finish! + // https://jmmv.dev/2008/10/boostprocess-and-sigchld.html + // Thus reset the SIGINT to its default, so that posix waitpid() and similar continue to work. + // Fixes Crash on Eject in Second Instance on macOS #5507 + signal(SIGINT, SIG_DFL); } catch (const std::exception& ex) { BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << bin_path.string() << "\": " << ex.what(); From 2b02747ec56d601124a37c3992d4bd1fe3ef4881 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 7 Jan 2021 19:14:48 +0100 Subject: [PATCH 13/13] On OSX, we use boost::process::spawn() to launch new instances of PrusaSlicer from another PrusaSlicer. boost::process::spawn() sets SIGCHLD to SIGIGN for the child process, thus if a child PrusaSlicer spawns another subprocess and the subrocess dies, the child PrusaSlicer will not receive information on end of subprocess (posix waitpid() call will always fail). https://jmmv.dev/2008/10/boostprocess-and-sigchld.html The child instance of PrusaSlicer has to reset SIGCHLD to its default, so that posix waitpid() and similar continue to work. Fixes #5507 --- src/slic3r/GUI/GUI_Init.cpp | 15 +++++++++++++++ src/slic3r/Utils/Process.cpp | 12 ++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_Init.cpp b/src/slic3r/GUI/GUI_Init.cpp index 70536c6ba..839782741 100644 --- a/src/slic3r/GUI/GUI_Init.cpp +++ b/src/slic3r/GUI/GUI_Init.cpp @@ -16,11 +16,26 @@ #include #include +#if __APPLE__ + #include +#endif // __APPLE__ + namespace Slic3r { namespace GUI { int GUI_Run(GUI_InitParams ¶ms) { +#if __APPLE__ + // On OSX, we use boost::process::spawn() to launch new instances of PrusaSlicer from another PrusaSlicer. + // boost::process::spawn() sets SIGCHLD to SIGIGN for the child process, thus if a child PrusaSlicer spawns another + // subprocess and the subrocess dies, the child PrusaSlicer will not receive information on end of subprocess + // (posix waitpid() call will always fail). + // https://jmmv.dev/2008/10/boostprocess-and-sigchld.html + // The child instance of PrusaSlicer has to reset SIGCHLD to its default, so that posix waitpid() and similar continue to work. + // See GH issue #5507 + signal(SIGCHLD, SIG_DFL); +#endif // __APPLE__ + try { GUI::GUI_App* gui = new GUI::GUI_App(params.start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor); if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) { diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index 1199e5e76..a8fc59f52 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -17,7 +17,6 @@ // For starting another PrusaSlicer instance on OSX. // Fails to compile on Windows on the build server. #ifdef __APPLE__ - #include #include #include #endif @@ -79,11 +78,12 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance if (instance_type == NewSlicerInstanceType::Slicer && single_instance) args.emplace_back("--single-instance"); boost::process::spawn(bin_path, args); - // boost::process::spawn() sets SIGINT to SIGIGN, which collides with boost::process waiting for a child to finish! - // https://jmmv.dev/2008/10/boostprocess-and-sigchld.html - // Thus reset the SIGINT to its default, so that posix waitpid() and similar continue to work. - // Fixes Crash on Eject in Second Instance on macOS #5507 - signal(SIGINT, SIG_DFL); + // boost::process::spawn() sets SIGCHLD to SIGIGN for the child process, thus if a child PrusaSlicer spawns another + // subprocess and the subrocess dies, the child PrusaSlicer will not receive information on end of subprocess + // (posix waitpid() call will always fail). + // https://jmmv.dev/2008/10/boostprocess-and-sigchld.html + // The child instance of PrusaSlicer has to reset SIGCHLD to its default, so that posix waitpid() and similar continue to work. + // See GH issue #5507 } catch (const std::exception& ex) { BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << bin_path.string() << "\": " << ex.what();