mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Merge branch 'master' of https://github.com/Prusa3d/PrusaSlicer
This commit is contained in:
		
						commit
						0b9b74e35e
					
				
					 41 changed files with 5144 additions and 348 deletions
				
			
		
							
								
								
									
										3
									
								
								deps/blosc-mods.patch
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								deps/blosc-mods.patch
									
										
									
									
										vendored
									
									
								
							|  | @ -1,8 +1,9 @@ | |||
| From 5669891dfaaa4c814f3ec667ca6bf4e693aea978 Mon Sep 17 00:00:00 2001 | ||||
| From 7cf6c014a36f1712efbdbe9bc52d2d4922b54673 Mon Sep 17 00:00:00 2001 | ||||
| From: tamasmeszaros <meszaros.q@gmail.com> | ||||
| Date: Wed, 30 Oct 2019 12:54:52 +0100 | ||||
| Subject: [PATCH] Blosc 1.17 fixes and cmake config script | ||||
| 
 | ||||
| Signed-off-by: tamasmeszaros <meszaros.q@gmail.com> | ||||
| ---
 | ||||
|  CMakeLists.txt                   | 105 +++++++++++++++++----------------- | ||||
|  blosc/CMakeLists.txt             | 118 +++++++++------------------------------ | ||||
|  |  | |||
							
								
								
									
										17
									
								
								deps/deps-unix-common.cmake
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								deps/deps-unix-common.cmake
									
										
									
									
										vendored
									
									
								
							|  | @ -55,14 +55,14 @@ find_package(Git REQUIRED) | |||
| 
 | ||||
| ExternalProject_Add(dep_qhull | ||||
|     EXCLUDE_FROM_ALL 1 | ||||
|     URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" | ||||
|     URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 | ||||
|     #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" | ||||
|     #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 | ||||
|     GIT_REPOSITORY  https://github.com/qhull/qhull.git | ||||
|     GIT_TAG         7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch | ||||
|     CMAKE_ARGS | ||||
|         -DBUILD_SHARED_LIBS=OFF | ||||
|         -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local | ||||
|         ${DEP_CMAKE_OPTS} | ||||
|     UPDATE_COMMAND "" | ||||
|     PATCH_COMMAND patch -p1 < ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch | ||||
| ) | ||||
| 
 | ||||
| ExternalProject_Add(dep_blosc | ||||
|  | @ -80,8 +80,8 @@ ExternalProject_Add(dep_blosc | |||
|         -DBUILD_TESTS=OFF  | ||||
|         -DBUILD_BENCHMARKS=OFF  | ||||
|         -DPREFER_EXTERNAL_ZLIB=ON | ||||
|     UPDATE_COMMAND "" | ||||
|     PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch | ||||
|     PATCH_COMMAND       ${GIT_EXECUTABLE} reset --hard && git clean -df &&  | ||||
|                         ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch | ||||
| ) | ||||
| 
 | ||||
| ExternalProject_Add(dep_openexr | ||||
|  | @ -96,7 +96,6 @@ ExternalProject_Add(dep_openexr | |||
|         -DPYILMBASE_ENABLE:BOOL=OFF  | ||||
|         -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF | ||||
|         -DOPENEXR_BUILD_UTILS:BOOL=OFF | ||||
|     UPDATE_COMMAND "" | ||||
| ) | ||||
| 
 | ||||
| ExternalProject_Add(dep_openvdb | ||||
|  | @ -116,6 +115,6 @@ ExternalProject_Add(dep_openvdb | |||
|         -DOPENVDB_CORE_STATIC=ON  | ||||
|         -DTBB_STATIC=ON | ||||
|         -DOPENVDB_BUILD_VDB_PRINT=ON | ||||
|     UPDATE_COMMAND "" | ||||
|     PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch | ||||
|     PATCH_COMMAND PATCH_COMMAND     ${GIT_EXECUTABLE} checkout -f -- . && git clean -df &&  | ||||
|                                     ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch | ||||
| ) | ||||
							
								
								
									
										18
									
								
								deps/deps-windows.cmake
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								deps/deps-windows.cmake
									
										
									
									
										vendored
									
									
								
							|  | @ -218,15 +218,16 @@ find_package(Git REQUIRED) | |||
| 
 | ||||
| ExternalProject_Add(dep_qhull | ||||
|     EXCLUDE_FROM_ALL 1 | ||||
|     URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" | ||||
|     URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 | ||||
|     #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" | ||||
|     #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 | ||||
|     GIT_REPOSITORY  https://github.com/qhull/qhull.git | ||||
|     GIT_TAG         7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch | ||||
|     CMAKE_GENERATOR "${DEP_MSVC_GEN}" | ||||
|     CMAKE_ARGS | ||||
|         -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local | ||||
|         -DBUILD_SHARED_LIBS=OFF | ||||
|         -DCMAKE_POSITION_INDEPENDENT_CODE=ON | ||||
|         -DCMAKE_DEBUG_POSTFIX=d | ||||
|     UPDATE_COMMAND "" | ||||
|     BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj | ||||
|     INSTALL_COMMAND "" | ||||
| ) | ||||
|  | @ -287,8 +288,8 @@ ExternalProject_Add(dep_blosc | |||
|         -DPREFER_EXTERNAL_ZLIB=ON | ||||
|         -DBLOSC_IS_SUBPROJECT:BOOL=ON | ||||
|         -DBLOSC_INSTALL:BOOL=ON | ||||
|     UPDATE_COMMAND "" | ||||
|     PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch | ||||
|     PATCH_COMMAND       ${GIT_EXECUTABLE} checkout -f -- . && git clean -df &&  | ||||
|                         ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch | ||||
|     BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj | ||||
|     INSTALL_COMMAND "" | ||||
| ) | ||||
|  | @ -310,7 +311,6 @@ ExternalProject_Add(dep_openexr | |||
|         -DPYILMBASE_ENABLE:BOOL=OFF  | ||||
|         -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF | ||||
|         -DOPENEXR_BUILD_UTILS:BOOL=OFF | ||||
|     UPDATE_COMMAND "" | ||||
|     BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj | ||||
|     INSTALL_COMMAND "" | ||||
| ) | ||||
|  | @ -323,7 +323,7 @@ ExternalProject_Add(dep_openvdb | |||
|     #URL_HASH SHA256=dc337399dce8e1c9f21f20e97b1ce7e4933cb0a63bb3b8b734d8fcc464aa0c48 | ||||
|     GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git | ||||
|     GIT_TAG  aebaf8d95be5e57fd33949281ec357db4a576c2e #v6.2.1 | ||||
|     DEPENDS dep_blosc dep_openexr #dep_tbb dep_boost | ||||
|     DEPENDS dep_blosc dep_openexr dep_tbb dep_boost | ||||
|     CMAKE_GENERATOR "${DEP_MSVC_GEN}" | ||||
|     CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" | ||||
|     CMAKE_ARGS | ||||
|  | @ -339,8 +339,8 @@ ExternalProject_Add(dep_openvdb | |||
|         -DTBB_STATIC=ON | ||||
|         -DOPENVDB_BUILD_VDB_PRINT=ON | ||||
|     BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj | ||||
|     UPDATE_COMMAND "" | ||||
|     PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch | ||||
|     PATCH_COMMAND       ${GIT_EXECUTABLE} checkout -f -- . && git clean -df &&  | ||||
|                         ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch | ||||
|     INSTALL_COMMAND "" | ||||
| ) | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										3
									
								
								deps/openvdb-mods.patch
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								deps/openvdb-mods.patch
									
										
									
									
										vendored
									
									
								
							|  | @ -1,8 +1,9 @@ | |||
| From e48f4a835fe7cb391f9f90945472bd367fb4c4f1 Mon Sep 17 00:00:00 2001 | ||||
| From dbe038fce8a15ddc9a5c83ec5156d7bc9e178015 Mon Sep 17 00:00:00 2001 | ||||
| From: tamasmeszaros <meszaros.q@gmail.com> | ||||
| Date: Wed, 16 Oct 2019 17:42:50 +0200 | ||||
| Subject: [PATCH] Build fixes for PrusaSlicer integration | ||||
| 
 | ||||
| Signed-off-by: tamasmeszaros <meszaros.q@gmail.com> | ||||
| ---
 | ||||
|  CMakeLists.txt                  |   3 - | ||||
|  cmake/FindBlosc.cmake           | 218 --------------- | ||||
|  |  | |||
|  | @ -1,109 +1,70 @@ | |||
| <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="180.7mm" height="180.6mm" viewBox="0 0 512.1 512"> | ||||
|   <title>bed_texture_denser</title> | ||||
|   <path d="M510.6,510.9" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <path d="M.4,510.9" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="511.3" y1="511.3" x2="511.3" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <path d="M.4.4" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <path d="M510.6.4" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="1.1" y1="0.8" x2="1.1" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="1.1" y1="511.3" x2="1.1" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="511.3" y1="0.8" x2="511.3" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="1.1" y1="511.3" x2="511.3" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="1.1" y1="0.8" x2="511.3" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
| <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="180.5mm" height="180.6mm" viewBox="0 0 511.7 512"> | ||||
|   <title>MINI_bed_texture</title> | ||||
|   <path d="M510.6,510.9" transform="translate(0.4 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <path d="M.4,510.9" transform="translate(0.4 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="511" y1="511.3" x2="511" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <path d="M.4.4" transform="translate(0.4 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <path d="M510.6.4" transform="translate(0.4 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="0.8" y1="0.8" x2="0.8" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="0.8" y1="511.3" x2="0.8" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="511" y1="0.8" x2="511" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="0.8" y1="511.3" x2="511" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <line x1="0.8" y1="0.8" x2="511" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/> | ||||
|   <g> | ||||
|     <g> | ||||
|       <line x1="1.1" y1="383.6" x2="3.2" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="5.4" y1="383.6" x2="7" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="8.1" y1="383.6" x2="508.1" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="509.2" y1="383.6" x2="511.3" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="0.8" y1="383.6" x2="2.9" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="5" y1="383.6" x2="6.6" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="7.7" y1="383.6" x2="507.8" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="508.9" y1="383.6" x2="511" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|     <g> | ||||
|       <line x1="1.1" y1="256" x2="3.2" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="5.4" y1="256" x2="7" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="8.1" y1="256" x2="508.1" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="509.2" y1="256" x2="511.3" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="0.8" y1="256" x2="2.9" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="5" y1="256" x2="6.6" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="7.7" y1="256" x2="507.8" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="508.9" y1="256" x2="511" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|     <g> | ||||
|       <line x1="511.3" y1="128.4" x2="509.2" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="507.1" y1="128.4" x2="505.4" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="504.4" y1="128.4" x2="4.3" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="3.2" y1="128.4" x2="1.1" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="511" y1="128.4" x2="508.9" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="506.7" y1="128.4" x2="505.1" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="504" y1="128.4" x2="3.9" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/> | ||||
|       <line x1="2.9" y1="128.4" x2="0.8" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|     <g> | ||||
|       <line x1="128.7" y1="511.3" x2="128.7" y2="509.1" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="128.7" y1="507" x2="128.7" y2="505.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="128.7" y1="504.3" x2="128.7" y2="3.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.2734222412109375,2.1367111206054688,0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="128.7" y1="2.9" x2="128.7" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="128.3" y1="511.3" x2="128.3" y2="509.1" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="128.3" y1="507" x2="128.3" y2="505.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="128.3" y1="504.3" x2="128.3" y2="3.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.2734222412109375,2.1367111206054688,0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="128.3" y1="2.9" x2="128.3" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|     <g> | ||||
|       <line x1="256.2" y1="0.8" x2="256.2" y2="2.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="256.2" y1="5" x2="256.2" y2="6.7" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="256.2" y1="7.7" x2="256.2" y2="508.1" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.2734222412109375,2.1367111206054688,0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="256.2" y1="509.1" x2="256.2" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="255.9" y1="0.8" x2="255.9" y2="2.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="255.9" y1="5" x2="255.9" y2="6.7" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="255.9" y1="7.7" x2="255.9" y2="508.1" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.2734222412109375,2.1367111206054688,0.569789707660675,2.1367111206054688"/> | ||||
|       <line x1="255.9" y1="509.1" x2="255.9" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|     <g> | ||||
|       <line x1="383.8" y1="482.3" x2="383.8" y2="480.2" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="383.8" y1="478" x2="383.8" y2="476.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5678567290306091,2.129462480545044"/> | ||||
|       <line x1="383.8" y1="475.3" x2="383.8" y2="3.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.258924961090088,2.129462480545044,0.5678567290306091,2.129462480545044"/> | ||||
|       <line x1="383.8" y1="2.9" x2="383.8" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="383.4" y1="484.8" x2="383.4" y2="482.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|       <line x1="383.4" y1="480.5" x2="383.4" y2="478.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5708136558532715,2.1405510902404785"/> | ||||
|       <line x1="383.4" y1="477.8" x2="383.4" y2="3.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.281102180480957,2.1405510902404785,0.5708136558532715,2.1405510902404785"/> | ||||
|       <line x1="383.4" y1="2.9" x2="383.4" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/> | ||||
|     </g> | ||||
|   </g> | ||||
|   <g> | ||||
|     <path d="M277.3,489.1c4.6,0,7.4,2.8,7.4,8.1s-2.9,8.1-7.4,8.1-7.4-2.9-7.4-8.1S272.9,489.1,277.3,489.1Zm3.7,8.1c0-3.8-1.5-5.7-3.7-5.7s-3.8,1.9-3.8,5.7,1.3,5.6,3.8,5.6S281,500.9,281,497.2Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M293.1,499h-2.5v6h-3.3V489.3h6.1a7.3,7.3,0,0,1,3.2.6,4.1,4.1,0,0,1,2.6,4,4.4,4.4,0,0,1-3.1,4.3h0l3.5,6.8H296Zm-.1-2.4c1.5,0,2.7-.7,2.7-2.5a2.4,2.4,0,0,0-2.7-2.5h-2.4v5Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M302,489.3h3.4V505H302Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M311.6,497.2c0,4,1.4,5.6,3.8,5.6s3.4-1.3,3.6-3.5V499h-3.7v-2.4h6.8V505h-2.6v-2.2h-.1a5,5,0,0,1-4.6,2.5c-4.4,0-6.8-3.1-6.8-7.9s3-8.3,7.4-8.3,6.1,1.7,6.4,4.9h-3.4a2.8,2.8,0,0,0-3-2.5C313,491.5,311.6,493.5,311.6,497.2Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M325.1,489.3h3.4V505h-3.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M331.7,489.3h3.7l5,8.5a16.8,16.8,0,0,1,1.2,2.3h0V489.3h3.1V505h-3.5l-5.3-8.7a12.8,12.8,0,0,1-1.1-2.4h-.1V505h-3Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M356.8,501.5H351l-1.1,3.5h-3.2l5.4-15.7h3.8l5.6,15.7h-3.6Zm-3.5-7.1-1.5,4.6H356l-1.5-4.5c-.4-1.4-.6-2.3-.6-2.3h0A15.3,15.3,0,0,1,353.3,494.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M363.4,489.3h3.4v13h6.8V505H363.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M384,499.6V505h-3.4V489.3h5.5c3.4,0,6,1.4,6,5s-2.8,5.3-6.3,5.3Zm2-2.5a2.5,2.5,0,0,0,2.8-2.7c0-1.9-1.1-2.7-2.8-2.7h-2v5.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M400.1,499h-2.4v6h-3.4V489.3h6.1a7.3,7.3,0,0,1,3.2.6,3.9,3.9,0,0,1,2.6,4,4.5,4.5,0,0,1-3,4.3h0l3.5,6.8H403Zm-.1-2.4c1.5,0,2.8-.7,2.8-2.5a2.4,2.4,0,0,0-2.7-2.5h-2.4v5Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M408.7,489.3H412v9.1a5.6,5.6,0,0,0,.6,3.2,3.6,3.6,0,0,0,5,0c.6-.8.5-2.1.5-3.2v-9.1h3.3v10.2c0,3.9-2.5,5.8-6.4,5.8s-6.3-1.8-6.3-5.8Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M432.4,493.5a2,2,0,0,0-2.3-2c-1.5,0-2.4.8-2.4,1.9s.6,1.7,2.1,1.9l2.4.5c2.8.5,4.1,2,4.1,4.3s-2.3,5.2-6.3,5.2-6.1-1.9-6.2-4.8h3.5a2.5,2.5,0,0,0,2.8,2.3c1.9,0,2.7-.9,2.7-2.1s-.5-1.7-2.2-2l-2.3-.4c-2.4-.5-4.1-1.9-4.1-4.4s2.2-4.8,5.9-4.8,5.7,2,5.8,4.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M447.4,501.5h-5.7l-1.2,3.5h-3.2l5.5-15.7h3.7l5.6,15.7h-3.5Zm-3.5-7.1-1.4,4.6h4.1l-1.4-4.5a11.3,11.3,0,0,1-.6-2.3h-.1A15.3,15.3,0,0,1,443.9,494.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M458.7,489.3h4.9l2.8,8.4a15.7,15.7,0,0,1,.7,2.3h0l.7-2.3,2.8-8.4h4.9V505h-3.3V492.7h-.1a26.9,26.9,0,0,1-1,3.3l-2.9,9h-2.5l-2.9-9a26.9,26.9,0,0,1-1-3.3h0V505h-3.1Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M478.6,489.3H482V505h-3.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M485.2,489.3h3.7l5,8.5a16.8,16.8,0,0,1,1.2,2.3h0V489.3h3.1V505h-3.5l-5.3-8.7a12.8,12.8,0,0,1-1.1-2.4h-.1V505h-3Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M501.3,489.3h3.4V505h-3.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/> | ||||
|     <path d="M277.3,489.1c4.6,0,7.4,2.8,7.4,8.1s-2.9,8.1-7.4,8.1-7.4-2.9-7.4-8.1S272.9,489.1,277.3,489.1Zm3.7,8.1c0-3.8-1.5-5.7-3.7-5.7s-3.8,1.9-3.8,5.7,1.3,5.6,3.8,5.6S281,500.9,281,497.2Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M293.1,499h-2.5v6h-3.3V489.3h6.1a7.3,7.3,0,0,1,3.2.6,4.1,4.1,0,0,1,2.6,4,4.4,4.4,0,0,1-3.1,4.3h0l3.5,6.8H296Zm-.1-2.4c1.5,0,2.7-.7,2.7-2.5a2.4,2.4,0,0,0-2.7-2.5h-2.4v5Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M302,489.3h3.4V505H302Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M311.6,497.2c0,4,1.4,5.6,3.8,5.6s3.4-1.3,3.6-3.5V499h-3.7v-2.4h6.8V505h-2.6v-2.2h-.1a5,5,0,0,1-4.6,2.5c-4.4,0-6.8-3.1-6.8-7.9s3-8.3,7.4-8.3,6.1,1.7,6.4,4.9h-3.4a2.8,2.8,0,0,0-3-2.5C313,491.5,311.6,493.5,311.6,497.2Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M325.1,489.3h3.4V505h-3.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M331.7,489.3h3.7l5,8.5a16.8,16.8,0,0,1,1.2,2.3h0V489.3h3.1V505h-3.5l-5.3-8.7a12.8,12.8,0,0,1-1.1-2.4h-.1V505h-3Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M356.8,501.5H351l-1.1,3.5h-3.2l5.4-15.7h3.8l5.6,15.7h-3.6Zm-3.5-7.1-1.5,4.6H356l-1.5-4.5c-.4-1.4-.6-2.3-.6-2.3h0A15.3,15.3,0,0,1,353.3,494.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M363.4,489.3h3.4v13h6.8V505H363.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M384,499.6V505h-3.4V489.3h5.5c3.4,0,6,1.4,6,5s-2.8,5.3-6.3,5.3Zm2-2.5a2.5,2.5,0,0,0,2.8-2.7c0-1.9-1.1-2.7-2.8-2.7h-2v5.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M400.1,499h-2.4v6h-3.4V489.3h6.1a7.3,7.3,0,0,1,3.2.6,3.9,3.9,0,0,1,2.6,4,4.5,4.5,0,0,1-3,4.3h0l3.5,6.8H403Zm-.1-2.4c1.5,0,2.8-.7,2.8-2.5a2.4,2.4,0,0,0-2.7-2.5h-2.4v5Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M408.7,489.3H412v9.1a5.6,5.6,0,0,0,.6,3.2,3.6,3.6,0,0,0,5,0c.6-.8.5-2.1.5-3.2v-9.1h3.3v10.2c0,3.9-2.5,5.8-6.4,5.8s-6.3-1.8-6.3-5.8Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M432.4,493.5a2,2,0,0,0-2.3-2c-1.5,0-2.4.8-2.4,1.9s.6,1.7,2.1,1.9l2.4.5c2.8.5,4.1,2,4.1,4.3s-2.3,5.2-6.3,5.2-6.1-1.9-6.2-4.8h3.5a2.5,2.5,0,0,0,2.8,2.3c1.9,0,2.7-.9,2.7-2.1s-.5-1.7-2.2-2l-2.3-.4c-2.4-.5-4.1-1.9-4.1-4.4s2.2-4.8,5.9-4.8,5.7,2,5.8,4.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M447.4,501.5h-5.7l-1.2,3.5h-3.2l5.5-15.7h3.7l5.6,15.7h-3.5Zm-3.5-7.1-1.4,4.6h4.1l-1.4-4.5a11.3,11.3,0,0,1-.6-2.3h-.1A15.3,15.3,0,0,1,443.9,494.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M458.7,489.3h4.9l2.8,8.4a15.7,15.7,0,0,1,.7,2.3h0l.7-2.3,2.8-8.4h4.9V505h-3.3V492.7h-.1a26.9,26.9,0,0,1-1,3.3l-2.9,9h-2.5l-2.9-9a26.9,26.9,0,0,1-1-3.3h0V505h-3.1Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M478.6,489.3H482V505h-3.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M485.2,489.3h3.7l5,8.5a16.8,16.8,0,0,1,1.2,2.3h0V489.3h3.1V505h-3.5l-5.3-8.7a12.8,12.8,0,0,1-1.1-2.4h-.1V505h-3Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|     <path d="M501.3,489.3h3.4V505h-3.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/> | ||||
|   </g> | ||||
|   <line x1="0.4" y1="28.7" x2="511" y2="28.7" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.4" y1="57.1" x2="511" y2="57.1" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.4" y1="28.7" x2="511" y2="28.7" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.1" y1="85.4" x2="510.7" y2="85.4" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.1" y1="113.8" x2="510.7" y2="113.8" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.9" y1="142.1" x2="511.5" y2="142.1" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <path d="M509.9,141.7" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <path d="M-.7,141.7" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="1.5" y1="198.8" x2="512.1" y2="198.8" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line y1="170.5" x2="510.6" y2="170.5" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.5" y1="227.2" x2="511.1" y2="227.2" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="283.9" x2="511.2" y2="283.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.5" y1="28.7" x2="511.1" y2="28.7" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="312.2" x2="511.2" y2="312.2" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="340.6" x2="511.2" y2="340.6" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="368.9" x2="511.2" y2="368.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="397.2" x2="511.2" y2="397.2" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="425.6" x2="511.2" y2="425.6" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="453.9" x2="511.2" y2="453.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="0.6" y1="482.3" x2="511.2" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <path d="M481.9,511.4" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <path d="M481.9.8" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="454.3" y1="1.2" x2="454.3" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="482.6" y1="1.2" x2="482.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="425.9" y1="0.8" x2="425.9" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="397.6" y1="0.8" x2="397.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="369.2" y1="1.7" x2="369.2" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="312.6" y1="2.2" x2="312.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="340.9" y1="0.8" x2="340.9" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="284.2" y1="1.3" x2="284.2" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="227.5" y1="1.3" x2="227.5" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="482.6" y1="1.3" x2="482.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="199.2" y1="1.3" x2="199.2" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="170.8" y1="1.3" x2="170.8" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="142.5" y1="1.3" x2="142.5" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="114.1" y1="1.3" x2="114.1" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="85.8" y1="1.3" x2="85.8" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="57.4" y1="1.3" x2="57.4" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
|   <line x1="29.1" y1="1.3" x2="29.1" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/> | ||||
| </svg> | ||||
|  |  | |||
| Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 8.8 KiB | 
							
								
								
									
										45
									
								
								resources/udev/90-3dconnexion.rules
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								resources/udev/90-3dconnexion.rules
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| # See src/slic3r/GUI/Mouse3DController.cpp for the list of devices | ||||
| 
 | ||||
| # Logitech vendor devices | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" | ||||
| 
 | ||||
| # 3D Connexion vendor devices | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" | ||||
| KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" | ||||
|  | @ -22,6 +22,8 @@ add_subdirectory(libslic3r) | |||
| 
 | ||||
| if (SLIC3R_GUI) | ||||
|     add_subdirectory(imgui) | ||||
|     add_subdirectory(hidapi) | ||||
|     include_directories(hidapi/include) | ||||
| 
 | ||||
|     if(WIN32) | ||||
|         message(STATUS "WXWIN environment set to: $ENV{WXWIN}") | ||||
|  |  | |||
|  | @ -184,10 +184,21 @@ extern void stl_mirror_xz(stl_file *stl); | |||
| 
 | ||||
| extern void stl_get_size(stl_file *stl); | ||||
| 
 | ||||
| // the following function is not used
 | ||||
| /*
 | ||||
| template<typename T> | ||||
| extern void stl_transform(stl_file *stl, T *trafo3x4) | ||||
| { | ||||
| 	for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { | ||||
|     Eigen::Matrix<T, 3, 3, Eigen::DontAlign> trafo3x3; | ||||
|     for (int i = 0; i < 3; ++i) | ||||
|     { | ||||
|         for (int j = 0; j < 3; ++j) | ||||
|         { | ||||
|             trafo3x3(i, j) = (i * 4) + j; | ||||
|         } | ||||
|     } | ||||
|     Eigen::Matrix<T, 3, 3, Eigen::DontAlign> r = trafo3x3.inverse().transpose(); | ||||
|     for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { | ||||
| 		stl_facet &face = stl->facet_start[i_face]; | ||||
| 		for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) { | ||||
| 			stl_vertex &v_dst = face.vertex[i_vertex]; | ||||
|  | @ -196,21 +207,18 @@ extern void stl_transform(stl_file *stl, T *trafo3x4) | |||
| 			v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6]  * v_src(2) + trafo3x4[7]); | ||||
| 			v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]); | ||||
| 		} | ||||
| 		stl_vertex &v_dst = face.normal; | ||||
| 		stl_vertex  v_src = v_dst; | ||||
| 		v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2]  * v_src(2)); | ||||
| 		v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6]  * v_src(2)); | ||||
| 		v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2)); | ||||
| 	} | ||||
|         face.normal = (r * face.normal.template cast<T>()).template cast<float>().eval(); | ||||
|     } | ||||
| 
 | ||||
| 	stl_get_size(stl); | ||||
| } | ||||
| */ | ||||
| 
 | ||||
| template<typename T> | ||||
| inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t) | ||||
| { | ||||
| 	const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0); | ||||
| 	for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { | ||||
|     const Eigen::Matrix<T, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0).inverse().transpose(); | ||||
|     for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { | ||||
| 		stl_facet &f = stl->facet_start[i]; | ||||
| 		for (size_t j = 0; j < 3; ++j) | ||||
| 			f.vertex[j] = (t * f.vertex[j].template cast<T>()).template cast<float>().eval(); | ||||
|  | @ -223,12 +231,13 @@ inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Aff | |||
| template<typename T> | ||||
| inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m) | ||||
| { | ||||
| 	for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { | ||||
|     const Eigen::Matrix<T, 3, 3, Eigen::DontAlign> r = m.inverse().transpose(); | ||||
|     for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { | ||||
| 		stl_facet &f = stl->facet_start[i]; | ||||
| 		for (size_t j = 0; j < 3; ++j) | ||||
| 			f.vertex[j] = (m * f.vertex[j].template cast<T>()).template cast<float>().eval(); | ||||
| 		f.normal = (m * f.normal.template cast<T>()).template cast<float>().eval(); | ||||
| 	} | ||||
|         f.normal = (r * f.normal.template cast<T>()).template cast<float>().eval(); | ||||
|     } | ||||
| 
 | ||||
| 	stl_get_size(stl); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										17
									
								
								src/hidapi/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/hidapi/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| 
 | ||||
| if (WIN32) | ||||
|     set(HIDAPI_IMPL win/hid.c) | ||||
| elseif (APPLE) | ||||
|     set(HIDAPI_IMPL mac/hid.c) | ||||
| else () | ||||
|     # Assume Linux or Unix other than Mac OS | ||||
|     set(HIDAPI_IMPL linux/hid.c) | ||||
| endif() | ||||
| 
 | ||||
| include_directories(include) | ||||
| 
 | ||||
| add_library(hidapi STATIC ${HIDAPI_IMPL}) | ||||
| 
 | ||||
| if (CMAKE_SYSTEM_NAME STREQUAL "Linux") | ||||
|     target_link_libraries(hidapi udev) | ||||
| endif() | ||||
							
								
								
									
										395
									
								
								src/hidapi/include/hidapi.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										395
									
								
								src/hidapi/include/hidapi.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,395 @@ | |||
| /*******************************************************
 | ||||
|  HIDAPI - Multi-Platform library for | ||||
|  communication with HID devices. | ||||
| 
 | ||||
|  Alan Ott | ||||
|  Signal 11 Software | ||||
| 
 | ||||
|  8/22/2009 | ||||
| 
 | ||||
|  Copyright 2009, All Rights Reserved. | ||||
| 
 | ||||
|  At the discretion of the user of this library, | ||||
|  this software may be licensed under the terms of the | ||||
|  GNU General Public License v3, a BSD-Style license, or the | ||||
|  original HIDAPI license as outlined in the LICENSE.txt, | ||||
|  LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt | ||||
|  files located at the root of the source distribution. | ||||
|  These files may also be found in the public source | ||||
|  code repository located at: | ||||
|         http://github.com/signal11/hidapi .
 | ||||
| ********************************************************/ | ||||
| 
 | ||||
| /** @file
 | ||||
|  * @defgroup API hidapi API | ||||
|  */ | ||||
| 
 | ||||
| #ifndef HIDAPI_H__ | ||||
| #define HIDAPI_H__ | ||||
| 
 | ||||
| #include <wchar.h> | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
|       #define HID_API_EXPORT __declspec(dllexport) | ||||
|       #define HID_API_CALL | ||||
| #else | ||||
|       #define HID_API_EXPORT /**< API export macro */ | ||||
|       #define HID_API_CALL /**< API call macro */ | ||||
| #endif | ||||
| 
 | ||||
| #define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 		struct hid_device_; | ||||
| 		typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ | ||||
| 
 | ||||
| 		/** hidapi info structure */ | ||||
| 		struct hid_device_info { | ||||
| 			/** Platform-specific device path */ | ||||
| 			char *path; | ||||
| 			/** Device Vendor ID */ | ||||
| 			unsigned short vendor_id; | ||||
| 			/** Device Product ID */ | ||||
| 			unsigned short product_id; | ||||
| 			/** Serial Number */ | ||||
| 			wchar_t *serial_number; | ||||
| 			/** Device Release Number in binary-coded decimal,
 | ||||
| 			    also known as Device Version Number */ | ||||
| 			unsigned short release_number; | ||||
| 			/** Manufacturer String */ | ||||
| 			wchar_t *manufacturer_string; | ||||
| 			/** Product string */ | ||||
| 			wchar_t *product_string; | ||||
| 			/** Usage Page for this Device/Interface
 | ||||
| 			    (Windows/Mac only). */ | ||||
| 			unsigned short usage_page; | ||||
| 			/** Usage for this Device/Interface
 | ||||
| 			    (Windows/Mac only).*/ | ||||
| 			unsigned short usage; | ||||
| 			/** The USB interface which this logical device
 | ||||
| 			    represents. | ||||
| 
 | ||||
| 				* Valid on both Linux implementations in all cases. | ||||
| 				* Valid on the Windows implementation only if the device | ||||
| 				  contains more than one interface. | ||||
| 				* Valid on the Mac implementation if and only if the device | ||||
| 				  is a USB HID device. */ | ||||
| 			int interface_number; | ||||
| 
 | ||||
| 			/** Pointer to the next device */ | ||||
| 			struct hid_device_info *next; | ||||
| 		}; | ||||
| 
 | ||||
| 
 | ||||
| 		/** @brief Initialize the HIDAPI library.
 | ||||
| 
 | ||||
| 			This function initializes the HIDAPI library. Calling it is not | ||||
| 			strictly necessary, as it will be called automatically by | ||||
| 			hid_enumerate() and any of the hid_open_*() functions if it is | ||||
| 			needed.  This function should be called at the beginning of | ||||
| 			execution however, if there is a chance of HIDAPI handles | ||||
| 			being opened by different threads simultaneously. | ||||
| 			 | ||||
| 			@ingroup API | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns 0 on success and -1 on error. | ||||
| 		*/ | ||||
| 		int HID_API_EXPORT HID_API_CALL hid_init(void); | ||||
| 
 | ||||
| 		/** @brief Finalize the HIDAPI library.
 | ||||
| 
 | ||||
| 			This function frees all of the static data associated with | ||||
| 			HIDAPI. It should be called at the end of execution to avoid | ||||
| 			memory leaks. | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 
 | ||||
| 		    @returns | ||||
| 				This function returns 0 on success and -1 on error. | ||||
| 		*/ | ||||
| 		int HID_API_EXPORT HID_API_CALL hid_exit(void); | ||||
| 
 | ||||
| 		/** @brief Enumerate the HID Devices.
 | ||||
| 
 | ||||
| 			This function returns a linked list of all the HID devices | ||||
| 			attached to the system which match vendor_id and product_id. | ||||
| 			If @p vendor_id is set to 0 then any vendor matches. | ||||
| 			If @p product_id is set to 0 then any product matches. | ||||
| 			If @p vendor_id and @p product_id are both set to 0, then | ||||
| 			all HID devices will be returned. | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param vendor_id The Vendor ID (VID) of the types of device | ||||
| 				to open. | ||||
| 			@param product_id The Product ID (PID) of the types of | ||||
| 				device to open. | ||||
| 
 | ||||
| 		    @returns | ||||
| 		    	This function returns a pointer to a linked list of type | ||||
| 		    	struct #hid_device_info, containing information about the HID devices | ||||
| 		    	attached to the system, or NULL in the case of failure. Free | ||||
| 		    	this linked list by calling hid_free_enumeration(). | ||||
| 		*/ | ||||
| 		struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); | ||||
| 
 | ||||
| 		/** @brief Free an enumeration Linked List
 | ||||
| 
 | ||||
| 		    This function frees a linked list created by hid_enumerate(). | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 		    @param devs Pointer to a list of struct_device returned from | ||||
| 		    	      hid_enumerate(). | ||||
| 		*/ | ||||
| 		void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); | ||||
| 
 | ||||
| 		/** @brief Open a HID device using a Vendor ID (VID), Product ID
 | ||||
| 			(PID) and optionally a serial number. | ||||
| 
 | ||||
| 			If @p serial_number is NULL, the first device with the | ||||
| 			specified VID and PID is opened. | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param vendor_id The Vendor ID (VID) of the device to open. | ||||
| 			@param product_id The Product ID (PID) of the device to open. | ||||
| 			@param serial_number The Serial Number of the device to open | ||||
| 				               (Optionally NULL). | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns a pointer to a #hid_device object on | ||||
| 				success or NULL on failure. | ||||
| 		*/ | ||||
| 		HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); | ||||
| 
 | ||||
| 		/** @brief Open a HID device by its path name.
 | ||||
| 
 | ||||
| 			The path name be determined by calling hid_enumerate(), or a | ||||
| 			platform-specific path name can be used (eg: /dev/hidraw0 on | ||||
| 			Linux). | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 		    @param path The path name of the device to open | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns a pointer to a #hid_device object on | ||||
| 				success or NULL on failure. | ||||
| 		*/ | ||||
| 		HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); | ||||
| 
 | ||||
| 		/** @brief Write an Output report to a HID device.
 | ||||
| 
 | ||||
| 			The first byte of @p data[] must contain the Report ID. For | ||||
| 			devices which only support a single report, this must be set | ||||
| 			to 0x0. The remaining bytes contain the report data. Since | ||||
| 			the Report ID is mandatory, calls to hid_write() will always | ||||
| 			contain one more byte than the report contains. For example, | ||||
| 			if a hid report is 16 bytes long, 17 bytes must be passed to | ||||
| 			hid_write(), the Report ID (or 0x0, for devices with a | ||||
| 			single report), followed by the report data (16 bytes). In | ||||
| 			this example, the length passed in would be 17. | ||||
| 
 | ||||
| 			hid_write() will send the data on the first OUT endpoint, if | ||||
| 			one exists. If it does not, it will send the data through | ||||
| 			the Control Endpoint (Endpoint 0). | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 			@param data The data to send, including the report number as | ||||
| 				the first byte. | ||||
| 			@param length The length in bytes of the data to send. | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns the actual number of bytes written and | ||||
| 				-1 on error. | ||||
| 		*/ | ||||
| 		int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length); | ||||
| 
 | ||||
| 		/** @brief Read an Input report from a HID device with timeout.
 | ||||
| 
 | ||||
| 			Input reports are returned | ||||
| 			to the host through the INTERRUPT IN endpoint. The first byte will | ||||
| 			contain the Report number if the device uses numbered reports. | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 			@param data A buffer to put the read data into. | ||||
| 			@param length The number of bytes to read. For devices with | ||||
| 				multiple reports, make sure to read an extra byte for | ||||
| 				the report number. | ||||
| 			@param milliseconds timeout in milliseconds or -1 for blocking wait. | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns the actual number of bytes read and | ||||
| 				-1 on error. If no packet was available to be read within | ||||
| 				the timeout period, this function returns 0. | ||||
| 		*/ | ||||
| 		int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); | ||||
| 
 | ||||
