Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/xs
diff options
context:
space:
mode:
authorbubnikv <bubnikv@gmail.com>2018-06-27 11:33:14 +0300
committerbubnikv <bubnikv@gmail.com>2018-06-27 11:33:14 +0300
commit68e82a734812551689fe88fae980d9205e8f8edc (patch)
treeb37c688a9489d094c34cabe878181eb01edbd293 /xs
parent22463343a738f7a36b6d59cd038b77a7ec83d8de (diff)
parent645cc65d2b340752f390359ce3ed09a49b463588 (diff)
Merge branch 'master' into amf_activate_existing_presets
Diffstat (limited to 'xs')
-rw-r--r--xs/CMakeLists.txt10
-rw-r--r--xs/src/avrdude/arduino.c11
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp19
-rw-r--r--xs/src/slic3r/GUI/3DScene.cpp18
-rw-r--r--xs/src/slic3r/GUI/3DScene.hpp4
-rw-r--r--xs/src/slic3r/GUI/Field.cpp18
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.cpp80
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.hpp3
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.cpp86
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.hpp5
-rw-r--r--xs/src/slic3r/GUI/GLTexture.cpp5
-rw-r--r--xs/src/slic3r/GUI/GUI.cpp2
-rw-r--r--xs/src/slic3r/GUI/MsgDialog.hpp2
-rw-r--r--xs/src/slic3r/GUI/OptionsGroup.cpp9
-rw-r--r--xs/src/slic3r/GUI/Tab.cpp12
-rw-r--r--xs/src/slic3r/Utils/Http.cpp86
-rw-r--r--xs/src/slic3r/Utils/Http.hpp38
-rw-r--r--xs/src/slic3r/Utils/OctoPrint.cpp92
-rw-r--r--xs/src/slic3r/Utils/OctoPrint.hpp5
-rw-r--r--xs/xsp/BoundingBox.xsp4
-rw-r--r--xs/xsp/GUI_3DScene.xsp11
-rw-r--r--xs/xsp/Utils_OctoPrint.xsp2
22 files changed, 413 insertions, 109 deletions
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index 66c1cdd6a..f4d0b17ea 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -8,6 +8,7 @@ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
# Workaround for an old CMake, which does not understand CMAKE_CXX_STANDARD.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall" )
+ find_package(PkgConfig REQUIRED)
endif()
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUXX)
@@ -579,6 +580,15 @@ if (SLIC3R_PRUSACONTROL)
#add_compile_options(${AlienWx_CXX_FLAGS})
add_definitions(${AlienWx_DEFINITIONS})
set(wxWidgets_LIBRARIES ${AlienWx_LIBRARIES})
+ # On Linux / gtk, we need to have a direct access to gtk+ for some workarounds.
+ if (AlienWx_GUI_TOOLKIT STREQUAL "gtk2")
+ pkg_check_modules(GTK2 gtk+-2.0)
+ include_directories(${GTK2_INCLUDE_DIRS})
+ endif()
+ if (AlienWx_GUI_TOOLKIT STREQUAL "gtk3")
+ pkg_check_modules(GTK3 gtk+-3.0)
+ include_directories(${GTK3_INCLUDE_DIRS})
+ endif()
else ()
find_package(wxWidgets REQUIRED COMPONENTS base core adv html gl)
include(${wxWidgets_USE_FILE})
diff --git a/xs/src/avrdude/arduino.c b/xs/src/avrdude/arduino.c
index fc9f4571f..5a9cb465e 100644
--- a/xs/src/avrdude/arduino.c
+++ b/xs/src/avrdude/arduino.c
@@ -99,7 +99,7 @@ static int prusa_init_external_flash(PROGRAMMER * pgm)
avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
return -1;
} else if (strncmp(buffer, entry_magic_send, recv_size) != 0) {
- avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname);
+ avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code: `%*s`\n", progname, recv_size, buffer);
return -1;
}
@@ -116,7 +116,7 @@ static int prusa_init_external_flash(PROGRAMMER * pgm)
avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
return -1;
} else if (strncmp(buffer, entry_magic_cfm, recv_size) != 0) {
- avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname);
+ avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect cfm code: `%*s`\n", progname, recv_size, buffer);
return -1;
}
@@ -140,6 +140,13 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
serial_set_dtr_rts(&pgm->fd, 1);
usleep(50*1000);
+ // Sometimes there may be line noise generating input on the printer's USB-to-serial IC
+ // Here we try to clean its input buffer with a sequence of newlines (a minimum of 9 is needed):
+ const char cleanup_newlines[] = "\n\n\n\n\n\n\n\n\n\n";
+ if (serial_send(&pgm->fd, cleanup_newlines, sizeof(cleanup_newlines) - 1) < 0) {
+ return -1;
+ }
+
/*
* drain any extraneous input
*/
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 6486f2917..fbde83754 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -142,24 +142,21 @@ public:
}
m_gcode += "G1";
- if (rot.x != rotated_current_pos.x) {
- m_gcode += set_format_X(rot.x); // Transform current position back to wipe tower coordinates (was updated by set_format_X)
- m_current_pos.x = x;
- }
- if (rot.y != rotated_current_pos.y) {
+ if (std::abs(dx) > EPSILON)
+ m_gcode += set_format_X(rot.x);
+
+ if (std::abs(dy) > EPSILON)
m_gcode += set_format_Y(rot.y);
- m_current_pos.y = y;
- }
if (e != 0.f)
m_gcode += set_format_E(e);
if (f != 0.f && f != m_current_feedrate)
m_gcode += set_format_F(f);
-
-
-
-
+
+ m_current_pos.x = x;
+ m_current_pos.y = y;
+
// Update the elapsed time with a rough estimate.
m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f;
m_gcode += "\n";
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 1879b3082..a499825a4 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -433,6 +433,7 @@ std::vector<int> GLVolumeCollection::load_object(
v.extruder_id = extruder_id;
}
v.is_modifier = model_volume->modifier;
+ v.outside_printer_detection_enabled = !model_volume->modifier;
}
}
@@ -663,7 +664,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config)
bool contained = true;
for (GLVolume* volume : this->volumes)
{
- if (volume != nullptr)
+ if ((volume != nullptr) && !volume->is_modifier)
{
bool state = print_volume.contains(volume->transformed_bounding_box());
contained &= state;
@@ -1753,11 +1754,26 @@ bool _3DScene::init(wxGLCanvas* canvas)
return s_canvas_mgr.init(canvas);
}
+bool _3DScene::set_current(wxGLCanvas* canvas, bool force)
+{
+ return s_canvas_mgr.set_current(canvas, force);
+}
+
+void _3DScene::reset_current_canvas()
+{
+ s_canvas_mgr.set_current(nullptr, false);
+}
+
void _3DScene::set_active(wxGLCanvas* canvas, bool active)
{
s_canvas_mgr.set_active(canvas, active);
}
+void _3DScene::set_as_dirty(wxGLCanvas* canvas)
+{
+ s_canvas_mgr.set_as_dirty(canvas);
+}
+
unsigned int _3DScene::get_volumes_count(wxGLCanvas* canvas)
{
return s_canvas_mgr.get_volumes_count(canvas);
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index c6a166397..f45bab276 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -516,7 +516,11 @@ public:
static bool init(wxGLCanvas* canvas);
+ static bool set_current(wxGLCanvas* canvas, bool force);
+ static void reset_current_canvas();
+
static void set_active(wxGLCanvas* canvas, bool active);
+ static void set_as_dirty(wxGLCanvas* canvas);
static unsigned int get_volumes_count(wxGLCanvas* canvas);
static void reset_volumes(wxGLCanvas* canvas);
diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp
index 43c9e7db9..85247b41b 100644
--- a/xs/src/slic3r/GUI/Field.cpp
+++ b/xs/src/slic3r/GUI/Field.cpp
@@ -12,10 +12,20 @@ namespace Slic3r { namespace GUI {
wxString double_to_string(double const value)
{
- int precision = 10 * value - int(10 * value) == 0 ? 1 : 2;
- return value - int(value) == 0 ?
- wxString::Format(_T("%i"), int(value)) :
- wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None);
+ if (value - int(value) == 0)
+ return wxString::Format(_T("%i"), int(value));
+ else {
+ int precision = 4;
+ for (size_t p = 1; p < 4; p++)
+ {
+ double cur_val = pow(10, p)*value;
+ if (cur_val - int(cur_val) == 0) {
+ precision = p;
+ break;
+ }
+ }
+ return wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None);
+ }
}
void Field::PostInitialize(){
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
index f9c10017e..2148579e8 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
@@ -405,16 +405,27 @@ GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const
const PresetBundle* bundle = get_preset_bundle();
if (bundle != nullptr)
{
- const Preset& curr = bundle->printers.get_selected_preset();
- if (curr.config.has("bed_shape") && _are_equal(m_shape, dynamic_cast<const ConfigOptionPoints*>(curr.config.option("bed_shape"))->values))
+ const Preset* curr = &bundle->printers.get_selected_preset();
+ while (curr != nullptr)
{
- if ((curr.vendor != nullptr) && (curr.vendor->name == "Prusa Research"))
+ if (curr->config.has("bed_shape") && _are_equal(m_shape, dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values))
{
- if (boost::contains(curr.name, "MK2"))
- type = MK2;
- else if (boost::contains(curr.name, "MK3"))
- type = MK3;
+ if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research"))
+ {
+ if (boost::contains(curr->name, "MK2"))
+ {
+ type = MK2;
+ break;
+ }
+ else if (boost::contains(curr->name, "MK3"))
+ {
+ type = MK3;
+ break;
+ }
+ }
}
+
+ curr = bundle->printers.get_preset_parent(*curr);
}
}
@@ -1509,13 +1520,10 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl)
return true;
}
-bool GLCanvas3D::set_current()
+bool GLCanvas3D::set_current(bool force)
{
- if ((m_canvas != nullptr) && (m_context != nullptr))
- {
- m_canvas->SetCurrent(*m_context);
- return true;
- }
+ if ((force || m_active) && (m_canvas != nullptr) && (m_context != nullptr))
+ return m_canvas->SetCurrent(*m_context);
return false;
}
@@ -1525,6 +1533,11 @@ void GLCanvas3D::set_active(bool active)
m_active = active;
}
+void GLCanvas3D::set_as_dirty()
+{
+ m_dirty = true;
+}
+
unsigned int GLCanvas3D::get_volumes_count() const
{
return (unsigned int)m_volumes.volumes.size();
@@ -1532,8 +1545,13 @@ unsigned int GLCanvas3D::get_volumes_count() const
void GLCanvas3D::reset_volumes()
{
- if (set_current())
+
+ if (!m_volumes.empty())
{
+ // ensures this canvas is current
+ if ((m_canvas == nullptr) || !_3DScene::set_current(m_canvas, true))
+ return;
+
m_volumes.release_geometry();
m_volumes.clear();
m_dirty = true;
@@ -1831,8 +1849,8 @@ void GLCanvas3D::render()
if (!_is_shown_on_screen())
return;
- // ensures that the proper context is selected and that this canvas is initialized
- if (!set_current() || !_3DScene::init(m_canvas))
+ // ensures this canvas is current and initialized
+ if (!_3DScene::set_current(m_canvas, false) || !_3DScene::init(m_canvas))
return;
if (m_force_zoom_to_bed_enabled)
@@ -1913,6 +1931,11 @@ void GLCanvas3D::reload_scene(bool force)
return;
reset_volumes();
+
+ // ensures this canvas is current
+ if (!_3DScene::set_current(m_canvas, true))
+ return;
+
set_bed_shape(dynamic_cast<const ConfigOptionPoints*>(m_config->option("bed_shape"))->values);
if (!m_canvas->IsShown() && !force)
@@ -1984,6 +2007,10 @@ void GLCanvas3D::reload_scene(bool force)
void GLCanvas3D::load_print_toolpaths()
{
+ // ensures this canvas is current
+ if (!_3DScene::set_current(m_canvas, true))
+ return;
+
if (m_print == nullptr)
return;
@@ -2348,8 +2375,8 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
{
if ((m_canvas != nullptr) && (m_print != nullptr))
{
- // ensures that the proper context is selected
- if (!set_current())
+ // ensures that this canvas is current
+ if (!_3DScene::set_current(m_canvas, true))
return;
if (m_volumes.empty())
@@ -2666,7 +2693,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
#endif
}
else if (evt.LeftDClick() && (m_hover_volume_id != -1))
+ {
+ m_active = false;
m_on_double_click_callback.call();
+ m_active = true;
+ }
else if (evt.LeftDown() || evt.RightDown())
{
// If user pressed left or right button we first check whether this happened
@@ -2762,7 +2793,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
{
// if right clicking on volume, propagate event through callback
if (m_volumes.volumes[volume_idx]->hover)
+ {
+ m_active = false;
m_on_right_click_callback.call(pos.x, pos.y);
+ m_active = true;
+ }
}
}
}
@@ -2980,10 +3015,11 @@ void GLCanvas3D::_force_zoom_to_bed()
void GLCanvas3D::_resize(unsigned int w, unsigned int h)
{
- if (m_context == nullptr)
+ if ((m_canvas == nullptr) && (m_context == nullptr))
return;
- set_current();
+ // ensures that this canvas is current
+ _3DScene::set_current(m_canvas, false);
::glViewport(0, 0, w, h);
::glMatrixMode(GL_PROJECTION);
@@ -3569,9 +3605,11 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt)
Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z)
{
- if (!set_current())
+ if (m_canvas == nullptr)
return Pointf3(DBL_MAX, DBL_MAX, DBL_MAX);
+ _camera_tranform();
+
GLint viewport[4];
::glGetIntegerv(GL_VIEWPORT, viewport);
GLdouble modelview_matrix[16];
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp
index c503d1845..237044e83 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp
@@ -449,9 +449,10 @@ public:
bool init(bool useVBOs, bool use_legacy_opengl);
- bool set_current();
+ bool set_current(bool force);
void set_active(bool active);
+ void set_as_dirty();
unsigned int get_volumes_count() const;
void reset_volumes();
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
index f288ee456..62d17827a 100644
--- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
@@ -26,33 +26,23 @@ GLCanvas3DManager::GLInfo::GLInfo()
{
}
-bool GLCanvas3DManager::GLInfo::detect()
+void GLCanvas3DManager::GLInfo::detect()
{
const char* data = (const char*)::glGetString(GL_VERSION);
- if (data == nullptr)
- return false;
-
- version = data;
+ if (data != nullptr)
+ version = data;
data = (const char*)::glGetString(GL_SHADING_LANGUAGE_VERSION);
- if (data == nullptr)
- return false;
-
- glsl_version = data;
+ if (data != nullptr)
+ glsl_version = data;
data = (const char*)::glGetString(GL_VENDOR);
- if (data == nullptr)
- return false;
-
- vendor = data;
+ if (data != nullptr)
+ vendor = data;
data = (const char*)::glGetString(GL_RENDERER);
- if (data == nullptr)
- return false;
-
- renderer = data;
-
- return true;
+ if (data != nullptr)
+ renderer = data;
}
bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
@@ -94,10 +84,10 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
std::string line_end = format_as_html ? "<br>" : "\n";
out << h2_start << "OpenGL installation" << h2_end << line_end;
- out << b_start << "GL version: " << b_end << version << line_end;
- out << b_start << "Vendor: " << b_end << vendor << line_end;
- out << b_start << "Renderer: " << b_end << renderer << line_end;
- out << b_start << "GLSL version: " << b_end << glsl_version << line_end;
+ out << b_start << "GL version: " << b_end << (version.empty() ? "N/A" : version) << line_end;
+ out << b_start << "Vendor: " << b_end << (vendor.empty() ? "N/A" : vendor) << line_end;
+ out << b_start << "Renderer: " << b_end << (renderer.empty() ? "N/A" : renderer) << line_end;
+ out << b_start << "GLSL version: " << b_end << (glsl_version.empty() ? "N/A" : glsl_version) << line_end;
if (extensions)
{
@@ -125,6 +115,7 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
GLCanvas3DManager::GLCanvas3DManager()
: m_context(nullptr)
+ , m_current(nullptr)
, m_gl_initialized(false)
, m_use_legacy_opengl(false)
, m_use_VBOs(false)
@@ -195,15 +186,11 @@ void GLCanvas3DManager::init_gl()
if (!m_gl_initialized)
{
glewInit();
- if (m_gl_info.detect())
- {
- const AppConfig* config = GUI::get_app_config();
- m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1");
- m_use_VBOs = !m_use_legacy_opengl && m_gl_info.is_version_greater_or_equal_to(2, 0);
- m_gl_initialized = true;
- }
- else
- throw std::runtime_error(std::string("Unable to initialize OpenGL driver\n"));
+ m_gl_info.detect();
+ const AppConfig* config = GUI::get_app_config();
+ m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1");
+ m_use_VBOs = !m_use_legacy_opengl && m_gl_info.is_version_greater_or_equal_to(2, 0);
+ m_gl_initialized = true;
}
}
@@ -226,6 +213,34 @@ bool GLCanvas3DManager::init(wxGLCanvas* canvas)
return false;
}
+bool GLCanvas3DManager::set_current(wxGLCanvas* canvas, bool force)
+{
+ // given canvas is already current, return
+ if (m_current == canvas)
+ return true;
+
+ if (canvas == nullptr)
+ {
+ m_current = nullptr;
+ return true;
+ }
+
+ // set given canvas as current
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ {
+ bool res = it->second->set_current(force);
+ if (res)
+ {
+ m_current = canvas;
+ return true;
+ }
+ }
+
+ m_current = nullptr;
+ return false;
+}
+
void GLCanvas3DManager::set_active(wxGLCanvas* canvas, bool active)
{
CanvasesMap::iterator it = _get_canvas(canvas);
@@ -233,6 +248,13 @@ void GLCanvas3DManager::set_active(wxGLCanvas* canvas, bool active)
it->second->set_active(active);
}
+void GLCanvas3DManager::set_as_dirty(wxGLCanvas* canvas)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_as_dirty();
+}
+
unsigned int GLCanvas3DManager::get_volumes_count(wxGLCanvas* canvas) const
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
index 6989da791..35a1db206 100644
--- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
@@ -35,7 +35,7 @@ class GLCanvas3DManager
GLInfo();
- bool detect();
+ void detect();
bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const;
std::string to_string(bool format_as_html, bool extensions) const;
@@ -45,6 +45,7 @@ class GLCanvas3DManager
wxGLContext* m_context;
CanvasesMap m_canvases;
+ wxGLCanvas* m_current;
GLInfo m_gl_info;
bool m_gl_initialized;
bool m_use_legacy_opengl;
@@ -69,7 +70,9 @@ public:
bool init(wxGLCanvas* canvas);
+ bool set_current(wxGLCanvas* canvas, bool force);
void set_active(wxGLCanvas* canvas, bool active);
+ void set_as_dirty(wxGLCanvas* canvas);
unsigned int get_volumes_count(wxGLCanvas* canvas) const;
void reset_volumes(wxGLCanvas* canvas);
diff --git a/xs/src/slic3r/GUI/GLTexture.cpp b/xs/src/slic3r/GUI/GLTexture.cpp
index 593362e54..924920bd8 100644
--- a/xs/src/slic3r/GUI/GLTexture.cpp
+++ b/xs/src/slic3r/GUI/GLTexture.cpp
@@ -4,6 +4,8 @@
#include <wx/image.h>
+#include <boost/filesystem.hpp>
+
#include <vector>
#include <algorithm>
@@ -27,6 +29,9 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap
{
reset();
+ if (!boost::filesystem::exists(filename))
+ return false;
+
// Load a PNG with an alpha channel.
wxImage image;
if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG))
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 0360d73d0..a46fbc476 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -57,6 +57,7 @@
#include "../Utils/PresetUpdater.hpp"
#include "../Config/Snapshot.hpp"
#include "libslic3r/I18N.hpp"
+#include "3DScene.hpp"
namespace Slic3r { namespace GUI {
@@ -382,6 +383,7 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
save_language();
show_info(g_wxTabPanel, _(L("Application will be restarted")), _(L("Attention!")));
if (event_language_change > 0) {
+ _3DScene::remove_all_canvases();// remove all canvas before recreate GUI
wxCommandEvent event(event_language_change);
g_wxApp->ProcessEvent(event);
}
diff --git a/xs/src/slic3r/GUI/MsgDialog.hpp b/xs/src/slic3r/GUI/MsgDialog.hpp
index 2d570a0bf..ca349eb5c 100644
--- a/xs/src/slic3r/GUI/MsgDialog.hpp
+++ b/xs/src/slic3r/GUI/MsgDialog.hpp
@@ -28,6 +28,8 @@ struct MsgDialog : wxDialog
MsgDialog &operator=(const MsgDialog &) = delete;
virtual ~MsgDialog();
+ // TODO: refactor with CreateStdDialogButtonSizer usage
+
protected:
enum {
CONTENT_WIDTH = 500,
diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp
index 57659d03d..629a9f3a0 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.cpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.cpp
@@ -150,8 +150,15 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
// Build a label if we have it
wxStaticText* label=nullptr;
if (label_width != 0) {
+ long label_style = staticbox ? 0 : wxALIGN_RIGHT;
+#ifdef __WXGTK__
+ // workaround for correct text align of the StaticBox on Linux
+ // flags wxALIGN_RIGHT and wxALIGN_CENTRE don't work when Ellipsize flags are _not_ given.
+ // Text is properly aligned only when Ellipsize is checked.
+ label_style |= staticbox ? 0 : wxST_ELLIPSIZE_END;
+#endif /* __WXGTK__ */
label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ":"),
- wxDefaultPosition, wxSize(label_width, -1), staticbox ? 0 : wxALIGN_RIGHT);
+ wxDefaultPosition, wxSize(label_width, -1), label_style);
label->SetFont(label_font);
label->Wrap(label_width); // avoid a Linux/GTK bug
grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5);
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 3bfab54b3..f89ddddcc 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -2619,14 +2619,24 @@ void SavePresetWindow::accept()
if (!m_chosen_name.empty()) {
const char* unusable_symbols = "<>:/\\|?*\"";
bool is_unusable_symbol = false;
+ bool is_unusable_postfix = false;
+ const std::string unusable_postfix = "(modified)";
for (size_t i = 0; i < std::strlen(unusable_symbols); i++){
if (m_chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos){
is_unusable_symbol = true;
break;
}
}
+ if (m_chosen_name.find(unusable_postfix) != std::string::npos)
+ is_unusable_postfix = true;
+
if (is_unusable_symbol) {
- show_error(this, _(L("The supplied name is not valid; the following characters are not allowed:"))+" <>:/\\|?*\"");
+ show_error(this,_(L("The supplied name is not valid;")) + "\n" +
+ _(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
+ }
+ else if (is_unusable_postfix){
+ show_error(this, _(L("The supplied name is not valid;")) + "\n" +
+ _(L("the following postfix are not allowed:")) + "\n\t" + unusable_postfix);
}
else if (m_chosen_name.compare("- default -") == 0) {
show_error(this, _(L("The supplied name is not available.")));
diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp
index 0826284d8..47021d39f 100644
--- a/xs/src/slic3r/Utils/Http.cpp
+++ b/xs/src/slic3r/Utils/Http.cpp
@@ -3,13 +3,16 @@
#include <cstdlib>
#include <functional>
#include <thread>
-#include <tuple>
+#include <deque>
+#include <boost/filesystem/fstream.hpp>
#include <boost/format.hpp>
#include <curl/curl.h>
#include "../../libslic3r/libslic3r.h"
+namespace fs = boost::filesystem;
+
namespace Slic3r {
@@ -34,7 +37,11 @@ struct Http::priv
::curl_httppost *form;
::curl_httppost *form_end;
::curl_slist *headerlist;
+ // Used for reading the body
std::string buffer;
+ // Used for storing file streams added as multipart form parts
+ // Using a deque here because unlike vector it doesn't ivalidate pointers on insertion
+ std::deque<fs::ifstream> form_files;
size_t limit;
bool cancel;
@@ -50,6 +57,10 @@ struct Http::priv
static size_t writecb(void *data, size_t size, size_t nmemb, void *userp);
static int xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
static int xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow);
+ static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp);
+
+ void form_add_file(const char *name, const fs::path &path, const char* filename);
+
std::string curl_error(CURLcode curlcode);
std::string body_size_error();
void http_perform();
@@ -135,6 +146,46 @@ int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double
return xfercb(userp, dltotal, dlnow, ultotal, ulnow);
}
+size_t Http::priv::form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp)
+{
+ auto stream = reinterpret_cast<fs::ifstream*>(userp);
+
+ try {
+ stream->read(buffer, size * nitems);
+ } catch (...) {
+ return CURL_READFUNC_ABORT;
+ }
+
+ return stream->gcount();
+}
+
+void Http::priv::form_add_file(const char *name, const fs::path &path, const char* filename)
+{
+ // We can't use CURLFORM_FILECONTENT, because curl doesn't support Unicode filenames on Windows
+ // and so we use CURLFORM_STREAM with boost ifstream to read the file.
+
+ if (filename == nullptr) {
+ filename = path.string().c_str();
+ }
+
+ form_files.emplace_back(path, std::ios::in | std::ios::binary);
+ auto &stream = form_files.back();
+ stream.seekg(0, std::ios::end);
+ size_t size = stream.tellg();
+ stream.seekg(0);
+
+ if (filename != nullptr) {
+ ::curl_formadd(&form, &form_end,
+ CURLFORM_COPYNAME, name,
+ CURLFORM_FILENAME, filename,
+ CURLFORM_CONTENTTYPE, "application/octet-stream",
+ CURLFORM_STREAM, static_cast<void*>(&stream),
+ CURLFORM_CONTENTSLENGTH, static_cast<long>(size),
+ CURLFORM_END
+ );
+ }
+}
+
std::string Http::priv::curl_error(CURLcode curlcode)
{
return (boost::format("%1% (%2%)")
@@ -154,6 +205,7 @@ void Http::priv::http_perform()
::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb);
::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(this));
+ ::curl_easy_setopt(curl, CURLOPT_READFUNCTION, form_file_read_cb);
::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
#if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 32
@@ -183,14 +235,20 @@ void Http::priv::http_perform()
if (res != CURLE_OK) {
if (res == CURLE_ABORTED_BY_CALLBACK) {
- Progress dummyprogress(0, 0, 0, 0);
- bool cancel = true;
- if (progressfn) { progressfn(dummyprogress, cancel); }
+ if (cancel) {
+ // The abort comes from the request being cancelled programatically
+ Progress dummyprogress(0, 0, 0, 0);
+ bool cancel = true;
+ if (progressfn) { progressfn(dummyprogress, cancel); }
+ } else {
+ // The abort comes from the CURLOPT_READFUNCTION callback, which means reading file failed
+ if (errorfn) { errorfn(std::move(buffer), "Error reading file for file upload", http_status); }
+ }
}
else if (res == CURLE_WRITE_ERROR) {
- if (errorfn) { errorfn(std::move(buffer), std::move(body_size_error()), http_status); }
+ if (errorfn) { errorfn(std::move(buffer), body_size_error(), http_status); }
} else {
- if (errorfn) { errorfn(std::move(buffer), std::move(curl_error(res)), http_status); }
+ if (errorfn) { errorfn(std::move(buffer), curl_error(res), http_status); }
};
} else {
if (completefn) {
@@ -265,17 +323,15 @@ Http& Http::form_add(const std::string &name, const std::string &contents)
return *this;
}
-Http& Http::form_add_file(const std::string &name, const std::string &filename)
+Http& Http::form_add_file(const std::string &name, const fs::path &path)
{
- if (p) {
- ::curl_formadd(&p->form, &p->form_end,
- CURLFORM_COPYNAME, name.c_str(),
- CURLFORM_FILE, filename.c_str(),
- CURLFORM_CONTENTTYPE, "application/octet-stream",
- CURLFORM_END
- );
- }
+ if (p) { p->form_add_file(name.c_str(), path.c_str(), nullptr); }
+ return *this;
+}
+Http& Http::form_add_file(const std::string &name, const fs::path &path, const std::string &filename)
+{
+ if (p) { p->form_add_file(name.c_str(), path.c_str(), filename.c_str()); }
return *this;
}
diff --git a/xs/src/slic3r/Utils/Http.hpp b/xs/src/slic3r/Utils/Http.hpp
index 7ed8196e6..73656bf88 100644
--- a/xs/src/slic3r/Utils/Http.hpp
+++ b/xs/src/slic3r/Utils/Http.hpp
@@ -4,6 +4,7 @@
#include <memory>
#include <string>
#include <functional>
+#include <boost/filesystem/path.hpp>
namespace Slic3r {
@@ -16,11 +17,11 @@ private:
public:
struct Progress
{
- size_t dltotal;
- size_t dlnow;
- size_t ultotal;
- size_t ulnow;
-
+ size_t dltotal; // Total bytes to download
+ size_t dlnow; // Bytes downloaded so far
+ size_t ultotal; // Total bytes to upload
+ size_t ulnow; // Bytes uploaded so far
+
Progress(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) :
dltotal(dltotal), dlnow(dlnow), ultotal(ultotal), ulnow(ulnow)
{}
@@ -33,6 +34,10 @@ public:
Http(Http &&other);
+ // Note: strings are expected to be UTF-8-encoded
+
+ // These are the primary constructors that create a HTTP object
+ // for a GET and a POST request respectively.
static Http get(std::string url);
static Http post(std::string url);
~Http();
@@ -41,21 +46,42 @@ public:
Http& operator=(const Http &) = delete;
Http& operator=(Http &&) = delete;
+ // Sets a maximum size of the data that can be received. The default is 5MB.
Http& size_limit(size_t sizeLimit);
+ // Sets a HTTP header field.
Http& header(std::string name, const std::string &value);
+ // Removes a header field.
Http& remove_header(std::string name);
+ // Sets a CA certificate file for usage with HTTPS. This is only supported on some backends,
+ // specifically, this is supported with OpenSSL and NOT supported with Windows and OS X native certificate store.
+ // See also ca_file_supported().
Http& ca_file(const std::string &filename);
+ // Add a HTTP multipart form field
Http& form_add(const std::string &name, const std::string &contents);
- Http& form_add_file(const std::string &name, const std::string &filename);
+ // Add a HTTP multipart form file data contents, `name` is the name of the part
+ Http& form_add_file(const std::string &name, const boost::filesystem::path &path);
+ // Same as above except also override the file's filename with a custom one
+ Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename);
+ // Callback called on HTTP request complete
Http& on_complete(CompleteFn fn);
+ // Callback called on an error occuring at any stage of the requests: Url parsing, DNS lookup,
+ // TCP connection, HTTP transfer, and finally also when the response indicates an error (status >= 400).
+ // Therefore, a response body may or may not be present.
Http& on_error(ErrorFn fn);
+ // Callback called on data download/upload prorgess (called fairly frequently).
+ // See the `Progress` structure for description of the data passed.
+ // Writing a true-ish value into the cancel reference parameter cancels the request.
Http& on_progress(ProgressFn fn);
+ // Starts performing the request in a background thread
Ptr perform();
+ // Starts performing the request on the current thread
void perform_sync();
+ // Cancels a request in progress
void cancel();
+ // Tells whether current backend supports seting up a CA file using ca_file()
static bool ca_file_supported();
private:
Http(const std::string &url);
diff --git a/xs/src/slic3r/Utils/OctoPrint.cpp b/xs/src/slic3r/Utils/OctoPrint.cpp
index e63a16c38..86049de16 100644
--- a/xs/src/slic3r/Utils/OctoPrint.cpp
+++ b/xs/src/slic3r/Utils/OctoPrint.cpp
@@ -1,20 +1,65 @@
#include "OctoPrint.hpp"
#include <algorithm>
+#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
+#include <boost/log/trivial.hpp>
#include <wx/frame.h>
#include <wx/event.h>
#include <wx/progdlg.h>
+#include <wx/sizer.h>
+#include <wx/stattext.h>
+#include <wx/textctrl.h>
+#include <wx/checkbox.h>
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/GUI.hpp"
+#include "slic3r/GUI/MsgDialog.hpp"
#include "Http.hpp"
+namespace fs = boost::filesystem;
+
namespace Slic3r {
+struct SendDialog : public GUI::MsgDialog
+{
+ wxTextCtrl *txt_filename;
+ wxCheckBox *box_print;
+
+ SendDialog(const fs::path &path) :
+ MsgDialog(nullptr, _(L("Send G-Code to printer")), _(L("Upload to OctoPrint with the following filename:")), wxID_NONE),
+ txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())),
+ box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload"))))
+ {
+ auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed.")));
+ label_dir_hint->Wrap(CONTENT_WIDTH);
+
+ content_sizer->Add(txt_filename, 0, wxEXPAND);
+ content_sizer->Add(label_dir_hint);
+ content_sizer->AddSpacer(VERT_SPACING);
+ content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
+
+ btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
+
+ txt_filename->SetFocus();
+ wxString stem(path.stem().wstring());
+ txt_filename->SetSelection(0, stem.Length());
+
+ Fit();
+ }
+
+ fs::path filename() const {
+ return fs::path(txt_filename->GetValue().wx_str());
+ }
+
+ bool print() const { return box_print->GetValue(); }
+};
+
+
+
OctoPrint::OctoPrint(DynamicPrintConfig *config) :
host(config->opt_string("octoprint_host")),
apikey(config->opt_string("octoprint_apikey")),
@@ -27,24 +72,39 @@ bool OctoPrint::test(wxString &msg) const
// it is ok to refer to `msg` from within the closure
bool res = true;
+ auto url = make_url("api/version");
+
+ BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Get version at: %1%") % url;
- auto url = std::move(make_url("api/version"));
auto http = Http::get(std::move(url));
set_auth(http);
http.on_error([&](std::string, std::string error, unsigned status) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error getting version: %1% (HTTP %2%)") % error % status;
res = false;
msg = format_error(error, status);
})
+ .on_complete([&](std::string body, unsigned) {
+ BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body;
+ })
.perform_sync();
return res;
}
-bool OctoPrint::send_gcode(const std::string &filename, bool print) const
+bool OctoPrint::send_gcode(const std::string &filename) const
{
enum { PROGRESS_RANGE = 1000 };
const auto errortitle = _(L("Error while uploading to the OctoPrint server"));
+ fs::path filepath(filename);
+
+ SendDialog send_dialog(filepath.filename());
+ if (send_dialog.ShowModal() != wxID_OK) { return false; }
+
+ const bool print = send_dialog.print();
+ const auto upload_filepath = send_dialog.filename();
+ const auto upload_filename = upload_filepath.filename();
+ const auto upload_parent_path = upload_filepath.parent_path();
wxProgressDialog progress_dialog(
_(L("OctoPrint upload")),
@@ -61,14 +121,26 @@ bool OctoPrint::send_gcode(const std::string &filename, bool print) const
bool res = true;
- auto http = Http::post(std::move(make_url("api/files/local")));
+ auto url = make_url("api/files/local");
+
+ BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%")
+ % filepath.string()
+ % url
+ % upload_filename.string()
+ % upload_parent_path.string()
+ % print;
+
+ auto http = Http::post(std::move(url));
set_auth(http);
http.form_add("print", print ? "true" : "false")
- .form_add_file("file", filename)
+ .form_add("path", upload_parent_path.string())
+ .form_add_file("file", filename, upload_filename.string())
.on_complete([&](std::string body, unsigned status) {
+ BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body;
progress_dialog.Update(PROGRESS_RANGE);
})
.on_error([&](std::string body, std::string error, unsigned status) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1% (HTTP %2%)") % error % status;
auto errormsg = wxString::Format("%s: %s", errortitle, format_error(error, status));
GUI::show_error(&progress_dialog, std::move(errormsg));
res = false;
@@ -102,24 +174,24 @@ std::string OctoPrint::make_url(const std::string &path) const
{
if (host.find("http://") == 0 || host.find("https://") == 0) {
if (host.back() == '/') {
- return std::move((boost::format("%1%%2%") % host % path).str());
+ return (boost::format("%1%%2%") % host % path).str();
} else {
- return std::move((boost::format("%1%/%2%") % host % path).str());
+ return (boost::format("%1%/%2%") % host % path).str();
}
} else {
- return std::move((boost::format("http://%1%/%2%") % host % path).str());
+ return (boost::format("http://%1%/%2%") % host % path).str();
}
}
-wxString OctoPrint::format_error(std::string error, unsigned status)
+wxString OctoPrint::format_error(const std::string &error, unsigned status)
{
- const wxString wxerror = error;
+ auto wxerror = wxString::FromUTF8(error.data());
if (status != 0) {
return wxString::Format("HTTP %u: %s", status,
(status == 401 ? _(L("Invalid API key")) : wxerror));
} else {
- return std::move(wxerror);
+ return wxerror;
}
}
diff --git a/xs/src/slic3r/Utils/OctoPrint.hpp b/xs/src/slic3r/Utils/OctoPrint.hpp
index 744b4fcc1..a8599faa9 100644
--- a/xs/src/slic3r/Utils/OctoPrint.hpp
+++ b/xs/src/slic3r/Utils/OctoPrint.hpp
@@ -17,7 +17,8 @@ public:
OctoPrint(DynamicPrintConfig *config);
bool test(wxString &curl_msg) const;
- bool send_gcode(const std::string &filename, bool print = false) const;
+ // Send gcode file to octoprint, filename is expected to be in UTF-8
+ bool send_gcode(const std::string &filename) const;
private:
std::string host;
std::string apikey;
@@ -25,7 +26,7 @@ private:
void set_auth(Http &http) const;
std::string make_url(const std::string &path) const;
- static wxString format_error(std::string error, unsigned status);
+ static wxString format_error(const std::string &error, unsigned status);
};
diff --git a/xs/xsp/BoundingBox.xsp b/xs/xsp/BoundingBox.xsp
index df8e6baea..ebeb17822 100644
--- a/xs/xsp/BoundingBox.xsp
+++ b/xs/xsp/BoundingBox.xsp
@@ -29,6 +29,10 @@
int x_max() %code{% RETVAL = THIS->max.x; %};
int y_min() %code{% RETVAL = THIS->min.y; %};
int y_max() %code{% RETVAL = THIS->max.y; %};
+ void set_x_min(double val) %code{% THIS->min.x = val; %};
+ void set_x_max(double val) %code{% THIS->max.x = val; %};
+ void set_y_min(double val) %code{% THIS->min.y = val; %};
+ void set_y_max(double val) %code{% THIS->max.y = val; %};
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld;%ld,%ld", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %};
bool defined() %code{% RETVAL = THIS->defined; %};
diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp
index 29f35293b..8ee88543d 100644
--- a/xs/xsp/GUI_3DScene.xsp
+++ b/xs/xsp/GUI_3DScene.xsp
@@ -191,12 +191,23 @@ remove_all_canvases()
_3DScene::remove_all_canvases();
void
+reset_current_canvas()
+ CODE:
+ _3DScene::reset_current_canvas();
+
+void
set_active(canvas, active)
SV *canvas;
bool active;
CODE:
_3DScene::set_active((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), active);
+void
+set_as_dirty(canvas)
+ SV *canvas;
+ CODE:
+ _3DScene::set_as_dirty((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+
unsigned int
get_volumes_count(canvas)
SV *canvas;
diff --git a/xs/xsp/Utils_OctoPrint.xsp b/xs/xsp/Utils_OctoPrint.xsp
index 282a3055d..28610cb01 100644
--- a/xs/xsp/Utils_OctoPrint.xsp
+++ b/xs/xsp/Utils_OctoPrint.xsp
@@ -9,5 +9,5 @@
OctoPrint(DynamicPrintConfig *config);
~OctoPrint();
- bool send_gcode(std::string filename, bool print = false) const;
+ bool send_gcode(std::string filename) const;
};