mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 12:41:20 -06:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into gui_improvements
This commit is contained in:
		
						commit
						d0d83526b4
					
				
					 32 changed files with 1917 additions and 143 deletions
				
			
		|  | @ -42,6 +42,21 @@ message("PATH: $ENV{PATH}") | |||
| message("PERL5LIB: $ENV{PERL5LIB}") | ||||
| find_package(Perl REQUIRED) | ||||
| 
 | ||||
| # CMAKE_PREFIX_PATH is used to point CMake to the remaining dependencies (Boost, TBB, ...) | ||||
| # We pick it from environment if it is not defined in another way | ||||
| if(NOT DEFINED CMAKE_PREFIX_PATH) | ||||
| 	if(DEFINED ENV{CMAKE_PREFIX_PATH}) | ||||
| 		set(CMAKE_PREFIX_PATH "$ENV{CMAKE_PREFIX_PATH}") | ||||
| 	endif() | ||||
| endif() | ||||
| 
 | ||||
| if (MSVC) | ||||
| 	# By default the startup project in MSVC is the 'ALL_BUILD' cmake-created project, | ||||
| 	# but we want 'slic3r' as the startup one because debugging run command is associated with it. | ||||
| 	# (Unfortunatelly it cannot be associated with ALL_BUILD using CMake.) | ||||
| 	set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT slic3r) | ||||
| endif () | ||||
| 
 | ||||
| add_subdirectory(xs) | ||||
| 
 | ||||
| enable_testing () | ||||
|  |  | |||
							
								
								
									
										59
									
								
								cmake/modules/FindCURL.cmake
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								cmake/modules/FindCURL.cmake
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| # Distributed under the OSI-approved BSD 3-Clause License.  See accompanying | ||||
| # file Copyright.txt or https://cmake.org/licensing for details. | ||||
| 
 | ||||
| #.rst: | ||||
| # FindCURL | ||||
| # -------- | ||||
| # | ||||
| # Find curl | ||||
| # | ||||
| # Find the native CURL headers and libraries. | ||||
| # | ||||
| # :: | ||||
| # | ||||
| #   CURL_INCLUDE_DIRS   - where to find curl/curl.h, etc. | ||||
| #   CURL_LIBRARIES      - List of libraries when using curl. | ||||
| #   CURL_FOUND          - True if curl found. | ||||
| #   CURL_VERSION_STRING - the version of curl found (since CMake 2.8.8) | ||||
| 
 | ||||
| # Look for the header file. | ||||
| find_path(CURL_INCLUDE_DIR NAMES curl/curl.h) | ||||
| mark_as_advanced(CURL_INCLUDE_DIR) | ||||
| 
 | ||||
| # Look for the library (sorted from most current/relevant entry to least). | ||||
| find_library(CURL_LIBRARY NAMES | ||||
|     curl | ||||
|   # Windows MSVC Makefile: | ||||
|     libcurl_a | ||||
|   # Windows MSVC prebuilts: | ||||
|     curllib | ||||
|     libcurl_imp | ||||
|     curllib_static | ||||
|   # Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip): | ||||
|     libcurl | ||||
| ) | ||||
| mark_as_advanced(CURL_LIBRARY) | ||||
| 
 | ||||
| if(CURL_INCLUDE_DIR) | ||||
|   foreach(_curl_version_header curlver.h curl.h) | ||||
|     if(EXISTS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}") | ||||
|       file(STRINGS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}" curl_version_str REGEX "^#define[\t ]+LIBCURL_VERSION[\t ]+\".*\"") | ||||
| 
 | ||||
|       string(REGEX REPLACE "^#define[\t ]+LIBCURL_VERSION[\t ]+\"([^\"]*)\".*" "\\1" CURL_VERSION_STRING "${curl_version_str}") | ||||
|       unset(curl_version_str) | ||||
|       break() | ||||
|     endif() | ||||
|   endforeach() | ||||
| endif() | ||||
| 
 | ||||
| find_package_handle_standard_args(CURL | ||||
|                                   REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR | ||||
|                                   VERSION_VAR CURL_VERSION_STRING) | ||||
| 
 | ||||
| if(CURL_FOUND) | ||||
|   set(CURL_LIBRARIES ${CURL_LIBRARY}) | ||||
|   set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR}) | ||||
| 
 | ||||
|   message(STATUS "  Curl libraries:        = ${CURL_LIBRARIES}") | ||||
|   message(STATUS "  Curl include dirs:     = ${CURL_INCLUDE_DIRS}") | ||||
| endif() | ||||
							
								
								
									
										9
									
								
								cmake/msvc/slic3r.wperl64d.props
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								cmake/msvc/slic3r.wperl64d.props
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
|   <PropertyGroup> | ||||
|     <LocalDebuggerCommand>C:\wperl64d\bin\perl.exe</LocalDebuggerCommand> | ||||
|     <LocalDebuggerCommandArguments>slic3r.pl</LocalDebuggerCommandArguments> | ||||
|     <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> | ||||
|     <LocalDebuggerWorkingDirectory>..\..</LocalDebuggerWorkingDirectory> | ||||
|   </PropertyGroup> | ||||
| </Project> | ||||
							
								
								
									
										11
									
								
								cmake/msvc/xs.wperl64d.props
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								cmake/msvc/xs.wperl64d.props
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
|   <ImportGroup Label="PropertySheets"> | ||||
|   </ImportGroup> | ||||
|   <PropertyGroup Label="UserMacros" /> | ||||
|   <PropertyGroup> | ||||
|     <ExecutablePath>$(VC_ExecutablePath_x64);$(WindowsSDK_ExecutablePath);$(VS_ExecutablePath);$(MSBuild_ExecutablePath);$(FxCopDir);$(PATH);c:\wperl64d\bin\;</ExecutablePath> | ||||
|   </PropertyGroup> | ||||
|   <ItemDefinitionGroup /> | ||||
|   <ItemGroup /> | ||||
| </Project> | ||||
							
								
								
									
										2
									
								
								doc/How to build - UNIX.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								doc/How to build - UNIX.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| # Building Slic3r PE on Linux/UNIX | ||||
| 
 | ||||
							
								
								
									
										86
									
								
								doc/How to build - Windows.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								doc/How to build - Windows.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | |||
| # Building Slic3r PE on Microsoft Windows | ||||
| 
 | ||||