| 		/** @brief Read an Input report from a HID device.
 | ||||
| 
 | ||||
| 			Input reports are returned | ||||
| 		    to the host through the INTERRUPT IN endpoint. The first byte will | ||||
| 			contain the Report number if the device uses numbered reports. | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 			@param data A buffer to put the read data into. | ||||
| 			@param length The number of bytes to read. For devices with | ||||
| 				multiple reports, make sure to read an extra byte for | ||||
| 				the report number. | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns the actual number of bytes read and | ||||
| 				-1 on error. If no packet was available to be read and | ||||
| 				the handle is in non-blocking mode, this function returns 0. | ||||
| 		*/ | ||||
| 		int  HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length); | ||||
| 
 | ||||
| 		/** @brief Set the device handle to be non-blocking.
 | ||||
| 
 | ||||
| 			In non-blocking mode calls to hid_read() will return | ||||
| 			immediately with a value of 0 if there is no data to be | ||||
| 			read. In blocking mode, hid_read() will wait (block) until | ||||
| 			there is data to read before returning. | ||||
| 
 | ||||
| 			Nonblocking can be turned on and off at any time. | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 			@param nonblock enable or not the nonblocking reads | ||||
| 			 - 1 to enable nonblocking | ||||
| 			 - 0 to disable nonblocking. | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns 0 on success and -1 on error. | ||||
| 		*/ | ||||
| 		int  HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock); | ||||
| 
 | ||||
| 		/** @brief Send a Feature report to the device.
 | ||||
| 
 | ||||
| 			Feature reports are sent over the Control endpoint as a | ||||
| 			Set_Report transfer.  The first byte of @p data[] must | ||||
| 			contain the Report ID. For devices which only support a | ||||
| 			single report, this must be set to 0x0. The remaining bytes | ||||
| 			contain the report data. Since the Report ID is mandatory, | ||||
| 			calls to hid_send_feature_report() will always contain one | ||||
| 			more byte than the report contains. For example, if a hid | ||||
| 			report is 16 bytes long, 17 bytes must be passed to | ||||
| 			hid_send_feature_report(): the Report ID (or 0x0, for | ||||
| 			devices which do not use numbered reports), followed by the | ||||
| 			report data (16 bytes). In this example, the length passed | ||||
| 			in would be 17. | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 			@param data The data to send, including the report number as | ||||
| 				the first byte. | ||||
| 			@param length The length in bytes of the data to send, including | ||||
| 				the report number. | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns the actual number of bytes written and | ||||
| 				-1 on error. | ||||
| 		*/ | ||||
| 		int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length); | ||||
| 
 | ||||
| 		/** @brief Get a feature report from a HID device.
 | ||||
| 
 | ||||
| 			Set the first byte of @p data[] to the Report ID of the | ||||
| 			report to be read.  Make sure to allow space for this | ||||
| 			extra byte in @p data[]. Upon return, the first byte will | ||||
| 			still contain the Report ID, and the report data will | ||||
| 			start in data[1]. | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 			@param data A buffer to put the read data into, including | ||||
| 				the Report ID. Set the first byte of @p data[] to the | ||||
| 				Report ID of the report to be read, or set it to zero | ||||
| 				if your device does not use numbered reports. | ||||
| 			@param length The number of bytes to read, including an | ||||
| 				extra byte for the report ID. The buffer can be longer | ||||
| 				than the actual report. | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns the number of bytes read plus | ||||
| 				one for the report ID (which is still in the first | ||||
| 				byte), or -1 on error. | ||||
| 		*/ | ||||
| 		int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length); | ||||
| 
 | ||||
| 		/** @brief Close a HID device.
 | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 		*/ | ||||
| 		void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev); | ||||
| 
 | ||||
| 		/** @brief Get The Manufacturer String from a HID device.
 | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 			@param string A wide string buffer to put the data into. | ||||
| 			@param maxlen The length of the buffer in multiples of wchar_t. | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns 0 on success and -1 on error. | ||||
| 		*/ | ||||
| 		int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen); | ||||
| 
 | ||||
| 		/** @brief Get The Product String from a HID device.
 | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 			@param string A wide string buffer to put the data into. | ||||
| 			@param maxlen The length of the buffer in multiples of wchar_t. | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns 0 on success and -1 on error. | ||||
| 		*/ | ||||
| 		int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen); | ||||
| 
 | ||||
| 		/** @brief Get The Serial Number String from a HID device.
 | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 			@param string A wide string buffer to put the data into. | ||||
| 			@param maxlen The length of the buffer in multiples of wchar_t. | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns 0 on success and -1 on error. | ||||
| 		*/ | ||||
| 		int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen); | ||||
| 
 | ||||
| 		/** @brief Get a string from a HID device, based on its string index.
 | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 			@param string_index The index of the string to get. | ||||
| 			@param string A wide string buffer to put the data into. | ||||
| 			@param maxlen The length of the buffer in multiples of wchar_t. | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns 0 on success and -1 on error. | ||||
| 		*/ | ||||
| 		int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen); | ||||
| 
 | ||||
| 		/** @brief Get a string describing the last error which occurred.
 | ||||
| 
 | ||||
| 			@ingroup API | ||||
| 			@param dev A device handle returned from hid_open(). | ||||
| 
 | ||||
| 			@returns | ||||
| 				This function returns a string containing the last error | ||||
| 				which occurred or NULL if none has occurred. | ||||
| 		*/ | ||||
| 		HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
							
								
								
									
										797
									
								
								src/hidapi/linux/hid.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										797
									
								
								src/hidapi/linux/hid.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,797 @@ | |||
| /*******************************************************
 | ||||
|  HIDAPI - Multi-Platform library for | ||||
|  communication with HID devices. | ||||
| 
 | ||||
|  Alan Ott | ||||
|  Signal 11 Software | ||||
| 
 | ||||
|  8/22/2009 | ||||
|  Linux Version - 6/2/2009 | ||||
| 
 | ||||
|  Copyright 2009, All Rights Reserved. | ||||
| 
 | ||||
|  At the discretion of the user of this library, | ||||
|  this software may be licensed under the terms of the | ||||
|  GNU General Public License v3, a BSD-Style license, or the | ||||
|  original HIDAPI license as outlined in the LICENSE.txt, | ||||
|  LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt | ||||
|  files located at the root of the source distribution. | ||||
|  These files may also be found in the public source | ||||
|  code repository located at: | ||||
|         http://github.com/signal11/hidapi .
 | ||||
| ********************************************************/ | ||||
| 
 | ||||
| /* C */ | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include <locale.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| /* Unix */ | ||||
| #include <unistd.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/ioctl.h> | ||||
| #include <sys/utsname.h> | ||||
| #include <fcntl.h> | ||||
| #include <poll.h> | ||||
| 
 | ||||
| /* Linux */ | ||||
| #include <linux/hidraw.h> | ||||
| #include <linux/version.h> | ||||
| #include <linux/input.h> | ||||
| #include <libudev.h> | ||||
| 
 | ||||
| #include "hidapi.h" | ||||
| 
 | ||||
| /* Definitions from linux/hidraw.h. Since these are new, some distros
 | ||||
|    may not have header files which contain them. */ | ||||
| #ifndef HIDIOCSFEATURE | ||||
| #define HIDIOCSFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) | ||||
| #endif | ||||
| #ifndef HIDIOCGFEATURE | ||||
| #define HIDIOCGFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| /* USB HID device property names */ | ||||
| const char *device_string_names[] = { | ||||
| 	"manufacturer", | ||||
| 	"product", | ||||
| 	"serial", | ||||
| }; | ||||
| 
 | ||||
| /* Symbolic names for the properties above */ | ||||
| enum device_string_id { | ||||
| 	DEVICE_STRING_MANUFACTURER, | ||||
| 	DEVICE_STRING_PRODUCT, | ||||
| 	DEVICE_STRING_SERIAL, | ||||
| 
 | ||||
| 	DEVICE_STRING_COUNT, | ||||
| }; | ||||
| 
 | ||||
