diff --git a/cad/PrusaSlicer/Makefile b/cad/PrusaSlicer/Makefile index 1a91b82b1bc4..d770864caeb9 100644 --- a/cad/PrusaSlicer/Makefile +++ b/cad/PrusaSlicer/Makefile @@ -1,63 +1,62 @@ PORTNAME= PrusaSlicer DISTVERSIONPREFIX=version_ -DISTVERSION= 2.8.0 -PORTREVISION= 2 +DISTVERSION= 2.8.1 CATEGORIES= cad DIST_SUBDIR= PrusaSlicer MAINTAINER= teodorsigaev@gmail.com COMMENT= Slicing application for 3D printers WWW= https://www.prusa3d.com/prusaslicer/ LICENSE= AGPLv3 LICENSE_FILE= ${WRKSRC}/LICENSE BUILD_DEPENDS= cereal>=1.3.0.10:devel/cereal \ cgal>=5.0.2:math/cgal \ opencascade>=7.7.0:cad/opencascade \ libbgcode>=0.2.0_1:cad/libbgcode LIB_DEPENDS+= libbgcode_convert.so:cad/libbgcode \ libbgcode_binarize.so:cad/libbgcode \ libbgcode_core.so:cad/libbgcode \ libtbb.so:devel/onetbb \ libboost_log.so:devel/boost-libs \ libImath.so:math/Imath \ libnlopt.so:math/nlopt \ libqhull_r.so:math/qhull \ libcurl.so:ftp/curl \ libexpat.so:textproc/expat2 \ libiconv.so:converters/libiconv \ libopenvdb.so:misc/openvdb \ libgmp.so:math/gmp \ libmpfr.so:math/mpfr \ libdbus-1.so:devel/dbus \ libpng.so:graphics/png \ libTKXSDRAWSTEP.so:cad/opencascade \ libtiff.so:graphics/tiff \ libfontconfig.so:x11-fonts/fontconfig \ libfreeimage.so:graphics/freeimage \ libfreetype.so:print/freetype2 \ libavcodec.so:multimedia/ffmpeg4 \ libharfbuzz.so:print/harfbuzz \ libwayland-egl.so:graphics/wayland USES= cmake cpe desktop-file-utils eigen:3 gettext gl gnome iconv \ jpeg pkgconfig xorg CPE_VENDOR= prusa3d USE_GITHUB= yes GH_ACCOUNT= prusa3d USE_GL= gl glu glew USE_GNOME= gtk30 pango atk cairo gdkpixbuf2 glib20 USE_WX= 3.2 USE_XORG= x11 CMAKE_ARGS+= -DwxWidgets_CONFIG_EXECUTABLE="${WX_CONFIG}" \ -DSLIC3R_GTK=3 \ -DSLIC3R_FHS=1 \ -DSLIC3R_PCH=OFF \ -DSLIC3R_BUILD_TESTS=OFF PORTDATA= * .include diff --git a/cad/PrusaSlicer/distinfo b/cad/PrusaSlicer/distinfo index 59cb1f3b0a83..dc388786dc81 100644 --- a/cad/PrusaSlicer/distinfo +++ b/cad/PrusaSlicer/distinfo @@ -1,3 +1,3 @@ -TIMESTAMP = 1719924925 -SHA256 (PrusaSlicer/prusa3d-PrusaSlicer-version_2.8.0_GH0.tar.gz) = 58d484f85b34d83026f8220bab983f855fbcd72767d0887f21f5cc382e5b19ab -SIZE (PrusaSlicer/prusa3d-PrusaSlicer-version_2.8.0_GH0.tar.gz) = 69806919 +TIMESTAMP = 1728981634 +SHA256 (PrusaSlicer/prusa3d-PrusaSlicer-version_2.8.1_GH0.tar.gz) = f71033dd4e9fdd8e5ac7b5be5803d0d6d09ec0fd87de92b152b3c6bbae04c103 +SIZE (PrusaSlicer/prusa3d-PrusaSlicer-version_2.8.1_GH0.tar.gz) = 69409077 diff --git a/cad/PrusaSlicer/files/patch-CMakeLists.txt b/cad/PrusaSlicer/files/patch-CMakeLists.txt index 44016fce0aca..64eae04effc9 100644 --- a/cad/PrusaSlicer/files/patch-CMakeLists.txt +++ b/cad/PrusaSlicer/files/patch-CMakeLists.txt @@ -1,49 +1,62 @@ ---- CMakeLists.txt.orig 2024-06-27 09:25:47 UTC +--- CMakeLists.txt.orig 2024-09-18 13:39:04 UTC +++ CMakeLists.txt @@ -14,6 +14,7 @@ include(CMakeDependentOption) include("version.inc") include(GNUInstallDirs) include(CMakeDependentOption) +add_compile_options(-DNDEBUG) set(SLIC3R_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/resources") file(TO_NATIVE_PATH "${SLIC3R_RESOURCES_DIR}" SLIC3R_RESOURCES_DIR_WIN) @@ -234,7 +235,7 @@ endif () endif () endif () -if (CMAKE_SYSTEM_NAME STREQUAL "Linux") +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") find_package(PkgConfig REQUIRED) if (CMAKE_VERSION VERSION_LESS "3.1") -@@ -264,6 +265,8 @@ if (NOT MSVC AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL +@@ -263,6 +264,8 @@ if (NOT MSVC AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL # On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error. add_compile_options(-Werror=return-type) + add_compile_options(-Wno-enum-constexpr-conversion -Wno-implicit-const-int-float-conversion) + # removes LOTS of extraneous Eigen warnings (GCC only supports it since 6.1) # https://eigen.tuxfamily.org/bz/show_bug.cgi?id=1221 if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0) -@@ -648,8 +651,8 @@ elseif (SLIC3R_FHS) +@@ -371,10 +374,10 @@ set(MINIMUM_BOOST_VERSION "1.66.0") + # boost::process was introduced first in version 1.64.0, + # boost::beast::detail::base64 was introduced first in version 1.66.0 + set(MINIMUM_BOOST_VERSION "1.66.0") +-set(_boost_components "system;filesystem;thread;log;locale;regex;chrono;atomic;date_time;iostreams;nowide") ++set(_boost_components "system;filesystem;thread;log;log_setup;locale;regex;chrono;atomic;date_time;iostreams;nowide") + find_package(Boost ${MINIMUM_BOOST_VERSION} REQUIRED COMPONENTS ${_boost_components}) + +-find_package(Eigen3 3.3.7 REQUIRED) ++find_package(Eigen3 3.3.7 REQUIRED NO_MODULE) + + add_library(boost_libs INTERFACE) + add_library(boost_headeronly INTERFACE) +@@ -636,8 +639,8 @@ elseif (SLIC3R_FHS) install(DIRECTORY ${SLIC3R_RESOURCES_DIR}/ DESTINATION ${SLIC3R_FHS_RESOURCES} PATTERN "*/udev" EXCLUDE ) - install(FILES src/platform/unix/PrusaSlicer.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) - install(FILES src/platform/unix/PrusaGcodeviewer.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) + install(FILES src/platform/unix/PrusaSlicer.desktop DESTINATION share/applications) + install(FILES src/platform/unix/PrusaGcodeviewer.desktop DESTINATION share/applications) foreach(SIZE 32 128 192) install(FILES ${SLIC3R_RESOURCES_DIR}/icons/PrusaSlicer_${SIZE}px.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}x${SIZE}/apps RENAME PrusaSlicer.png -@@ -658,7 +661,8 @@ elseif (SLIC3R_FHS) +@@ -646,7 +649,8 @@ elseif (SLIC3R_FHS) DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}x${SIZE}/apps RENAME PrusaSlicer-gcodeviewer.png ) endforeach() - install(DIRECTORY ${SLIC3R_RESOURCES_DIR}/udev/ DESTINATION lib/udev/rules.d) + #FreeBSD doesn't have a udev + #install(DIRECTORY ${SLIC3R_RESOURCES_DIR}/udev/ DESTINATION lib/udev/rules.d) else () install(FILES src/platform/unix/PrusaSlicer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications) install(FILES src/platform/unix/PrusaGcodeviewer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications) diff --git a/cad/PrusaSlicer/files/patch-src_avrdude_arduino.c b/cad/PrusaSlicer/files/patch-bundled__deps_avrdude_avrdude_arduino.c similarity index 52% rename from cad/PrusaSlicer/files/patch-src_avrdude_arduino.c rename to cad/PrusaSlicer/files/patch-bundled__deps_avrdude_avrdude_arduino.c index c7c282d53832..9aad1b125e9a 100644 --- a/cad/PrusaSlicer/files/patch-src_avrdude_arduino.c +++ b/cad/PrusaSlicer/files/patch-bundled__deps_avrdude_avrdude_arduino.c @@ -1,10 +1,10 @@ ---- src/avrdude/arduino.c.orig 2023-07-25 13:23:52 UTC -+++ src/avrdude/arduino.c +--- bundled_deps/avrdude/avrdude/arduino.c.orig 2024-09-18 13:39:04 UTC ++++ bundled_deps/avrdude/avrdude/arduino.c @@ -28,6 +28,7 @@ #include "ac_cfg.h" #include +#include #include #include diff --git a/cad/PrusaSlicer/files/patch-src_avrdude_libavrdude.h b/cad/PrusaSlicer/files/patch-bundled__deps_avrdude_avrdude_libavrdude.h similarity index 63% rename from cad/PrusaSlicer/files/patch-src_avrdude_libavrdude.h rename to cad/PrusaSlicer/files/patch-bundled__deps_avrdude_avrdude_libavrdude.h index ed7c44e0e5bc..6d03bb0bc90d 100644 --- a/cad/PrusaSlicer/files/patch-src_avrdude_libavrdude.h +++ b/cad/PrusaSlicer/files/patch-bundled__deps_avrdude_avrdude_libavrdude.h @@ -1,11 +1,11 @@ ---- src/avrdude/libavrdude.h.orig 2022-09-06 07:09:19 UTC -+++ src/avrdude/libavrdude.h +--- bundled_deps/avrdude/avrdude/libavrdude.h.orig 2024-09-18 13:39:04 UTC ++++ bundled_deps/avrdude/avrdude/libavrdude.h @@ -950,6 +950,8 @@ int read_config_builtin(); // Header file for alloca() #if defined(WIN32NATIVE) # include +#elif defined __FreeBSD__ +# include #else # include #endif diff --git a/cad/PrusaSlicer/files/patch-src_avrdude_main.c b/cad/PrusaSlicer/files/patch-bundled__deps_avrdude_avrdude_main.c similarity index 84% rename from cad/PrusaSlicer/files/patch-src_avrdude_main.c rename to cad/PrusaSlicer/files/patch-bundled__deps_avrdude_avrdude_main.c index 57f18516cc78..a6912d0091ef 100644 --- a/cad/PrusaSlicer/files/patch-src_avrdude_main.c +++ b/cad/PrusaSlicer/files/patch-bundled__deps_avrdude_avrdude_main.c @@ -1,11 +1,11 @@ ---- src/avrdude/main.c.orig 2022-09-06 07:09:19 UTC -+++ src/avrdude/main.c +--- bundled_deps/avrdude/avrdude/main.c.orig 2024-09-18 13:39:04 UTC ++++ bundled_deps/avrdude/avrdude/main.c @@ -806,7 +806,7 @@ int avrdude_main(int argc, char * argv []) avrdude_message(MSG_NOTICE, "\n%s: Version %s, compiled on %s at %s\n" "%sCopyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/\n" "%sCopyright (c) 2007-2014 Joerg Wunsch\n\n", - progname, version, __DATE__, __TIME__, progbuf, progbuf); + progname, version, "REDACTED", "REDACTED", progbuf, progbuf); // avrdude_message(MSG_NOTICE, "%sSystem wide configuration file is \"%s\"\n", // progbuf, sys_config); diff --git a/cad/PrusaSlicer/files/patch-src_hidapi_CMakeLists.txt b/cad/PrusaSlicer/files/patch-bundled__deps_hidapi_CMakeLists.txt similarity index 66% rename from cad/PrusaSlicer/files/patch-src_hidapi_CMakeLists.txt rename to cad/PrusaSlicer/files/patch-bundled__deps_hidapi_CMakeLists.txt index 282d616f9efa..a8f856cd3cf2 100644 --- a/cad/PrusaSlicer/files/patch-src_hidapi_CMakeLists.txt +++ b/cad/PrusaSlicer/files/patch-bundled__deps_hidapi_CMakeLists.txt @@ -1,20 +1,20 @@ ---- src/hidapi/CMakeLists.txt.orig 2022-09-06 07:09:19 UTC -+++ src/hidapi/CMakeLists.txt +--- bundled_deps/hidapi/CMakeLists.txt.orig 2024-09-18 13:39:04 UTC ++++ bundled_deps/hidapi/CMakeLists.txt @@ -1,8 +1,9 @@ - if (WIN32) set(HIDAPI_IMPL win/hid.c) elseif (APPLE) set(HIDAPI_IMPL mac/hid.c) +elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set(HIDAPI_IMPL libusb/hid.c) else () # Assume Linux or Unix other than Mac OS set(HIDAPI_IMPL linux/hid.c) -@@ -16,4 +17,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux") +@@ -16,4 +17,6 @@ target_link_libraries(hidapi PRIVATE dl) # Don't link the udev library, as there are two versions out there (libudev.so.0, libudev.so.1), so they are linked explicitely. # target_link_libraries(hidapi udev) - target_link_libraries(hidapi dl) + target_link_libraries(hidapi PRIVATE dl) +elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") -+ target_link_libraries(hidapi usb iconv) ++ target_link_libraries(hidapi usb iconv) endif() diff --git a/cad/PrusaSlicer/files/patch-src_hidapi_libusb_hid.c b/cad/PrusaSlicer/files/patch-bundled__deps_hidapi_libusb_hid.c similarity index 99% copy from cad/PrusaSlicer/files/patch-src_hidapi_libusb_hid.c copy to cad/PrusaSlicer/files/patch-bundled__deps_hidapi_libusb_hid.c index 5cc678a4b344..df486242573f 100644 --- a/cad/PrusaSlicer/files/patch-src_hidapi_libusb_hid.c +++ b/cad/PrusaSlicer/files/patch-bundled__deps_hidapi_libusb_hid.c @@ -1,1517 +1,1517 @@ ---- src/hidapi/libusb/hid.c.orig 2022-11-13 14:36:46 UTC -+++ src/hidapi/libusb/hid.c +--- bundled_deps/hidapi/libusb/hid.c.orig 2024-10-15 09:17:30 UTC ++++ bundled_deps/hidapi/libusb/hid.c @@ -0,0 +1,1514 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + Linux Version - 6/2/2010 + Libusb Version - 8/13/2010 + FreeBSD Version - 11/1/2011 + + 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 . +********************************************************/ + +#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ + +/* C */ +#include +#include +#include +#include +#include +#include + +/* Unix */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* GNU / LibUSB */ +#include +#ifndef __ANDROID__ +#include +#endif + +#include "hidapi.h" + +#ifdef __ANDROID__ + +/* Barrier implementation because Android/Bionic don't have pthread_barrier. + This implementation came from Brent Priddy and was posted on + StackOverflow. It is used with his permission. */ +typedef int pthread_barrierattr_t; +typedef struct pthread_barrier { + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int trip_count; +} pthread_barrier_t; + +static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) +{ + if(count == 0) { + errno = EINVAL; + return -1; + } + + if(pthread_mutex_init(&barrier->mutex, 0) < 0) { + return -1; + } + if(pthread_cond_init(&barrier->cond, 0) < 0) { + pthread_mutex_destroy(&barrier->mutex); + return -1; + } + barrier->trip_count = count; + barrier->count = 0; + + return 0; +} + +static int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +static int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + pthread_mutex_lock(&barrier->mutex); + ++(barrier->count); + if(barrier->count >= barrier->trip_count) + { + barrier->count = 0; + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return 1; + } + else + { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + pthread_mutex_unlock(&barrier->mutex); + return 0; + } +} + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DEBUG_PRINTF +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#else +#define LOG(...) do {} while (0) +#endif + +#ifndef __FreeBSD__ +#define DETACH_KERNEL_DRIVER +#endif + +/* Uncomment to enable the retrieval of Usage and Usage Page in +hid_enumerate(). Warning, on platforms different from FreeBSD +this is very invasive as it requires the detach +and re-attach of the kernel driver. See comments inside hid_enumerate(). +libusb HIDAPI programs are encouraged to use the interface number +instead to differentiate between interfaces on a composite HID device. */ +/*#define INVASIVE_GET_USAGE*/ + +/* Linked List of input reports received from the device. */ +struct input_report { + uint8_t *data; + size_t len; + struct input_report *next; +}; + + +struct hid_device_ { + /* Handle to the actual device. */ + libusb_device_handle *device_handle; + + /* Endpoint information */ + int input_endpoint; + int output_endpoint; + int input_ep_max_packet_size; + + /* The interface number of the HID */ + int interface; + + /* Indexes of Strings */ + int manufacturer_index; + int product_index; + int serial_index; + + /* Whether blocking reads are used */ + int blocking; /* boolean */ + + /* Read thread objects */ + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + int shutdown_thread; + int cancelled; + struct libusb_transfer *transfer; + + /* List of received input reports. */ + struct input_report *input_reports; +}; + +static libusb_context *usb_context = NULL; + +uint16_t get_usb_code_for_current_locale(void); +static int return_data(hid_device *dev, unsigned char *data, size_t length); + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->blocking = 1; + + pthread_mutex_init(&dev->mutex, NULL); + pthread_cond_init(&dev->condition, NULL); + pthread_barrier_init(&dev->barrier, NULL, 2); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + /* Clean up the thread objects */ + pthread_barrier_destroy(&dev->barrier); + pthread_cond_destroy(&dev->condition); + pthread_mutex_destroy(&dev->mutex); + + /* Free the device itself */ + free(dev); +} + +#if 0 +/*TODO: Implement this funciton on hidapi/libusb.. */ +static void register_error(hid_device *dev, const char *op) +{ + +} +#endif + +#ifdef INVASIVE_GET_USAGE +/* Get bytes from a HID Report Descriptor. + Only call with a num_bytes of 0, 1, 2, or 4. */ +static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur) +{ + /* Return if there aren't enough bytes. */ + if (cur + num_bytes >= len) + return 0; + + if (num_bytes == 0) + return 0; + else if (num_bytes == 1) { + return rpt[cur+1]; + } + else if (num_bytes == 2) { + return (rpt[cur+2] * 256 + rpt[cur+1]); + } + else if (num_bytes == 4) { + return (rpt[cur+4] * 0x01000000 + + rpt[cur+3] * 0x00010000 + + rpt[cur+2] * 0x00000100 + + rpt[cur+1] * 0x00000001); + } + else + return 0; +} + +/* Retrieves the device's Usage Page and Usage from the report + descriptor. The algorithm is simple, as it just returns the first + Usage and Usage Page that it finds in the descriptor. + The return value is 0 on success and -1 on failure. */ +static int get_usage(uint8_t *report_descriptor, size_t size, + unsigned short *usage_page, unsigned short *usage) +{ + unsigned int i = 0; + int size_code; + int data_len, key_size; + int usage_found = 0, usage_page_found = 0; + + while (i < size) { + int key = report_descriptor[i]; + int key_cmd = key & 0xfc; + + //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; + } + + if (key_cmd == 0x4) { + *usage_page = get_bytes(report_descriptor, size, data_len, i); + usage_page_found = 1; + //printf("Usage Page: %x\n", (uint32_t)*usage_page); + } + if (key_cmd == 0x8) { + *usage = get_bytes(report_descriptor, size, data_len, i); + usage_found = 1; + //printf("Usage: %x\n", (uint32_t)*usage); + } + + if (usage_page_found && usage_found) + return 0; /* success */ + + /* Skip over this key and it's associated data */ + i += data_len + key_size; + } + + return -1; /* failure */ +} +#endif /* INVASIVE_GET_USAGE */ + +#if defined(__FreeBSD__) && __FreeBSD__ < 10 +/* The libusb version included in FreeBSD < 10 doesn't have this function. In + mainline libusb, it's inlined in libusb.h. This function will bear a striking + resemblance to that one, because there's about one way to code it. + + Note that the data parameter is Unicode in UTF-16LE encoding. + Return value is the number of bytes in data, or LIBUSB_ERROR_*. + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, + uint8_t descriptor_index, uint16_t lang_id, + unsigned char *data, int length) +{ + return libusb_control_transfer(dev, + LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */ + LIBUSB_REQUEST_GET_DESCRIPTOR, + (LIBUSB_DT_STRING << 8) | descriptor_index, + lang_id, data, (uint16_t) length, 1000); +} + +#endif + + +/* Get the first language the device says it reports. This comes from + USB string #0. */ +static uint16_t get_first_language(libusb_device_handle *dev) +{ + uint16_t buf[32]; + int len; + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + 0x0, /* String ID */ + 0x0, /* Language */ + (unsigned char*)buf, + sizeof(buf)); + if (len < 4) + return 0x0; + + return buf[1]; /* First two bytes are len and descriptor type. */ +} + +static int is_language_supported(libusb_device_handle *dev, uint16_t lang) +{ + uint16_t buf[32]; + int len; + int i; + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + 0x0, /* String ID */ + 0x0, /* Language */ + (unsigned char*)buf, + sizeof(buf)); + if (len < 4) + return 0x0; + + + len /= 2; /* language IDs are two-bytes each. */ + /* Start at index 1 because there are two bytes of protocol data. */ + for (i = 1; i < len; i++) { + if (buf[i] == lang) + return 1; + } + + return 0; +} + + +/* This function returns a newly allocated wide string containing the USB + device string numbered by the index. The returned string must be freed + by using free(). */ +static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) +{ + char buf[512]; + int len; + wchar_t *str = NULL; + +#ifndef __ANDROID__ /* we don't use iconv on Android */ + wchar_t wbuf[256]; + /* iconv variables */ + iconv_t ic; + size_t inbytes; + size_t outbytes; + size_t res; +#ifdef __FreeBSD__ + const char *inptr; +#else + char *inptr; +#endif + char *outptr; +#endif + + /* Determine which language to use. */ + uint16_t lang; + lang = get_usb_code_for_current_locale(); + if (!is_language_supported(dev, lang)) + lang = get_first_language(dev); + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + idx, + lang, + (unsigned char*)buf, + sizeof(buf)); + if (len < 0) + return NULL; + +#ifdef __ANDROID__ + + /* Bionic does not have iconv support nor wcsdup() function, so it + has to be done manually. The following code will only work for + code points that can be represented as a single UTF-16 character, + and will incorrectly convert any code points which require more + than one UTF-16 character. + + Skip over the first character (2-bytes). */ + len -= 2; + str = malloc((len / 2 + 1) * sizeof(wchar_t)); + int i; + for (i = 0; i < len / 2; i++) { + str[i] = buf[i * 2 + 2] | (buf[i * 2 + 3] << 8); + } + str[len / 2] = 0x00000000; + +#else + + /* buf does not need to be explicitly NULL-terminated because + it is only passed into iconv() which does not need it. */ + + /* Initialize iconv. */ + ic = iconv_open("WCHAR_T", "UTF-16LE"); + if (ic == (iconv_t)-1) { + LOG("iconv_open() failed\n"); + return NULL; + } + + /* Convert to native wchar_t (UTF-32 on glibc/BSD systems). + Skip the first character (2-bytes). */ + inptr = buf+2; + inbytes = len-2; + outptr = (char*) wbuf; + outbytes = sizeof(wbuf); + res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes); + if (res == (size_t)-1) { + LOG("iconv() failed\n"); + goto err; + } + + /* Write the terminating NULL. */ + wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000; + if (outbytes >= sizeof(wbuf[0])) + *((wchar_t*)outptr) = 0x00000000; + + /* Allocate and copy the string. */ + str = wcsdup(wbuf); + +err: + iconv_close(ic); + +#endif + + return str; +} + +static char *make_path(libusb_device *dev, int interface_number) +{ + char str[64]; + snprintf(str, sizeof(str), "%04x:%04x:%02x", + libusb_get_bus_number(dev), + libusb_get_device_address(dev), + interface_number); + str[sizeof(str)-1] = '\0'; + + return strdup(str); +} + + +int HID_API_EXPORT hid_init(void) +{ + if (!usb_context) { + const char *locale; + + /* Init Libusb */ + if (libusb_init(&usb_context)) + return -1; + + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); + } + + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + if (usb_context) { + libusb_exit(usb_context); + usb_context = NULL; + } + + return 0; +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + libusb_device **devs; + libusb_device *dev; + libusb_device_handle *handle; + ssize_t num_devs; + int i = 0; + + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + + if(hid_init() < 0) + return NULL; + + num_devs = libusb_get_device_list(usb_context, &devs); + if (num_devs < 0) + return NULL; + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor desc; + struct libusb_config_descriptor *conf_desc = NULL; + int j, k; + int interface_num = 0; + + int res = libusb_get_device_descriptor(dev, &desc); + unsigned short dev_vid = desc.idVendor; + unsigned short dev_pid = desc.idProduct; + + res = libusb_get_active_config_descriptor(dev, &conf_desc); + if (res < 0) + libusb_get_config_descriptor(dev, 0, &conf_desc); + if (conf_desc) { + for (j = 0; j < conf_desc->bNumInterfaces; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc; + intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + interface_num = intf_desc->bInterfaceNumber; + + /* 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 = calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = make_path(dev, interface_num); + + res = libusb_open(dev, &handle); + + if (res >= 0) { + /* Serial Number */ + if (desc.iSerialNumber > 0) + cur_dev->serial_number = + get_usb_string(handle, desc.iSerialNumber); + + /* Manufacturer and Product strings */ + if (desc.iManufacturer > 0) + cur_dev->manufacturer_string = + get_usb_string(handle, desc.iManufacturer); + if (desc.iProduct > 0) + cur_dev->product_string = + get_usb_string(handle, desc.iProduct); + +#ifdef INVASIVE_GET_USAGE +{ + /* + This section is removed because it is too + invasive on the system. Getting a Usage Page + and Usage requires parsing the HID Report + descriptor. Getting a HID Report descriptor + involves claiming the interface. Claiming the + interface involves detaching the kernel driver. + Detaching the kernel driver is hard on the system + because it will unclaim interfaces (if another + app has them claimed) and the re-attachment of + the driver will sometimes change /dev entry names. + It is for these reasons that this section is + #if 0. For composite devices, use the interface + field in the hid_device_info struct to distinguish + between interfaces. */ + unsigned char data[256]; +#ifdef DETACH_KERNEL_DRIVER + int detached = 0; + /* Usage Page and Usage */ + res = libusb_kernel_driver_active(handle, interface_num); + if (res == 1) { + res = libusb_detach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't detach kernel driver, even though a kernel driver was attached."); + else + detached = 1; + } +#endif + res = libusb_claim_interface(handle, interface_num); + if (res >= 0) { + /* Get the HID Report Descriptor. */ + res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000); + if (res >= 0) { + unsigned short page=0, usage=0; + /* Parse the usage and usage page + out of the report descriptor. */ + get_usage(data, res, &page, &usage); + cur_dev->usage_page = page; + cur_dev->usage = usage; + } + else + LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res); + + /* Release the interface */ + res = libusb_release_interface(handle, interface_num); + if (res < 0) + LOG("Can't release the interface.\n"); + } + else + LOG("Can't claim interface %d\n", res); +#ifdef DETACH_KERNEL_DRIVER + /* Re-attach kernel driver if necessary. */ + if (detached) { + res = libusb_attach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't re-attach kernel driver.\n"); + } +#endif +} +#endif /* INVASIVE_GET_USAGE */ + + libusb_close(handle); + } + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + cur_dev->release_number = desc.bcdDevice; + + /* Interface Number */ + cur_dev->interface_number = interface_num; + } + } + } /* altsettings */ + } /* interfaces */ + libusb_free_config_descriptor(conf_desc); + } + } + + libusb_free_device_list(devs, 1); + + 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 (cur_dev->serial_number && + 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; +} + +static void read_callback(struct libusb_transfer *transfer) +{ + hid_device *dev = transfer->user_data; + int res; + + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + + struct input_report *rpt = malloc(sizeof(*rpt)); + rpt->data = malloc(transfer->actual_length); + memcpy(rpt->data, transfer->buffer, transfer->actual_length); + rpt->len = transfer->actual_length; + rpt->next = NULL; + + pthread_mutex_lock(&dev->mutex); + + /* Attach the new report object to the end of the list. */ + if (dev->input_reports == NULL) { + /* The list is empty. Put it at the root. */ + dev->input_reports = rpt; + pthread_cond_signal(&dev->condition); + } + else { + /* Find the end of the list and attach. */ + struct input_report *cur = dev->input_reports; + int num_queued = 0; + while (cur->next != NULL) { + cur = cur->next; + num_queued++; + } + cur->next = rpt; + + /* Pop one off if we've reached 30 in the queue. This + way we don't grow forever if the user never reads + anything from the device. */ + if (num_queued > 30) { + return_data(dev, NULL, 0); + } + } + pthread_mutex_unlock(&dev->mutex); + } + else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { + dev->shutdown_thread = 1; + dev->cancelled = 1; + return; + } + else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) { + dev->shutdown_thread = 1; + dev->cancelled = 1; + return; + } + else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { + //LOG("Timeout (normal)\n"); + } + else { + LOG("Unknown transfer code: %d\n", transfer->status); + } + + /* Re-submit the transfer object. */ + res = libusb_submit_transfer(transfer); + if (res != 0) { + LOG("Unable to submit URB. libusb error code: %d\n", res); + dev->shutdown_thread = 1; + dev->cancelled = 1; + } +} + + +static void *read_thread(void *param) +{ + hid_device *dev = param; + unsigned char *buf; + const size_t length = dev->input_ep_max_packet_size; + + /* Set up the transfer object. */ + buf = malloc(length); + dev->transfer = libusb_alloc_transfer(0); + libusb_fill_interrupt_transfer(dev->transfer, + dev->device_handle, + dev->input_endpoint, + buf, + length, + read_callback, + dev, + 5000/*timeout*/); + + /* Make the first submission. Further submissions are made + from inside read_callback() */ + libusb_submit_transfer(dev->transfer); + + /* Notify the main thread that the read thread is up and running. */ + pthread_barrier_wait(&dev->barrier); + + /* Handle all the events. */ + while (!dev->shutdown_thread) { + int res; + res = libusb_handle_events(usb_context); + if (res < 0) { + /* There was an error. */ + LOG("read_thread(): libusb reports error # %d\n", res); + + /* Break out of this loop only on fatal error.*/ + if (res != LIBUSB_ERROR_BUSY && + res != LIBUSB_ERROR_TIMEOUT && + res != LIBUSB_ERROR_OVERFLOW && + res != LIBUSB_ERROR_INTERRUPTED) { + break; + } + } + } + + /* Cancel any transfer that may be pending. This call will fail + if no transfers are pending, but that's OK. */ + libusb_cancel_transfer(dev->transfer); + + while (!dev->cancelled) + libusb_handle_events_completed(usb_context, &dev->cancelled); + + /* Now that the read thread is stopping, Wake any threads which are + waiting on data (in hid_read_timeout()). Do this under a mutex to + make sure that a thread which is about to go to sleep waiting on + the condition actually will go to sleep before the condition is + signaled. */ + pthread_mutex_lock(&dev->mutex); + pthread_cond_broadcast(&dev->condition); + pthread_mutex_unlock(&dev->mutex); + + /* The dev->transfer->buffer and dev->transfer objects are cleaned up + in hid_close(). They are not cleaned up here because this thread + could end either due to a disconnect or due to a user + call to hid_close(). In both cases the objects can be safely + cleaned up after the call to pthread_join() (in hid_close()), but + since hid_close() calls libusb_cancel_transfer(), on these objects, + they can not be cleaned up here. */ + + return NULL; +} + + +hid_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + + libusb_device **devs; + libusb_device *usb_dev; + int res; + int d = 0; + int good_open = 0; + + if(hid_init() < 0) + return NULL; + + dev = new_hid_device(); + + libusb_get_device_list(usb_context, &devs); + while ((usb_dev = devs[d++]) != NULL) { + struct libusb_device_descriptor desc; + struct libusb_config_descriptor *conf_desc = NULL; + int i,j,k; + libusb_get_device_descriptor(usb_dev, &desc); + + if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) + continue; + for (j = 0; j < conf_desc->bNumInterfaces; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc; + intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber); + if (!strcmp(dev_path, path)) { + /* Matched Paths. Open this device */ + + /* OPEN HERE */ + res = libusb_open(usb_dev, &dev->device_handle); + if (res < 0) { + LOG("can't open device\n"); + free(dev_path); + break; + } + good_open = 1; +#ifdef DETACH_KERNEL_DRIVER + /* Detach the kernel driver, but only if the + device is managed by the kernel */ + if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { + res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + libusb_close(dev->device_handle); + LOG("Unable to detach Kernel Driver\n"); + free(dev_path); + good_open = 0; + break; + } + } +#endif + res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); + free(dev_path); + libusb_close(dev->device_handle); + good_open = 0; + break; + } + + /* Store off the string descriptor indexes */ + dev->manufacturer_index = desc.iManufacturer; + dev->product_index = desc.iProduct; + dev->serial_index = desc.iSerialNumber; + + /* Store off the interface number */ + dev->interface = intf_desc->bInterfaceNumber; + + /* Find the INPUT and OUTPUT endpoints. An + OUTPUT endpoint is not required. */ + for (i = 0; i < intf_desc->bNumEndpoints; i++) { + const struct libusb_endpoint_descriptor *ep + = &intf_desc->endpoint[i]; + + /* Determine the type and direction of this + endpoint. */ + int is_interrupt = + (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) + == LIBUSB_TRANSFER_TYPE_INTERRUPT; + int is_output = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_OUT; + int is_input = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_IN; + + /* Decide whether to use it for input or output. */ + if (dev->input_endpoint == 0 && + is_interrupt && is_input) { + /* Use this endpoint for INPUT */ + dev->input_endpoint = ep->bEndpointAddress; + dev->input_ep_max_packet_size = ep->wMaxPacketSize; + } + if (dev->output_endpoint == 0 && + is_interrupt && is_output) { + /* Use this endpoint for OUTPUT */ + dev->output_endpoint = ep->bEndpointAddress; + } + } + + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + + } + free(dev_path); + } + } + } + libusb_free_config_descriptor(conf_desc); + + } + + libusb_free_device_list(devs, 1); + + /* If we have a good handle, return it. */ + if (good_open) { + return dev; + } + else { + /* Unable to open any devices. */ + free_hid_device(dev); + return NULL; + } +} + + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + int res; + int report_number = data[0]; + int skipped_report_id = 0; + + if (report_number == 0x0) { + data++; + length--; + skipped_report_id = 1; + } + + + if (dev->output_endpoint <= 0) { + /* No interrupt out endpoint. Use the Control Endpoint */ + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, + 0x09/*HID Set_Report*/, + (2/*HID output*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + if (skipped_report_id) + length++; + + return length; + } + else { + /* Use the interrupt out endpoint */ + int actual_length; + res = libusb_interrupt_transfer(dev->device_handle, + dev->output_endpoint, + (unsigned char*)data, + length, + &actual_length, 1000); + + if (res < 0) + return -1; + + if (skipped_report_id) + actual_length++; + + return actual_length; + } +} + +/* Helper function, to simplify hid_read(). + This should be called with dev->mutex locked. */ +static int return_data(hid_device *dev, unsigned char *data, size_t length) +{ + /* Copy the data out of the linked list item (rpt) into the + return buffer (data), and delete the liked list item. */ + struct input_report *rpt = dev->input_reports; + size_t len = (length < rpt->len)? length: rpt->len; + if (len > 0) + memcpy(data, rpt->data, len); + dev->input_reports = rpt->next; + free(rpt->data); + free(rpt); + return len; +} + +static void cleanup_mutex(void *param) +{ + hid_device *dev = param; + pthread_mutex_unlock(&dev->mutex); +} + + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read = -1; + +#if 0 + int transferred; + int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000); + LOG("transferred: %d\n", transferred); + return transferred; +#endif + + pthread_mutex_lock(&dev->mutex); + pthread_cleanup_push(&cleanup_mutex, dev); + + /* There's an input report queued up. Return it. */ + if (dev->input_reports) { + /* Return the first one */ + bytes_read = return_data(dev, data, length); + goto ret; + } + + if (dev->shutdown_thread) { + /* This means the device has been disconnected. + An error code of -1 should be returned. */ + bytes_read = -1; + goto ret; + } + + if (milliseconds == -1) { + /* Blocking */ + while (!dev->input_reports && !dev->shutdown_thread) { + pthread_cond_wait(&dev->condition, &dev->mutex); + } + if (dev->input_reports) { + bytes_read = return_data(dev, data, length); + } + } + else if (milliseconds > 0) { + /* Non-blocking, but called with timeout. */ + int res; + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += milliseconds / 1000; + ts.tv_nsec += (milliseconds % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + while (!dev->input_reports && !dev->shutdown_thread) { + res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts); + if (res == 0) { + if (dev->input_reports) { + bytes_read = return_data(dev, data, length); + break; + } + + /* If we're here, there was a spurious wake up + or the read thread was shutdown. Run the + loop again (ie: don't break). */ + } + else if (res == ETIMEDOUT) { + /* Timed out. */ + bytes_read = 0; + break; + } + else { + /* Error. */ + bytes_read = -1; + break; + } + } + } + else { + /* Purely non-blocking */ + bytes_read = 0; + } + +ret: + pthread_mutex_unlock(&dev->mutex); + pthread_cleanup_pop(0); + + 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) +{ + dev->blocking = !nonblock; + + return 0; +} + + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + int res = -1; + int skipped_report_id = 0; + int report_number = data[0]; + + if (report_number == 0x0) { + data++; + length--; + skipped_report_id = 1; + } + + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, + 0x09/*HID set_report*/, + (3/*HID feature*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + /* Account for the report ID */ + if (skipped_report_id) + length++; + + return length; +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + int res = -1; + int skipped_report_id = 0; + int report_number = data[0]; + + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + data++; + length--; + skipped_report_id = 1; + } + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN, + 0x01/*HID get_report*/, + (3/*HID feature*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + if (skipped_report_id) + res++; + + return res; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + + /* Cause read_thread() to stop. */ + dev->shutdown_thread = 1; + libusb_cancel_transfer(dev->transfer); + + /* Wait for read_thread() to end. */ + pthread_join(dev->thread, NULL); + + /* Clean up the Transfer objects allocated in read_thread(). */ + free(dev->transfer->buffer); + libusb_free_transfer(dev->transfer); + + /* release the interface */ + libusb_release_interface(dev->device_handle, dev->interface); + + /* Close the handle */ + libusb_close(dev->device_handle); + + /* Clear out the queue of received reports. */ + pthread_mutex_lock(&dev->mutex); + while (dev->input_reports) { + return_data(dev, NULL, 0); + } + pthread_mutex_unlock(&dev->mutex); + + free_hid_device(dev); +} + + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->manufacturer_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->product_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->serial_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + wchar_t *str; + + str = get_usb_string(dev->device_handle, string_index); + if (str) { + wcsncpy(string, str, maxlen); + string[maxlen-1] = L'\0'; + free(str); + return 0; + } + else + return -1; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return NULL; +} + + +struct lang_map_entry { + const char *name; + const char *string_code; + uint16_t usb_code; +}; + +#define LANG(name,code,usb_code) { name, code, usb_code } +static struct lang_map_entry lang_map[] = { + LANG("Afrikaans", "af", 0x0436), + LANG("Albanian", "sq", 0x041C), + LANG("Arabic - United Arab Emirates", "ar_ae", 0x3801), + LANG("Arabic - Bahrain", "ar_bh", 0x3C01), + LANG("Arabic - Algeria", "ar_dz", 0x1401), + LANG("Arabic - Egypt", "ar_eg", 0x0C01), + LANG("Arabic - Iraq", "ar_iq", 0x0801), + LANG("Arabic - Jordan", "ar_jo", 0x2C01), + LANG("Arabic - Kuwait", "ar_kw", 0x3401), + LANG("Arabic - Lebanon", "ar_lb", 0x3001), + LANG("Arabic - Libya", "ar_ly", 0x1001), + LANG("Arabic - Morocco", "ar_ma", 0x1801), + LANG("Arabic - Oman", "ar_om", 0x2001), + LANG("Arabic - Qatar", "ar_qa", 0x4001), + LANG("Arabic - Saudi Arabia", "ar_sa", 0x0401), + LANG("Arabic - Syria", "ar_sy", 0x2801), + LANG("Arabic - Tunisia", "ar_tn", 0x1C01), + LANG("Arabic - Yemen", "ar_ye", 0x2401), + LANG("Armenian", "hy", 0x042B), + LANG("Azeri - Latin", "az_az", 0x042C), + LANG("Azeri - Cyrillic", "az_az", 0x082C), + LANG("Basque", "eu", 0x042D), + LANG("Belarusian", "be", 0x0423), + LANG("Bulgarian", "bg", 0x0402), + LANG("Catalan", "ca", 0x0403), + LANG("Chinese - China", "zh_cn", 0x0804), + LANG("Chinese - Hong Kong SAR", "zh_hk", 0x0C04), + LANG("Chinese - Macau SAR", "zh_mo", 0x1404), + LANG("Chinese - Singapore", "zh_sg", 0x1004), + LANG("Chinese - Taiwan", "zh_tw", 0x0404), + LANG("Croatian", "hr", 0x041A), + LANG("Czech", "cs", 0x0405), + LANG("Danish", "da", 0x0406), + LANG("Dutch - Netherlands", "nl_nl", 0x0413), + LANG("Dutch - Belgium", "nl_be", 0x0813), + LANG("English - Australia", "en_au", 0x0C09), + LANG("English - Belize", "en_bz", 0x2809), + LANG("English - Canada", "en_ca", 0x1009), + LANG("English - Caribbean", "en_cb", 0x2409), + LANG("English - Ireland", "en_ie", 0x1809), + LANG("English - Jamaica", "en_jm", 0x2009), + LANG("English - New Zealand", "en_nz", 0x1409), + LANG("English - Phillippines", "en_ph", 0x3409), + LANG("English - Southern Africa", "en_za", 0x1C09), + LANG("English - Trinidad", "en_tt", 0x2C09), + LANG("English - Great Britain", "en_gb", 0x0809), + LANG("English - United States", "en_us", 0x0409), + LANG("Estonian", "et", 0x0425), + LANG("Farsi", "fa", 0x0429), + LANG("Finnish", "fi", 0x040B), + LANG("Faroese", "fo", 0x0438), + LANG("French - France", "fr_fr", 0x040C), + LANG("French - Belgium", "fr_be", 0x080C), + LANG("French - Canada", "fr_ca", 0x0C0C), + LANG("French - Luxembourg", "fr_lu", 0x140C), + LANG("French - Switzerland", "fr_ch", 0x100C), + LANG("Gaelic - Ireland", "gd_ie", 0x083C), + LANG("Gaelic - Scotland", "gd", 0x043C), + LANG("German - Germany", "de_de", 0x0407), + LANG("German - Austria", "de_at", 0x0C07), + LANG("German - Liechtenstein", "de_li", 0x1407), + LANG("German - Luxembourg", "de_lu", 0x1007), + LANG("German - Switzerland", "de_ch", 0x0807), + LANG("Greek", "el", 0x0408), + LANG("Hebrew", "he", 0x040D), + LANG("Hindi", "hi", 0x0439), + LANG("Hungarian", "hu", 0x040E), + LANG("Icelandic", "is", 0x040F), + LANG("Indonesian", "id", 0x0421), + LANG("Italian - Italy", "it_it", 0x0410), + LANG("Italian - Switzerland", "it_ch", 0x0810), + LANG("Japanese", "ja", 0x0411), + LANG("Korean", "ko", 0x0412), + LANG("Latvian", "lv", 0x0426), + LANG("Lithuanian", "lt", 0x0427), + LANG("F.Y.R.O. Macedonia", "mk", 0x042F), + LANG("Malay - Malaysia", "ms_my", 0x043E), + LANG("Malay – Brunei", "ms_bn", 0x083E), + LANG("Maltese", "mt", 0x043A), + LANG("Marathi", "mr", 0x044E), + LANG("Norwegian - Bokml", "no_no", 0x0414), + LANG("Norwegian - Nynorsk", "no_no", 0x0814), + LANG("Polish", "pl", 0x0415), + LANG("Portuguese - Portugal", "pt_pt", 0x0816), + LANG("Portuguese - Brazil", "pt_br", 0x0416), + LANG("Raeto-Romance", "rm", 0x0417), + LANG("Romanian - Romania", "ro", 0x0418), + LANG("Romanian - Republic of Moldova", "ro_mo", 0x0818), + LANG("Russian", "ru", 0x0419), + LANG("Russian - Republic of Moldova", "ru_mo", 0x0819), + LANG("Sanskrit", "sa", 0x044F), + LANG("Serbian - Cyrillic", "sr_sp", 0x0C1A), + LANG("Serbian - Latin", "sr_sp", 0x081A), + LANG("Setsuana", "tn", 0x0432), + LANG("Slovenian", "sl", 0x0424), + LANG("Slovak", "sk", 0x041B), + LANG("Sorbian", "sb", 0x042E), + LANG("Spanish - Spain (Traditional)", "es_es", 0x040A), + LANG("Spanish - Argentina", "es_ar", 0x2C0A), + LANG("Spanish - Bolivia", "es_bo", 0x400A), + LANG("Spanish - Chile", "es_cl", 0x340A), + LANG("Spanish - Colombia", "es_co", 0x240A), + LANG("Spanish - Costa Rica", "es_cr", 0x140A), + LANG("Spanish - Dominican Republic", "es_do", 0x1C0A), + LANG("Spanish - Ecuador", "es_ec", 0x300A), + LANG("Spanish - Guatemala", "es_gt", 0x100A), + LANG("Spanish - Honduras", "es_hn", 0x480A), + LANG("Spanish - Mexico", "es_mx", 0x080A), + LANG("Spanish - Nicaragua", "es_ni", 0x4C0A), + LANG("Spanish - Panama", "es_pa", 0x180A), + LANG("Spanish - Peru", "es_pe", 0x280A), + LANG("Spanish - Puerto Rico", "es_pr", 0x500A), + LANG("Spanish - Paraguay", "es_py", 0x3C0A), + LANG("Spanish - El Salvador", "es_sv", 0x440A), + LANG("Spanish - Uruguay", "es_uy", 0x380A), + LANG("Spanish - Venezuela", "es_ve", 0x200A), + LANG("Southern Sotho", "st", 0x0430), + LANG("Swahili", "sw", 0x0441), + LANG("Swedish - Sweden", "sv_se", 0x041D), + LANG("Swedish - Finland", "sv_fi", 0x081D), + LANG("Tamil", "ta", 0x0449), + LANG("Tatar", "tt", 0X0444), + LANG("Thai", "th", 0x041E), + LANG("Turkish", "tr", 0x041F), + LANG("Tsonga", "ts", 0x0431), + LANG("Ukrainian", "uk", 0x0422), + LANG("Urdu", "ur", 0x0420), + LANG("Uzbek - Cyrillic", "uz_uz", 0x0843), + LANG("Uzbek – Latin", "uz_uz", 0x0443), + LANG("Vietnamese", "vi", 0x042A), + LANG("Xhosa", "xh", 0x0434), + LANG("Yiddish", "yi", 0x043D), + LANG("Zulu", "zu", 0x0435), + LANG(NULL, NULL, 0x0), +}; + +uint16_t get_usb_code_for_current_locale(void) +{ + char *locale; + char search_string[64]; + char *ptr; + struct lang_map_entry *lang; + + /* Get the current locale. */ + locale = setlocale(0, NULL); + if (!locale) + return 0x0; + + /* Make a copy of the current locale string. */ + strncpy(search_string, locale, sizeof(search_string)); + search_string[sizeof(search_string)-1] = '\0'; + + /* Chop off the encoding part, and make it lower case. */ + ptr = search_string; + while (*ptr) { + *ptr = tolower(*ptr); + if (*ptr == '.') { + *ptr = '\0'; + break; + } + ptr++; + } + + /* Find the entry which matches the string code of our locale. */ + lang = lang_map; + while (lang->string_code) { + if (!strcmp(lang->string_code, search_string)) { + return lang->usb_code; + } + lang++; + } + + /* There was no match. Find with just the language only. */ + /* Chop off the variant. Chop it off at the '_'. */ + ptr = search_string; + while (*ptr) { + *ptr = tolower(*ptr); + if (*ptr == '_') { + *ptr = '\0'; + break; + } + ptr++; + } + +#if 0 /* TODO: Do we need this? */ + /* Find the entry which matches the string code of our language. */ + lang = lang_map; + while (lang->string_code) { + if (!strcmp(lang->string_code, search_string)) { + return lang->usb_code; + } + lang++; + } +#endif + + /* Found nothing. */ + return 0x0; +} + +#ifdef __cplusplus +} +#endif diff --git a/cad/PrusaSlicer/files/patch-src_hints_HintsToPot.cpp b/cad/PrusaSlicer/files/patch-bundled__deps_hints_HintsToPot.cpp similarity index 69% rename from cad/PrusaSlicer/files/patch-src_hints_HintsToPot.cpp rename to cad/PrusaSlicer/files/patch-bundled__deps_hints_HintsToPot.cpp index 26f89c4506e5..4c83f2707722 100644 --- a/cad/PrusaSlicer/files/patch-src_hints_HintsToPot.cpp +++ b/cad/PrusaSlicer/files/patch-bundled__deps_hints_HintsToPot.cpp @@ -1,10 +1,10 @@ ---- src/hints/HintsToPot.cpp.orig 2022-11-13 19:31:07 UTC -+++ src/hints/HintsToPot.cpp +--- bundled_deps/hints/HintsToPot.cpp.orig 2024-09-18 13:39:04 UTC ++++ bundled_deps/hints/HintsToPot.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include diff --git a/cad/PrusaSlicer/files/patch-src_CMakeLists.txt b/cad/PrusaSlicer/files/patch-src_CMakeLists.txt index 7d4b3ff67350..5e5d7da84a52 100644 --- a/cad/PrusaSlicer/files/patch-src_CMakeLists.txt +++ b/cad/PrusaSlicer/files/patch-src_CMakeLists.txt @@ -1,20 +1,20 @@ ---- src/CMakeLists.txt.orig 2024-06-27 09:25:47 UTC +--- src/CMakeLists.txt.orig 2024-09-18 13:39:04 UTC +++ src/CMakeLists.txt -@@ -60,7 +60,7 @@ if (SLIC3R_GUI) +@@ -46,7 +46,7 @@ if (SLIC3R_GUI) endif() find_package(JPEG MODULE QUIET) - find_package(NanoSVG REQUIRED) + #find_package(NanoSVG REQUIRED) string(REGEX MATCH "wxpng" WX_PNG_BUILTIN ${wxWidgets_LIBRARIES}) if (PNG_FOUND AND NOT WX_PNG_BUILTIN) -@@ -123,7 +123,7 @@ endif () +@@ -109,7 +109,7 @@ endif () set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") endif () -target_link_libraries(PrusaSlicer libslic3r libcereal) +target_link_libraries(PrusaSlicer libslic3r) if (APPLE) # add_compile_options(-stdlib=libc++) diff --git a/cad/PrusaSlicer/files/patch-src_hidapi_libusb_hid.c b/cad/PrusaSlicer/files/patch-src_hidapi_libusb_hid.c index 5cc678a4b344..f5adfcade526 100644 --- a/cad/PrusaSlicer/files/patch-src_hidapi_libusb_hid.c +++ b/cad/PrusaSlicer/files/patch-src_hidapi_libusb_hid.c @@ -1,1517 +1,1517 @@ ---- src/hidapi/libusb/hid.c.orig 2022-11-13 14:36:46 UTC +--- src/hidapi/libusb/hid.c.orig 2024-10-15 09:14:40 UTC +++ src/hidapi/libusb/hid.c @@ -0,0 +1,1514 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + Linux Version - 6/2/2010 + Libusb Version - 8/13/2010 + FreeBSD Version - 11/1/2011 + + 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 . +********************************************************/ + +#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ + +/* C */ +#include +#include +#include +#include +#include +#include + +/* Unix */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* GNU / LibUSB */ +#include +#ifndef __ANDROID__ +#include +#endif + +#include "hidapi.h" + +#ifdef __ANDROID__ + +/* Barrier implementation because Android/Bionic don't have pthread_barrier. + This implementation came from Brent Priddy and was posted on + StackOverflow. It is used with his permission. */ +typedef int pthread_barrierattr_t; +typedef struct pthread_barrier { + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int trip_count; +} pthread_barrier_t; + +static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) +{ + if(count == 0) { + errno = EINVAL; + return -1; + } + + if(pthread_mutex_init(&barrier->mutex, 0) < 0) { + return -1; + } + if(pthread_cond_init(&barrier->cond, 0) < 0) { + pthread_mutex_destroy(&barrier->mutex); + return -1; + } + barrier->trip_count = count; + barrier->count = 0; + + return 0; +} + +static int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +static int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + pthread_mutex_lock(&barrier->mutex); + ++(barrier->count); + if(barrier->count >= barrier->trip_count) + { + barrier->count = 0; + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return 1; + } + else + { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + pthread_mutex_unlock(&barrier->mutex); + return 0; + } +} + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DEBUG_PRINTF +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#else +#define LOG(...) do {} while (0) +#endif + +#ifndef __FreeBSD__ +#define DETACH_KERNEL_DRIVER +#endif + +/* Uncomment to enable the retrieval of Usage and Usage Page in +hid_enumerate(). Warning, on platforms different from FreeBSD +this is very invasive as it requires the detach +and re-attach of the kernel driver. See comments inside hid_enumerate(). +libusb HIDAPI programs are encouraged to use the interface number +instead to differentiate between interfaces on a composite HID device. */ +/*#define INVASIVE_GET_USAGE*/ + +/* Linked List of input reports received from the device. */ +struct input_report { + uint8_t *data; + size_t len; + struct input_report *next; +}; + + +struct hid_device_ { + /* Handle to the actual device. */ + libusb_device_handle *device_handle; + + /* Endpoint information */ + int input_endpoint; + int output_endpoint; + int input_ep_max_packet_size; + + /* The interface number of the HID */ + int interface; + + /* Indexes of Strings */ + int manufacturer_index; + int product_index; + int serial_index; + + /* Whether blocking reads are used */ + int blocking; /* boolean */ + + /* Read thread objects */ + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + int shutdown_thread; + int cancelled; + struct libusb_transfer *transfer; + + /* List of received input reports. */ + struct input_report *input_reports; +}; + +static libusb_context *usb_context = NULL; + +uint16_t get_usb_code_for_current_locale(void); +static int return_data(hid_device *dev, unsigned char *data, size_t length); + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->blocking = 1; + + pthread_mutex_init(&dev->mutex, NULL); + pthread_cond_init(&dev->condition, NULL); + pthread_barrier_init(&dev->barrier, NULL, 2); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + /* Clean up the thread objects */ + pthread_barrier_destroy(&dev->barrier); + pthread_cond_destroy(&dev->condition); + pthread_mutex_destroy(&dev->mutex); + + /* Free the device itself */ + free(dev); +} + +#if 0 +/*TODO: Implement this funciton on hidapi/libusb.. */ +static void register_error(hid_device *dev, const char *op) +{ + +} +#endif + +#ifdef INVASIVE_GET_USAGE +/* Get bytes from a HID Report Descriptor. + Only call with a num_bytes of 0, 1, 2, or 4. */ +static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur) +{ + /* Return if there aren't enough bytes. */ + if (cur + num_bytes >= len) + return 0; + + if (num_bytes == 0) + return 0; + else if (num_bytes == 1) { + return rpt[cur+1]; + } + else if (num_bytes == 2) { + return (rpt[cur+2] * 256 + rpt[cur+1]); + } + else if (num_bytes == 4) { + return (rpt[cur+4] * 0x01000000 + + rpt[cur+3] * 0x00010000 + + rpt[cur+2] * 0x00000100 + + rpt[cur+1] * 0x00000001); + } + else + return 0; +} + +/* Retrieves the device's Usage Page and Usage from the report + descriptor. The algorithm is simple, as it just returns the first + Usage and Usage Page that it finds in the descriptor. + The return value is 0 on success and -1 on failure. */ +static int get_usage(uint8_t *report_descriptor, size_t size, + unsigned short *usage_page, unsigned short *usage) +{ + unsigned int i = 0; + int size_code; + int data_len, key_size; + int usage_found = 0, usage_page_found = 0; + + while (i < size) { + int key = report_descriptor[i]; + int key_cmd = key & 0xfc; + + //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; + } + + if (key_cmd == 0x4) { + *usage_page = get_bytes(report_descriptor, size, data_len, i); + usage_page_found = 1; + //printf("Usage Page: %x\n", (uint32_t)*usage_page); + } + if (key_cmd == 0x8) { + *usage = get_bytes(report_descriptor, size, data_len, i); + usage_found = 1; + //printf("Usage: %x\n", (uint32_t)*usage); + } + + if (usage_page_found && usage_found) + return 0; /* success */ + + /* Skip over this key and it's associated data */ + i += data_len + key_size; + } + + return -1; /* failure */ +} +#endif /* INVASIVE_GET_USAGE */ + +#if defined(__FreeBSD__) && __FreeBSD__ < 10 +/* The libusb version included in FreeBSD < 10 doesn't have this function. In + mainline libusb, it's inlined in libusb.h. This function will bear a striking + resemblance to that one, because there's about one way to code it. + + Note that the data parameter is Unicode in UTF-16LE encoding. + Return value is the number of bytes in data, or LIBUSB_ERROR_*. + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, + uint8_t descriptor_index, uint16_t lang_id, + unsigned char *data, int length) +{ + return libusb_control_transfer(dev, + LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */ + LIBUSB_REQUEST_GET_DESCRIPTOR, + (LIBUSB_DT_STRING << 8) | descriptor_index, + lang_id, data, (uint16_t) length, 1000); +} + +#endif + + +/* Get the first language the device says it reports. This comes from + USB string #0. */ +static uint16_t get_first_language(libusb_device_handle *dev) +{ + uint16_t buf[32]; + int len; + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + 0x0, /* String ID */ + 0x0, /* Language */ + (unsigned char*)buf, + sizeof(buf)); + if (len < 4) + return 0x0; + + return buf[1]; /* First two bytes are len and descriptor type. */ +} + +static int is_language_supported(libusb_device_handle *dev, uint16_t lang) +{ + uint16_t buf[32]; + int len; + int i; + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + 0x0, /* String ID */ + 0x0, /* Language */ + (unsigned char*)buf, + sizeof(buf)); + if (len < 4) + return 0x0; + + + len /= 2; /* language IDs are two-bytes each. */ + /* Start at index 1 because there are two bytes of protocol data. */ + for (i = 1; i < len; i++) { + if (buf[i] == lang) + return 1; + } + + return 0; +} + + +/* This function returns a newly allocated wide string containing the USB + device string numbered by the index. The returned string must be freed + by using free(). */ +static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) +{ + char buf[512]; + int len; + wchar_t *str = NULL; + +#ifndef __ANDROID__ /* we don't use iconv on Android */ + wchar_t wbuf[256]; + /* iconv variables */ + iconv_t ic; + size_t inbytes; + size_t outbytes; + size_t res; +#ifdef __FreeBSD__ + const char *inptr; +#else + char *inptr; +#endif + char *outptr; +#endif + + /* Determine which language to use. */ + uint16_t lang; + lang = get_usb_code_for_current_locale(); + if (!is_language_supported(dev, lang)) + lang = get_first_language(dev); + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + idx, + lang, + (unsigned char*)buf, + sizeof(buf)); + if (len < 0) + return NULL; + +#ifdef __ANDROID__ + + /* Bionic does not have iconv support nor wcsdup() function, so it + has to be done manually. The following code will only work for + code points that can be represented as a single UTF-16 character, + and will incorrectly convert any code points which require more + than one UTF-16 character. + + Skip over the first character (2-bytes). */ + len -= 2; + str = malloc((len / 2 + 1) * sizeof(wchar_t)); + int i; + for (i = 0; i < len / 2; i++) { + str[i] = buf[i * 2 + 2] | (buf[i * 2 + 3] << 8); + } + str[len / 2] = 0x00000000; + +#else + + /* buf does not need to be explicitly NULL-terminated because + it is only passed into iconv() which does not need it. */ + + /* Initialize iconv. */ + ic = iconv_open("WCHAR_T", "UTF-16LE"); + if (ic == (iconv_t)-1) { + LOG("iconv_open() failed\n"); + return NULL; + } + + /* Convert to native wchar_t (UTF-32 on glibc/BSD systems). + Skip the first character (2-bytes). */ + inptr = buf+2; + inbytes = len-2; + outptr = (char*) wbuf; + outbytes = sizeof(wbuf); + res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes); + if (res == (size_t)-1) { + LOG("iconv() failed\n"); + goto err; + } + + /* Write the terminating NULL. */ + wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000; + if (outbytes >= sizeof(wbuf[0])) + *((wchar_t*)outptr) = 0x00000000; + + /* Allocate and copy the string. */ + str = wcsdup(wbuf); + +err: + iconv_close(ic); + +#endif + + return str; +} + +static char *make_path(libusb_device *dev, int interface_number) +{ + char str[64]; + snprintf(str, sizeof(str), "%04x:%04x:%02x", + libusb_get_bus_number(dev), + libusb_get_device_address(dev), + interface_number); + str[sizeof(str)-1] = '\0'; + + return strdup(str); +} + + +int HID_API_EXPORT hid_init(void) +{ + if (!usb_context) { + const char *locale; + + /* Init Libusb */ + if (libusb_init(&usb_context)) + return -1; + + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); + } + + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + if (usb_context) { + libusb_exit(usb_context); + usb_context = NULL; + } + + return 0; +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + libusb_device **devs; + libusb_device *dev; + libusb_device_handle *handle; + ssize_t num_devs; + int i = 0; + + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + + if(hid_init() < 0) + return NULL; + + num_devs = libusb_get_device_list(usb_context, &devs); + if (num_devs < 0) + return NULL; + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor desc; + struct libusb_config_descriptor *conf_desc = NULL; + int j, k; + int interface_num = 0; + + int res = libusb_get_device_descriptor(dev, &desc); + unsigned short dev_vid = desc.idVendor; + unsigned short dev_pid = desc.idProduct; + + res = libusb_get_active_config_descriptor(dev, &conf_desc); + if (res < 0) + libusb_get_config_descriptor(dev, 0, &conf_desc); + if (conf_desc) { + for (j = 0; j < conf_desc->bNumInterfaces; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc; + intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + interface_num = intf_desc->bInterfaceNumber; + + /* 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 = calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = make_path(dev, interface_num); + + res = libusb_open(dev, &handle); + + if (res >= 0) { + /* Serial Number */ + if (desc.iSerialNumber > 0) + cur_dev->serial_number = + get_usb_string(handle, desc.iSerialNumber); + + /* Manufacturer and Product strings */ + if (desc.iManufacturer > 0) + cur_dev->manufacturer_string = + get_usb_string(handle, desc.iManufacturer); + if (desc.iProduct > 0) + cur_dev->product_string = + get_usb_string(handle, desc.iProduct); + +#ifdef INVASIVE_GET_USAGE +{ + /* + This section is removed because it is too + invasive on the system. Getting a Usage Page + and Usage requires parsing the HID Report + descriptor. Getting a HID Report descriptor + involves claiming the interface. Claiming the + interface involves detaching the kernel driver. + Detaching the kernel driver is hard on the system + because it will unclaim interfaces (if another + app has them claimed) and the re-attachment of + the driver will sometimes change /dev entry names. + It is for these reasons that this section is + #if 0. For composite devices, use the interface + field in the hid_device_info struct to distinguish + between interfaces. */ + unsigned char data[256]; +#ifdef DETACH_KERNEL_DRIVER + int detached = 0; + /* Usage Page and Usage */ + res = libusb_kernel_driver_active(handle, interface_num); + if (res == 1) { + res = libusb_detach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't detach kernel driver, even though a kernel driver was attached."); + else + detached = 1; + } +#endif + res = libusb_claim_interface(handle, interface_num); + if (res >= 0) { + /* Get the HID Report Descriptor. */ + res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000); + if (res >= 0) { + unsigned short page=0, usage=0; + /* Parse the usage and usage page + out of the report descriptor. */ + get_usage(data, res, &page, &usage); + cur_dev->usage_page = page; + cur_dev->usage = usage; + } + else + LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res); + + /* Release the interface */ + res = libusb_release_interface(handle, interface_num); + if (res < 0) + LOG("Can't release the interface.\n"); + } + else + LOG("Can't claim interface %d\n", res); +#ifdef DETACH_KERNEL_DRIVER + /* Re-attach kernel driver if necessary. */ + if (detached) { + res = libusb_attach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't re-attach kernel driver.\n"); + } +#endif +} +#endif /* INVASIVE_GET_USAGE */ + + libusb_close(handle); + } + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + cur_dev->release_number = desc.bcdDevice; + + /* Interface Number */ + cur_dev->interface_number = interface_num; + } + } + } /* altsettings */ + } /* interfaces */ + libusb_free_config_descriptor(conf_desc); + } + } + + libusb_free_device_list(devs, 1); + + 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 (cur_dev->serial_number && + 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; +} + +static void read_callback(struct libusb_transfer *transfer) +{ + hid_device *dev = transfer->user_data; + int res; + + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + + struct input_report *rpt = malloc(sizeof(*rpt)); + rpt->data = malloc(transfer->actual_length); + memcpy(rpt->data, transfer->buffer, transfer->actual_length); + rpt->len = transfer->actual_length; + rpt->next = NULL; + + pthread_mutex_lock(&dev->mutex); + + /* Attach the new report object to the end of the list. */ + if (dev->input_reports == NULL) { + /* The list is empty. Put it at the root. */ + dev->input_reports = rpt; + pthread_cond_signal(&dev->condition); + } + else { + /* Find the end of the list and attach. */ + struct input_report *cur = dev->input_reports; + int num_queued = 0; + while (cur->next != NULL) { + cur = cur->next; + num_queued++; + } + cur->next = rpt; + + /* Pop one off if we've reached 30 in the queue. This + way we don't grow forever if the user never reads + anything from the device. */ + if (num_queued > 30) { + return_data(dev, NULL, 0); + } + } + pthread_mutex_unlock(&dev->mutex); + } + else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { + dev->shutdown_thread = 1; + dev->cancelled = 1; + return; + } + else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) { + dev->shutdown_thread = 1; + dev->cancelled = 1; + return; + } + else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { + //LOG("Timeout (normal)\n"); + } + else { + LOG("Unknown transfer code: %d\n", transfer->status); + } + + /* Re-submit the transfer object. */ + res = libusb_submit_transfer(transfer); + if (res != 0) { + LOG("Unable to submit URB. libusb error code: %d\n", res); + dev->shutdown_thread = 1; + dev->cancelled = 1; + } +} + + +static void *read_thread(void *param) +{ + hid_device *dev = param; + unsigned char *buf; + const size_t length = dev->input_ep_max_packet_size; + + /* Set up the transfer object. */ + buf = malloc(length); + dev->transfer = libusb_alloc_transfer(0); + libusb_fill_interrupt_transfer(dev->transfer, + dev->device_handle, + dev->input_endpoint, + buf, + length, + read_callback, + dev, + 5000/*timeout*/); + + /* Make the first submission. Further submissions are made + from inside read_callback() */ + libusb_submit_transfer(dev->transfer); + + /* Notify the main thread that the read thread is up and running. */ + pthread_barrier_wait(&dev->barrier); + + /* Handle all the events. */ + while (!dev->shutdown_thread) { + int res; + res = libusb_handle_events(usb_context); + if (res < 0) { + /* There was an error. */ + LOG("read_thread(): libusb reports error # %d\n", res); + + /* Break out of this loop only on fatal error.*/ + if (res != LIBUSB_ERROR_BUSY && + res != LIBUSB_ERROR_TIMEOUT && + res != LIBUSB_ERROR_OVERFLOW && + res != LIBUSB_ERROR_INTERRUPTED) { + break; + } + } + } + + /* Cancel any transfer that may be pending. This call will fail + if no transfers are pending, but that's OK. */ + libusb_cancel_transfer(dev->transfer); + + while (!dev->cancelled) + libusb_handle_events_completed(usb_context, &dev->cancelled); + + /* Now that the read thread is stopping, Wake any threads which are + waiting on data (in hid_read_timeout()). Do this under a mutex to + make sure that a thread which is about to go to sleep waiting on + the condition actually will go to sleep before the condition is + signaled. */ + pthread_mutex_lock(&dev->mutex); + pthread_cond_broadcast(&dev->condition); + pthread_mutex_unlock(&dev->mutex); + + /* The dev->transfer->buffer and dev->transfer objects are cleaned up + in hid_close(). They are not cleaned up here because this thread + could end either due to a disconnect or due to a user + call to hid_close(). In both cases the objects can be safely + cleaned up after the call to pthread_join() (in hid_close()), but + since hid_close() calls libusb_cancel_transfer(), on these objects, + they can not be cleaned up here. */ + + return NULL; +} + + +hid_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + + libusb_device **devs; + libusb_device *usb_dev; + int res; + int d = 0; + int good_open = 0; + + if(hid_init() < 0) + return NULL; + + dev = new_hid_device(); + + libusb_get_device_list(usb_context, &devs); + while ((usb_dev = devs[d++]) != NULL) { + struct libusb_device_descriptor desc; + struct libusb_config_descriptor *conf_desc = NULL; + int i,j,k; + libusb_get_device_descriptor(usb_dev, &desc); + + if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) + continue; + for (j = 0; j < conf_desc->bNumInterfaces; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc; + intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber); + if (!strcmp(dev_path, path)) { + /* Matched Paths. Open this device */ + + /* OPEN HERE */ + res = libusb_open(usb_dev, &dev->device_handle); + if (res < 0) { + LOG("can't open device\n"); + free(dev_path); + break; + } + good_open = 1; +#ifdef DETACH_KERNEL_DRIVER + /* Detach the kernel driver, but only if the + device is managed by the kernel */ + if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { + res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + libusb_close(dev->device_handle); + LOG("Unable to detach Kernel Driver\n"); + free(dev_path); + good_open = 0; + break; + } + } +#endif + res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); + free(dev_path); + libusb_close(dev->device_handle); + good_open = 0; + break; + } + + /* Store off the string descriptor indexes */ + dev->manufacturer_index = desc.iManufacturer; + dev->product_index = desc.iProduct; + dev->serial_index = desc.iSerialNumber; + + /* Store off the interface number */ + dev->interface = intf_desc->bInterfaceNumber; + + /* Find the INPUT and OUTPUT endpoints. An + OUTPUT endpoint is not required. */ + for (i = 0; i < intf_desc->bNumEndpoints; i++) { + const struct libusb_endpoint_descriptor *ep + = &intf_desc->endpoint[i]; + + /* Determine the type and direction of this + endpoint. */ + int is_interrupt = + (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) + == LIBUSB_TRANSFER_TYPE_INTERRUPT; + int is_output = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_OUT; + int is_input = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_IN; + + /* Decide whether to use it for input or output. */ + if (dev->input_endpoint == 0 && + is_interrupt && is_input) { + /* Use this endpoint for INPUT */ + dev->input_endpoint = ep->bEndpointAddress; + dev->input_ep_max_packet_size = ep->wMaxPacketSize; + } + if (dev->output_endpoint == 0 && + is_interrupt && is_output) { + /* Use this endpoint for OUTPUT */ + dev->output_endpoint = ep->bEndpointAddress; + } + } + + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + + } + free(dev_path); + } + } + } + libusb_free_config_descriptor(conf_desc); + + } + + libusb_free_device_list(devs, 1); + + /* If we have a good handle, return it. */ + if (good_open) { + return dev; + } + else { + /* Unable to open any devices. */ + free_hid_device(dev); + return NULL; + } +} + + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + int res; + int report_number = data[0]; + int skipped_report_id = 0; + + if (report_number == 0x0) { + data++; + length--; + skipped_report_id = 1; + } + + + if (dev->output_endpoint <= 0) { + /* No interrupt out endpoint. Use the Control Endpoint */ + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, + 0x09/*HID Set_Report*/, + (2/*HID output*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + if (skipped_report_id) + length++; + + return length; + } + else { + /* Use the interrupt out endpoint */ + int actual_length; + res = libusb_interrupt_transfer(dev->device_handle, + dev->output_endpoint, + (unsigned char*)data, + length, + &actual_length, 1000); + + if (res < 0) + return -1; + + if (skipped_report_id) + actual_length++; + + return actual_length; + } +} + +/* Helper function, to simplify hid_read(). + This should be called with dev->mutex locked. */ +static int return_data(hid_device *dev, unsigned char *data, size_t length) +{ + /* Copy the data out of the linked list item (rpt) into the + return buffer (data), and delete the liked list item. */ + struct input_report *rpt = dev->input_reports; + size_t len = (length < rpt->len)? length: rpt->len; + if (len > 0) + memcpy(data, rpt->data, len); + dev->input_reports = rpt->next; + free(rpt->data); + free(rpt); + return len; +} + +static void cleanup_mutex(void *param) +{ + hid_device *dev = param; + pthread_mutex_unlock(&dev->mutex); +} + + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read = -1; + +#if 0 + int transferred; + int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000); + LOG("transferred: %d\n", transferred); + return transferred; +#endif + + pthread_mutex_lock(&dev->mutex); + pthread_cleanup_push(&cleanup_mutex, dev); + + /* There's an input report queued up. Return it. */ + if (dev->input_reports) { + /* Return the first one */ + bytes_read = return_data(dev, data, length); + goto ret; + } + + if (dev->shutdown_thread) { + /* This means the device has been disconnected. + An error code of -1 should be returned. */ + bytes_read = -1; + goto ret; + } + + if (milliseconds == -1) { + /* Blocking */ + while (!dev->input_reports && !dev->shutdown_thread) { + pthread_cond_wait(&dev->condition, &dev->mutex); + } + if (dev->input_reports) { + bytes_read = return_data(dev, data, length); + } + } + else if (milliseconds > 0) { + /* Non-blocking, but called with timeout. */ + int res; + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += milliseconds / 1000; + ts.tv_nsec += (milliseconds % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + while (!dev->input_reports && !dev->shutdown_thread) { + res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts); + if (res == 0) { + if (dev->input_reports) { + bytes_read = return_data(dev, data, length); + break; + } + + /* If we're here, there was a spurious wake up + or the read thread was shutdown. Run the + loop again (ie: don't break). */ + } + else if (res == ETIMEDOUT) { + /* Timed out. */ + bytes_read = 0; + break; + } + else { + /* Error. */ + bytes_read = -1; + break; + } + } + } + else { + /* Purely non-blocking */ + bytes_read = 0; + } + +ret: + pthread_mutex_unlock(&dev->mutex); + pthread_cleanup_pop(0); + + 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) +{ + dev->blocking = !nonblock; + + return 0; +} + + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + int res = -1; + int skipped_report_id = 0; + int report_number = data[0]; + + if (report_number == 0x0) { + data++; + length--; + skipped_report_id = 1; + } + + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, + 0x09/*HID set_report*/, + (3/*HID feature*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + /* Account for the report ID */ + if (skipped_report_id) + length++; + + return length; +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + int res = -1; + int skipped_report_id = 0; + int report_number = data[0]; + + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + data++; + length--; + skipped_report_id = 1; + } + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN, + 0x01/*HID get_report*/, + (3/*HID feature*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + if (skipped_report_id) + res++; + + return res; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + + /* Cause read_thread() to stop. */ + dev->shutdown_thread = 1; + libusb_cancel_transfer(dev->transfer); + + /* Wait for read_thread() to end. */ + pthread_join(dev->thread, NULL); + + /* Clean up the Transfer objects allocated in read_thread(). */ + free(dev->transfer->buffer); + libusb_free_transfer(dev->transfer); + + /* release the interface */ + libusb_release_interface(dev->device_handle, dev->interface); + + /* Close the handle */ + libusb_close(dev->device_handle); + + /* Clear out the queue of received reports. */ + pthread_mutex_lock(&dev->mutex); + while (dev->input_reports) { + return_data(dev, NULL, 0); + } + pthread_mutex_unlock(&dev->mutex); + + free_hid_device(dev); +} + + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->manufacturer_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->product_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->serial_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + wchar_t *str; + + str = get_usb_string(dev->device_handle, string_index); + if (str) { + wcsncpy(string, str, maxlen); + string[maxlen-1] = L'\0'; + free(str); + return 0; + } + else + return -1; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return NULL; +} + + +struct lang_map_entry { + const char *name; + const char *string_code; + uint16_t usb_code; +}; + +#define LANG(name,code,usb_code) { name, code, usb_code } +static struct lang_map_entry lang_map[] = { + LANG("Afrikaans", "af", 0x0436), + LANG("Albanian", "sq", 0x041C), + LANG("Arabic - United Arab Emirates", "ar_ae", 0x3801), + LANG("Arabic - Bahrain", "ar_bh", 0x3C01), + LANG("Arabic - Algeria", "ar_dz", 0x1401), + LANG("Arabic - Egypt", "ar_eg", 0x0C01), + LANG("Arabic - Iraq", "ar_iq", 0x0801), + LANG("Arabic - Jordan", "ar_jo", 0x2C01), + LANG("Arabic - Kuwait", "ar_kw", 0x3401), + LANG("Arabic - Lebanon", "ar_lb", 0x3001), + LANG("Arabic - Libya", "ar_ly", 0x1001), + LANG("Arabic - Morocco", "ar_ma", 0x1801), + LANG("Arabic - Oman", "ar_om", 0x2001), + LANG("Arabic - Qatar", "ar_qa", 0x4001), + LANG("Arabic - Saudi Arabia", "ar_sa", 0x0401), + LANG("Arabic - Syria", "ar_sy", 0x2801), + LANG("Arabic - Tunisia", "ar_tn", 0x1C01), + LANG("Arabic - Yemen", "ar_ye", 0x2401), + LANG("Armenian", "hy", 0x042B), + LANG("Azeri - Latin", "az_az", 0x042C), + LANG("Azeri - Cyrillic", "az_az", 0x082C), + LANG("Basque", "eu", 0x042D), + LANG("Belarusian", "be", 0x0423), + LANG("Bulgarian", "bg", 0x0402), + LANG("Catalan", "ca", 0x0403), + LANG("Chinese - China", "zh_cn", 0x0804), + LANG("Chinese - Hong Kong SAR", "zh_hk", 0x0C04), + LANG("Chinese - Macau SAR", "zh_mo", 0x1404), + LANG("Chinese - Singapore", "zh_sg", 0x1004), + LANG("Chinese - Taiwan", "zh_tw", 0x0404), + LANG("Croatian", "hr", 0x041A), + LANG("Czech", "cs", 0x0405), + LANG("Danish", "da", 0x0406), + LANG("Dutch - Netherlands", "nl_nl", 0x0413), + LANG("Dutch - Belgium", "nl_be", 0x0813), + LANG("English - Australia", "en_au", 0x0C09), + LANG("English - Belize", "en_bz", 0x2809), + LANG("English - Canada", "en_ca", 0x1009), + LANG("English - Caribbean", "en_cb", 0x2409), + LANG("English - Ireland", "en_ie", 0x1809), + LANG("English - Jamaica", "en_jm", 0x2009), + LANG("English - New Zealand", "en_nz", 0x1409), + LANG("English - Phillippines", "en_ph", 0x3409), + LANG("English - Southern Africa", "en_za", 0x1C09), + LANG("English - Trinidad", "en_tt", 0x2C09), + LANG("English - Great Britain", "en_gb", 0x0809), + LANG("English - United States", "en_us", 0x0409), + LANG("Estonian", "et", 0x0425), + LANG("Farsi", "fa", 0x0429), + LANG("Finnish", "fi", 0x040B), + LANG("Faroese", "fo", 0x0438), + LANG("French - France", "fr_fr", 0x040C), + LANG("French - Belgium", "fr_be", 0x080C), + LANG("French - Canada", "fr_ca", 0x0C0C), + LANG("French - Luxembourg", "fr_lu", 0x140C), + LANG("French - Switzerland", "fr_ch", 0x100C), + LANG("Gaelic - Ireland", "gd_ie", 0x083C), + LANG("Gaelic - Scotland", "gd", 0x043C), + LANG("German - Germany", "de_de", 0x0407), + LANG("German - Austria", "de_at", 0x0C07), + LANG("German - Liechtenstein", "de_li", 0x1407), + LANG("German - Luxembourg", "de_lu", 0x1007), + LANG("German - Switzerland", "de_ch", 0x0807), + LANG("Greek", "el", 0x0408), + LANG("Hebrew", "he", 0x040D), + LANG("Hindi", "hi", 0x0439), + LANG("Hungarian", "hu", 0x040E), + LANG("Icelandic", "is", 0x040F), + LANG("Indonesian", "id", 0x0421), + LANG("Italian - Italy", "it_it", 0x0410), + LANG("Italian - Switzerland", "it_ch", 0x0810), + LANG("Japanese", "ja", 0x0411), + LANG("Korean", "ko", 0x0412), + LANG("Latvian", "lv", 0x0426), + LANG("Lithuanian", "lt", 0x0427), + LANG("F.Y.R.O. Macedonia", "mk", 0x042F), + LANG("Malay - Malaysia", "ms_my", 0x043E), + LANG("Malay – Brunei", "ms_bn", 0x083E), + LANG("Maltese", "mt", 0x043A), + LANG("Marathi", "mr", 0x044E), + LANG("Norwegian - Bokml", "no_no", 0x0414), + LANG("Norwegian - Nynorsk", "no_no", 0x0814), + LANG("Polish", "pl", 0x0415), + LANG("Portuguese - Portugal", "pt_pt", 0x0816), + LANG("Portuguese - Brazil", "pt_br", 0x0416), + LANG("Raeto-Romance", "rm", 0x0417), + LANG("Romanian - Romania", "ro", 0x0418), + LANG("Romanian - Republic of Moldova", "ro_mo", 0x0818), + LANG("Russian", "ru", 0x0419), + LANG("Russian - Republic of Moldova", "ru_mo", 0x0819), + LANG("Sanskrit", "sa", 0x044F), + LANG("Serbian - Cyrillic", "sr_sp", 0x0C1A), + LANG("Serbian - Latin", "sr_sp", 0x081A), + LANG("Setsuana", "tn", 0x0432), + LANG("Slovenian", "sl", 0x0424), + LANG("Slovak", "sk", 0x041B), + LANG("Sorbian", "sb", 0x042E), + LANG("Spanish - Spain (Traditional)", "es_es", 0x040A), + LANG("Spanish - Argentina", "es_ar", 0x2C0A), + LANG("Spanish - Bolivia", "es_bo", 0x400A), + LANG("Spanish - Chile", "es_cl", 0x340A), + LANG("Spanish - Colombia", "es_co", 0x240A), + LANG("Spanish - Costa Rica", "es_cr", 0x140A), + LANG("Spanish - Dominican Republic", "es_do", 0x1C0A), + LANG("Spanish - Ecuador", "es_ec", 0x300A), + LANG("Spanish - Guatemala", "es_gt", 0x100A), + LANG("Spanish - Honduras", "es_hn", 0x480A), + LANG("Spanish - Mexico", "es_mx", 0x080A), + LANG("Spanish - Nicaragua", "es_ni", 0x4C0A), + LANG("Spanish - Panama", "es_pa", 0x180A), + LANG("Spanish - Peru", "es_pe", 0x280A), + LANG("Spanish - Puerto Rico", "es_pr", 0x500A), + LANG("Spanish - Paraguay", "es_py", 0x3C0A), + LANG("Spanish - El Salvador", "es_sv", 0x440A), + LANG("Spanish - Uruguay", "es_uy", 0x380A), + LANG("Spanish - Venezuela", "es_ve", 0x200A), + LANG("Southern Sotho", "st", 0x0430), + LANG("Swahili", "sw", 0x0441), + LANG("Swedish - Sweden", "sv_se", 0x041D), + LANG("Swedish - Finland", "sv_fi", 0x081D), + LANG("Tamil", "ta", 0x0449), + LANG("Tatar", "tt", 0X0444), + LANG("Thai", "th", 0x041E), + LANG("Turkish", "tr", 0x041F), + LANG("Tsonga", "ts", 0x0431), + LANG("Ukrainian", "uk", 0x0422), + LANG("Urdu", "ur", 0x0420), + LANG("Uzbek - Cyrillic", "uz_uz", 0x0843), + LANG("Uzbek – Latin", "uz_uz", 0x0443), + LANG("Vietnamese", "vi", 0x042A), + LANG("Xhosa", "xh", 0x0434), + LANG("Yiddish", "yi", 0x043D), + LANG("Zulu", "zu", 0x0435), + LANG(NULL, NULL, 0x0), +}; + +uint16_t get_usb_code_for_current_locale(void) +{ + char *locale; + char search_string[64]; + char *ptr; + struct lang_map_entry *lang; + + /* Get the current locale. */ + locale = setlocale(0, NULL); + if (!locale) + return 0x0; + + /* Make a copy of the current locale string. */ + strncpy(search_string, locale, sizeof(search_string)); + search_string[sizeof(search_string)-1] = '\0'; + + /* Chop off the encoding part, and make it lower case. */ + ptr = search_string; + while (*ptr) { + *ptr = tolower(*ptr); + if (*ptr == '.') { + *ptr = '\0'; + break; + } + ptr++; + } + + /* Find the entry which matches the string code of our locale. */ + lang = lang_map; + while (lang->string_code) { + if (!strcmp(lang->string_code, search_string)) { + return lang->usb_code; + } + lang++; + } + + /* There was no match. Find with just the language only. */ + /* Chop off the variant. Chop it off at the '_'. */ + ptr = search_string; + while (*ptr) { + *ptr = tolower(*ptr); + if (*ptr == '_') { + *ptr = '\0'; + break; + } + ptr++; + } + +#if 0 /* TODO: Do we need this? */ + /* Find the entry which matches the string code of our language. */ + lang = lang_map; + while (lang->string_code) { + if (!strcmp(lang->string_code, search_string)) { + return lang->usb_code; + } + lang++; + } +#endif + + /* Found nothing. */ + return 0x0; +} + +#ifdef __cplusplus +} +#endif diff --git a/cad/PrusaSlicer/files/patch-src_libnanosvg_nanosvgrast.h b/cad/PrusaSlicer/files/patch-src_libnanosvg_nanosvgrast.h index 1c1f0565a022..42d0fb84acd2 100644 --- a/cad/PrusaSlicer/files/patch-src_libnanosvg_nanosvgrast.h +++ b/cad/PrusaSlicer/files/patch-src_libnanosvg_nanosvgrast.h @@ -1,1485 +1,1485 @@ ---- src/libnanosvg/nanosvgrast.h.orig 2024-01-12 13:12:38 UTC +--- src/libnanosvg/nanosvgrast.h.orig 2024-10-16 10:58:04 UTC +++ src/libnanosvg/nanosvgrast.h @@ -0,0 +1,1482 @@ +/* + * Copyright (c) 2013-14 Mikko Mononen memon@inside.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The polygon rasterization is heavily based on stb_truetype rasterizer + * by Sean Barrett - http://nothings.org/ + * + */ + +/* Modified by FLTK to support non-square X,Y axes scaling. + * + * Added: nsvgRasterizeXY() +*/ + + +#ifndef NANOSVGRAST_H +#define NANOSVGRAST_H + +#include "nanosvg.h" + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +extern "C" { +#endif +#endif + +typedef struct NSVGrasterizer NSVGrasterizer; + +/* Example Usage: + // Load SVG + NSVGimage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + + // Create rasterizer (can be used to render multiple images). + struct NSVGrasterizer* rast = nsvgCreateRasterizer(); + // Allocate memory for image + unsigned char* img = malloc(w*h*4); + // Rasterize + nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); + + // For non-square X,Y scaling, use + nsvgRasterizeXY(rast, image, 0,0,1,1, img, w, h, w*4); +*/ + +// Allocated rasterizer context. -+NSVGrasterizer* nsvgCreateRasterizer(void); ++static NSVGrasterizer* nsvgCreateRasterizer(void); + +// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) +// r - pointer to rasterizer context +// image - pointer to image to rasterize +// tx,ty - image offset (applied after scaling) +// scale - image scale (assumes square aspect ratio) +// dst - pointer to destination image data, 4 bytes per pixel (RGBA) +// w - width of the image to render +// h - height of the image to render +// stride - number of bytes per scaleline in the destination buffer -+void nsvgRasterize(NSVGrasterizer* r, ++static void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride); + +// As above, but allow X and Y axes to scale independently for non-square aspects -+void nsvgRasterizeXY(NSVGrasterizer* r, ++static void nsvgRasterizeXY(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, + float sx, float sy, + unsigned char* dst, int w, int h, int stride); + +// Deletes rasterizer context. -+void nsvgDeleteRasterizer(NSVGrasterizer*); ++static void nsvgDeleteRasterizer(NSVGrasterizer*); + + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + -+#ifdef NANOSVGRAST_IMPLEMENTATION ++#if 1 || defined(NANOSVGRAST_IMPLEMENTATION) + +#include +#include +#include + +#define NSVG__SUBSAMPLES 5 +#define NSVG__FIXSHIFT 10 +#define NSVG__FIX (1 << NSVG__FIXSHIFT) +#define NSVG__FIXMASK (NSVG__FIX-1) +#define NSVG__MEMPAGE_SIZE 1024 + +typedef struct NSVGedge { + float x0,y0, x1,y1; + int dir; + struct NSVGedge* next; +} NSVGedge; + +typedef struct NSVGpoint { + float x, y; + float dx, dy; + float len; + float dmx, dmy; + unsigned char flags; +} NSVGpoint; + +typedef struct NSVGactiveEdge { + int x,dx; + float ey; + int dir; + struct NSVGactiveEdge *next; +} NSVGactiveEdge; + +typedef struct NSVGmemPage { + unsigned char mem[NSVG__MEMPAGE_SIZE]; + int size; + struct NSVGmemPage* next; +} NSVGmemPage; + +typedef struct NSVGcachedPaint { + signed char type; + char spread; + float xform[6]; + unsigned int colors[256]; +} NSVGcachedPaint; + +struct NSVGrasterizer +{ + float px, py; + + float tessTol; + float distTol; + + NSVGedge* edges; + int nedges; + int cedges; + + NSVGpoint* points; + int npoints; + int cpoints; + + NSVGpoint* points2; + int npoints2; + int cpoints2; + + NSVGactiveEdge* freelist; + NSVGmemPage* pages; + NSVGmemPage* curpage; + + unsigned char* scanline; + int cscanline; + + unsigned char* bitmap; + int width, height, stride; +}; + -+NSVGrasterizer* nsvgCreateRasterizer(void) ++static NSVGrasterizer* nsvgCreateRasterizer(void) +{ + NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); + if (r == NULL) goto error; + memset(r, 0, sizeof(NSVGrasterizer)); + + r->tessTol = 0.25f; + r->distTol = 0.01f; + + return r; + +error: + nsvgDeleteRasterizer(r); + return NULL; +} + -+void nsvgDeleteRasterizer(NSVGrasterizer* r) ++static void nsvgDeleteRasterizer(NSVGrasterizer* r) +{ + NSVGmemPage* p; + + if (r == NULL) return; + + p = r->pages; + while (p != NULL) { + NSVGmemPage* next = p->next; + free(p); + p = next; + } + + if (r->edges) free(r->edges); + if (r->points) free(r->points); + if (r->points2) free(r->points2); + if (r->scanline) free(r->scanline); + + free(r); +} + +static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) +{ + NSVGmemPage *newp; + + // If using existing chain, return the next page in chain + if (cur != NULL && cur->next != NULL) { + return cur->next; + } + + // Alloc new page + newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); + if (newp == NULL) return NULL; + memset(newp, 0, sizeof(NSVGmemPage)); + + // Add to linked list + if (cur != NULL) + cur->next = newp; + else + r->pages = newp; + + return newp; +} + +static void nsvg__resetPool(NSVGrasterizer* r) +{ + NSVGmemPage* p = r->pages; + while (p != NULL) { + p->size = 0; + p = p->next; + } + r->curpage = r->pages; +} + +static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) +{ + unsigned char* buf; + if (size > NSVG__MEMPAGE_SIZE) return NULL; + if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { + r->curpage = nsvg__nextPage(r, r->curpage); + } + buf = &r->curpage->mem[r->curpage->size]; + r->curpage->size += size; + return buf; +} + +static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) +{ + float dx = x2 - x1; + float dy = y2 - y1; + return dx*dx + dy*dy < tol*tol; +} + +static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) +{ + NSVGpoint* pt; + + if (r->npoints > 0) { + pt = &r->points[r->npoints-1]; + if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { + pt->flags = (unsigned char)(pt->flags | flags); + return; + } + } + + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + + pt = &r->points[r->npoints]; + pt->x = x; + pt->y = y; + pt->flags = (unsigned char)flags; + r->npoints++; +} + +static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) +{ + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + r->points[r->npoints] = pt; + r->npoints++; +} + +static void nsvg__duplicatePoints(NSVGrasterizer* r) +{ + if (r->npoints > r->cpoints2) { + r->cpoints2 = r->npoints; + r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); + if (r->points2 == NULL) return; + } + + memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); + r->npoints2 = r->npoints; +} + +static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) +{ + NSVGedge* e; + + // Skip horizontal edges + if (y0 == y1) + return; + + if (r->nedges+1 > r->cedges) { + r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; + r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); + if (r->edges == NULL) return; + } + + e = &r->edges[r->nedges]; + r->nedges++; + + if (y0 < y1) { + e->x0 = x0; + e->y0 = y0; + e->x1 = x1; + e->y1 = y1; + e->dir = 1; + } else { + e->x0 = x1; + e->y0 = y1; + e->x1 = x0; + e->y1 = y0; + e->dir = -1; + } +} + +static float nsvg__normalize(float *x, float* y) +{ + float d = sqrtf((*x)*(*x) + (*y)*(*y)); + if (d > 1e-6f) { + float id = 1.0f / d; + *x *= id; + *y *= id; + } + return d; +} + +static float nsvg__absf(float x) { return x < 0 ? -x : x; } + +static void nsvg__flattenCubicBez(NSVGrasterizer* r, + float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4, + int level, int type) +{ + float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; + float dx,dy,d2,d3; + + if (level > 10) return; + + x12 = (x1+x2)*0.5f; + y12 = (y1+y2)*0.5f; + x23 = (x2+x3)*0.5f; + y23 = (y2+y3)*0.5f; + x34 = (x3+x4)*0.5f; + y34 = (y3+y4)*0.5f; + x123 = (x12+x23)*0.5f; + y123 = (y12+y23)*0.5f; + + dx = x4 - x1; + dy = y4 - y1; + d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); + d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); + + if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { + nsvg__addPathPoint(r, x4, y4, type); + return; + } + + x234 = (x23+x34)*0.5f; + y234 = (y23+y34)*0.5f; + x1234 = (x123+x234)*0.5f; + y1234 = (y123+y234)*0.5f; + + nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); + nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); +} + +static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float sx, float sy) +{ + int i, j; + NSVGpath* path; + + for (path = shape->paths; path != NULL; path = path->next) { + r->npoints = 0; + // Flatten path + nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 0); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*sx,p[1]*sy, p[2]*sx,p[3]*sy, p[4]*sx,p[5]*sy, p[6]*sx,p[7]*sy, 0, 0); + } + // Close path + nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 0); + // Build edges + for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) + nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); + } +} + +enum NSVGpointFlags +{ + NSVG_PT_CORNER = 0x01, + NSVG_PT_BEVEL = 0x02, + NSVG_PT_LEFT = 0x04 +}; + +static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + float len = nsvg__normalize(&dx, &dy); + float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x - dx*w, py = p->y - dy*w; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +#ifndef NSVG_PI +#define NSVG_PI (3.14159265358979323846264338327f) +#endif + +static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) +{ + int i; + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; + + for (i = 0; i < ncap; i++) { + float a = (float)i/(float)(ncap-1)*NSVG_PI; + float ax = cosf(a) * w, ay = sinf(a) * w; + float x = px - dlx*ax - dx*ay; + float y = py - dly*ax - dy*ay; + + if (i > 0) + nsvg__addEdge(r, prevx, prevy, x, y); + + prevx = x; + prevy = y; + + if (i == 0) { + lx = x; ly = y; + } else if (i == ncap-1) { + rx = x; ry = y; + } + } + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); + float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); + float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); + float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); + + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0, rx0, lx1, rx1; + float ly0, ry0, ly1, ry1; + + if (p1->flags & NSVG_PT_LEFT) { + lx0 = lx1 = p1->x - p1->dmx * w; + ly0 = ly1 = p1->y - p1->dmy * w; + nsvg__addEdge(r, lx1, ly1, left->x, left->y); + + rx0 = p1->x + (dlx0 * w); + ry0 = p1->y + (dly0 * w); + rx1 = p1->x + (dlx1 * w); + ry1 = p1->y + (dly1 * w); + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + } else { + lx0 = p1->x - (dlx0 * w); + ly0 = p1->y - (dly0 * w); + lx1 = p1->x - (dlx1 * w); + ly1 = p1->y - (dly1 * w); + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + rx0 = rx1 = p1->x + p1->dmx * w; + ry0 = ry1 = p1->y + p1->dmy * w; + nsvg__addEdge(r, right->x, right->y, rx1, ry1); + } + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) +{ + int i, n; + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float a0 = atan2f(dly0, dlx0); + float a1 = atan2f(dly1, dlx1); + float da = a1 - a0; + float lx, ly, rx, ry; + + if (da < NSVG_PI) da += NSVG_PI*2; + if (da > NSVG_PI) da -= NSVG_PI*2; + + n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); + if (n < 2) n = 2; + if (n > ncap) n = ncap; + + lx = left->x; + ly = left->y; + rx = right->x; + ry = right->y; + + for (i = 0; i < n; i++) { + float u = (float)i/(float)(n-1); + float a = a0 + u*da; + float ax = cosf(a) * w, ay = sinf(a) * w; + float lx1 = p1->x - ax, ly1 = p1->y - ay; + float rx1 = p1->x + ax, ry1 = p1->y + ay; + + nsvg__addEdge(r, lx1, ly1, lx, ly); + nsvg__addEdge(r, rx, ry, rx1, ry1); + + lx = lx1; ly = ly1; + rx = rx1; ry = ry1; + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); + float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); + + nsvg__addEdge(r, lx, ly, left->x, left->y); + nsvg__addEdge(r, right->x, right->y, rx, ry); + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static int nsvg__curveDivs(float r, float arc, float tol) +{ + float da = acosf(r / (r + tol)) * 2.0f; + int divs = (int)ceilf(arc / da); + if (divs < 2) divs = 2; + return divs; +} + +static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) +{ + int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. + NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; + NSVGpoint* p0, *p1; + int j, s, e; + + // Build stroke edges + if (closed) { + // Looping + p0 = &points[npoints-1]; + p1 = &points[0]; + s = 0; + e = npoints; + } else { + // Add cap + p0 = &points[0]; + p1 = &points[1]; + s = 1; + e = npoints-1; + } + + if (closed) { + nsvg__initClosed(&left, &right, p0, p1, lineWidth); + firstLeft = left; + firstRight = right; + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); + } + + for (j = s; j < e; ++j) { + if (p1->flags & NSVG_PT_CORNER) { + if (lineJoin == NSVG_JOIN_ROUND) + nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); + else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) + nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); + else + nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); + } else { + nsvg__straightJoin(r, &left, &right, p1, lineWidth); + } + p0 = p1++; + } + + if (closed) { + // Loop it + nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); + nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); + } +} + +static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) +{ + int i, j; + NSVGpoint* p0, *p1; + + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (i = 0; i < r->npoints; i++) { + // Calculate segment direction and length + p0->dx = p1->x - p0->x; + p0->dy = p1->y - p0->y; + p0->len = nsvg__normalize(&p0->dx, &p0->dy); + // Advance + p0 = p1++; + } + + // calculate joins + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (j = 0; j < r->npoints; j++) { + float dlx0, dly0, dlx1, dly1, dmr2, cross; + dlx0 = p0->dy; + dly0 = -p0->dx; + dlx1 = p1->dy; + dly1 = -p1->dx; + // Calculate extrusions + p1->dmx = (dlx0 + dlx1) * 0.5f; + p1->dmy = (dly0 + dly1) * 0.5f; + dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; + if (dmr2 > 0.000001f) { + float s2 = 1.0f / dmr2; + if (s2 > 600.0f) { + s2 = 600.0f; + } + p1->dmx *= s2; + p1->dmy *= s2; + } + + // Clear flags, but keep the corner. + p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; + + // Keep track of left turns. + cross = p1->dx * p0->dy - p0->dx * p1->dy; + if (cross > 0.0f) + p1->flags |= NSVG_PT_LEFT; + + // Check to see if the corner needs to be beveled. + if (p1->flags & NSVG_PT_CORNER) { + if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { + p1->flags |= NSVG_PT_BEVEL; + } + } + + p0 = p1++; + } +} + +static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float sx, float sy) +{ + int i, j, closed; + NSVGpath* path; + NSVGpoint* p0, *p1; + float miterLimit = shape->miterLimit; + int lineJoin = shape->strokeLineJoin; + int lineCap = shape->strokeLineCap; + const float sw = (sx + sy) / 2; // average scaling factor + const float lineWidth = shape->strokeWidth * sw; // FIXME (?) + + for (path = shape->paths; path != NULL; path = path->next) { + // Flatten path + r->npoints = 0; + nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, NSVG_PT_CORNER); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*sx,p[1]*sy, p[2]*sx,p[3]*sy, p[4]*sx,p[5]*sy, p[6]*sx,p[7]*sy, 0, NSVG_PT_CORNER); + } + if (r->npoints < 2) + continue; + + closed = path->closed; + + // If the first and last points are the same, remove the last, mark as closed path. + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { + r->npoints--; + p0 = &r->points[r->npoints-1]; + closed = 1; + } + + if (shape->strokeDashCount > 0) { + int idash = 0, dashState = 1; + float totalDist = 0, dashLen, allDashLen, dashOffset; + NSVGpoint cur; + + if (closed) + nsvg__appendPathPoint(r, r->points[0]); + + // Duplicate points -> points2. + nsvg__duplicatePoints(r); + + r->npoints = 0; + cur = r->points2[0]; + nsvg__appendPathPoint(r, cur); + + // Figure out dash offset. + allDashLen = 0; + for (j = 0; j < shape->strokeDashCount; j++) + allDashLen += shape->strokeDashArray[j]; + if (shape->strokeDashCount & 1) + allDashLen *= 2.0f; + // Find location inside pattern + dashOffset = fmodf(shape->strokeDashOffset, allDashLen); + if (dashOffset < 0.0f) + dashOffset += allDashLen; + + while (dashOffset > shape->strokeDashArray[idash]) { + dashOffset -= shape->strokeDashArray[idash]; + idash = (idash + 1) % shape->strokeDashCount; + } + dashLen = (shape->strokeDashArray[idash] - dashOffset) * sw; + + for (j = 1; j < r->npoints2; ) { + float dx = r->points2[j].x - cur.x; + float dy = r->points2[j].y - cur.y; + float dist = sqrtf(dx*dx + dy*dy); + + if ((totalDist + dist) > dashLen) { + // Calculate intermediate point + float d = (dashLen - totalDist) / dist; + float x = cur.x + dx * d; + float y = cur.y + dy * d; + nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); + + // Stroke + if (r->npoints > 1 && dashState) { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } + // Advance dash pattern + dashState = !dashState; + idash = (idash+1) % shape->strokeDashCount; + dashLen = shape->strokeDashArray[idash] * sw; + // Restart + cur.x = x; + cur.y = y; + cur.flags = NSVG_PT_CORNER; + totalDist = 0.0f; + r->npoints = 0; + nsvg__appendPathPoint(r, cur); + } else { + totalDist += dist; + cur = r->points2[j]; + nsvg__appendPathPoint(r, cur); + j++; + } + } + // Stroke any leftover path + if (r->npoints > 1 && dashState) + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } else { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); + } + } +} + +static int nsvg__cmpEdge(const void *p, const void *q) +{ + const NSVGedge* a = (const NSVGedge*)p; + const NSVGedge* b = (const NSVGedge*)q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + + +static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) +{ + NSVGactiveEdge* z; + + if (r->freelist != NULL) { + // Restore from freelist. + z = r->freelist; + r->freelist = z->next; + } else { + // Alloc new edge. + z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); + if (z == NULL) return NULL; + } + + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); +// STBTT_assert(e->y0 <= start_point); + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = (int)(-floorf(NSVG__FIX * -dxdy)); + else + z->dx = (int)floorf(NSVG__FIX * dxdy); + z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); +// z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->dir = e->dir; + + return z; +} + +static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) +{ + z->next = r->freelist; + r->freelist = z; +} + +static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) +{ + int i = x0 >> NSVG__FIXSHIFT; + int j = x1 >> NSVG__FIXSHIFT; + if (i < *xmin) *xmin = i; + if (j > *xmax) *xmax = j; + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = (unsigned char)(scanline[i] + maxWeight); + } + } +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) +{ + // non-zero winding fill + int x0 = 0, w = 0; + + if (fillRule == NSVG_FILLRULE_NONZERO) { + // Non-zero + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->dir; + } else { + int x1 = e->x; w += e->dir; + // if we went to zero, we need to draw + if (w == 0) + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } else if (fillRule == NSVG_FILLRULE_EVENODD) { + // Even-odd + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w = 1; + } else { + int x1 = e->x; w = 0; + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } +} + +static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } + +static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return ((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16) | ((unsigned int)a << 24); +} + +static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; + int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; + int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; + int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static unsigned int nsvg__applyOpacity(unsigned int c, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (c) & 0xff; + int g = (c>>8) & 0xff; + int b = (c>>16) & 0xff; + int a = (((c>>24) & 0xff)*iu) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static inline int nsvg__div255(int x) +{ + return ((x+1) * 257) >> 16; +} + +static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, + float tx, float ty, float sx, float sy, NSVGcachedPaint* cache) +{ + + if (cache->type == NSVG_PAINT_COLOR) { + int i, cr, cg, cb, ca; + cr = cache->colors[0] & 0xff; + cg = (cache->colors[0] >> 8) & 0xff; + cb = (cache->colors[0] >> 16) & 0xff; + ca = (cache->colors[0] >> 24) & 0xff; + + for (i = 0; i < count; i++) { + int r,g,b; + int a = nsvg__div255((int)cover[0] * ca); + int ia = 255 - a; + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + } + } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + float fx, fy, dx, gy; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / sx; + fy = ((float)y - ty) / sy; + dx = 1.0f / sx; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gy = fx*t[1] + fy*t[3] + t[5]; + c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + // TODO: focus (fx,fy) + float fx, fy, dx, gx, gy, gd; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / sx; + fy = ((float)y - ty) / sy; + dx = 1.0f / sx; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gx = fx*t[0] + fy*t[2] + t[4]; + gy = fx*t[1] + fy*t[3] + t[5]; + gd = sqrtf(gx*gx + gy*gy); + c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } +} + +static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float sx, float sy, NSVGcachedPaint* cache, char fillRule) +{ + NSVGactiveEdge *active = NULL; + int y, s; + int e = 0; + int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline + int xmin, xmax; + + for (y = 0; y < r->height; y++) { + memset(r->scanline, 0, r->width); + xmin = r->width; + xmax = 0; + for (s = 0; s < NSVG__SUBSAMPLES; ++s) { + // find center of pixel for this scanline + float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; + NSVGactiveEdge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + NSVGactiveEdge *z = *step; + if (z->ey <= scany) { + *step = z->next; // delete from list +// NSVG__assert(z->valid); + nsvg__freeActive(r, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for (;;) { + int changed = 0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + NSVGactiveEdge* t = *step; + NSVGactiveEdge* q = t->next; + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e < r->nedges && r->edges[e].y0 <= scany) { + if (r->edges[e].y1 > scany) { + NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); + if (z == NULL) break; + // find insertion point + if (active == NULL) { + active = z; + } else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + NSVGactiveEdge* p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + e++; + } + + // now process all active edges in non-zero fashion + if (active != NULL) + nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); + } + // Blit + if (xmin < 0) xmin = 0; + if (xmax > r->width-1) xmax = r->width-1; + if (xmin <= xmax) { + nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, sx, sy, cache); + } + } + +} + +static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) +{ + int x,y; + + // Unpremultiply + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = row[0], g = row[1], b = row[2], a = row[3]; + if (a != 0) { + row[0] = (unsigned char)(r*255/a); + row[1] = (unsigned char)(g*255/a); + row[2] = (unsigned char)(b*255/a); + } + row += 4; + } + } + + // Defringe + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = 0, g = 0, b = 0, a = row[3], n = 0; + if (a == 0) { + if (x-1 > 0 && row[-1] != 0) { + r += row[-4]; + g += row[-3]; + b += row[-2]; + n++; + } + if (x+1 < w && row[7] != 0) { + r += row[4]; + g += row[5]; + b += row[6]; + n++; + } + if (y-1 > 0 && row[-stride+3] != 0) { + r += row[-stride]; + g += row[-stride+1]; + b += row[-stride+2]; + n++; + } + if (y+1 < h && row[stride+3] != 0) { + r += row[stride]; + g += row[stride+1]; + b += row[stride+2]; + n++; + } + if (n > 0) { + row[0] = (unsigned char)(r/n); + row[1] = (unsigned char)(g/n); + row[2] = (unsigned char)(b/n); + } + } + row += 4; + } + } +} + + +static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) +{ + int i, j; + NSVGgradient* grad; + + cache->type = paint->type; + + if (paint->type == NSVG_PAINT_COLOR) { + cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); + return; + } + + grad = paint->gradient; + + cache->spread = grad->spread; + memcpy(cache->xform, grad->xform, sizeof(float)*6); + + if (grad->nstops == 0) { + for (i = 0; i < 256; i++) + cache->colors[i] = 0; + } if (grad->nstops == 1) { + for (i = 0; i < 256; i++) + cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity); + } else { + unsigned int ca, cb = 0; + float ua, ub, du, u; + int ia, ib, count; + + ca = nsvg__applyOpacity(grad->stops[0].color, opacity); + ua = nsvg__clampf(grad->stops[0].offset, 0, 1); + ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + for (i = 0; i < ia; i++) { + cache->colors[i] = ca; + } + + for (i = 0; i < grad->nstops-1; i++) { + ca = nsvg__applyOpacity(grad->stops[i].color, opacity); + cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); + ua = nsvg__clampf(grad->stops[i].offset, 0, 1); + ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + count = ib - ia; + if (count <= 0) continue; + u = 0; + du = 1.0f / (float)count; + for (j = 0; j < count; j++) { + cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); + u += du; + } + } + + for (i = ib; i < 256; i++) + cache->colors[i] = cb; + } + +} + +/* +static void dumpEdges(NSVGrasterizer* r, const char* name) +{ + float xmin = 0, xmax = 0, ymin = 0, ymax = 0; + NSVGedge *e = NULL; + int i; + if (r->nedges == 0) return; + FILE* fp = fopen(name, "w"); + if (fp == NULL) return; + + xmin = xmax = r->edges[0].x0; + ymin = ymax = r->edges[0].y0; + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + xmin = nsvg__minf(xmin, e->x0); + xmin = nsvg__minf(xmin, e->x1); + xmax = nsvg__maxf(xmax, e->x0); + xmax = nsvg__maxf(xmax, e->x1); + ymin = nsvg__minf(ymin, e->y0); + ymin = nsvg__minf(ymin, e->y1); + ymax = nsvg__maxf(ymax, e->y0); + ymax = nsvg__maxf(ymax, e->y1); + } + + fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); + + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); + } + + for (i = 0; i < r->npoints; i++) { + if (i+1 < r->npoints) + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); + } + + fprintf(fp, ""); + fclose(fp); +} +*/ + -+void nsvgRasterizeXY(NSVGrasterizer* r, ++static void nsvgRasterizeXY(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, + float sx, float sy, + unsigned char* dst, int w, int h, int stride) +{ + NSVGshape *shape = NULL; + NSVGedge *e = NULL; + NSVGcachedPaint cache; + int i; + + r->bitmap = dst; + r->width = w; + r->height = h; + r->stride = stride; + + if (w > r->cscanline) { + r->cscanline = w; + r->scanline = (unsigned char*)realloc(r->scanline, w); + if (r->scanline == NULL) return; + } + + for (i = 0; i < h; i++) + memset(&dst[i*stride], 0, w*4); + + for (shape = image->shapes; shape != NULL; shape = shape->next) { + if (!(shape->flags & NSVG_FLAGS_VISIBLE)) + continue; + + if (shape->fill.type != NSVG_PAINT_NONE) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShape(r, shape, sx, sy); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + if (r->nedges != 0) + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->fill, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty, sx, sy, &cache, shape->fillRule); + } + if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * sx) > 0.01f) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShapeStroke(r, shape, sx, sy); + +// dumpEdges(r, "edge.svg"); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + if (r->nedges != 0) + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->stroke, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,sx, sy, &cache, NSVG_FILLRULE_NONZERO); + } + } + + nsvg__unpremultiplyAlpha(dst, w, h, stride); + + r->bitmap = NULL; + r->width = 0; + r->height = 0; + r->stride = 0; +} + -+void nsvgRasterize(NSVGrasterizer* r, ++static void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride) +{ + nsvgRasterizeXY(r,image, tx, ty, scale, scale, dst, w, h, stride); +} + +#endif // NANOSVGRAST_IMPLEMENTATION + +#endif // NANOSVGRAST_H diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_Arrange_Core_NFP_NFPConcave__CGAL.cpp b/cad/PrusaSlicer/files/patch-src_libslic3r_Arrange_Core_NFP_NFPConcave__CGAL.cpp index 6ac5039ca58f..b576e929a3a7 100644 --- a/cad/PrusaSlicer/files/patch-src_libslic3r_Arrange_Core_NFP_NFPConcave__CGAL.cpp +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_Arrange_Core_NFP_NFPConcave__CGAL.cpp @@ -1,11 +1,12 @@ ---- src/libslic3r/Arrange/Core/NFP/NFPConcave_CGAL.cpp.orig 2024-03-01 12:13:10 UTC +--- src/libslic3r/Arrange/Core/NFP/NFPConcave_CGAL.cpp.orig 2024-09-18 13:39:04 UTC +++ src/libslic3r/Arrange/Core/NFP/NFPConcave_CGAL.cpp -@@ -5,6 +5,8 @@ - #include "NFP.hpp" - #include "NFPConcave_CGAL.hpp" - +@@ -2,6 +2,9 @@ + ///|/ + ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher + ///|/ ++ +#include + #include #include #include diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_CMakeLists.txt b/cad/PrusaSlicer/files/patch-src_libslic3r_CMakeLists.txt index 770fb44cf65a..f98b32884c91 100644 --- a/cad/PrusaSlicer/files/patch-src_libslic3r_CMakeLists.txt +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_CMakeLists.txt @@ -1,10 +1,10 @@ ---- src/libslic3r/CMakeLists.txt.orig 2024-06-27 09:25:47 UTC +--- src/libslic3r/CMakeLists.txt.orig 2024-09-18 13:39:04 UTC +++ src/libslic3r/CMakeLists.txt -@@ -611,7 +611,6 @@ target_link_libraries(libslic3r - target_link_libraries(libslic3r +@@ -611,7 +611,6 @@ target_link_libraries(libslic3r PRIVATE + + target_link_libraries(libslic3r PRIVATE libnest2d - admesh - libcereal - libigl - miniz boost_libs + clipper + libexpat diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_CutSurface.cpp b/cad/PrusaSlicer/files/patch-src_libslic3r_CutSurface.cpp index cc1eb6867fd6..ef21b28fe2d0 100644 --- a/cad/PrusaSlicer/files/patch-src_libslic3r_CutSurface.cpp +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_CutSurface.cpp @@ -1,10 +1,11 @@ ---- src/libslic3r/CutSurface.cpp.orig 2024-03-01 11:43:14 UTC +--- src/libslic3r/CutSurface.cpp.orig 2024-09-18 13:39:04 UTC +++ src/libslic3r/CutSurface.cpp -@@ -29,6 +29,7 @@ using namespace Slic3r; - using namespace Slic3r; - #include "ExPolygonsIndex.hpp" +@@ -27,6 +27,8 @@ using namespace Slic3r; + //#define DEBUG_OUTPUT_DIR std::string("C:/data/temp/cutSurface/") + using namespace Slic3r; +#include ++ #include #include #include diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_EmbossShape.hpp b/cad/PrusaSlicer/files/patch-src_libslic3r_EmbossShape.hpp index 77071f9a68b9..bd5b00181892 100644 --- a/cad/PrusaSlicer/files/patch-src_libslic3r_EmbossShape.hpp +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_EmbossShape.hpp @@ -1,11 +1,11 @@ ---- src/libslic3r/EmbossShape.hpp.orig 2024-01-12 13:37:33 UTC +--- src/libslic3r/EmbossShape.hpp.orig 2024-09-18 13:39:04 UTC +++ src/libslic3r/EmbossShape.hpp @@ -12,7 +12,7 @@ #include "Point.hpp" // Transform3d #include "ExPolygon.hpp" #include "ExPolygonSerialize.hpp" -#include "nanosvg/nanosvg.h" // NSVGimage -+#include "libnanosvg/nanosvg.h" // NSVGimage ++#include // NSVGimage namespace Slic3r { diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_Format_SL1__SVG.cpp b/cad/PrusaSlicer/files/patch-src_libslic3r_Format_SL1__SVG.cpp index 20e244e4398b..4193cd3fa627 100644 --- a/cad/PrusaSlicer/files/patch-src_libslic3r_Format_SL1__SVG.cpp +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_Format_SL1__SVG.cpp @@ -1,12 +1,11 @@ ---- src/libslic3r/Format/SL1_SVG.cpp.orig 2023-12-12 14:21:21 UTC +--- src/libslic3r/Format/SL1_SVG.cpp.orig 2024-09-18 13:39:04 UTC +++ src/libslic3r/Format/SL1_SVG.cpp -@@ -10,7 +10,8 @@ - #include "libslic3r/Format/ZipperArchiveImport.hpp" +@@ -28,7 +28,7 @@ + #include + #include - #define NANOSVG_IMPLEMENTATION -#include "nanosvg/nanosvg.h" -+#define NANOSVGRAST_IMPLEMENTATION -+#include "libnanosvg/nanosvgrast.h" ++#include - #include - #include + namespace Slic3r { + class SLAPrint; diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_Format_SVG.cpp b/cad/PrusaSlicer/files/patch-src_libslic3r_Format_SVG.cpp new file mode 100644 index 000000000000..da1a588c74a0 --- /dev/null +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_Format_SVG.cpp @@ -0,0 +1,11 @@ +--- src/libslic3r/Format/SVG.cpp.orig 2024-09-18 13:39:04 UTC ++++ src/libslic3r/Format/SVG.cpp +@@ -7,7 +7,7 @@ + ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher + ///|/ + #include +-#include ++#include + #include + #include + #include diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_Geometry_VoronoiUtilsCgal.cpp b/cad/PrusaSlicer/files/patch-src_libslic3r_Geometry_VoronoiUtilsCgal.cpp index f511e85ee2dc..ed19dfea6089 100644 --- a/cad/PrusaSlicer/files/patch-src_libslic3r_Geometry_VoronoiUtilsCgal.cpp +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_Geometry_VoronoiUtilsCgal.cpp @@ -1,17 +1,17 @@ ---- src/libslic3r/Geometry/VoronoiUtilsCgal.cpp.orig 2024-03-01 11:50:11 UTC +--- src/libslic3r/Geometry/VoronoiUtilsCgal.cpp.orig 2024-09-18 13:39:04 UTC +++ src/libslic3r/Geometry/VoronoiUtilsCgal.cpp @@ -2,6 +2,7 @@ ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ +#include #include #include #include -@@ -325,4 +326,4 @@ VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(cons +@@ -333,4 +334,4 @@ VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(cons return true; } -} // namespace Slic3r::Geometry \ No newline at end of file +} // namespace Slic3r::Geometry diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_MeshBoolean.cpp b/cad/PrusaSlicer/files/patch-src_libslic3r_MeshBoolean.cpp index 20e08fb5b937..70349ca87532 100644 --- a/cad/PrusaSlicer/files/patch-src_libslic3r_MeshBoolean.cpp +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_MeshBoolean.cpp @@ -1,11 +1,11 @@ ---- src/libslic3r/MeshBoolean.cpp.orig 2024-03-01 11:44:05 UTC +--- src/libslic3r/MeshBoolean.cpp.orig 2024-09-18 13:39:04 UTC +++ src/libslic3r/MeshBoolean.cpp -@@ -8,6 +8,8 @@ - #include "libslic3r/TryCatchSignal.hpp" +@@ -10,6 +10,8 @@ + #undef PI +#include + // Include igl first. It defines "L" macro which then clashes with our localization - #include - #undef L + #include // IWYU pragma: keep + #include diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_NSVGUtils.cpp b/cad/PrusaSlicer/files/patch-src_libslic3r_NSVGUtils.cpp new file mode 100644 index 000000000000..8e0cecb16d24 --- /dev/null +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_NSVGUtils.cpp @@ -0,0 +1,11 @@ +--- src/libslic3r/NSVGUtils.cpp.orig 2024-09-18 13:39:04 UTC ++++ src/libslic3r/NSVGUtils.cpp +@@ -5,7 +5,7 @@ + #include "NSVGUtils.hpp" + + #include +-#include ++#include + #include + #include + #include diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_NSVGUtils.hpp b/cad/PrusaSlicer/files/patch-src_libslic3r_NSVGUtils.hpp index 28acfa15ccec..5889e58ddbfc 100644 --- a/cad/PrusaSlicer/files/patch-src_libslic3r_NSVGUtils.hpp +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_NSVGUtils.hpp @@ -1,11 +1,11 @@ ---- src/libslic3r/NSVGUtils.hpp.orig 2023-12-12 14:21:21 UTC +--- src/libslic3r/NSVGUtils.hpp.orig 2024-09-18 13:39:04 UTC +++ src/libslic3r/NSVGUtils.hpp -@@ -11,7 +11,7 @@ +@@ -15,7 +15,7 @@ #include "Polygon.hpp" #include "ExPolygon.hpp" #include "EmbossShape.hpp" // ExPolygonsWithIds -#include "nanosvg/nanosvg.h" // load SVG file -+#include "libnanosvg/nanosvg.h" // load SVG file ++#include + #include "libslic3r/Point.hpp" + #include "libslic3r/libslic3r.h" - // Helper function to work with nano svg - namespace Slic3r { diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_Platform.cpp b/cad/PrusaSlicer/files/patch-src_libslic3r_Platform.cpp index 7a2ff6c43b2b..5eaefd436470 100644 --- a/cad/PrusaSlicer/files/patch-src_libslic3r_Platform.cpp +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_Platform.cpp @@ -1,13 +1,21 @@ ---- src/libslic3r/Platform.cpp.orig 2023-12-12 14:21:21 UTC +--- src/libslic3r/Platform.cpp.orig 2024-09-18 13:39:04 UTC +++ src/libslic3r/Platform.cpp @@ -90,6 +90,10 @@ void detect_platform() BOOST_LOG_TRIVIAL(info) << "Platform: OpenBSD"; s_platform = Platform::BSDUnix; s_platform_flavor = PlatformFlavor::OpenBSD; +#elif defined(__FreeBSD__) + BOOST_LOG_TRIVIAL(info) << "Platform: FreeBSD"; + s_platform = Platform::BSDUnix; + s_platform_flavor = PlatformFlavor::FreeBSD; #else // This should not happen. BOOST_LOG_TRIVIAL(info) << "Platform: Unknown"; +@@ -138,6 +142,7 @@ std::string platform_flavor_to_string(PlatformFlavor p + case PlatformFlavor::WSL : return "WSL"; + case PlatformFlavor::WSL2 : return "WSL2"; + case PlatformFlavor::OpenBSD : return "OpenBSD"; ++ case PlatformFlavor::FreeBSD : return "FreeBSD"; + case PlatformFlavor::GenericOSX : return "GenericOSX"; + case PlatformFlavor::OSXOnX86 : return "OSXOnX86"; + case PlatformFlavor::OSXOnArm : return "OSXOnArm"; diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_SupportSpotsGenerator.cpp b/cad/PrusaSlicer/files/patch-src_libslic3r_SupportSpotsGenerator.cpp index fb72be9e02cc..78d404572e88 100644 --- a/cad/PrusaSlicer/files/patch-src_libslic3r_SupportSpotsGenerator.cpp +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_SupportSpotsGenerator.cpp @@ -1,11 +1,11 @@ ---- src/libslic3r/SupportSpotsGenerator.cpp.orig 2024-02-29 13:03:32 UTC +--- src/libslic3r/SupportSpotsGenerator.cpp.orig 2024-09-18 13:39:04 UTC +++ src/libslic3r/SupportSpotsGenerator.cpp -@@ -1054,7 +1054,7 @@ SliceMappings update_active_object_parts(const Layer +@@ -1062,7 +1062,7 @@ SliceMappings update_active_object_parts(const Layer } } const float bottom_z = layer->bottom_z(); - auto estimate_conn_strength = [bottom_z](const SliceConnection &conn) { + auto estimate_conn_strength = [bottom_z](const SliceConnection &conn) -> float { if (conn.area < EPSILON) { // connection is empty, does not exists. Return max strength so that it is not picked as the // weakest connection. return INFINITY; diff --git a/cad/PrusaSlicer/files/patch-src_libslic3r_Triangulation.cpp b/cad/PrusaSlicer/files/patch-src_libslic3r_Triangulation.cpp index 43387a72e12d..832bb05f2ee4 100644 --- a/cad/PrusaSlicer/files/patch-src_libslic3r_Triangulation.cpp +++ b/cad/PrusaSlicer/files/patch-src_libslic3r_Triangulation.cpp @@ -1,12 +1,12 @@ ---- src/libslic3r/Triangulation.cpp.orig 2024-03-01 11:48:14 UTC +--- src/libslic3r/Triangulation.cpp.orig 2024-09-18 13:39:04 UTC +++ src/libslic3r/Triangulation.cpp @@ -2,6 +2,9 @@ ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ + +#include + #include "Triangulation.hpp" - #include "IntersectionPoints.hpp" + #include diff --git a/cad/PrusaSlicer/files/patch-src_occt__wrapper_CMakeLists.txt b/cad/PrusaSlicer/files/patch-src_occt__wrapper_CMakeLists.txt index 562dec3deff8..55cd9289846e 100644 --- a/cad/PrusaSlicer/files/patch-src_occt__wrapper_CMakeLists.txt +++ b/cad/PrusaSlicer/files/patch-src_occt__wrapper_CMakeLists.txt @@ -1,29 +1,29 @@ ---- src/occt_wrapper/CMakeLists.txt.orig 2023-06-19 12:07:14 UTC +--- src/occt_wrapper/CMakeLists.txt.orig 2024-09-18 13:39:04 UTC +++ src/occt_wrapper/CMakeLists.txt @@ -19,14 +19,13 @@ generate_export_header(OCCTWrapper) generate_export_header(OCCTWrapper) --find_package(OpenCASCADE 7.6.2 REQUIRED) +-find_package(OpenCASCADE 7.6.1 REQUIRED) +list(APPEND CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib/cmake) +find_package(OpenCASCADE REQUIRED) set(OCCT_LIBS - TKXDESTEP - TKSTEP - TKSTEP209 - TKSTEPAttr - TKSTEPBase + -L${CMAKE_INSTALL_PREFIX}/lib -+ TKXSDRAWSTEP -+ TKDESTEP ++ TKXSDRAWSTEP ++ TKDESTEP TKXCAF TKXSBase TKVCAF -@@ -58,5 +57,5 @@ include(GNUInstallDirs) +@@ -59,5 +58,5 @@ include(GNUInstallDirs) include(GNUInstallDirs) -install(TARGETS OCCTWrapper DESTINATION "${CMAKE_INSTALL_BINDIR}") +install(TARGETS OCCTWrapper DESTINATION "${CMAKE_INSTALL_LIBDIR}") diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_CMakeLists.txt b/cad/PrusaSlicer/files/patch-src_slic3r_CMakeLists.txt index 4d1d84d171cd..22fc6d300879 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_CMakeLists.txt +++ b/cad/PrusaSlicer/files/patch-src_slic3r_CMakeLists.txt @@ -1,26 +1,45 @@ ---- src/slic3r/CMakeLists.txt.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/CMakeLists.txt.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/CMakeLists.txt -@@ -375,7 +375,8 @@ set(SLIC3R_GUI_SOURCES - Utils/PrusaConnect.cpp +@@ -382,7 +382,8 @@ set(SLIC3R_GUI_SOURCES + Utils/ServiceConfig.cpp ) -find_package(NanoSVG REQUIRED) -+# find_package(NanoSVG REQUIRED) ++#find_package(NanoSVG REQUIRED) +find_package(OpenSSL REQUIRED) if (APPLE) list(APPEND SLIC3R_GUI_SOURCES -@@ -404,11 +405,11 @@ encoding_check(libslic3r_gui) - - encoding_check(libslic3r_gui) - --target_link_libraries(libslic3r_gui libslic3r avrdude libcereal imgui libvgcode GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES} NanoSVG::nanosvg NanoSVG::nanosvgrast) -+target_link_libraries(libslic3r_gui libslic3r avrdude imgui libvgcode GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +@@ -426,7 +427,6 @@ target_link_libraries( + PUBLIC + libslic3r + avrdude +- libcereal + imgui + libvgcode + GLEW::GLEW +@@ -434,19 +434,21 @@ target_link_libraries( + hidapi + libcurl + ${wxWidgets_LIBRARIES} +- NanoSVG::nanosvg +- NanoSVG::nanosvgrast + stb_dxt + fastfloat ++ OpenSSL::SSL ++ OpenSSL::Crypto + ) if (MSVC) - target_link_libraries(libslic3r_gui Setupapi.lib) + target_link_libraries(libslic3r_gui PUBLIC Setupapi.lib) -elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") - target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES}) + target_link_libraries(libslic3r_gui PUBLIC ${DBUS_LIBRARIES}) elseif (APPLE) - target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY} ${COREWLAN_LIBRARY}) + target_link_libraries(libslic3r_gui PUBLIC ${DISKARBITRATION_LIBRARY} ${COREWLAN_LIBRARY}) + endif() ++ ++add_definitions(-DBOOST_LOG_DYN_LINK) + + #if (SLIC3R_STATIC) + # FIXME: This was previously exported by wx-config but the wxWidgets diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_BitmapCache.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_BitmapCache.cpp index 6ca562b20d59..0397cfc9295c 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_BitmapCache.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_BitmapCache.cpp @@ -1,32 +1,32 @@ ---- src/slic3r/GUI/BitmapCache.cpp.orig 2023-12-12 14:21:21 UTC +--- src/slic3r/GUI/BitmapCache.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/BitmapCache.cpp @@ -20,8 +20,9 @@ #include #endif /* __WXGTK2__ */ -#include -#include +#include -+#include "libnanosvg/nanosvg.h" -+#include "libnanosvg/nanosvgrast.h" ++#include ++#include namespace Slic3r { namespace GUI { @@ -73,7 +74,7 @@ wxBitmapBundle* BitmapCache::insert_bndl(const std::st wxVector bitmaps; std::set scales = {1.0}; -#ifndef __linux__ +#if !defined(__linux__) && !defined(__FreeBSD__) #ifdef __APPLE__ scales.emplace(m_scale); @@ -554,7 +555,7 @@ wxBitmapBundle BitmapCache::mksolid(size_t width_in, s wxVector bitmaps; std::set scales = { 1.0 }; -#ifndef __linux__ +#if !defined(__linux__) && !defined(__FreeBSD__) #ifdef __APPLE__ scales.emplace(m_scale); diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ConfigWizard.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ConfigWizard.cpp index 17e8a257528d..8912fc74fa5c 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ConfigWizard.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ConfigWizard.cpp @@ -1,47 +1,63 @@ ---- src/slic3r/GUI/ConfigWizard.cpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/ConfigWizard.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/ConfigWizard.cpp @@ -638,7 +638,7 @@ void PageWelcome::set_run_reason(ConfigWizard::RunReas const bool data_empty = run_reason == ConfigWizard::RR_DATA_EMPTY; welcome_text->Show(data_empty); cbox_reset->Show(!data_empty); -#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(SLIC3R_DESKTOP_INTEGRATION) if (!DesktopIntegrationDialog::is_integrated()) cbox_integrate->Show(true); else @@ -1696,7 +1696,7 @@ PageDownloader::PageDownloader(ConfigWizard* parent) )); } -#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(SLIC3R_DESKTOP_INTEGRATION) append_text(wxString::Format(_L( "On Linux systems the process of registration also creates desktop integration files for this version of application." ))); -@@ -1759,7 +1759,7 @@ bool DownloaderUtils::Worker::perform_register(const s +@@ -1714,7 +1714,7 @@ bool PageDownloader::on_finish_downloader() const + return m_downloader->on_finish(); + } + +-#ifdef __linux__ ++#if defined(__linux__) || defined(__FreeBSD__) + bool DownloaderUtils::Worker::perform_registration_linux = false; + #endif // __linux__ + +@@ -1763,10 +1763,10 @@ bool DownloaderUtils::Worker::perform_url_register() } //key_full = "\"C:\\Program Files\\Prusa3D\\PrusaSlicer\\prusa-slicer-console.exe\" \"%1\""; key_full = key_string; -#elif __APPLE__ +#elif defined(__APPLE__) || defined(__FreeBSD__) // Apple registers for custom url in info.plist thus it has to be already registered since build. // The url will always trigger opening of prusaslicer and we have to check that user has allowed it. (GUI_App::MacOpenURL is the triggered method) - #elif defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) -@@ -1778,7 +1778,7 @@ void DownloaderUtils::Worker::deregister() +-#elif defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) ++#elif (defined(__linux__) || defined(__FreeBSD__)) && defined(SLIC3R_DESKTOP_INTEGRATION + // the performation should be called later during desktop integration + perform_registration_linux = true; + #endif +@@ -1782,9 +1782,9 @@ void DownloaderUtils::Worker::deregister() return; } key_full = key_string; -#elif __APPLE__ +#elif defined(__APPLE__) || defined(__FreeBSD__) // TODO - #elif defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) +-#elif defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) ++#elif (defined(__linux__) || defined(__FreeBSD__)) && defined(SLIC3R_DESKTOP_INTEGRATION BOOST_LOG_TRIVIAL(debug) << "DesktopIntegrationDialog::undo_downloader_registration"; -@@ -3422,7 +3422,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_c + DesktopIntegrationDialog::undo_downloader_registration(); + perform_registration_linux = false; +@@ -3421,7 +3421,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_c if ((check_unsaved_preset_changes = install_bundles.size() > 0)) header = _L_PLURAL("A new vendor was installed and one of its printers will be activated", "New vendors were installed and one of theirs printers will be activated", install_bundles.size()); -#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(SLIC3R_DESKTOP_INTEGRATION) // Desktop integration on Linux BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << DownloaderUtils::Worker::perform_registration_linux; if (page_welcome->integrate_desktop()) diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ConfigWizard.hpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ConfigWizard.hpp index 49df66adfe4d..63af1840bafa 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ConfigWizard.hpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ConfigWizard.hpp @@ -1,11 +1,11 @@ ---- src/slic3r/GUI/ConfigWizard.hpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/ConfigWizard.hpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/ConfigWizard.hpp -@@ -53,7 +53,7 @@ namespace DownloaderUtils { - +@@ -54,7 +54,7 @@ namespace DownloaderUtils { bool on_finish(); - static bool perform_register(const std::string& path); + static bool perform_download_register(const std::string& path); + static bool perform_url_register(); -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) static bool perform_registration_linux; #endif // __linux__ }; diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_DesktopIntegrationDialog.hpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_DesktopIntegrationDialog.hpp index 7cd3f1114751..0b3241780c71 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_DesktopIntegrationDialog.hpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_DesktopIntegrationDialog.hpp @@ -1,18 +1,18 @@ ---- src/slic3r/GUI/DesktopIntegrationDialog.hpp.orig 2023-12-12 14:21:21 UTC +--- src/slic3r/GUI/DesktopIntegrationDialog.hpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/DesktopIntegrationDialog.hpp @@ -2,7 +2,7 @@ ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) #ifndef slic3r_DesktopIntegrationDialog_hpp_ #define slic3r_DesktopIntegrationDialog_hpp_ -@@ -46,4 +46,4 @@ class DesktopIntegrationDialog : public wxDialog (priv +@@ -47,4 +47,4 @@ class DesktopIntegrationDialog : public wxDialog (priv } // namespace Slic3r #endif // slic3r_DesktopIntegrationDialog_hpp_ -#endif // __linux__ \ No newline at end of file +#endif // __linux__ diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_EditGCodeDialog.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_EditGCodeDialog.cpp new file mode 100644 index 000000000000..b85d7617c5f8 --- /dev/null +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_EditGCodeDialog.cpp @@ -0,0 +1,29 @@ +--- src/slic3r/GUI/EditGCodeDialog.cpp.orig 2024-10-15 09:06:23 UTC ++++ src/slic3r/GUI/EditGCodeDialog.cpp +@@ -566,7 +566,7 @@ void ParamsModel::GetValue(wxVariant& variant, const w + + ParamsNode* node = static_cast(item.GetID()); + if (col == (unsigned int)0) +-#ifdef __linux__ ++#if defined(__linux__) || defined(__FreeBSD__) + variant << wxDataViewIconText(node->text, get_bmp_bundle(node->icon_name)->GetIconFor(m_ctrl->GetParent())); + #else + variant << DataViewBitmapText(node->text, get_bmp_bundle(node->icon_name)->GetBitmapFor(m_ctrl->GetParent())); +@@ -581,7 +581,7 @@ bool ParamsModel::SetValue(const wxVariant& variant, c + + ParamsNode* node = static_cast(item.GetID()); + if (col == (unsigned int)0) { +-#ifdef __linux__ ++#if defined(__linux__) || defined(__FreeBSD__) + wxDataViewIconText data; + data << variant; + node->icon = data.GetIcon(); +@@ -658,7 +658,7 @@ ParamsViewCtrl::ParamsViewCtrl(wxWindow *parent, wxSiz + this->AssociateModel(model); + model->SetAssociatedControl(this); + +-#ifdef __linux__ ++#if defined(__linux__) || defined(__FreeBSD__) + wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); + #ifdef SUPPORTS_MARKUP + rd->EnableMarkup(true); diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Field.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Field.cpp index f21bbca1ba56..5ba4452cf0ed 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Field.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Field.cpp @@ -1,11 +1,11 @@ ---- src/slic3r/GUI/Field.cpp.orig 2023-12-12 14:21:21 UTC +--- src/slic3r/GUI/Field.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/Field.cpp -@@ -221,7 +221,7 @@ bool Field::is_matched(const std::string& string, cons +@@ -222,7 +222,7 @@ static wxString na_value(bool for_spin_ctrl = false) static wxString na_value(bool for_spin_ctrl = false) { -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) if (for_spin_ctrl) return ""; #endif diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GLCanvas3D.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GLCanvas3D.cpp index 840d7463283b..2a9da38ad3af 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GLCanvas3D.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GLCanvas3D.cpp @@ -1,11 +1,11 @@ ---- src/slic3r/GUI/GLCanvas3D.cpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/GLCanvas3D.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/GLCanvas3D.cpp -@@ -104,7 +104,7 @@ float RetinaHelper::get_scale_factor() { return float( +@@ -107,7 +107,7 @@ float RetinaHelper::get_scale_factor() { return float( #endif // __WXGTK3__ // Fixed the collision between BuildVolume::Type::Convex and macro Convex defined inside /usr/include/X11/X.h that is included by WxWidgets 3.0. -#if defined(__linux__) && defined(Convex) +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(Convex) #undef Convex #endif diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GLTexture.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GLTexture.cpp index a1a487b06d0f..a941583bbda0 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GLTexture.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GLTexture.cpp @@ -1,13 +1,13 @@ ---- src/slic3r/GUI/GLTexture.cpp.orig 2023-12-12 14:21:21 UTC +--- src/slic3r/GUI/GLTexture.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/GLTexture.cpp @@ -25,8 +25,8 @@ #define STB_DXT_IMPLEMENTATION - #include "stb_dxt/stb_dxt.h" + #include -#include -#include -+#include "libnanosvg/nanosvg.h" -+#include "libnanosvg/nanosvgrast.h" ++#include ++#include #include "libslic3r/Utils.hpp" diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GUI__App.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GUI__App.cpp index c50a678ec045..e760f2c122b4 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GUI__App.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_GUI__App.cpp @@ -1,128 +1,128 @@ ---- src/slic3r/GUI/GUI_App.cpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/GUI_App.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/GUI_App.cpp -@@ -411,7 +411,7 @@ class SplashScreen : public wxSplashScreen (private) +@@ -412,7 +412,7 @@ class SplashScreen : public wxSplashScreen (private) }; -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) bool static check_old_linux_datadir(const wxString& app_name) { // If we are on Linux and the datadir does not exist yet, look into the old // location where the datadir was before version 2.3. If we find it there, -@@ -1254,7 +1254,8 @@ bool GUI_App::on_init_inner() - +@@ -1256,6 +1256,9 @@ bool GUI_App::on_init_inner() // Set initialization of image handlers before any UI actions - See GH issue #7469 wxInitAllImageHandlers(); -- -+ // Silence warnings generated with wxWidgets 3.2 -+ wxSizerFlags::DisableConsistencyChecks(); - #if defined(_WIN32) && ! defined(_WIN64) - // Win32 32bit build. - if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "64") { -@@ -1282,7 +1283,7 @@ bool GUI_App::on_init_inner() - wxCHECK_MSG(wxDirExists(resources_dir), false, - wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir)); + ++ // Silence warnings generated with wxWidgets 3.2 ++ wxSizerFlags::DisableConsistencyChecks(); ++ + // Set our own gui log as an active target + m_log_gui = new LogGui(); + wxLog::SetActiveTarget(m_log_gui); +@@ -1286,7 +1289,7 @@ bool GUI_App::on_init_inner() + const wxString resources_dir = from_u8(Slic3r::resources_dir()); + wxCHECK_MSG(wxDirExists(resources_dir), false, wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir)); -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) if (! check_old_linux_datadir(GetAppName())) { std::cerr << "Quitting, user chose to move their data to new location." << std::endl; return false; -@@ -1387,7 +1388,7 @@ bool GUI_App::on_init_inner() +@@ -1391,7 +1394,7 @@ bool GUI_App::on_init_inner() if (!default_splashscreen_pos) // revert "restore_win_position" value if application wasn't crashed get_app_config()->set("restore_win_position", "1"); -#ifndef __linux__ +#if !defined(__linux__) && !defined(__FreeBSD__) wxYield(); #endif scrn->SetText(_L("Loading configuration")+ dots); -@@ -1546,7 +1547,7 @@ bool GUI_App::on_init_inner() +@@ -1557,7 +1560,7 @@ bool GUI_App::on_init_inner() // and wxEVT_SET_FOCUS before GUI_App::post_init is called) wasn't called before GUI_App::post_init and OpenGL wasn't initialized. // Since issue #9774 Where same problem occured on MacOS Ventura, we decided to have this check on MacOS as well. -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) if (!m_post_initialized && m_opengl_initialized) { #else if (!m_post_initialized) { -@@ -2236,7 +2237,7 @@ bool GUI_App::switch_language() +@@ -2247,7 +2250,7 @@ bool GUI_App::switch_language() } } -#ifdef __linux__ +#if defined(__linux__) || (__FreeBSD__) static const wxLanguageInfo* linux_get_existing_locale_language(const wxLanguageInfo* language, const wxLanguageInfo* system_language) { -@@ -2438,7 +2439,7 @@ bool GUI_App::load_language(wxString language, bool in +@@ -2449,7 +2452,7 @@ bool GUI_App::load_language(wxString language, bool in m_language_info_best = wxLocale::FindLanguageInfo(best_language); BOOST_LOG_TRIVIAL(trace) << boost::format("Best translation language detected (may be different from user locales): %1%") % m_language_info_best->CanonicalName.ToUTF8().data(); } - #ifdef __linux__ + #if defined(__linux__) || defined(__FreeBSD__) wxString lc_all; if (wxGetEnv("LC_ALL", &lc_all) && ! lc_all.IsEmpty()) { // Best language returned by wxWidgets on Linux apparently does not respect LC_ALL. -@@ -2447,6 +2448,7 @@ bool GUI_App::load_language(wxString language, bool in +@@ -2458,6 +2461,7 @@ bool GUI_App::load_language(wxString language, bool in } #endif } + m_language_info_best = nullptr; } const wxLanguageInfo *language_info = language.empty() ? nullptr : wxLocale::FindLanguageInfo(language); -@@ -2491,7 +2493,7 @@ bool GUI_App::load_language(wxString language, bool in +@@ -2502,7 +2506,7 @@ bool GUI_App::load_language(wxString language, bool in } else if (m_language_info_system != nullptr && language_info->CanonicalName.BeforeFirst('_') == m_language_info_system->CanonicalName.BeforeFirst('_')) language_info = m_language_info_system; -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) // If we can't find this locale , try to use different one for the language // instead of just reporting that it is impossible to switch. if (! wxLocale::IsAvailable(language_info->Language)) { -@@ -2612,7 +2614,7 @@ wxMenu* GUI_App::get_config_menu(MainFrame* main_frame +@@ -2623,7 +2627,7 @@ wxMenu* GUI_App::get_config_menu(MainFrame* main_frame local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _L("Take Configuration &Snapshot"), _L("Capture a configuration snapshot")); local_menu->Append(config_id_base + ConfigMenuUpdateConf, _L("Check for Configuration Updates"), _L("Check for configuration updates")); local_menu->Append(config_id_base + ConfigMenuUpdateApp, _L("Check for Application Updates"), _L("Check for new version of application")); -#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(SLIC3R_DESKTOP_INTEGRATION) //if (DesktopIntegrationDialog::integration_possible()) local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration")); #endif //(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) -@@ -2646,7 +2648,7 @@ wxMenu* GUI_App::get_config_menu(MainFrame* main_frame +@@ -2657,7 +2661,7 @@ wxMenu* GUI_App::get_config_menu(MainFrame* main_frame case ConfigMenuUpdateApp: app_version_check(true); break; -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) case ConfigMenuDesktopIntegration: show_desktop_integration_dialog(); break; -@@ -3318,7 +3320,7 @@ void GUI_App::show_desktop_integration_dialog() +@@ -3336,7 +3340,7 @@ void GUI_App::show_desktop_integration_dialog() void GUI_App::show_desktop_integration_dialog() { -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) //wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null"); DesktopIntegrationDialog dialog(mainframe); dialog.ShowModal(); -@@ -3338,7 +3340,7 @@ void GUI_App::show_downloader_registration_dialog() +@@ -3356,7 +3360,7 @@ void GUI_App::show_downloader_registration_dialog() if (msg.ShowModal() == wxID_YES) { auto downloader_worker = new DownloaderUtils::Worker(nullptr); - downloader_worker->perform_register(app_config->get("url_downloader_dest")); + downloader_worker->perform_download_register(app_config->get("url_downloader_dest")); -#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) -+#if (defined(__linux__) || defined(__FreeBSD__)) && defined(SLIC3R_DESKTOP_INTEGRATION) ++#if (defined(__linux__) || defined(__FreeBSD__)) && defined(SLIC3R_DESKTOP_INTEGRATION) if (DownloaderUtils::Worker::perform_registration_linux) DesktopIntegrationDialog::perform_downloader_desktop_integration(); #endif //(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) -@@ -3746,7 +3748,7 @@ void GUI_App::start_download(std::string url) +@@ -3790,7 +3794,7 @@ void GUI_App::start_download(std::string url) return; } - #if defined(__APPLE__) || (defined(__linux__) && !defined(SLIC3R_DESKTOP_INTEGRATION)) + #if defined(__APPLE__) || ((defined(__linux__) || defined(__FreeBSD__)) && !defined(SLIC3R_DESKTOP_INTEGRATION)) if (app_config && !app_config->get_bool("downloader_url_registered")) { notification_manager()->push_notification(NotificationType::URLNotRegistered); diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Gizmos_GLGizmoSVG.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Gizmos_GLGizmoSVG.cpp index 96d559c54334..50c74f19993c 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Gizmos_GLGizmoSVG.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Gizmos_GLGizmoSVG.cpp @@ -1,20 +1,20 @@ ---- src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp @@ -23,7 +23,7 @@ #include "libslic3r/ClipperUtils.hpp" // union_ex #include "imgui/imgui_stdlib.h" // using std::string for inputs -#include "nanosvg/nanosvg.h" // load SVG file -+#include "libnanosvg/nanosvg.h" // load SVG file ++#include // load SVG file #include // detection of change DPI #include -@@ -587,7 +587,7 @@ void GLGizmoSVG::on_dragging(const UpdateData &data) { +@@ -589,7 +589,7 @@ void GLGizmoSVG::on_dragging(const UpdateData &data) { void GLGizmoSVG::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); } #include "slic3r/GUI/BitmapCache.hpp" -#include "nanosvg/nanosvgrast.h" -+#include "libnanosvg/nanosvgrast.h" ++#include #include "libslic3r/AABBTreeLines.hpp" // aabb lines for draw filled expolygon namespace{ diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_HintNotification.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_HintNotification.cpp new file mode 100644 index 000000000000..33bd3e4ca053 --- /dev/null +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_HintNotification.cpp @@ -0,0 +1,16 @@ +--- src/slic3r/GUI/HintNotification.cpp.orig 2024-10-15 09:07:17 UTC ++++ src/slic3r/GUI/HintNotification.cpp +@@ -161,6 +161,13 @@ TagCheckResult tag_check_system(const std::string& tag + return TagCheckNegative; + #endif // __linux__ + ++ if (tag == "FreeBSD") ++#ifdef __FreeBSD__ ++ return TagCheckAffirmative; ++#else ++ return TagCheckNegative; ++#endif // __FreeBSD__ ++ + if (tag == "OSX") + #ifdef __APPLE__ + return TagCheckAffirmative; diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_IconManager.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_IconManager.cpp index 7d71e6139b65..3296ea3ef4eb 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_IconManager.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_IconManager.cpp @@ -1,13 +1,13 @@ ---- src/slic3r/GUI/IconManager.cpp.orig 2024-01-12 13:38:59 UTC +--- src/slic3r/GUI/IconManager.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/IconManager.cpp @@ -6,8 +6,8 @@ #include #include #include -#include "nanosvg/nanosvg.h" -#include "nanosvg/nanosvgrast.h" -+#include "libnanosvg/nanosvg.h" -+#include "libnanosvg/nanosvgrast.h" ++#include ++#include #include "libslic3r/Utils.hpp" // ScopeGuard #include "3DScene.hpp" // glsafe diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ImGuiWrapper.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ImGuiWrapper.cpp index 2deca2fa60b4..cfd8fac6f7e0 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ImGuiWrapper.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_ImGuiWrapper.cpp @@ -1,13 +1,13 @@ ---- src/slic3r/GUI/ImGuiWrapper.cpp.orig 2023-12-12 14:21:21 UTC +--- src/slic3r/GUI/ImGuiWrapper.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/ImGuiWrapper.cpp @@ -40,8 +40,8 @@ #include "GUI_App.hpp" #include "../Utils/MacDarkMode.hpp" -#include -#include -+#include "libnanosvg/nanosvg.h" -+#include "libnanosvg/nanosvgrast.h" ++#include ++#include // suggest location #include "libslic3r/ClipperUtils.hpp" // Slic3r::intersection diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_InstanceCheck.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_InstanceCheck.cpp index 2d4aad87e7d2..9eb3e97d2088 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_InstanceCheck.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_InstanceCheck.cpp @@ -1,29 +1,29 @@ ---- src/slic3r/GUI/InstanceCheck.cpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/InstanceCheck.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/InstanceCheck.cpp @@ -27,7 +27,7 @@ #include #endif //WIN32 -#if __linux__ +#if defined(__linux__) || defined (__FreeBSD__) #include /* Pull in all of D-Bus headers. */ #endif //__linux__ -@@ -229,7 +229,7 @@ namespace instance_check_internal +@@ -240,7 +240,7 @@ namespace instance_check_internal return false; } -#elif defined(__linux__) +#elif defined(__linux__) || defined (__FreeBSD__) static bool send_message(const std::string &message_text, const std::string &version) { -@@ -317,7 +317,7 @@ bool instance_check(int argc, char** argv, bool app_co +@@ -328,7 +328,7 @@ bool instance_check(int argc, char** argv, bool app_co hashed_path = std::hash{}(boost::filesystem::system_complete(argv[0]).string()); #else boost::system::error_code ec; -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) // If executed by an AppImage, start the AppImage, not the main process. // see https://docs.appimage.org/packaging-guide/environment-variables.html#id2 const char *appimage_env = std::getenv("APPIMAGE"); diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_OptionsGroup.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_OptionsGroup.cpp index 61172494d9be..658e4fde073b 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_OptionsGroup.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_OptionsGroup.cpp @@ -1,20 +1,20 @@ ---- src/slic3r/GUI/OptionsGroup.cpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/OptionsGroup.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/OptionsGroup.cpp -@@ -1215,7 +1215,7 @@ void ogStaticText::SetPathEnd(const std::string& link) +@@ -1216,7 +1216,7 @@ void ogStaticText::SetPathEnd(const std::string& link) void ogStaticText::SetPathEnd(const std::string& link) { -#ifndef __linux__ +#if !defined(__linux__) && !defined(__FreeBSD__) Bind(wxEVT_ENTER_WINDOW, [this, link](wxMouseEvent& event) { SetToolTip(OptionsGroup::get_url(get_app_config()->get("suppress_hyperlinks") != "1" ? link : std::string())); -@@ -1268,7 +1268,7 @@ void ogStaticText::FocusText(bool focus) +@@ -1269,7 +1269,7 @@ void ogStaticText::FocusText(bool focus) SetFont(focus ? Slic3r::GUI::wxGetApp().link_font() : Slic3r::GUI::wxGetApp().normal_font()); -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) this->GetContainingSizer()->Layout(); #endif Refresh(); diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_PhysicalPrinterDialog.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_PhysicalPrinterDialog.cpp index 45dc17baa3c2..2fc2ed86341d 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_PhysicalPrinterDialog.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_PhysicalPrinterDialog.cpp @@ -1,11 +1,11 @@ ---- src/slic3r/GUI/PhysicalPrinterDialog.cpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/PhysicalPrinterDialog.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/PhysicalPrinterDialog.cpp -@@ -606,7 +606,7 @@ void PhysicalPrinterDialog::build_printhost_settings(C +@@ -607,7 +607,7 @@ void PhysicalPrinterDialog::build_printhost_settings(C // Always fill in the "printhost_port" combo box from the config and select it. { Choice* choice = dynamic_cast(m_optgroup->get_field("printhost_port")); - choice->set_values({ m_config->opt_string("printhost_port") }); + choice->set_values((const std::vector){ m_config->opt_string("printhost_port") }); choice->set_selection(); } diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Plater.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Plater.cpp index 466c87ed88bd..cc0f5ef683c6 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Plater.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Plater.cpp @@ -1,38 +1,47 @@ ---- src/slic3r/GUI/Plater.cpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/Plater.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/Plater.cpp -@@ -1163,7 +1163,7 @@ std::vector Plater::priv::load_files(const std +@@ -894,7 +894,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) + + auto open_external_login = [this](wxCommandEvent& evt){ + DownloaderUtils::Worker::perform_url_register(); +-#if defined(__linux__) ++#if defined(__linux__) || defined(__FreeBSD__) + // Remove all desktop files registering prusaslicer:// url done by previous versions. + DesktopIntegrationDialog::undo_downloader_registration_rigid(); + #if defined(SLIC3R_DESKTOP_INTEGRATION) +@@ -1201,7 +1201,7 @@ std::vector Plater::priv::load_files(const std // when loading a project file. However, creating the dialog on heap causes issues on macOS, where it does not // appear at all. Therefore, we create the dialog on stack on Win and macOS, and on heap on Linux, which // is the only system that needed the workarounds in the first place. -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) auto progress_dlg = new wxProgressDialog(loading, "", 100, find_toplevel_parent(q), wxPD_APP_MODAL | wxPD_AUTO_HIDE); Slic3r::ScopeGuard([&progress_dlg](){ if (progress_dlg) progress_dlg->Destroy(); progress_dlg = nullptr; }); #else -@@ -1218,7 +1218,7 @@ std::vector Plater::priv::load_files(const std +@@ -1256,7 +1256,7 @@ std::vector Plater::priv::load_files(const std bool is_project_file = type_prusa; try { if (type_3mf || type_zip_amf) { -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) // On Linux Constructor of the ProgressDialog calls DisableOtherWindows() function which causes a disabling of all children of the find_toplevel_parent(q) // And a destructor of the ProgressDialog calls ReenableOtherWindows() function which revert previously disabled children. // But if printer technology will be changes during project loading, -@@ -3171,7 +3171,7 @@ void Plater::priv::on_right_click(RBtnEvent& evt) +@@ -3225,7 +3225,7 @@ void Plater::priv::on_right_click(RBtnEvent& evt) Vec2d mouse_position = evt.data.first; wxPoint position(static_cast(mouse_position.x()), static_cast(mouse_position.y())); -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) // For some reason on Linux the menu isn't displayed if position is // specified (even though the position is sane). position = wxDefaultPosition; -@@ -4048,7 +4048,7 @@ void Plater::load_project(const wxString& filename) +@@ -4106,7 +4106,7 @@ void Plater::load_project(const wxString& filename) p->reset(); - if (! load_files({ into_path(filename) }).empty()) { + if (! load_files((const std::vector){ into_path(filename) }).empty()) { // At least one file was loaded. p->set_project_filename(filename); // Save the names of active presets and project specific config into ProjectDirtyStateManager. diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Preferences.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Preferences.cpp index 7dac66e12ce6..a7a651f300ff 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Preferences.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Preferences.cpp @@ -1,38 +1,38 @@ ---- src/slic3r/GUI/Preferences.cpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/Preferences.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/Preferences.cpp @@ -29,7 +29,7 @@ #ifdef WIN32 #include #endif // WIN32 -#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(SLIC3R_DESKTOP_INTEGRATION) #include "DesktopIntegrationDialog.hpp" #endif //(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) -@@ -267,7 +267,7 @@ void PreferencesDialog::build() +@@ -270,7 +270,7 @@ void PreferencesDialog::build() tabs = new Notebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME | wxNB_DEFAULT); #else tabs = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL |wxNB_NOPAGETHEME | wxNB_DEFAULT ); -#ifdef __linux__ +#if defined __linux__ || defined __FreeBSD__ tabs->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxBookCtrlEvent& e) { e.Skip(); CallAfter([this]() { tabs->GetCurrentPage()->Layout(); }); -@@ -752,7 +752,7 @@ void PreferencesDialog::accept(wxEvent&) +@@ -755,7 +755,7 @@ void PreferencesDialog::accept(wxEvent&) downloader->allow(it->second == "1"); if (!downloader->on_finish()) return; -#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(SLIC3R_DESKTOP_INTEGRATION) if(DownloaderUtils::Worker::perform_registration_linux) DesktopIntegrationDialog::perform_downloader_desktop_integration(); #endif //(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) -@@ -1111,7 +1111,7 @@ void PreferencesDialog::create_settings_font_widget() +@@ -1113,7 +1113,7 @@ void PreferencesDialog::create_settings_font_widget() font_example->SetFont(font); m_values[opt_key] = format("%1%", val); stb_sizer->Layout(); -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) CallAfter([this]() { refresh_og(m_optgroup_other); }); #else refresh_og(m_optgroup_other); diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Search.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Search.cpp new file mode 100644 index 000000000000..7ac7fddc2862 --- /dev/null +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Search.cpp @@ -0,0 +1,20 @@ +--- src/slic3r/GUI/Search.cpp.orig 2024-10-15 09:09:17 UTC ++++ src/slic3r/GUI/Search.cpp +@@ -455,7 +455,7 @@ void OptionsSearcher::check_and_hide_dialog() + + void OptionsSearcher::check_and_hide_dialog() + { +-#ifdef __linux__ ++#if defined(__linux__) || defined(__FreeBSD__) + // Temporary linux specific workaround: + // has_focus(search_dialog) always returns false + // That's why search dialog will be hidden whole the time +@@ -531,7 +531,7 @@ void OptionsSearcher::process_key_down_from_input(wxKe + search_dialog->Hide(); + else if (search_dialog && (key == WXK_UP || key == WXK_DOWN || key == WXK_NUMPAD_ENTER || key == WXK_RETURN)) { + search_dialog->KeyDown(e); +-#ifdef __linux__ ++#if defined(__linux__) || defined(__FreeBSD__) + search_dialog->SetFocus(); + #endif // __linux__ + } diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Tab.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Tab.cpp index 5018b0d09b12..da78c0abd491 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Tab.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_Tab.cpp @@ -1,38 +1,38 @@ ---- src/slic3r/GUI/Tab.cpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/Tab.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/Tab.cpp @@ -278,7 +278,7 @@ void Tab::create_preset_tab() m_treectrl = new wxTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(20 * m_em_unit, -1), wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS); m_treectrl->SetFont(wxGetApp().normal_font()); -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) m_treectrl->SetBackgroundColour(m_parent->GetBackgroundColour()); #endif m_left_sizer->Add(m_treectrl, 1, wxEXPAND); @@ -292,7 +292,7 @@ void Tab::create_preset_tab() // This helps to process all the cursor key events on Windows in the tree control, // so that the cursor jumps to the last item. m_treectrl->Bind(wxEVT_TREE_SEL_CHANGED, [this](wxTreeEvent&) { -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) // Events queue is opposite On Linux. wxEVT_SET_FOCUS invokes after wxEVT_TREE_SEL_CHANGED, // and a result wxEVT_KILL_FOCUS doesn't invoke for the TextCtrls. // see https://github.com/prusa3d/PrusaSlicer/issues/5720 -@@ -4106,7 +4106,7 @@ bool Tab::tree_sel_change_delayed() +@@ -4143,7 +4143,7 @@ bool Tab::tree_sel_change_delayed() // There is a bug related to Ubuntu overlay scrollbars, see https://github.com/prusa3d/PrusaSlicer/issues/898 and https://github.com/prusa3d/PrusaSlicer/issues/952. // The issue apparently manifests when Show()ing a window with overlay scrollbars while the UI is frozen. For this reason, // we will Thaw the UI prematurely on Linux. This means destroing the no_updates object prematurely. -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) std::unique_ptr no_updates(new wxWindowUpdateLocker(this)); #else /* On Windows we use DoubleBuffering during rendering, -@@ -4152,7 +4152,7 @@ bool Tab::tree_sel_change_delayed() +@@ -4189,7 +4189,7 @@ bool Tab::tree_sel_change_delayed() if (wxGetApp().mainframe!=nullptr && wxGetApp().mainframe->is_active_and_shown_tab(this)) activate_selected_page(throw_if_canceled); - #ifdef __linux__ + #if defined(__linux__) || defined(__FreeBSD__) no_updates.reset(nullptr); #endif diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_TopBar.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_TopBar.cpp new file mode 100644 index 000000000000..966d656c0407 --- /dev/null +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_TopBar.cpp @@ -0,0 +1,11 @@ +--- src/slic3r/GUI/TopBar.cpp.orig 2024-10-15 09:10:59 UTC ++++ src/slic3r/GUI/TopBar.cpp +@@ -52,7 +52,7 @@ TopBarItemsCtrl::Button::Button(wxWindow* parent, cons + Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { set_hovered(false); event.Skip(); }); + + Bind(wxEVT_PAINT, [this](wxPaintEvent&) { render(); }); +-#ifdef __linux__ ++#if defined(__linux__) || defined(__FreeBSD__) + Bind(wxEVT_LEFT_UP, [this](wxMouseEvent& event) { + #else + Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& event) { diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UnsavedChangesDialog.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UnsavedChangesDialog.cpp index 87d421aee3f3..ab6e0e643896 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UnsavedChangesDialog.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UnsavedChangesDialog.cpp @@ -1,65 +1,65 @@ ---- src/slic3r/GUI/UnsavedChangesDialog.cpp.orig 2024-06-27 09:25:47 UTC +--- src/slic3r/GUI/UnsavedChangesDialog.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/UnsavedChangesDialog.cpp -@@ -31,7 +31,7 @@ using boost::optional; +@@ -32,7 +32,7 @@ using boost::optional; using boost::optional; -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) #define wxLinux true #else #define wxLinux false -@@ -111,7 +111,7 @@ ModelNode::ModelNode(ModelNode* parent, const wxString +@@ -112,7 +112,7 @@ ModelNode::ModelNode(ModelNode* parent, const wxString UpdateIcons(); } -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) wxIcon ModelNode::get_bitmap(const wxString& color) #else wxBitmap ModelNode::get_bitmap(const wxString& color) -@@ -120,7 +120,7 @@ wxBitmap ModelNode::get_bitmap(const wxString& color) +@@ -121,7 +121,7 @@ wxBitmap ModelNode::get_bitmap(const wxString& color) wxBitmap bmp = get_solid_bmp_bundle(64, 16, into_u8(color))->GetBitmapFor(m_parent_win); if (!m_toggle) bmp = bmp.ConvertToDisabled(); -#ifndef __linux__ +#if !defined(__linux__) && !defined(__FreeBSD__) return bmp; #else wxIcon icon; -@@ -224,7 +224,7 @@ void ModelNode::UpdateIcons() +@@ -225,7 +225,7 @@ void ModelNode::UpdateIcons() if (!m_toggle) bmp = bmp.ConvertToDisabled(); -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) m_icon.CopyFromBitmap(bmp); #else m_icon = bmp; -@@ -376,7 +376,7 @@ void DiffModel::GetValue(wxVariant& variant, const wxD +@@ -377,7 +377,7 @@ void DiffModel::GetValue(wxVariant& variant, const wxD case colToggle: variant = node->m_toggle; break; -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) case colIconText: variant << wxDataViewIconText(node->m_text, node->m_icon); break; -@@ -419,7 +419,7 @@ bool DiffModel::SetValue(const wxVariant& variant, con +@@ -420,7 +420,7 @@ bool DiffModel::SetValue(const wxVariant& variant, con case colToggle: node->m_toggle = variant.GetBool(); return true; -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) case colIconText: { wxDataViewIconText data; data << variant; -@@ -647,7 +647,7 @@ void DiffViewCtrl::AppendBmpTextColumn(const wxString& +@@ -648,7 +648,7 @@ void DiffViewCtrl::AppendBmpTextColumn(const wxString& void DiffViewCtrl::AppendBmpTextColumn(const wxString& label, unsigned model_column, int width, bool set_expander/* = false*/) { m_columns_width.emplace(this->GetColumnCount(), width); -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); #ifdef SUPPORTS_MARKUP rd->EnableMarkup(true); diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UpdateDialogs.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UpdateDialogs.cpp index 187196d10d43..d4f691292fa9 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UpdateDialogs.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UpdateDialogs.cpp @@ -1,20 +1,20 @@ ---- src/slic3r/GUI/UpdateDialogs.cpp.orig 2023-12-12 14:21:21 UTC +--- src/slic3r/GUI/UpdateDialogs.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/UpdateDialogs.cpp -@@ -147,7 +147,7 @@ AppUpdateDownloadDialog::AppUpdateDownloadDialog( cons +@@ -153,7 +153,7 @@ AppUpdateDownloadDialog::AppUpdateDownloadDialog( cons versions->Add(new wxStaticText(this, wxID_ANY, ver_online.to_string())); content_sizer->Add(versions); content_sizer->AddSpacer(VERT_SPACING); -#ifndef __linux__ +#if !defined(__linux__) && !defined(__FreeBSD__) cbox_run = new wxCheckBox(this, wxID_ANY, _(L("Run installer after download. (Otherwise file explorer will be opened)"))); content_sizer->Add(cbox_run); #endif -@@ -253,7 +253,7 @@ AppUpdateDownloadDialog::~AppUpdateDownloadDialog() {} +@@ -259,7 +259,7 @@ bool AppUpdateDownloadDialog::run_after_download() con bool AppUpdateDownloadDialog::run_after_download() const { -#ifndef __linux__ +#if !defined(__linux__) && !defined(__FreeBSD__) return cbox_run->GetValue(); #endif return false; diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UserAccountCommunication.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UserAccountCommunication.cpp index e92da0515b48..c2a79e267391 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UserAccountCommunication.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_UserAccountCommunication.cpp @@ -1,46 +1,46 @@ ---- src/slic3r/GUI/UserAccountCommunication.cpp.orig 2024-07-03 10:33:48 UTC +--- src/slic3r/GUI/UserAccountCommunication.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/GUI/UserAccountCommunication.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -37,7 +38,7 @@ #include #endif -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) #include #include #include @@ -137,7 +138,7 @@ bool load_secret(const std::string& opt, std::string& #endif // wxUSE_SECRETSTORE } -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) void load_refresh_token_linux(std::string& refresh_token) { // Load refresh token from UserAccount.dat @@ -201,7 +202,7 @@ UserAccountCommunication::UserAccountCommunication(wxE shared_session_key = key0; } else { -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) load_refresh_token_linux(refresh_token); #endif } -@@ -253,7 +254,7 @@ void UserAccountCommunication::set_username(const std: +@@ -254,7 +255,7 @@ void UserAccountCommunication::set_username(const std: save_secret("tokens", m_session->get_shared_session_key(), tokens); } else { -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) // If we can't store the tokens in secret store, store them in file with chmod 600 boost::filesystem::path target(boost::filesystem::path(Slic3r::data_dir()) / "UserAccount.dat") ; std::string data = m_session->get_refresh_token(); diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_GUI_WipeTowerDialog.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_WipeTowerDialog.cpp new file mode 100644 index 000000000000..ffd72a02a351 --- /dev/null +++ b/cad/PrusaSlicer/files/patch-src_slic3r_GUI_WipeTowerDialog.cpp @@ -0,0 +1,11 @@ +--- src/slic3r/GUI/WipeTowerDialog.cpp.orig 2024-10-15 09:12:21 UTC ++++ src/slic3r/GUI/WipeTowerDialog.cpp +@@ -379,7 +379,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std:: + int xpos = m_gridsizer_advanced->GetPosition().x; + if (!m_page_advanced->IsEnabled()) { + dc.SetTextForeground(wxSystemSettings::GetColour( +-#if defined (__linux__) && defined (__WXGTK2__) ++#if (defined(__linux__) || defined(__FreeBSD__)) && defined (__WXGTK2__) + wxSYS_COLOUR_BTNTEXT + #else + wxSYS_COLOUR_GRAYTEXT diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_Utils_AppUpdater.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_Utils_AppUpdater.cpp new file mode 100644 index 000000000000..6585ac8d34b4 --- /dev/null +++ b/cad/PrusaSlicer/files/patch-src_slic3r_Utils_AppUpdater.cpp @@ -0,0 +1,11 @@ +--- src/slic3r/Utils/AppUpdater.cpp.orig 2024-10-15 09:13:05 UTC ++++ src/slic3r/Utils/AppUpdater.cpp +@@ -156,7 +156,7 @@ AppUpdater::priv::priv() : + + AppUpdater::priv::priv() : + m_cancel (false) +-#ifdef __linux__ ++#if defined(__linux__) || defined(__FreeBSD__) + , m_default_dest_folder (boost::filesystem::path("/tmp")) + #else + , m_default_dest_folder (boost::filesystem::path(data_dir()) / "cache") diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_Utils_FontConfigHelp.hpp b/cad/PrusaSlicer/files/patch-src_slic3r_Utils_FontConfigHelp.hpp index 2dc70cf22907..0f4eb01b49e3 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_Utils_FontConfigHelp.hpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_Utils_FontConfigHelp.hpp @@ -1,11 +1,11 @@ ---- src/slic3r/Utils/FontConfigHelp.hpp.orig 2023-12-12 14:21:21 UTC +--- src/slic3r/Utils/FontConfigHelp.hpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/Utils/FontConfigHelp.hpp -@@ -5,7 +5,7 @@ - #ifndef slic3r_FontConfigHelp_hpp_ +@@ -6,7 +6,7 @@ #define slic3r_FontConfigHelp_hpp_ + #include -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) #define EXIST_FONT_CONFIG_INCLUDE #endif diff --git a/cad/PrusaSlicer/files/patch-src_slic3r_Utils_WxFontUtils.cpp b/cad/PrusaSlicer/files/patch-src_slic3r_Utils_WxFontUtils.cpp index 880e2fbecc42..5df7a478eadd 100644 --- a/cad/PrusaSlicer/files/patch-src_slic3r_Utils_WxFontUtils.cpp +++ b/cad/PrusaSlicer/files/patch-src_slic3r_Utils_WxFontUtils.cpp @@ -1,45 +1,38 @@ ---- src/slic3r/Utils/WxFontUtils.cpp.orig 2023-12-12 14:21:21 UTC +--- src/slic3r/Utils/WxFontUtils.cpp.orig 2024-09-18 13:39:04 UTC +++ src/slic3r/Utils/WxFontUtils.cpp -@@ -12,7 +12,7 @@ +@@ -19,7 +19,7 @@ #include #include // wxNativeFontInfo #include -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) #include "slic3r/Utils/FontConfigHelp.hpp" #endif -@@ -76,7 +76,7 @@ bool WxFontUtils::can_load(const wxFont &font) +@@ -83,7 +83,7 @@ bool WxFontUtils::can_load(const wxFont &font) #elif defined(__APPLE__) return true; //return is_valid_ttf(get_file_path(font)); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) return true; // font config check file path take about 4000ms for chech them all //std::string font_path = Slic3r::GUI::get_font_path(font); -@@ -97,7 +97,7 @@ std::unique_ptr WxFontUtils::create_ +@@ -104,7 +104,7 @@ std::unique_ptr WxFontUtils::create_ return nullptr; } return Emboss::create_font_file(file_path.c_str()); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) std::string font_path = Slic3r::GUI::get_font_path(font); if (font_path.empty()){ BOOST_LOG_TRIVIAL(error) << "Can not read font('" << get_human_readable_name(font) << "'), " -@@ -118,7 +118,7 @@ EmbossStyle::Type WxFontUtils::get_current_type() +@@ -125,7 +125,7 @@ EmbossStyle::Type WxFontUtils::get_current_type() return EmbossStyle::Type::wx_win_font_descr; #elif defined(__APPLE__) return EmbossStyle::Type::wx_mac_font_descr; -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) return EmbossStyle::Type::wx_lin_font_descr; #else return EmbossStyle::Type::undefined; -@@ -351,4 +351,4 @@ std::unique_ptr WxFontUtils::set_bol - // There is NO bold font by wx - font.SetWeight(orig_weight); - return nullptr; --} -\ No newline at end of file -+} diff --git a/cad/PrusaSlicer/files/patch-tests_libslic3r_test__emboss.cpp b/cad/PrusaSlicer/files/patch-tests_libslic3r_test__emboss.cpp index 0d471ff6b3f0..5421f3b1f7c4 100644 --- a/cad/PrusaSlicer/files/patch-tests_libslic3r_test__emboss.cpp +++ b/cad/PrusaSlicer/files/patch-tests_libslic3r_test__emboss.cpp @@ -1,11 +1,11 @@ ---- tests/libslic3r/test_emboss.cpp.orig 2023-12-12 14:21:21 UTC +--- tests/libslic3r/test_emboss.cpp.orig 2024-09-18 13:39:04 UTC +++ tests/libslic3r/test_emboss.cpp -@@ -195,7 +195,7 @@ TEST_CASE("Visualize glyph from font", "[Emboss]") +@@ -196,7 +196,7 @@ TEST_CASE("Visualize glyph from font", "[Emboss]") #endif // VISUALIZE #include "test_utils.hpp" -#include // load SVG file +#include // load SVG file #include #include ExPolygons heal_and_check(const Polygons &polygons)