| The currently supported way of building Slic3r PE on Windows is with MS Visual Studio 2013 | ||||
| using our Perl binary distribution (compiled from official Perl sources). | ||||
| You can use the free [Visual Studio 2013 Community Edition](https://www.visualstudio.com/vs/older-downloads/). | ||||
| 
 | ||||
| Other setups (such as mingw + Strawberry Perl) _may_ work, but we cannot guarantee this will work | ||||
| and cannot provide guidance. | ||||
| 
 | ||||
| 
 | ||||
| ### Geting the dependencies | ||||
| 
 | ||||
| First, download and upnack our Perl + wxWidgets binary distribution: | ||||
| 
 | ||||
|   - 32 bit, release mode: [wperl32-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl32-5.24.0-2018-03-02.7z) | ||||
|   - 64 bit, release mode: [wperl64-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl64-5.24.0-2018-03-02.7z) | ||||
|   - 64 bit, release mode + debug symbols: [wperl64d-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl64d-5.24.0-2018-03-02.7z) | ||||
| 
 | ||||
| It is recommended to unpack this package into `C:\`. | ||||
| 
 | ||||
| Apart from wxWidgets and Perl, you will also need additional dependencies: | ||||
| 
 | ||||
|   - Boost | ||||
|   - Intel TBB | ||||
|   - libcurl | ||||
| 
 | ||||
| We have prepared a binary package of the listed libraries: | ||||
| 
 | ||||
|   - 32 bit: [slic3r-destdir-32.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-32.7z) | ||||
|   - 64 bit: [slic3r-destdir-64.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-64.7z) | ||||
| 
 | ||||
| It is recommended you unpack this package into `C:\local\` as the environment | ||||
| setup script expects it there. | ||||
| 
 | ||||
| Alternatively you can also compile the additional dependencies yourself. | ||||
| There is a [powershell script](./deps-build/windows/slic3r-makedeps.ps1) which automates this process. | ||||
| 
 | ||||
| ### Building Slic3r PE | ||||
| 
 | ||||
| Once the dependencies are set up in their respective locations, | ||||
| go to the `wperl*` directory extracted earlier and launch the `cmdline.lnk` file | ||||
| which opens a command line prompt with appropriate environment variables set up. | ||||
| 
 | ||||
| In this command line, `cd` into the directory with Slic3r sources | ||||
| and use these commands to build the Slic3r from the command line: | ||||
| 
 | ||||
|     perl Build.PL | ||||
|     perl Build.PL --gui | ||||
|     mkdir build | ||||
|     cd build | ||||
|     cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release | ||||
|     nmake | ||||
|     ctest --verbose    # TODO: ??? | ||||
|     cd .. | ||||
|     perl slic3r.pl | ||||
| 
 | ||||
| The above commands use `nmake` Makefiles. | ||||
| You may also build Slic3r PE with other build tools: | ||||
| 
 | ||||
| 
 | ||||
| ### Building with Visual Studio | ||||
| 
 | ||||
| To build, lanuch and/or debug Slic3r PE with Visual Studio (64 bits), replace the `cmake` command with: | ||||
| 
 | ||||
|     cmake .. -G "Visual Studio 12 Win64" -DCMAKE_CONFIGURATION_TYPES=Release;RelWithDebInfo || exit /b | ||||
| 
 | ||||
| For the 32-bit variant, use: | ||||
| 
 | ||||
|     cmake .. -G "Visual Studio 12" -DCMAKE_CONFIGURATION_TYPES=Release;RelWithDebInfo || exit /b | ||||
| 
 | ||||
| After `cmake` has finished, go to the `Slic3r\build` directory and open the `Slic3r.sln` solution file. | ||||
| This should open Visual Studio and load all the Slic3r solution containing all the projects. | ||||
| Make sure you use Visual Studio 2013 to open the solution. | ||||
| 
 | ||||
| You can then use the usual Visual Studio controls to build Slic3r. | ||||
| If you want to run or debug Slic3r from within Visual Studio, make sure the `slic3r` project is activated. | ||||
| There are multiple projects in the Slic3r solution, but only the `slic3r` project is configured with the right | ||||
| commands to run Slic3r. | ||||
| 
 | ||||
| 
 | ||||
| ### Building with ninja | ||||
| 
 | ||||
| To use [Ninja](TODO), replace the `cmake` and `nmake` commands with: | ||||
| 
 | ||||
|     cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release | ||||
|     ninja | ||||
							
								
								
									
										76
									
								
								doc/Localization_guide.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								doc/Localization_guide.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,76 @@ | |||
| # Localization and translation guide | ||||
| 
 | ||||
| The purpose of this guide is to describe how to contribute to the Slic3rPE translations. We use GNUgettext for extracting string resources from the project and PoEdit for editing translations. | ||||
| 
 | ||||
| Those are possible to download here:  | ||||
| -    https://sourceforge.net/directory/os:windows/?q=gnu+gettext GNUgettext package contains a set of tools to extract strings from the source code and to create the translation Catalog. | ||||
| -    https://poedit.net PoEdit provides good interface for the translators. | ||||
| 
 | ||||
| After GNUgettext is installed it is recommended to add the path to gettext/bin to PATH variable. | ||||
| 
 | ||||
| Full manual for GNUgettext you can see here: http://www.gnu.org/software/gettext/manual/gettext.html | ||||
| 
 | ||||
| 
 | ||||
| ### Scenario 1. How do I add a translation or fix the existing translation | ||||
| 1. Get PO-file from corresponding folder here: | ||||
| https://github.com/prusa3d/Slic3r/tree/master/resources/localization | ||||
| 2. Open this file in PoEdit as "Edit a translation" | ||||
| 3. Apply your corrections to translation | ||||
| 4. Push changed Slic3rPE.po and Slic3rPE.mo (will create automatically after saving of Slic3r.po in PoEdit) back to to the enter folder. | ||||
| 
 | ||||
| ### Scenario 2. How do I add a new language support | ||||
| 1. Get file Slic3rPE.pot here : | ||||
| https://github.com/prusa3d/Slic3r/tree/master/resources/localization | ||||
| 2. Open it in PoEdit for "Create new translation" | ||||
| 3. Select Translation Language (for example French). | ||||
| 4. As a result you will have fr.po - the file contaning translation to French. | ||||
| Notice. When the transtation is complete you need to: | ||||
|     - Rename the file to Slic3rPE.po | ||||
|     - Click "Save file" button. Slic3rPE.mo will be created immediatly | ||||
|     - Both Slic3rPE.po and Slic3rPE.mo have to be saved here: | ||||
| https://github.com/prusa3d/Slic3r/tree/master/resources/localization/fr | ||||
| ( name of folder "fr" means "French" - the translation language).  | ||||
| 
 | ||||
| ### Scenario 3. How do I add a new text resource when implementing a feature to Slic3rPE | ||||
| Each string resource in Slic3rPE available for translation needs to be explicitly marked using L() macro like this: | ||||
| ```C++ | ||||
| auto msg = L("This message to be localized") | ||||
| ``` | ||||
| To get translated text use one of needed macro/function (`_(s)`, `_CHB(s)` or `L_str(s)` ). | ||||
| If you add new file resourse, add it to list of files contaned macro `L()` | ||||
| 
 | ||||
| ### Scenario 4. How do I use GNUgettext to localize my own application taking Slic3rPE as an example | ||||
| 
 | ||||
| 1.  For conviniance create list of files with this macro `L(s)`. We have  | ||||
| https://github.com/prusa3d/Slic3r/tree/master/resources/localization/list.txt. | ||||
| 
 | ||||
| 2.  Create template file(*.POT) with GNUgettext command: | ||||
|     ``` | ||||
|         xgettext --keyword=L --from-code=UTF-8 --debug -o Slic3rPE.pot -f list.txt | ||||
|     ``` | ||||
| 
 | ||||
|     Use flag `--from-code=UTF-8` to specify that the source strings are in UTF-8 encoding | ||||
|     Use flag `--debug` to correctly extract formated strings(used %d, %s etc.) | ||||
| 
 | ||||
| 3.  Create PO- and MO-files for your project as described above. | ||||
| 
 | ||||
| 4.  To merge old PO-file with strings from creaded new POT-file use command: | ||||
|     ``` | ||||
|         msgmerge -N -o new.po old.po new.pot | ||||
|     ``` | ||||
|     Use option `-N` to not using fuzzy matching when an exact match is not found. | ||||
| 
 | ||||
| 5.  To concatenate old PO-file with strings from new PO-file use command: | ||||
|     ``` | ||||
|         msgcat -o new.po old.po | ||||
|     ``` | ||||
| 
 | ||||
| 6.  Create an English translation catalog with command: | ||||
|     ```     | ||||
|         msgen -o new.po old.po | ||||
|     ``` | ||||
|     Notice, in this Catalog it will be totally same strings for initial text and translated. | ||||
| 
 | ||||
| When you have Catalog to translation open POT or PO file in PoEdit and start to translation. | ||||
| It's very important to keep attention to every gaps and punctuation. Especially with  | ||||
| formated strings. (used %d, %s etc.)  | ||||
|  | @ -1,45 +0,0 @@ | |||
| From the begining you need to have GNUgettext and PoEdit. | ||||
| GNUgettext package contains a set of tools to extract strings from the source code and  | ||||
| to create the Catalog to translation. | ||||
| PoEdit provide good interface for the translators. | ||||
| 
 | ||||
| Those are possible to download here:  | ||||
| 	GNUgettext	- https://sourceforge.net/directory/os:windows/?q=gnu+gettext | ||||
| 	PoEdit		- https://poedit.net/ | ||||
| 
 | ||||
| When GNUgettext and poEdit are downloaded and installationed, next step is  | ||||
| to add path to gettext/bin directory to your PATH variable. | ||||
| You can use gettext from cmdline now. | ||||
| 
 | ||||
| 
 | ||||
| In Slic3rPE we have one macro (L) used to markup strings to localizations. | ||||
| 
 | ||||
| So, to create Catalog to translation there are next steps: | ||||
| 	1.  create list of files with this macro (list.txt) | ||||
| 
 | ||||
| 	2.  create template file(*.POT) with command: | ||||
| 		xgettext --keyword=L --from-code=UTF-8 --debug -o Slic3rPE.pot -f list.txt | ||||
| 	    Use flag --from-code=UTF-8 to specify that the source strings are in UTF-8 encoding | ||||
| 	    Use flag --debug to correctly extract formated strings(used %d, %s etc.) | ||||
| 
 | ||||
| 	3.1 if you start to create PO-file for your projest just open this POT-file in PoEdit. | ||||
| 	    When you select translation language after first opening of POT-files,  | ||||
| 	    PO-file will be created immediatly. | ||||
| 
 | ||||
| 	3.2 if you already have PO-file created before, you have to merge old PO-file with | ||||
| 	    strings from creaded POT-file. You can do that with command:  | ||||
| 		msgmerge -N -o new.po old.po new.pot | ||||
| 	    Use option -N to not using fuzzy matching when an exact match is not found. | ||||
| 
 | ||||
| 	3.3 if you already have PO-file created before and new PO-file created from new sourse files | ||||
| 	    which is not related with first one, you have to concatenate old PO-file with | ||||
| 	    strings from new PO-file. You can do that with command: | ||||
| 		msgcat -o new.po old.po | ||||
| 
 | ||||
| 	4.  create an English translation catalog with command: | ||||
| 		msgen -o new.po old.po | ||||
| 	    Notice, in this Catalog it will be totally same strings for initial text and translated. | ||||
| 
 | ||||
| When you have Catalog to translation open POT or PO file in PoEdit and start to translation. | ||||
| It's very important to keep attention to every gaps and punctuation. Especially with  | ||||
| formated strings. (used %d, %s etc.)  | ||||
							
								
								
									
										132
									
								
								doc/deps-build/unix-static/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								doc/deps-build/unix-static/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,132 @@ | |||
| 
 | ||||
| #
 | ||||
| # This makefile downloads, configures and builds Slic3r PE dependencies for Unix.
 | ||||
| # (That is, all dependencies except perl + wxWidgets.)
 | ||||
| # The libraries are installed in DESTDIR, which you can customize like so:
 | ||||
| #
 | ||||
| #     DESTDIR=foo/bar make
 | ||||
| #
 | ||||
| # The default DESTDIR is ~/slic3r-destdir
 | ||||
| # If the DESTDIR doesn't exits, the makefile tries to create it
 | ||||
| #
 | ||||
| # To pass the DESTDIR path along to cmake, set the use CMAKE_PREFIX_PATH variable
 | ||||
| # and set it to $DESTDIR/usr/local
 | ||||
| #
 | ||||
| # You can also customize the NPROC variable in the same way to configure the number
 | ||||
| # of cores the build process uses. By default this is set to what the `nproc` command says.
 | ||||
| #
 | ||||
| 
 | ||||
| 
 | ||||
| DESTDIR ?= $(HOME)/slic3r-destdir | ||||
| NPROC ?= $(shell nproc) | ||||
| 
 | ||||
| 
 | ||||
| BOOST = boost_1_66_0 | ||||
| TBB_SHA = a0dc9bf76d0120f917b641ed095360448cabc85b | ||||
| TBB = tbb-$(TBB_SHA) | ||||
| OPENSSL = openssl-OpenSSL_1_1_0g | ||||
| CURL = curl-7.58.0 | ||||
| 
 | ||||
| .PHONY: all destdir boost libcurl libopenssl libtbb | ||||
| 
 | ||||
| all: destdir boost libtbb libcurl | ||||
| 	@echo | ||||
| 	@echo "All done!" | ||||
| 	@echo | ||||
| 
 | ||||
| destdir: | ||||
| 	mkdir -p $(DESTDIR) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| boost: $(BOOST).tar.gz | ||||
| 	tar -zxvf $(BOOST).tar.gz | ||||
| 	cd $(BOOST) && ./bootstrap.sh --with-libraries=system,filesystem,thread,log,locale,regex --prefix=$(DESTDIR)/usr/local | ||||
| 	cd $(BOOST) && ./b2 \
 | ||||
| 		-j $(NPROC) \
 | ||||
| 		link=static \
 | ||||
| 		variant=release \
 | ||||
| 		threading=multi \
 | ||||
| 		boost.locale.icu=off \
 | ||||
| 		cxxflags=-fPIC cflags=-fPIC \
 | ||||
| 		install | ||||
| 
 | ||||
| $(BOOST).tar.gz: | ||||
| 	curl -L -o $@ https://dl.bintray.com/boostorg/release/1.66.0/source/$@ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| libtbb: $(TBB).tar.gz | ||||
| 	tar -zxvf $(TBB).tar.gz | ||||
| 	mkdir -p $(TBB)/mybuild | ||||
| 	cd $(TBB)/mybuild && cmake .. -DTBB_BUILD_SHARED=OFF -DTBB_BUILD_TESTS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON | ||||
| 	$(MAKE) -C $(TBB)/mybuild -j$(NPROC) | ||||
| 	$(MAKE) -C $(TBB)/mybuild install DESTDIR=$(DESTDIR) | ||||
| 
 | ||||
| $(TBB).tar.gz: | ||||
| 	curl -L -o $@ https://github.com/wjakob/tbb/archive/$(TBB_SHA).tar.gz | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| libopenssl: $(OPENSSL).tar.gz | ||||
| 	tar -zxvf $(OPENSSL).tar.gz | ||||
| 	cd $(OPENSSL) && ./config --openssldir=/etc/ssl shared no-ssl3-method no-dynamic-engine '-Wa,--noexecstack' | ||||
| 	make -C $(OPENSSL) depend | ||||
| 	make -C $(OPENSSL) -j$(NPROC) | ||||
| 	make -C $(OPENSSL) install DESTDIR=$(DESTDIR) | ||||
| 
 | ||||
| $(OPENSSL).tar.gz: | ||||
| 	curl -L -o $@ 'https://github.com/openssl/openssl/archive/OpenSSL_1_1_0g.tar.gz' | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| libcurl: libopenssl $(CURL).tar.gz | ||||
| 	tar -zxvf $(CURL).tar.gz | ||||
| # XXX: disable shared?
 | ||||
| # Setting PKG_CONFIG_PATH should make libcurl find our previously built openssl
 | ||||
| 	cd $(CURL) && PKG_CONFIG_PATH=$(DESTDIR)/usr/local/lib/pkgconfig ./configure \
 | ||||
| 		--enable-static \
 | ||||
| 		--enable-shared \
 | ||||
| 		--with-pic \
 | ||||
| 		--enable-ipv6 \
 | ||||
| 		--enable-versioned-symbols \
 | ||||
| 		--enable-threaded-resolver \
 | ||||
| 		--with-random=/dev/urandom \
 | ||||
| 		--with-ca-bundle=/etc/ssl/certs/ca-certificates.crt \
 | ||||
| 		--disable-ldap \
 | ||||
| 		--disable-ldaps \
 | ||||
| 		--disable-manual \
 | ||||
| 		--disable-rtsp \
 | ||||
| 		--disable-dict \
 | ||||
| 		--disable-telnet \
 | ||||
| 		--disable-pop3 \
 | ||||
| 		--disable-imap \
 | ||||
| 		--disable-smb \
 | ||||
| 		--disable-smtp \
 | ||||
| 		--disable-gopher \
 | ||||
| 		--disable-crypto-auth \
 | ||||
| 		--without-gssapi \
 | ||||
| 		--without-libpsl \
 | ||||
| 		--without-libidn2 \
 | ||||
| 		--without-gnutls \
 | ||||
| 		--without-polarssl \
 | ||||
| 		--without-mbedtls \
 | ||||
| 		--without-cyassl \
 | ||||
| 		--without-nss \
 | ||||
| 		--without-axtls \
 | ||||
| 		--without-brotli \
 | ||||
| 		--without-libmetalink \
 | ||||
| 		--without-libssh \
 | ||||
| 		--without-libssh2 \
 | ||||
| 		--without-librtmp \
 | ||||
| 		--without-nghttp2 \
 | ||||
| 		--without-zsh-functions-dir | ||||
| 	$(MAKE) -C $(CURL) -j$(NPROC) | ||||
| 	$(MAKE) -C $(CURL) install DESTDIR=$(DESTDIR) | ||||
| 
 | ||||
| $(CURL).tar.gz: | ||||
| 	curl -L -o $@ https://curl.haxx.se/download/$@ | ||||
| 
 | ||||
| 
 | ||||
| clean: | ||||
| 	rm -rf $(BOOST) $(BOOST).tar.gz $(TBB) $(TBB).tar.gz $(OPENSSL) $(OPENSSL).tar.gz $(CURL) $(CURL).tar.gz | ||||
							
								
								
									
										141
									
								
								doc/deps-build/windows/slic3r-makedeps.ps1
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								doc/deps-build/windows/slic3r-makedeps.ps1
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,141 @@ | |||
| #!powershell | ||||
| # | ||||
| # This script downloads, configures and builds Slic3r PE dependencies for Unix. | ||||
| # (That is, all dependencies except perl + wxWidgets.) | ||||
| # | ||||
| # To use this script, launch the Visual Studio command line, | ||||
| # `cd` into the directory containing this script and use this command: | ||||
| # | ||||
| #     powershell .\slic3r-makedeps.ps1 | ||||
| # | ||||
| # The dependencies will be downloaded and unpacked into the current dir. | ||||
| # This script WILL NOT try to guess the build architecture (64 vs 32 bits), | ||||
| # it will by default build the 64-bit variant. To build the 32-bit variant, use: | ||||
| # | ||||
| #     powershell .\slic3r-makedeps.ps1 -b32 | ||||
| # | ||||
| # Built libraries are installed into $destdir, | ||||
| # which by default is C:\local\slic3r-destdir-$bits | ||||
| # You can customize the $destdir using: | ||||
| # | ||||
| #     powershell .\slic3r-makedeps.ps1 -destdir C:\foo\bar | ||||
| # | ||||
| # To pass the $destdir path along to cmake, set the use CMAKE_PREFIX_PATH variable | ||||
| # and set it to $destdir\usr\local | ||||
| # | ||||
| # Script requirements: PowerShell 3.0, .NET 4.5 | ||||
| # | ||||
| 
 | ||||
| 
 | ||||
| param( | ||||
|     [switch]$b32 = $false, | ||||
|     [string]$destdir = "" | ||||
| ) | ||||
| 
 | ||||
| if ($destdir -eq "") { | ||||
|     $destdir = "C:\local\slic3r-destdir-" + ('32', '64')[!$b32] | ||||
| } | ||||
| 
 | ||||
| $BOOST = 'boost_1_63_0' | ||||
| $CURL = 'curl-7.28.0' | ||||
| $TBB_SHA = 'a0dc9bf76d0120f917b641ed095360448cabc85b' | ||||
| $TBB = "tbb-$TBB_SHA" | ||||
| 
 | ||||
| 
 | ||||
| try | ||||
| { | ||||
| 
 | ||||
| 
 | ||||
| # Set up various settings and utilities: | ||||
| [Environment]::CurrentDirectory = Get-Location | ||||
| $NPROC = (Get-WmiObject -class Win32_processor).NumberOfLogicalProcessors | ||||
| Add-Type -A System.IO.Compression.FileSystem | ||||
| #   This fxies SSL/TLS errors, credit goes to Ansible; see their `win_get_url.ps1` file | ||||
| $security_protcols = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::SystemDefault | ||||
| if ([Net.SecurityProtocolType].GetMember('Tls11').Count -gt 0) { | ||||
|     $security_protcols = $security_protcols -bor [Net.SecurityProtocolType]::Tls11 | ||||
| } | ||||
| if ([Net.SecurityProtocolType].GetMember('Tls12').Count -gt 0) { | ||||
|     $security_protcols = $security_protcols -bor [Net.SecurityProtocolType]::Tls12 | ||||
| } | ||||
| [Net.ServicePointManager]::SecurityProtocol = $security_protcols | ||||
| $webclient = New-Object System.Net.WebClient | ||||
| 
 | ||||
| 
 | ||||
| # Ensure DESTDIR exists: | ||||
| mkdir $destdir -ea 0 | ||||
| mkdir "$destdir\usr\local" -ea 0 | ||||
| 
 | ||||
| 
 | ||||
| # Download sources: | ||||
| echo 'Downloading sources ...' | ||||
| if (!(Test-Path "$BOOST.zip")) { $webclient.DownloadFile("https://dl.bintray.com/boostorg/release/1.63.0/source/$BOOST.zip", "$BOOST.zip") } | ||||
| if (!(Test-Path "$TBB.zip")) { $webclient.DownloadFile("https://github.com/wjakob/tbb/archive/$TBB_SHA.zip", "$TBB.zip") } | ||||
| if (!(Test-Path "$CURL.zip")) { $webclient.DownloadFile("https://curl.haxx.se/download/$CURL.zip", ".\$CURL.zip") } | ||||
| 
 | ||||
| 
 | ||||
| # Unpack sources: | ||||
| echo 'Unpacking ...' | ||||
| if (!(Test-Path $BOOST)) { [IO.Compression.ZipFile]::ExtractToDirectory("$BOOST.zip", '.') } | ||||
| if (!(Test-Path $TBB)) { [IO.Compression.ZipFile]::ExtractToDirectory("$TBB.zip", '.') } | ||||
| if (!(Test-Path $CURL)) { [IO.Compression.ZipFile]::ExtractToDirectory("$CURL.zip", '.') } | ||||
| 
 | ||||
| 
 | ||||
| # Build libraries: | ||||
| echo 'Building ...' | ||||
| 
 | ||||
| # Build boost | ||||
| pushd "$BOOST" | ||||
| .\bootstrap | ||||
| $adr_mode = ('32', '64')[!$b32] | ||||
| .\b2 ` | ||||
|     -j "$NPROC" ` | ||||
|     --with-system ` | ||||
|     --with-filesystem ` | ||||
|     --with-thread ` | ||||
|     --with-log ` | ||||
|     --with-locale ` | ||||
|     --with-regex ` | ||||
|     "--prefix=$destdir/usr/local" ` | ||||
|     "address-model=$adr_mode" ` | ||||
|     toolset=msvc-12.0 ` | ||||
|     link=static ` | ||||
|     variant=release ` | ||||
|     threading=multi ` | ||||
|     boost.locale.icu=off ` | ||||
|     install | ||||
| popd | ||||
| 
 | ||||
| # Build TBB | ||||
| pushd "$TBB" | ||||
| mkdir 'mybuild' -ea 0 | ||||
| cd 'mybuild' | ||||
| $generator = ('Visual Studio 12', 'Visual Studio 12 Win64')[!$b32] | ||||
| cmake .. ` | ||||
|     -G "$generator" ` | ||||
|     -DCMAKE_CONFIGURATION_TYPES=Release ` | ||||
|     -DTBB_BUILD_SHARED=OFF ` | ||||
|     -DTBB_BUILD_TESTS=OFF "-DCMAKE_INSTALL_PREFIX:PATH=$destdir\usr\local" | ||||
| msbuild /P:Configuration=Release INSTALL.vcxproj | ||||
| popd | ||||
| 
 | ||||
| # Build libcurl: | ||||
| pushd "$CURL\winbuild" | ||||
| $machine = ("x86", "x64")[!$b32] | ||||
| nmake /f Makefile.vc mode=static VC=12 GEN_PDB=yes DEBUG=no "MACHINE=$machine" | ||||
| Copy-Item -R -Force ..\builds\libcurl-*-winssl\include\* "$destdir\usr\local\include\" | ||||
| Copy-Item -R -Force ..\builds\libcurl-*-winssl\lib\* "$destdir\usr\local\lib\" | ||||
| popd | ||||
| 
 | ||||
| 
 | ||||
| echo "" | ||||
| echo "All done!" | ||||
| echo "" | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| catch [Exception] | ||||
| { | ||||
|     # This prints errors in a verbose manner | ||||
|     echo $_.Exception|format-list -force | ||||
| } | ||||
|  | @ -174,7 +174,6 @@ sub _init_tabpanel { | |||
|     EVT_COMMAND($self, -1, $BUTTON_BROWSE_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         my $msg = $event->GetString; | ||||
|         print "BUTTON_BROWSE_EVENT: ", $msg, "\n"; | ||||
| 
 | ||||
|         # look for devices | ||||
|         my $entries; | ||||
|  | @ -197,7 +196,6 @@ sub _init_tabpanel { | |||
|     EVT_COMMAND($self, -1, $BUTTON_TEST_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         my $msg = $event->GetString; | ||||
|         print "BUTTON_TEST_EVENT: ", $msg, "\n"; | ||||
| 
 | ||||
|         my $ua = LWP::UserAgent->new; | ||||
|         $ua->timeout(10); | ||||
|  | @ -409,7 +407,7 @@ sub _init_menubar { | |||
|             wxTheApp->about; | ||||
|         }); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     # menubar | ||||
|     # assign menubar to frame after appending items, otherwise special items | ||||
|     # will not be handled correctly | ||||
|  | @ -424,6 +422,7 @@ sub _init_menubar { | |||
|         # (Select application language from the list of installed languages) | ||||
|         Slic3r::GUI::add_debug_menu($menubar, $self->{lang_ch_event}); | ||||
|         $menubar->Append($helpMenu, L("&Help")); | ||||
|         # Add an optional debug menu. In production code, the add_debug_menu() call should do nothing. | ||||
|         $self->SetMenuBar($menubar); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -52,7 +52,7 @@ sub new { | |||
|     my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); | ||||
|     $self->{config} = Slic3r::Config::new_from_defaults_keys([qw( | ||||
|         bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height | ||||
|         serial_port serial_speed octoprint_host octoprint_apikey | ||||
|         serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile | ||||
|         nozzle_diameter single_extruder_multi_material  | ||||
|         wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour | ||||
|     )]); | ||||
|  | @ -1458,8 +1458,13 @@ sub on_export_completed { | |||
|     wxTheApp->notify($message); | ||||
|      | ||||
|     $self->do_print if $do_print; | ||||
| 
 | ||||
|     # Send $self->{send_gcode_file} to OctoPrint. | ||||
|     $self->send_gcode if $send_gcode; | ||||
|     if ($send_gcode) { | ||||
|         my $op = Slic3r::OctoPrint->new($self->{config}); | ||||
|         $op->send_gcode($self->GetId(), $PROGRESS_BAR_EVENT, $ERROR_EVENT, $self->{send_gcode_file}); | ||||
|     } | ||||
| 
 | ||||
|     $self->{print_file} = undef; | ||||
|     $self->{send_gcode_file} = undef; | ||||
|     $self->{"print_info_cost"}->SetLabel(sprintf("%.2f" , $self->{print}->total_cost)); | ||||
|  | @ -1488,45 +1493,6 @@ sub do_print { | |||
|     my $filament_names = wxTheApp->{preset_bundle}->filament_presets; | ||||
|     $filament_stats = { map { $filament_names->[$_] => $filament_stats->{$_} } keys %$filament_stats }; | ||||
|     $printer_panel->load_print_job($self->{print_file}, $filament_stats); | ||||
|      | ||||
|     $self->GetFrame->select_tab(1); | ||||
| } | ||||
| 
 | ||||
| # Send $self->{send_gcode_file} to OctoPrint. | ||||
| #FIXME Currently this call blocks the UI. Make it asynchronous. | ||||
| sub send_gcode { | ||||
|     my ($self) = @_; | ||||
|      | ||||
|     $self->statusbar->StartBusy; | ||||
|      | ||||
|     my $ua = LWP::UserAgent->new; | ||||
|     $ua->timeout(180); | ||||
|      | ||||
|     my $res = $ua->post( | ||||
|         "http://" . $self->{config}->octoprint_host . "/api/files/local", | ||||
|         Content_Type => 'form-data', | ||||
|         'X-Api-Key' => $self->{config}->octoprint_apikey, | ||||
|         Content => [ | ||||
|             file => [  | ||||
|                 # On Windows, the path has to be encoded in local code page for perl to be able to open it. | ||||
|                 Slic3r::encode_path($self->{send_gcode_file}), | ||||
|                 # Remove the UTF-8 flag from the perl string, so the LWP::UserAgent can insert  | ||||
|                 # the UTF-8 encoded string into the request as a byte stream. | ||||
|                 Slic3r::path_to_filename_raw($self->{send_gcode_file}) | ||||
|             ], | ||||
|             print => $self->{send_gcode_file_print} ? 1 : 0, | ||||
|         ], | ||||
|     ); | ||||
|      | ||||
|     $self->statusbar->StopBusy; | ||||
|      | ||||
|     if ($res->is_success) { | ||||
|         $self->statusbar->SetStatusText(L("G-code file successfully uploaded to the OctoPrint server")); | ||||
|     } else { | ||||
|         my $message = L("Error while uploading to the OctoPrint server: ") . $res->status_line; | ||||
|         Slic3r::GUI::show_error($self, $message); | ||||
|         $self->statusbar->SetStatusText($message); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub export_stl { | ||||
|  |  | |||
|  | @ -15,4 +15,4 @@ c:\src\Slic3r\lib\Slic3r\GUI.pm | |||
| c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm | ||||
| c:\src\Slic3r\lib\Slic3r\GUI\Plater.pm | ||||
| c:\src\Slic3r\lib\Slic3r\GUI\Plater\2D.pm | ||||
| c:\src\Slic3r\lib\Slic3r\GUI\Plater\3DPreview.pm | ||||
| c:\src\Slic3r\lib\Slic3r\GUI\Plater\3DPreview.pm | ||||
|  |  | |||
|  | @ -199,6 +199,12 @@ add_library(libslic3r_gui STATIC | |||
|     ${LIBDIR}/slic3r/GUI/2DBed.hpp | ||||
|     ${LIBDIR}/slic3r/GUI/wxExtensions.cpp | ||||
|     ${LIBDIR}/slic3r/GUI/wxExtensions.hpp | ||||
|     ${LIBDIR}/slic3r/Utils/Http.cpp | ||||
|     ${LIBDIR}/slic3r/Utils/Http.hpp | ||||
|     ${LIBDIR}/slic3r/Utils/OctoPrint.cpp | ||||
|     ${LIBDIR}/slic3r/Utils/OctoPrint.hpp | ||||
|     ${LIBDIR}/slic3r/Utils/Bonjour.cpp | ||||
|     ${LIBDIR}/slic3r/Utils/Bonjour.hpp | ||||
| ) | ||||
| 
 | ||||
| add_library(admesh STATIC | ||||
|  | @ -338,6 +344,7 @@ set(XS_XSP_FILES | |||
|     ${XSP_DIR}/Surface.xsp | ||||
|     ${XSP_DIR}/SurfaceCollection.xsp | ||||
|     ${XSP_DIR}/TriangleMesh.xsp | ||||
|     ${XSP_DIR}/Utils_OctoPrint.xsp | ||||
|     ${XSP_DIR}/XS.xsp | ||||
| ) | ||||
| foreach (file ${XS_XSP_FILES}) | ||||
|  | @ -475,6 +482,7 @@ if(SLIC3R_STATIC) | |||
|     # Use boost libraries linked statically to the C++ runtime. | ||||
|     # set(Boost_USE_STATIC_RUNTIME ON) | ||||
| endif() | ||||
| #set(Boost_DEBUG ON) | ||||
| find_package(Boost REQUIRED COMPONENTS system filesystem thread log locale regex) | ||||
| if(Boost_FOUND) | ||||
|     include_directories(${Boost_INCLUDE_DIRS}) | ||||
|  | @ -522,6 +530,27 @@ if (SLIC3R_PRUSACONTROL) | |||
|     target_link_libraries(XS ${wxWidgets_LIBRARIES}) | ||||
| endif() | ||||
| 
 | ||||
| find_package(CURL REQUIRED) | ||||
| include_directories(${CURL_INCLUDE_DIRS}) | ||||
| target_link_libraries(XS ${CURL_LIBRARIES}) | ||||
| 
 | ||||
| if (SLIC3R_STATIC) | ||||
|     if (NOT APPLE) | ||||
|         # libcurl is always linked dynamically to the system libcurl on OSX. | ||||
|         # On other systems, libcurl is linked statically if SLIC3R_STATIC is set. | ||||
|         add_definitions(-DCURL_STATICLIB) | ||||
|     endif() | ||||
|     if (CMAKE_SYSTEM_NAME STREQUAL "Linux") | ||||
|         # As of now, our build system produces a statically linked libcurl, | ||||
|         # which links the OpenSSL library dynamically. | ||||
|         find_package(OpenSSL REQUIRED) | ||||
|         message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}") | ||||
|         message("OpenSSL libraries: ${OPENSSL_LIBRARIES}") | ||||
|         include_directories(${OPENSSL_INCLUDE_DIR}) | ||||
|         target_link_libraries(XS ${OPENSSL_LIBRARIES}) | ||||
|     endif() | ||||
| endif() | ||||
| 
 | ||||
| ## OPTIONAL packages | ||||
| 
 | ||||
| # Find eigen3 or use bundled version | ||||
|  | @ -596,6 +625,17 @@ elseif (NOT MSVC) | |||
|     target_link_libraries(slic3r -lstdc++) | ||||
| endif () | ||||
| 
 | ||||
| if (MSVC) | ||||
|     # Here we associate some additional properties with the MSVC projects to enable compilation and debugging out of the box. | ||||
|     # It seems a props file needs to be copied to the same dir as the proj file, otherwise MSVC doesn't load it up. | ||||
|     # For copying, the configure_file() function seems to work much better than the file() function. | ||||
|     configure_file("${PROJECT_SOURCE_DIR}/cmake/msvc/xs.wperl64d.props" ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) | ||||
|     set_target_properties(XS PROPERTIES VS_USER_PROPS "xs.wperl64d.props") | ||||
|     configure_file("${PROJECT_SOURCE_DIR}/cmake/msvc/slic3r.wperl64d.props" ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) | ||||
|     set_target_properties(slic3r PROPERTIES VS_USER_PROPS "slic3r.wperl64d.props") | ||||
| endif () | ||||
| 
 | ||||
| 
 | ||||
| # Installation | ||||
| install(TARGETS XS DESTINATION lib/slic3r-prusa3d/auto/Slic3r/XS) | ||||
| install(FILES lib/Slic3r/XS.pm DESTINATION lib/slic3r-prusa3d/Slic3r) | ||||
| install(TARGETS XS DESTINATION ${PERL_VENDORARCH}/auto/Slic3r/XS) | ||||
| install(FILES lib/Slic3r/XS.pm DESTINATION ${PERL_VENDORLIB}/Slic3r) | ||||
|  |  | |||
|  | @ -1402,18 +1402,13 @@ void GCode::apply_print_config(const PrintConfig &print_config) | |||
| 
 | ||||
| void GCode::append_full_config(const Print& print, std::string& str) | ||||
| { | ||||
|     char buff[4096]; | ||||
| 
 | ||||
|     const StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config }; | ||||
|     for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) { | ||||
|         const StaticPrintConfig *cfg = configs[i]; | ||||
|         for (const std::string &key : cfg->keys()) | ||||
|         { | ||||
|             if (key != "compatible_printers") | ||||
|             { | ||||
|                 sprintf(buff, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str()); | ||||
|                 str += buff; | ||||
|             } | ||||
|                 str += "; " + key + " = " + cfg->serialize(key) + "\n"; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -904,10 +904,17 @@ PrintConfigDef::PrintConfigDef() | |||
|     def->cli = "octoprint-apikey=s"; | ||||
|     def->default_value = new ConfigOptionString(""); | ||||
|      | ||||
|     def = this->add("octoprint_cafile", coString); | ||||
|     def->label = "HTTPS CA file"; | ||||
|     def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. " | ||||
|                    "If left blank, the default OS CA certificate repository is used."; | ||||
|     def->cli = "octoprint-cafile=s"; | ||||
|     def->default_value = new ConfigOptionString(""); | ||||
| 
 | ||||
|     def = this->add("octoprint_host", coString); | ||||
|     def->label = L("Host or IP"); | ||||
|     def->label = L("Hostname, IP or URL"); | ||||
|     def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain " | ||||
|                    "the hostname or IP address of the OctoPrint instance."); | ||||
|                    "the hostname, IP address or URL of the OctoPrint instance."); | ||||
|     def->cli = "octoprint-host=s"; | ||||
|     def->default_value = new ConfigOptionString(""); | ||||
| 
 | ||||
|  |  | |||
|  | @ -684,6 +684,7 @@ class HostConfig : public StaticPrintConfig | |||
| public: | ||||
|     ConfigOptionString              octoprint_host; | ||||
|     ConfigOptionString              octoprint_apikey; | ||||
|     ConfigOptionString              octoprint_cafile; | ||||
|     ConfigOptionString              serial_port; | ||||
|     ConfigOptionInt                 serial_speed; | ||||
|      | ||||
|  | @ -692,6 +693,7 @@ protected: | |||
|     { | ||||
|         OPT_PTR(octoprint_host); | ||||
|         OPT_PTR(octoprint_apikey); | ||||
|         OPT_PTR(octoprint_cafile); | ||||
|         OPT_PTR(serial_port); | ||||
|         OPT_PTR(serial_speed); | ||||
|     } | ||||
|  |  | |||
|  | @ -1186,40 +1186,46 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic | |||
|         loops correctly in some edge cases when original model had overlapping facets | ||||
|     */ | ||||
| 
 | ||||
|     std::vector<double> area; | ||||
|     std::vector<size_t> sorted_area;  // vector of indices
 | ||||
|     for (Polygons::const_iterator loop = loops.begin(); loop != loops.end(); ++ loop) { | ||||
|         area.push_back(loop->area()); | ||||
|         sorted_area.push_back(loop - loops.begin()); | ||||
|     } | ||||
|      | ||||
|     // outer first
 | ||||
|     std::sort(sorted_area.begin(), sorted_area.end(), | ||||
|         [&area](size_t a, size_t b) { return std::abs(area[a]) > std::abs(area[b]); }); | ||||
|     /* The following lines are commented out because they can generate wrong polygons,
 | ||||
|        see for example issue #661 */ | ||||
| 
 | ||||
|     // we don't perform a safety offset now because it might reverse cw loops
 | ||||
|     Polygons p_slices; | ||||
|     for (std::vector<size_t>::const_iterator loop_idx = sorted_area.begin(); loop_idx != sorted_area.end(); ++ loop_idx) { | ||||
|         /* we rely on the already computed area to determine the winding order
 | ||||
|            of the loops, since the Orientation() function provided by Clipper | ||||
|            would do the same, thus repeating the calculation */ | ||||
|         Polygons::const_iterator loop = loops.begin() + *loop_idx; | ||||
|         if (area[*loop_idx] > +EPSILON) | ||||
|             p_slices.push_back(*loop); | ||||
|         else if (area[*loop_idx] < -EPSILON) | ||||
|             //FIXME This is arbitrary and possibly very slow.
 | ||||
|             // If the hole is inside a polygon, then there is no need to diff.
 | ||||
|             // If the hole intersects a polygon boundary, then diff it, but then
 | ||||
|             // there is no guarantee of an ordering of the loops.
 | ||||
|             // Maybe we can test for the intersection before running the expensive diff algorithm?
 | ||||
|             p_slices = diff(p_slices, *loop); | ||||
|     } | ||||
|     //std::vector<double> area;
 | ||||
|     //std::vector<size_t> sorted_area;  // vector of indices
 | ||||
|     //for (Polygons::const_iterator loop = loops.begin(); loop != loops.end(); ++ loop) {
 | ||||
|     //    area.push_back(loop->area());
 | ||||
|     //    sorted_area.push_back(loop - loops.begin());
 | ||||
|     //}
 | ||||
|     //
 | ||||
|     //// outer first
 | ||||
|     //std::sort(sorted_area.begin(), sorted_area.end(),
 | ||||
|     //    [&area](size_t a, size_t b) { return std::abs(area[a]) > std::abs(area[b]); });
 | ||||
| 
 | ||||
|     //// we don't perform a safety offset now because it might reverse cw loops
 | ||||
|     //Polygons p_slices;
 | ||||
|     //for (std::vector<size_t>::const_iterator loop_idx = sorted_area.begin(); loop_idx != sorted_area.end(); ++ loop_idx) {
 | ||||
|     //    /* we rely on the already computed area to determine the winding order
 | ||||
|     //       of the loops, since the Orientation() function provided by Clipper
 | ||||
|     //       would do the same, thus repeating the calculation */
 | ||||
|     //    Polygons::const_iterator loop = loops.begin() + *loop_idx;
 | ||||
|     //    if (area[*loop_idx] > +EPSILON)
 | ||||
|     //        p_slices.push_back(*loop);
 | ||||
|     //    else if (area[*loop_idx] < -EPSILON)
 | ||||
|     //        //FIXME This is arbitrary and possibly very slow.
 | ||||
|     //        // If the hole is inside a polygon, then there is no need to diff.
 | ||||
|     //        // If the hole intersects a polygon boundary, then diff it, but then
 | ||||
|     //        // there is no guarantee of an ordering of the loops.
 | ||||
|     //        // Maybe we can test for the intersection before running the expensive diff algorithm?
 | ||||
|     //        p_slices = diff(p_slices, *loop);
 | ||||
|     //}
 | ||||
| 
 | ||||
|     // perform a safety offset to merge very close facets (TODO: find test case for this)
 | ||||
|     double safety_offset = scale_(0.0499); | ||||
| //FIXME see https://github.com/prusa3d/Slic3r/issues/520
 | ||||
| //    double safety_offset = scale_(0.0001);
 | ||||
|     ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset); | ||||
| 
 | ||||
|     /* The following line is commented out because it can generate wrong polygons,
 | ||||
|        see for example issue #661 */ | ||||
|     //ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset);
 | ||||
|      | ||||
|     #ifdef SLIC3R_TRIANGLEMESH_DEBUG | ||||
|     size_t holes_count = 0; | ||||
|  | @ -1230,7 +1236,10 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic | |||
|     #endif | ||||
|      | ||||
|     // append to the supplied collection
 | ||||
|     expolygons_append(*slices, ex_slices); | ||||
|     /* Fix for issue #661 { */ | ||||
|     expolygons_append(*slices, offset2_ex(union_(loops, false), +safety_offset, -safety_offset)); | ||||
|     //expolygons_append(*slices, ex_slices);
 | ||||
|     /* } */ | ||||
| } | ||||
| 
 | ||||
| void TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const | ||||
|  |  | |||
|  | @ -64,6 +64,7 @@ REGISTER_CLASS(PresetCollection, "GUI::PresetCollection"); | |||
| REGISTER_CLASS(PresetBundle, "GUI::PresetBundle"); | ||||
| REGISTER_CLASS(PresetHints, "GUI::PresetHints"); | ||||
| REGISTER_CLASS(TabIface, "GUI::Tab"); | ||||
| REGISTER_CLASS(OctoPrint, "OctoPrint"); | ||||
| 
 | ||||
| SV* ConfigBase__as_hash(ConfigBase* THIS) | ||||
| { | ||||
|  |  | |||
|  | @ -5,9 +5,9 @@ | |||
| #include <boost/algorithm/string/predicate.hpp> | ||||
| #include <boost/filesystem.hpp> | ||||
| #include <boost/lexical_cast.hpp> | ||||
| 
 | ||||
| #include <boost/algorithm/string/split.hpp> | ||||
| #include <boost/algorithm/string/classification.hpp> | ||||
| #include <boost/format.hpp> | ||||
| 
 | ||||
| #if __APPLE__ | ||||
| #import <IOKit/pwr_mgt/IOPMLib.h> | ||||
|  | @ -428,7 +428,7 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b | |||
| 			} | ||||
| 			else{ | ||||
| 				ConfigOptionStrings* vec_new = new ConfigOptionStrings{ boost::any_cast<std::string>(value) }; | ||||
| 				config.option<ConfigOptionStrings>(opt_key)->set_at(vec_new, opt_index, opt_index); | ||||
| 				config.option<ConfigOptionStrings>(opt_key)->set_at(vec_new, opt_index, 0); | ||||
| 			} | ||||
| 			} | ||||
| 			break; | ||||
|  | @ -437,14 +437,14 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b | |||
| 			break; | ||||
| 		case coBools:{ | ||||
| 			ConfigOptionBools* vec_new = new ConfigOptionBools{ boost::any_cast<bool>(value) }; | ||||
| 			config.option<ConfigOptionBools>(opt_key)->set_at(vec_new, opt_index, opt_index); | ||||
| 			config.option<ConfigOptionBools>(opt_key)->set_at(vec_new, opt_index, 0); | ||||
| 			break;} | ||||
| 		case coInt: | ||||
| 			config.set_key_value(opt_key, new ConfigOptionInt(boost::any_cast<int>(value))); | ||||
| 			break; | ||||
| 		case coInts:{ | ||||
| 			ConfigOptionInts* vec_new = new ConfigOptionInts{ boost::any_cast<int>(value) }; | ||||
| 			config.option<ConfigOptionInts>(opt_key)->set_at(vec_new, opt_index, opt_index); | ||||
| 			config.option<ConfigOptionInts>(opt_key)->set_at(vec_new, opt_index, 0); | ||||
| 			} | ||||
| 			break; | ||||
| 		case coEnum:{ | ||||
|  | @ -580,4 +580,18 @@ wxString from_u8(const std::string &str) | |||
| 	return wxString::FromUTF8(str.c_str()); | ||||
| } | ||||
| 
 | ||||
| wxWindow *get_widget_by_id(int id) | ||||
| { | ||||
|     if (g_wxMainFrame == nullptr) { | ||||
|         throw std::runtime_error("Main frame not set"); | ||||
|     } | ||||
| 
 | ||||
|     wxWindow *window = g_wxMainFrame->FindWindow(id); | ||||
|     if (window == nullptr) { | ||||
|         throw std::runtime_error((boost::format("Could not find widget by ID: %1%") % id).str()); | ||||
|     } | ||||
| 
 | ||||
|     return window; | ||||
| } | ||||
| 
 | ||||
| } } | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| #include "Config.hpp" | ||||
| 
 | ||||
| class wxApp; | ||||
| class wxWindow; | ||||
| class wxFrame; | ||||
| class wxWindow; | ||||
| class wxMenuBar; | ||||
|  | @ -123,6 +124,8 @@ wxString	L_str(const std::string &str); | |||
| // Return wxString from std::string in UTF8
 | ||||
| wxString	from_u8(const std::string &str); | ||||
| 
 | ||||
| wxWindow *get_widget_by_id(int id); | ||||
| 
 | ||||
| } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -224,7 +224,7 @@ const std::vector<std::string>& Preset::printer_options() | |||
|     if (s_opts.empty()) { | ||||
|         s_opts = { | ||||
|             "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",  | ||||
|             "octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",  | ||||
|             "octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", | ||||
|             "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", | ||||
|             "between_objects_gcode", "printer_notes" | ||||
|         }; | ||||
|  |  | |||
|  | @ -179,12 +179,25 @@ void Tab::update_tab_ui() | |||
| 	m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets); | ||||
| } | ||||
| 
 | ||||
| template<class T> | ||||
| boost::any get_new_value(const DynamicPrintConfig &config_new, const DynamicPrintConfig &config_old, std::string opt_key, int &index) | ||||
| { | ||||
| 	for (int i = 0; i < config_new.option<T>(opt_key)->values.size(); i++) | ||||
| 		if (config_new.option<T>(opt_key)->values[i] != | ||||
| 			config_old.option<T>(opt_key)->values[i]){ | ||||
| 			index = i; | ||||
| 			break; | ||||
| 		} | ||||
| 	return config_new.option<T>(opt_key)->values[index]; | ||||
| } | ||||
| 
 | ||||
| // Load a provied DynamicConfig into the tab, modifying the active preset.
 | ||||
| // This could be used for example by setting a Wipe Tower position by interactive manipulation in the 3D view.
 | ||||
| void Tab::load_config(DynamicPrintConfig config) | ||||
| { | ||||
| 	bool modified = 0; | ||||
| 	boost::any value; | ||||
| 	int opt_index = 0; | ||||
| 	for(auto opt_key : m_config->diff(config)) { | ||||
| 		switch ( config.def()->get(opt_key)->type ){ | ||||
| 		case coFloatOrPercent: | ||||
|  | @ -200,28 +213,26 @@ void Tab::load_config(DynamicPrintConfig config) | |||
| 			value = config.opt_string(opt_key); | ||||
| 			break; | ||||
| 		case coPercents: | ||||
| 			value = config.option<ConfigOptionPercents>(opt_key)->values.at(0); | ||||
| 			value = get_new_value<ConfigOptionPercents>(config, *m_config, opt_key, opt_index); | ||||
| 			break; | ||||
| 		case coFloats: | ||||
| 			value = config.opt_float(opt_key, 0); | ||||
| 			value = get_new_value<ConfigOptionFloats>(config, *m_config, opt_key, opt_index); | ||||
| 			break; | ||||
| 		case coStrings: | ||||
| 			if (config.option<ConfigOptionStrings>(opt_key)->values.empty()) | ||||
| 				value = ""; | ||||
| 			else | ||||
| 				value = config.opt_string(opt_key, static_cast<unsigned int>(0)); | ||||
| 			value = config.option<ConfigOptionStrings>(opt_key)->values.empty() ? "" : | ||||
| 				get_new_value<ConfigOptionStrings>(config, *m_config, opt_key, opt_index); | ||||
| 			break; | ||||
| 		case coBool: | ||||
| 			value = config.opt_bool(opt_key); | ||||
| 			break; | ||||
| 		case coBools: | ||||
| 			value = config.opt_bool(opt_key, 0); | ||||
| 			value = get_new_value<ConfigOptionBools>(config, *m_config, opt_key, opt_index); | ||||
| 			break; | ||||
| 		case coInt: | ||||
| 			value = config.opt_int(opt_key); | ||||
| 			break; | ||||
| 		case coInts: | ||||
| 			value = config.opt_int(opt_key, 0); | ||||
| 			value = get_new_value<ConfigOptionInts>(config, *m_config, opt_key, opt_index); | ||||
| 			break; | ||||
| 		case coEnum:{ | ||||
| 			if (opt_key.compare("external_fill_pattern") == 0 || | ||||
|  | @ -242,7 +253,7 @@ void Tab::load_config(DynamicPrintConfig config) | |||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		change_opt_value(*m_config, opt_key, value); | ||||
| 		change_opt_value(*m_config, opt_key, value, opt_index); | ||||
| 		modified = 1; | ||||
| //		get_field(opt_key)->m_Label->SetBackgroundColour(*get_modified_label_clr());
 | ||||
| 	} | ||||
|  |  | |||
							
								
								
									
										704
									
								
								xs/src/slic3r/Utils/Bonjour.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										704
									
								
								xs/src/slic3r/Utils/Bonjour.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,704 @@ | |||
| #include "Bonjour.hpp" | ||||
| 
 | ||||
| #include <iostream>  // XXX
 | ||||
| #include <cstdint> | ||||
| #include <algorithm> | ||||
| #include <unordered_map> | ||||
| #include <array> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <random> | ||||
| #include <thread> | ||||
| #include <boost/optional.hpp> | ||||
| #include <boost/system/error_code.hpp> | ||||
| #include <boost/endian/conversion.hpp> | ||||
| #include <boost/asio.hpp> | ||||
| #include <boost/date_time/posix_time/posix_time_duration.hpp> | ||||
| #include <boost/format.hpp> | ||||
| 
 | ||||
| using boost::optional; | ||||
| using boost::system::error_code; | ||||
| namespace endian = boost::endian; | ||||
| namespace asio = boost::asio; | ||||
| using boost::asio::ip::udp; | ||||
| 
 | ||||
| 
 | ||||
| // TODO: Fuzzing test (done without TXT)
 | ||||
| // FIXME: check char retype to unsigned
 | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| 
 | ||||
| // Minimal implementation of a MDNS/DNS-SD client
 | ||||
| // This implementation is extremely simple, only the bits that are useful
 | ||||
| // for very basic MDNS discovery are present.
 | ||||
| 
 | ||||
| struct DnsName: public std::string | ||||
| { | ||||
| 	enum | ||||
| 	{ | ||||
| 		MAX_RECURSION = 10,     // Keep this low
 | ||||
| 	}; | ||||
| 
 | ||||
| 	static optional<DnsName> decode(const std::vector<char> &buffer, size_t &offset, unsigned depth = 0) | ||||
| 	{ | ||||
| 		// Check offset sanity:
 | ||||
| 		if (offset + 1 >= buffer.size()) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		// Check for recursion depth to prevent parsing names that are nested too deeply
 | ||||
| 		// or end up cyclic:
 | ||||
| 		if (depth >= MAX_RECURSION) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		DnsName res; | ||||
| 		const size_t bsize = buffer.size(); | ||||
| 
 | ||||
| 		while (true) { | ||||
| 			const char* ptr = buffer.data() + offset; | ||||
| 			unsigned len = static_cast<unsigned char>(*ptr); | ||||
| 			if (len & 0xc0) { | ||||
| 				// This is a recursive label
 | ||||
| 				unsigned len_2 = static_cast<unsigned char>(ptr[1]); | ||||
| 				size_t pointer = (len & 0x3f) << 8 | len_2; | ||||
| 				const auto nested = decode(buffer, pointer, depth + 1); | ||||
| 				if (!nested) { | ||||
| 					return boost::none; | ||||
| 				} else { | ||||
| 					if (res.size() > 0) { | ||||
| 						res.push_back('.'); | ||||
| 					} | ||||
| 					res.append(*nested); | ||||
| 					offset += 2; | ||||
| 					return std::move(res); | ||||
| 				} | ||||
| 			} else if (len == 0) { | ||||
| 				// This is a name terminator
 | ||||
| 				offset++; | ||||
| 				break; | ||||
| 			} else { | ||||
| 				// This is a regular label
 | ||||
| 				len &= 0x3f; | ||||
| 				if (len + offset + 1 >= bsize) { | ||||
| 					return boost::none; | ||||
| 				} | ||||
| 
 | ||||
| 				res.reserve(len); | ||||
| 				if (res.size() > 0) { | ||||
| 					res.push_back('.'); | ||||
| 				} | ||||
| 
 | ||||
| 				ptr++; | ||||
| 				for (const auto end = ptr + len; ptr < end; ptr++) { | ||||
| 					char c = *ptr; | ||||
| 					if (c >= 0x20 && c <= 0x7f) { | ||||
| 						res.push_back(c); | ||||
| 					} else { | ||||
| 						return boost::none; | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				offset += len + 1; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (res.size() > 0) { | ||||
| 			return std::move(res); | ||||
| 		} else { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct DnsHeader | ||||
| { | ||||
| 	uint16_t id; | ||||
| 	uint16_t flags; | ||||
| 	uint16_t qdcount; | ||||
| 	uint16_t ancount; | ||||
| 	uint16_t nscount; | ||||
| 	uint16_t arcount; | ||||
| 
 | ||||
| 	enum | ||||
| 	{ | ||||
| 		SIZE = 12, | ||||
| 	}; | ||||
| 
 | ||||
| 	static DnsHeader decode(const std::vector<char> &buffer) { | ||||
| 		DnsHeader res; | ||||
| 		const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(buffer.data()); | ||||
| 		res.id = endian::big_to_native(data_16[0]); | ||||
| 		res.flags = endian::big_to_native(data_16[1]); | ||||
| 		res.qdcount = endian::big_to_native(data_16[2]); | ||||
| 		res.ancount = endian::big_to_native(data_16[3]); | ||||
| 		res.nscount = endian::big_to_native(data_16[4]); | ||||
| 		res.arcount = endian::big_to_native(data_16[5]); | ||||
| 		return res; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t rrcount() const { | ||||
| 		return ancount + nscount + arcount; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct DnsQuestion | ||||
| { | ||||
| 	enum | ||||
| 	{ | ||||
| 		MIN_SIZE = 5, | ||||
| 	}; | ||||
| 
 | ||||
| 	DnsName name; | ||||
| 	uint16_t type; | ||||
| 	uint16_t qclass; | ||||
| 
 | ||||
| 	DnsQuestion() : | ||||
| 		type(0), | ||||
| 		qclass(0) | ||||
| 	{} | ||||
| 
 | ||||
| 	static optional<DnsQuestion> decode(const std::vector<char> &buffer, size_t &offset) | ||||
| 	{ | ||||
| 		auto qname = DnsName::decode(buffer, offset); | ||||
| 		if (!qname) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		DnsQuestion res; | ||||
| 		res.name = std::move(*qname); | ||||
| 		const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(buffer.data() + offset); | ||||
| 		res.type = endian::big_to_native(data_16[0]); | ||||
| 		res.qclass = endian::big_to_native(data_16[1]); | ||||
| 
 | ||||
| 		offset += 4; | ||||
| 		return std::move(res); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct DnsResource | ||||
| { | ||||
| 	DnsName name; | ||||
| 	uint16_t type; | ||||
| 	uint16_t rclass; | ||||
| 	uint32_t ttl; | ||||
| 	std::vector<char> data; | ||||
| 
 | ||||
| 	DnsResource() : | ||||
| 		type(0), | ||||
| 		rclass(0), | ||||
| 		ttl(0) | ||||
| 	{} | ||||
| 
 | ||||
| 	static optional<DnsResource> decode(const std::vector<char> &buffer, size_t &offset, size_t &dataoffset) | ||||
| 	{ | ||||
| 		const size_t bsize = buffer.size(); | ||||
| 		if (offset + 1 >= bsize) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		auto rname = DnsName::decode(buffer, offset); | ||||
| 		if (!rname) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		if (offset + 10 >= bsize) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		DnsResource res; | ||||
| 		res.name = std::move(*rname); | ||||
| 		const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(buffer.data() + offset); | ||||
| 		res.type = endian::big_to_native(data_16[0]); | ||||
| 		res.rclass = endian::big_to_native(data_16[1]); | ||||
| 		res.ttl = endian::big_to_native(*reinterpret_cast<const uint32_t*>(data_16 + 2)); | ||||
| 		uint16_t rdlength = endian::big_to_native(data_16[4]); | ||||
| 
 | ||||
| 		offset += 10; | ||||
| 		if (offset + rdlength > bsize) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		dataoffset = offset; | ||||
| 		res.data = std::move(std::vector<char>(buffer.begin() + offset, buffer.begin() + offset + rdlength)); | ||||
| 		offset += rdlength; | ||||
| 
 | ||||
| 		return std::move(res); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct DnsRR_A | ||||
| { | ||||
| 	enum { TAG = 0x1 }; | ||||
| 
 | ||||
| 	asio::ip::address_v4 ip; | ||||
| 
 | ||||
| 	static void decode(optional<DnsRR_A> &result, const DnsResource &rr) | ||||
| 	{ | ||||
| 		if (rr.data.size() == 4) { | ||||
| 			DnsRR_A res; | ||||
| 			const uint32_t ip = endian::big_to_native(*reinterpret_cast<const uint32_t*>(rr.data.data())); | ||||
| 			res.ip = asio::ip::address_v4(ip); | ||||
| 			result = std::move(res); | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct DnsRR_AAAA | ||||
| { | ||||
| 	enum { TAG = 0x1c }; | ||||
| 
 | ||||
| 	asio::ip::address_v6 ip; | ||||
| 
 | ||||
| 	static void decode(optional<DnsRR_AAAA> &result, const DnsResource &rr) | ||||
| 	{ | ||||
| 		if (rr.data.size() == 16) { | ||||
| 			DnsRR_AAAA res; | ||||
| 			std::array<unsigned char, 16> ip; | ||||
| 			std::copy_n(rr.data.begin(), 16, ip.begin()); | ||||
| 			res.ip = asio::ip::address_v6(ip); | ||||
| 			result = std::move(res); | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct DnsRR_SRV | ||||
| { | ||||
| 	enum | ||||
| 	{ | ||||
| 		TAG = 0x21, | ||||
| 		MIN_SIZE = 8, | ||||
| 	}; | ||||
| 
 | ||||
| 	uint16_t priority; | ||||
| 	uint16_t weight; | ||||
| 	uint16_t port; | ||||
| 	DnsName hostname; | ||||
| 
 | ||||
| 	static optional<DnsRR_SRV> decode(const std::vector<char> &buffer, const DnsResource &rr, size_t dataoffset) | ||||
| 	{ | ||||
| 		if (rr.data.size() < MIN_SIZE) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		DnsRR_SRV res; | ||||
| 
 | ||||
| 		const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(rr.data.data()); | ||||
| 		res.priority = endian::big_to_native(data_16[0]); | ||||
| 		res.weight = endian::big_to_native(data_16[1]); | ||||
| 		res.port = endian::big_to_native(data_16[2]); | ||||
| 
 | ||||
| 		size_t offset = dataoffset + 6; | ||||
| 		auto hostname = DnsName::decode(buffer, offset); | ||||
| 
 | ||||
| 		if (hostname) { | ||||
| 			res.hostname = std::move(*hostname); | ||||
| 			return std::move(res); | ||||
| 		} else { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct DnsRR_TXT | ||||
| { | ||||
| 	enum | ||||
| 	{ | ||||
| 		TAG = 0x10, | ||||
| 	}; | ||||
| 
 | ||||
| 	std::vector<std::string> values; | ||||
| 
 | ||||
| 	static optional<DnsRR_TXT> decode(const DnsResource &rr) | ||||
| 	{ | ||||
| 		const size_t size = rr.data.size(); | ||||
| 		if (size < 2) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		DnsRR_TXT res; | ||||
| 
 | ||||
| 		for (auto it = rr.data.begin(); it != rr.data.end(); ) { | ||||
| 			unsigned val_size = static_cast<unsigned char>(*it); | ||||
| 			if (val_size == 0 || it + val_size >= rr.data.end()) { | ||||
| 				return boost::none; | ||||
| 			} | ||||
| 			++it; | ||||
| 
 | ||||
| 			std::string value(val_size, ' '); | ||||
| 			std::copy(it, it + val_size, value.begin()); | ||||
| 			res.values.push_back(std::move(value)); | ||||
| 
 | ||||
| 			it += val_size; | ||||
| 		} | ||||
| 
 | ||||
| 		return std::move(res); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct DnsSDPair | ||||
| { | ||||
| 	optional<DnsRR_SRV> srv; | ||||
| 	optional<DnsRR_TXT> txt; | ||||
| }; | ||||
| 
 | ||||
| struct DnsSDMap : public std::map<std::string, DnsSDPair> | ||||
| { | ||||
| 	void insert_srv(std::string &&name, DnsRR_SRV &&srv) | ||||
| 	{ | ||||
| 		auto hit = this->find(name); | ||||
| 		if (hit != this->end()) { | ||||
| 			hit->second.srv = std::move(srv); | ||||
| 		} else { | ||||
| 			DnsSDPair pair; | ||||
| 			pair.srv = std::move(srv); | ||||
| 			this->insert(std::make_pair(std::move(name), std::move(pair))); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void insert_txt(std::string &&name, DnsRR_TXT &&txt) | ||||
| 	{ | ||||
| 		auto hit = this->find(name); | ||||
| 		if (hit != this->end()) { | ||||
| 			hit->second.txt = std::move(txt); | ||||
| 		} else { | ||||
| 			DnsSDPair pair; | ||||
| 			pair.txt = std::move(txt); | ||||
| 			this->insert(std::make_pair(std::move(name), std::move(pair))); | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct DnsMessage | ||||
| { | ||||
| 	enum | ||||
| 	{ | ||||
| 		MAX_SIZE = 4096, | ||||
| 		MAX_ANS = 30, | ||||
| 	}; | ||||
| 
 | ||||
| 	DnsHeader header; | ||||
| 	optional<DnsQuestion> question; | ||||
| 
 | ||||
| 	optional<DnsRR_A> rr_a; | ||||
| 	optional<DnsRR_AAAA> rr_aaaa; | ||||
| 	std::vector<DnsRR_SRV> rr_srv; | ||||
| 
 | ||||
| 	DnsSDMap sdmap; | ||||
| 
 | ||||
| 	static optional<DnsMessage> decode(const std::vector<char> &buffer, optional<uint16_t> id_wanted = boost::none) | ||||
| 	{ | ||||
| 		const auto size = buffer.size(); | ||||
| 		if (size < DnsHeader::SIZE + DnsQuestion::MIN_SIZE || size > MAX_SIZE) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		DnsMessage res; | ||||
| 		res.header = DnsHeader::decode(buffer); | ||||
| 
 | ||||
| 		if (id_wanted && *id_wanted != res.header.id) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		if (res.header.qdcount > 1 || res.header.ancount > MAX_ANS) { | ||||
| 			return boost::none; | ||||
| 		} | ||||
| 
 | ||||
| 		size_t offset = DnsHeader::SIZE; | ||||
| 		if (res.header.qdcount == 1) { | ||||
| 			res.question = DnsQuestion::decode(buffer, offset); | ||||
| 		} | ||||
| 
 | ||||
| 		for (unsigned i = 0; i < res.header.rrcount(); i++) { | ||||
| 			size_t dataoffset = 0; | ||||
| 			auto rr = DnsResource::decode(buffer, offset, dataoffset); | ||||
| 			if (!rr) { | ||||
| 				return boost::none; | ||||
| 			} else { | ||||
| 				res.parse_rr(buffer, std::move(*rr), dataoffset); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return std::move(res); | ||||
| 	} | ||||
| private: | ||||
| 	void parse_rr(const std::vector<char> &buffer, DnsResource &&rr, size_t dataoffset) | ||||
| 	{ | ||||
| 		switch (rr.type) { | ||||
| 			case DnsRR_A::TAG: DnsRR_A::decode(this->rr_a, rr); break; | ||||
| 			case DnsRR_AAAA::TAG: DnsRR_AAAA::decode(this->rr_aaaa, rr); break; | ||||
| 			case DnsRR_SRV::TAG: { | ||||
| 				auto srv = DnsRR_SRV::decode(buffer, rr, dataoffset); | ||||
| 				if (srv) { this->sdmap.insert_srv(std::move(rr.name), std::move(*srv)); } | ||||
| 				break; | ||||
| 			} | ||||
| 			case DnsRR_TXT::TAG: { | ||||
| 				auto txt = DnsRR_TXT::decode(rr); | ||||
| 				if (txt) { this->sdmap.insert_txt(std::move(rr.name), std::move(*txt)); } | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| struct BonjourRequest | ||||
| { | ||||
| 	static const asio::ip::address_v4 MCAST_IP4; | ||||
| 	static const uint16_t MCAST_PORT; | ||||
| 
 | ||||
| 	uint16_t id; | ||||
| 	std::vector<char> data; | ||||
| 
 | ||||
| 	static optional<BonjourRequest> make(const std::string &service, const std::string &protocol); | ||||
| 
 | ||||
| private: | ||||
| 	BonjourRequest(uint16_t id, std::vector<char> &&data) : | ||||
| 		id(id), | ||||
| 		data(std::move(data)) | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| const asio::ip::address_v4 BonjourRequest::MCAST_IP4{0xe00000fb}; | ||||
| const uint16_t BonjourRequest::MCAST_PORT = 5353; | ||||
| 
 | ||||
| optional<BonjourRequest> BonjourRequest::make(const std::string &service, const std::string &protocol) | ||||
| { | ||||
| 	if (service.size() > 15 || protocol.size() > 15) { | ||||
| 		return boost::none; | ||||
| 	} | ||||
| 
 | ||||
| 	std::random_device dev; | ||||
| 	std::uniform_int_distribution<uint16_t> dist; | ||||
| 	uint16_t id = dist(dev); | ||||
| 	uint16_t id_big = endian::native_to_big(id); | ||||
| 	const char *id_char = reinterpret_cast<char*>(&id_big); | ||||
| 
 | ||||
| 	std::vector<char> data; | ||||
| 	data.reserve(service.size() + 18); | ||||
| 
 | ||||
| 	// Add the transaction ID
 | ||||
| 	data.push_back(id_char[0]); | ||||
| 	data.push_back(id_char[1]); | ||||
| 
 | ||||
| 	// Add metadata
 | ||||
| 	static const unsigned char rq_meta[] = { | ||||
| 		0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||||
| 	}; | ||||
| 	std::copy(rq_meta, rq_meta + sizeof(rq_meta), std::back_inserter(data)); | ||||
| 
 | ||||
| 	// Add PTR query name
 | ||||
| 	data.push_back(service.size() + 1); | ||||
| 	data.push_back('_'); | ||||
| 	data.insert(data.end(), service.begin(), service.end()); | ||||
| 	data.push_back(protocol.size() + 1); | ||||
| 	data.push_back('_'); | ||||
| 	data.insert(data.end(), protocol.begin(), protocol.end()); | ||||
| 
 | ||||
| 	// Add the rest of PTR record
 | ||||
| 	static const unsigned char ptr_tail[] = { | ||||
| 		0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x00, 0xff, | ||||
| 	}; | ||||
| 	std::copy(ptr_tail, ptr_tail + sizeof(ptr_tail), std::back_inserter(data)); | ||||
| 
 | ||||
| 	return BonjourRequest(id, std::move(data)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // API - private part
 | ||||
| 
 | ||||
| struct Bonjour::priv | ||||
| { | ||||
| 	const std::string service; | ||||
| 	const std::string protocol; | ||||
| 	const std::string service_dn; | ||||
| 	unsigned timeout; | ||||
| 	uint16_t rq_id; | ||||
| 
 | ||||
| 	std::vector<char> buffer; | ||||
| 	std::thread io_thread; | ||||
| 	Bonjour::ReplyFn replyfn; | ||||
| 	Bonjour::CompleteFn completefn; | ||||
| 
 | ||||
| 	priv(std::string service, std::string protocol); | ||||
| 
 | ||||
| 	void udp_receive(udp::endpoint from, size_t bytes); | ||||
| 	void lookup_perform(); | ||||
| }; | ||||
| 
 | ||||
| Bonjour::priv::priv(std::string service, std::string protocol) : | ||||
| 	service(std::move(service)), | ||||
| 	protocol(std::move(protocol)), | ||||
| 	service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()), | ||||
| 	timeout(10), | ||||
| 	rq_id(0) | ||||
| { | ||||
| 	buffer.resize(DnsMessage::MAX_SIZE); | ||||
| } | ||||
| 
 | ||||
| void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes) | ||||
| { | ||||
| 	if (bytes == 0 || !replyfn) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	buffer.resize(bytes); | ||||
| 	const auto dns_msg = DnsMessage::decode(buffer, rq_id); | ||||
| 	if (dns_msg) { | ||||
| 		asio::ip::address ip = from.address(); | ||||
| 		if (dns_msg->rr_a) { ip = dns_msg->rr_a->ip; } | ||||
| 		else if (dns_msg->rr_aaaa) { ip = dns_msg->rr_aaaa->ip; } | ||||
| 
 | ||||
| 		for (const auto &sdpair : dns_msg->sdmap) { | ||||
| 			if (! sdpair.second.srv) { | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			const auto &srv = *sdpair.second.srv; | ||||
| 			BonjourReply reply(ip, sdpair.first, srv.hostname); | ||||
| 
 | ||||
| 			if (sdpair.second.txt) { | ||||
| 				static const std::string tag_path = "path="; | ||||
| 				static const std::string tag_version = "version="; | ||||
| 
 | ||||
| 				for (const auto &value : sdpair.second.txt->values) { | ||||
| 					if (value.size() > tag_path.size() && value.compare(0, tag_path.size(), tag_path) == 0) { | ||||
| 						reply.path = value.substr(tag_path.size()); | ||||
| 					} else if (value.size() > tag_version.size() && value.compare(0, tag_version.size(), tag_version) == 0) { | ||||
| 						reply.version = value.substr(tag_version.size()); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			replyfn(std::move(reply)); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Bonjour::priv::lookup_perform() | ||||
| { | ||||
| 	const auto brq = BonjourRequest::make(service, protocol); | ||||
| 	if (!brq) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	auto self = this; | ||||
| 	rq_id = brq->id; | ||||
| 
 | ||||
| 	try { | ||||
| 		boost::asio::io_service io_service; | ||||
| 		udp::socket socket(io_service); | ||||
| 		socket.open(udp::v4()); | ||||
| 		socket.set_option(udp::socket::reuse_address(true)); | ||||
| 		udp::endpoint mcast(BonjourRequest::MCAST_IP4, BonjourRequest::MCAST_PORT); | ||||
| 		socket.send_to(asio::buffer(brq->data), mcast); | ||||
| 
 | ||||
| 		bool timeout = false; | ||||
| 		asio::deadline_timer timer(io_service); | ||||
| 		timer.expires_from_now(boost::posix_time::seconds(10)); | ||||
| 		timer.async_wait([=, &timeout](const error_code &error) { | ||||
| 			timeout = true; | ||||
| 			if (self->completefn) { | ||||
| 				self->completefn(); | ||||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		udp::endpoint recv_from; | ||||
| 		const auto recv_handler = [&](const error_code &error, size_t bytes) { | ||||
| 			if (!error) { self->udp_receive(recv_from, bytes); } | ||||
| 		}; | ||||
| 		socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler); | ||||
| 
 | ||||
| 		while (io_service.run_one()) { | ||||
| 			if (timeout) { | ||||
| 				socket.cancel(); | ||||
| 			} else { | ||||
| 				buffer.resize(DnsMessage::MAX_SIZE); | ||||
| 				socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler); | ||||
| 			} | ||||
| 		} | ||||
| 	} catch (std::exception& e) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // API - public part
 | ||||
| 
 | ||||
| BonjourReply::BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname) : | ||||
| 	ip(std::move(ip)), | ||||
| 	service_name(std::move(service_name)), | ||||
| 	hostname(std::move(hostname)), | ||||
| 	path("/"), | ||||
| 	version("Unknown") | ||||
| {} | ||||
| 
 | ||||
| std::ostream& operator<<(std::ostream &os, const BonjourReply &reply) | ||||
| { | ||||
| 	os << "BonjourReply(" << reply.ip.to_string() << ", " << reply.service_name << ", " | ||||
| 		<< reply.hostname << ", " << reply.path << ", " << reply.version << ")"; | ||||
| 	return os; | ||||
| } | ||||
| 
 | ||||
| Bonjour::Bonjour(std::string service, std::string protocol) : | ||||
| 	p(new priv(std::move(service), std::move(protocol))) | ||||
| {} | ||||
| 
 | ||||
| Bonjour::Bonjour(Bonjour &&other) : p(std::move(other.p)) {} | ||||
| 
 | ||||
| Bonjour::~Bonjour() | ||||
| { | ||||
| 	if (p && p->io_thread.joinable()) { | ||||
| 		p->io_thread.detach(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| Bonjour& Bonjour::set_timeout(unsigned timeout) | ||||
| { | ||||
| 	if (p) { p->timeout = timeout; } | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Bonjour& Bonjour::on_reply(ReplyFn fn) | ||||
| { | ||||
| 	if (p) { p->replyfn = std::move(fn); } | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Bonjour& Bonjour::on_complete(CompleteFn fn) | ||||
| { | ||||
| 	if (p) { p->completefn = std::move(fn); } | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Bonjour::Ptr Bonjour::lookup() | ||||
| { | ||||
| 	auto self = std::make_shared<Bonjour>(std::move(*this)); | ||||
| 
 | ||||
| 	if (self->p) { | ||||
| 		auto io_thread = std::thread([self](){ | ||||
| 				self->p->lookup_perform(); | ||||
| 			}); | ||||
| 		self->p->io_thread = std::move(io_thread); | ||||
| 	} | ||||
| 
 | ||||
| 	return self; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void Bonjour::pokus()   // XXX
 | ||||
| { | ||||
| 	auto bonjour = Bonjour("octoprint") | ||||
| 		.set_timeout(15) | ||||
| 		.on_reply([](BonjourReply &&reply) { | ||||
| 			std::cerr << "BonjourReply: " << reply << std::endl; | ||||
| 		}) | ||||
| 		.on_complete([](){ | ||||
| 			std::cerr << "MDNS lookup complete" << std::endl; | ||||
| 		}) | ||||
| 		.lookup(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										56
									
								
								xs/src/slic3r/Utils/Bonjour.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								xs/src/slic3r/Utils/Bonjour.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | |||
| #ifndef slic3r_Bonjour_hpp_ | ||||
| #define slic3r_Bonjour_hpp_ | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <functional> | ||||
| // #include <ostream>
 | ||||
| #include <boost/asio/ip/address.hpp> | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| 
 | ||||
| // TODO: reply data structure
 | ||||
| struct BonjourReply | ||||
| { | ||||
| 	boost::asio::ip::address ip; | ||||
| 	std::string service_name; | ||||
| 	std::string hostname; | ||||
| 	std::string path; | ||||
| 	std::string version; | ||||
| 
 | ||||
| 	BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname); | ||||
| }; | ||||
| 
 | ||||
| std::ostream& operator<<(std::ostream &, const BonjourReply &); | ||||
| 
 | ||||
| 
 | ||||
| /// Bonjour lookup performer
 | ||||
| class Bonjour : public std::enable_shared_from_this<Bonjour> { | ||||
| private: | ||||
| 	struct priv; | ||||
| public: | ||||
| 	typedef std::shared_ptr<Bonjour> Ptr; | ||||
| 	typedef std::function<void(BonjourReply &&reply)> ReplyFn; | ||||
| 	typedef std::function<void()> CompleteFn; | ||||
| 
 | ||||
| 	Bonjour(std::string service, std::string protocol = "tcp"); | ||||
| 	Bonjour(Bonjour &&other); | ||||
| 	~Bonjour(); | ||||
| 
 | ||||
| 	Bonjour& set_timeout(unsigned timeout); | ||||
| 	Bonjour& on_reply(ReplyFn fn); | ||||
| 	Bonjour& on_complete(CompleteFn fn); | ||||
| 
 | ||||
| 	Ptr lookup(); | ||||
| 
 | ||||
| 	static void pokus();    // XXX: remove
 | ||||
| private: | ||||
| 	std::unique_ptr<priv> p; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										261
									
								
								xs/src/slic3r/Utils/Http.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								xs/src/slic3r/Utils/Http.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,261 @@ | |||
| #include "Http.hpp" | ||||
| 
 | ||||
| #include <cstdlib> | ||||
| #include <functional> | ||||
| #include <thread> | ||||
| #include <iostream> | ||||
| #include <tuple> | ||||
| #include <boost/format.hpp> | ||||
| 
 | ||||
| #include <curl/curl.h> | ||||
| 
 | ||||
| #include "../../libslic3r/libslic3r.h" | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| 
 | ||||
| // Private
 | ||||
| 
 | ||||
| class CurlGlobalInit | ||||
| { | ||||
| 	static const CurlGlobalInit instance; | ||||
| 
 | ||||
| 	CurlGlobalInit()  { ::curl_global_init(CURL_GLOBAL_DEFAULT); } | ||||
| 	~CurlGlobalInit() { ::curl_global_cleanup(); } | ||||
| }; | ||||
| 
 | ||||
| struct Http::priv | ||||
| { | ||||
| 	enum { | ||||
| 		DEFAULT_SIZE_LIMIT = 5 * 1024 * 1024, | ||||
| 	}; | ||||
| 
 | ||||
| 	::CURL *curl; | ||||
| 	::curl_httppost *form; | ||||
| 	::curl_httppost *form_end; | ||||
| 	::curl_slist *headerlist; | ||||
| 	std::string buffer; | ||||
| 	size_t limit; | ||||
| 
 | ||||
| 	std::thread io_thread; | ||||
| 	Http::CompleteFn completefn; | ||||
| 	Http::ErrorFn errorfn; | ||||
| 
 | ||||
| 	priv(const std::string &url); | ||||
| 	~priv(); | ||||
| 
 | ||||
| 	static size_t writecb(void *data, size_t size, size_t nmemb, void *userp); | ||||
| 	std::string body_size_error(); | ||||
| 	void http_perform(); | ||||
| }; | ||||
| 
 | ||||
| Http::priv::priv(const std::string &url) : | ||||
| 	curl(::curl_easy_init()), | ||||
| 	form(nullptr), | ||||
| 	form_end(nullptr), | ||||
| 	headerlist(nullptr) | ||||
| { | ||||
| 	if (curl == nullptr) { | ||||
| 		throw std::runtime_error(std::string("Could not construct Curl object")); | ||||
| 	} | ||||
| 
 | ||||
| 	::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());   // curl makes a copy internally
 | ||||
| 	::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION); | ||||
| } | ||||
| 
 | ||||
| Http::priv::~priv() | ||||
| { | ||||
| 	::curl_easy_cleanup(curl); | ||||
| 	::curl_formfree(form); | ||||
| 	::curl_slist_free_all(headerlist); | ||||
| } | ||||
| 
 | ||||
| size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp) | ||||
| { | ||||
| 	auto self = static_cast<priv*>(userp); | ||||
| 	const char *cdata = static_cast<char*>(data); | ||||
| 	const size_t realsize = size * nmemb; | ||||
| 
 | ||||
| 	const size_t limit = self->limit > 0 ? self->limit : DEFAULT_SIZE_LIMIT; | ||||
| 	if (self->buffer.size() + realsize > limit) { | ||||
| 		// This makes curl_easy_perform return CURLE_WRITE_ERROR
 | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	self->buffer.append(cdata, realsize); | ||||
| 
 | ||||
| 	return realsize; | ||||
| } | ||||
| 
 | ||||
| std::string Http::priv::body_size_error() | ||||
| { | ||||
| 	return (boost::format("HTTP body data size exceeded limit (%1% bytes)") % limit).str(); | ||||
| } | ||||
| 
 | ||||
| void Http::priv::http_perform() | ||||
| { | ||||
| 	::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); | ||||
| 	::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); | ||||
| 	::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb); | ||||
| 	::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(this)); | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
| 	::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); | ||||
| #endif | ||||
| 
 | ||||
| 	if (headerlist != nullptr) { | ||||
| 		::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); | ||||
| 	} | ||||
| 
 | ||||
| 	if (form != nullptr) { | ||||
| 		::curl_easy_setopt(curl, CURLOPT_HTTPPOST, form); | ||||
| 	} | ||||
| 
 | ||||
| 	CURLcode res = ::curl_easy_perform(curl); | ||||
| 	long http_status = 0; | ||||
| 	::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status); | ||||
| 
 | ||||
| 	if (res != CURLE_OK) { | ||||
| 		std::string error; | ||||
| 		if (res == CURLE_WRITE_ERROR) { | ||||
| 			error = std::move(body_size_error()); | ||||
| 		} else { | ||||
| 			error = ::curl_easy_strerror(res); | ||||
| 		}; | ||||
| 
 | ||||
| 		if (errorfn) { | ||||
| 			errorfn(std::move(buffer), std::move(error), http_status); | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (completefn) { | ||||
| 			completefn(std::move(buffer), http_status); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| Http::Http(const std::string &url) : p(new priv(url)) {} | ||||
| 
 | ||||
| 
 | ||||
| // Public
 | ||||
| 
 | ||||
| Http::Http(Http &&other) : p(std::move(other.p)) {} | ||||
| 
 | ||||
| Http::~Http() | ||||
| { | ||||
| 	if (p && p->io_thread.joinable()) { | ||||
| 		p->io_thread.detach(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Http& Http::size_limit(size_t sizeLimit) | ||||
| { | ||||
| 	if (p) { p->limit = sizeLimit; } | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Http& Http::header(std::string name, const std::string &value) | ||||
| { | ||||
| 	if (!p) { return * this; } | ||||
| 
 | ||||
| 	if (name.size() > 0) { | ||||
| 		name.append(": ").append(value); | ||||
| 	} else { | ||||
| 		name.push_back(':'); | ||||
| 	} | ||||
| 	p->headerlist = curl_slist_append(p->headerlist, name.c_str()); | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Http& Http::remove_header(std::string name) | ||||
| { | ||||
| 	if (p) { | ||||
| 		name.push_back(':'); | ||||
| 		p->headerlist = curl_slist_append(p->headerlist, name.c_str()); | ||||
| 	} | ||||
| 
 | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Http& Http::ca_file(const std::string &name) | ||||
| { | ||||
| 	if (p) { | ||||
| 		::curl_easy_setopt(p->curl, CURLOPT_CAINFO, name.c_str()); | ||||
| 	} | ||||
| 
 | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Http& Http::form_add(const std::string &name, const std::string &contents) | ||||
| { | ||||
| 	if (p) { | ||||
| 		::curl_formadd(&p->form, &p->form_end, | ||||
| 			CURLFORM_COPYNAME, name.c_str(), | ||||
| 			CURLFORM_COPYCONTENTS, contents.c_str(), | ||||
| 			CURLFORM_END | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Http& Http::form_add_file(const std::string &name, const std::string &filename) | ||||
| { | ||||
| 	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 | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Http& Http::on_complete(CompleteFn fn) | ||||
| { | ||||
| 	if (p) { p->completefn = std::move(fn); } | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Http& Http::on_error(ErrorFn fn) | ||||
| { | ||||
| 	if (p) { p->errorfn = std::move(fn); } | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| Http::Ptr Http::perform() | ||||
| { | ||||
| 	auto self = std::make_shared<Http>(std::move(*this)); | ||||
| 
 | ||||
| 	if (self->p) { | ||||
| 		auto io_thread = std::thread([self](){ | ||||
| 				self->p->http_perform(); | ||||
| 			}); | ||||
| 		self->p->io_thread = std::move(io_thread); | ||||
| 	} | ||||
| 
 | ||||
| 	return self; | ||||
| } | ||||
| 
 | ||||
| void Http::perform_sync() | ||||
| { | ||||
| 	if (p) { p->http_perform(); } | ||||
| } | ||||
| 
 | ||||
| Http Http::get(std::string url) | ||||
| { | ||||
| 	return std::move(Http{std::move(url)}); | ||||
| } | ||||
| 
 | ||||
| Http Http::post(std::string url) | ||||
| { | ||||
| 	Http http{std::move(url)}; | ||||
| 	curl_easy_setopt(http.p->curl, CURLOPT_POST, 1L); | ||||
| 	return http; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										53
									
								
								xs/src/slic3r/Utils/Http.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								xs/src/slic3r/Utils/Http.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,53 @@ | |||
| #ifndef slic3r_Http_hpp_ | ||||
| #define slic3r_Http_hpp_ | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <functional> | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| 
 | ||||
| /// Represetns a Http request
 | ||||
| class Http : public std::enable_shared_from_this<Http> { | ||||
| private: | ||||
| 	struct priv; | ||||
| public: | ||||
| 	typedef std::shared_ptr<Http> Ptr; | ||||
| 	typedef std::function<void(std::string /* body */, unsigned /* http_status */)> CompleteFn; | ||||
| 	typedef std::function<void(std::string /* body */, std::string /* error */, unsigned /* http_status */)> ErrorFn; | ||||
| 
 | ||||
| 	Http(Http &&other); | ||||
| 
 | ||||
| 	static Http get(std::string url); | ||||
| 	static Http post(std::string url); | ||||
| 	~Http(); | ||||
| 
 | ||||
| 	Http(const Http &) = delete; | ||||
| 	Http& operator=(const Http &) = delete; | ||||
| 	Http& operator=(Http &&) = delete; | ||||
| 
 | ||||
| 	Http& size_limit(size_t sizeLimit); | ||||
| 	Http& header(std::string name, const std::string &value); | ||||
| 	Http& remove_header(std::string name); | ||||
| 	Http& ca_file(const std::string &filename); | ||||
| 	Http& form_add(const std::string &name, const std::string &contents); | ||||
| 	Http& form_add_file(const std::string &name, const std::string &filename); | ||||
| 
 | ||||
| 	Http& on_complete(CompleteFn fn); | ||||
| 	Http& on_error(ErrorFn fn); | ||||
| 
 | ||||
| 	Ptr perform(); | ||||
| 	void perform_sync(); | ||||
| 
 | ||||
| private: | ||||
| 	Http(const std::string &url); | ||||
| 
 | ||||
| 	std::unique_ptr<priv> p; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										105
									
								
								xs/src/slic3r/Utils/OctoPrint.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								xs/src/slic3r/Utils/OctoPrint.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,105 @@ | |||
| #include "OctoPrint.hpp" | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <boost/format.hpp> | ||||
| 
 | ||||
| #include <wx/frame.h> | ||||
| #include <wx/event.h> | ||||
| 
 | ||||
| #include "libslic3r/PrintConfig.hpp" | ||||
| #include "slic3r/GUI/GUI.hpp" | ||||
| #include "Http.hpp" | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| 
 | ||||
| OctoPrint::OctoPrint(DynamicPrintConfig *config) : | ||||
| 	host(config->opt_string("octoprint_host")), | ||||
| 	apikey(config->opt_string("octoprint_apikey")), | ||||
| 	cafile(config->opt_string("octoprint_cafile")) | ||||
| {} | ||||
| 
 | ||||
| std::string  OctoPrint::test() const | ||||
| { | ||||
| 	// Since the request is performed synchronously here,
 | ||||
| 	// it is ok to refer to `res` from within the closure
 | ||||
| 	std::string res; | ||||
| 
 | ||||
| 	auto http = Http::get(std::move(make_url("api/version"))); | ||||
| 	set_auth(http); | ||||
| 	http.on_error([&](std::string, std::string error, unsigned status) { | ||||
| 			res = format_error(error, status); | ||||
| 		}) | ||||
| 		.perform_sync(); | ||||
| 
 | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| void OctoPrint::send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print) const | ||||
| { | ||||
| 	auto http = Http::post(std::move(make_url("api/files/local"))); | ||||
| 	set_auth(http); | ||||
| 	http.form_add("print", print ? "true" : "false") | ||||
| 		.form_add_file("file", filename) | ||||
| 		.on_complete([=](std::string body, unsigned status) { | ||||
| 			wxWindow *window = GUI::get_widget_by_id(windowId); | ||||
| 			wxCommandEvent* evt = new wxCommandEvent(completeEvt); | ||||
| 			evt->SetString("G-code file successfully uploaded to the OctoPrint server"); | ||||
| 			evt->SetInt(100); | ||||
| 			wxQueueEvent(window, evt); | ||||
| 		}) | ||||
| 		.on_error([=](std::string body, std::string error, unsigned status) { | ||||
| 			wxWindow *window = GUI::get_widget_by_id(windowId); | ||||
| 
 | ||||
| 			wxCommandEvent* evt_complete = new wxCommandEvent(completeEvt); | ||||
| 			evt_complete->SetInt(100); | ||||
| 			wxQueueEvent(window, evt_complete); | ||||
| 
 | ||||
| 			wxCommandEvent* evt_error = new wxCommandEvent(errorEvt); | ||||
| 			evt_error->SetString(wxString::Format("Error while uploading to the OctoPrint server: %s", format_error(error, status))); | ||||
| 			wxQueueEvent(window, evt_error); | ||||
| 		}) | ||||
| 		.perform(); | ||||
| } | ||||
| 
 | ||||
| void OctoPrint::set_auth(Http &http) const | ||||
| { | ||||
| 	http.header("X-Api-Key", apikey); | ||||
| 
 | ||||
| 	if (! cafile.empty()) { | ||||
| 		http.ca_file(cafile); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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()); | ||||
| 		} else { | ||||
| 			return std::move((boost::format("%1%/%2%") % host % path).str()); | ||||
| 		} | ||||
| 	} else { | ||||
| 		return std::move((boost::format("http://%1%/%2%") % host % path).str()); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| std::string OctoPrint::format_error(std::string error, unsigned status) | ||||
| { | ||||
| 	if (status != 0) { | ||||
| 		std::string res{"HTTP "}; | ||||
| 		res.append(std::to_string(status)); | ||||
| 
 | ||||
| 		if (status == 401) { | ||||
| 			res.append(": Invalid API key"); | ||||
| 		} | ||||
| 
 | ||||
| 		return std::move(res); | ||||
| 	} else { | ||||
| 		return std::move(error); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										35
									
								
								xs/src/slic3r/Utils/OctoPrint.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								xs/src/slic3r/Utils/OctoPrint.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| #ifndef slic3r_OctoPrint_hpp_ | ||||
| #define slic3r_OctoPrint_hpp_ | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| // #include "Http.hpp"    // XXX: ?
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| 
 | ||||
| class DynamicPrintConfig; | ||||
| class Http; | ||||
| 
 | ||||
| class OctoPrint | ||||
| { | ||||
| public: | ||||
| 	OctoPrint(DynamicPrintConfig *config); | ||||
| 
 | ||||
| 	std::string test() const; | ||||
| 	// XXX: style
 | ||||
| 	void send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print = false) const; | ||||
| private: | ||||
| 	std::string host; | ||||
| 	std::string apikey; | ||||
| 	std::string cafile; | ||||
| 
 | ||||
| 	void set_auth(Http &http) const; | ||||
| 	std::string make_url(const std::string &path) const; | ||||
| 	static std::string format_error(std::string error, unsigned status); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -59,6 +59,15 @@ extern "C" { | |||
|     #undef seek | ||||
|     #undef send | ||||
|     #undef write | ||||
|     #undef open | ||||
|     #undef close | ||||
|     #undef seekdir | ||||
|     #undef setbuf | ||||
|     #undef fread | ||||
|     #undef fseek | ||||
|     #undef fputc | ||||
|     #undef fwrite | ||||
|     #undef fclose | ||||
| #endif /* _MSC_VER */ | ||||
| } | ||||
| #endif | ||||
|  |  | |||
							
								
								
									
										14
									
								
								xs/xsp/Utils_OctoPrint.xsp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								xs/xsp/Utils_OctoPrint.xsp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| %module{Slic3r::XS}; | ||||
| 
 | ||||
| %{ | ||||
| #include <xsinit.h> | ||||
| #include "slic3r/Utils/OctoPrint.hpp" | ||||
| %} | ||||
| 
 | ||||
| %name{Slic3r::OctoPrint} class OctoPrint { | ||||
|     OctoPrint(DynamicPrintConfig *config); | ||||
|     ~OctoPrint(); | ||||
| 
 | ||||
|     std::string test() const; | ||||
|     void send_gcode(int windowId, int completeEvt, int errorEvt, std::string filename, bool print = false) const; | ||||
| }; | ||||
|  | @ -236,6 +236,10 @@ Ref<PresetHints>    		O_OBJECT_SLIC3R_T | |||
| TabIface*	   				O_OBJECT_SLIC3R | ||||
| Ref<TabIface> 				O_OBJECT_SLIC3R_T | ||||
| 
 | ||||
| OctoPrint*                  O_OBJECT_SLIC3R | ||||
| Ref<OctoPrint>              O_OBJECT_SLIC3R_T | ||||
| Clone<OctoPrint>            O_OBJECT_SLIC3R_T | ||||
| 
 | ||||
| Axis                  T_UV | ||||
| ExtrusionLoopRole     T_UV | ||||
| ExtrusionRole     T_UV | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 YuSanka
						YuSanka