| struct hid_device_ { | ||||
| 	int device_handle; | ||||
| 	int blocking; | ||||
| 	int uses_numbered_reports; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static __u32 kernel_version = 0; | ||||
| 
 | ||||
| static __u32 detect_kernel_version(void) | ||||
| { | ||||
| 	struct utsname name; | ||||
| 	int major, minor, release; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	uname(&name); | ||||
| 	ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); | ||||
| 	if (ret == 3) { | ||||
| 		return KERNEL_VERSION(major, minor, release); | ||||
| 	} | ||||
| 
 | ||||
| 	ret = sscanf(name.release, "%d.%d", &major, &minor); | ||||
| 	if (ret == 2) { | ||||
| 		return KERNEL_VERSION(major, minor, 0); | ||||
| 	} | ||||
| 
 | ||||
| 	printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static hid_device *new_hid_device(void) | ||||
| { | ||||
| 	hid_device *dev = calloc(1, sizeof(hid_device)); | ||||
| 	dev->device_handle = -1; | ||||
| 	dev->blocking = 1; | ||||
| 	dev->uses_numbered_reports = 0; | ||||
| 
 | ||||
| 	return dev; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* The caller must free the returned string with free(). */ | ||||
| static wchar_t *utf8_to_wchar_t(const char *utf8) | ||||
| { | ||||
| 	wchar_t *ret = NULL; | ||||
| 
 | ||||
| 	if (utf8) { | ||||
| 		size_t wlen = mbstowcs(NULL, utf8, 0); | ||||
| 		if ((size_t) -1 == wlen) { | ||||
| 			return wcsdup(L""); | ||||
| 		} | ||||
| 		ret = calloc(wlen+1, sizeof(wchar_t)); | ||||
| 		mbstowcs(ret, utf8, wlen+1); | ||||
| 		ret[wlen] = 0x0000; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /* Get an attribute value from a udev_device and return it as a whar_t
 | ||||
|    string. The returned string must be freed with free() when done.*/ | ||||
| static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) | ||||
| { | ||||
| 	return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name)); | ||||
| } | ||||
| 
 | ||||
| /* uses_numbered_reports() returns 1 if report_descriptor describes a device
 | ||||
|    which contains numbered reports. */ | ||||
| static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { | ||||
| 	unsigned int i = 0; | ||||
| 	int size_code; | ||||
| 	int data_len, key_size; | ||||
| 
 | ||||
| 	while (i < size) { | ||||
| 		int key = report_descriptor[i]; | ||||
| 
 | ||||
| 		/* Check for the Report ID key */ | ||||
| 		if (key == 0x85/*Report ID*/) { | ||||
| 			/* This device has a Report ID, which means it uses
 | ||||
| 			   numbered reports. */ | ||||
| 			return 1; | ||||
| 		} | ||||
| 
 | ||||
| 		//printf("key: %02hhx\n", key);
 | ||||
| 
 | ||||
| 		if ((key & 0xf0) == 0xf0) { | ||||
| 			/* This is a Long Item. The next byte contains the
 | ||||
| 			   length of the data section (value) for this key. | ||||
| 			   See the HID specification, version 1.11, section | ||||
| 			   6.2.2.3, titled "Long Items." */ | ||||
| 			if (i+1 < size) | ||||
| 				data_len = report_descriptor[i+1]; | ||||
| 			else | ||||
| 				data_len = 0; /* malformed report */ | ||||
| 			key_size = 3; | ||||
| 		} | ||||
| 		else { | ||||
| 			/* This is a Short Item. The bottom two bits of the
 | ||||
| 			   key contain the size code for the data section | ||||
| 			   (value) for this key.  Refer to the HID | ||||
| 			   specification, version 1.11, section 6.2.2.2, | ||||
| 			   titled "Short Items." */ | ||||
| 			size_code = key & 0x3; | ||||
| 			switch (size_code) { | ||||
| 			case 0: | ||||
| 			case 1: | ||||
| 			case 2: | ||||
| 				data_len = size_code; | ||||
| 				break; | ||||
| 			case 3: | ||||
| 				data_len = 4; | ||||
| 				break; | ||||
| 			default: | ||||
| 				/* Can't ever happen since size_code is & 0x3 */ | ||||
| 				data_len = 0; | ||||
| 				break; | ||||
| 			}; | ||||
| 			key_size = 1; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Skip over this key and it's associated data */ | ||||
| 		i += data_len + key_size; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Didn't find a Report ID key. Device doesn't use numbered reports. */ | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * The caller is responsible for free()ing the (newly-allocated) character | ||||
|  * strings pointed to by serial_number_utf8 and product_name_utf8 after use. | ||||
|  */ | ||||
| static int | ||||
| parse_uevent_info(const char *uevent, int *bus_type, | ||||
| 	unsigned short *vendor_id, unsigned short *product_id, | ||||
| 	char **serial_number_utf8, char **product_name_utf8) | ||||
| { | ||||
| 	char *tmp = strdup(uevent); | ||||
| 	char *saveptr = NULL; | ||||
| 	char *line; | ||||
| 	char *key; | ||||
| 	char *value; | ||||
| 
 | ||||
| 	int found_id = 0; | ||||
| 	int found_serial = 0; | ||||
| 	int found_name = 0; | ||||
| 
 | ||||
| 	line = strtok_r(tmp, "\n", &saveptr); | ||||
| 	while (line != NULL) { | ||||
| 		/* line: "KEY=value" */ | ||||
| 		key = line; | ||||
| 		value = strchr(line, '='); | ||||
| 		if (!value) { | ||||
| 			goto next_line; | ||||
| 		} | ||||
| 		*value = '\0'; | ||||
| 		value++; | ||||
| 
 | ||||
| 		if (strcmp(key, "HID_ID") == 0) { | ||||
| 			/**
 | ||||
| 			 *        type vendor   product | ||||
| 			 * HID_ID=0003:000005AC:00008242 | ||||
| 			 **/ | ||||
| 			int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); | ||||
| 			if (ret == 3) { | ||||
| 				found_id = 1; | ||||
| 			} | ||||
| 		} else if (strcmp(key, "HID_NAME") == 0) { | ||||
| 			/* The caller has to free the product name */ | ||||
| 			*product_name_utf8 = strdup(value); | ||||
| 			found_name = 1; | ||||
| 		} else if (strcmp(key, "HID_UNIQ") == 0) { | ||||
| 			/* The caller has to free the serial number */ | ||||
| 			*serial_number_utf8 = strdup(value); | ||||
| 			found_serial = 1; | ||||
| 		} | ||||
| 
 | ||||
| next_line: | ||||
| 		line = strtok_r(NULL, "\n", &saveptr); | ||||
| 	} | ||||
| 
 | ||||
| 	free(tmp); | ||||
| 	return (found_id && found_name && found_serial); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen) | ||||
| { | ||||
| 	struct udev *udev; | ||||
| 	struct udev_device *udev_dev, *parent, *hid_dev; | ||||
| 	struct stat s; | ||||
| 	int ret = -1; | ||||
|         char *serial_number_utf8 = NULL; | ||||
|         char *product_name_utf8 = NULL; | ||||
| 
 | ||||
| 	/* Create the udev object */ | ||||
| 	udev = udev_new(); | ||||
| 	if (!udev) { | ||||
| 		printf("Can't create udev\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Get the dev_t (major/minor numbers) from the file handle. */ | ||||
| 	ret = fstat(dev->device_handle, &s); | ||||
| 	if (-1 == ret) | ||||
| 		return ret; | ||||
| 	/* Open a udev device from the dev_t. 'c' means character device. */ | ||||
| 	udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); | ||||
| 	if (udev_dev) { | ||||
| 		hid_dev = udev_device_get_parent_with_subsystem_devtype( | ||||
| 			udev_dev, | ||||
| 			"hid", | ||||
| 			NULL); | ||||
| 		if (hid_dev) { | ||||
| 			unsigned short dev_vid; | ||||
| 			unsigned short dev_pid; | ||||
| 			int bus_type; | ||||
| 			size_t retm; | ||||
| 
 | ||||
| 			ret = parse_uevent_info( | ||||
| 			           udev_device_get_sysattr_value(hid_dev, "uevent"), | ||||
| 			           &bus_type, | ||||
| 			           &dev_vid, | ||||
| 			           &dev_pid, | ||||
| 			           &serial_number_utf8, | ||||
| 			           &product_name_utf8); | ||||
| 
 | ||||
| 			if (bus_type == BUS_BLUETOOTH) { | ||||
| 				switch (key) { | ||||
| 					case DEVICE_STRING_MANUFACTURER: | ||||
| 						wcsncpy(string, L"", maxlen); | ||||
| 						ret = 0; | ||||
| 						break; | ||||
| 					case DEVICE_STRING_PRODUCT: | ||||
| 						retm = mbstowcs(string, product_name_utf8, maxlen); | ||||
| 						ret = (retm == (size_t)-1)? -1: 0; | ||||
| 						break; | ||||
| 					case DEVICE_STRING_SERIAL: | ||||
| 						retm = mbstowcs(string, serial_number_utf8, maxlen); | ||||
| 						ret = (retm == (size_t)-1)? -1: 0; | ||||
| 						break; | ||||
| 					case DEVICE_STRING_COUNT: | ||||
| 					default: | ||||
| 						ret = -1; | ||||
| 						break; | ||||
| 				} | ||||
| 			} | ||||
| 			else { | ||||
| 				/* This is a USB device. Find its parent USB Device node. */ | ||||
| 				parent = udev_device_get_parent_with_subsystem_devtype( | ||||
| 					   udev_dev, | ||||
| 					   "usb", | ||||
| 					   "usb_device"); | ||||
| 				if (parent) { | ||||
| 					const char *str; | ||||
| 					const char *key_str = NULL; | ||||
| 
 | ||||
| 					if (key >= 0 && key < DEVICE_STRING_COUNT) { | ||||
| 						key_str = device_string_names[key]; | ||||
| 					} else { | ||||
| 						ret = -1; | ||||
| 						goto end; | ||||
| 					} | ||||
| 
 | ||||
| 					str = udev_device_get_sysattr_value(parent, key_str); | ||||
| 					if (str) { | ||||
| 						/* Convert the string from UTF-8 to wchar_t */ | ||||
| 						retm = mbstowcs(string, str, maxlen); | ||||
| 						ret = (retm == (size_t)-1)? -1: 0; | ||||
| 						goto end; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| end: | ||||
|         free(serial_number_utf8); | ||||
|         free(product_name_utf8); | ||||
| 
 | ||||
| 	udev_device_unref(udev_dev); | ||||
| 	/* parent and hid_dev don't need to be (and can't be) unref'd.
 | ||||
| 	   I'm not sure why, but they'll throw double-free() errors. */ | ||||
| 	udev_unref(udev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT hid_init(void) | ||||
| { | ||||
| 	const char *locale; | ||||
| 
 | ||||
| 	/* Set the locale if it's not set. */ | ||||
| 	locale = setlocale(LC_CTYPE, NULL); | ||||
| 	if (!locale) | ||||
| 		setlocale(LC_CTYPE, ""); | ||||
| 
 | ||||
| 	kernel_version = detect_kernel_version(); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT hid_exit(void) | ||||
| { | ||||
| 	/* Nothing to do for this in the Linux/hidraw implementation. */ | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| struct hid_device_info  HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) | ||||
| { | ||||
| 	struct udev *udev; | ||||
| 	struct udev_enumerate *enumerate; | ||||
| 	struct udev_list_entry *devices, *dev_list_entry; | ||||
| 
 | ||||
| 	struct hid_device_info *root = NULL; /* return object */ | ||||
| 	struct hid_device_info *cur_dev = NULL; | ||||
| 	struct hid_device_info *prev_dev = NULL; /* previous device */ | ||||
| 
 | ||||
| 	hid_init(); | ||||
| 
 | ||||
| 	/* Create the udev object */ | ||||
| 	udev = udev_new(); | ||||
| 	if (!udev) { | ||||
| 		printf("Can't create udev\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Create a list of the devices in the 'hidraw' subsystem. */ | ||||
| 	enumerate = udev_enumerate_new(udev); | ||||
| 	udev_enumerate_add_match_subsystem(enumerate, "hidraw"); | ||||
| 	udev_enumerate_scan_devices(enumerate); | ||||
| 	devices = udev_enumerate_get_list_entry(enumerate); | ||||
| 	/* For each item, see if it matches the vid/pid, and if so
 | ||||
| 	   create a udev_device record for it */ | ||||
| 	udev_list_entry_foreach(dev_list_entry, devices) { | ||||
| 		const char *sysfs_path; | ||||
| 		const char *dev_path; | ||||
| 		const char *str; | ||||
| 		struct udev_device *raw_dev; /* The device's hidraw udev node. */ | ||||
| 		struct udev_device *hid_dev; /* The device's HID udev node. */ | ||||
| 		struct udev_device *usb_dev; /* The device's USB udev node. */ | ||||
| 		struct udev_device *intf_dev; /* The device's interface (in the USB sense). */ | ||||
| 		unsigned short dev_vid; | ||||
| 		unsigned short dev_pid; | ||||
| 		char *serial_number_utf8 = NULL; | ||||
| 		char *product_name_utf8 = NULL; | ||||
| 		int bus_type; | ||||
| 		int result; | ||||
| 
 | ||||
| 		/* Get the filename of the /sys entry for the device
 | ||||
| 		   and create a udev_device object (dev) representing it */ | ||||
| 		sysfs_path = udev_list_entry_get_name(dev_list_entry); | ||||
| 		raw_dev = udev_device_new_from_syspath(udev, sysfs_path); | ||||
| 		dev_path = udev_device_get_devnode(raw_dev); | ||||
| 
 | ||||
| 		hid_dev = udev_device_get_parent_with_subsystem_devtype( | ||||
| 			raw_dev, | ||||
| 			"hid", | ||||
| 			NULL); | ||||
| 
 | ||||
| 		if (!hid_dev) { | ||||
| 			/* Unable to find parent hid device. */ | ||||
| 			goto next; | ||||
| 		} | ||||
| 
 | ||||
| 		result = parse_uevent_info( | ||||
| 			udev_device_get_sysattr_value(hid_dev, "uevent"), | ||||
| 			&bus_type, | ||||
| 			&dev_vid, | ||||
| 			&dev_pid, | ||||
| 			&serial_number_utf8, | ||||
| 			&product_name_utf8); | ||||
| 
 | ||||
| 		if (!result) { | ||||
| 			/* parse_uevent_info() failed for at least one field. */ | ||||
| 			goto next; | ||||
| 		} | ||||
| 
 | ||||
| 		if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) { | ||||
| 			/* We only know how to handle USB and BT devices. */ | ||||
| 			goto next; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Check the VID/PID against the arguments */ | ||||
| 		if ((vendor_id == 0x0 || vendor_id == dev_vid) && | ||||
| 		    (product_id == 0x0 || product_id == dev_pid)) { | ||||
| 			struct hid_device_info *tmp; | ||||
| 
 | ||||
| 			/* VID/PID match. Create the record. */ | ||||
| 			tmp = malloc(sizeof(struct hid_device_info)); | ||||
| 			if (cur_dev) { | ||||
| 				cur_dev->next = tmp; | ||||
| 			} | ||||
| 			else { | ||||
| 				root = tmp; | ||||
| 			} | ||||
| 			prev_dev = cur_dev; | ||||
| 			cur_dev = tmp; | ||||
| 
 | ||||
| 			/* Fill out the record */ | ||||
| 			cur_dev->next = NULL; | ||||
| 			cur_dev->path = dev_path? strdup(dev_path): NULL; | ||||
| 
 | ||||
| 			/* VID/PID */ | ||||
| 			cur_dev->vendor_id = dev_vid; | ||||
| 			cur_dev->product_id = dev_pid; | ||||
| 
 | ||||
| 			/* Serial Number */ | ||||
| 			cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); | ||||
| 
 | ||||
| 			/* Release Number */ | ||||
| 			cur_dev->release_number = 0x0; | ||||
| 
 | ||||
| 			/* Interface Number */ | ||||
| 			cur_dev->interface_number = -1; | ||||
| 
 | ||||
| 			switch (bus_type) { | ||||
| 				case BUS_USB: | ||||
| 					/* The device pointed to by raw_dev contains information about
 | ||||
| 					   the hidraw device. In order to get information about the | ||||
| 					   USB device, get the parent device with the | ||||
| 					   subsystem/devtype pair of "usb"/"usb_device". This will | ||||
| 					   be several levels up the tree, but the function will find | ||||
| 					   it. */ | ||||
| 					usb_dev = udev_device_get_parent_with_subsystem_devtype( | ||||
| 							raw_dev, | ||||
| 							"usb", | ||||
| 							"usb_device"); | ||||
| 
 | ||||
| 					if (!usb_dev) { | ||||
| 						/* Free this device */ | ||||
| 						free(cur_dev->serial_number); | ||||
| 						free(cur_dev->path); | ||||
| 						free(cur_dev); | ||||
| 
 | ||||
| 						/* Take it off the device list. */ | ||||
| 						if (prev_dev) { | ||||
| 							prev_dev->next = NULL; | ||||
| 							cur_dev = prev_dev; | ||||
| 						} | ||||
| 						else { | ||||
| 							cur_dev = root = NULL; | ||||
| 						} | ||||
| 
 | ||||
| 						goto next; | ||||
| 					} | ||||
| 
 | ||||
| 					/* Manufacturer and Product strings */ | ||||
| 					cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]); | ||||
| 					cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]); | ||||
| 
 | ||||
| 					/* Release Number */ | ||||
| 					str = udev_device_get_sysattr_value(usb_dev, "bcdDevice"); | ||||
| 					cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; | ||||
| 
 | ||||
| 					/* Get a handle to the interface's udev node. */ | ||||
| 					intf_dev = udev_device_get_parent_with_subsystem_devtype( | ||||
| 							raw_dev, | ||||
| 							"usb", | ||||
| 							"usb_interface"); | ||||
| 					if (intf_dev) { | ||||
| 						str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); | ||||
| 						cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; | ||||
| 					} | ||||
| 
 | ||||
| 					break; | ||||
| 
 | ||||
| 				case BUS_BLUETOOTH: | ||||
| 					/* Manufacturer and Product strings */ | ||||
| 					cur_dev->manufacturer_string = wcsdup(L""); | ||||
| 					cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); | ||||
| 
 | ||||
| 					break; | ||||
| 
 | ||||
| 				default: | ||||
| 					/* Unknown device type - this should never happen, as we
 | ||||
| 					 * check for USB and Bluetooth devices above */ | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	next: | ||||
| 		free(serial_number_utf8); | ||||
| 		free(product_name_utf8); | ||||
| 		udev_device_unref(raw_dev); | ||||
| 		/* hid_dev, usb_dev and intf_dev don't need to be (and can't be)
 | ||||
| 		   unref()d.  It will cause a double-free() error.  I'm not | ||||
| 		   sure why.  */ | ||||
| 	} | ||||
| 	/* Free the enumerator and udev objects. */ | ||||
| 	udev_enumerate_unref(enumerate); | ||||
| 	udev_unref(udev); | ||||
| 
 | ||||
| 	return root; | ||||
| } | ||||
| 
 | ||||
| void  HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) | ||||
| { | ||||
| 	struct hid_device_info *d = devs; | ||||
| 	while (d) { | ||||
| 		struct hid_device_info *next = d->next; | ||||
| 		free(d->path); | ||||
| 		free(d->serial_number); | ||||
| 		free(d->manufacturer_string); | ||||
| 		free(d->product_string); | ||||
| 		free(d); | ||||
| 		d = next; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) | ||||
| { | ||||
| 	struct hid_device_info *devs, *cur_dev; | ||||
| 	const char *path_to_open = NULL; | ||||
| 	hid_device *handle = NULL; | ||||
| 
 | ||||
| 	devs = hid_enumerate(vendor_id, product_id); | ||||
| 	cur_dev = devs; | ||||
| 	while (cur_dev) { | ||||
| 		if (cur_dev->vendor_id == vendor_id && | ||||
| 		    cur_dev->product_id == product_id) { | ||||
| 			if (serial_number) { | ||||
| 				if (wcscmp(serial_number, cur_dev->serial_number) == 0) { | ||||
| 					path_to_open = cur_dev->path; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			else { | ||||
| 				path_to_open = cur_dev->path; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		cur_dev = cur_dev->next; | ||||
| 	} | ||||
| 
 | ||||
| 	if (path_to_open) { | ||||
| 		/* Open the device */ | ||||
| 		handle = hid_open_path(path_to_open); | ||||
| 	} | ||||
| 
 | ||||
| 	hid_free_enumeration(devs); | ||||
| 
 | ||||
| 	return handle; | ||||
| } | ||||
| 
 | ||||
| hid_device * HID_API_EXPORT hid_open_path(const char *path) | ||||
| { | ||||
| 	hid_device *dev = NULL; | ||||
| 
 | ||||
| 	hid_init(); | ||||
| 
 | ||||
| 	dev = new_hid_device(); | ||||
| 
 | ||||
| 	/* OPEN HERE */ | ||||
| 	dev->device_handle = open(path, O_RDWR); | ||||
| 
 | ||||
| 	/* If we have a good handle, return it. */ | ||||
| 	if (dev->device_handle > 0) { | ||||
| 
 | ||||
| 		/* Get the report descriptor */ | ||||
| 		int res, desc_size = 0; | ||||
| 		struct hidraw_report_descriptor rpt_desc; | ||||
| 
 | ||||
| 		memset(&rpt_desc, 0x0, sizeof(rpt_desc)); | ||||
| 
 | ||||
| 		/* Get Report Descriptor Size */ | ||||
| 		res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); | ||||
| 		if (res < 0) | ||||
| 			perror("HIDIOCGRDESCSIZE"); | ||||
| 
 | ||||
| 
 | ||||
| 		/* Get Report Descriptor */ | ||||
| 		rpt_desc.size = desc_size; | ||||
| 		res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc); | ||||
| 		if (res < 0) { | ||||
| 			perror("HIDIOCGRDESC"); | ||||
| 		} else { | ||||
| 			/* Determine if this device uses numbered reports. */ | ||||
| 			dev->uses_numbered_reports = | ||||
| 				uses_numbered_reports(rpt_desc.value, | ||||
| 				                      rpt_desc.size); | ||||
| 		} | ||||
| 
 | ||||
| 		return dev; | ||||
| 	} | ||||
| 	else { | ||||
| 		/* Unable to open any devices. */ | ||||
| 		free(dev); | ||||
| 		return NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) | ||||
| { | ||||
| 	int bytes_written; | ||||
| 
 | ||||
| 	bytes_written = write(dev->device_handle, data, length); | ||||
| 
 | ||||
| 	return bytes_written; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) | ||||
| { | ||||
| 	int bytes_read; | ||||
| 
 | ||||
| 	if (milliseconds >= 0) { | ||||
| 		/* Milliseconds is either 0 (non-blocking) or > 0 (contains
 | ||||
| 		   a valid timeout). In both cases we want to call poll() | ||||
| 		   and wait for data to arrive.  Don't rely on non-blocking | ||||
| 		   operation (O_NONBLOCK) since some kernels don't seem to | ||||
| 		   properly report device disconnection through read() when | ||||
| 		   in non-blocking mode.  */ | ||||
| 		int ret; | ||||
| 		struct pollfd fds; | ||||
| 
 | ||||
| 		fds.fd = dev->device_handle; | ||||
| 		fds.events = POLLIN; | ||||
| 		fds.revents = 0; | ||||
| 		ret = poll(&fds, 1, milliseconds); | ||||
| 		if (ret == -1 || ret == 0) { | ||||
| 			/* Error or timeout */ | ||||
| 			return ret; | ||||
| 		} | ||||
| 		else { | ||||
| 			/* Check for errors on the file descriptor. This will
 | ||||
| 			   indicate a device disconnection. */ | ||||
| 			if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) | ||||
| 				return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	bytes_read = read(dev->device_handle, data, length); | ||||
| 	if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS)) | ||||
| 		bytes_read = 0; | ||||
| 
 | ||||
| 	if (bytes_read >= 0 && | ||||
| 	    kernel_version != 0 && | ||||
| 	    kernel_version < KERNEL_VERSION(2,6,34) && | ||||
| 	    dev->uses_numbered_reports) { | ||||
| 		/* Work around a kernel bug. Chop off the first byte. */ | ||||
| 		memmove(data, data+1, bytes_read); | ||||
| 		bytes_read--; | ||||
| 	} | ||||
| 
 | ||||
| 	return bytes_read; | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) | ||||
| { | ||||
| 	return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) | ||||
| { | ||||
| 	/* Do all non-blocking in userspace using poll(), since it looks
 | ||||
| 	   like there's a bug in the kernel in some versions where | ||||
| 	   read() will not return -1 on disconnection of the USB device */ | ||||
| 
 | ||||
| 	dev->blocking = !nonblock; | ||||
| 	return 0; /* Success */ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) | ||||
| { | ||||
| 	int res; | ||||
| 
 | ||||
| 	res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); | ||||
| 	if (res < 0) | ||||
| 		perror("ioctl (SFEATURE)"); | ||||
| 
 | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) | ||||
| { | ||||
| 	int res; | ||||
| 
 | ||||
| 	res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); | ||||
| 	if (res < 0) | ||||
| 		perror("ioctl (GFEATURE)"); | ||||
| 
 | ||||
| 
 | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void HID_API_EXPORT hid_close(hid_device *dev) | ||||
| { | ||||
| 	if (!dev) | ||||
| 		return; | ||||
| 	close(dev->device_handle); | ||||
| 	free(dev); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) | ||||
| { | ||||
| 	return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen); | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) | ||||
| { | ||||
| 	return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen); | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) | ||||
| { | ||||
| 	return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen); | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) | ||||
| { | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| HID_API_EXPORT const wchar_t * HID_API_CALL  hid_error(hid_device *dev) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
							
								
								
									
										1121
									
								
								src/hidapi/mac/hid.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1121
									
								
								src/hidapi/mac/hid.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										956
									
								
								src/hidapi/win/hid.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										956
									
								
								src/hidapi/win/hid.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,956 @@ | |||
| /*******************************************************
 | ||||
|  HIDAPI - Multi-Platform library for | ||||
|  communication with HID devices. | ||||
| 
 | ||||
|  Alan Ott | ||||
|  Signal 11 Software | ||||
| 
 | ||||
|  8/22/2009 | ||||
| 
 | ||||
|  Copyright 2009, All Rights Reserved. | ||||
|   | ||||
|  At the discretion of the user of this library, | ||||
|  this software may be licensed under the terms of the | ||||
|  GNU General Public License v3, a BSD-Style license, or the | ||||
|  original HIDAPI license as outlined in the LICENSE.txt, | ||||
|  LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt | ||||
|  files located at the root of the source distribution. | ||||
|  These files may also be found in the public source | ||||
|  code repository located at: | ||||
|         http://github.com/signal11/hidapi .
 | ||||
| ********************************************************/ | ||||
| 
 | ||||
| #include <windows.h> | ||||
| 
 | ||||
| #ifndef _NTDEF_ | ||||
| typedef LONG NTSTATUS; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef __MINGW32__ | ||||
| #include <ntdef.h> | ||||
| #include <winbase.h> | ||||
| #endif | ||||
| 
 | ||||
| #ifdef __CYGWIN__ | ||||
| #include <ntdef.h> | ||||
| #define _wcsdup wcsdup | ||||
| #endif | ||||
| 
 | ||||
| /* The maximum number of characters that can be passed into the
 | ||||
|    HidD_Get*String() functions without it failing.*/ | ||||
| #define MAX_STRING_WCHARS 0xFFF | ||||
| 
 | ||||
| /*#define HIDAPI_USE_DDK*/ | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 	#include <setupapi.h> | ||||
| 	#include <winioctl.h> | ||||
| 	#ifdef HIDAPI_USE_DDK | ||||
| 		#include <hidsdi.h> | ||||
| 	#endif | ||||
| 
 | ||||
| 	/* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */ | ||||
| 	#define HID_OUT_CTL_CODE(id)  \ | ||||
| 		CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) | ||||
| 	#define IOCTL_HID_GET_FEATURE                   HID_OUT_CTL_CODE(100) | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| #endif | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| 
 | ||||
| #include "hidapi.h" | ||||
| 
 | ||||
| #undef MIN | ||||
| #define MIN(x,y) ((x) < (y)? (x): (y)) | ||||
| 
 | ||||
| #ifdef _MSC_VER | ||||
| 	/* Thanks Microsoft, but I know how to use strncpy(). */ | ||||
| 	#pragma warning(disable:4996) | ||||
| #endif | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| #ifndef HIDAPI_USE_DDK | ||||
| 	/* Since we're not building with the DDK, and the HID header
 | ||||
| 	   files aren't part of the SDK, we have to define all this | ||||
| 	   stuff here. In lookup_functions(), the function pointers | ||||
| 	   defined below are set. */ | ||||
| 	typedef struct _HIDD_ATTRIBUTES{ | ||||
| 		ULONG Size; | ||||
| 		USHORT VendorID; | ||||
| 		USHORT ProductID; | ||||
| 		USHORT VersionNumber; | ||||
| 	} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; | ||||
| 
 | ||||
| 	typedef USHORT USAGE; | ||||
| 	typedef struct _HIDP_CAPS { | ||||
| 		USAGE Usage; | ||||
| 		USAGE UsagePage; | ||||
| 		USHORT InputReportByteLength; | ||||
| 		USHORT OutputReportByteLength; | ||||
| 		USHORT FeatureReportByteLength; | ||||
| 		USHORT Reserved[17]; | ||||
| 		USHORT fields_not_used_by_hidapi[10]; | ||||
| 	} HIDP_CAPS, *PHIDP_CAPS; | ||||
| 	typedef void* PHIDP_PREPARSED_DATA; | ||||
| 	#define HIDP_STATUS_SUCCESS 0x110000 | ||||
| 
 | ||||
| 	typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); | ||||
| 	typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); | ||||
| 	typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); | ||||
| 	typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); | ||||
| 	typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); | ||||
| 	typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); | ||||
| 	typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); | ||||
| 	typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); | ||||
| 	typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); | ||||
| 	typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); | ||||
| 	typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers); | ||||
| 
 | ||||
| 	static HidD_GetAttributes_ HidD_GetAttributes; | ||||
| 	static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; | ||||
| 	static HidD_GetManufacturerString_ HidD_GetManufacturerString; | ||||
| 	static HidD_GetProductString_ HidD_GetProductString; | ||||
| 	static HidD_SetFeature_ HidD_SetFeature; | ||||
| 	static HidD_GetFeature_ HidD_GetFeature; | ||||
| 	static HidD_GetIndexedString_ HidD_GetIndexedString; | ||||
| 	static HidD_GetPreparsedData_ HidD_GetPreparsedData; | ||||
| 	static HidD_FreePreparsedData_ HidD_FreePreparsedData; | ||||
| 	static HidP_GetCaps_ HidP_GetCaps; | ||||
| 	static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers; | ||||
| 
 | ||||
| 	static HMODULE lib_handle = NULL; | ||||
| 	static BOOLEAN initialized = FALSE; | ||||
| #endif /* HIDAPI_USE_DDK */ | ||||
| 
 | ||||
| struct hid_device_ { | ||||
| 		HANDLE device_handle; | ||||
| 		BOOL blocking; | ||||
| 		USHORT output_report_length; | ||||
| 		size_t input_report_length; | ||||
| 		void *last_error_str; | ||||
| 		DWORD last_error_num; | ||||
| 		BOOL read_pending; | ||||
| 		char *read_buf; | ||||
| 		OVERLAPPED ol; | ||||
| }; | ||||
| 
 | ||||
| static hid_device *new_hid_device() | ||||
| { | ||||
| 	hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); | ||||
| 	dev->device_handle = INVALID_HANDLE_VALUE; | ||||
| 	dev->blocking = TRUE; | ||||
| 	dev->output_report_length = 0; | ||||
| 	dev->input_report_length = 0; | ||||
| 	dev->last_error_str = NULL; | ||||
| 	dev->last_error_num = 0; | ||||
| 	dev->read_pending = FALSE; | ||||
| 	dev->read_buf = NULL; | ||||
| 	memset(&dev->ol, 0, sizeof(dev->ol)); | ||||
| 	dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); | ||||
| 
 | ||||
| 	return dev; | ||||
| } | ||||
| 
 | ||||
| static void free_hid_device(hid_device *dev) | ||||
| { | ||||
| 	CloseHandle(dev->ol.hEvent); | ||||
| 	CloseHandle(dev->device_handle); | ||||
| 	LocalFree(dev->last_error_str); | ||||
| 	free(dev->read_buf); | ||||
| 	free(dev); | ||||
| } | ||||
| 
 | ||||
| static void register_error(hid_device *dev, const char *op) | ||||
| { | ||||
| 	WCHAR *ptr, *msg; | ||||
| 
 | ||||
| 	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | | ||||
| 		FORMAT_MESSAGE_FROM_SYSTEM | | ||||
| 		FORMAT_MESSAGE_IGNORE_INSERTS, | ||||
| 		NULL, | ||||
| 		GetLastError(), | ||||
| 		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | ||||
| 		(LPVOID)&msg, 0/*sz*/, | ||||
| 		NULL); | ||||
| 	 | ||||
| 	/* Get rid of the CR and LF that FormatMessage() sticks at the
 | ||||
| 	   end of the message. Thanks Microsoft! */ | ||||
| 	ptr = msg; | ||||
| 	while (*ptr) { | ||||
| 		if (*ptr == '\r') { | ||||
| 			*ptr = 0x0000; | ||||
| 			break; | ||||
| 		} | ||||
| 		ptr++; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Store the message off in the Device entry so that
 | ||||
| 	   the hid_error() function can pick it up. */ | ||||
| 	LocalFree(dev->last_error_str); | ||||
| 	dev->last_error_str = msg; | ||||
| } | ||||
| 
 | ||||
| #ifndef HIDAPI_USE_DDK | ||||
| static int lookup_functions() | ||||
| { | ||||
| 	lib_handle = LoadLibraryA("hid.dll"); | ||||
| 	if (lib_handle) { | ||||
| #define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; | ||||
| 		RESOLVE(HidD_GetAttributes); | ||||
| 		RESOLVE(HidD_GetSerialNumberString); | ||||
| 		RESOLVE(HidD_GetManufacturerString); | ||||
| 		RESOLVE(HidD_GetProductString); | ||||
| 		RESOLVE(HidD_SetFeature); | ||||
| 		RESOLVE(HidD_GetFeature); | ||||
| 		RESOLVE(HidD_GetIndexedString); | ||||
| 		RESOLVE(HidD_GetPreparsedData); | ||||
| 		RESOLVE(HidD_FreePreparsedData); | ||||
| 		RESOLVE(HidP_GetCaps); | ||||
| 		RESOLVE(HidD_SetNumInputBuffers); | ||||
| #undef RESOLVE | ||||
| 	} | ||||
| 	else | ||||
| 		return -1; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static HANDLE open_device(const char *path, BOOL open_rw) | ||||
| { | ||||
| 	HANDLE handle; | ||||
| 	DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0; | ||||
| 	DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; | ||||
| 
 | ||||
| 	handle = CreateFileA(path, | ||||
| 		desired_access, | ||||
| 		share_mode, | ||||
| 		NULL, | ||||
| 		OPEN_EXISTING, | ||||
| 		FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ | ||||
| 		0); | ||||
| 
 | ||||
| 	return handle; | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT hid_init(void) | ||||
| { | ||||
| #ifndef HIDAPI_USE_DDK | ||||
| 	if (!initialized) { | ||||
| 		if (lookup_functions() < 0) { | ||||
| 			hid_exit(); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		initialized = TRUE; | ||||
| 	} | ||||
| #endif | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT hid_exit(void) | ||||
| { | ||||
| #ifndef HIDAPI_USE_DDK | ||||
| 	if (lib_handle) | ||||
| 		FreeLibrary(lib_handle); | ||||
| 	lib_handle = NULL; | ||||
| 	initialized = FALSE; | ||||
| #endif | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) | ||||
| { | ||||
| 	BOOL res; | ||||
| 	struct hid_device_info *root = NULL; /* return object */ | ||||
| 	struct hid_device_info *cur_dev = NULL; | ||||
| 
 | ||||
| 	/* Windows objects for interacting with the driver. */ | ||||
| 	GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; | ||||
| 	SP_DEVINFO_DATA devinfo_data; | ||||
| 	SP_DEVICE_INTERFACE_DATA device_interface_data; | ||||
| 	SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; | ||||
| 	HDEVINFO device_info_set = INVALID_HANDLE_VALUE; | ||||
| 	int device_index = 0; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (hid_init() < 0) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	/* Initialize the Windows objects. */ | ||||
| 	memset(&devinfo_data, 0x0, sizeof(devinfo_data)); | ||||
| 	devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); | ||||
| 	device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | ||||
| 
 | ||||
| 	/* Get information for all the devices belonging to the HID class. */ | ||||
| 	device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); | ||||
| 	 | ||||
| 	/* Iterate over each device in the HID class, looking for the right one. */ | ||||
| 	 | ||||
| 	for (;;) { | ||||
| 		HANDLE write_handle = INVALID_HANDLE_VALUE; | ||||
| 		DWORD required_size = 0; | ||||
| 		HIDD_ATTRIBUTES attrib; | ||||
| 
 | ||||
| 		res = SetupDiEnumDeviceInterfaces(device_info_set, | ||||
| 			NULL, | ||||
| 			&InterfaceClassGuid, | ||||
| 			device_index, | ||||
| 			&device_interface_data); | ||||
| 		 | ||||
| 		if (!res) { | ||||
| 			/* A return of FALSE from this function means that
 | ||||
| 			   there are no more devices. */ | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Call with 0-sized detail size, and let the function
 | ||||
| 		   tell us how long the detail struct needs to be. The | ||||
| 		   size is put in &required_size. */ | ||||
| 		res = SetupDiGetDeviceInterfaceDetailA(device_info_set, | ||||
| 			&device_interface_data, | ||||
| 			NULL, | ||||
| 			0, | ||||
| 			&required_size, | ||||
| 			NULL); | ||||
| 
 | ||||
| 		/* Allocate a long enough structure for device_interface_detail_data. */ | ||||
| 		device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); | ||||
| 		device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); | ||||
| 
 | ||||
| 		/* Get the detailed data for this device. The detail data gives us
 | ||||
| 		   the device path for this device, which is then passed into | ||||
| 		   CreateFile() to get a handle to the device. */ | ||||
| 		res = SetupDiGetDeviceInterfaceDetailA(device_info_set, | ||||
| 			&device_interface_data, | ||||
| 			device_interface_detail_data, | ||||
| 			required_size, | ||||
| 			NULL, | ||||
| 			NULL); | ||||
| 
 | ||||
| 		if (!res) { | ||||
| 			/* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail");
 | ||||
| 			   Continue to the next device. */ | ||||
| 			goto cont; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Make sure this device is of Setup Class "HIDClass" and has a
 | ||||
| 		   driver bound to it. */ | ||||
| 		for (i = 0; ; i++) { | ||||
| 			char driver_name[256]; | ||||
| 
 | ||||
| 			/* Populate devinfo_data. This function will return failure
 | ||||
| 			   when there are no more interfaces left. */ | ||||
| 			res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); | ||||
| 			if (!res) | ||||
| 				goto cont; | ||||
| 
 | ||||
| 			res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, | ||||
| 			               SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); | ||||
| 			if (!res) | ||||
| 				goto cont; | ||||
| 
 | ||||
| 			if ((strcmp(driver_name, "HIDClass") == 0) || | ||||
| 				(strcmp(driver_name, "Mouse") == 0) || | ||||
| 				(strcmp(driver_name, "Keyboard") == 0)) { | ||||
| 				/* See if there's a driver bound. */ | ||||
| 				res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, | ||||
| 				           SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); | ||||
| 				if (res) | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		//wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);
 | ||||
| 
 | ||||
| 		/* Open a handle to the device */ | ||||
| 		write_handle = open_device(device_interface_detail_data->DevicePath, FALSE); | ||||
| 
 | ||||
| 		/* Check validity of write_handle. */ | ||||
| 		if (write_handle == INVALID_HANDLE_VALUE) { | ||||
| 			/* Unable to open the device. */ | ||||
| 			//register_error(dev, "CreateFile");
 | ||||
| 			goto cont_close; | ||||
| 		}		 | ||||
| 
 | ||||
| 
 | ||||
| 		/* Get the Vendor ID and Product ID for this device. */ | ||||
| 		attrib.Size = sizeof(HIDD_ATTRIBUTES); | ||||
| 		HidD_GetAttributes(write_handle, &attrib); | ||||
| 		//wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);
 | ||||
| 
 | ||||
| 		/* Check the VID/PID to see if we should add this
 | ||||
| 		   device to the enumeration list. */ | ||||
| 		if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && | ||||
| 		    (product_id == 0x0 || attrib.ProductID == product_id)) { | ||||
| 
 | ||||
| 			#define WSTR_LEN 512 | ||||
| 			const char *str; | ||||
| 			struct hid_device_info *tmp; | ||||
| 			PHIDP_PREPARSED_DATA pp_data = NULL; | ||||
| 			HIDP_CAPS caps; | ||||
| 			BOOLEAN res; | ||||
| 			NTSTATUS nt_res; | ||||
| 			wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ | ||||
| 			size_t len; | ||||
| 
 | ||||
| 			/* VID/PID match. Create the record. */ | ||||
| 			tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); | ||||
| 			if (cur_dev) { | ||||
| 				cur_dev->next = tmp; | ||||
| 			} | ||||
| 			else { | ||||
| 				root = tmp; | ||||
| 			} | ||||
| 			cur_dev = tmp; | ||||
| 
 | ||||
| 			/* Get the Usage Page and Usage for this device. */ | ||||
| 			res = HidD_GetPreparsedData(write_handle, &pp_data); | ||||
| 			if (res) { | ||||
| 				nt_res = HidP_GetCaps(pp_data, &caps); | ||||
| 				if (nt_res == HIDP_STATUS_SUCCESS) { | ||||
| 					cur_dev->usage_page = caps.UsagePage; | ||||
| 					cur_dev->usage = caps.Usage; | ||||
| 				} | ||||
| 
 | ||||
| 				HidD_FreePreparsedData(pp_data); | ||||
| 			} | ||||
| 			 | ||||
| 			/* Fill out the record */ | ||||
| 			cur_dev->next = NULL; | ||||
| 			str = device_interface_detail_data->DevicePath; | ||||
| 			if (str) { | ||||
| 				len = strlen(str); | ||||
| 				cur_dev->path = (char*) calloc(len+1, sizeof(char)); | ||||
| 				strncpy(cur_dev->path, str, len+1); | ||||
| 				cur_dev->path[len] = '\0'; | ||||
| 			} | ||||
| 			else | ||||
| 				cur_dev->path = NULL; | ||||
| 
 | ||||
| 			/* Serial Number */ | ||||
| 			res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); | ||||
| 			wstr[WSTR_LEN-1] = 0x0000; | ||||
| 			if (res) { | ||||
| 				cur_dev->serial_number = _wcsdup(wstr); | ||||
| 			} | ||||
| 
 | ||||
| 			/* Manufacturer String */ | ||||
| 			res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); | ||||
| 			wstr[WSTR_LEN-1] = 0x0000; | ||||
| 			if (res) { | ||||
| 				cur_dev->manufacturer_string = _wcsdup(wstr); | ||||
| 			} | ||||
| 
 | ||||
| 			/* Product String */ | ||||
| 			res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); | ||||
| 			wstr[WSTR_LEN-1] = 0x0000; | ||||
| 			if (res) { | ||||
| 				cur_dev->product_string = _wcsdup(wstr); | ||||
| 			} | ||||
| 
 | ||||
| 			/* VID/PID */ | ||||
| 			cur_dev->vendor_id = attrib.VendorID; | ||||
| 			cur_dev->product_id = attrib.ProductID; | ||||
| 
 | ||||
| 			/* Release Number */ | ||||
| 			cur_dev->release_number = attrib.VersionNumber; | ||||
| 
 | ||||
| 			/* Interface Number. It can sometimes be parsed out of the path
 | ||||
| 			   on Windows if a device has multiple interfaces. See | ||||
| 			   http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or
 | ||||
| 			   search for "Hardware IDs for HID Devices" at MSDN. If it's not | ||||
| 			   in the path, it's set to -1. */ | ||||
| 			cur_dev->interface_number = -1; | ||||
| 			if (cur_dev->path) { | ||||
| 				char *interface_component = strstr(cur_dev->path, "&mi_"); | ||||
| 				if (interface_component) { | ||||
| 					char *hex_str = interface_component + 4; | ||||
| 					char *endptr = NULL; | ||||
| 					cur_dev->interface_number = strtol(hex_str, &endptr, 16); | ||||
| 					if (endptr == hex_str) { | ||||
| 						/* The parsing failed. Set interface_number to -1. */ | ||||
| 						cur_dev->interface_number = -1; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| cont_close: | ||||
| 		CloseHandle(write_handle); | ||||
| cont: | ||||
| 		/* We no longer need the detail data. It can be freed */ | ||||
| 		free(device_interface_detail_data); | ||||
| 
 | ||||
| 		device_index++; | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	/* Close the device information handle. */ | ||||
| 	SetupDiDestroyDeviceInfoList(device_info_set); | ||||
| 
 | ||||
| 	return root; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) | ||||
| { | ||||
| 	/* TODO: Merge this with the Linux version. This function is platform-independent. */ | ||||
| 	struct hid_device_info *d = devs; | ||||
| 	while (d) { | ||||
| 		struct hid_device_info *next = d->next; | ||||
| 		free(d->path); | ||||
| 		free(d->serial_number); | ||||
| 		free(d->manufacturer_string); | ||||
| 		free(d->product_string); | ||||
| 		free(d); | ||||
| 		d = next; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) | ||||
| { | ||||
| 	/* TODO: Merge this functions with the Linux version. This function should be platform independent. */ | ||||
| 	struct hid_device_info *devs, *cur_dev; | ||||
| 	const char *path_to_open = NULL; | ||||
| 	hid_device *handle = NULL; | ||||
| 	 | ||||
| 	devs = hid_enumerate(vendor_id, product_id); | ||||
| 	cur_dev = devs; | ||||
| 	while (cur_dev) { | ||||
| 		if (cur_dev->vendor_id == vendor_id && | ||||
| 		    cur_dev->product_id == product_id) { | ||||
| 			if (serial_number) { | ||||
| 				if (wcscmp(serial_number, cur_dev->serial_number) == 0) { | ||||
| 					path_to_open = cur_dev->path; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			else { | ||||
| 				path_to_open = cur_dev->path; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		cur_dev = cur_dev->next; | ||||
| 	} | ||||
| 
 | ||||
| 	if (path_to_open) { | ||||
| 		/* Open the device */ | ||||
| 		handle = hid_open_path(path_to_open); | ||||
| 	} | ||||
| 
 | ||||
| 	hid_free_enumeration(devs); | ||||
| 	 | ||||
| 	return handle; | ||||
| } | ||||
| 
 | ||||
| HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) | ||||
| { | ||||
| 	hid_device *dev; | ||||
| 	HIDP_CAPS caps; | ||||
| 	PHIDP_PREPARSED_DATA pp_data = NULL; | ||||
| 	BOOLEAN res; | ||||
| 	NTSTATUS nt_res; | ||||
| 
 | ||||
| 	if (hid_init() < 0) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	dev = new_hid_device(); | ||||
| 
 | ||||
| 	/* Open a handle to the device */ | ||||
| 	dev->device_handle = open_device(path, TRUE); | ||||
| 
 | ||||
| 	/* Check validity of write_handle. */ | ||||
| 	if (dev->device_handle == INVALID_HANDLE_VALUE) { | ||||
| 		/* System devices, such as keyboards and mice, cannot be opened in
 | ||||
| 		   read-write mode, because the system takes exclusive control over | ||||
| 		   them.  This is to prevent keyloggers.  However, feature reports | ||||
| 		   can still be sent and received.  Retry opening the device, but | ||||
| 		   without read/write access. */ | ||||
| 		dev->device_handle = open_device(path, FALSE); | ||||
| 
 | ||||
| 		/* Check the validity of the limited device_handle. */ | ||||
| 		if (dev->device_handle == INVALID_HANDLE_VALUE) { | ||||
| 			/* Unable to open the device, even without read-write mode. */ | ||||
| 			register_error(dev, "CreateFile"); | ||||
| 			goto err; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Set the Input Report buffer size to 64 reports. */ | ||||
| 	res = HidD_SetNumInputBuffers(dev->device_handle, 64); | ||||
| 	if (!res) { | ||||
| 		register_error(dev, "HidD_SetNumInputBuffers"); | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Get the Input Report length for the device. */ | ||||
| 	res = HidD_GetPreparsedData(dev->device_handle, &pp_data); | ||||
| 	if (!res) { | ||||
| 		register_error(dev, "HidD_GetPreparsedData"); | ||||
| 		goto err; | ||||
| 	} | ||||
| 	nt_res = HidP_GetCaps(pp_data, &caps); | ||||
| 	if (nt_res != HIDP_STATUS_SUCCESS) { | ||||
| 		register_error(dev, "HidP_GetCaps");	 | ||||
| 		goto err_pp_data; | ||||
| 	} | ||||
| 	dev->output_report_length = caps.OutputReportByteLength; | ||||
| 	dev->input_report_length = caps.InputReportByteLength; | ||||
| 	HidD_FreePreparsedData(pp_data); | ||||
| 
 | ||||
| 	dev->read_buf = (char*) malloc(dev->input_report_length); | ||||
| 
 | ||||
| 	return dev; | ||||
| 
 | ||||
| err_pp_data: | ||||
| 		HidD_FreePreparsedData(pp_data); | ||||
| err:	 | ||||
| 		free_hid_device(dev); | ||||
| 		return NULL; | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) | ||||
| { | ||||
| 	DWORD bytes_written; | ||||
| 	BOOL res; | ||||
| 
 | ||||
| 	OVERLAPPED ol; | ||||
| 	unsigned char *buf; | ||||
| 	memset(&ol, 0, sizeof(ol)); | ||||
| 
 | ||||
| 	/* Make sure the right number of bytes are passed to WriteFile. Windows
 | ||||
| 	   expects the number of bytes which are in the _longest_ report (plus | ||||
| 	   one for the report number) bytes even if the data is a report | ||||
| 	   which is shorter than that. Windows gives us this value in | ||||
| 	   caps.OutputReportByteLength. If a user passes in fewer bytes than this, | ||||
| 	   create a temporary buffer which is the proper size. */ | ||||
| 	if (length >= dev->output_report_length) { | ||||
| 		/* The user passed the right number of bytes. Use the buffer as-is. */ | ||||
| 		buf = (unsigned char *) data; | ||||
| 	} else { | ||||
| 		/* Create a temporary buffer and copy the user's data
 | ||||
| 		   into it, padding the rest with zeros. */ | ||||
| 		buf = (unsigned char *) malloc(dev->output_report_length); | ||||
| 		memcpy(buf, data, length); | ||||
| 		memset(buf + length, 0, dev->output_report_length - length); | ||||
| 		length = dev->output_report_length; | ||||
| 	} | ||||
| 
 | ||||
| 	res = WriteFile(dev->device_handle, buf, length, NULL, &ol); | ||||
| 	 | ||||
| 	if (!res) { | ||||
| 		if (GetLastError() != ERROR_IO_PENDING) { | ||||
| 			/* WriteFile() failed. Return error. */ | ||||
| 			register_error(dev, "WriteFile"); | ||||
| 			bytes_written = -1; | ||||
| 			goto end_of_function; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Wait here until the write is done. This makes
 | ||||
| 	   hid_write() synchronous. */ | ||||
| 	res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); | ||||
| 	if (!res) { | ||||
| 		/* The Write operation failed. */ | ||||
| 		register_error(dev, "WriteFile"); | ||||
| 		bytes_written = -1; | ||||
| 		goto end_of_function; | ||||
| 	} | ||||
| 
 | ||||
| end_of_function: | ||||
| 	if (buf != data) | ||||
| 		free(buf); | ||||
| 
 | ||||
| 	return bytes_written; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) | ||||
| { | ||||
| 	DWORD bytes_read = 0; | ||||
| 	size_t copy_len = 0; | ||||
| 	BOOL res; | ||||
| 
 | ||||
| 	/* Copy the handle for convenience. */ | ||||
| 	HANDLE ev = dev->ol.hEvent; | ||||
| 
 | ||||
| 	if (!dev->read_pending) { | ||||
| 		/* Start an Overlapped I/O read. */ | ||||
| 		dev->read_pending = TRUE; | ||||
| 		memset(dev->read_buf, 0, dev->input_report_length); | ||||
| 		ResetEvent(ev); | ||||
| 		res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); | ||||
| 		 | ||||
| 		if (!res) { | ||||
| 			if (GetLastError() != ERROR_IO_PENDING) { | ||||
| 				/* ReadFile() has failed.
 | ||||
| 				   Clean up and return error. */ | ||||
| 				CancelIo(dev->device_handle); | ||||
| 				dev->read_pending = FALSE; | ||||
| 				goto end_of_function; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (milliseconds >= 0) { | ||||
| 		/* See if there is any data yet. */ | ||||
| 		res = WaitForSingleObject(ev, milliseconds); | ||||
| 		if (res != WAIT_OBJECT_0) { | ||||
| 			/* There was no data this time. Return zero bytes available,
 | ||||
| 			   but leave the Overlapped I/O running. */ | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Either WaitForSingleObject() told us that ReadFile has completed, or
 | ||||
| 	   we are in non-blocking mode. Get the number of bytes read. The actual | ||||
| 	   data has been copied to the data[] array which was passed to ReadFile(). */ | ||||
| 	res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); | ||||
| 	 | ||||
| 	/* Set pending back to false, even if GetOverlappedResult() returned error. */ | ||||
| 	dev->read_pending = FALSE; | ||||
| 
 | ||||
| 	if (res && bytes_read > 0) { | ||||
| 		if (dev->read_buf[0] == 0x0) { | ||||
| 			/* If report numbers aren't being used, but Windows sticks a report
 | ||||
| 			   number (0x0) on the beginning of the report anyway. To make this | ||||
| 			   work like the other platforms, and to make it work more like the | ||||
| 			   HID spec, we'll skip over this byte. */ | ||||
| 			bytes_read--; | ||||
| 			copy_len = length > bytes_read ? bytes_read : length; | ||||
| 			memcpy(data, dev->read_buf+1, copy_len); | ||||
| 		} | ||||
| 		else { | ||||
| 			/* Copy the whole buffer, report number and all. */ | ||||
| 			copy_len = length > bytes_read ? bytes_read : length; | ||||
| 			memcpy(data, dev->read_buf, copy_len); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| end_of_function: | ||||
| 	if (!res) { | ||||
| 		register_error(dev, "GetOverlappedResult"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	 | ||||
| 	return copy_len; | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) | ||||
| { | ||||
| 	return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) | ||||
| { | ||||
| 	dev->blocking = !nonblock; | ||||
| 	return 0; /* Success */ | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) | ||||
| { | ||||
| 	BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); | ||||
| 	if (!res) { | ||||
| 		register_error(dev, "HidD_SetFeature"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return length; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) | ||||
| { | ||||
| 	BOOL res; | ||||
| #if 0 | ||||
| 	res = HidD_GetFeature(dev->device_handle, data, length); | ||||
| 	if (!res) { | ||||
| 		register_error(dev, "HidD_GetFeature"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ | ||||
| #else | ||||
| 	DWORD bytes_returned; | ||||
| 
 | ||||
| 	OVERLAPPED ol; | ||||
| 	memset(&ol, 0, sizeof(ol)); | ||||
| 
 | ||||
| 	res = DeviceIoControl(dev->device_handle, | ||||
| 		IOCTL_HID_GET_FEATURE, | ||||
| 		data, length, | ||||
| 		data, length, | ||||
| 		&bytes_returned, &ol); | ||||
| 
 | ||||
| 	if (!res) { | ||||
| 		if (GetLastError() != ERROR_IO_PENDING) { | ||||
| 			/* DeviceIoControl() failed. Return error. */ | ||||
| 			register_error(dev, "Send Feature Report DeviceIoControl"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Wait here until the write is done. This makes
 | ||||
| 	   hid_get_feature_report() synchronous. */ | ||||
| 	res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); | ||||
| 	if (!res) { | ||||
| 		/* The operation failed. */ | ||||
| 		register_error(dev, "Send Feature Report GetOverLappedResult"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* bytes_returned does not include the first byte which contains the
 | ||||
| 	   report ID. The data buffer actually contains one more byte than | ||||
| 	   bytes_returned. */ | ||||
| 	bytes_returned++; | ||||
| 
 | ||||
| 	return bytes_returned; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) | ||||
| { | ||||
| 	if (!dev) | ||||
| 		return; | ||||
| 	CancelIo(dev->device_handle); | ||||
| 	free_hid_device(dev); | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) | ||||
| { | ||||
| 	BOOL res; | ||||
| 
 | ||||
| 	res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); | ||||
| 	if (!res) { | ||||
| 		register_error(dev, "HidD_GetManufacturerString"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) | ||||
| { | ||||
| 	BOOL res; | ||||
| 
 | ||||
| 	res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); | ||||
| 	if (!res) { | ||||
| 		register_error(dev, "HidD_GetProductString"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) | ||||
| { | ||||
| 	BOOL res; | ||||
| 
 | ||||
| 	res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); | ||||
| 	if (!res) { | ||||
| 		register_error(dev, "HidD_GetSerialNumberString"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) | ||||
| { | ||||
| 	BOOL res; | ||||
| 
 | ||||
| 	res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); | ||||
| 	if (!res) { | ||||
| 		register_error(dev, "HidD_GetIndexedString"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| HID_API_EXPORT const wchar_t * HID_API_CALL  hid_error(hid_device *dev) | ||||
| { | ||||
| 	return (wchar_t*)dev->last_error_str; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*#define PICPGM*/ | ||||
| /*#define S11*/ | ||||
| #define P32 | ||||
| #ifdef S11  | ||||
|   unsigned short VendorID = 0xa0a0; | ||||
| 	unsigned short ProductID = 0x0001; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef P32 | ||||
|   unsigned short VendorID = 0x04d8; | ||||
| 	unsigned short ProductID = 0x3f; | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| #ifdef PICPGM | ||||
|   unsigned short VendorID = 0x04d8; | ||||
|   unsigned short ProductID = 0x0033; | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| #if 0 | ||||
| int __cdecl main(int argc, char* argv[]) | ||||
| { | ||||
| 	int res; | ||||
| 	unsigned char buf[65]; | ||||
| 
 | ||||
| 	UNREFERENCED_PARAMETER(argc); | ||||
| 	UNREFERENCED_PARAMETER(argv); | ||||
| 
 | ||||
| 	/* Set up the command buffer. */ | ||||
| 	memset(buf,0x00,sizeof(buf)); | ||||
| 	buf[0] = 0; | ||||
| 	buf[1] = 0x81; | ||||
| 	 | ||||
| 
 | ||||
| 	/* Open the device. */ | ||||
| 	int handle = open(VendorID, ProductID, L"12345"); | ||||
| 	if (handle < 0) | ||||
| 		printf("unable to open device\n"); | ||||
| 
 | ||||
| 
 | ||||
| 	/* Toggle LED (cmd 0x80) */ | ||||
| 	buf[1] = 0x80; | ||||
| 	res = write(handle, buf, 65); | ||||
| 	if (res < 0) | ||||
| 		printf("Unable to write()\n"); | ||||
| 
 | ||||
| 	/* Request state (cmd 0x81) */ | ||||
| 	buf[1] = 0x81; | ||||
| 	write(handle, buf, 65); | ||||
| 	if (res < 0) | ||||
| 		printf("Unable to write() (2)\n"); | ||||
| 
 | ||||
| 	/* Read requested state */ | ||||
| 	read(handle, buf, 65); | ||||
| 	if (res < 0) | ||||
| 		printf("Unable to read()\n"); | ||||
| 
 | ||||
| 	/* Print out the returned buffer. */ | ||||
| 	for (int i = 0; i < 4; i++) | ||||
| 		printf("buf[%d]: %d\n", i, buf[i]); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| #endif | ||||
|  | @ -6,9 +6,6 @@ | |||
| #include "Geometry.hpp" | ||||
| #include "GCode/PrintExtents.hpp" | ||||
| #include "GCode/WipeTower.hpp" | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| #include "GCode/ThumbnailData.hpp" | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| #include "ShortestPath.hpp" | ||||
| #include "Utils.hpp" | ||||
| 
 | ||||
|  | @ -695,7 +692,7 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec | |||
| } | ||||
| 
 | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, const std::vector<ThumbnailData>* thumbnail_data) | ||||
| void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) | ||||
| #else | ||||
| void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_data) | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|  | @ -725,7 +722,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ | |||
|     try { | ||||
|         m_placeholder_parser_failed_templates.clear(); | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|         this->_do_export(*print, file, thumbnail_data); | ||||
|         this->_do_export(*print, file, thumbnail_cb); | ||||
| #else | ||||
|         this->_do_export(*print, file); | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|  | @ -793,9 +790,9 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ | |||
| } | ||||
| 
 | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| void GCode::_do_export(Print& print, FILE* file, const std::vector<ThumbnailData>* thumbnail_data) | ||||
| void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb) | ||||
| #else | ||||
| void GCode::_do_export(Print &print, FILE *file) | ||||
| void GCode::_do_export(Print& print, FILE* file) | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| { | ||||
|     PROFILE_FUNC(); | ||||
|  | @ -812,46 +809,46 @@ void GCode::_do_export(Print &print, FILE *file) | |||
|     // shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor.
 | ||||
|     if (print.config().gcode_flavor.value == gcfMarlin) { | ||||
|         m_normal_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values[0]); | ||||
| 		m_normal_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[0]); | ||||
| 		m_normal_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[0]); | ||||
| 		m_normal_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[0]); | ||||
| 		m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[0]); | ||||
|         m_normal_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[0]); | ||||
|         m_normal_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[0]); | ||||
|         m_normal_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[0]); | ||||
|         m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[0]); | ||||
| 
 | ||||
|         if (m_silent_time_estimator_enabled) | ||||
|         { | ||||
|             m_silent_time_estimator.reset(); | ||||
|             m_silent_time_estimator.set_dialect(print.config().gcode_flavor); | ||||
|             /* "Stealth mode" values can be just a copy of "normal mode" values 
 | ||||
|             /* "Stealth mode" values can be just a copy of "normal mode" values
 | ||||
|             * (when they aren't input for a printer preset). | ||||
|             * Thus, use back value from values, instead of second one, which could be absent | ||||
|             */ | ||||
| 			m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values.back()); | ||||
| 			m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values.back()); | ||||
| 			m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values.back()); | ||||
| 			m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values.back()); | ||||
| 			m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values.back()); | ||||
|             m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values.back()); | ||||
|             m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values.back()); | ||||
|             m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values.back()); | ||||
|             m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values.back()); | ||||
|             m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values.back()); | ||||
|             if (print.config().single_extruder_multi_material) { | ||||
|                 // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
 | ||||
|                 // are considered to be active for the single extruder multi-material printers only.
 | ||||
|  | @ -909,7 +906,8 @@ void GCode::_do_export(Print &print, FILE *file) | |||
|             std::sort(zs.begin(), zs.end()); | ||||
|             m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); | ||||
|         } | ||||
|     } else { | ||||
|     } | ||||
|     else { | ||||
|         // Print all objects with the same print_z together.
 | ||||
|         std::vector<coordf_t> zs; | ||||
|         for (auto object : print.objects()) { | ||||
|  | @ -927,7 +925,7 @@ void GCode::_do_export(Print &print, FILE *file) | |||
|     m_enable_cooling_markers = true; | ||||
|     this->apply_print_config(print.config()); | ||||
|     this->set_extruders(print.extruders()); | ||||
|      | ||||
| 
 | ||||
|     // Initialize colorprint.
 | ||||
|     m_colorprint_heights = cast<float>(print.config().colorprint_heights.values); | ||||
| 
 | ||||
|  | @ -936,31 +934,31 @@ void GCode::_do_export(Print &print, FILE *file) | |||
|         // get the minimum cross-section used in the print
 | ||||
|         std::vector<double> mm3_per_mm; | ||||
|         for (auto object : print.objects()) { | ||||
|             for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { | ||||
|             for (size_t region_id = 0; region_id < object->region_volumes.size(); ++region_id) { | ||||
|                 const PrintRegion* region = print.regions()[region_id]; | ||||
|                 for (auto layer : object->layers()) { | ||||
|                     const LayerRegion* layerm = layer->regions()[region_id]; | ||||
|                     if (region->config().get_abs_value("perimeter_speed"          ) == 0 ||  | ||||
|                         region->config().get_abs_value("small_perimeter_speed"    ) == 0 ||  | ||||
|                         region->config().get_abs_value("external_perimeter_speed" ) == 0 ||  | ||||
|                         region->config().get_abs_value("bridge_speed"             ) == 0) | ||||
|                     if (region->config().get_abs_value("perimeter_speed") == 0 || | ||||
|                         region->config().get_abs_value("small_perimeter_speed") == 0 || | ||||
|                         region->config().get_abs_value("external_perimeter_speed") == 0 || | ||||
|                         region->config().get_abs_value("bridge_speed") == 0) | ||||
|                         mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm()); | ||||
|                     if (region->config().get_abs_value("infill_speed"             ) == 0 ||  | ||||
|                         region->config().get_abs_value("solid_infill_speed"       ) == 0 ||  | ||||
|                         region->config().get_abs_value("top_solid_infill_speed"   ) == 0 ||  | ||||
|                         region->config().get_abs_value("bridge_speed"             ) == 0) | ||||
|                     if (region->config().get_abs_value("infill_speed") == 0 || | ||||
|                         region->config().get_abs_value("solid_infill_speed") == 0 || | ||||
|                         region->config().get_abs_value("top_solid_infill_speed") == 0 || | ||||
|                         region->config().get_abs_value("bridge_speed") == 0) | ||||
|                         mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm()); | ||||
|                 } | ||||
|             } | ||||
|             if (object->config().get_abs_value("support_material_speed"           ) == 0 ||  | ||||
|                 object->config().get_abs_value("support_material_interface_speed" ) == 0) | ||||
|             if (object->config().get_abs_value("support_material_speed") == 0 || | ||||
|                 object->config().get_abs_value("support_material_interface_speed") == 0) | ||||
|                 for (auto layer : object->support_layers()) | ||||
|                     mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm()); | ||||
|         } | ||||
|         print.throw_if_canceled(); | ||||
|         // filter out 0-width segments
 | ||||
|         mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end()); | ||||
|         if (! mm3_per_mm.empty()) { | ||||
|         if (!mm3_per_mm.empty()) { | ||||
|             // In order to honor max_print_speed we need to find a target volumetric
 | ||||
|             // speed that we can use throughout the print. So we define this target 
 | ||||
|             // volumetric speed as the volumetric speed produced by printing the 
 | ||||
|  | @ -973,7 +971,7 @@ void GCode::_do_export(Print &print, FILE *file) | |||
|         } | ||||
|     } | ||||
|     print.throw_if_canceled(); | ||||
|      | ||||
| 
 | ||||
|     m_cooling_buffer = make_unique<CoolingBuffer>(*this); | ||||
|     if (print.config().spiral_vase.value) | ||||
|         m_spiral_vase = make_unique<SpiralVase>(print.config()); | ||||
|  | @ -991,11 +989,12 @@ void GCode::_do_export(Print &print, FILE *file) | |||
| 
 | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|     // Write thumbnails using base64 encoding
 | ||||
|     if (thumbnail_data != nullptr) | ||||
|     if (thumbnail_cb != nullptr) | ||||
|     { | ||||
|         const size_t max_row_length = 78; | ||||
| 
 | ||||
|         for (const ThumbnailData& data : *thumbnail_data) | ||||
|         ThumbnailsList thumbnails; | ||||
|         thumbnail_cb(thumbnails, print.full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, false); | ||||
|         for (const ThumbnailData& data : thumbnails) | ||||
|         { | ||||
|             if (data.is_valid()) | ||||
|             { | ||||
|  |  | |||
|  | @ -17,6 +17,9 @@ | |||
| #include "GCodeTimeEstimator.hpp" | ||||
| #include "EdgeGrid.hpp" | ||||
| #include "GCode/Analyzer.hpp" | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| #include "GCode/ThumbnailData.hpp" | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <string> | ||||
|  | @ -30,9 +33,6 @@ namespace Slic3r { | |||
| // Forward declarations.
 | ||||
| class GCode; | ||||
| class GCodePreviewData; | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| struct ThumbnailData; | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| 
 | ||||
| class AvoidCrossingPerimeters { | ||||
| public: | ||||
|  | @ -167,7 +167,7 @@ public: | |||
|     // throws std::runtime_exception on error,
 | ||||
|     // throws CanceledException through print->throw_if_canceled().
 | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|     void            do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, const std::vector<ThumbnailData>* thumbnail_data = nullptr); | ||||
|     void            do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); | ||||
| #else | ||||
|     void            do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr); | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|  | @ -199,7 +199,7 @@ public: | |||
| 
 | ||||
| protected: | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|     void            _do_export(Print& print, FILE* file, const std::vector<ThumbnailData>* thumbnail_data); | ||||
|     void            _do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb); | ||||
| #else | ||||
|     void            _do_export(Print &print, FILE *file); | ||||
| #endif //ENABLE_THUMBNAIL_GENERATOR
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| 
 | ||||
| #include <vector> | ||||
| #include "libslic3r/Point.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -20,6 +21,9 @@ struct ThumbnailData | |||
|     bool is_valid() const; | ||||
| }; | ||||
| 
 | ||||
| typedef std::vector<ThumbnailData> ThumbnailsList; | ||||
| typedef std::function<void(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background)> ThumbnailsGeneratorCallback; | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|  |  | |||
|  | @ -1538,7 +1538,7 @@ void Print::process() | |||
| // write error into the G-code, cannot execute post-processing scripts).
 | ||||
| // It is up to the caller to show an error message.
 | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, const std::vector<ThumbnailData>* thumbnail_data) | ||||
| std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) | ||||
| #else | ||||
| std::string Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data) | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|  | @ -1559,7 +1559,7 @@ std::string Print::export_gcode(const std::string &path_template, GCodePreviewDa | |||
|     // The following line may die for multiple reasons.
 | ||||
|     GCode gcode; | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|     gcode.do_export(this, path.c_str(), preview_data, thumbnail_data); | ||||
|     gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb); | ||||
| #else | ||||
|     gcode.do_export(this, path.c_str(), preview_data); | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|  |  | |||
|  | @ -11,6 +11,9 @@ | |||
| #include "Slicing.hpp" | ||||
| #include "GCode/ToolOrdering.hpp" | ||||
| #include "GCode/WipeTower.hpp" | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| #include "GCode/ThumbnailData.hpp" | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -19,9 +22,6 @@ class PrintObject; | |||
| class ModelObject; | ||||
| class GCode; | ||||
| class GCodePreviewData; | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| struct ThumbnailData; | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| 
 | ||||
| // Print step IDs for keeping track of the print state.
 | ||||
| enum PrintStep { | ||||
|  | @ -311,7 +311,7 @@ public: | |||
|     // Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
 | ||||
|     // If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
 | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|     std::string         export_gcode(const std::string& path_template, GCodePreviewData* preview_data, const std::vector<ThumbnailData>* thumbnail_data = nullptr); | ||||
|     std::string         export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); | ||||
| #else | ||||
|     std::string         export_gcode(const std::string &path_template, GCodePreviewData *preview_data); | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|  |  | |||
|  | @ -1522,9 +1522,9 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c | |||
|         layer_height_profile.clear(); | ||||
| 
 | ||||
|     if (layer_height_profile.empty()) { | ||||
|             //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
 | ||||
|             layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); | ||||
|        	updated = true; | ||||
|         //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
 | ||||
|         layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); | ||||
|         updated = true; | ||||
|     } | ||||
|     return updated; | ||||
| } | ||||
|  |  | |||
|  | @ -224,40 +224,59 @@ std::vector<coordf_t> layer_height_profile_from_ranges( | |||
| 
 | ||||
| // Based on the work of @platsch
 | ||||
| // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
 | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| std::vector<double> layer_height_profile_adaptive(const SlicingParameters& slicing_params, | ||||
|     const ModelObject& object, float cusp_value) | ||||
| #else | ||||
| std::vector<coordf_t> layer_height_profile_adaptive( | ||||
|     const SlicingParameters     &slicing_params, | ||||
|     const t_layer_config_ranges & /* layer_config_ranges */, | ||||
|     const ModelVolumePtrs		&volumes) | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| { | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     // 1) Initialize the SlicingAdaptive class with the object meshes.
 | ||||
|     SlicingAdaptive as; | ||||
|     as.set_slicing_parameters(slicing_params); | ||||
|     for (const ModelVolume *volume : volumes) | ||||
|     as.set_object(object); | ||||
| #else | ||||
|     // 1) Initialize the SlicingAdaptive class with the object meshes.
 | ||||
|     SlicingAdaptive as; | ||||
|     as.set_slicing_parameters(slicing_params); | ||||
|     for (const ModelVolume* volume : volumes) | ||||
|         if (volume->is_model_part()) | ||||
|             as.add_mesh(&volume->mesh()); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
|     as.prepare(); | ||||
| 
 | ||||
|     // 2) Generate layers using the algorithm of @platsch 
 | ||||
|     // loop until we have at least one layer and the max slice_z reaches the object height
 | ||||
|     //FIXME make it configurable
 | ||||
|     // Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm.
 | ||||
|     const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value');
 | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     double cusp_value = 0.2; | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
|     std::vector<coordf_t> layer_height_profile; | ||||
|     layer_height_profile.push_back(0.); | ||||
|     std::vector<double> layer_height_profile; | ||||
|     layer_height_profile.push_back(0.0); | ||||
|     layer_height_profile.push_back(slicing_params.first_object_layer_height); | ||||
|     if (slicing_params.first_object_layer_height_fixed()) { | ||||
|         layer_height_profile.push_back(slicing_params.first_object_layer_height); | ||||
|         layer_height_profile.push_back(slicing_params.first_object_layer_height); | ||||
|     } | ||||
|     coordf_t slice_z = slicing_params.first_object_layer_height; | ||||
|     coordf_t height  = slicing_params.first_object_layer_height; | ||||
|     double slice_z = slicing_params.first_object_layer_height; | ||||
|     int current_facet = 0; | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     while (slice_z <= slicing_params.object_print_z_height()) { | ||||
|         double height = 999.0; | ||||
| #else | ||||
|     double height = slicing_params.first_object_layer_height; | ||||
|     while ((slice_z - height) <= slicing_params.object_print_z_height()) { | ||||
|         height = 999; | ||||
|         height = 999.0; | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|         // Slic3r::debugf "\n Slice layer: %d\n", $id;
 | ||||
|         // determine next layer height
 | ||||
|         coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet); | ||||
|         double cusp_height = as.cusp_height((float)slice_z, cusp_value, current_facet); | ||||
| 
 | ||||
|         // check for horizontal features and object size
 | ||||
|         /*
 | ||||
|         if($self->config->get_value('match_horizontal_surfaces')) { | ||||
|  | @ -303,19 +322,113 @@ std::vector<coordf_t> layer_height_profile_adaptive( | |||
|         layer_height_profile.push_back(slice_z); | ||||
|         layer_height_profile.push_back(height); | ||||
|         slice_z += height; | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|         layer_height_profile.push_back(slice_z); | ||||
|         layer_height_profile.push_back(height); | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|     } | ||||
| 
 | ||||
|     coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     double z_gap = slicing_params.object_print_z_height() - layer_height_profile[layer_height_profile.size() - 2]; | ||||
|     if (z_gap > 0.0) | ||||
|     { | ||||
|         layer_height_profile.push_back(slicing_params.object_print_z_height()); | ||||
|         layer_height_profile.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, z_gap)); | ||||
|     } | ||||
| #else | ||||
|     double last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); | ||||
|     layer_height_profile.push_back(last); | ||||
|     layer_height_profile.push_back(slicing_params.first_object_layer_height); | ||||
|     layer_height_profile.push_back(slicing_params.object_print_z_height()); | ||||
|     layer_height_profile.push_back(slicing_params.first_object_layer_height); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
|     return layer_height_profile; | ||||
| } | ||||
| 
 | ||||
| std::vector<double> smooth_height_profile(const std::vector<double>& profile, const SlicingParameters& slicing_params, const HeightProfileSmoothingParams& smoothing_params) | ||||
| { | ||||
|     auto gauss_blur = [&slicing_params](const std::vector<double>& profile, const HeightProfileSmoothingParams& smoothing_params) -> std::vector<double> { | ||||
|         auto gauss_kernel = [] (unsigned int radius) -> std::vector<double> { | ||||
|             unsigned int size = 2 * radius + 1; | ||||
|             std::vector<double> ret; | ||||
|             ret.reserve(size); | ||||
| 
 | ||||
|             // Reworked from static inline int getGaussianKernelSize(float sigma) taken from opencv-4.1.2\modules\features2d\src\kaze\AKAZEFeatures.cpp
 | ||||
|             double sigma = 0.3 * (double)(radius - 1) + 0.8; | ||||
|             double two_sq_sigma = 2.0 * sigma * sigma; | ||||
|             double inv_root_two_pi_sq_sigma = 1.0 / ::sqrt(M_PI * two_sq_sigma); | ||||
| 
 | ||||
|             for (unsigned int i = 0; i < size; ++i) | ||||
|             { | ||||
|                 double x = (double)i - (double)radius; | ||||
|                 ret.push_back(inv_root_two_pi_sq_sigma * ::exp(-x * x / two_sq_sigma)); | ||||
|             } | ||||
| 
 | ||||
|             return ret; | ||||
|         }; | ||||
| 
 | ||||
|         // skip first layer ?
 | ||||
|         size_t skip_count = slicing_params.first_object_layer_height_fixed() ? 4 : 0; | ||||
| 
 | ||||
|         // not enough data to smmoth
 | ||||
|         if ((int)profile.size() - (int)skip_count < 6) | ||||
|             return profile; | ||||
|          | ||||
|         unsigned int radius = std::max(smoothing_params.radius, (unsigned int)1); | ||||
|         std::vector<double> kernel = gauss_kernel(radius); | ||||
|         int two_radius = 2 * (int)radius; | ||||
| 
 | ||||
|         std::vector<double> ret; | ||||
|         size_t size = profile.size(); | ||||
|         ret.reserve(size); | ||||
| 
 | ||||
|         // leave first layer untouched
 | ||||
|         for (size_t i = 0; i < skip_count; ++i) | ||||
|         { | ||||
|             ret.push_back(profile[i]); | ||||
|         } | ||||
| 
 | ||||
|         // smooth the rest of the profile by biasing a gaussian blur
 | ||||
|         // the bias moves the smoothed profile closer to the min_layer_height
 | ||||
|         double delta_h = slicing_params.max_layer_height - slicing_params.min_layer_height; | ||||
|         double inv_delta_h = (delta_h != 0.0) ? 1.0 / delta_h : 1.0; | ||||
| 
 | ||||
|         double max_dz_band = (double)radius * slicing_params.layer_height; | ||||
|         for (size_t i = skip_count; i < size; i += 2) | ||||
|         { | ||||
|             double zi = profile[i]; | ||||
|             double hi = profile[i + 1]; | ||||
|             ret.push_back(zi); | ||||
|             ret.push_back(0.0); | ||||
|             double& height = ret.back(); | ||||
|             int begin = std::max((int)i - two_radius, (int)skip_count); | ||||
|             int end = std::min((int)i + two_radius, (int)size - 2); | ||||
|             double weight_total = 0.0; | ||||
|             for (int j = begin; j <= end; j += 2) | ||||
|             { | ||||
|                 int kernel_id = radius + (j - (int)i) / 2; | ||||
|                 double dz = std::abs(zi - profile[j]); | ||||
|                 if (dz * slicing_params.layer_height <= max_dz_band) | ||||
|                 { | ||||
|                     double dh = std::abs(slicing_params.max_layer_height - profile[j + 1]); | ||||
|                     double weight = kernel[kernel_id] * sqrt(dh * inv_delta_h); | ||||
|                     height += weight * profile[j + 1]; | ||||
|                     weight_total += weight; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (weight_total != 0.0) ? height /= weight_total : hi); | ||||
|             if (smoothing_params.keep_min) | ||||
|                 height = std::min(height, hi); | ||||
|         } | ||||
| 
 | ||||
|         return ret; | ||||
|     }; | ||||
| 
 | ||||
|     return gauss_blur(profile, smoothing_params); | ||||
| } | ||||
| 
 | ||||
| void adjust_layer_height_profile( | ||||
|     const SlicingParameters     &slicing_params, | ||||
|     std::vector<coordf_t> 		&layer_height_profile, | ||||
|  | @ -609,7 +722,11 @@ int generate_layer_height_texture( | |||
|             const Vec3crd &color1 = palette_raw[idx1]; | ||||
|             const Vec3crd &color2 = palette_raw[idx2]; | ||||
|             coordf_t z = cell_to_z * coordf_t(cell); | ||||
| 			assert(z >= lo && z <= hi); | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|             assert((lo - EPSILON <= z) && (z <= hi + EPSILON)); | ||||
| #else | ||||
|             assert(z >= lo && z <= hi); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|             // Intensity profile to visualize the layers.
 | ||||
|             coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h); | ||||
|             // Color mapping from layer height to RGB.
 | ||||
|  |  | |||
|  | @ -18,8 +18,12 @@ namespace Slic3r | |||
| 
 | ||||
| class PrintConfig; | ||||
| class PrintObjectConfig; | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| class ModelObject; | ||||
| #else | ||||
| class ModelVolume; | ||||
| typedef std::vector<ModelVolume*> ModelVolumePtrs; | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| // Parameters to guide object slicing and support generation.
 | ||||
| // The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow
 | ||||
|  | @ -138,11 +142,29 @@ extern std::vector<coordf_t> layer_height_profile_from_ranges( | |||
|     const SlicingParameters     &slicing_params, | ||||
|     const t_layer_config_ranges &layer_config_ranges); | ||||
| 
 | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| extern std::vector<double> layer_height_profile_adaptive( | ||||
|     const SlicingParameters& slicing_params, | ||||
|     const ModelObject& object, float cusp_value); | ||||
| 
 | ||||
| struct HeightProfileSmoothingParams | ||||
| { | ||||
|     unsigned int radius; | ||||
|     bool keep_min; | ||||
| 
 | ||||
|     HeightProfileSmoothingParams() : radius(5), keep_min(false) {} | ||||
|     HeightProfileSmoothingParams(unsigned int radius, bool keep_min) : radius(radius), keep_min(keep_min) {} | ||||
| }; | ||||
| 
 | ||||
| extern std::vector<double> smooth_height_profile( | ||||
|     const std::vector<double>& profile, const SlicingParameters& slicing_params, | ||||
|     const HeightProfileSmoothingParams& smoothing_params); | ||||
| #else | ||||
| extern std::vector<coordf_t> layer_height_profile_adaptive( | ||||
|     const SlicingParameters     &slicing_params, | ||||
|     const t_layer_config_ranges &layer_config_ranges, | ||||
|     const ModelVolumePtrs       &volumes); | ||||
| 
 | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| enum LayerHeightEditActionType : unsigned int { | ||||
|     LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0, | ||||
|  |  | |||
|  | @ -1,16 +1,22 @@ | |||
| #include "libslic3r.h" | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| #include "Model.hpp" | ||||
| #else | ||||
| #include "TriangleMesh.hpp" | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| #include "SlicingAdaptive.hpp" | ||||
| 
 | ||||
| namespace Slic3r | ||||
| { | ||||
| 
 | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| void SlicingAdaptive::clear() | ||||
| { | ||||
| 	m_meshes.clear(); | ||||
|     m_meshes.clear(); | ||||
| 	m_faces.clear(); | ||||
| 	m_face_normal_z.clear(); | ||||
| } | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| std::pair<float, float> face_z_span(const stl_facet *f) | ||||
| { | ||||
|  | @ -21,21 +27,42 @@ std::pair<float, float> face_z_span(const stl_facet *f) | |||
| 
 | ||||
| void SlicingAdaptive::prepare() | ||||
| { | ||||
| 	// 1) Collect faces of all meshes.
 | ||||
| 	int nfaces_total = 0; | ||||
| 	for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     if (m_object == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     m_faces.clear(); | ||||
|     m_face_normal_z.clear(); | ||||
| 
 | ||||
|     m_mesh = m_object->raw_mesh(); | ||||
|     const ModelInstance* first_instance = m_object->instances.front(); | ||||
|     m_mesh.transform(first_instance->get_matrix(), first_instance->is_left_handed()); | ||||
|     for (stl_facet& facet : m_mesh.stl.facet_start) | ||||
|     { | ||||
|         facet.normal.normalize(); | ||||
|     } | ||||
| 
 | ||||
|     // 1) Collect faces from mesh.
 | ||||
|     m_faces.reserve(m_mesh.stl.stats.number_of_facets); | ||||
|     for (const stl_facet& face : m_mesh.stl.facet_start) | ||||
|         m_faces.emplace_back(&face); | ||||
| #else | ||||
|     // 1) Collect faces of all meshes.
 | ||||
|     int nfaces_total = 0; | ||||
|     for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) | ||||
| 		nfaces_total += (*it_mesh)->stl.stats.number_of_facets; | ||||
| 	m_faces.reserve(nfaces_total); | ||||
| 	for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) | ||||
| 		for (const stl_facet &face : (*it_mesh)->stl.facet_start) | ||||
| 			m_faces.emplace_back(&face); | ||||
|     m_faces.reserve(nfaces_total); | ||||
|     for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) | ||||
|         for (const stl_facet& face : (*it_mesh)->stl.facet_start) | ||||
|             m_faces.emplace_back(&face); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| 	// 2) Sort faces lexicographically by their Z span.
 | ||||
| 	std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { | ||||
| 		std::pair<float, float> span1 = face_z_span(f1); | ||||
|         std::pair<float, float> span1 = face_z_span(f1); | ||||
| 		std::pair<float, float> span2 = face_z_span(f2); | ||||
| 		return span1 < span2; | ||||
| 	}); | ||||
|         return span1 < span2; | ||||
|     }); | ||||
| 
 | ||||
| 	// 3) Generate Z components of the facet normals.
 | ||||
| 	m_face_normal_z.assign(m_faces.size(), 0.f); | ||||
|  | @ -45,14 +72,14 @@ void SlicingAdaptive::prepare() | |||
| 
 | ||||
| float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet) | ||||
| { | ||||
| 	float height = m_slicing_params.max_layer_height; | ||||
| 	float height = (float)m_slicing_params.max_layer_height; | ||||
| 	bool first_hit = false; | ||||
| 	 | ||||
| 	// find all facets intersecting the slice-layer
 | ||||
| 	int ordered_id = current_facet; | ||||
| 	for (; ordered_id < int(m_faces.size()); ++ ordered_id) { | ||||
| 		std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]); | ||||
| 		// facet's minimum is higher than slice_z -> end loop
 | ||||
|         std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]); | ||||
|         // facet's minimum is higher than slice_z -> end loop
 | ||||
| 		if (zspan.first >= z) | ||||
| 			break; | ||||
| 		// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
 | ||||
|  | @ -77,8 +104,8 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet | |||
| 	// check for sloped facets inside the determined layer and correct height if necessary
 | ||||
| 	if (height > m_slicing_params.min_layer_height) { | ||||
| 		for (; ordered_id < int(m_faces.size()); ++ ordered_id) { | ||||
| 			std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]); | ||||
| 			// facet's minimum is higher than slice_z + height -> end loop
 | ||||
|             std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]); | ||||
|             // facet's minimum is higher than slice_z + height -> end loop
 | ||||
| 			if (zspan.first >= z + height) | ||||
| 				break; | ||||
| 
 | ||||
|  | @ -117,24 +144,25 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet | |||
| 	return height;  | ||||
| } | ||||
| 
 | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| // Returns the distance to the next horizontal facet in Z-dir 
 | ||||
| // to consider horizontal object features in slice thickness
 | ||||
| float SlicingAdaptive::horizontal_facet_distance(float z) | ||||
| { | ||||
| 	for (size_t i = 0; i < m_faces.size(); ++ i) { | ||||
| 		std::pair<float, float> zspan = face_z_span(m_faces[i]); | ||||
| 		// facet's minimum is higher than max forward distance -> end loop
 | ||||
|         std::pair<float, float> zspan = face_z_span(m_faces[i]); | ||||
|         // facet's minimum is higher than max forward distance -> end loop
 | ||||
| 		if (zspan.first > z + m_slicing_params.max_layer_height) | ||||
| 			break; | ||||
| 		// min_z == max_z -> horizontal facet
 | ||||
| 		if (zspan.first > z && zspan.first == zspan.second) | ||||
| 		if ((zspan.first > z) && (zspan.first == zspan.second)) | ||||
| 			return zspan.first - z; | ||||
| 	} | ||||
| 	 | ||||
| 	// objects maximum?
 | ||||
| 	return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ?  | ||||
| 		std::max<float>(m_slicing_params.object_print_z_height() - z, 0.f) : | ||||
| 		m_slicing_params.max_layer_height; | ||||
| 	return (z + (float)m_slicing_params.max_layer_height > (float)m_slicing_params.object_print_z_height()) ?  | ||||
| 		std::max((float)m_slicing_params.object_print_z_height() - z, 0.f) : (float)m_slicing_params.max_layer_height; | ||||
| } | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| }; // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -5,29 +5,49 @@ | |||
| 
 | ||||
| #include "Slicing.hpp" | ||||
| #include "admesh/stl.h" | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| #include "TriangleMesh.hpp" | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| namespace Slic3r | ||||
| { | ||||
| 
 | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| class ModelVolume; | ||||
| #else | ||||
| class TriangleMesh; | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| class SlicingAdaptive | ||||
| { | ||||
| public: | ||||
| 	void clear(); | ||||
| 	void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } | ||||
| 	void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); } | ||||
| 	void prepare(); | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     void clear(); | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|     void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     void set_object(const ModelObject& object) { m_object = &object; } | ||||
| #else | ||||
|     void add_mesh(const TriangleMesh* mesh) { m_meshes.push_back(mesh); } | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|     void prepare(); | ||||
| 	float cusp_height(float z, float cusp_value, int ¤t_facet); | ||||
| 	float horizontal_facet_distance(float z); | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     float horizontal_facet_distance(float z); | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| protected: | ||||
| 	SlicingParameters 					m_slicing_params; | ||||
| 
 | ||||
| 	std::vector<const TriangleMesh*>	m_meshes; | ||||
| 	// Collected faces of all meshes, sorted by raising Z of the bottom most face.
 | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     const ModelObject*                  m_object; | ||||
|     TriangleMesh                        m_mesh; | ||||
| #else | ||||
|     std::vector<const TriangleMesh*>	m_meshes; | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|     // Collected faces of all meshes, sorted by raising Z of the bottom most face.
 | ||||
| 	std::vector<const stl_facet*>		m_faces; | ||||
| 	// Z component of face normals, normalized.
 | ||||
|     // Z component of face normals, normalized.
 | ||||
| 	std::vector<float>					m_face_normal_z; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -42,4 +42,7 @@ | |||
| #define ENABLE_THUMBNAIL_GENERATOR_DEBUG (0 && ENABLE_THUMBNAIL_GENERATOR) | ||||
| #define ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE (1 && ENABLE_THUMBNAIL_GENERATOR) | ||||
| 
 | ||||
| // Enable adaptive layer height profile
 | ||||
| #define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE (1 && ENABLE_2_2_0_ALPHA1) | ||||
| 
 | ||||
| #endif // _technologies_h_
 | ||||
|  |  | |||
|  | @ -194,7 +194,7 @@ inline typename CONTAINER_TYPE::size_type next_idx_modulo(typename CONTAINER_TYP | |||
| } | ||||
| 
 | ||||
| template<typename CONTAINER_TYPE> | ||||
| inline typename const CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)  | ||||
| inline const typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) | ||||
| {  | ||||
| 	return container[prev_idx_modulo(idx, container.size())]; | ||||
| } | ||||
|  | @ -206,7 +206,7 @@ inline typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER | |||
| } | ||||
| 
 | ||||
| template<typename CONTAINER_TYPE> | ||||
| inline typename const CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) | ||||
| inline const typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container) | ||||
| {  | ||||
| 	return container[next_idx_modulo(idx, container.size())]; | ||||
| } | ||||
|  |  | |||
|  | @ -136,6 +136,8 @@ set(SLIC3R_GUI_SOURCES | |||
|     GUI/ProgressStatusBar.cpp | ||||
|     GUI/PrintHostDialogs.cpp | ||||
|     GUI/PrintHostDialogs.hpp | ||||
|     GUI/Mouse3DController.cpp | ||||
|     GUI/Mouse3DController.hpp | ||||
|     Utils/Http.cpp | ||||
|     Utils/Http.hpp | ||||
|     Utils/FixModelByWin10.cpp | ||||
|  | @ -170,7 +172,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) | |||
| 
 | ||||
| encoding_check(libslic3r_gui) | ||||
| 
 | ||||
| target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES}) | ||||
| target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES} hidapi) | ||||
| if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) | ||||
|     add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) | ||||
| endif () | ||||
|  |  | |||
|  | @ -271,6 +271,80 @@ void AppConfig::set_recent_projects(const std::vector<std::string>& recent_proje | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone) | ||||
| { | ||||
|     std::string key = std::string("mouse_device:") + name; | ||||
|     auto it = m_storage.find(key); | ||||
|     if (it == m_storage.end()) | ||||
|         it = m_storage.insert(std::map<std::string, std::map<std::string, std::string>>::value_type(key, std::map<std::string, std::string>())).first; | ||||
| 
 | ||||
|     it->second.clear(); | ||||
|     it->second["translation_speed"] = std::to_string(translation_speed); | ||||
|     it->second["translation_deadzone"] = std::to_string(translation_deadzone); | ||||
|     it->second["rotation_speed"] = std::to_string(rotation_speed); | ||||
|     it->second["rotation_deadzone"] = std::to_string(rotation_deadzone); | ||||
| } | ||||
| 
 | ||||
| bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& speed) | ||||
| { | ||||
|     std::string key = std::string("mouse_device:") + name; | ||||
|     auto it = m_storage.find(key); | ||||
|     if (it == m_storage.end()) | ||||
|         return false; | ||||
| 
 | ||||
|     auto it_val = it->second.find("translation_speed"); | ||||
|     if (it_val == it->second.end()) | ||||
|         return false; | ||||
| 
 | ||||
|     speed = ::atof(it_val->second.c_str()); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool AppConfig::get_mouse_device_translation_deadzone(const std::string& name, double& deadzone) | ||||
| { | ||||
|     std::string key = std::string("mouse_device:") + name; | ||||
|     auto it = m_storage.find(key); | ||||
|     if (it == m_storage.end()) | ||||
|         return false; | ||||
| 
 | ||||
|     auto it_val = it->second.find("translation_deadzone"); | ||||
|     if (it_val == it->second.end()) | ||||
|         return false; | ||||
| 
 | ||||
|     deadzone = ::atof(it_val->second.c_str()); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& speed) | ||||
| { | ||||
|     std::string key = std::string("mouse_device:") + name; | ||||
|     auto it = m_storage.find(key); | ||||
|     if (it == m_storage.end()) | ||||
|         return false; | ||||
| 
 | ||||
|     auto it_val = it->second.find("rotation_speed"); | ||||
|     if (it_val == it->second.end()) | ||||
|         return false; | ||||
| 
 | ||||
|     speed = (float)::atof(it_val->second.c_str()); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool AppConfig::get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone) | ||||
| { | ||||
|     std::string key = std::string("mouse_device:") + name; | ||||
|     auto it = m_storage.find(key); | ||||
|     if (it == m_storage.end()) | ||||
|         return false; | ||||
| 
 | ||||
|     auto it_val = it->second.find("rotation_deadzone"); | ||||
|     if (it_val == it->second.end()) | ||||
|         return false; | ||||
| 
 | ||||
|     deadzone = (float)::atof(it_val->second.c_str()); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void AppConfig::update_config_dir(const std::string &dir) | ||||
| { | ||||
|     this->set("recent", "config_directory", dir); | ||||
|  |  | |||
|  | @ -131,8 +131,15 @@ public: | |||
|     std::vector<std::string> get_recent_projects() const; | ||||
|     void set_recent_projects(const std::vector<std::string>& recent_projects); | ||||
| 
 | ||||
|     void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone); | ||||
|     bool get_mouse_device_translation_speed(const std::string& name, double& speed); | ||||
|     bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone); | ||||
|     bool get_mouse_device_rotation_speed(const std::string& name, float& speed); | ||||
|     bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone); | ||||
| 
 | ||||
| 	static const std::string SECTION_FILAMENTS; | ||||
|     static const std::string SECTION_MATERIALS; | ||||
| 
 | ||||
| private: | ||||
| 	// Map of section, name -> value
 | ||||
| 	std::map<std::string, std::map<std::string, std::string>> 	m_storage; | ||||
|  |  | |||
|  | @ -20,9 +20,6 @@ | |||
| #include "libslic3r/Utils.hpp" | ||||
| #include "libslic3r/GCode/PostProcessor.hpp" | ||||
| #include "libslic3r/GCode/PreviewData.hpp" | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| #include "libslic3r/GCode/ThumbnailData.hpp" | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| #include "libslic3r/libslic3r.h" | ||||
| 
 | ||||
| #include <cassert> | ||||
|  | @ -91,7 +88,7 @@ void BackgroundSlicingProcess::process_fff() | |||
|     m_print->process(); | ||||
| 	wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id)); | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|     m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_data); | ||||
|     m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb); | ||||
| #else | ||||
|     m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|  | @ -139,9 +136,12 @@ void BackgroundSlicingProcess::process_sla() | |||
|             m_sla_print->export_raster(zipper); | ||||
| 
 | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|             if (m_thumbnail_data != nullptr) | ||||
|             if (m_thumbnail_cb != nullptr) | ||||
|             { | ||||
|                 for (const ThumbnailData& data : *m_thumbnail_data) | ||||
|                 ThumbnailsList thumbnails; | ||||
|                 m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, false); | ||||
| //                m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, false, false); // renders also supports and pad
 | ||||
|                 for (const ThumbnailData& data : thumbnails) | ||||
|                 { | ||||
|                     if (data.is_valid()) | ||||
|                         write_thumbnail(zipper, data); | ||||
|  | @ -461,9 +461,12 @@ void BackgroundSlicingProcess::prepare_upload() | |||
|         Zipper zipper{source_path.string()}; | ||||
|         m_sla_print->export_raster(zipper, m_upload_job.upload_data.upload_path.string()); | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|         if (m_thumbnail_data != nullptr) | ||||
|         if (m_thumbnail_cb != nullptr) | ||||
|         { | ||||
|             for (const ThumbnailData& data : *m_thumbnail_data) | ||||
|             ThumbnailsList thumbnails; | ||||
|             m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, false); | ||||
| //            m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, false, false); // renders also supports and pad
 | ||||
|             for (const ThumbnailData& data : thumbnails) | ||||
|             { | ||||
|                 if (data.is_valid()) | ||||
|                     write_thumbnail(zipper, data); | ||||
|  |  | |||
|  | @ -17,9 +17,6 @@ namespace Slic3r { | |||
| 
 | ||||
| class DynamicPrintConfig; | ||||
| class GCodePreviewData; | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| struct ThumbnailData; | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| class Model; | ||||
| class SLAPrint; | ||||
| 
 | ||||
|  | @ -53,7 +50,7 @@ public: | |||
| 	void set_sla_print(SLAPrint *print) { m_sla_print = print; } | ||||
| 	void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|     void set_thumbnail_data(const std::vector<ThumbnailData>* data) { m_thumbnail_data = data; } | ||||
|     void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; } | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| 
 | ||||
| 	// The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished
 | ||||
|  | @ -159,8 +156,8 @@ private: | |||
| 	// Data structure, to which the G-code export writes its annotations.
 | ||||
| 	GCodePreviewData 		   *m_gcode_preview_data = nullptr; | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|     // Data structures, used to write thumbnails into gcode.
 | ||||
|     const std::vector<ThumbnailData>* m_thumbnail_data = nullptr; | ||||
|     // Callback function, used to write thumbnails into gcode.
 | ||||
|     ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr; | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| 	// Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
 | ||||
| 	std::string 				m_temp_output_path; | ||||
|  |  | |||
|  | @ -91,10 +91,16 @@ void Camera::select_next_type() | |||
| 
 | ||||
| void Camera::set_target(const Vec3d& target) | ||||
| { | ||||
|     m_target = target; | ||||
|     m_target(0) = clamp(m_scene_box.min(0), m_scene_box.max(0), m_target(0)); | ||||
|     m_target(1) = clamp(m_scene_box.min(1), m_scene_box.max(1), m_target(1)); | ||||
|     m_target(2) = clamp(m_scene_box.min(2), m_scene_box.max(2), m_target(2)); | ||||
|     BoundingBoxf3 test_box = m_scene_box; | ||||
|     test_box.translate(-m_scene_box.center()); | ||||
|     // We may let this factor be customizable
 | ||||
|     static const double ScaleFactor = 1.5; | ||||
|     test_box.scale(ScaleFactor); | ||||
|     test_box.translate(m_scene_box.center()); | ||||
| 
 | ||||
|     m_target(0) = clamp(test_box.min(0), test_box.max(0), target(0)); | ||||
|     m_target(1) = clamp(test_box.min(1), test_box.max(1), target(1)); | ||||
|     m_target(2) = clamp(test_box.min(2), test_box.max(2), target(2)); | ||||
| } | ||||
| 
 | ||||
| void Camera::set_theta(float theta, bool apply_limit) | ||||
|  | @ -109,20 +115,20 @@ void Camera::set_theta(float theta, bool apply_limit) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h) | ||||
| void Camera::update_zoom(double delta_zoom) | ||||
| { | ||||
|     zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0; | ||||
|     zoom = m_zoom / (1.0 - zoom); | ||||
|     set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); | ||||
| } | ||||
| 
 | ||||
| void Camera::set_zoom(double zoom) | ||||
| { | ||||
|     // Don't allow to zoom too far outside the scene.
 | ||||
|     double zoom_min = calc_zoom_to_bounding_box_factor(max_box, canvas_w, canvas_h); | ||||
|     double zoom_min = calc_zoom_to_bounding_box_factor(m_scene_box, (int)m_viewport[2], (int)m_viewport[3]); | ||||
|     if (zoom_min > 0.0) | ||||
|         zoom = std::max(zoom, zoom_min * 0.7); | ||||
| 
 | ||||
|     // Don't allow to zoom too close to the scene.
 | ||||
|     zoom = std::min(zoom, 100.0); | ||||
| 
 | ||||
|     m_zoom = zoom; | ||||
|     m_zoom = std::min(zoom, 100.0); | ||||
| } | ||||
| 
 | ||||
| bool Camera::select_view(const std::string& direction) | ||||
|  |  | |||
|  | @ -70,8 +70,8 @@ public: | |||
|     void set_theta(float theta, bool apply_limit); | ||||
| 
 | ||||
|     double get_zoom() const { return m_zoom; } | ||||
|     void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h); | ||||
|     void set_zoom(double zoom) { m_zoom = zoom; } | ||||
|     void update_zoom(double delta_zoom); | ||||
|     void set_zoom(double zoom); | ||||
| 
 | ||||
|     const BoundingBoxf3& get_scene_box() const { return m_scene_box; } | ||||
|     void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; } | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ | |||
| #include "GUI_App.hpp" | ||||
| #include "GUI_ObjectList.hpp" | ||||
| #include "GUI_ObjectManipulation.hpp" | ||||
| #include "Mouse3DController.hpp" | ||||
| #include "I18N.hpp" | ||||
| 
 | ||||
| #if ENABLE_RETINA_GL | ||||
|  | @ -130,6 +131,9 @@ GLCanvas3D::LayersEditing::LayersEditing() | |||
|     , m_object_max_z(0.f) | ||||
|     , m_slicing_parameters(nullptr) | ||||
|     , m_layer_height_profile_modified(false) | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     , m_adaptive_cusp(0.2f) | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|     , state(Unknown) | ||||
|     , band_width(2.0f) | ||||
|     , strength(0.005f) | ||||
|  | @ -150,7 +154,9 @@ GLCanvas3D::LayersEditing::~LayersEditing() | |||
| } | ||||
| 
 | ||||
| const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f; | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| const float GLCanvas3D::LayersEditing::THICKNESS_RESET_BUTTON_HEIGHT = 22.0f; | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) | ||||
| { | ||||
|  | @ -217,13 +223,103 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const | |||
|     if (!m_enabled) | ||||
|         return; | ||||
| 
 | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     static const ImVec4 orange(0.757f, 0.404f, 0.216f, 1.0f); | ||||
| 
 | ||||
|     const Size& cnv_size = canvas.get_canvas_size(); | ||||
|     float canvas_w = (float)cnv_size.get_width(); | ||||
|     float canvas_h = (float)cnv_size.get_height(); | ||||
| 
 | ||||
|     ImGuiWrapper& imgui = *wxGetApp().imgui(); | ||||
|     imgui.set_next_window_pos(canvas_w - imgui.get_style_scaling() * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); | ||||
|     imgui.set_next_window_bg_alpha(0.5f); | ||||
| 
 | ||||
|     ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); | ||||
| 
 | ||||
|     imgui.begin(_(L("Layer height profile")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); | ||||
| 
 | ||||
|     ImGui::PushStyleColor(ImGuiCol_Text, orange); | ||||
|     imgui.text(_(L("Left mouse button:"))); | ||||
|     ImGui::PopStyleColor(); | ||||
|     ImGui::SameLine(); | ||||
|     imgui.text(_(L("Add detail"))); | ||||
| 
 | ||||
|     ImGui::PushStyleColor(ImGuiCol_Text, orange); | ||||
|     imgui.text(_(L("Right mouse button:"))); | ||||
|     ImGui::PopStyleColor(); | ||||
|     ImGui::SameLine(); | ||||
|     imgui.text(_(L("Remove detail"))); | ||||
| 
 | ||||
|     ImGui::PushStyleColor(ImGuiCol_Text, orange); | ||||
|     imgui.text(_(L("Shift + Left mouse button:"))); | ||||
|     ImGui::PopStyleColor(); | ||||
|     ImGui::SameLine(); | ||||
|     imgui.text(_(L("Reset to base"))); | ||||
| 
 | ||||
|     ImGui::PushStyleColor(ImGuiCol_Text, orange); | ||||
|     imgui.text(_(L("Shift + Right mouse button:"))); | ||||
|     ImGui::PopStyleColor(); | ||||
|     ImGui::SameLine(); | ||||
|     imgui.text(_(L("Smoothing"))); | ||||
| 
 | ||||
|     ImGui::PushStyleColor(ImGuiCol_Text, orange); | ||||
|     imgui.text(_(L("Mouse wheel:"))); | ||||
|     ImGui::PopStyleColor(); | ||||
|     ImGui::SameLine(); | ||||
|     imgui.text(_(L("Increase/decrease edit area"))); | ||||
|      | ||||
|     ImGui::Separator(); | ||||
|     if (imgui.button(_(L("Adaptive")))) | ||||
|         wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event<float>(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_cusp)); | ||||
| 
 | ||||
|     ImGui::SameLine(); | ||||
|     float text_align = ImGui::GetCursorPosX(); | ||||
|     imgui.text(_(L("Cusp (mm)"))); | ||||
|     ImGui::SameLine(); | ||||
|     float widget_align = ImGui::GetCursorPosX(); | ||||
|     ImGui::PushItemWidth(120.0f); | ||||
|     m_adaptive_cusp = std::min(m_adaptive_cusp, (float)m_slicing_parameters->max_layer_height); | ||||
|     ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f"); | ||||
| 
 | ||||
|     ImGui::Separator(); | ||||
|     if (imgui.button(_(L("Smooth")))) | ||||
|         wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), HeightProfileSmoothEvent(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_params )); | ||||
| 
 | ||||
|     ImGui::SameLine(); | ||||
|     ImGui::SetCursorPosX(text_align); | ||||
|     imgui.text(_(L("Radius"))); | ||||
|     ImGui::SameLine(); | ||||
|     ImGui::PushItemWidth(120.0f); | ||||
|     ImGui::SetCursorPosX(widget_align); | ||||
|     int radius = (int)m_smooth_params.radius; | ||||
|     if (ImGui::SliderInt("##1", &radius, 1, 10)) | ||||
|         m_smooth_params.radius = (unsigned int)radius; | ||||
| 
 | ||||
|     ImGui::SetCursorPosX(text_align); | ||||
|     imgui.text(_(L("Keep min"))); | ||||
|     ImGui::SameLine(); | ||||
|     ImGui::PushItemWidth(120.0f); | ||||
|     ImGui::SetCursorPosX(widget_align); | ||||
|     imgui.checkbox("##2", m_smooth_params.keep_min); | ||||
| 
 | ||||
|     ImGui::Separator(); | ||||
|     if (imgui.button(_(L("Reset")))) | ||||
|         wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); | ||||
| 
 | ||||
|     imgui.end(); | ||||
| 
 | ||||
|     ImGui::PopStyleVar(); | ||||
| 
 | ||||
|     const Rect& bar_rect = get_bar_rect_viewport(canvas); | ||||
| #else | ||||
|     const Rect& bar_rect = get_bar_rect_viewport(canvas); | ||||
|     const Rect& reset_rect = get_reset_rect_viewport(canvas); | ||||
| 
 | ||||
|     _render_tooltip_texture(canvas, bar_rect, reset_rect); | ||||
|     _render_reset_texture(reset_rect); | ||||
|     _render_active_object_annotations(canvas, bar_rect); | ||||
|     _render_profile(bar_rect); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|     render_active_object_annotations(canvas, bar_rect); | ||||
|     render_profile(bar_rect); | ||||
| } | ||||
| 
 | ||||
| float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) | ||||
|  | @ -248,11 +344,13 @@ bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, floa | |||
|     return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); | ||||
| } | ||||
| 
 | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| bool GLCanvas3D::LayersEditing::reset_rect_contains(const GLCanvas3D& canvas, float x, float y) | ||||
| { | ||||
|     const Rect& rect = get_reset_rect_screen(canvas); | ||||
|     return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); | ||||
| } | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) | ||||
| { | ||||
|  | @ -260,9 +358,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) | |||
|     float w = (float)cnv_size.get_width(); | ||||
|     float h = (float)cnv_size.get_height(); | ||||
| 
 | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     return Rect(w - thickness_bar_width(canvas), 0.0f, w, h); | ||||
| #else | ||||
|     return Rect(w - thickness_bar_width(canvas), 0.0f, w, h - reset_button_height(canvas)); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| } | ||||
| 
 | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas) | ||||
| { | ||||
|     const Size& cnv_size = canvas.get_canvas_size(); | ||||
|  | @ -271,6 +374,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas) | |||
| 
 | ||||
|     return Rect(w - thickness_bar_width(canvas), h - reset_button_height(canvas), w, h); | ||||
| } | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) | ||||
| { | ||||
|  | @ -281,9 +385,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) | |||
|     float zoom = (float)canvas.get_camera().get_zoom(); | ||||
|     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; | ||||
| 
 | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); | ||||
| #else | ||||
|     return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| } | ||||
| 
 | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas) | ||||
| { | ||||
|     const Size& cnv_size = canvas.get_canvas_size(); | ||||
|  | @ -295,13 +404,14 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas | |||
| 
 | ||||
|     return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); | ||||
| } | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| 
 | ||||
| bool GLCanvas3D::LayersEditing::_is_initialized() const | ||||
| bool GLCanvas3D::LayersEditing::is_initialized() const | ||||
| { | ||||
|     return m_shader.is_initialized(); | ||||
| } | ||||
| 
 | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const | ||||
| { | ||||
|     // TODO: do this with ImGui
 | ||||
|  | @ -347,8 +457,9 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co | |||
| 
 | ||||
|     GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); | ||||
| } | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const | ||||
| void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const | ||||
| { | ||||
|     m_shader.start_using(); | ||||
| 
 | ||||
|  | @ -379,7 +490,7 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas | |||
|     m_shader.stop_using(); | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::LayersEditing::_render_profile(const Rect& bar_rect) const | ||||
| void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) const | ||||
| { | ||||
|     //FIXME show some kind of legend.
 | ||||
| 
 | ||||
|  | @ -496,6 +607,24 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas) | |||
|     canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp) | ||||
| { | ||||
|     m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, cusp); | ||||
|     const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile; | ||||
|     m_layers_texture.valid = false; | ||||
|     canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params) | ||||
| { | ||||
|     m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, smoothing_params); | ||||
|     const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile; | ||||
|     m_layers_texture.valid = false; | ||||
|     canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); | ||||
| } | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| void GLCanvas3D::LayersEditing::generate_layer_height_texture() | ||||
| { | ||||
| 	this->update_slicing_parameters(); | ||||
|  | @ -557,6 +686,7 @@ float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D &canvas) | |||
|          * THICKNESS_BAR_WIDTH; | ||||
| } | ||||
| 
 | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas) | ||||
| { | ||||
|     return | ||||
|  | @ -567,6 +697,7 @@ float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas) | |||
| #endif | ||||
|          * THICKNESS_RESET_BUTTON_HEIGHT; | ||||
| } | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| 
 | ||||
| const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); | ||||
|  | @ -1118,6 +1249,11 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); | |||
| wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); | ||||
| wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); | ||||
| wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); | ||||
| wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>); | ||||
| wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
| const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25; | ||||
|  | @ -1372,7 +1508,7 @@ void GLCanvas3D::set_model(Model* model) | |||
| 
 | ||||
| void GLCanvas3D::bed_shape_changed() | ||||
| { | ||||
|     m_camera.set_scene_box(scene_bounding_box()); | ||||
|     refresh_camera_scene_box(); | ||||
|     m_camera.requires_zoom_to_bed = true; | ||||
|     m_dirty = true; | ||||
|     if (m_bed.is_prusa()) | ||||
|  | @ -1398,7 +1534,7 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const | |||
| BoundingBoxf3 GLCanvas3D::scene_bounding_box() const | ||||
| { | ||||
|     BoundingBoxf3 bb = volumes_bounding_box(); | ||||
|     bb.merge(m_bed.get_bounding_box(false)); | ||||
|     bb.merge(m_bed.get_bounding_box(true)); | ||||
| 
 | ||||
|     if (m_config != nullptr) | ||||
|     { | ||||
|  | @ -1420,6 +1556,29 @@ bool GLCanvas3D::is_layers_editing_allowed() const | |||
|     return m_layers_editing.is_allowed(); | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| void GLCanvas3D::reset_layer_height_profile() | ||||
| { | ||||
|     m_layers_editing.reset_layer_height_profile(*this); | ||||
|     m_layers_editing.state = LayersEditing::Completed; | ||||
|     m_dirty = true; | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::adaptive_layer_height_profile(float cusp) | ||||
| { | ||||
|     m_layers_editing.adaptive_layer_height_profile(*this, cusp); | ||||
|     m_layers_editing.state = LayersEditing::Completed; | ||||
|     m_dirty = true; | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params) | ||||
| { | ||||
|     m_layers_editing.smooth_layer_height_profile(*this, smoothing_params); | ||||
|     m_layers_editing.state = LayersEditing::Completed; | ||||
|     m_dirty = true; | ||||
| } | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| bool GLCanvas3D::is_reload_delayed() const | ||||
| { | ||||
|     return m_reload_delayed; | ||||
|  | @ -1546,10 +1705,11 @@ void GLCanvas3D::render() | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const Size& cnv_size = get_canvas_size(); | ||||
| 
 | ||||
|     if (m_camera.requires_zoom_to_bed) | ||||
|     { | ||||
|         zoom_to_bed(); | ||||
|         const Size& cnv_size = get_canvas_size(); | ||||
|         _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); | ||||
|         m_camera.requires_zoom_to_bed = false; | ||||
|     } | ||||
|  | @ -1640,6 +1800,8 @@ void GLCanvas3D::render() | |||
|     m_camera.debug_render(); | ||||
| #endif // ENABLE_CAMERA_STATISTICS
 | ||||
| 
 | ||||
|     wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); | ||||
| 
 | ||||
|     wxGetApp().imgui()->render(); | ||||
| 
 | ||||
|     m_canvas->SwapBuffers(); | ||||
|  | @ -2098,7 +2260,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|         post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false)); | ||||
|     } | ||||
| 
 | ||||
|     m_camera.set_scene_box(scene_bounding_box()); | ||||
|     refresh_camera_scene_box(); | ||||
| 
 | ||||
|     if (m_selection.is_empty()) | ||||
|     { | ||||
|  | @ -2338,14 +2500,21 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) | |||
|     m_dirty |= m_main_toolbar.update_items_state(); | ||||
|     m_dirty |= m_undoredo_toolbar.update_items_state(); | ||||
|     m_dirty |= m_view_toolbar.update_items_state(); | ||||
|     bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); | ||||
|     m_dirty |= mouse3d_controller_applied; | ||||
| 
 | ||||
|     if (!m_dirty) | ||||
|         return; | ||||
| 
 | ||||
|     _refresh_if_shown_on_screen(); | ||||
| 
 | ||||
|     if (m_keep_dirty) | ||||
|     if (m_keep_dirty || mouse3d_controller_applied) | ||||
|     { | ||||
|         m_dirty = true; | ||||
|         evt.RequestMore(); | ||||
|     } | ||||
|     else | ||||
|         m_dirty = false; | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::on_char(wxKeyEvent& evt) | ||||
|  | @ -2390,6 +2559,20 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) | |||
| #endif /* __APPLE__ */ | ||||
|             post_event(SimpleEvent(EVT_GLTOOLBAR_COPY)); | ||||
|         break; | ||||
| 
 | ||||
| #ifdef __APPLE__ | ||||
|         case 'm': | ||||
|         case 'M': | ||||
| #else /* __APPLE__ */ | ||||
|         case WXK_CONTROL_M: | ||||
| #endif /* __APPLE__ */ | ||||
|             { | ||||
|                 Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); | ||||
|                 controller.show_settings_dialog(!controller.is_settings_dialog_shown()); | ||||
|                 m_dirty = true; | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
| #ifdef __APPLE__ | ||||
|         case 'v': | ||||
|         case 'V': | ||||
|  | @ -2457,11 +2640,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) | |||
|         case 'B': | ||||
|         case 'b': { zoom_to_bed(); break; } | ||||
|         case 'I': | ||||
|         case 'i': { set_camera_zoom(1.0); break; } | ||||
|         case 'i': { _update_camera_zoom(1.0); break; } | ||||
|         case 'K': | ||||
|         case 'k': { m_camera.select_next_type(); m_dirty = true; break; } | ||||
|         case 'O': | ||||
|         case 'o': { set_camera_zoom(-1.0); break; } | ||||
|         case 'o': { _update_camera_zoom(-1.0); break; } | ||||
| #if ENABLE_RENDER_PICKING_PASS | ||||
|         case 'T': | ||||
|         case 't': { | ||||
|  | @ -2564,6 +2747,11 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) | |||
| 
 | ||||
| void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) | ||||
| { | ||||
|     // try to filter out events coming from mouse 3d 
 | ||||
|     Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); | ||||
|     if (controller.process_mouse_wheel()) | ||||
|         return; | ||||
| 
 | ||||
|     if (!m_initialized) | ||||
|         return; | ||||
| 
 | ||||
|  | @ -2608,7 +2796,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) | |||
|         return; | ||||
| 
 | ||||
|     // Calculate the zoom delta and apply it to the current zoom factor
 | ||||
|     set_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); | ||||
|     _update_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::on_timer(wxTimerEvent& evt) | ||||
|  | @ -2800,6 +2988,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) | |||
|             m_layers_editing.state = LayersEditing::Editing; | ||||
|             _perform_layer_editing_action(&evt); | ||||
|         } | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|         else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos(0), pos(1))) | ||||
|         { | ||||
|             if (evt.LeftDown()) | ||||
|  | @ -2812,6 +3001,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) | |||
|                 m_dirty = true; | ||||
|             } | ||||
|         } | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|         else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) | ||||
|         { | ||||
|             if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports) | ||||
|  | @ -3391,13 +3581,6 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) | |||
|     m_dirty = true; | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::set_camera_zoom(double zoom) | ||||
| { | ||||
|     const Size& cnv_size = get_canvas_size(); | ||||
|     m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height()); | ||||
|     m_dirty = true; | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::update_gizmos_on_off_state() | ||||
| { | ||||
|     set_as_dirty(); | ||||
|  | @ -4183,8 +4366,6 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) | |||
| 
 | ||||
|     // updates camera
 | ||||
|     m_camera.apply_viewport(0, 0, w, h); | ||||
| 
 | ||||
|     m_dirty = false; | ||||
| } | ||||
| 
 | ||||
| BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const | ||||
|  | @ -4221,6 +4402,12 @@ void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box) | |||
| } | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| 
 | ||||
| void GLCanvas3D::_update_camera_zoom(double zoom) | ||||
| { | ||||
|     m_camera.update_zoom(zoom); | ||||
|     m_dirty = true; | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::_refresh_if_shown_on_screen() | ||||
| { | ||||
|     if (_is_shown_on_screen()) | ||||
|  |  | |||
|  | @ -81,6 +81,8 @@ template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>; | |||
| using Vec3dEvent = Event<Vec3d>; | ||||
| template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>; | ||||
| 
 | ||||
| using HeightProfileSmoothEvent = Event<HeightProfileSmoothingParams>; | ||||
| 
 | ||||
| wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); | ||||
| wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); | ||||
| wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent); | ||||
|  | @ -104,6 +106,11 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); | |||
| wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); | ||||
| wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); | ||||
| wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
| wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); | ||||
| wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>); | ||||
| wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
| class GLCanvas3D | ||||
| { | ||||
|  | @ -153,13 +160,17 @@ private: | |||
| 
 | ||||
|     private: | ||||
|         static const float THICKNESS_BAR_WIDTH; | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|         static const float THICKNESS_RESET_BUTTON_HEIGHT; | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
|         bool                        m_enabled; | ||||
|         Shader                      m_shader; | ||||
|         unsigned int                m_z_texture_id; | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|         mutable GLTexture           m_tooltip_texture; | ||||
|         mutable GLTexture           m_reset_texture; | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|         // Not owned by LayersEditing.
 | ||||
|         const DynamicPrintConfig   *m_config; | ||||
|         // ModelObject for the currently selected object (Model::objects[last_object_id]).
 | ||||
|  | @ -171,6 +182,11 @@ private: | |||
|         std::vector<coordf_t>       m_layer_height_profile; | ||||
|         bool                        m_layer_height_profile_modified; | ||||
| 
 | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|         mutable float               m_adaptive_cusp; | ||||
|         mutable HeightProfileSmoothingParams m_smooth_params; | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
|         class LayersTexture | ||||
|         { | ||||
|         public: | ||||
|  | @ -217,28 +233,42 @@ private: | |||
| 		void adjust_layer_height_profile(); | ||||
| 		void accept_changes(GLCanvas3D& canvas); | ||||
|         void reset_layer_height_profile(GLCanvas3D& canvas); | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|         void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp); | ||||
|         void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_paramsn); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
|         static float get_cursor_z_relative(const GLCanvas3D& canvas); | ||||
|         static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|         static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y); | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|         static Rect get_bar_rect_screen(const GLCanvas3D& canvas); | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|         static Rect get_reset_rect_screen(const GLCanvas3D& canvas); | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|         static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|         static Rect get_reset_rect_viewport(const GLCanvas3D& canvas); | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
|         float object_max_z() const { return m_object_max_z; } | ||||
| 
 | ||||
|     private: | ||||
|         bool _is_initialized() const; | ||||
|         bool is_initialized() const; | ||||
|         void generate_layer_height_texture(); | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|         void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; | ||||
|         void _render_reset_texture(const Rect& reset_rect) const; | ||||
|         void _render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const; | ||||
|         void _render_profile(const Rect& bar_rect) const; | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|         void render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const; | ||||
|         void render_profile(const Rect& bar_rect) const; | ||||
|         void update_slicing_parameters(); | ||||
| 
 | ||||
|         static float thickness_bar_width(const GLCanvas3D &canvas); | ||||
| #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|         static float reset_button_height(const GLCanvas3D &canvas); | ||||
| #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
|     }; | ||||
| 
 | ||||
|     struct Mouse | ||||
|  | @ -493,6 +523,7 @@ public: | |||
|     void set_color_by(const std::string& value); | ||||
| 
 | ||||
|     const Camera& get_camera() const { return m_camera; } | ||||
|     Camera& get_camera() { return m_camera; } | ||||
| 
 | ||||
|     BoundingBoxf3 volumes_bounding_box() const; | ||||
|     BoundingBoxf3 scene_bounding_box() const; | ||||
|  | @ -500,6 +531,12 @@ public: | |||
|     bool is_layers_editing_enabled() const; | ||||
|     bool is_layers_editing_allowed() const; | ||||
| 
 | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     void reset_layer_height_profile(); | ||||
|     void adaptive_layer_height_profile(float cusp); | ||||
|     void smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
|     bool is_reload_delayed() const; | ||||
| 
 | ||||
|     void enable_layers_editing(bool enable); | ||||
|  | @ -576,8 +613,6 @@ public: | |||
|     void do_flatten(const Vec3d& normal, const std::string& snapshot_type); | ||||
|     void do_mirror(const std::string& snapshot_type); | ||||
| 
 | ||||
|     void set_camera_zoom(double zoom); | ||||
| 
 | ||||
|     void update_gizmos_on_off_state(); | ||||
|     void reset_all_gizmos() { m_gizmos.reset_all_states(); } | ||||
| 
 | ||||
|  | @ -655,6 +690,7 @@ private: | |||
| #else | ||||
|     void _zoom_to_box(const BoundingBoxf3& box); | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|     void _update_camera_zoom(double zoom); | ||||
| 
 | ||||
|     void _refresh_if_shown_on_screen(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -528,6 +528,9 @@ void ImGuiWrapper::init_style() | |||
|     // Slider
 | ||||
|     set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK); | ||||
|     set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT); | ||||
| 
 | ||||
|     // Separator
 | ||||
|     set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT); | ||||
| } | ||||
| 
 | ||||
| void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) | ||||
|  |  | |||
|  | @ -157,6 +157,7 @@ void KBShortcutsDialog::fill_shortcuts() | |||
|     plater_shortcuts.push_back(Shortcut("Z",        L("Zoom to selected object"))); | ||||
|     plater_shortcuts.push_back(Shortcut("I",        L("Zoom in"))); | ||||
|     plater_shortcuts.push_back(Shortcut("O",        L("Zoom out"))); | ||||
|     plater_shortcuts.push_back(Shortcut(ctrl+"M",   L("Show/Hide 3Dconnexion devices settings dialog"))); | ||||
|     plater_shortcuts.push_back(Shortcut("ESC",      L("Unselect gizmo / Clear selection"))); | ||||
| #if ENABLE_RENDER_PICKING_PASS | ||||
|     // Don't localize debugging texts.
 | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ | |||
| #include "PrintHostDialogs.hpp" | ||||
| #include "wxExtensions.hpp" | ||||
| #include "GUI_ObjectList.hpp" | ||||
| #include "Mouse3DController.hpp" | ||||
| #include "I18N.hpp" | ||||
| 
 | ||||
| #include <fstream> | ||||
|  |  | |||
							
								
								
									
										812
									
								
								src/slic3r/GUI/Mouse3DController.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										812
									
								
								src/slic3r/GUI/Mouse3DController.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,812 @@ | |||
| #include "libslic3r/libslic3r.h" | ||||
| #include "Mouse3DController.hpp" | ||||
| 
 | ||||
| #include "Camera.hpp" | ||||
| #include "GUI_App.hpp" | ||||
| #include "PresetBundle.hpp" | ||||
| #include "AppConfig.hpp" | ||||
| 
 | ||||
| #include <wx/glcanvas.h> | ||||
| 
 | ||||
| #include <boost/nowide/convert.hpp> | ||||
| #include <boost/log/trivial.hpp> | ||||
| #include "I18N.hpp" | ||||
| 
 | ||||
| #include <bitset> | ||||
| 
 | ||||
| // WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules
 | ||||
| 
 | ||||
| static const std::vector<int> _3DCONNEXION_VENDORS = | ||||
| { | ||||
|     0x046d,  // LOGITECH = 1133 // Logitech (3Dconnexion is made by Logitech)
 | ||||
|     0x256F   // 3DCONNECTION = 9583 // 3Dconnexion
 | ||||
| }; | ||||
| 
 | ||||
| // See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202
 | ||||
| static const std::vector<int> _3DCONNEXION_DEVICES = | ||||
| { | ||||
|     0xc603,	/* 50691 spacemouse plus XT */ | ||||
|     0xc605,	/* 50693 cadman */ | ||||
|     0xc606,	/* 50694 spacemouse classic */ | ||||
|     0xc621,	/* 50721 spaceball 5000 */ | ||||
|     0xc623,	/* 50723 space traveller */ | ||||
|     0xc625,	/* 50725 space pilot */ | ||||
|     0xc626,	/* 50726 space navigator *TESTED* */ | ||||
|     0xc627,	/* 50727 space explorer */ | ||||
|     0xc628,	/* 50728 space navigator for notebooks*/ | ||||
|     0xc629,	/* 50729 space pilot pro*/ | ||||
|     0xc62b,	/* 50731 space mouse pro*/ | ||||
|     0xc62e,	/* 50734 spacemouse wireless (USB cable) *TESTED* */ | ||||
|     0xc62f,	/* 50735 spacemouse wireless receiver */ | ||||
|     0xc631,	/* 50737 spacemouse pro wireless *TESTED* */ | ||||
|     0xc632,	/* 50738 spacemouse pro wireless receiver */ | ||||
|     0xc633,	/* 50739 spacemouse enterprise */ | ||||
|     0xc635,	/* 50741 spacemouse compact *TESTED* */ | ||||
|     0xc636,	/* 50742 spacemouse module */ | ||||
|     0xc640,	/* 50752 nulooq */ | ||||
|     0xc652, /* 50770 3Dconnexion universal receiver *TESTED* */ | ||||
| }; | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace GUI { | ||||
|      | ||||
| const double Mouse3DController::State::DefaultTranslationScale = 2.5; | ||||
| const double Mouse3DController::State::MaxTranslationDeadzone = 0.2; | ||||
| const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone; | ||||
| const float Mouse3DController::State::DefaultRotationScale = 1.0f; | ||||
| const float Mouse3DController::State::MaxRotationDeadzone = (float)Mouse3DController::State::MaxTranslationDeadzone; | ||||
| const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone; | ||||
| 
 | ||||
| Mouse3DController::State::State() | ||||
|     : m_buttons_enabled(false) | ||||
|     , m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) | ||||
|     , m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) | ||||
|     , m_mouse_wheel_counter(0) | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|     , m_translation_queue_max_size(0) | ||||
|     , m_rotation_queue_max_size(0) | ||||
|     , m_buttons_queue_max_size(0) | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void Mouse3DController::State::append_translation(const Vec3d& translation) | ||||
| { | ||||
|     while (m_translation.queue.size() >= m_translation.max_size) | ||||
|     { | ||||
|         m_translation.queue.pop(); | ||||
|     } | ||||
|     m_translation.queue.push(translation); | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|     m_translation_queue_max_size = std::max(m_translation_queue_max_size, m_translation.queue.size()); | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
| } | ||||
| 
 | ||||
| void Mouse3DController::State::append_rotation(const Vec3f& rotation) | ||||
| { | ||||
|     while (m_rotation.queue.size() >= m_rotation.max_size) | ||||
|     { | ||||
|         m_rotation.queue.pop(); | ||||
|     } | ||||
|     m_rotation.queue.push(rotation); | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|     m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, m_rotation.queue.size()); | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|     if (rotation(0) != 0.0f) | ||||
|         ++m_mouse_wheel_counter; | ||||
| } | ||||
| 
 | ||||
| void Mouse3DController::State::append_button(unsigned int id) | ||||
| { | ||||
|     if (!m_buttons_enabled) | ||||
|         return; | ||||
| 
 | ||||
|     m_buttons.push(id); | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|     m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, m_buttons.size()); | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
| } | ||||
| 
 | ||||
| bool Mouse3DController::State::process_mouse_wheel() | ||||
| { | ||||
|     if (m_mouse_wheel_counter == 0) | ||||
|         return false; | ||||
|     else if (!m_rotation.queue.empty()) | ||||
|     { | ||||
|         --m_mouse_wheel_counter; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     m_mouse_wheel_counter = 0; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void Mouse3DController::State::set_queues_max_size(size_t size) | ||||
| { | ||||
|     if (size > 0) | ||||
|     { | ||||
|         m_translation.max_size = size; | ||||
|         m_rotation.max_size = size; | ||||
| 
 | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|         m_translation_queue_max_size = 0; | ||||
|         m_rotation_queue_max_size = 0; | ||||
|         m_buttons_queue_max_size = 0; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool Mouse3DController::State::apply(Camera& camera) | ||||
| { | ||||
|     if (!wxGetApp().IsActive()) | ||||
|         return false; | ||||
| 
 | ||||
|     bool ret = false; | ||||
| 
 | ||||
|     if (has_translation()) | ||||
|     { | ||||
|         const Vec3d& translation = m_translation.queue.front(); | ||||
|         camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); | ||||
|         m_translation.queue.pop(); | ||||
|         ret = true; | ||||
|     } | ||||
| 
 | ||||
|     if (has_rotation()) | ||||
|     { | ||||
|         const Vec3f& rotation = m_rotation.queue.front(); | ||||
|         float theta = m_rotation_params.scale * rotation(0); | ||||
|         float phi = m_rotation_params.scale * rotation(2); | ||||
|         float sign = camera.inverted_phi ? -1.0f : 1.0f; | ||||
|         camera.phi += sign * phi; | ||||
|         camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); | ||||
|         m_rotation.queue.pop(); | ||||
|         ret = true; | ||||
|     } | ||||
| 
 | ||||
|     if (m_buttons_enabled && has_button()) | ||||
|     { | ||||
|         unsigned int button = m_buttons.front(); | ||||
|         switch (button) | ||||
|         { | ||||
|         case 0: { camera.update_zoom(1.0); break; } | ||||
|         case 1: { camera.update_zoom(-1.0); break; } | ||||
|         default: { break; } | ||||
|         } | ||||
|         m_buttons.pop(); | ||||
|         ret = true; | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| Mouse3DController::Mouse3DController() | ||||
|     : m_initialized(false) | ||||
|     , m_device(nullptr) | ||||
|     , m_device_str("") | ||||
|     , m_running(false) | ||||
|     , m_settings_dialog(false) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void Mouse3DController::init() | ||||
| { | ||||
|     if (m_initialized) | ||||
|         return; | ||||
| 
 | ||||
|     // Initialize the hidapi library
 | ||||
|     int res = hid_init(); | ||||
|     if (res != 0) | ||||
|     { | ||||
|         BOOST_LOG_TRIVIAL(error) << "Unable to initialize hidapi library"; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     m_initialized = true; | ||||
| } | ||||
| 
 | ||||
| void Mouse3DController::shutdown() | ||||
| { | ||||
|     if (!m_initialized) | ||||
|         return; | ||||
| 
 | ||||
|     stop(); | ||||
|     disconnect_device(); | ||||
| 
 | ||||
|     // Finalize the hidapi library
 | ||||
|     hid_exit(); | ||||
|     m_initialized = false; | ||||
| } | ||||
| 
 | ||||
| bool Mouse3DController::apply(Camera& camera) | ||||
| { | ||||
|     if (!m_initialized) | ||||
|         return false; | ||||
| 
 | ||||
|     std::lock_guard<std::mutex> lock(m_mutex); | ||||
| 
 | ||||
|     // check if the user unplugged the device
 | ||||
|     if (!m_running && is_device_connected()) | ||||
|     { | ||||
|         disconnect_device(); | ||||
|         // hides the settings dialog if the user re-plug the device
 | ||||
|         m_settings_dialog = false; | ||||
|     } | ||||
| 
 | ||||
|     // check if the user plugged the device
 | ||||
|     if (connect_device()) | ||||
|         start(); | ||||
| 
 | ||||
|     return is_device_connected() ? m_state.apply(camera) : false; | ||||
| } | ||||
| 
 | ||||
| void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const | ||||
| { | ||||
|     if (!m_running || !m_settings_dialog) | ||||
|         return; | ||||
| 
 | ||||
|     ImGuiWrapper& imgui = *wxGetApp().imgui(); | ||||
| 
 | ||||
|     imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f); | ||||
|     imgui.set_next_window_bg_alpha(0.5f); | ||||
| 
 | ||||
|     ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); | ||||
| 
 | ||||
|     imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); | ||||
| 
 | ||||
|     const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator); | ||||
|     ImGui::PushStyleColor(ImGuiCol_Text, color); | ||||
|     imgui.text(_(L("Device:"))); | ||||
|     ImGui::PopStyleColor(); | ||||
|     ImGui::SameLine(); | ||||
|     imgui.text(m_device_str); | ||||
| 
 | ||||
|     ImGui::Separator(); | ||||
|     ImGui::PushStyleColor(ImGuiCol_Text, color); | ||||
|     imgui.text(_(L("Speed:"))); | ||||
|     ImGui::PopStyleColor(); | ||||
| 
 | ||||
|     float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; | ||||
|     if (ImGui::SliderFloat(_(L("Translation##1")), &translation_scale, 0.5f, 2.0f, "%.1f")) | ||||
|         m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale); | ||||
| 
 | ||||
|     float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale; | ||||
|     if (ImGui::SliderFloat(_(L("Rotation##1")), &rotation_scale, 0.5f, 2.0f, "%.1f")) | ||||
|         m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale); | ||||
| 
 | ||||
|     ImGui::Separator(); | ||||
|     ImGui::PushStyleColor(ImGuiCol_Text, color); | ||||
|     imgui.text(_(L("Deadzone:"))); | ||||
|     ImGui::PopStyleColor(); | ||||
| 
 | ||||
|     float translation_deadzone = (float)m_state.get_translation_deadzone(); | ||||
|     if (ImGui::SliderFloat(_(L("Translation##2")), &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f")) | ||||
|         m_state.set_translation_deadzone((double)translation_deadzone); | ||||
| 
 | ||||
|     float rotation_deadzone = m_state.get_rotation_deadzone(); | ||||
|     if (ImGui::SliderFloat(_(L("Rotation##2")), &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f")) | ||||
|         m_state.set_rotation_deadzone(rotation_deadzone); | ||||
| 
 | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|     ImGui::Separator(); | ||||
|     ImGui::Separator(); | ||||
|     ImGui::PushStyleColor(ImGuiCol_Text, color); | ||||
|     imgui.text("DEBUG:"); | ||||
|     imgui.text("Vectors:"); | ||||
|     ImGui::PopStyleColor(); | ||||
|     Vec3f translation = m_state.get_translation().cast<float>(); | ||||
|     Vec3f rotation = m_state.get_rotation(); | ||||
|     ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); | ||||
|     ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); | ||||
| 
 | ||||
|     ImGui::PushStyleColor(ImGuiCol_Text, color); | ||||
|     imgui.text("Queue size:"); | ||||
|     ImGui::PopStyleColor(); | ||||
| 
 | ||||
|     int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() }; | ||||
|     int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() }; | ||||
|     int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() }; | ||||
| 
 | ||||
|     ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly); | ||||
|     ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); | ||||
|     ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); | ||||
| 
 | ||||
|     int queue_size = (int)m_state.get_queues_max_size(); | ||||
|     if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly)) | ||||
|     { | ||||
|         if (queue_size > 0) | ||||
|             m_state.set_queues_max_size(queue_size); | ||||
|     } | ||||
| 
 | ||||
|     ImGui::Separator(); | ||||
|     ImGui::PushStyleColor(ImGuiCol_Text, color); | ||||
|     imgui.text("Camera:"); | ||||
|     ImGui::PopStyleColor(); | ||||
|     Vec3f target = wxGetApp().plater()->get_camera().get_target().cast<float>(); | ||||
|     ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
| 
 | ||||
|     imgui.end(); | ||||
| 
 | ||||
|     ImGui::PopStyleVar(); | ||||
| } | ||||
| 
 | ||||
| bool Mouse3DController::connect_device() | ||||
| { | ||||
|     if (is_device_connected()) | ||||
|         return false; | ||||
| 
 | ||||
|     // Enumerates devices
 | ||||
|     hid_device_info* devices = hid_enumerate(0, 0); | ||||
|     if (devices == nullptr) | ||||
|     { | ||||
|         BOOST_LOG_TRIVIAL(error) << "Unable to enumerate HID devices"; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // Searches for 1st connected 3Dconnexion device
 | ||||
|     struct DeviceData | ||||
|     { | ||||
|         std::string path; | ||||
|         unsigned short usage_page; | ||||
|         unsigned short usage; | ||||
| 
 | ||||
|         DeviceData() | ||||
|             : path(""), usage_page(0), usage(0) | ||||
|         {} | ||||
|         DeviceData(const std::string& path, unsigned short usage_page, unsigned short usage) | ||||
|             : path(path), usage_page(usage_page), usage(usage) | ||||
|         {} | ||||
| 
 | ||||
|         bool has_valid_usage() const { return (usage_page == 1) && (usage == 8); } | ||||
|     }; | ||||
| 
 | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|     hid_device_info* cur = devices; | ||||
|     std::cout << std::endl << "======================================================================================================================================" << std::endl; | ||||
|     std::cout << "Detected devices:" << std::endl; | ||||
|     while (cur != nullptr) | ||||
|     { | ||||
|         std::cout << "\""; | ||||
|         std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); | ||||
|         std::cout << "/"; | ||||
|         std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); | ||||
|         std::cout << "\" code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; | ||||
|         std::cout << " serial number: '"; | ||||
|         std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); | ||||
|         std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; | ||||
| 
 | ||||
|         cur = cur->next; | ||||
|     } | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
| 
 | ||||
|     // When using 3Dconnexion universal receiver, multiple devices are detected sharing the same vendor_id and product_id.
 | ||||
|     // To choose from them the right one we use:
 | ||||
|     // On Windows and Mac: usage_page == 1 and usage == 8
 | ||||
|     // On Linux: as usage_page and usage are not defined (see hidapi.h) we try all detected devices until one is succesfully open
 | ||||
|     // When only a single device is detected, as for wired connections, vendor_id and product_id are enough
 | ||||
| 
 | ||||
|     // First we count all the valid devices from the enumerated list,
 | ||||
| 
 | ||||
|     hid_device_info* current = devices; | ||||
|     typedef std::pair<unsigned short, unsigned short> DeviceIds; | ||||
|     typedef std::vector<DeviceData> DeviceDataList; | ||||
|     typedef std::map<DeviceIds, DeviceDataList> DetectedDevices; | ||||
|     DetectedDevices detected_devices; | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|     std::cout << std::endl << "Detected 3D connexion devices:" << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|     while (current != nullptr) | ||||
|     { | ||||
|         unsigned short vendor_id = 0; | ||||
|         unsigned short product_id = 0; | ||||
| 
 | ||||
|         for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i) | ||||
|         { | ||||
|             if (_3DCONNEXION_VENDORS[i] == current->vendor_id) | ||||
|             { | ||||
|                 vendor_id = current->vendor_id; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (vendor_id != 0) | ||||
|         { | ||||
|             for (size_t i = 0; i < _3DCONNEXION_DEVICES.size(); ++i) | ||||
|             { | ||||
|                 if (_3DCONNEXION_DEVICES[i] == current->product_id) | ||||
|                 { | ||||
|                     product_id = current->product_id; | ||||
|                     DeviceIds detected_device(vendor_id, product_id); | ||||
|                     DetectedDevices::iterator it = detected_devices.find(detected_device); | ||||
|                     if (it == detected_devices.end()) | ||||
|                         it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first; | ||||
| 
 | ||||
|                     it->second.emplace_back(current->path, current->usage_page, current->usage); | ||||
| 
 | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|                     std::wcout << "\"" << ((current->manufacturer_string != nullptr) ? current->manufacturer_string : L"Unknown"); | ||||
|                     std::cout << "/"; | ||||
|                     std::wcout << ((current->product_string != nullptr) ? current->product_string : L"Unknown"); | ||||
|                     std::cout << "\" code: " << current->vendor_id << "/" << current->product_id << " (" << std::hex << current->vendor_id << "/" << current->product_id << std::dec << ")"; | ||||
|                     std::cout << " serial number: '"; | ||||
|                     std::wcout << ((current->serial_number != nullptr) ? current->serial_number : L"Unknown"); | ||||
|                     std::cout << "' usage page: " << current->usage_page << " usage: " << current->usage << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         current = current->next; | ||||
|     } | ||||
| 
 | ||||
|     // Free enumerated devices
 | ||||
|     hid_free_enumeration(devices); | ||||
| 
 | ||||
|     if (detected_devices.empty()) | ||||
|         return false; | ||||
| 
 | ||||
|     std::string path = ""; | ||||
|     unsigned short vendor_id = 0; | ||||
|     unsigned short product_id = 0; | ||||
| 
 | ||||
|     // Then we'll decide the choosing logic to apply in dependence of the device count and operating system
 | ||||
| 
 | ||||
|     for (const DetectedDevices::value_type& device : detected_devices) | ||||
|     { | ||||
|         if (device.second.size() == 1) | ||||
|         { | ||||
| #ifdef __linux__ | ||||
|             hid_device* test_device = hid_open(device.first.first, device.first.second, nullptr); | ||||
|             if (test_device != nullptr) | ||||
|             { | ||||
|                 hid_close(test_device); | ||||
| #else | ||||
|             if (device.second.front().has_valid_usage()) | ||||
|             { | ||||
| #endif // __linux__
 | ||||
|                 vendor_id = device.first.first; | ||||
|                 product_id = device.first.second; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             bool found = false; | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|             std::cout << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|             for (const DeviceData& data : device.second) | ||||
|             { | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|                 std::cout << "Test device: " << std::hex << device.first.first << std::dec << "/" << std::hex << device.first.second << std::dec << " \"" << data.path << "\""; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
| #ifdef __linux__ | ||||
|                 hid_device* test_device = hid_open_path(data.path.c_str()); | ||||
|                 if (test_device != nullptr) | ||||
|                 { | ||||
|                     path = data.path; | ||||
|                     vendor_id = device.first.first; | ||||
|                     product_id = device.first.second; | ||||
|                     found = true; | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|                     std::cout << "-> PASSED" << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|                     hid_close(test_device); | ||||
|                     break; | ||||
|                 } | ||||
| #else | ||||
|                 if (data.has_valid_usage()) | ||||
|                 { | ||||
|                     path = data.path; | ||||
|                     vendor_id = device.first.first; | ||||
|                     product_id = device.first.second; | ||||
|                     found = true; | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|                     std::cout << "-> PASSED" << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|                     break; | ||||
|                 } | ||||
| #endif // __linux__
 | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|                 else | ||||
|                     std::cout << "-> NOT PASSED" << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|             } | ||||
| 
 | ||||
|             if (found) | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (path.empty()) | ||||
|     { | ||||
|         if ((vendor_id != 0) && (product_id != 0)) | ||||
|         { | ||||
|             // Open the 3Dconnexion device using vendor_id and product_id
 | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|             std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << " using hid_open()" << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|             m_device = hid_open(vendor_id, product_id, nullptr); | ||||
|         } | ||||
|         else | ||||
|             return false; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Open the 3Dconnexion device using the device path
 | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|         std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << "\"" << path << "\" using hid_open_path()" << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|         m_device = hid_open_path(path.c_str()); | ||||
|     } | ||||
| 
 | ||||
|     if (m_device != nullptr) | ||||
|     { | ||||
|         std::vector<wchar_t> manufacturer(1024, 0); | ||||
|         hid_get_manufacturer_string(m_device, manufacturer.data(), 1024); | ||||
|         m_device_str = boost::nowide::narrow(manufacturer.data()); | ||||
| 
 | ||||
|         std::vector<wchar_t> product(1024, 0); | ||||
|         hid_get_product_string(m_device, product.data(), 1024); | ||||
|         m_device_str += "/" + boost::nowide::narrow(product.data()); | ||||
| 
 | ||||
|         BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str; | ||||
| 
 | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|         std::cout << std::endl << "Connected device:" << std::endl; | ||||
|         std::cout << "Manufacturer/product: " << m_device_str << std::endl; | ||||
|         std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; | ||||
|         std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; | ||||
|         std::cout << "Path................: '" << path << "'" << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
| 
 | ||||
|         // get device parameters from the config, if present
 | ||||
|         double translation_speed = 1.0; | ||||
|         float rotation_speed = 1.0; | ||||
|         double translation_deadzone = State::DefaultTranslationDeadzone; | ||||
|         float rotation_deadzone = State::DefaultRotationDeadzone; | ||||
|         wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation_speed); | ||||
|         wxGetApp().app_config->get_mouse_device_translation_deadzone(m_device_str, translation_deadzone); | ||||
|         wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation_speed); | ||||
|         wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone); | ||||
|         // clamp to valid values
 | ||||
|         m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation_speed))); | ||||
|         m_state.set_translation_deadzone(std::max(0.0, std::min(State::MaxTranslationDeadzone, translation_deadzone))); | ||||
|         m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation_speed))); | ||||
|         m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone))); | ||||
|     } | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|     else | ||||
|     { | ||||
|         std::cout << std::endl << "Unable to connect to device:" << std::endl; | ||||
|         std::cout << "Manufacturer/product: " << m_device_str << std::endl; | ||||
|         std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; | ||||
|         std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; | ||||
|         std::cout << "Path................: '" << path << "'" << std::endl; | ||||
|     } | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
| 
 | ||||
|     return (m_device != nullptr); | ||||
| } | ||||
| 
 | ||||
| void Mouse3DController::disconnect_device() | ||||
| { | ||||
|     if (!is_device_connected()) | ||||
|         return; | ||||
|      | ||||
|     // Stop the secondary thread, if running
 | ||||
|     if (m_thread.joinable()) | ||||
|         m_thread.join(); | ||||
| 
 | ||||
|     // Store current device parameters into the config
 | ||||
|     wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(), | ||||
|         m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone()); | ||||
|     wxGetApp().app_config->save(); | ||||
| 
 | ||||
|     // Close the 3Dconnexion device
 | ||||
|     hid_close(m_device); | ||||
|     m_device = nullptr; | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(info) << "Disconnected device: " << m_device_str; | ||||
| 
 | ||||
|     m_device_str = ""; | ||||
| } | ||||
| 
 | ||||
| void Mouse3DController::start() | ||||
| { | ||||
|     if (!is_device_connected() || m_running) | ||||
|         return; | ||||
| 
 | ||||
|     m_thread = std::thread(&Mouse3DController::run, this); | ||||
| } | ||||
| 
 | ||||
| void Mouse3DController::run() | ||||
| { | ||||
|     m_running = true; | ||||
|     while (m_running) | ||||
|     { | ||||
|         collect_input(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Mouse3DController::collect_input() | ||||
| { | ||||
|     DataPacket packet = { 0 }; | ||||
|     int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100); | ||||
|     if (res < 0) | ||||
|     { | ||||
|         // An error occourred (device detached from pc ?)
 | ||||
|         stop(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!wxGetApp().IsActive()) | ||||
|         return; | ||||
| 
 | ||||
|     std::lock_guard<std::mutex> lock(m_mutex); | ||||
| 
 | ||||
|     bool updated = false; | ||||
| 
 | ||||
|     if (res == 7) | ||||
|         updated = handle_packet(packet); | ||||
|     else if (res == 13) | ||||
|         updated = handle_wireless_packet(packet); | ||||
|     else if ((res == 3) && (packet[0] == 3)) | ||||
|         // On Mac button packets can be 3 bytes long
 | ||||
|         updated = handle_packet(packet); | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|     else if (res > 0) | ||||
|         std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
| 
 | ||||
|     if (updated) | ||||
|         // ask for an idle event to update 3D scene
 | ||||
|         wxWakeUpIdle(); | ||||
| } | ||||
| 
 | ||||
| bool Mouse3DController::handle_packet(const DataPacket& packet) | ||||
| { | ||||
|     switch (packet[0]) | ||||
|     { | ||||
|     case 1: // Translation
 | ||||
|         { | ||||
|             if (handle_packet_translation(packet)) | ||||
|                 return true; | ||||
| 
 | ||||
|             break; | ||||
|         } | ||||
|     case 2: // Rotation
 | ||||
|         { | ||||
|             if (handle_packet_rotation(packet, 1)) | ||||
|                 return true; | ||||
| 
 | ||||
|             break; | ||||
|         } | ||||
|     case 3: // Button
 | ||||
|         { | ||||
|             if (handle_packet_button(packet, packet.size() - 1)) | ||||
|                 return true; | ||||
| 
 | ||||
|             break; | ||||
|         } | ||||
|     case 23: // Battery charge
 | ||||
|         { | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|             std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|             break; | ||||
|         } | ||||
|     default: | ||||
|         { | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|             std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) | ||||
| { | ||||
|     switch (packet[0]) | ||||
|     { | ||||
|     case 1: // Translation + Rotation
 | ||||
|         { | ||||
|             bool updated = handle_packet_translation(packet); | ||||
|             updated |= handle_packet_rotation(packet, 7); | ||||
| 
 | ||||
|             if (updated) | ||||
|                 return true; | ||||
| 
 | ||||
|             break; | ||||
|         } | ||||
|     case 3: // Button
 | ||||
|         { | ||||
|             if (handle_packet_button(packet, 12)) | ||||
|                 return true; | ||||
| 
 | ||||
|             break; | ||||
|         } | ||||
|     case 23: // Battery charge
 | ||||
|         { | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|             std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|             break; | ||||
|         } | ||||
|     default: | ||||
|         { | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|             std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| double convert_input(unsigned char first, unsigned char second, double deadzone) | ||||
| { | ||||
|     short value = first | second << 8; | ||||
|     double ret = (double)value / 350.0; | ||||
|     return (std::abs(ret) > deadzone) ? ret : 0.0; | ||||
| } | ||||
| 
 | ||||
| bool Mouse3DController::handle_packet_translation(const DataPacket& packet) | ||||
| { | ||||
|     double deadzone = m_state.get_translation_deadzone(); | ||||
|     Vec3d translation(-convert_input(packet[1], packet[2], deadzone), | ||||
|         convert_input(packet[3], packet[4], deadzone), | ||||
|         convert_input(packet[5], packet[6], deadzone)); | ||||
| 
 | ||||
|     if (!translation.isApprox(Vec3d::Zero())) | ||||
|     { | ||||
|         m_state.append_translation(translation); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte) | ||||
| { | ||||
|     double deadzone = (double)m_state.get_rotation_deadzone(); | ||||
|     Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1], deadzone), | ||||
|         (float)convert_input(packet[first_byte + 2], packet[first_byte + 3], deadzone), | ||||
|         -(float)convert_input(packet[first_byte + 4], packet[first_byte + 5], deadzone)); | ||||
| 
 | ||||
|     if (!rotation.isApprox(Vec3f::Zero())) | ||||
|     { | ||||
|         m_state.append_rotation(rotation); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size) | ||||
| { | ||||
|     unsigned int data = 0; | ||||
|     for (unsigned int i = 1; i < packet_size; ++i) | ||||
|     { | ||||
|         data |= packet[i] << 8 * (i - 1); | ||||
|     } | ||||
| 
 | ||||
|     const std::bitset<32> data_bits{ data }; | ||||
|     for (size_t i = 0; i < data_bits.size(); ++i) | ||||
|     { | ||||
|         if (data_bits.test(i)) | ||||
|         { | ||||
|             m_state.append_button((unsigned int)i); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| } // namespace GUI
 | ||||
| } // namespace Slic3r
 | ||||
							
								
								
									
										173
									
								
								src/slic3r/GUI/Mouse3DController.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/slic3r/GUI/Mouse3DController.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,173 @@ | |||
| #ifndef slic3r_Mouse3DController_hpp_ | ||||
| #define slic3r_Mouse3DController_hpp_ | ||||
| 
 | ||||
| // Enabled debug output to console and extended imgui dialog
 | ||||
| #define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 1 | ||||
| 
 | ||||
| #include "libslic3r/Point.hpp" | ||||
| 
 | ||||
| #include "hidapi.h" | ||||
| 
 | ||||
| #include <queue> | ||||
| #include <thread> | ||||
| #include <mutex> | ||||
| #include <vector> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace GUI { | ||||
| 
 | ||||
| struct Camera; | ||||
| 
 | ||||
| class Mouse3DController | ||||
| { | ||||
|     class State | ||||
|     { | ||||
|     public: | ||||
|         static const double DefaultTranslationScale; | ||||
|         static const double MaxTranslationDeadzone; | ||||
|         static const double DefaultTranslationDeadzone; | ||||
|         static const float DefaultRotationScale; | ||||
|         static const float MaxRotationDeadzone; | ||||
|         static const float DefaultRotationDeadzone; | ||||
| 
 | ||||
|     private: | ||||
|         template <typename Number> | ||||
|         struct CustomParameters | ||||
|         { | ||||
|             Number scale; | ||||
|             Number deadzone; | ||||
| 
 | ||||
|             CustomParameters(Number scale, Number deadzone) : scale(scale), deadzone(deadzone) {} | ||||
|         }; | ||||
| 
 | ||||
|         template <class T> | ||||
|         struct InputQueue | ||||
|         { | ||||
|             size_t max_size; | ||||
|             std::queue<T> queue; | ||||
| 
 | ||||
|             // The default value of 5 for max_size seems to work fine on all platforms
 | ||||
|             // The effects of changing this value can be tested by setting ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT to 1
 | ||||
|             // and playing with the imgui dialog which shows by pressing CTRL+M
 | ||||
|             InputQueue() : max_size(5) {} | ||||
|         }; | ||||
| 
 | ||||
|         InputQueue<Vec3d> m_translation; | ||||
|         InputQueue<Vec3f> m_rotation; | ||||
|         std::queue<unsigned int> m_buttons; | ||||
| 
 | ||||
|         bool m_buttons_enabled; | ||||
| 
 | ||||
|         CustomParameters<double> m_translation_params; | ||||
|         CustomParameters<float> m_rotation_params; | ||||
| 
 | ||||
|         // When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected.
 | ||||
|         // We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere
 | ||||
|         // by triggering unwanted zoom in/out of the scene
 | ||||
|         // The following variable is used to count the potential mouse wheel events triggered and is updated by: 
 | ||||
|         // Mouse3DController::collect_input() through the call to the append_rotation() method
 | ||||
|         // GLCanvas3D::on_mouse_wheel() through the call to the process_mouse_wheel() method
 | ||||
|         // GLCanvas3D::on_idle() through the call to the apply() method
 | ||||
|         unsigned int m_mouse_wheel_counter; | ||||
| 
 | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|         size_t m_translation_queue_max_size; | ||||
|         size_t m_rotation_queue_max_size; | ||||
|         size_t m_buttons_queue_max_size; | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
| 
 | ||||
|     public: | ||||
|         State(); | ||||
| 
 | ||||
|         void append_translation(const Vec3d& translation); | ||||
|         void append_rotation(const Vec3f& rotation); | ||||
|         void append_button(unsigned int id); | ||||
| 
 | ||||
|         bool has_translation() const { return !m_translation.queue.empty(); } | ||||
|         bool has_rotation() const { return !m_rotation.queue.empty(); } | ||||
|         bool has_button() const { return !m_buttons.empty(); } | ||||
| 
 | ||||
|         bool process_mouse_wheel(); | ||||
| 
 | ||||
|         double get_translation_scale() const { return m_translation_params.scale; } | ||||
|         void set_translation_scale(double scale) { m_translation_params.scale = scale; } | ||||
| 
 | ||||
|         float get_rotation_scale() const { return m_rotation_params.scale; } | ||||
|         void set_rotation_scale(float scale) { m_rotation_params.scale = scale; } | ||||
| 
 | ||||
|         double get_translation_deadzone() const { return m_translation_params.deadzone; } | ||||
|         void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; } | ||||
| 
 | ||||
|         float get_rotation_deadzone() const { return m_rotation_params.deadzone; } | ||||
|         void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; } | ||||
| 
 | ||||
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT | ||||
|         Vec3d get_translation() const { return has_translation() ? m_translation.queue.front() : Vec3d::Zero(); } | ||||
|         Vec3f get_rotation() const { return has_rotation() ? m_rotation.queue.front() : Vec3f::Zero(); } | ||||
|         unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; } | ||||
| 
 | ||||
|         unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.queue.size(); } | ||||
|         unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.queue.size(); } | ||||
|         unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); } | ||||
| 
 | ||||
|         size_t get_translation_queue_max_size() const { return m_translation_queue_max_size; } | ||||
|         size_t get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } | ||||
|         size_t get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } | ||||
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | ||||
| 
 | ||||
|         size_t get_queues_max_size() const { return m_translation.max_size; } | ||||
|         void set_queues_max_size(size_t size); | ||||
| 
 | ||||
|         // return true if any change to the camera took place
 | ||||
|         bool apply(Camera& camera); | ||||
|     }; | ||||
| 
 | ||||
|     bool m_initialized; | ||||
|     mutable State m_state; | ||||
|     std::mutex m_mutex; | ||||
|     std::thread m_thread; | ||||
|     hid_device* m_device; | ||||
|     std::string m_device_str; | ||||
|     bool m_running; | ||||
|     bool m_settings_dialog; | ||||
| 
 | ||||
| public: | ||||
|     Mouse3DController(); | ||||
| 
 | ||||
|     void init(); | ||||
|     void shutdown(); | ||||
| 
 | ||||
|     bool is_device_connected() const { return m_device != nullptr; } | ||||
|     bool is_running() const { return m_running; } | ||||
| 
 | ||||
|     bool process_mouse_wheel() { std::lock_guard<std::mutex> lock(m_mutex); return m_state.process_mouse_wheel(); } | ||||
| 
 | ||||
|     bool apply(Camera& camera); | ||||
| 
 | ||||
|     bool is_settings_dialog_shown() const { return m_settings_dialog; } | ||||
|     void show_settings_dialog(bool show) { m_settings_dialog = show && is_running(); } | ||||
|     void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const; | ||||
| 
 | ||||
| private: | ||||
|     bool connect_device(); | ||||
|     void disconnect_device(); | ||||
|     void start(); | ||||
|     void stop() { m_running = false; } | ||||
| 
 | ||||
|     // secondary thread methods
 | ||||
|     void run(); | ||||
|     void collect_input(); | ||||
| 
 | ||||
|     typedef std::array<unsigned char, 13> DataPacket; | ||||
|     bool handle_packet(const DataPacket& packet); | ||||
|     bool handle_wireless_packet(const DataPacket& packet); | ||||
|     bool handle_packet_translation(const DataPacket& packet); | ||||
|     bool handle_packet_rotation(const DataPacket& packet, unsigned int first_byte); | ||||
|     bool handle_packet_button(const DataPacket& packet, unsigned int packet_size); | ||||
| }; | ||||
| 
 | ||||
| } // namespace GUI
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif // slic3r_Mouse3DController_hpp_
 | ||||
| 
 | ||||
|  | @ -65,6 +65,7 @@ | |||
| #include "GUI_Preview.hpp" | ||||
| #include "3DBed.hpp" | ||||
| #include "Camera.hpp" | ||||
| #include "Mouse3DController.hpp" | ||||
| #include "Tab.hpp" | ||||
| #include "PresetBundle.hpp" | ||||
| #include "BackgroundSlicingProcess.hpp" | ||||
|  | @ -1387,9 +1388,6 @@ struct Plater::priv | |||
|     Slic3r::Model               model; | ||||
|     PrinterTechnology           printer_technology = ptFFF; | ||||
|     Slic3r::GCodePreviewData    gcode_preview_data; | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|     std::vector<Slic3r::ThumbnailData> thumbnail_data; | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| 
 | ||||
|     // GUI elements
 | ||||
|     wxSizer* panel_sizer{ nullptr }; | ||||
|  | @ -1398,6 +1396,7 @@ struct Plater::priv | |||
|     Sidebar *sidebar; | ||||
|     Bed3D bed; | ||||
|     Camera camera; | ||||
|     Mouse3DController mouse3d_controller; | ||||
|     View3D* view3D; | ||||
|     GLToolbar view_toolbar; | ||||
|     Preview *preview; | ||||
|  | @ -1946,6 +1945,7 @@ struct Plater::priv | |||
| 
 | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|     void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool transparent_background); | ||||
|     void generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background); | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| 
 | ||||
|     void msw_rescale_object_menu(); | ||||
|  | @ -2016,7 +2016,15 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) | |||
|     background_process.set_sla_print(&sla_print); | ||||
|     background_process.set_gcode_preview_data(&gcode_preview_data); | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|     background_process.set_thumbnail_data(&thumbnail_data); | ||||
|     background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background) | ||||
|         { | ||||
|             std::packaged_task<void(ThumbnailsList&, const Vec2ds&, bool, bool, bool)> task([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background) { | ||||
|                 generate_thumbnails(thumbnails, sizes, printable_only, parts_only, transparent_background); | ||||
|                 }); | ||||
|             std::future<void> result = task.get_future(); | ||||
|             wxTheApp->CallAfter([&]() { task(thumbnails, sizes, printable_only, parts_only, transparent_background); }); | ||||
|             result.wait(); | ||||
|         }); | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|     background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED); | ||||
|     background_process.set_finished_event(EVT_PROCESS_COMPLETED); | ||||
|  | @ -2087,6 +2095,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) | |||
|     view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); }); | ||||
|     view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); }); | ||||
|     view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); | ||||
| #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE | ||||
|     view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); | ||||
|     view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); | ||||
|     view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); | ||||
| #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
 | ||||
| 
 | ||||
|     // 3DScene/Toolbar:
 | ||||
|     view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); | ||||
|  | @ -2136,12 +2149,16 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) | |||
|     // updates camera type from .ini file
 | ||||
|     camera.set_type(get_config("use_perspective_camera")); | ||||
| 
 | ||||
|     mouse3d_controller.init(); | ||||
| 
 | ||||
|     // Initialize the Undo / Redo stack with a first snapshot.
 | ||||
|     this->take_snapshot(_(L("New Project"))); | ||||
| } | ||||
| 
 | ||||
| Plater::priv::~priv() | ||||
| { | ||||
|     mouse3d_controller.shutdown(); | ||||
| 
 | ||||
|     if (config != nullptr) | ||||
|         delete config; | ||||
| } | ||||
|  | @ -3062,37 +3079,6 @@ bool Plater::priv::restart_background_process(unsigned int state) | |||
|          ( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) || | ||||
|            (state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) != 0 || | ||||
|            (state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ) ) { | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|         if (((state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) == 0) && | ||||
|              (this->background_process.state() != BackgroundSlicingProcess::STATE_RUNNING)) | ||||
|         { | ||||
|             // update thumbnail data
 | ||||
|             const std::vector<Vec2d> &thumbnail_sizes = this->background_process.current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values; | ||||
|             if (this->printer_technology == ptFFF) | ||||
|             { | ||||
|                 // for ptFFF we need to generate the thumbnails before the export of gcode starts
 | ||||
|                 this->thumbnail_data.clear(); | ||||
|                 for (const Vec2d &sized : thumbnail_sizes) | ||||
|                 { | ||||
|                     this->thumbnail_data.push_back(ThumbnailData()); | ||||
| 					Point size(sized); // round to ints
 | ||||
|                     generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false); | ||||
|                 } | ||||
|             } | ||||
|             else if (this->printer_technology == ptSLA) | ||||
|             { | ||||
|                 // for ptSLA generate thumbnails without supports and pad (not yet calculated)
 | ||||
|                 // to render also supports and pad see on_slicing_update()
 | ||||
|                 this->thumbnail_data.clear(); | ||||
|                 for (const Vec2d &sized : thumbnail_sizes) | ||||
|                 { | ||||
|                     this->thumbnail_data.push_back(ThumbnailData()); | ||||
| 					Point size(sized); // round to ints
 | ||||
| 					generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
|         // The print is valid and it can be started.
 | ||||
|         if (this->background_process.start()) { | ||||
|             this->statusbar()->set_cancel_callback([this]() { | ||||
|  | @ -3339,6 +3325,7 @@ void Plater::priv::set_current_panel(wxPanel* panel) | |||
|             } else | ||||
|                 view3D->reload_scene(true); | ||||
|         } | ||||
| 
 | ||||
|         // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
 | ||||
|         view3D->set_as_dirty(); | ||||
|         view_toolbar.select_item("3D"); | ||||
|  | @ -3353,6 +3340,7 @@ void Plater::priv::set_current_panel(wxPanel* panel) | |||
|             this->q->reslice(); | ||||
|         // keeps current gcode preview, if any
 | ||||
|         preview->reload_print(true); | ||||
| 
 | ||||
|         preview->set_canvas_as_dirty(); | ||||
|         view_toolbar.select_item("Preview"); | ||||
|     } | ||||
|  | @ -3430,23 +3418,6 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) | |||
|     } else if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) { | ||||
|         // Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways.
 | ||||
|         this->preview->reload_print(); | ||||
| 
 | ||||
|         // uncomment the following lines if you want to render into the thumbnail also supports and pad for SLA printer
 | ||||
| /*
 | ||||
| #if ENABLE_THUMBNAIL_GENERATOR | ||||
|         // update thumbnail data
 | ||||
|         // for ptSLA generate the thumbnail after supports and pad have been calculated to have them rendered
 | ||||
|         if ((this->printer_technology == ptSLA) && (evt.status.percent == -3)) | ||||
|         { | ||||
|             this->thumbnail_data.clear(); | ||||
|             for (const std::pair<unsigned int, unsigned int>& size : THUMBNAIL_SIZE_SLA) | ||||
|             { | ||||
|                 this->thumbnail_data.push_back(ThumbnailData()); | ||||
|                 generate_thumbnail(this->thumbnail_data.back(), size.first, size.second, true, false, false); | ||||
|             } | ||||
|         } | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| */ | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -3677,6 +3648,19 @@ void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsig | |||
| { | ||||
|     view3D->get_canvas3d()->render_thumbnail(data, w, h, printable_only, parts_only, transparent_background); | ||||
| } | ||||
| 
 | ||||
| void Plater::priv::generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background) | ||||
| { | ||||
|     thumbnails.clear(); | ||||
|     for (const Vec2d& size : sizes) | ||||
|     { | ||||
|         thumbnails.push_back(ThumbnailData()); | ||||
|         Point isize(size); // round to ints
 | ||||
|         generate_thumbnail(thumbnails.back(), isize.x(), isize.y(), printable_only, parts_only, transparent_background); | ||||
|         if (!thumbnails.back().is_valid()) | ||||
|             thumbnails.pop_back(); | ||||
|     } | ||||
| } | ||||
| #endif // ENABLE_THUMBNAIL_GENERATOR
 | ||||
| 
 | ||||
| void Plater::priv::msw_rescale_object_menu() | ||||
|  | @ -5239,6 +5223,16 @@ const Camera& Plater::get_camera() const | |||
|     return p->camera; | ||||
| } | ||||
| 
 | ||||
| const Mouse3DController& Plater::get_mouse3d_controller() const | ||||
| { | ||||
|     return p->mouse3d_controller; | ||||
| } | ||||
| 
 | ||||
| Mouse3DController& Plater::get_mouse3d_controller() | ||||
| { | ||||
|     return p->mouse3d_controller; | ||||
| } | ||||
| 
 | ||||
| bool Plater::can_delete() const { return p->can_delete(); } | ||||
| bool Plater::can_delete_all() const { return p->can_delete_all(); } | ||||
| bool Plater::can_increase_instances() const { return p->can_increase_instances(); } | ||||
|  |  | |||
|  | @ -41,6 +41,7 @@ class ObjectSettings; | |||
| class ObjectLayers; | ||||
| class ObjectList; | ||||
| class GLCanvas3D; | ||||
| class Mouse3DController; | ||||
| 
 | ||||
| using t_optgroups = std::vector <std::shared_ptr<ConfigOptionsGroup>>; | ||||
| 
 | ||||
|  | @ -260,6 +261,8 @@ public: | |||
|     void msw_rescale(); | ||||
| 
 | ||||
|     const Camera& get_camera() const; | ||||
|     const Mouse3DController& get_mouse3d_controller() const; | ||||
|     Mouse3DController& get_mouse3d_controller(); | ||||
| 
 | ||||
| 	// ROII wrapper for suppressing the Undo / Redo snapshot to be taken.
 | ||||
| 	class SuppressSnapshots | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bubnikv
						bubnikv